Show program messages in 3D view.
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 23 Mar 2021 00:00:27 +0000 (17:00 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 23 Mar 2021 00:00:27 +0000 (17:00 -0700)
Moved documentation pages to DOCS page.
Allow multiple file upload.
Show upload progress when uploading files or firmware.
Warn when leaving Settings without having saved.
Many other fixes for v1.0.0

78 files changed:
CHANGELOG.md
MANIFEST.in
Makefile
src/js/api.js
src/js/app.js
src/js/dialog.js
src/js/dragbar.js [new file with mode: 0644]
src/js/file-dialog.js
src/js/files.js
src/js/main.js
src/js/message.js
src/js/path-viewer.js
src/js/settings-admin.js
src/js/upload-dialog.js [new file with mode: 0644]
src/js/util.js
src/js/video.js
src/js/view-control.js
src/js/view-docs.js [new file with mode: 0644]
src/js/view-editor.js
src/js/view-settings.js
src/js/view-viewer.js
src/pug/index.pug
src/pug/templates/dialog.pug
src/pug/templates/docs-gcode.pug [new file with mode: 0644]
src/pug/templates/docs-help.pug [new file with mode: 0644]
src/pug/templates/docs-license.pug [new file with mode: 0644]
src/pug/templates/dragbar.pug [new file with mode: 0644]
src/pug/templates/file-dialog.pug
src/pug/templates/files.pug
src/pug/templates/indicators.pug
src/pug/templates/message.pug
src/pug/templates/path-viewer.pug
src/pug/templates/settings-admin.pug
src/pug/templates/settings-macros.pug
src/pug/templates/upload-dialog.pug [new file with mode: 0644]
src/pug/templates/view-cheat-sheet.pug [deleted file]
src/pug/templates/view-control.pug
src/pug/templates/view-docs.pug [new file with mode: 0644]
src/pug/templates/view-editor.pug
src/pug/templates/view-files.pug
src/pug/templates/view-help.pug [deleted file]
src/pug/templates/view-license.pug [deleted file]
src/pug/templates/view-settings.pug
src/py/bbctrl/Ctrl.py
src/py/bbctrl/FileSystem.py
src/py/bbctrl/FileSystemHandler.py
src/py/bbctrl/Mach.py
src/py/bbctrl/Planner.py
src/py/bbctrl/Preplanner.py
src/py/bbctrl/ProgramQueue.py [new file with mode: 0644]
src/py/bbctrl/Web.py
src/py/bbctrl/__init__.py
src/py/bbctrl/plan.py
src/resources/config-template.json
src/static/js/codemirror-javascript.js [new file with mode: 0644]
src/static/js/codemirror.js
src/static/js/socket.io.js [deleted file]
src/stylus/cheat-sheet.styl
src/stylus/dragbar.styl [new file with mode: 0644]
src/stylus/files.styl
src/stylus/header.styl
src/stylus/indicators.styl
src/stylus/loading-message.styl
src/stylus/main.styl
src/stylus/menu.styl
src/stylus/modal.styl
src/stylus/path-viewer.styl
src/stylus/style.styl
src/stylus/tabs.styl
src/stylus/upload-dialog.styl [new file with mode: 0644]
src/stylus/view-camera.styl
src/stylus/view-control.styl
src/stylus/view-docs.styl [new file with mode: 0644]
src/stylus/view-editor.styl
src/stylus/view-files.styl
src/stylus/view-help.styl [deleted file]
src/stylus/view-settings.styl
src/stylus/view-viewer.styl

index e7eece2bafface1055199542cf98c877d2178b4b..8e8c1985cfa664732be502509a9db0247002420f 100644 (file)
@@ -3,7 +3,7 @@ Buildbotics CNC Controller Firmware Changelog
 
 ## v1.0.0
  - Added online GCode editor.
- - Added online file dialog.
+ - Added online file manager.
  - Allow subdirectories of files.
  - Full page 3D viewer.
  - Full page camera view.
@@ -16,6 +16,11 @@ Buildbotics CNC Controller Firmware Changelog
  - Better gamepad handling.
  - Fixed problem with web layout while using virtual keyboard.
  - Fixed problem with entering config values while virtual keyboard is active.
+ - Warn when leaving Settings without having saved.
+ - Show upload progress when uploading files or firmware.
+ - Allow multiple file upload.
+ - Moved documentation pages to ``DOCS`` page.
+ - Show program messages in 3D view.
 
 ## v0.4.16
  - Improved axis under/over warning tooltip.
index 98cc101f431bfcb30aa5baee60ca13aa3d54366c..8e31cd16efc7709f13e69c3784d9e1f14a3214b6 100644 (file)
@@ -1,4 +1,5 @@
 recursive-include src/py/bbctrl/http *
+recursive-include src/py/bbctrl/tpl_lib *
 include src/py/bbctrl/camotics.so
 include package.json README.md scripts/install.sh
 include src/avr/bbctrl-avr-firmware.hex
index 7234a51815b894497b580690c8d0b16bf6c6ceb9..9234d4fe4edb273c723098b4f12c381c3ef4bce0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -54,6 +54,7 @@ bbemu:
        $(MAKE) -C src/avr/emu
 
 pkg: all $(AVR_FIRMWARE) bbserial
+       cp -a rpi-share/camotics/tpl_lib src/py/bbctrl/
        ./setup.py sdist
 
 beta-pkg: pkg
index 7425417a5db20e2a95a444b1c3061c849ca9835c..903226dc17d8b5fbf6f369452b47154bd3fe4b09 100644 (file)
@@ -82,11 +82,27 @@ module.exports = {
   },
 
 
-  upload: function(url, data, config) {
+  upload: function(url, data, config, progress) {
+    var xhr = function () {
+      var xhr = new window.XMLHttpRequest();
+
+      if (progress) {
+        xhr.upload.addEventListener('progress', function (e) {
+          if (e.lengthComputable) progress(e.loaded / e.total, false, xhr);
+        })
+        xhr.upload.addEventListener('loadend', function (e) {
+          progress(e.loaded / e.total, true, xhr);
+        })
+      }
+
+      return xhr;
+    }
+
     config = $.extend({
       processData: false,
       contentType: false,
       cache: false,
+      xhr: xhr,
       data: data
     }, config);
 
index 6b55cb778adee879418be185fa80aaf8ad498a48..da57e2a3663667f6ef6a547142ba5e5a12d6094e 100644 (file)
@@ -30,6 +30,7 @@
 var api    = require('./api');
 var cookie = require('./cookie');
 var Sock   = require('./sock');
+var util   = require('./util');
 
 
 function compare_versions(a, b) {
@@ -108,7 +109,9 @@ module.exports = new Vue({
       errorMessage: '',
       checkedUpgrade: false,
       latestVersion: '',
-      showError: false
+      showError: false,
+      showPopup: true,
+      webGLSupported: util.webgl_supported()
     }
   },
 
@@ -122,12 +125,7 @@ module.exports = new Vue({
     'view-settings':  require('./view-settings'),
     'view-files':     require('./view-files'),
     'view-camera':    {template: '#view-camera-template'},
-    'view-help':      {template: '#view-help-template'},
-    'view-license':   {template: '#view-license-template'},
-    'view-cheat-sheet': {
-      template: '#view-cheat-sheet-template',
-      data: function () {return {showUnimplemented: false}}
-    }
+    'view-docs':      require('./view-docs')
   },
 
 
@@ -137,6 +135,13 @@ module.exports = new Vue({
 
 
   events: {
+    route: function (path) {
+      if (typeof this.$options.components['view-' + path[0]] == 'undefined')
+        return location.hash = 'control';
+      else this.currentView = path[0];
+    },
+
+
     'hostname-changed': function (hostname) {this.hostname = hostname},
 
 
@@ -148,8 +153,8 @@ module.exports = new Vue({
     },
 
 
-    connected: function () {this.update()},
-    update: function () {this.update()},
+    connected: function () {this.update(this.parse_hash)},
+    update: function () {this.update(this.parse_hash)},
 
 
     check: function (show_message) {
@@ -210,6 +215,8 @@ module.exports = new Vue({
         if (!/^#/.test(text)) msgs.push(text);
       }
 
+      this.showPopup = msgs.length != 0;
+
       return msgs;
     },
 
@@ -224,6 +231,22 @@ module.exports = new Vue({
   ready: function () {
     $(window).on('hashchange', this.parse_hash);
     this.connect();
+
+    var warned = cookie.get_bool('webgl-warning', false);
+    if (util.webgl_supported() && !util.webgl_supported(true)) {
+      var msg = 'Your browser does not have hardware support for 3D ' +
+          'graphics.  3D viewer performance may be slow.'
+
+      console.warn(msg);
+
+      if (!warned)
+        this.open_dialog({
+          title: 'WebGL Warning',
+          body: msg,
+          buttons: 'ok',
+          callback: function () {cookie.set_bool('webgl-warning', true)}
+        });
+    }
   },
 
 
@@ -243,10 +266,9 @@ module.exports = new Vue({
     },
 
 
-    update: function () {
+    update: function (done) {
       api.get('config/load').done(function (config) {
         update_object(this.config, config, true);
-        this.parse_hash();
 
         if (!this.checkedUpgrade) {
           this.checkedUpgrade = true;
@@ -255,6 +277,8 @@ module.exports = new Vue({
           if (typeof check == 'undefined' || check)
             this.$emit('check');
         }
+
+        if (done != undefined) done(true);
       }.bind(this))
     },
 
@@ -301,22 +325,32 @@ module.exports = new Vue({
     },
 
 
-    parse_hash: function () {
-      var hash = location.hash.substr(1);
+    route: function (path) {
+      var cancel = false;
+      var cb = function () {cancel = true}
 
-      if (!hash.trim().length) {
-        location.hash = 'control';
-        return;
-      }
+      this.$emit('route-changing', path, cb);
+      this.$broadcast('route-changing', path, cb);
 
-      var parts = hash.split(':');
+      if (cancel) return history.back();
+      this.$emit('route', path);
+      this.$broadcast('route', path);
+    },
 
-      if (typeof this.$options.components['view-' + parts[0]] == 'undefined') {
-        location.hash = 'control';
-        return;
 
-      } else this.currentView = parts[0];
+    replace_route: function (hash) {
+      history.replaceState(null, '', '#' + hash);
+      this.parse_hash()
+    },
+
+
+    parse_hash: function () {
+      var hash = location.hash.substr(1);
+
+      if (!hash.trim().length)
+        return location.hash = 'control';
 
+      this.route(hash.split(':'));
     },
 
 
@@ -332,16 +366,17 @@ module.exports = new Vue({
     },
 
 
-    file_dialog: function (config) {this.$refs.fileDialog.open(config)},
-    open_dialog: function (config) {this.$refs.dialog.open(config)},
-    error_dialog:   function (msg) {this.$refs.dialog.error(msg)},
-    warning_dialog: function (msg) {this.$refs.dialog.warning(msg)},
-    success_dialog: function (msg) {this.$refs.dialog.success(msg)},
+    file_dialog:    function (config) {this.$refs.fileDialog.open(config)},
+    upload:         function (config) {this.$refs.uploader.upload(config)},
+    open_dialog:    function (config) {this.$refs.dialog.open(config)},
+    error_dialog:   function (msg)    {this.$refs.dialog.error(msg)},
+    warning_dialog: function (msg)    {this.$refs.dialog.warning(msg)},
+    success_dialog: function (msg)    {this.$refs.dialog.success(msg)},
 
 
     api_error: function (msg, error) {
-      if (typeof error != 'undefined') {
-        if (typeof error.message != 'undefined')
+      if (error != undefined) {
+        if (error.message != undefined)
           msg += '\n' + error.message;
         else msg += '\n' + JSON.stringify(error);
       }
index dd025ab4e1da815a887487b35e6f6604eed18d39..12a309ff403ab957f5106f59b4ceb3f93e03ead2 100644 (file)
 
 function get_icon(action) {
   switch (action.toLowerCase()) {
-  case 'ok': case 'yes': return 'check';
-  case 'cancel': case 'no': return 'times';
+  case 'ok':     case 'yes': return 'check';
+  case 'cancel': case 'no':  return 'times';
+  case 'save':               return 'floppy-o';
+  case 'discard':            return 'trash';
   }
 
   return undefined
 }
 
 
+function make_button(button) {
+  if (typeof button == 'string') button = {text: button}
+  button.action = button.action || button.text.toLowerCase()
+  button.icon = button.icon || get_icon(button.action);
+  return button;
+}
+
+
 module.exports = {
   template: '#dialog-template',
 
@@ -79,19 +89,8 @@ module.exports = {
       if (typeof buttons == 'string') buttons = buttons.split(' ');
 
       this.buttons = [];
-      for (var i = 0; i < buttons.length; i++) {
-        if (typeof buttons[i] == 'string')
-          this.buttons.push({
-            action: buttons[i].toLowerCase(),
-            text: buttons[i],
-            icon: get_icon(buttons[i])
-          })
-
-        else {
-          buttons[i].action = buttons[i].action || buttons[i].text.toLowerCase()
-          this.buttons.push(buttons[i]);
-        }
-      }
+      for (var i = 0; i < buttons.length; i++)
+        this.buttons.push(make_button(buttons[i]))
 
       this.show = true;
     },
diff --git a/src/js/dragbar.js b/src/js/dragbar.js
new file mode 100644 (file)
index 0000000..2417de1
--- /dev/null
@@ -0,0 +1,108 @@
+/******************************************************************************\
+
+                  This file is part of the Buildbotics firmware.
+
+         Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.
+
+          This Source describes Open Hardware and is licensed under the
+                                  CERN-OHL-S v2.
+
+          You may redistribute and modify this Source and make products
+     using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl).
+            This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED
+     WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS
+      FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable
+                                   conditions.
+
+                 Source location: https://github.com/buildbotics
+
+       As per CERN-OHL-S v2 section 4, should You produce hardware based on
+     these sources, You must maintain the Source Location clearly visible on
+     the external case of the CNC Controller or other product you make using
+                                   this Source.
+
+                 For more information, email info@buildbotics.com
+
+\******************************************************************************/
+
+'use strict'
+
+
+module.exports = {
+  template: '#dragbar-template',
+
+  props: {
+    direction: {
+      validator: function (value) {
+        return value == 'veritcal' || value == 'horizontal';
+      },
+      default: 'vertical'
+    }
+  },
+
+
+  data: function() {
+    return {
+      last: 0
+    }
+  },
+
+
+  methods: {
+    resize: function (e) {
+      if (e.which != 1) return; // Primary click only
+      e.preventDefault();
+
+      var parent = e.target.parentNode;
+      var startX = e.pageX;
+      var startY = e.pageY;
+      var startHeight = parent.clientHeight;
+      var startWidth = parent.clientWidth;
+      var cursor = document.body.style.cursor;
+      document.body.style.cursor =
+        this.direction == 'vertical' ? 'row-resize' : 'col-resize';
+
+      var onmove = function (e) {
+        e.preventDefault();
+
+        if (this.direction == 'vertical') {
+          var h = startHeight + (startY - e.pageY)
+          if (h < 0) h = 0;
+          parent.style.height = h + 'px';
+
+        } else {
+          var w = startWidth + (startX - e.pageX)
+          if (w < 0) w = 0;
+          parent.style.width = w + 'px';
+        }
+
+        window.dispatchEvent(new Event('resize'));
+      }.bind(this);
+
+      var onup = function (e) {
+        e.preventDefault();
+
+        document.body.style.cursor = cursor;
+        window.removeEventListener('mousemove', onmove);
+        window.removeEventListener('mouseup', onup);
+
+        // Toggle on double click
+        var now = new Date().getTime();
+        if (now - this.last < 500) {
+          if (this.direction == 'vertical') parent.style.height =
+            parent.style.height == '0px' ? '10000px' : '0px';
+          else parent.style.width =
+            parent.style.width == '0px' ? '10000px' : '0px';
+
+          this.last = 0;
+
+        } else this.last = now;
+
+        window.dispatchEvent(new Event('resize'));
+      }.bind(this);
+
+      window.addEventListener('mousemove', onmove);
+      window.addEventListener('mouseup', onup);
+    }
+  }
+}
index 3f82134e476d77874aa385295936c37661af55a2..5e44f9920c797791f5d427b9a824d7e16b9fccef 100644 (file)
@@ -68,7 +68,7 @@ module.exports = {
     response: function (path) {
       this.show = false;
 
-      if (this.config.save) {
+      if (path && this.config.save) {
         var filename = util.basename(path);
         var exists = this.$refs.files.has_file(filename);
 
index ba3c9879a46c892a48f512e842316171f2cce803..3e61c7a11b0c2f18211d5e1d48a2e332d0efd005 100644 (file)
@@ -53,7 +53,6 @@ module.exports = {
       selected: -1,
       filename: '',
       folder: '',
-      activeFile: {},
       showNewFolder: false
     }
   },
@@ -174,16 +173,17 @@ module.exports = {
     },
 
 
-    load: function (path) {
+    load: function (path, done) {
       api.get('fs/' + path)
         .done(function (data) {
           this.fs = data
           this.selected = -1;
+          if (done) done();
         }.bind(this))
     },
 
 
-    reload: function () {this.load(this.fs.path || '')},
+    reload: function (done) {this.load(this.fs.path || '', done)},
 
 
     path_at: function (index) {
@@ -212,8 +212,10 @@ module.exports = {
       if (!this.folder_valid) return;
       this.showNewFolder = false;
 
-      api.put('fs/' + this.fs.path + '/' + this.folder)
-        .done(this.reload);
+      var path = this.fs.path + '/' + this.folder;
+
+      api.put('fs/' + path)
+        .done(function () {this.open(path)}.bind(this));
     },
 
 
@@ -238,52 +240,54 @@ module.exports = {
     },
 
 
-    upload: function ()  {
-      // If we don't reset the form the browser may cache file if name is same
-      // even if contents have changed
-      this.$els.uploadForm.reset();
-      this.$els.uploadFormInput.click();
+    activate_file: function (filename) {
+      for (var i = 0; i < this.files.length; i++)
+        if (this.files[i].name == filename)
+          this.activate(this.files[i]);
     },
 
 
-    do_upload: function (e)  {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-
-      var file = files[0];
-      var filename = util.basename(util.unix_path(file.name));
-
-      var upload = function() {
-        var fd = new FormData();
-        fd.append('file', file);
-
-        api.upload('fs/' + this.fs.path + '/' + filename, fd)
-          .done(this.reload)
-          .fail(function (error) {
-            this.$root.api_error('Upload failed', error)
-          }.bind(this));
-      }.bind(this);
-
-      // Check if file already exists
-      var other = this.find_file(filename);
-
-      if (other) {
-        if (other.dir)
-          this.$root.open_dialog({
-            title: 'Cannot overwrite',
-            body: 'Cannot overwrite directory ' + filename + '.',
-            buttons: 'OK'
-          });
-
-        else
-          this.$root.open_dialog({
-            title: 'Overwrite file?',
-            body: 'Are you sure you want to overwrite ' + filename + '?',
-            buttons: 'Cancel OK',
-            callback: function (action) {if (action == 'ok') upload()}
-          });
-
-      } else upload();
+    confirm_upload: function (file, cb) {
+      file.filename = util.basename(util.unix_path(file.name));
+
+      var other = this.find_file(file.filename);
+      if (!other) return cb(true);
+
+      if (other.dir)
+        this.$root.open_dialog({
+          title: 'Cannot overwrite',
+          body: 'Cannot overwrite directory ' + file.filename + '.',
+          buttons: 'OK',
+          callback: function () {cb(false)}
+        });
+
+      else
+        this.$root.open_dialog({
+          title: 'Overwrite file?',
+          body: 'Are you sure you want to overwrite ' + file.filename + '?',
+          buttons: 'Cancel OK',
+          callback: function (action) {cb(action == 'ok')}
+        });
+    },
+
+
+    upload_success: function (file, next) {
+      this.reload(function () {
+        if (this.mode == 'open') this.activate_file(file.filename)
+        next();
+      }.bind(this));
+    },
+
+
+    upload: function ()  {
+      this.$root.upload({
+        multiple: this.mode != 'open',
+        url: function (file) {
+          return 'fs/' + this.fs.path + '/' + file.filename;
+        }.bind(this),
+        on_confirm: this.confirm_upload,
+        on_success: this.upload_success
+      })
     }
   }
 }
index 228d1e4e62a6a1f244a4ccb50b1156c9f1e4e8d7..d59b9f6b2c1c0b0f37a88d6469521f2e94f0da4e 100644 (file)
@@ -32,28 +32,26 @@ var cookie = require('./cookie');
 var util   = require('./util');
 
 
-function ui() {
+function menu_ui() {
   var layout   = document.getElementById('layout');
-  var menu     = document.getElementById('menu');
   var menuLink = document.getElementById('menuLink');
 
+  var collapse = function () {
+    layout.classList.remove('active');
+    window.removeEventListener('click', collapse);
+  }
+
   menuLink.onclick = function (e) {
     e.preventDefault();
+    e.stopPropagation();
     layout.classList.toggle('active');
-    menu.classList.toggle('active');
-    menuLink.classList.toggle('active');
-  }
-
-  menu.onclick = function (e) {
-    layout.classList.remove('active');
-    menu.classList.remove('active');
-    menuLink.classList.remove('active');
+    window.addEventListener('click', collapse);
   }
 }
 
 
 $(function() {
-  ui();
+  menu_ui();
 
   if (typeof cookie.get('client-id') == 'undefined')
     cookie.set('client-id', util.uuid());
@@ -75,10 +73,12 @@ $(function() {
   Vue.component('unit-value',      require('./unit-value'));
   Vue.component('files',           require('./files'));
   Vue.component('file-dialog',     require('./file-dialog'));
+  Vue.component('upload-dialog',   require('./upload-dialog'));
   Vue.component('nav-menu',        require('./nav-menu'));
   Vue.component('nav-item',        require('./nav-item'));
   Vue.component('video',           require('./video'));
   Vue.component('color-picker',    require('./color-picker'));
+  Vue.component('dragbar',         require('./dragbar'));
 
   require('./filters')();
 
index 33d7a421a10b9a3edb35e918ccb9de4a5be3d39e..e98ab50f7d58249036629e8f0d353a2898c60cf5 100644 (file)
@@ -41,11 +41,37 @@ module.exports = {
     click_away_close: {
       type: Boolean,
       default: true
+    },
+
+    width: {
+      type: String,
+      default: ''
     }
   },
 
 
   events: {
     'click-away': function () {if (this.click_away_close) this.show = false}
+  },
+
+
+  watch: {
+    show: function (show) {if (show) Vue.nextTick(this.focus)}
+  },
+
+
+  methods: {
+    click_away: function (e) {
+      if (!e.target.classList.contains('modal-wrapper')) return;
+      this.$emit('click-away')
+    },
+
+
+    focus: function () {
+      $(this.$el).find('[focus]').each(function (index, e) {
+        e.focus()
+        return false;
+      })
+    }
   }
 }
index 2c113e868804da3365708fb96987687f09bee69b..9469148bd61ce5d440bdbd4b0abf9e0dbe06b0f5 100644 (file)
@@ -161,8 +161,15 @@ module.exports = {
       var d2 = api.download('speeds/' + path, 'arraybuffer');
 
       $.when(d1, d2).done(function (positions, speeds) {
-        this.positions = new Float32Array(positions[0]);
+        console.log('Loaded ' + path);
+
+        this.positions = new Float32Array(positions[0])
         this.speeds = new Float32Array(speeds[0]);
+
+      }.bind(this)).fail(function () {
+        console.log('Loading ' + path + ' failed');
+
+      }.bind(this)).always(function () {
         this.loading = false;
         this.redraw();
         this.snap(this.snapView);
@@ -247,7 +254,10 @@ module.exports = {
     graphics: function () {
       try {
         // Renderer
-        this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
+        this.renderer = new THREE.WebGLRenderer({
+          antialias: true,
+          alpha: true
+        });
         this.renderer.setPixelRatio(window.devicePixelRatio);
         this.renderer.setClearColor(0, 0);
         this.target.appendChild(this.renderer.domElement);
@@ -354,9 +364,9 @@ module.exports = {
 
       var geometry = new THREE.BufferGeometry();
 
-      geometry.addAttribute
+      geometry.setAttribute
       ('position', new THREE.Float32BufferAttribute(this.vertices, 3));
-      geometry.addAttribute
+      geometry.setAttribute
       ('normal', new THREE.Float32BufferAttribute(this.normals, 3));
 
       geometry.computeBoundingSphere();
@@ -367,6 +377,8 @@ module.exports = {
 
 
     draw_tool: function (scene, bbox) {
+      if (bbox.isEmpty()) return new THREE.Group();
+
       // Tool size is relative to bounds
       var size = bbox.getSize(new THREE.Vector3());
       var length = (size.x + size.y + size.z) / 24;
@@ -462,6 +474,7 @@ module.exports = {
       grid.material = material;
       grid.rotation.x = Math.PI / 2;
 
+      grid.visible = this.show.grid;
       scene.add(grid);
 
       return grid;
@@ -522,6 +535,12 @@ module.exports = {
         transparent: true
       });
 
+      // Prevent any zero dimensions
+      bounds = bounds.clone();
+      if (bounds.min.x == bounds.max.x) bounds.max.x += 0.0000001;
+      if (bounds.min.y == bounds.max.y) bounds.max.y += 0.0000001;
+      if (bounds.min.z == bounds.max.z) bounds.max.z += 0.0000001;
+
       var box = new THREE.Box3Helper(bounds);
       box.material = material;
       group.add(box);
@@ -535,12 +554,14 @@ module.exports = {
       group.visible = this.show.dims;
       scene.add(group);
 
-      // Bounds
-      group.add(this.draw_box_dims(bbox, 0x0c2d53));
+      if (!bbox.isEmpty()) {
+        // Bounds
+        group.add(this.draw_box_dims(bbox, 0x0c2d53));
 
-      // Envelope
-      if (this.envelope)
-        group.add(this.draw_box_dims(this.envelope, 0x00f7ff));
+        // Envelope
+        if (this.envelope)
+          group.add(this.draw_box_dims(this.envelope, 0x00f7ff));
+      }
 
       return group;
     },
@@ -556,6 +577,8 @@ module.exports = {
 
 
     draw_path: function (scene) {
+      if (!this.positions.length) return new THREE.Group();
+
       var geometry = new THREE.BufferGeometry();
       var material =
           new THREE.LineBasicMaterial({
@@ -564,7 +587,7 @@ module.exports = {
           });
 
       var positions = new THREE.Float32BufferAttribute(this.positions, 3);
-      geometry.addAttribute('position', positions);
+      geometry.setAttribute('position', positions);
 
       var colors = [];
       for (var i = 0; i < this.speeds.length; i++) {
@@ -573,7 +596,7 @@ module.exports = {
       }
 
       colors = new THREE.Uint8BufferAttribute(colors, 3, true);
-      geometry.addAttribute('color', colors);
+      geometry.setAttribute('color', colors);
 
       geometry.computeBoundingSphere();
       geometry.computeBoundingBox();
@@ -600,12 +623,13 @@ module.exports = {
 
       // Compute bounding box
       var bbox = this.get_model_bounds();
+      var realBBox = this.get_model_bounds(true);
 
       // Tool, axes & bounds
-      this.toolView     = this.draw_tool(scene, bbox);
-      this.axesView     = this.draw_axes(scene, bbox);
-      this.gridView     = this.draw_grid(scene, bbox);
-      this.dimsView     = this.draw_dims(scene, bbox);
+      this.toolView = this.draw_tool(scene, realBBox);
+      this.axesView = this.draw_axes(scene, bbox);
+      this.gridView = this.draw_grid(scene, bbox);
+      this.dimsView = this.draw_dims(scene, realBBox);
     },
 
 
@@ -623,7 +647,7 @@ module.exports = {
     },
 
 
-    get_model_bounds: function () {
+    get_model_bounds: function (real) {
       var bbox = undefined;
 
       function add(o) {
@@ -639,6 +663,10 @@ module.exports = {
       add(this.surfaceMesh);
       add(this.workpieceMesh);
 
+      if (bbox.isEmpty() && !real)
+        bbox = new THREE.Box3(new THREE.Vector3(-10, -10, -10),
+                              new THREE.Vector3(10, 10, 10));
+
       return bbox;
     },
 
index d838e8e10036216ac6a86b3814aeb1175da3549c..522421f7a3999ced16a313baa79c04b90be21df9 100644 (file)
@@ -62,17 +62,14 @@ module.exports = {
 
 
     restore_config: function () {
-      // If we don't reset the form the browser may cache file if name is same
-      // even if contents have changed
-      $('.restore-config')[0].reset();
-      $('.restore-config input').click();
+      this.$root.upload({
+        accept: '.json',
+        on_confirm: this.restore
+      })
     },
 
 
-    restore: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-
+    restore: function (file, done) {
       var fr = new FileReader();
       fr.onload = function (e) {
         var config;
@@ -81,7 +78,7 @@ module.exports = {
           config = JSON.parse(e.target.result);
         } catch (ex) {
           this.$root.error_dialog("Invalid config file");
-          return;
+          return done(false);
         }
 
         api.put('config/save', config).done(function (data) {
@@ -90,10 +87,11 @@ module.exports = {
 
         }.bind(this)).fail(function (error) {
           this.$root.api_error('Restore failed', error);
-        }.bind(this))
+
+        }.bind(this)).always(function () {done(false)})
       }.bind(this);
 
-      fr.readAsText(files[0]);
+      fr.readAsText(file);
     },
 
 
@@ -138,47 +136,38 @@ module.exports = {
     },
 
 
-    upload_firmware: function () {
-      // If we don't reset the form the browser may cache file if name is same
-      // even if contents have changed
-      $('.upload-firmware')[0].reset();
-      $('.upload-firmware input').click();
-    },
-
-
-    upload: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-
-      this.firmware = files[0];
-      this.firmwareName = files[0].name;
+    upload: function () {
       this.password = '';
-      this.show.upload = true;
-    },
 
+      this.$root.upload({
+        url: 'firmware/update',
+        accept: '.bz2',
+        form: function(file) {
+          return {
+            firmware: file,
+            password: this.password
+          }
+        }.bind(this),
+
+        on_confirm: function (file, ok) {
+          this.confirm_cb = ok;
+          this.show.upload = true;
+          this.firmwareName = file.name;
+        }.bind(this),
+
+        on_success: function (file, next) {
+          this.show.upgrading = true;
+          next();
+        }.bind(this),
+
+        on_failure: function (file, error) {
+          this.error_dialog('Invalid password or bad firmware');
+        }.bind(this)
+      })
+    },
 
-    upload_confirmed: function () {
-      this.show.upload = false;
-
-      var form = new FormData();
-      form.append('firmware', this.firmware);
-      if (this.password) form.append('password', this.password);
-
-      $.ajax({
-        url: '/api/firmware/update',
-        type: 'PUT',
-        data: form,
-        cache: false,
-        contentType: false,
-        processData: false
-
-      }).success(function () {
-        this.show.upgrading = true;
 
-      }.bind(this)).error(function () {
-        this.error_dialog('Invalid password or bad firmware');
-      }.bind(this))
-    },
+    upload_confirmed: function () {this.confirm_cb(true)},
 
 
     change_auto_check_upgrade: function () {
diff --git a/src/js/upload-dialog.js b/src/js/upload-dialog.js
new file mode 100644 (file)
index 0000000..c3d7c4c
--- /dev/null
@@ -0,0 +1,136 @@
+/******************************************************************************\
+
+                  This file is part of the Buildbotics firmware.
+
+         Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.
+
+          This Source describes Open Hardware and is licensed under the
+                                  CERN-OHL-S v2.
+
+          You may redistribute and modify this Source and make products
+     using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl).
+            This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED
+     WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS
+      FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable
+                                   conditions.
+
+                 Source location: https://github.com/buildbotics
+
+       As per CERN-OHL-S v2 section 4, should You produce hardware based on
+     these sources, You must maintain the Source Location clearly visible on
+     the external case of the CNC Controller or other product you make using
+                                   this Source.
+
+                 For more information, email info@buildbotics.com
+
+\******************************************************************************/
+
+'use strict'
+
+var api  = require('./api');
+var util = require('./util');
+
+
+function get_value(value, file, defaultValue) {
+  if (typeof value == 'function') value = value(file);
+  if (value == undefined) value = defaultValue;
+  return value;
+}
+
+
+module.exports = {
+  template: '#upload-dialog-template',
+
+
+  data: function () {
+    return {
+      config: {},
+      show: false,
+      progress: 0,
+      msg: ''
+    }
+  },
+
+
+  methods: {
+    upload: function (config)  {
+      this.config = config;
+
+      // Reset <form> so the browser does not cache the file when the name is
+      // same but the contents have changed.
+      this.$els.form.reset();
+
+      var input = this.$els.input;
+      if (config.multiple) input.setAttribute('multiple', '');
+      else input.removeAttribute('multiple');
+      input.setAttribute('accept', get_value(config.accept, undefined, ''));
+      input.click();
+    },
+
+
+    cancel: function () {
+      this.canceled = true;
+      if (this.xhr) this.xhr.abort();
+    },
+
+
+    _upload_file: function (file, next) {
+      var _progress = function (fraction, done, xhr) {
+        if (typeof this.config.on_progress == 'function')
+          this.config.on_progress(fraction, done, xhr);
+
+        this.xhr = xhr;
+        this.progress = (fraction * 100).toFixed(1);
+      }.bind(this);
+
+      this.progress = 0;
+      this.show = true;
+
+      this.msg = get_value(this.config.msg,  file, file.name);
+      var url  = get_value(this.config.url,  file);
+      var form = get_value(this.config.form, file, {file: file});
+      var data = get_value(this.config.data, file, {});
+
+      var fd = new FormData();
+      for (const key in form)
+        fd.append(key, get_value(form[key], file));
+
+      api.upload(url, fd, data, _progress)
+        .done(function () {
+          if (typeof this.config.on_success == 'function')
+            this.config.on_success(file, next);
+          else next();
+
+        }.bind(this)).fail(function (error) {
+          this.show = false;
+          if (this.canceled) return;
+          if (typeof this.config.on_failure == 'function')
+            this.config.on_failure(file, error);
+          else this.$root.api_error(this.msg + ' failed', error)
+        }.bind(this));
+    },
+
+
+    _upload_files: function (files, i) {
+      if (files.length <= i || this.canceled) return this.show = false;
+      var file = files[i];
+
+      var _next   = function () {this._upload_files(files, i + 1)}.bind(this);
+      var _upload = function () {this._upload_file(file,   _next)}.bind(this);
+
+      if (typeof this.config.on_confirm == 'function')
+        this.config.on_confirm(file, function (ok) {
+          if (ok) _upload();
+          else _next();
+        }.bind(this));
+
+      else _upload();
+    },
+
+
+    _upload: function (e)  {
+      this.canceled = false;
+      this._upload_files(e.target.files || e.dataTransfer.files, 0);
+    }
+  }
+}
index 57463d563317d8ff407b276f79f39c5d66acd78d..517c6d944657a66531056e91899ca1a241f4d695 100644 (file)
@@ -108,6 +108,27 @@ var util = {
       s += util.uuid_chars[Math.floor(Math.random() * util.uuid_chars.length)];
 
     return s
+  },
+
+
+  get_highlight_mode: function (path) {
+    if (path.endsWith('.tpl') || path.endsWith('.js') ||
+        path.endsWith('.json'))
+      return 'javascript';
+
+    return 'gcode'
+  },
+
+
+  webgl_supported: function (performance) {
+    try {
+      var opts = {failIfMajorPerformanceCaveat: performance || false};
+      var canvas = document.createElement('canvas');
+      return !!(window.WebGLRenderingContext &&
+                (canvas.getContext('webgl', opts) ||
+                 canvas.getContext('experimental-webgl', opts)));
+
+    } catch (e) {return false}
   }
 }
 
index ba8cbd947a5eb7efa191dc9ff690ec6a303375f2..060f8e14090d2aae7fa0a3652650aa753704bdd7 100644 (file)
 module.exports = {
   template: '#video-template',
 
+  attached: function () {Vue.nextTick(this.resize)},
+
 
   ready: function () {
-    Vue.nextTick(this.resize);
     window.addEventListener('resize', this.resize, false);
   },
 
@@ -45,14 +46,14 @@ module.exports = {
     resize: function () {
       var width = this.$els.video.clientWidth;
       var height = this.$els.video.clientHeight;
-      var aspect = 480 / 640; // TODO should probably not be hard coded
+      var aspect = 3 / 4; // TODO should probably not be hard coded
 
       if (!width) return;
 
       width = Math.min(width, height / aspect);
       height = Math.min(height, width * aspect);
 
-      this.$els.img.style.width = width + 'px';
+      this.$els.img.style.width  = width  + 'px';
       this.$els.img.style.height = height + 'px';
     }
   }
index 66a51c8024c04ff916faa74462f1370e8128c959..95dd54c81969604a01f90608b46ffd64a1b5186f 100644 (file)
@@ -89,7 +89,7 @@ module.exports = {
 
 
     'state.line': function () {
-      if (this.mach_state != 'HOMING') this.highlight_gcode();
+      if (this.mach_state != 'HOMING') this.highlight_code();
     },
 
 
@@ -222,13 +222,27 @@ module.exports = {
   },
 
 
+  attached: function () {
+    if (this.editor != undefined) this.editor.refresh()
+  },
+
+
   methods: {
+    goto: function (hash) {window.location.hash = hash},
     send: function (msg) {this.$dispatch('send', msg)},
     on_scroll: function (cm, e) {e.preventDefault()},
-    run_macro: function (macro) {api.put('macro/' + macro)},
 
 
-    highlight_gcode: function () {
+    run_macro: function (macro) {
+      api.put('macro/' + macro)
+        .fail(function (error) {
+          this.$root.error_dialog('Failed to run macro "' + macro + '":\n' +
+                                  error.message);
+        }.bind(this))
+    },
+
+
+    highlight_code: function () {
       if (typeof this.editor == 'undefined') return;
       var line = this.state.line - 1;
       var doc = this.editor.getDoc();
@@ -244,11 +258,15 @@ module.exports = {
 
 
     load: function (path) {
+      if (path == undefined) return;
+
       api.download('fs/' + path)
         .done(function (data) {
           if (this.state.queued != path) return;
+
+          this.editor.setOption('mode', util.get_highlight_mode(path))
           this.editor.setValue(data);
-          this.highlight_gcode();
+          this.highlight_code();
         }.bind(this))
     },
 
diff --git a/src/js/view-docs.js b/src/js/view-docs.js
new file mode 100644 (file)
index 0000000..1d2be91
--- /dev/null
@@ -0,0 +1,66 @@
+/******************************************************************************\
+
+                  This file is part of the Buildbotics firmware.
+
+         Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.
+
+          This Source describes Open Hardware and is licensed under the
+                                  CERN-OHL-S v2.
+
+          You may redistribute and modify this Source and make products
+     using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl).
+            This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED
+     WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS
+      FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable
+                                   conditions.
+
+                 Source location: https://github.com/buildbotics
+
+       As per CERN-OHL-S v2 section 4, should You produce hardware based on
+     these sources, You must maintain the Source Location clearly visible on
+     the external case of the CNC Controller or other product you make using
+                                   this Source.
+
+                 For more information, email info@buildbotics.com
+
+\******************************************************************************/
+
+'use strict'
+
+
+module.exports = {
+  template: '#view-docs-template',
+  props: ['config', 'template', 'state'],
+
+
+  data: function () {
+    return {
+      view: undefined
+    }
+  },
+
+
+  components: {
+    'docs-help':    {template: '#docs-help-template'},
+    'docs-gcode': {
+      template: '#docs-gcode-template',
+      data: function () {return {showUnimplemented: false}}
+    },
+    'docs-license':   {template: '#docs-license-template'}
+  },
+
+
+  events: {
+    route: function (path) {
+      if (path[0] != 'docs') return;
+      var view = path.length < 2 ? '' : path[1];
+
+      if (typeof this.$options.components['docs-' + view] == 'undefined')
+        this.$root.replace_route('docs:help');
+      else this.view = view;
+    },
+  },
+
+
+  ready: function () {this.$root.parse_hash()}
+}
index 9ff4de4a099545dce3b3aa5147500fda1ae5f111..c82330edb4ce9ceca4721780b2d38244c7f68c63 100644 (file)
@@ -41,7 +41,7 @@ module.exports = {
     return {
       loading: false,
       path: undefined,
-      dirty: false,
+      modified: false,
       canRedo: false,
       canUndo: false,
       clipboard: ''
@@ -51,7 +51,7 @@ module.exports = {
 
   computed: {
     filename: function () {
-      return (this.dirty ? '* ' : '') +
+      return (this.modified ? '* ' : '') +
         (util.display_path(this.path) || '(unnamed)')
     },
 
@@ -69,6 +69,15 @@ module.exports = {
   },
 
 
+  events: {
+    'route-changing': function (path, cancel) {
+      if (!this.modified || path[0] == 'editor') return;
+      cancel();
+      this.check_save(function () {location.hash = path.join(':')});
+    }
+  },
+
+
   attached: function (done) {
     if (typeof this.doc == 'undefined') return;
     this.load(cookie.get('selected-path'));
@@ -91,7 +100,7 @@ module.exports = {
 
   methods: {
     change: function () {
-      this.dirty = !this.doc.isClean()
+      this.modified = !this.doc.isClean()
 
       var size = this.doc.historySize();
       this.canRedo = !!size.redo;
@@ -99,7 +108,7 @@ module.exports = {
     },
 
 
-    do_load: function (path) {
+    do_load: function (path, ok) {
       if (!path) this.set_path();
       else {
         this.loading = true;
@@ -109,6 +118,7 @@ module.exports = {
             this.set_path(path);
             this.set(data);
             this.loading = false;
+            if (ok) ok();
 
           }.bind(this)).fail(function (text, xhr, status) {
             this.loading = false;
@@ -129,6 +139,7 @@ module.exports = {
     set_path: function (path) {
       this.path = path;
       cookie.set('selected-path', path || '');
+      this.editor.setOption('mode', util.get_highlight_mode(path))
     },
 
 
@@ -136,22 +147,36 @@ module.exports = {
       this.doc.setValue(text);
       this.doc.clearHistory();
       this.doc.markClean();
-      this.dirty = false;
+      this.modified = false;
       this.canRedo = false;
       this.canUndo = false;
     },
 
 
     check_save: function (ok) {
-      if (!this.dirty) ok();
-      else this.$root.open_dialog({
+      if (!this.modified) return ok();
+
+      this.$root.open_dialog({
         title: 'Save file?',
         body: 'The current file has been modified.  ' +
           'Would you like to save it first?',
-        buttons: 'Cancel No Yes',
+        width: '320px',
+        buttons: [
+          {
+            text: 'Cancel',
+            title: 'Stay on the editor page.'
+          }, {
+            text: 'Discard',
+            title: "Discard changes."
+          }, {
+            text: 'Save',
+            title: 'Save changes.',
+            'class': 'button-success'
+          }
+        ],
         callback: {
-          yes: function () {this.save(ok)}.bind(this),
-          no: ok
+          save: function () {this.save(ok)}.bind(this),
+          discard: function () {this.revert(ok)}.bind(this)
         }
       })
     },
@@ -185,7 +210,7 @@ module.exports = {
       api.upload('fs/' + path, fd)
         .done(function () {
           this.set_path(path);
-          this.dirty = false;
+          this.modified = false;
           this.doc.markClean();
           if (typeof ok != 'undefined') ok()
 
@@ -212,12 +237,12 @@ module.exports = {
     },
 
 
-    revert: function () {
-      if (this.dirty) {
+    revert: function (ok) {
+      if (this.modified) {
         var path = this.path;
         this.path = undefined;
-        this.dirty = false;
-        this.load(path)
+        this.modified = false;
+        this.do_load(path, ok);
       }
     },
 
@@ -230,11 +255,7 @@ module.exports = {
     },
 
 
-    view: function () {
-      this.check_save(function () {this.$root.view(this.path)}.bind(this))
-    },
-
-
+    view: function () {this.$root.view(this.path)},
     undo: function () {this.doc.undo()},
     redo: function () {this.doc.redo()},
 
index 09f284ec1aadd2508da7719e896fecf333aed23a..bb27521177605ad341398b47ac392468f5ad8885 100644 (file)
@@ -46,7 +46,6 @@ module.exports = {
 
 
   components: {
-    'settings-not-found': {template: '<h2>Settings page not found</h2>'},
     'settings-general':   require('./settings-general'),
     'settings-motor':     require('./settings-motor'),
     'settings-tool':      require('./settings-tool'),
@@ -58,6 +57,55 @@ module.exports = {
 
 
   events: {
+    'route-changing': function (path, cancel) {
+      if (this.modified && path[0] != 'settings') {
+        cancel();
+
+        var self = this;
+
+        function done(success) {
+          if (success) location.hash = path.join(':')
+        }
+
+        this.$root.open_dialog({
+          title: 'Save settings?',
+          body: 'Changes to the settings have not been saved.  ' +
+            'Would you like to save them now?',
+          width: '320px',
+          buttons: [
+            {
+              text: 'Cancel',
+              title: 'Stay on the Settings page.'
+            }, {
+              text: 'Discard',
+              title: 'Discard settings changes.'
+            }, {
+              text: 'Save',
+              title: 'Save settings.',
+              'class': 'button-success'
+            }
+          ],
+          callback: {
+            save: function () {this.save(done)}.bind(this),
+            discard: function () {this.discard(done)}.bind(this)
+          }.bind(this)
+        });
+      }
+    },
+
+
+    route: function (path) {
+      if (path[0] != 'settings') return;
+      var view = path.length < 2 ? '' : path[1];
+
+      if (typeof this.$options.components['settings-' + view] == 'undefined')
+        return this.$root.replace_route('settings:general');
+
+      if (path.length == 3) this.index = path[2];
+      this.view = view;
+    },
+
+
     'config-changed': function () {
       this.modified = true;
       return false;
@@ -71,39 +119,26 @@ module.exports = {
   },
 
 
-  ready: function () {
-    $(window).on('hashchange', this.parse_hash);
-    this.parse_hash();
-  },
+  ready: function () {this.$root.parse_hash()},
 
 
   methods: {
-    parse_hash: function () {
-      var hash = location.hash.substr(1);
-
-      if (!hash.trim().length) {
-        location.hash = 'settings:general';
-        return;
-      }
-
-      var parts = hash.split(':');
-      var view = parts.length == 1 ? 'general' : parts[1];
-
-      if (parts.length == 3) this.index = parts[2];
-
-      if (typeof this.$options.components['settings-' + view] == 'undefined')
-        this.view = 'not-found';
-
-      else this.view = view;
-    },
-
-
-    save: function () {
+    save: function (done) {
       api.put('config/save', this.config).done(function (data) {
         this.modified = false;
+        done(true);
       }.bind(this)).fail(function (error) {
         this.api_error('Save failed', error);
+        done(false);
       }.bind(this));
+    },
+
+
+    discard: function (done) {
+      this.$root.update(function (success) {
+        this.modified = false;
+        done(success);
+      }.bind(this))
     }
   }
 }
index 5253b1f998f7c56931d3d0a4b938d35c19877294..505a37397c1ab7c4ac4573209e031e16dec1cb11 100644 (file)
@@ -117,7 +117,8 @@ module.exports = {
 
 
     load: function(path) {
-      if (!path || this.path == path) return;
+      //if (!path || this.path == path) return;
+      if (!path) return;
 
       cookie.set('selected-path', path)
       this.path = path;
index fee88e18706d2034726db34086e2965a7bb5bf90..7b0d2f9aebee2711690536a37d20ed158fa1935f 100644 (file)
@@ -34,7 +34,6 @@ html(lang="en")
     title Buildbotics Controller - Web interface
 
     style: include ../static/css/pure-min.css
-    style: include ../static/css/side-menu.css
 
     style: include ../static/css/font-awesome.min.css
     style: include ../static/css/Audiowide.css
@@ -46,7 +45,7 @@ html(lang="en")
     #overlay(v-if="status != 'connected'")
       span {{status}}
     #layout(:class="'view-' + currentView + '-page'")
-      a#menuLink.menu-link(href="#menu"): span
+      #menuLink.menu-link(href="#menu"): span
 
       #menu
         .pure-menu
@@ -54,7 +53,7 @@ html(lang="en")
             li.pure-menu-heading
               a.pure-menu-link(href="#control") Control
 
-            li.pure-menu-heading
+            li.pure-menu-heading(v-if="$root.webGLSupported")
               a.pure-menu-link(href="#viewer") 3D View
 
             li.pure-menu-heading
@@ -70,13 +69,7 @@ html(lang="en")
               a.pure-menu-link(href="#settings") Settings
 
             li.pure-menu-heading
-              a.pure-menu-link(href="#cheat-sheet") Cheat Sheet
-
-            li.pure-menu-heading
-              a.pure-menu-link(href="#help") Help
-
-            li.pure-menu-heading
-              a.pure-menu-link(href="#license") License
+              a.pure-menu-link(href="#docs") Docs
 
       #main
         .header
@@ -108,6 +101,7 @@ html(lang="en")
             :template="template", :state="state", keep-alive)
 
     file-dialog(v-ref:file-dialog, :locations="state.locations")
+    upload-dialog(v-ref:uploader)
     dialog(v-ref:dialog)
 
     message.error-message(:show.sync="showError")
@@ -128,7 +122,7 @@ html(lang="en")
       div(slot="footer")
         button.pure-button.pure-button-primary(@click="showError = false") Ok
 
-    message(v-if="popupMessages.length", :show="true")
+    message(v-if="popupMessages.length", :show.sync="showPopup")
       h3(slot="header") GCode message
 
       div(slot="body")
@@ -159,4 +153,5 @@ html(lang="en")
     script: include ../static/js/chart.bundle-2.9.3.min.js
     script: include:browserify ../js/main.js
     script: include ../static/js/codemirror.js
+    script: include ../static/js/codemirror-javascript.js
     script: include ../static/js/jscolor.min.js
index eeb568424bae9db7014bd14f716271cd5b33c5c9..1eaa931f0de59c961c941c0252196c052e508fb9 100644 (file)
@@ -27,7 +27,8 @@
 
 script#dialog-template(type="text/x-template")
   .dialog
-    message(:show="show", click_away_close="false", @click-away="click_away")
+    message(:show.sync="show", :click_away_close="false",
+      @click-away="click_away", :width="config.width || '300px'")
       h3(slot="header").
         #[.fa(v-if="config.icon", class="'fa-' + config.icon")]
         {{config.title || ''}}
@@ -35,7 +36,7 @@ script#dialog-template(type="text/x-template")
       div(slot="body") {{{config.body || ''}}}
 
       div(slot="footer")
-        button.pure-button(v-for="button in buttons",
-          @click="close(button.action)").
+        button.pure-button(v-for="button in buttons", :title="button.title",
+          @click="close(button.action)", :class="button.class").
           #[.fa(v-if="button.icon", :class="'fa-' + button.icon")]
           {{button.text}}
diff --git a/src/pug/templates/docs-gcode.pug b/src/pug/templates/docs-gcode.pug
new file mode 100644 (file)
index 0000000..713b325
--- /dev/null
@@ -0,0 +1,597 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#docs-gcode-template(type="text/x-template")
+  // Modified from http://linuxcnc.org/docs/html/gcode.html
+  - var base = 'http://linuxcnc.org/docs/html/gcode';
+  - var camotics_base = 'https://camotics.org/gcode.html';
+
+  .cheat-sheet
+    h1 GCode Cheat Sheet
+
+    table
+      tr
+        th Code
+        th Parameters
+        th Description
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Motion
+      tr
+        td
+          a(href=`${base}/g-code.html#gcode:g0`) G0
+        td
+        td Rapid Move
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g1`) G1
+        td
+        td Linear Move
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g2-g3`) G2, G3
+        td I J K or R, P
+        td Arc Move
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g4`) G4
+        td P
+        td Dwell
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g5`) G5
+        td I J P Q
+        td Cubic Spline
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g5.1`) G5.1
+        td I J
+        td Quadratic Spline
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g5.2-g5.3`) G5.2
+        td P L
+        td NURBS
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g33.1`) G33.1
+        td K
+        td Rigid Tapping
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Homing & Probing
+      tr
+        td
+          a(target="_blank", href=`${camotics_base}#gcodes-g28_2-28_3`)
+            | G28.2, G28.3
+        td
+        td (Un)set Axis Homed State
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g38`) G38.2 - G38.5
+        td
+        td Straight Probe
+      tr
+        td
+          a(target="_blank", href=`${camotics_base}#gcodes-g38_6-38_9`)
+            | G38.6 - G38.9
+        td
+        td Seek Switch
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Tool Control
+      tr
+        td
+          a(href=`${base}/other-code.html#sec:select-tool`) T
+        td
+        td Select Tool
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m6`) M6
+        td T
+        td Tool Change
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m61`) M61
+        td Q
+        td Set Current Tool
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l1`) G10 L1
+        td P Q R
+        td Set Tool Table
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l10`) G10 L10
+        td P
+        td Set Tool Table
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l11`) G10 L11
+        td P
+        td Set Tool Table
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g43`) G43
+        td  H
+        td Tool Length Offset
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g43.1`) G43.1
+        td
+        td Dynamic Tool Length Offset
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g43.2`) G43.2
+        td  H
+        td Apply additional Tool Length Offset
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g49`) G49
+        td
+        td Cancel Tool Length Compensation
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Feed Control
+      tr
+        td
+          a(href=`${base}/other-code.html#sec:set-feed-rate`) F
+        td
+        td Set Feed Rate
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g93-g94-g95`)
+            | G93, G94, G95
+        td
+        td Feed Rate Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m52`) M52
+        td P0 (off) or P1 (on)
+        td Adaptive Feed Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m53`) M53
+        td P0 (off) or P1 (on)
+        td Feed Stop Control
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Spindle Control
+      tr
+        td
+          a(href=`${base}/other-code.html#sec:set-spindle-speed`) S
+        td
+        td Set Spindle Speed
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m3-m4-m5`)
+            | M3, M4, M5
+        td S
+        td Spindle Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m19`) M19
+        td
+        td Orient Spindle
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g96-g97`) G96, G97
+        td  S D
+        td Spindle Control Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g33`) G33
+        td K
+        td Spindle Synchronized Motion
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Coolant
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m7-m8-m9`)
+            | M7, M8, M9
+        td
+        td Coolant Control
+      tr
+        td
+          a(target="_blank", href=`${camotics_base}#mcodes-m7_1-m8_1`)
+            | M7.1, M8.1
+        td
+        td Disable Coolant
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Stopping
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m0-m1`) M0, M1
+        td
+        td Program Pause
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m2-m30`) M2, M30
+        td
+        td Program End
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m60`) M60
+        td
+        td Pallet Change Pause
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Units
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g20-g21`) G20, G21
+        td
+        td Units (inch, mm)
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Distance Mode
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g90-g91`) G90, G91
+        td
+        td Distance Mode
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g90.1-g91.1`)
+            | G90.1, G91.1
+        td
+        td Arc Distance Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g7`) G7
+        td
+        td Lathe Diameter Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g8`) G8
+        td
+        td Lathe Radius Mode
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Cutter Radius Compensation
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g40`) G40
+        td
+        td Compensation Off
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g41-g42`) G41,G42
+        td D
+        td Cutter Compensation
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g41.1-g42.1`)
+            | G41.1, G42.1
+        td D L
+        td Dynamic Cutter Compensation
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Path Control Mode
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g61-g61.1`)
+            | G61 G61.1
+        td
+        td Exact Path Mode
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g64`) G64
+        td P Q
+        td Path Blending (Partial support)
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Overrides
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m48-m49`) M48, M49
+        td
+        td Speed and Feed Override Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m50`) M50
+        td P0 (off) or P1 (on)
+        td Feed Override Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m51`) M51
+        td P0 (off) or P1 (on)
+        td Spindle Speed Override Control
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Coordinate Systems, Offsets & Planes
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g54-g59.3`)
+            | G54-G59.3
+        td
+        td Select Coordinate System
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l2`) G10 L2
+        td P R
+        td Set Coordinate System
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l20`) G10 L20
+        td P
+        td Set Coordinate System
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g52`) G52
+        td
+        td Local Coordinate System Offset
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g53`) G53
+        td
+        td Move in Machine Coordinates
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g92`) G92
+        td
+        td Coordinate System Offset
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g92.1-g92.2`)
+            | G92.1, G92.2
+        td
+        td Reset G92 Offsets
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g92.3`) G92.3
+        td
+        td Restore G92 Offsets
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g28-g28.1`)
+            | G28, G28.1
+        td
+        td Go/Set Predefined Position
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g30-g30.1`)
+            | G30, G30.1
+        td
+        td Go/Set Predefined Position
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g17-g19.1`)
+            | G17 - G19.1
+        td
+        td Plane Select
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Flow-control Codes
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:subroutines`)
+            | o sub/endsub/call
+        td
+        td Subroutines, sub/endsub call
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:looping`) o while
+        td
+        td Looping, while/endwhile do/while
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:conditional`) o if
+        td
+        td Conditional, if/else/endif
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:repeat`) o repeat
+        td
+        td Repeat a loop of code
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:indirection`) []
+        td
+        td Indirection
+      tr
+        td
+          a(target="_blank", href=`${base}/o-code.html#ocode:calling-files`)
+            | o call
+        td
+        td Call named or numbered file
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Modal State
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m70`) M70
+        td
+        td Save modal state
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m71`) M71
+        td
+        td Invalidate stored state
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m72`) M72
+        td
+        td Restore modal state
+      tr
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m73`) M73
+        td
+        td Save/restore modal state
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Input/Output
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m62-m65`) M62 - M65
+        td P
+        td Digital Output Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m66`) M66
+        td P E L Q
+        td Wait on Input
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m67`) M67
+        td T
+        td Analog Output,Synchronized
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m68`) M68
+        td T
+        td Analog Output, Immediate
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') User Defined Commands
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/m-code.html#mcode:m100-m199`)
+            | M101 - M199
+        td P Q
+        td User Defined Commands
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Canned cycles
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g80`) G80
+        td
+        td Cancel Canned Cycle
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g81`) G81
+        td R L (P)
+        td Drilling Cycle
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g82`) G82
+        td R L (P)
+        td Drilling Cycle, Dwell
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g83`) G83
+        td R L Q
+        td Drilling Cycle, Peck
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g73`) G73
+        td R L Q
+        td Drilling Cycle, Chip Breaking
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g85`) G85
+        td R L (P)
+        td Boring Cycle, Feed Out
+      tr
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g89`) G89
+        td R L (P)
+        td Boring Cycle, Dwell, Feed Out
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g76`) G76
+        td P Z I J R K Q H L E
+        td Threading Cycle
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/g-code.html#gcode:g98-g99`) G98, G99
+        td
+        td Canned Cycle Return Level
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Comments & Messages
+      tr
+        td
+          a(target="_blank", href=`${base}/overview.html#gcode:comments`) ; (…)
+        td
+        td Comments
+      tr
+        td
+          a(target="_blank", href=`${base}/overview.html#gcode:messages`)
+            | (MSG,…)
+        td
+        td Messages
+      tr
+        td
+          a(target="_blank", href=`${base}/overview.html#gcode:debug`) (DEBUG,…)
+        td
+        td Debug Messages
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href=`${base}/overview.html#gcode:print`) (PRINT,…)
+        td
+        td Print Messages
+      tr
+        td
+          a(target="_blank", href=`${base}/overview.html#_logging`) (LOG,…)
+        td
+        td Logging Messages
+
+    div
+      input(type="checkbox", v-model="showUnimplemented")
+      label Show unsupported codes
+
+    h2 Further GCode Programming Documentation
+
+    p
+      | The Buildbotics controller implements a subset of LinuxCNC GCode.
+      | Supported commands are listed above.  You can find further help with
+      | #[a(href="http://wikipedia.com/wiki/G-code", target="_blank") GCode]
+      | programming on the LinuxCNC website:
+
+    ul
+      li: a(href="http://linuxcnc.org/docs/html/gcode/overview.html",
+        target="_blank")
+        | G Code overview
+      li: a(href="http://linuxcnc.org/docs/html/gcode/g-code.html",
+        target="_blank")
+        | G Code reference
+      li: a(href="http://linuxcnc.org/docs/html/gcode/m-code.html",
+        target="_blank")
+        | M Code reference
diff --git a/src/pug/templates/docs-help.pug b/src/pug/templates/docs-help.pug
new file mode 100644 (file)
index 0000000..8ae6b33
--- /dev/null
@@ -0,0 +1,70 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#docs-help-template(type="text/x-template")
+  #help
+    h1 Help
+    h2 User Manual
+    p
+      | You can find a detailed user manual at
+      |
+      a(href="http://buildbotics.com/docs", target="_blank")
+        | buildbotics.com/docs
+      |.
+
+    h2 Discussion Forum
+    p
+      | If you're having trouble or just want to chat with other Buildbotics
+      | CNC controller owners, head over to the Buildbotics forum at
+      |
+      a(href="http://forum.buildbotics.com", target="_blank")
+        | forum.buildbotics.com
+      |.  Register on the site and post a message. We'll be happy to help.
+
+    h2 CAD/CAM Software
+    p
+      a(href="http://wikipedia.com/wiki/Computer-aided_manufacturing",
+        target="_blank") CAM
+      |
+      | software can be used to create GCode
+      | automatically from
+      |
+      a(href="http://wikipedia.com/wiki/Computer-aided_design",
+        target="_blank") CAD
+      |
+      | models.  Here are a few CAD/CAM resources:
+    ul
+      li: a(href="http://camotics.org/", target="_blank")
+        | CAMotics - Open-Source CNC Simulator
+      li: a(href="http://librecad.org/", target="_blank")
+        | LibreCAD - Open-Source 2D CAD
+      li: a(href="https://www.freecadweb.org/", target="_blank")
+        | FreeCAD - Open-Source 3D CAD
+      li: a(href="http://www.openscad.org/", target="_blank")
+        | OpenSCAD - Open-Source 3D CAD for programmers
+      li: a(href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Cam",
+        target="_blank") LinuxCNC CAM resources
diff --git a/src/pug/templates/docs-license.pug b/src/pug/templates/docs-license.pug
new file mode 100644 (file)
index 0000000..561129b
--- /dev/null
@@ -0,0 +1,57 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#docs-license-template(type="text/x-template")
+  #license
+    h1 License
+
+    p.
+      This is Open Hardware licensed under the CERN-OHL-S v2.  Unless
+      otherwise noted, all sources are copyright 2015 - 2021,
+      Buildbotics LLC.
+
+    p.
+      You may redistribute and modify the Source and make products
+      using it under the terms of the CERN-OHL-S v2 (
+      #[a(href="https:/cern.ch/cern-ohl") https:/cern.ch/cern-ohl]).
+      The Source is distributed WITHOUT ANY EXPRESS OR IMPLIED
+      WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS
+      FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable
+
+    p.
+      The Source can be found at
+      #[a(href="//github.com/buildbotics") github.com/buildbotics].
+
+    p.
+      As per CERN-OHL-S v2 section 4, should You produce hardware based on
+      the Source, You must maintain the Source Location clearly visible on
+      the external case of the CNC Controller or other product you make using
+      the Source.
+
+    p.
+      For information regarding this software, email
+      #[a(href="mailto:info@buildbotics.com") info@buildbotics.com].
diff --git a/src/pug/templates/dragbar.pug b/src/pug/templates/dragbar.pug
new file mode 100644 (file)
index 0000000..b6c9ae0
--- /dev/null
@@ -0,0 +1,29 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#dragbar-template(type="text/x-template")
+  .dragbar(:class="'dragbar-' + direction", @mousedown="resize")
index 48b3fbb3630c6762c843e02a181308615534b1c7..e75fba989d786a84ddf06dc5ddf97451a59f9c8f 100644 (file)
@@ -27,7 +27,7 @@
 
 script#file-dialog-template(type="text/x-template")
   .file-dialog
-    message(:show="show", click_away_close="false")
+    message(:show.sync="show", :click_away_close="false")
       h3(slot="header") {{config.save ? 'Save' : 'Open'}} file
 
       div(slot="body")
index d25f506a055567279c36c67cdce9177a4691842b..6870ce7f076500e6f8d13b1c7fca75f8e6088724 100644 (file)
@@ -29,17 +29,10 @@ script#files-template(type="text/x-template")
   .files(v-if="fs.files")
     .files-name(v-if="mode == 'save'")
       label Name:
-      input(v-model="filename", @input="filename_changed")
+      input(v-model="filename", @input="filename_changed", focus)
 
     .files-body
       .files-locations
-        .files-location.files-upload(v-if="mode != 'save'",
-          title="Upload a file from this computer to the controller.",
-            @click="upload") #[.fa.fa-upload] Upload
-
-          form.file-upload(v-el:upload-form)
-            input(type="file", v-el:upload-form-input @change="do_upload")
-
         .files-location(v-for="name in locations", @click="open(name)",
           :class="{active: name == location}", :title="location_title(name)")
           .fa.fa-home(v-if="name == 'Home'")
@@ -55,22 +48,16 @@ script#files-template(type="text/x-template")
               :title="path_title($index)", @click="load_path($index)")
               | {{path}}
 
+          .files-upload(v-if="mode != 'save'")
+            button.pure-button.pure-button-primary(
+              title="Upload a file from this computer to the controller.",
+              @click="upload") #[.fa.fa-upload] Upload
+
           .new-folder
             button.pure-button(title="Create a new folder.",
               @click="new_folder", :disabled="10 < paths.length").
               #[.fa.fa-plus] New Folder
 
-            message(:show.sync="showNewFolder")
-              h3(slot="header") Folder Name
-              p(slot="body")
-                input(v-model="folder", @keyup.enter="create_folder")
-              div(slot="footer")
-                button.pure-button(@click="showNewFolder = false")
-                  | #[.fa.fa-times] Cancel
-                button.pure-button.pure-button-primary(
-                  :disabled="!folder_valid", @click="create_folder")
-                  | #[.fa.fa-plus] Create
-
         .files-list
           table
             thead
@@ -88,3 +75,14 @@ script#files-template(type="text/x-template")
                   {{file.name}}
                 td.size: span(v-if="!file.dir") {{file.size | size}}
                 td.modified {{file.modified | ago}}
+
+    message.new-folder(v-ref:new-folder, :show.sync="showNewFolder")
+      h3(slot="header") Folder Name
+      p(slot="body")
+        input(v-model="folder", @keyup.enter="create_folder", focus)
+      div(slot="footer")
+        button.pure-button(@click="showNewFolder = false")
+          | #[.fa.fa-times] Cancel
+        button.pure-button.pure-button-primary(
+          :disabled="!folder_valid", @click="create_folder")
+          | #[.fa.fa-plus] Create
index 69102573a45da948927dac95b50f10fb0025f99a..f23b9445c0be5e127f6d85f3726bfa00f008317e 100644 (file)
@@ -35,19 +35,19 @@ script#indicators-template(type="text/x-template")
         td
          .fa.fa-plus-circle.io
         th Hi/+3.3v
-        th.separator
+      tr
         td
          .fa.fa-minus-circle.io
         th Lo/Gnd
-        th.separator
+      tr
         td
          .fa.fa-circle.io.active
         th Active
-        th.separator
+      tr
         td
          .fa.fa-circle.io.inactive
         th Inactive
-        th.separator
+      tr
         td
          .fa.fa-circle-o.io
         th Tristated/Disabled
@@ -257,8 +257,10 @@ script#indicators-template(type="text/x-template")
           | {{state.rpi_temp | fixed 0}} ℃
         th RPi Temp
 
-    h2 DB25 breakout box
-    img(src="images/DB25_breakout_box.png")
+    div
+      h2 DB25 breakout box
+      img(src="images/DB25_breakout_box.png")
 
-    h2 DB25-M2 breakout
-    img(src="images/DB25-M2_breakout.png")
+    div
+      h2 DB25-M2 breakout
+      img(src="images/DB25-M2_breakout.png")
index 18ad84b3aaf33f045fa45e2fa6a8769eb1bc25c2..fe32c3413e75478a2359676b8b48805b0716b1ca 100644 (file)
@@ -27,8 +27,8 @@
 
 script#message-template(type="text/x-template")
   .modal-mask(v-show="show", transition="modal")
-    .modal-wrapper(@click="$emit('click-away')", v-el:wrapper)
-      .modal-container(@click.stop="")
+    .modal-wrapper(@click="click_away", v-el:wrapper)
+      .modal-container(:style="{width: width}")
         .modal-header
           slot(name="header") default header
 
index 56f503781798c3da21b2512f5371a8b5c67fee7f..d7d8da77588a681748bfad4cd6bf4ead321fe033 100644 (file)
@@ -29,17 +29,19 @@ script#path-viewer-template(type="text/x-template")
   .path-viewer(v-show="enabled")
     .path-viewer-content
 
-    table.path-viewer-messages(
+    .path-viewer-messages(
       v-if="typeof toolpath.messages != 'undefined' && " +
         "toolpath.messages.length")
-      tr
-        th Level
-        th Location
-        th Message
+      dragbar(title="Resize message panel.")
+      table
+        tr
+          th Level
+          th Location
+          th Message
 
-      tr(v-for="msg in toolpath.messages", :class="'log-' + msg.level")
-        td.level {{msg.level}}
-        td.location
-          | {{msg.line}}
-          span(v-if="msg.column") :{{msg.column}}
-        td.message {{msg.msg}}
+        tr(v-for="msg in toolpath.messages", :class="'log-' + msg.level")
+          td.level {{msg.level}}
+          td.location
+            | {{msg.line}}
+            span(v-if="msg.column") :{{msg.column}}
+          td.message {{msg.msg}}
index 7e54174a085976f190e31dab4cf6719c675e643a..51e07eb42436b8c688e91f38d4748203f2827115 100644 (file)
@@ -31,19 +31,13 @@ script#settings-admin-template(type="text/x-template")
 
     h2 Configuration
     button.pure-button.pure-button-primary(@click="backup") Backup
-
-    label.pure-button.pure-button-primary(@click="restore_config") Restore
-    form.restore-config.file-upload
-      input(type="file", accept=".json", @change="restore")
-
+    button.pure-button.pure-button-primary(@click="restore_config") Restore
     button.pure-button.pure-button-primary(@click="reset") Reset
 
     h2 Firmware
     button.pure-button.pure-button-primary(@click="check") Check
     button.pure-button.pure-button-primary(@click="upgrade") Upgrade
-    label.pure-button.pure-button-primary(@click="upload_firmware") Upload
-    form.upload-firmware.file-upload
-      input(type="file", accept=".bz2", @change="upload")
+    button.pure-button.pure-button-primary(@click="upload") Upload
 
     p
       input(type="checkbox", v-model="autoCheckUpgrade",
@@ -67,7 +61,7 @@ script#settings-admin-template(type="text/x-template")
         p.pure-control-group
           label(for="pass") Password
           input(name="pass", v-model="password", type="password",
-            @keyup.enter="upgrade_confirmed")
+            @keyup.enter="upgrade_confirmed", focus)
 
       div(slot="footer")
         button.pure-button(@click="show.upgrade = false") Cancel
@@ -82,14 +76,14 @@ script#settings-admin-template(type="text/x-template")
         p.pure-control-group
           label(for="pass") Password
           input(name="pass", v-model="password", type="password",
-            @keyup.enter="upload_confirmed")
+            @keyup.enter="upload_confirmed", focus)
 
       div(slot="footer")
         button.pure-button(@click="show.upload = false") Cancel
         button.pure-button.pure-button-primary(@click="upload_confirmed")
           | Upload
 
-    message(:show.sync="show.upgrading", click_away_close="false")
+    message.upgrading(:show.sync="show.upgrading", :click_away_close="false")
       h3(slot="header") Firmware upgrading
       div(slot="body")
         h3 Please wait...
index 4538f504004921584be226f0e205d7178736d849..28b0197fad42e7c78ac8ea06969ea2389f5a3406 100644 (file)
@@ -34,7 +34,7 @@ script#settings-macros-template(type="text/x-template")
         .fa.fa-plus
         | Add
 
-    ol.macros
+    ol.macros(v-if="macros.length")
       li(v-for="macro in macros")
         label.index {{"#" + ($index + 1)}}
         .macro(draggable="true", @dragstart="dragstart", @mousedown="mousedown",
@@ -52,3 +52,10 @@ script#settings-macros-template(type="text/x-template")
               title="Select the macro file.")
             .fa.fa-trash(@click="remove($index)",
               :title="'Delete macro ' + ($index + 1) + '.'")
+
+    p.
+      Macros are programs that you can run by clicking the corrisponding
+      button on the #[a(href="#control") control page].  To create a new
+      macro, click the "Add" button.  Then set the button color, name and
+      select a program to run from the file menu.  You can upload new
+      programs or create them with the #[a(href="#editor") editor].
diff --git a/src/pug/templates/upload-dialog.pug b/src/pug/templates/upload-dialog.pug
new file mode 100644 (file)
index 0000000..5f5b447
--- /dev/null
@@ -0,0 +1,45 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#upload-dialog-template(type="text/x-template")
+  .upload-dialog
+    message(:show.sync="show", :click_away_close="false")
+      h3(slot="header") Uploading
+
+      div(slot="body")
+        div {{msg}}
+        .progress
+          .bar(:style="{width: 'calc(' + progress + '% - 1em)'}")
+            | {{progress}}%
+
+      div(slot="footer")
+        button.pure-button(title="Cancel upload", @click="cancel")
+          .fa.fa-times
+          | Cancel
+
+    form(v-el:form)
+      input(type="file", v-el:input, @change="_upload")
diff --git a/src/pug/templates/view-cheat-sheet.pug b/src/pug/templates/view-cheat-sheet.pug
deleted file mode 100644 (file)
index 9f851a3..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-//-/////////////////////////////////////////////////////////////////////////////
-//-                                                                           //
-//-               This file is part of the Buildbotics firmware.              //
-//-                                                                           //
-//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
-//-                                                                           //
-//-       This Source describes Open Hardware and is licensed under the       //
-//-                               CERN-OHL-S v2.                              //
-//-                                                                           //
-//-       You may redistribute and modify this Source and make products       //
-//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
-//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
-//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
-//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
-//-                                conditions.                                //
-//-                                                                           //
-//-              Source location: https://github.com/buildbotics              //
-//-                                                                           //
-//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
-//-  these sources, You must maintain the Source Location clearly visible on  //
-//-  the external case of the CNC Controller or other product you make using  //
-//-                                this Source.                               //
-//-                                                                           //
-//-              For more information, email info@buildbotics.com             //
-//-                                                                           //
-//-/////////////////////////////////////////////////////////////////////////////
-
-script#view-cheat-sheet-template(type="text/x-template")
-  // Modified from http://linuxcnc.org/docs/html/gcode.html
-  - var base = 'http://linuxcnc.org/docs/html/gcode';
-  - var camotics_base = 'https://camotics.org/gcode.html';
-
-  .cheat-sheet
-    h2 GCode Cheat Sheet
-
-    table
-      tr
-        th Code
-        th Parameters
-        th Description
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Motion
-      tr
-        td
-          a(href=`${base}/g-code.html#gcode:g0`) G0
-        td
-        td Rapid Move
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g1`) G1
-        td
-        td Linear Move
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g2-g3`) G2, G3
-        td I J K or R, P
-        td Arc Move
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g4`) G4
-        td P
-        td Dwell
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g5`) G5
-        td I J P Q
-        td Cubic Spline
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g5.1`) G5.1
-        td I J
-        td Quadratic Spline
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g5.2-g5.3`) G5.2
-        td P L
-        td NURBS
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g33.1`) G33.1
-        td K
-        td Rigid Tapping
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Homing & Probing
-      tr
-        td
-          a(target="_blank", href=`${camotics_base}#gcodes-g28_2-28_3`)
-            | G28.2, G28.3
-        td
-        td (Un)set Axis Homed State
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g38`) G38.2 - G38.5
-        td
-        td Straight Probe
-      tr
-        td
-          a(target="_blank", href=`${camotics_base}#gcodes-g38_6-38_9`)
-            | G38.6 - G38.9
-        td
-        td Seek Switch
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Tool Control
-      tr
-        td
-          a(href=`${base}/other-code.html#sec:select-tool`) T
-        td
-        td Select Tool
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m6`) M6
-        td T
-        td Tool Change
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m61`) M61
-        td Q
-        td Set Current Tool
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l1`) G10 L1
-        td P Q R
-        td Set Tool Table
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l10`) G10 L10
-        td P
-        td Set Tool Table
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l11`) G10 L11
-        td P
-        td Set Tool Table
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g43`) G43
-        td  H
-        td Tool Length Offset
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g43.1`) G43.1
-        td
-        td Dynamic Tool Length Offset
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g43.2`) G43.2
-        td  H
-        td Apply additional Tool Length Offset
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g49`) G49
-        td
-        td Cancel Tool Length Compensation
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Feed Control
-      tr
-        td
-          a(href=`${base}/other-code.html#sec:set-feed-rate`) F
-        td
-        td Set Feed Rate
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g93-g94-g95`)
-            | G93, G94, G95
-        td
-        td Feed Rate Mode
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m52`) M52
-        td P0 (off) or P1 (on)
-        td Adaptive Feed Control
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m53`) M53
-        td P0 (off) or P1 (on)
-        td Feed Stop Control
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Spindle Control
-      tr
-        td
-          a(href=`${base}/other-code.html#sec:set-spindle-speed`) S
-        td
-        td Set Spindle Speed
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m3-m4-m5`)
-            | M3, M4, M5
-        td S
-        td Spindle Control
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m19`) M19
-        td
-        td Orient Spindle
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g96-g97`) G96, G97
-        td  S D
-        td Spindle Control Mode
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g33`) G33
-        td K
-        td Spindle Synchronized Motion
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Coolant
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m7-m8-m9`)
-            | M7, M8, M9
-        td
-        td Coolant Control
-      tr
-        td
-          a(target="_blank", href=`${camotics_base}#mcodes-m7_1-m8_1`)
-            | M7.1, M8.1
-        td
-        td Disable Coolant
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Stopping
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m0-m1`) M0, M1
-        td
-        td Program Pause
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m2-m30`) M2, M30
-        td
-        td Program End
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m60`) M60
-        td
-        td Pallet Change Pause
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Units
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g20-g21`) G20, G21
-        td
-        td Units (inch, mm)
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Distance Mode
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g90-g91`) G90, G91
-        td
-        td Distance Mode
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g90.1-g91.1`)
-            | G90.1, G91.1
-        td
-        td Arc Distance Mode
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g7`) G7
-        td
-        td Lathe Diameter Mode
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g8`) G8
-        td
-        td Lathe Radius Mode
-
-      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
-      tr.header-row.unimplemented(v-if="showUnimplemented")
-        th(colspan='3') Cutter Radius Compensation
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g40`) G40
-        td
-        td Compensation Off
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g41-g42`) G41,G42
-        td D
-        td Cutter Compensation
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g41.1-g42.1`)
-            | G41.1, G42.1
-        td D L
-        td Dynamic Cutter Compensation
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Path Control Mode
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g61-g61.1`)
-            | G61 G61.1
-        td
-        td Exact Path Mode
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g64`) G64
-        td P Q
-        td Path Blending (Partial support)
-
-      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
-      tr.header-row.unimplemented(v-if="showUnimplemented")
-        th(colspan='3') Overrides
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m48-m49`) M48, M49
-        td
-        td Speed and Feed Override Control
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m50`) M50
-        td P0 (off) or P1 (on)
-        td Feed Override Control
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m51`) M51
-        td P0 (off) or P1 (on)
-        td Spindle Speed Override Control
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Coordinate Systems, Offsets & Planes
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g54-g59.3`)
-            | G54-G59.3
-        td
-        td Select Coordinate System
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l2`) G10 L2
-        td P R
-        td Set Coordinate System
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g10-l20`) G10 L20
-        td P
-        td Set Coordinate System
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g52`) G52
-        td
-        td Local Coordinate System Offset
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g53`) G53
-        td
-        td Move in Machine Coordinates
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g92`) G92
-        td
-        td Coordinate System Offset
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g92.1-g92.2`)
-            | G92.1, G92.2
-        td
-        td Reset G92 Offsets
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g92.3`) G92.3
-        td
-        td Restore G92 Offsets
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g28-g28.1`)
-            | G28, G28.1
-        td
-        td Go/Set Predefined Position
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g30-g30.1`)
-            | G30, G30.1
-        td
-        td Go/Set Predefined Position
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g17-g19.1`)
-            | G17 - G19.1
-        td
-        td Plane Select
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Flow-control Codes
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:subroutines`)
-            | o sub/endsub/call
-        td
-        td Subroutines, sub/endsub call
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:looping`) o while
-        td
-        td Looping, while/endwhile do/while
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:conditional`) o if
-        td
-        td Conditional, if/else/endif
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:repeat`) o repeat
-        td
-        td Repeat a loop of code
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:indirection`) []
-        td
-        td Indirection
-      tr
-        td
-          a(target="_blank", href=`${base}/o-code.html#ocode:calling-files`)
-            | o call
-        td
-        td Call named or numbered file
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Modal State
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m70`) M70
-        td
-        td Save modal state
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m71`) M71
-        td
-        td Invalidate stored state
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m72`) M72
-        td
-        td Restore modal state
-      tr
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m73`) M73
-        td
-        td Save/restore modal state
-
-      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
-      tr.header-row.unimplemented(v-if="showUnimplemented")
-        th(colspan='3') Input/Output
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m62-m65`) M62 - M65
-        td P
-        td Digital Output Control
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m66`) M66
-        td P E L Q
-        td Wait on Input
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m67`) M67
-        td T
-        td Analog Output,Synchronized
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m68`) M68
-        td T
-        td Analog Output, Immediate
-
-      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
-      tr.header-row.unimplemented(v-if="showUnimplemented")
-        th(colspan='3') User Defined Commands
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/m-code.html#mcode:m100-m199`)
-            | M101 - M199
-        td P Q
-        td User Defined Commands
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Canned cycles
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g80`) G80
-        td
-        td Cancel Canned Cycle
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g81`) G81
-        td R L (P)
-        td Drilling Cycle
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g82`) G82
-        td R L (P)
-        td Drilling Cycle, Dwell
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g83`) G83
-        td R L Q
-        td Drilling Cycle, Peck
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g73`) G73
-        td R L Q
-        td Drilling Cycle, Chip Breaking
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g85`) G85
-        td R L (P)
-        td Boring Cycle, Feed Out
-      tr
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g89`) G89
-        td R L (P)
-        td Boring Cycle, Dwell, Feed Out
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g76`) G76
-        td P Z I J R K Q H L E
-        td Threading Cycle
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/g-code.html#gcode:g98-g99`) G98, G99
-        td
-        td Canned Cycle Return Level
-
-      tr.spacer-row: th
-      tr.header-row
-        th(colspan='3') Comments & Messages
-      tr
-        td
-          a(target="_blank", href=`${base}/overview.html#gcode:comments`) ; (…)
-        td
-        td Comments
-      tr
-        td
-          a(target="_blank", href=`${base}/overview.html#gcode:messages`)
-            | (MSG,…)
-        td
-        td Messages
-      tr
-        td
-          a(target="_blank", href=`${base}/overview.html#gcode:debug`) (DEBUG,…)
-        td
-        td Debug Messages
-      tr.unimplemented(v-if="showUnimplemented")
-        td
-          a(target="_blank", href=`${base}/overview.html#gcode:print`) (PRINT,…)
-        td
-        td Print Messages
-      tr
-        td
-          a(target="_blank", href=`${base}/overview.html#_logging`) (LOG,…)
-        td
-        td Logging Messages
-
-    div
-      input(type="checkbox", v-model="showUnimplemented")
-      label Show unsupported codes
-
-    h2 Further GCode Programming Documentation
-
-    p
-      | The Buildbotics controller implements a subset of LinuxCNC GCode.
-      | Supported commands are listed above.  You can find further help with
-      | #[a(href="http://wikipedia.com/wiki/G-code", target="_blank") GCode]
-      | programming on the LinuxCNC website:
-
-    ul
-      li: a(href="http://linuxcnc.org/docs/html/gcode/overview.html",
-        target="_blank")
-        | G Code overview
-      li: a(href="http://linuxcnc.org/docs/html/gcode/g-code.html",
-        target="_blank")
-        | G Code reference
-      li: a(href="http://linuxcnc.org/docs/html/gcode/m-code.html",
-        target="_blank")
-        | M Code reference
index 2c61ff49fc28f87750b740e5f6bce7765db01ace..669b4e37f7427fb64795d6fec0a6455e8f530075 100644 (file)
@@ -115,78 +115,84 @@ script#view-control-template(type="text/x-template")
       button.pure-button.macro(v-for="macro in config.macros",
         v-if="macro.path", @click="run_macro($index + 1)",
         :title="'Run macro ' + ($index + 1) + ' ' + macro.name + '\n' + macro.path",
-        :style="{background: macro.color}")
+        :style="{'background-color': macro.color}")
         | {{($index + 1) + ' ' + macro.name}}
 
-    table.info
-      tr
-        th State
-        td(:class="{attention: highlight_state}") {{mach_state}}
-
-      tr
-        th Message
-        td.message(:class="{attention: highlight_state}")
-          | {{message.replace(/^#/, '')}}
-
-      tr(title="Active machine units")
-        th Units
-        td.mach_units {{mach_units}}
-
-      tr(title="Active tool")
-        th Tool
-        td {{state.tool || 0}}
-
-    table.info
-      tr(
-        title="Current velocity in {{metric ? 'meters' : 'inches'}} per minute")
-        th Velocity
-        td
-          unit-value(:value="state.v", precision="2", unit="", iunit="",
-            scale="0.0254")
-          | {{metric ? ' m/min' : ' IPM'}}
-
-      tr(title="Programmed feed rate.")
-        th Feed
-        td
-          unit-value(:value="state.feed", precision="2", unit="", iunit="")
-          | {{metric ? ' mm/min' : ' IPM'}}
-
-      tr(title="Programed and actual speed.")
-        th Speed
-        td
-          | {{state.speed || 0 | fixed 0}}
-          span(v-if="!isNaN(state.s)") &nbsp;({{state.s | fixed 0}})
-          = ' RPM'
-
-      tr(title="Load switch states.")
-        th Loads
-        td
-          span(:class="state['1oa'] ? 'load-on' : ''")
-            | 1:{{state['1oa'] ? 'On' : 'Off'}}
-          | &nbsp;
-          span(:class="state['2oa'] ? 'load-on' : ''")
-            | 2:{{state['2oa'] ? 'On' : 'Off'}}
-
-    table.info
-      tr
-        th Remaining
-        td(title="Total run time (days:hours:mins:secs)").
-          #[span(v-if="remaining") {{remaining | time}} of]
-          {{total_time | time}}
-      tr
-        th ETA
-        td.eta {{eta}}
-      tr
-        th Line
-        td
-          | {{0 <= state.line ? state.line : 0 | number}}
-          span(v-if="state.lines")
-            | &nbsp;of {{state.lines | number}}
-      tr
-        th {{this.simulating ? 'Simulating' : 'Progress'}}
-        td.progress
-          label {{(progress || 0) | percent}}
-          .bar(:style="'width:' + (progress || 0) * 100 + '%'")
+      button.pure-button.macro(@click="goto('#settings:macros')",
+        title="Go to macro settings")
+        .fa.fa-cog
+        | Macros
+
+    .info-blocks
+      table.info
+        tr
+          th State
+          td(:class="{attention: highlight_state}") {{mach_state}}
+
+        tr
+          th Message
+          td.message(:class="{attention: highlight_state}")
+            | {{message.replace(/^#/, '')}}
+
+        tr(title="Active machine units")
+          th Units
+          td.mach_units {{mach_units}}
+
+        tr(title="Active tool")
+          th Tool
+          td {{state.tool || 0}}
+
+      table.info
+        tr(
+          title="Velocity in {{metric ? 'meters' : 'inches'}} per minute")
+          th Velocity
+          td
+            unit-value(:value="state.v", precision="2", unit="", iunit="",
+              scale="0.0254")
+            | {{metric ? ' m/min' : ' IPM'}}
+
+        tr(title="Programmed feed rate.")
+          th Feed
+          td
+            unit-value(:value="state.feed", precision="2", unit="", iunit="")
+            | {{metric ? ' mm/min' : ' IPM'}}
+
+        tr(title="Programed and actual speed.")
+          th Speed
+          td
+            | {{state.speed || 0 | fixed 0}}
+            span(v-if="!isNaN(state.s)") &nbsp;({{state.s | fixed 0}})
+            = ' RPM'
+
+        tr(title="Load switch states.")
+          th Loads
+          td
+            span(:class="state['1oa'] ? 'load-on' : ''")
+              | 1:{{state['1oa'] ? 'On' : 'Off'}}
+            | &nbsp;
+            span(:class="state['2oa'] ? 'load-on' : ''")
+              | 2:{{state['2oa'] ? 'On' : 'Off'}}
+
+      table.info
+        tr
+          th Remaining
+          td(title="Total run time (days:hours:mins:secs)").
+            #[span(v-if="remaining") {{remaining | time}} of]
+            {{total_time | time}}
+        tr
+          th ETA
+          td.eta {{eta}}
+        tr
+          th Line
+          td
+            | {{0 <= state.line ? state.line : 0 | number}}
+            span(v-if="state.lines")
+              | &nbsp;of {{state.lines | number}}
+        tr
+          th {{this.simulating ? 'Simulating' : 'Progress'}}
+          td.progress
+            label {{(progress || 0) | percent}}
+            .bar(:style="'width:' + (progress || 0) * 100 + '%'")
 
     .override(title="Feed rate override.")
       label Feed
@@ -246,7 +252,7 @@ script#view-control-template(type="text/x-template")
             #[.fa.fa-pencil] Edit
 
           button.pure-button(title="Open 3D view.", @click="view",
-            :disabled="!state.queued").
+            v-if="$root.webGLSupported", :disabled="!state.queued").
             #[.fa.fa-eye] View
 
           .filename {{filename}}
diff --git a/src/pug/templates/view-docs.pug b/src/pug/templates/view-docs.pug
new file mode 100644 (file)
index 0000000..1964c77
--- /dev/null
@@ -0,0 +1,36 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-               This file is part of the Buildbotics firmware.              //
+//-                                                                           //
+//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
+//-                                                                           //
+//-       This Source describes Open Hardware and is licensed under the       //
+//-                               CERN-OHL-S v2.                              //
+//-                                                                           //
+//-       You may redistribute and modify this Source and make products       //
+//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
+//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
+//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
+//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
+//-                                conditions.                                //
+//-                                                                           //
+//-              Source location: https://github.com/buildbotics              //
+//-                                                                           //
+//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
+//-  these sources, You must maintain the Source Location clearly visible on  //
+//-  the external case of the CNC Controller or other product you make using  //
+//-                                this Source.                               //
+//-                                                                           //
+//-              For more information, email info@buildbotics.com             //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#view-docs-template(type="text/x-template")
+  div
+    nav.navbar
+      a.nav-item(href="#docs:help") Help
+      a.nav-item(href="#docs:gcode") GCode
+      a.nav-item(href="#docs:license") License
+
+    .docs-view(v-if="view", :is="'docs-' + view", :config="config",
+      :template="template", :state="state", keep-alive)
index e71cd55e6a2007f0d2f6601a94e716810a09f6e1..18241839b7de712dcb3c8e8a682f9ceef54ca85a 100644 (file)
@@ -41,7 +41,7 @@ script#view-editor-template(type="text/x-template")
             .fa.fa-folder-open
             span Open
 
-          nav-item(@click="save()", :disabled="!dirty")
+          nav-item(@click="save()", :disabled="!modified")
             .fa.fa-save
             span Save
 
@@ -49,7 +49,7 @@ script#view-editor-template(type="text/x-template")
             .fa.fa-save
             span Save As
 
-          nav-item(@click="revert()", :disabled="!dirty")
+          nav-item(@click="revert()", :disabled="!modified")
             .fa.fa-undo
             span Revert
 
@@ -58,7 +58,7 @@ script#view-editor-template(type="text/x-template")
             span Download
             a(v-el:download, style="display:none", :download="basename")
 
-          nav-item(@click="view()")
+          nav-item(@click="view()", v-if="$root.webGLSupported")
             .fa.fa-eye
             span View
 
index 8ed9d480e04bde17fa1b3d7950264739d7f7cddd..3d15b85ac9749cbce6b5c95533ec2a3395b8fcb8 100644 (file)
@@ -48,7 +48,8 @@ script#view-files-template(type="text/x-template")
             .fa.fa-pencil
             span Edit
 
-          nav-item(@click="view", :disabled="!selected || is_dir")
+          nav-item(@click="view", :disabled="!selected || is_dir",
+            v-if="$root.webGLSupported")
             .fa.fa-eye
             span View
 
diff --git a/src/pug/templates/view-help.pug b/src/pug/templates/view-help.pug
deleted file mode 100644 (file)
index f6fae97..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-//-/////////////////////////////////////////////////////////////////////////////
-//-                                                                           //
-//-               This file is part of the Buildbotics firmware.              //
-//-                                                                           //
-//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
-//-                                                                           //
-//-       This Source describes Open Hardware and is licensed under the       //
-//-                               CERN-OHL-S v2.                              //
-//-                                                                           //
-//-       You may redistribute and modify this Source and make products       //
-//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
-//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
-//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
-//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
-//-                                conditions.                                //
-//-                                                                           //
-//-              Source location: https://github.com/buildbotics              //
-//-                                                                           //
-//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
-//-  these sources, You must maintain the Source Location clearly visible on  //
-//-  the external case of the CNC Controller or other product you make using  //
-//-                                this Source.                               //
-//-                                                                           //
-//-              For more information, email info@buildbotics.com             //
-//-                                                                           //
-//-/////////////////////////////////////////////////////////////////////////////
-
-script#view-help-template(type="text/x-template")
-  #help
-    h2 User Manual
-    p
-      | You can find a detailed user manual at
-      |
-      a(href="http://buildbotics.com/docs", target="_blank")
-        | buildbotics.com/docs
-      |.
-
-    h2 Discussion Forum
-    p
-      | If you're having trouble or just want to chat with other Buildbotics
-      | CNC controller owners, head over to the Buildbotics forum at
-      |
-      a(href="http://forum.buildbotics.com", target="_blank")
-        | forum.buildbotics.com
-      |.  Register on the site and post a message. We'll be happy to help.
-
-    h2 CAD/CAM Software
-    p
-      a(href="http://wikipedia.com/wiki/Computer-aided_manufacturing",
-        target="_blank") CAM
-      |
-      | software can be used to create GCode
-      | automatically from
-      |
-      a(href="http://wikipedia.com/wiki/Computer-aided_design",
-        target="_blank") CAD
-      |
-      | models.  Here are a few CAD/CAM resources:
-    ul
-      li: a(href="http://camotics.org/", target="_blank")
-        | CAMotics - Open-Source CNC Simulator
-      li: a(href="http://librecad.org/", target="_blank")
-        | LibreCAD - Open-Source 2D CAD
-      li: a(href="https://www.freecadweb.org/", target="_blank")
-        | FreeCAD - Open-Source 3D CAD
-      li: a(href="http://www.openscad.org/", target="_blank")
-        | OpenSCAD - Open-Source 3D CAD for programmers
-      li: a(href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Cam",
-        target="_blank") LinuxCNC CAM resources
diff --git a/src/pug/templates/view-license.pug b/src/pug/templates/view-license.pug
deleted file mode 100644 (file)
index 6fd1acb..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-//-/////////////////////////////////////////////////////////////////////////////
-//-                                                                           //
-//-               This file is part of the Buildbotics firmware.              //
-//-                                                                           //
-//-      Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved.     //
-//-                                                                           //
-//-       This Source describes Open Hardware and is licensed under the       //
-//-                               CERN-OHL-S v2.                              //
-//-                                                                           //
-//-       You may redistribute and modify this Source and make products       //
-//-  using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). //
-//-         This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED         //
-//-  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS //
-//-   FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable   //
-//-                                conditions.                                //
-//-                                                                           //
-//-              Source location: https://github.com/buildbotics              //
-//-                                                                           //
-//-    As per CERN-OHL-S v2 section 4, should You produce hardware based on   //
-//-  these sources, You must maintain the Source Location clearly visible on  //
-//-  the external case of the CNC Controller or other product you make using  //
-//-                                this Source.                               //
-//-                                                                           //
-//-              For more information, email info@buildbotics.com             //
-//-                                                                           //
-//-/////////////////////////////////////////////////////////////////////////////
-
-script#view-license-template(type="text/x-template")
-  #license
-    h2 License
-
-    p.
-      This is Open Hardware licensed under the CERN-OHL-S v2.  Unless
-      otherwise noted, all sources are copyright 2015 - 2021,
-      Buildbotics LLC.
-
-    p.
-      You may redistribute and modify the Source and make products
-      using it under the terms of the CERN-OHL-S v2 (
-      #[a(href="https:/cern.ch/cern-ohl") https:/cern.ch/cern-ohl]).
-      The Source is distributed WITHOUT ANY EXPRESS OR IMPLIED
-      WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS
-      FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable
-
-    p.
-      The Source can be found at
-      #[a(href="//github.com/buildbotics") github.com/buildbotics].
-
-    p.
-      As per CERN-OHL-S v2 section 4, should You produce hardware based on
-      the Source, You must maintain the Source Location clearly visible on
-      the external case of the CNC Controller or other product you make using
-      the Source.
-
-    p.
-      For information regarding this software, email
-      #[a(href="mailto:info@buildbotics.com") info@buildbotics.com].
index e67724da308852ba3e2b74d3c66403c34d536528..674c2ee3dfd833722b04b7c78b41b23d00d9f837 100644 (file)
@@ -45,7 +45,9 @@ script#view-settings-template(type="text/x-template")
       a.nav-item(href="#settings:admin") Admin
 
       button.save.pure-button.button-success(:disabled="!modified",
-        @click="save") Save
+        @click="save", title="Save settings")
+        .fa.fa-floppy-o
+        | Save
 
     .settings-view(v-if="view", :is="'settings-' + view", :index="index",
       :config="config", :template="template", :state="state", keep-alive)
index 55976104edfb7f26ea549fd77b09ec85b7a665ed..d1dc82349c68ad420f76c6bf4ef87c372ff54d12 100644 (file)
@@ -62,6 +62,7 @@ class Ctrl(object):
             self.mach = bbctrl.Mach(self, self.avr)
             self.preplanner = bbctrl.Preplanner(self)
             self.fs = bbctrl.FileSystem(self)
+            self.queue = bbctrl.ProgramQueue(self)
             if not args.demo: self.jog = bbctrl.Jog(self)
             self.pwr = bbctrl.Pwr(self)
 
index 4f1ef0fbef34bacf7e52b7b4f15a994946861951..3b281a848b773a45be32db1780e76f0dbc92d0e1 100644 (file)
@@ -44,24 +44,7 @@ class FileSystem:
       from shutil import copy
       copy(bbctrl.get_resource('http/buildbotics.nc'), upload)
 
-    ctrl.events.on('invalidate', self.invalidate)
-    ctrl.events.on('invalidate-all', self.invalidate_all)
     self.usb_update()
-    self.queue_next_file()
-
-
-  def queue_next_file(self):
-    upload = self.ctrl.root + '/upload'
-
-    files = []
-    for path in os.listdir(upload):
-      if os.path.isfile(upload + '/' + path):
-        files.append(path)
-
-    files.sort()
-
-    if len(files): self.queue_file('Home/' + files[0])
-    else: self.queue_file('')
 
 
   def realpath(self, path):
@@ -83,72 +66,6 @@ class FileSystem:
   def isfile(self, path): return os.path.isfile(self.realpath(path))
 
 
-  def invalidate(self, path):
-    if path == self.ctrl.state.get('queued', ''):
-      self.ctrl.ioloop.add_callback(self.requeue)
-
-
-  def invalidate_all(self):
-    self.ctrl.ioloop.add_callback(self.requeue)
-
-
-  def requeue(self):
-    self.queue_file(self.ctrl.state.get('queued', ''))
-
-
-  def set_bounds(self, bounds):
-      import json
-      self.log.info('bounds %s' % json.dumps(bounds))
-
-      for axis in 'xyzabc':
-          for name in ('min', 'max'):
-              value = bounds[name][axis] if axis in bounds[name] else 0
-              self.ctrl.state.set('queued_%s_%s' % (name, axis), value)
-
-
-  def clear_bounds(self):
-      for axis in 'xyzabc':
-          for name in ('min', 'max'):
-              self.ctrl.state.set('queued_%s_%s' % (name, axis), 0)
-
-
-  def queue_file(self, path):
-    realpath = self.realpath(path)
-
-    if os.path.exists(realpath): modified = os.path.getmtime(realpath)
-    else: path, modified = '', 0
-
-    state = self.ctrl.state
-    state.set('queued', path)
-    state.set('queued_modified', modified)
-    state.set('queued_time', 0)
-    state.set('queued_messages', [])
-    state.set('line', 0)
-    self.clear_bounds()
-
-    if not modified: return
-
-
-    def check_progress():
-      if state.get('queued', '') != path: return
-      progress = self.ctrl.preplanner.get_plan_progress(path)
-      state.set('queued_progress', progress)
-      if progress < 1: self.ctrl.ioloop.call_later(1, check_progress)
-
-    check_progress()
-
-
-    def set_state(future):
-      meta = future.result()[0]
-
-      self.set_bounds(meta['bounds'])
-      state.set('queued_time', meta['time'])
-      state.set('queued_messages', meta['messages'])
-
-    future = self.ctrl.preplanner.get_plan(path)
-    self.ctrl.ioloop.add_future(future, set_state)
-
-
   def delete(self, path):
     realpath = self.realpath(path)
 
index a85baa8306d3640f9b0d2e2b1d85d169ba7135ff..29c35f67447d83e128df8d53268eff2a7c0c4ef1 100644 (file)
@@ -90,5 +90,5 @@ class FileSystemHandler(bbctrl.RequestHandler):
             self.write(json.dumps(d, separators = (',', ':')))
 
         else:
-            with open(realpath.encode('utf8'), 'r') as f:
+            with open(realpath.encode('utf8'), 'rb') as f:
                 self.write(f.read())
index a37be92abda8f4d8544eb6d863b2db02d0ea236f..9a248ae35dc4b3638fa3bdd16c877ff122b30f09 100644 (file)
@@ -25,6 +25,8 @@
 #                                                                              #
 ################################################################################
 
+from tornado.web import HTTPError
+
 import bbctrl
 from bbctrl.Comm import Comm
 import bbctrl.Cmd as Cmd
@@ -295,7 +297,7 @@ class Mach(Comm):
 
 
     def start(self):
-        path = self.ctrl.state.get('queued', '')
+        path = self.ctrl.queue.get()
         if not path: return
         self._begin_cycle('running')
         self.planner.load(path)
@@ -360,7 +362,12 @@ class Mach(Comm):
         if len(macros) < macro: raise Exception('Invalid macro id %d' % macro)
         path = 'Home/' + macros[macro - 1]['path']
 
+        if not self.ctrl.fs.exists(path):
+            raise HTTPError(404, 'Macro file not found')
+
         self.mlog.info('Running macro %d %s' % (macro, path))
         self._begin_cycle('running')
-        self.planner.load(path)
+
+        self.ctrl.queue.push(path)
+        self.planner.load(path, lambda: self.ctrl.queue.pop())
         super().resume()
index 3898ff6916af9548a6ca09fab257a274ef673585..54bdc1ddc37f7e99182c7574d0afe9b9b4c7dc2a 100644 (file)
@@ -67,6 +67,7 @@ class Planner():
         self.planner = None
         self._position_dirty = False
         self.where = ''
+        self.end_cb = None
 
         ctrl.state.add_listener(self._update)
 
@@ -309,7 +310,7 @@ class Planner():
             return Cmd.seek(sw, block['active'], block['error'])
 
         if type == 'end':
-            self.cmdq.enqueue(id, self._log_time, 'Program End: ')
+            self.cmdq.enqueue(id, self._end_program, 'Program end: ')
             return '' # Sends id
 
         raise Exception('Unknown planner command "%s"' % type)
@@ -334,15 +335,16 @@ class Planner():
         # Release planner callbacks
         if self.planner is not None:
             self.planner.set_resolver(None)
-            self.planner.set_logger(None)
+            camotics.set_logger(None)
 
 
     def reset(self, stop = True):
+        self._end_program()
         if stop: self.ctrl.mach.stop()
         self.planner = camotics.Planner()
         self.planner.set_resolver(self._get_var_cb)
         # TODO logger is global and will not work correctly in demo mode
-        self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3')
+        camotics.set_logger(self._log_cb, 1, 'LinePlanner:3')
         self._position_dirty = True
         self.cmdq.clear()
         self.reset_times()
@@ -357,7 +359,19 @@ class Planner():
         self.reset_times()
 
 
-    def load(self, path):
+    def _end_program(self, msg = None):
+        if self.end_cb is not None:
+            self.end_cb()
+            self.end_cb = None
+
+        if msg is not None: self._log_time(msg)
+
+
+    def load(self, path, done):
+        if done is not None and self.end_cb is not None:
+            raise Exception('End callback already set')
+
+        self.end_cb = done
         self.where = path
         path = self.ctrl.fs.realpath(path)
         self.log.info('GCode:' + path)
@@ -371,7 +385,7 @@ class Planner():
         try:
             self.planner.stop()
             self.cmdq.clear()
-            self._log_time('Program Stop: ')
+            self._end_program('Program Stop: ')
 
         except:
             self.log.exception()
index 25bc46418ee8a281e9ca5969dd498ce38afe4f32..b447ef4ae7b402c5387b1d06dd72a958c7f88df3 100644 (file)
@@ -184,7 +184,7 @@ class Plan(object):
 
         except:
             self.preplanner.log.exception()
-
+            self.future.set_result(None)
 
 
 class Preplanner(object):
diff --git a/src/py/bbctrl/ProgramQueue.py b/src/py/bbctrl/ProgramQueue.py
new file mode 100644 (file)
index 0000000..3a80c97
--- /dev/null
@@ -0,0 +1,160 @@
+################################################################################
+#                                                                              #
+#                 This file is part of the Buildbotics firmware.               #
+#                                                                              #
+#        Copyright (c) 2015 - 2021, Buildbotics LLC, All rights reserved.      #
+#                                                                              #
+#         This Source describes Open Hardware and is licensed under the        #
+#                                 CERN-OHL-S v2.                               #
+#                                                                              #
+#         You may redistribute and modify this Source and make products        #
+#    using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl).  #
+#           This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED          #
+#    WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS  #
+#     FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable    #
+#                                  conditions.                                 #
+#                                                                              #
+#                Source location: https://github.com/buildbotics               #
+#                                                                              #
+#      As per CERN-OHL-S v2 section 4, should You produce hardware based on    #
+#    these sources, You must maintain the Source Location clearly visible on   #
+#    the external case of the CNC Controller or other product you make using   #
+#                                  this Source.                                #
+#                                                                              #
+#                For more information, email info@buildbotics.com              #
+#                                                                              #
+################################################################################
+
+import os
+import bbctrl
+
+
+class ProgramQueue:
+  def __init__(self, ctrl):
+    self.ctrl = ctrl
+    self.log = ctrl.log.get('Queue')
+
+    self.q = []
+    self.updating = False
+    self.dirty = True
+
+    ctrl.events.on('invalidate', self.invalidate)
+    ctrl.events.on('invalidate-all', self.invalidate_all)
+
+    self.next()
+    self._update_progress()
+
+
+  def _update_progress(self):
+    path = self.get()
+
+    if path:
+      progress = self.ctrl.preplanner.get_plan_progress(path)
+      self.ctrl.state.set('queued_progress', progress)
+
+    self.ctrl.ioloop.call_later(1, self._update_progress)
+
+
+  def invalidate(self, path):
+    if path == self.get():
+      self.ctrl.ioloop.add_callback(self.update)
+
+
+  def invalidate_all(self):
+    self.ctrl.ioloop.add_callback(self.update)
+
+
+  def get(self): return self.ctrl.state.get('queued', '')
+
+
+  def load(self, path):
+    if self.get() == path: return;
+    self.log.info('Loading ' + path)
+
+    realpath = self.ctrl.fs.realpath(path)
+
+    if os.path.exists(realpath): modified = os.path.getmtime(realpath)
+    else: path, modified = '', 0
+
+    state = self.ctrl.state
+    state.set('queued', path)
+    state.set('queued_modified', modified)
+    state.set('queued_time', 0)
+    state.set('queued_messages', [])
+    state.set('line', 0)
+    self.clear_bounds()
+    self.update()
+
+
+  def set(self, path):
+    self.load(path)
+    self.q = [path]
+
+
+  def next(self):
+    # Get GCode files from root upload directory
+    upload = self.ctrl.root + '/upload'
+
+    files = []
+    for path in os.listdir(upload):
+      parts = os.path.splitext(path)
+      if (len(parts) == 2 and parts[1] in ('.nc', '.gc', '.gcode') and
+          os.path.isfile(upload + '/' + path)):
+        files.append(path)
+
+    files.sort()
+
+    # Select first file
+    if len(files): self.set('Home/' + files[0])
+    else: self.set('')
+
+
+  def push(self, path):
+    self.log.info('push(%s)' % path)
+
+    self.q.append(path)
+    self.load(path)
+
+
+  def pop(self):
+    self.log.info('pop() q=%s' % self.q)
+
+    if 1 < len(self.q):
+      self.q.pop()
+      self.load(self.q[-1])
+
+
+  def update(self):
+    self.dirty = True
+    if self.updating: return
+
+    path = self.get()
+    if not path: return
+
+    def _update(future):
+      if path == self.get():
+        meta = future.result()[0]
+
+        self.set_bounds(meta['bounds'])
+        self.ctrl.state.set('queued_time', meta['time'])
+        self.ctrl.state.set('queued_messages', meta['messages'])
+        self.dirty = False
+
+      self.updating = False
+      if self.dirty: self.update()
+
+    future = self.ctrl.preplanner.get_plan(path)
+    self.ctrl.ioloop.add_future(future, _update)
+
+
+  def set_bounds(self, bounds):
+      for axis in 'xyzabc':
+          for name in ('min', 'max'):
+              value = bounds[name][axis] if axis in bounds[name] else 0
+              self.ctrl.state.set('queued_%s_%s' % (name, axis), value)
+
+
+  def clear_bounds(self):
+      for axis in 'xyzabc':
+          for name in ('min', 'max'):
+              self.ctrl.state.set('queued_%s_%s' % (name, axis), 0)
index 508b900c8795c6d42c6be423e50539c2fbf2fcf5..7af074b4503e0ed07e08bac7218a635fec56bcde 100644 (file)
@@ -131,7 +131,7 @@ class BugReportHandler(bbctrl.RequestHandler):
             check_add_basename('%s.%d' % (path, i))
         check_add_basename('/var/log/syslog')
         check_add('config.json')
-        check_add(ctrl.fs.realpath(ctrl.state.get('queued', '')))
+        check_add(ctrl.fs.realpath(ctrl.queue.get()))
 
         return files
 
@@ -317,7 +317,7 @@ class QueueHandler(bbctrl.APIHandler):
 
         realpath = self.get_ctrl().fs.realpath(path)
         if not os.path.exists(realpath): raise HTTPError(404, 'File not found')
-        self.get_ctrl().fs.queue_file(path)
+        self.get_ctrl().queue.set(path)
 
 
 class USBUpdateHandler(bbctrl.APIHandler):
index 15b19c32f0dec0fd4a39af18aa11cb7d048f129b..7ad34c03e66f1410fb2f02d4c65558b003c3af61 100644 (file)
@@ -40,6 +40,7 @@ from bbctrl.RequestHandler import RequestHandler
 from bbctrl.APIHandler import APIHandler
 from bbctrl.FileSystemHandler import FileSystemHandler
 from bbctrl.FileSystem import FileSystem
+from bbctrl.ProgramQueue import ProgramQueue
 from bbctrl.Config import Config
 from bbctrl.LCD import LCD, LCDPage
 from bbctrl.Mach import Mach
@@ -181,6 +182,10 @@ def run():
     # Set ObjGraph signal handler
     if args.debug: Debugger(ioloop, args.debug)
 
+    # Set TPL path
+    os.environ['TPL_PATH'] = \
+        '/var/lib/bbctrl/upload/lib/:' + get_resource('tpl_lib/')
+
     # Start server
     web = Web(args, ioloop)
 
index 71eaacba6c0484671608cbe5d59a8ab2eb88454a..f1580f2a972a11dd6f615a44d361ccab6d55f6a4 100644 (file)
@@ -89,8 +89,7 @@ class Plan(object):
 
         self.planner = camotics.Planner()
         self.planner.set_resolver(self.get_var_cb)
-        self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3')
-        self.planner.load(self.path, config)
+        camotics.set_logger(self._log_cb, 1, 'LinePlanner:3')
 
         self.messages = []
         self.levels = dict(I = 'info', D = 'debug', W = 'warning', E = 'error',
@@ -151,9 +150,15 @@ class Plan(object):
             msg.startswith('Auto-creating missing tool')):
             return
 
-        self.messages.append(
-            dict(level = level, msg = msg, filename = filename, line = line,
-                 column = column))
+        msg = dict(
+            level    = level,
+            msg      = msg,
+            filename = filename,
+            line     = line,
+            column   = column)
+        msg = {k: v for k, v in msg.items() if v is not None}
+
+        self.messages.append(msg)
 
 
     def _log_cb(self, line):
@@ -161,11 +166,11 @@ class Plan(object):
         m = reLogLine.match(line)
         if not m: return
 
-        level = m.group('level')
-        msg = m.group('msg')
+        level    = m.group('level')
+        msg      = m.group('msg')
         filename = m.group('file')
-        line = m.group('line')
-        column = m.group('column')
+        line     = m.group('line')
+        column   = m.group('column')
 
         where = ':'.join(filter(None.__ne__, [filename, line, column]))
 
@@ -198,6 +203,8 @@ class Plan(object):
 
         # Execute plan
         try:
+            self.planner.load(self.path, self.config)
+
             while self.planner.has_more():
                 cmd = self.planner.next()
                 self.planner.set_active(cmd['id']) # Release plan
@@ -296,10 +303,10 @@ class Plan(object):
 
         with open('meta.json', 'w') as f:
             meta = dict(
-                time = self.time,
-                lines = self.lines,
+                time     = self.time,
+                lines    = self.lines,
                 maxSpeed = self.maxSpeed,
-                bounds = self.get_bounds(),
+                bounds   = self.get_bounds(),
                 messages = self.messages)
 
             json.dump(meta, f)
index 869d88b4d616a57a68bee6c2d0c0d0c08b714e2f..9212ebf2fdb360e668dab9ceea75b5268f184636 100644 (file)
 
   "macros": {
     "type": "list",
-    "default": [
-      {"name": "test1", "color": "#eceff1", "path": "macros/macro1.nc"},
-      {"name": "test2", "color": "#8d6e63", "path": "macros/macro2.nc"},
-      {"name": "test3", "color": "#ff7043", "path": "macros/macro3.nc"}
-    ],
+    "default": [],
     "template": {
       "name": {"type": "string"},
       "path": {"type": "string"},
diff --git a/src/static/js/codemirror-javascript.js b/src/static/js/codemirror-javascript.js
new file mode 100644 (file)
index 0000000..3426750
--- /dev/null
@@ -0,0 +1 @@
+!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(et){"use strict";et.defineMode("javascript",function(e,l){var t,r,n,a,f=e.indentUnit,d=l.statementIndent,i=l.jsonld,o=l.json||i,u=l.typescript,p=l.wordCharacters||/[\w$\xa1-\uffff]/,c=(t=s("keyword a"),r=s("keyword b"),n=s("keyword c"),a=s("keyword d"),e=s("operator"),{if:s("if"),while:t,with:t,else:r,do:r,try:r,finally:r,return:a,break:a,continue:a,new:s("new"),delete:n,void:n,throw:n,debugger:s("debugger"),var:s("var"),const:s("var"),let:s("var"),function:s("function"),catch:s("catch"),for:s("for"),switch:s("switch"),case:s("case"),default:s("default"),in:e,typeof:e,instanceof:e,true:e={type:"atom",style:"atom"},false:e,null:e,undefined:e,NaN:e,Infinity:e,this:s("this"),class:s("class"),super:s("atom"),yield:n,export:s("export"),import:s("import"),extends:n,await:n});function s(e){return{type:e,style:"keyword"}}var m,v,k=/[+\-*&%=<>!?|~^@]/,y=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function w(e,t,r){return m=e,v=r,t}function b(e,t){var a,r=e.next();if('"'==r||"'"==r)return t.tokenize=(a=r,function(e,t){var r,n=!1;if(i&&"@"==e.peek()&&e.match(y))return t.tokenize=b,w("jsonld-keyword","meta");for(;null!=(r=e.next())&&(r!=a||n);)n=!n&&"\\"==r;return n||(t.tokenize=b),w("string","string")}),t.tokenize(e,t);if("."==r&&e.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/))return w("number","number");if("."==r&&e.match(".."))return w("spread","meta");if(/[\[\]{}\(\),;\:\.]/.test(r))return w(r);if("="==r&&e.eat(">"))return w("=>","operator");if("0"==r&&e.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/))return w("number","number");if(/\d/.test(r))return e.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/),w("number","number");if("/"==r)return e.eat("*")?(t.tokenize=x)(e,t):e.eat("/")?(e.skipToEnd(),w("comment","comment")):Ze(e,t,1)?(function(e){for(var t,r=!1,n=!1;null!=(t=e.next());){if(!r){if("/"==t&&!n)return;"["==t?n=!0:n&&"]"==t&&(n=!1)}r=!r&&"\\"==t}}(e),e.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/),w("regexp","string-2")):(e.eat("="),w("operator","operator",e.current()));if("`"==r)return(t.tokenize=h)(e,t);if("#"==r&&"!"==e.peek())return e.skipToEnd(),w("meta","meta");if("#"==r&&e.eatWhile(p))return w("variable","property");if("<"==r&&e.match("!--")||"-"==r&&e.match("->")&&!/\S/.test(e.string.slice(0,e.start)))return e.skipToEnd(),w("comment","comment");if(k.test(r))return">"==r&&t.lexical&&">"==t.lexical.type||(e.eat("=")?"!"!=r&&"="!=r||e.eat("="):/[<>*+\-|&?]/.test(r)&&(e.eat(r),">"==r&&e.eat(r))),"?"==r&&e.eat(".")?w("."):w("operator","operator",e.current());if(p.test(r)){e.eatWhile(p);r=e.current();if("."!=t.lastType){if(c.propertyIsEnumerable(r)){t=c[r];return w(t.type,t.style,r)}if("async"==r&&e.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/,!1))return w("async","keyword",r)}return w("variable","variable",r)}}function x(e,t){for(var r,n=!1;r=e.next();){if("/"==r&&n){t.tokenize=b;break}n="*"==r}return w("comment","comment")}function h(e,t){for(var r,n=!1;null!=(r=e.next());){if(!n&&("`"==r||"$"==r&&e.eat("{"))){t.tokenize=b;break}n=!n&&"\\"==r}return w("quasi","string-2",e.current())}var g="([{}])";function j(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var r,n=e.string.indexOf("=>",e.start);if(!(n<0)){!u||(r=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,n)))&&(n=r.index);for(var a=0,i=!1,o=n-1;0<=o;--o){var c=e.string.charAt(o),s=g.indexOf(c);if(0<=s&&s<3){if(!a){++o;break}if(0==--a){"("==c&&(i=!0);break}}else if(3<=s&&s<6)++a;else if(p.test(c))i=!0;else if(/["'\/`]/.test(c))for(;;--o){if(0==o)return;if(e.string.charAt(o-1)==c&&"\\"!=e.string.charAt(o-2)){o--;break}}else if(i&&!a){++o;break}}i&&!a&&(t.fatArrowAt=o)}}var M={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,this:!0,"jsonld-keyword":!0};function A(e,t,r,n,a,i){this.indented=e,this.column=t,this.type=r,this.prev=a,this.info=i,null!=n&&(this.align=n)}function V(e,t,r,n,a){var i=e.cc;for(E.state=e,E.stream=a,E.marked=null,E.cc=i,E.style=t,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;)if((i.length?i.pop():o?D:F)(r,n)){for(;i.length&&i[i.length-1].lex;)i.pop()();return E.marked?E.marked:"variable"==r&&function(e,t){for(var r=e.localVars;r;r=r.next)if(r.name==t)return 1;for(var n=e.context;n;n=n.prev)for(r=n.vars;r;r=r.next)if(r.name==t)return 1}(e,n)?"variable-2":t}}var E={state:null,column:null,marked:null,cc:null};function z(){for(var e=arguments.length-1;0<=e;e--)E.cc.push(arguments[e])}function I(){return z.apply(null,arguments),!0}function T(e,t){for(var r=t;r;r=r.next)if(r.name==e)return 1}function $(e){var t=E.state;if(E.marked="def",t.context)if("var"==t.lexical.info&&t.context&&t.context.block){var r=function e(t,r){{if(r){if(r.block){var n=e(t,r.prev);return n?n==r.prev?r:new _(n,r.vars,!0):null}return T(t,r.vars)?r:new _(r.prev,new O(t,r.vars),!1)}return null}}(e,t.context);if(null!=r)return void(t.context=r)}else if(!T(e,t.localVars))return void(t.localVars=new O(e,t.localVars));l.globalVars&&!T(e,t.globalVars)&&(t.globalVars=new O(e,t.globalVars))}function C(e){return"public"==e||"private"==e||"protected"==e||"abstract"==e||"readonly"==e}function _(e,t,r){this.prev=e,this.vars=t,this.block=r}function O(e,t){this.name=e,this.next=t}var q=new O("this",new O("arguments",null));function S(){E.state.context=new _(E.state.context,E.state.localVars,!1),E.state.localVars=q}function P(){E.state.context=new _(E.state.context,E.state.localVars,!0),E.state.localVars=null}function N(){E.state.localVars=E.state.context.vars,E.state.context=E.state.context.prev}function U(n,a){function e(){var e=E.state,t=e.indented;if("stat"==e.lexical.type)t=e.lexical.indented;else for(var r=e.lexical;r&&")"==r.type&&r.align;r=r.prev)t=r.indented;e.lexical=new A(t,E.stream.column(),n,null,e.lexical,a)}return e.lex=!0,e}function W(){var e=E.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function B(r){return function e(t){return t==r?I():";"==r||"}"==t||")"==t||"]"==t?z():I(e)}}function F(e,t){return"var"==e?I(U("vardef",t),Me,B(";"),W):"keyword a"==e?I(U("form"),J,F,W):"keyword b"==e?I(U("form"),F,W):"keyword d"==e?E.stream.match(/^\s*$/,!1)?I():I(U("stat"),L,B(";"),W):"debugger"==e?I(B(";")):"{"==e?I(U("}"),P,le,W,N):";"==e?I():"if"==e?("else"==E.state.lexical.info&&E.state.cc[E.state.cc.length-1]==W&&E.state.cc.pop()(),I(U("form"),J,F,W,Te)):"function"==e?I(Oe):"for"==e?I(U("form"),$e,F,W):"class"==e||u&&"interface"==t?(E.marked="keyword",I(U("form","class"==e?e:t),Ue,W)):"variable"==e?u&&"declare"==t?(E.marked="keyword",I(F)):u&&("module"==t||"enum"==t||"type"==t)&&E.stream.match(/^\s*\w/,!1)?(E.marked="keyword","enum"==t?I(Xe):"type"==t?I(Se,B("operator"),ve,B(";")):I(U("form"),Ae,B("{"),U("}"),le,W,W)):u&&"namespace"==t?(E.marked="keyword",I(U("form"),D,F,W)):u&&"abstract"==t?(E.marked="keyword",I(F)):I(U("stat"),ne):"switch"==e?I(U("form"),J,B("{"),U("}","switch"),P,le,W,W,N):"case"==e?I(D,B(":")):"default"==e?I(B(":")):"catch"==e?I(U("form"),S,H,F,W,N):"export"==e?I(U("stat"),He,W):"import"==e?I(U("stat"),Ge,W):"async"==e?I(F):"@"==t?I(D,F):z(U("stat"),D,B(";"),W)}function H(e){if("("==e)return I(Pe,B(")"))}function D(e,t){return K(e,t,!1)}function G(e,t){return K(e,t,!0)}function J(e){return"("!=e?z():I(U(")"),L,B(")"),W)}function K(e,t,r){if(E.state.fatArrowAt==E.stream.start){var n=r?ee:Z;if("("==e)return I(S,U(")"),se(Pe,")"),W,B("=>"),n,N);if("variable"==e)return z(S,Ae,B("=>"),n,N)}var a,n=r?R:Q;return M.hasOwnProperty(e)?I(n):"function"==e?I(Oe,n):"class"==e||u&&"interface"==t?(E.marked="keyword",I(U("form"),Ne,W)):"keyword c"==e||"async"==e?I(r?G:D):"("==e?I(U(")"),L,B(")"),W,n):"operator"==e||"spread"==e?I(r?G:D):"["==e?I(U("]"),Re,W,n):"{"==e?ue(ie,"}",null,n):"quasi"==e?z(X,n):"new"==e?I((a=r,function(e){return"."==e?I(a?re:te):"variable"==e&&u?I(he,a?R:Q):z(a?G:D)})):"import"==e?I(D):I()}function L(e){return e.match(/[;\}\)\],]/)?z():z(D)}function Q(e,t){return","==e?I(L):R(e,t,!1)}function R(e,t,r){var n=0==r?Q:R,a=0==r?D:G;return"=>"==e?I(S,r?ee:Z,N):"operator"==e?/\+\+|--/.test(t)||u&&"!"==t?I(n):u&&"<"==t&&E.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/,!1)?I(U(">"),se(ve,">"),W,n):"?"==t?I(D,B(":"),a):I(a):"quasi"==e?z(X,n):";"!=e?"("==e?ue(G,")","call",n):"."==e?I(ae,n):"["==e?I(U("]"),L,B("]"),W,n):u&&"as"==t?(E.marked="keyword",I(ve,n)):"regexp"==e?(E.state.lastType=E.marked="operator",E.stream.backUp(E.stream.pos-E.stream.start-1),I(a)):void 0:void 0}function X(e,t){return"quasi"!=e?z():"${"!=t.slice(t.length-2)?I(X):I(D,Y)}function Y(e){if("}"==e)return E.marked="string-2",E.state.tokenize=h,I(X)}function Z(e){return j(E.stream,E.state),z("{"==e?F:D)}function ee(e){return j(E.stream,E.state),z("{"==e?F:G)}function te(e,t){if("target"==t)return E.marked="keyword",I(Q)}function re(e,t){if("target"==t)return E.marked="keyword",I(R)}function ne(e){return":"==e?I(W,F):z(Q,B(";"),W)}function ae(e){if("variable"==e)return E.marked="property",I()}function ie(e,t){return"async"==e?(E.marked="property",I(ie)):"variable"!=e&&"keyword"!=E.style?"number"==e||"string"==e?(E.marked=i?"property":E.style+" property",I(ce)):"jsonld-keyword"==e?I(ce):u&&C(t)?(E.marked="keyword",I(ie)):"["==e?I(D,fe,B("]"),ce):"spread"==e?I(G,ce):"*"==t?(E.marked="keyword",I(ie)):":"==e?z(ce):void 0:(E.marked="property","get"==t||"set"==t?I(oe):(u&&E.state.fatArrowAt==E.stream.start&&(r=E.stream.match(/^\s*:\s*/,!1))&&(E.state.fatArrowAt=E.stream.pos+r[0].length),I(ce)));var r}function oe(e){return"variable"!=e?z(ce):(E.marked="property",I(Oe))}function ce(e){return":"==e?I(G):"("==e?z(Oe):void 0}function se(n,a,i){function o(e,t){if(i?-1<i.indexOf(e):","==e){var r=E.state.lexical;return"call"==r.info&&(r.pos=(r.pos||0)+1),I(function(e,t){return e==a||t==a?z():z(n)},o)}return e==a||t==a?I():i&&-1<i.indexOf(";")?z(n):I(B(a))}return function(e,t){return e==a||t==a?I():z(n,o)}}function ue(e,t,r){for(var n=3;n<arguments.length;n++)E.cc.push(arguments[n]);return I(U(t,r),se(e,t),W)}function le(e){return"}"==e?I():z(F,le)}function fe(e,t){if(u)return":"==e?I(ve):"?"==t?I(fe):void 0}function de(e,t){if(u&&(":"==e||"in"==t))return I(ve)}function pe(e){if(u&&":"==e)return E.stream.match(/^\s*\w+\s+is\b/,!1)?I(D,me,ve):I(ve)}function me(e,t){if("is"==t)return E.marked="keyword",I()}function ve(e,t){return"keyof"==t||"typeof"==t||"infer"==t?(E.marked="keyword",I("typeof"==t?G:ve)):"variable"==e||"void"==t?(E.marked="type",I(xe)):"|"==t||"&"==t?I(ve):"string"==e||"number"==e||"atom"==e?I(xe):"["==e?I(U("]"),se(ve,"]",","),W,xe):"{"==e?I(U("}"),ye,W,xe):"("==e?I(se(be,")"),ke,xe):"<"==e?I(se(ve,">"),ve):void 0}function ke(e){if("=>"==e)return I(ve)}function ye(e){return e.match(/[\}\)\]]/)?I():","==e||";"==e?I(ye):z(we,ye)}function we(e,t){return"variable"==e||"keyword"==E.style?(E.marked="property",I(we)):"?"==t||"number"==e||"string"==e?I(we):":"==e?I(ve):"["==e?I(B("variable"),de,B("]"),we):"("==e?z(qe,we):e.match(/[;\}\)\],]/)?void 0:I()}function be(e,t){return"variable"==e&&E.stream.match(/^\s*[?:]/,!1)||"?"==t?I(be):":"==e?I(ve):"spread"==e?I(be):z(ve)}function xe(e,t){return"<"==t?I(U(">"),se(ve,">"),W,xe):"|"==t||"."==e||"&"==t?I(ve):"["==e?I(ve,B("]"),xe):"extends"==t||"implements"==t?(E.marked="keyword",I(ve)):"?"==t?I(ve,B(":"),ve):void 0}function he(e,t){if("<"==t)return I(U(">"),se(ve,">"),W,xe)}function ge(){return z(ve,je)}function je(e,t){if("="==t)return I(ve)}function Me(e,t){return"enum"==t?(E.marked="keyword",I(Xe)):z(Ae,fe,ze,Ie)}function Ae(e,t){return u&&C(t)?(E.marked="keyword",I(Ae)):"variable"==e?($(t),I()):"spread"==e?I(Ae):"["==e?ue(Ee,"]"):"{"==e?ue(Ve,"}"):void 0}function Ve(e,t){return"variable"!=e||E.stream.match(/^\s*:/,!1)?("variable"==e&&(E.marked="property"),"spread"==e?I(Ae):"}"==e?z():"["==e?I(D,B("]"),B(":"),Ve):I(B(":"),Ae,ze)):($(t),I(ze))}function Ee(){return z(Ae,ze)}function ze(e,t){if("="==t)return I(G)}function Ie(e){if(","==e)return I(Me)}function Te(e,t){if("keyword b"==e&&"else"==t)return I(U("form","else"),F,W)}function $e(e,t){return"await"==t?I($e):"("==e?I(U(")"),Ce,W):void 0}function Ce(e){return"var"==e?I(Me,_e):("variable"==e?I:z)(_e)}function _e(e,t){return")"==e?I():";"==e?I(_e):"in"==t||"of"==t?(E.marked="keyword",I(D,_e)):z(D,_e)}function Oe(e,t){return"*"==t?(E.marked="keyword",I(Oe)):"variable"==e?($(t),I(Oe)):"("==e?I(S,U(")"),se(Pe,")"),W,pe,F,N):u&&"<"==t?I(U(">"),se(ge,">"),W,Oe):void 0}function qe(e,t){return"*"==t?(E.marked="keyword",I(qe)):"variable"==e?($(t),I(qe)):"("==e?I(S,U(")"),se(Pe,")"),W,pe,N):u&&"<"==t?I(U(">"),se(ge,">"),W,qe):void 0}function Se(e,t){return"keyword"==e||"variable"==e?(E.marked="type",I(Se)):"<"==t?I(U(">"),se(ge,">"),W):void 0}function Pe(e,t){return"@"==t&&I(D,Pe),"spread"==e?I(Pe):u&&C(t)?(E.marked="keyword",I(Pe)):u&&"this"==e?I(fe,ze):z(Ae,fe,ze)}function Ne(e,t){return("variable"==e?Ue:We)(e,t)}function Ue(e,t){if("variable"==e)return $(t),I(We)}function We(e,t){return"<"==t?I(U(">"),se(ge,">"),W,We):"extends"==t||"implements"==t||u&&","==e?("implements"==t&&(E.marked="keyword"),I(u?ve:D,We)):"{"==e?I(U("}"),Be,W):void 0}function Be(e,t){return"async"==e||"variable"==e&&("static"==t||"get"==t||"set"==t||u&&C(t))&&E.stream.match(/^\s+[\w$\xa1-\uffff]/,!1)?(E.marked="keyword",I(Be)):"variable"==e||"keyword"==E.style?(E.marked="property",I(Fe,Be)):"number"==e||"string"==e?I(Fe,Be):"["==e?I(D,fe,B("]"),Fe,Be):"*"==t?(E.marked="keyword",I(Be)):u&&"("==e?z(qe,Be):";"==e||","==e?I(Be):"}"==e?I():"@"==t?I(D,Be):void 0}function Fe(e,t){if("?"==t)return I(Fe);if(":"==e)return I(ve,ze);if("="==t)return I(G);t=E.state.lexical.prev;return z(t&&"interface"==t.info?qe:Oe)}function He(e,t){return"*"==t?(E.marked="keyword",I(Qe,B(";"))):"default"==t?(E.marked="keyword",I(D,B(";"))):"{"==e?I(se(De,"}"),Qe,B(";")):z(F)}function De(e,t){return"as"==t?(E.marked="keyword",I(B("variable"))):"variable"==e?z(G,De):void 0}function Ge(e){return"string"==e?I():"("==e?z(D):z(Je,Ke,Qe)}function Je(e,t){return"{"==e?ue(Je,"}"):("variable"==e&&$(t),"*"==t&&(E.marked="keyword"),I(Le))}function Ke(e){if(","==e)return I(Je,Ke)}function Le(e,t){if("as"==t)return E.marked="keyword",I(Je)}function Qe(e,t){if("from"==t)return E.marked="keyword",I(D)}function Re(e){return"]"==e?I():z(se(G,"]"))}function Xe(){return z(U("form"),Ae,B("{"),U("}"),se(Ye,"}"),W,W)}function Ye(){return z(Ae,ze)}function Ze(e,t,r){return t.tokenize==b&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(t.lastType)||"quasi"==t.lastType&&/\{\s*$/.test(e.string.slice(0,e.pos-(r||0)))}return W.lex=N.lex=!0,{startState:function(e){e={tokenize:b,lastType:"sof",cc:[],lexical:new A((e||0)-f,0,"block",!1),localVars:l.localVars,context:l.localVars&&new _(null,null,!1),indented:e||0};return l.globalVars&&"object"==typeof l.globalVars&&(e.globalVars=l.globalVars),e},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation(),j(e,t)),t.tokenize!=x&&e.eatSpace())return null;var r=t.tokenize(e,t);return"comment"==m?r:(t.lastType="operator"!=m||"++"!=v&&"--"!=v?m:"incdec",V(t,r,m,v,e))},indent:function(e,t){if(e.tokenize==x||e.tokenize==h)return et.Pass;if(e.tokenize!=b)return 0;var r,n=t&&t.charAt(0),a=e.lexical;if(!/^\s*else\b/.test(t))for(var i=e.cc.length-1;0<=i;--i){var o=e.cc[i];if(o==W)a=a.prev;else if(o!=Te)break}for(;("stat"==a.type||"form"==a.type)&&("}"==n||(r=e.cc[e.cc.length-1])&&(r==Q||r==R)&&!/^[,\.=+\-*:?[\(]/.test(t));)a=a.prev;d&&")"==a.type&&"stat"==a.prev.type&&(a=a.prev);var c,s=a.type,u=n==s;return"vardef"==s?a.indented+("operator"==e.lastType||","==e.lastType?a.info.length+1:0):"form"==s&&"{"==n?a.indented:"form"==s?a.indented+f:"stat"==s?a.indented+(c=t,"operator"==(s=e).lastType||","==s.lastType||k.test(c.charAt(0))||/[,.]/.test(c.charAt(0))?d||f:0):"switch"!=a.info||u||0==l.doubleIndentSwitch?a.align?a.column+(u?0:1):a.indented+(u?0:f):a.indented+(/^(?:case|default)\b/.test(t)?f:2*f)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:o?null:"/*",blockCommentEnd:o?null:"*/",blockCommentContinue:o?null:" * ",lineComment:o?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:o?"json":"javascript",jsonldMode:i,jsonMode:o,expressionAllowed:Ze,skipExpression:function(e){var t=e.cc[e.cc.length-1];t!=D&&t!=G||e.cc.pop()}}}),et.registerHelper("wordChars","javascript",/[\w$]/),et.defineMIME("text/javascript","javascript"),et.defineMIME("text/ecmascript","javascript"),et.defineMIME("application/javascript","javascript"),et.defineMIME("application/x-javascript","javascript"),et.defineMIME("application/ecmascript","javascript"),et.defineMIME("application/json",{name:"javascript",json:!0}),et.defineMIME("application/x-json",{name:"javascript",json:!0}),et.defineMIME("application/manifest+json",{name:"javascript",json:!0}),et.defineMIME("application/ld+json",{name:"javascript",jsonld:!0}),et.defineMIME("text/typescript",{name:"javascript",typescript:!0}),et.defineMIME("application/typescript",{name:"javascript",typescript:!0})});
\ No newline at end of file
index 9489e39c3a48e3d3081bd59778dcc010cd823c88..094a63497cbd460aa0f0f7d427e3c5bb832a8e1d 100644 (file)
@@ -1,9807 +1 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// This is CodeMirror (https://codemirror.net), a code editor
-// implemented in JavaScript on top of the browser's DOM.
-//
-// You can find some technical background for some of the code below
-// at http://marijnhaverbeke.nl/blog/#cm-internals .
-
-(function (global, factory) {
-  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-  typeof define === 'function' && define.amd ? define(factory) :
-  (global.CodeMirror = factory());
-}(this, (function () { 'use strict';
-
-  // Kludges for bugs and behavior differences that can't be feature
-  // detected are enabled based on userAgent etc sniffing.
-  var userAgent = navigator.userAgent;
-  var platform = navigator.platform;
-
-  var gecko = /gecko\/\d/i.test(userAgent);
-  var ie_upto10 = /MSIE \d/.test(userAgent);
-  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
-  var edge = /Edge\/(\d+)/.exec(userAgent);
-  var ie = ie_upto10 || ie_11up || edge;
-  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
-  var webkit = !edge && /WebKit\//.test(userAgent);
-  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
-  var chrome = !edge && /Chrome\//.test(userAgent);
-  var presto = /Opera\//.test(userAgent);
-  var safari = /Apple Computer/.test(navigator.vendor);
-  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
-  var phantom = /PhantomJS/.test(userAgent);
-
-  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
-  var android = /Android/.test(userAgent);
-  // This is woefully incomplete. Suggestions for alternative methods welcome.
-  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
-  var mac = ios || /Mac/.test(platform);
-  var chromeOS = /\bCrOS\b/.test(userAgent);
-  var windows = /win/i.test(platform);
-
-  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
-  if (presto_version) { presto_version = Number(presto_version[1]); }
-  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
-  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
-  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
-  var captureRightClick = gecko || (ie && ie_version >= 9);
-
-  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
-
-  var rmClass = function(node, cls) {
-    var current = node.className;
-    var match = classTest(cls).exec(current);
-    if (match) {
-      var after = current.slice(match.index + match[0].length);
-      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
-    }
-  };
-
-  function removeChildren(e) {
-    for (var count = e.childNodes.length; count > 0; --count)
-      { e.removeChild(e.firstChild); }
-    return e
-  }
-
-  function removeChildrenAndAdd(parent, e) {
-    return removeChildren(parent).appendChild(e)
-  }
-
-  function elt(tag, content, className, style) {
-    var e = document.createElement(tag);
-    if (className) { e.className = className; }
-    if (style) { e.style.cssText = style; }
-    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
-    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
-    return e
-  }
-  // wrapper for elt, which removes the elt from the accessibility tree
-  function eltP(tag, content, className, style) {
-    var e = elt(tag, content, className, style);
-    e.setAttribute("role", "presentation");
-    return e
-  }
-
-  var range;
-  if (document.createRange) { range = function(node, start, end, endNode) {
-    var r = document.createRange();
-    r.setEnd(endNode || node, end);
-    r.setStart(node, start);
-    return r
-  }; }
-  else { range = function(node, start, end) {
-    var r = document.body.createTextRange();
-    try { r.moveToElementText(node.parentNode); }
-    catch(e) { return r }
-    r.collapse(true);
-    r.moveEnd("character", end);
-    r.moveStart("character", start);
-    return r
-  }; }
-
-  function contains(parent, child) {
-    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
-      { child = child.parentNode; }
-    if (parent.contains)
-      { return parent.contains(child) }
-    do {
-      if (child.nodeType == 11) { child = child.host; }
-      if (child == parent) { return true }
-    } while (child = child.parentNode)
-  }
-
-  function activeElt() {
-    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
-    // IE < 10 will throw when accessed while the page is loading or in an iframe.
-    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
-    var activeElement;
-    try {
-      activeElement = document.activeElement;
-    } catch(e) {
-      activeElement = document.body || null;
-    }
-    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
-      { activeElement = activeElement.shadowRoot.activeElement; }
-    return activeElement
-  }
-
-  function addClass(node, cls) {
-    var current = node.className;
-    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
-  }
-  function joinClasses(a, b) {
-    var as = a.split(" ");
-    for (var i = 0; i < as.length; i++)
-      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
-    return b
-  }
-
-  var selectInput = function(node) { node.select(); };
-  if (ios) // Mobile Safari apparently has a bug where select() is broken.
-    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
-  else if (ie) // Suppress mysterious IE10 errors
-    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
-
-  function bind(f) {
-    var args = Array.prototype.slice.call(arguments, 1);
-    return function(){return f.apply(null, args)}
-  }
-
-  function copyObj(obj, target, overwrite) {
-    if (!target) { target = {}; }
-    for (var prop in obj)
-      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
-        { target[prop] = obj[prop]; } }
-    return target
-  }
-
-  // Counts the column offset in a string, taking tabs into account.
-  // Used mostly to find indentation.
-  function countColumn(string, end, tabSize, startIndex, startValue) {
-    if (end == null) {
-      end = string.search(/[^\s\u00a0]/);
-      if (end == -1) { end = string.length; }
-    }
-    for (var i = startIndex || 0, n = startValue || 0;;) {
-      var nextTab = string.indexOf("\t", i);
-      if (nextTab < 0 || nextTab >= end)
-        { return n + (end - i) }
-      n += nextTab - i;
-      n += tabSize - (n % tabSize);
-      i = nextTab + 1;
-    }
-  }
-
-  var Delayed = function() {
-    this.id = null;
-    this.f = null;
-    this.time = 0;
-    this.handler = bind(this.onTimeout, this);
-  };
-  Delayed.prototype.onTimeout = function (self) {
-    self.id = 0;
-    if (self.time <= +new Date) {
-      self.f();
-    } else {
-      setTimeout(self.handler, self.time - +new Date);
-    }
-  };
-  Delayed.prototype.set = function (ms, f) {
-    this.f = f;
-    var time = +new Date + ms;
-    if (!this.id || time < this.time) {
-      clearTimeout(this.id);
-      this.id = setTimeout(this.handler, ms);
-      this.time = time;
-    }
-  };
-
-  function indexOf(array, elt) {
-    for (var i = 0; i < array.length; ++i)
-      { if (array[i] == elt) { return i } }
-    return -1
-  }
-
-  // Number of pixels added to scroller and sizer to hide scrollbar
-  var scrollerGap = 30;
-
-  // Returned or thrown by various protocols to signal 'I'm not
-  // handling this'.
-  var Pass = {toString: function(){return "CodeMirror.Pass"}};
-
-  // Reused option objects for setSelection & friends
-  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
-
-  // The inverse of countColumn -- find the offset that corresponds to
-  // a particular column.
-  function findColumn(string, goal, tabSize) {
-    for (var pos = 0, col = 0;;) {
-      var nextTab = string.indexOf("\t", pos);
-      if (nextTab == -1) { nextTab = string.length; }
-      var skipped = nextTab - pos;
-      if (nextTab == string.length || col + skipped >= goal)
-        { return pos + Math.min(skipped, goal - col) }
-      col += nextTab - pos;
-      col += tabSize - (col % tabSize);
-      pos = nextTab + 1;
-      if (col >= goal) { return pos }
-    }
-  }
-
-  var spaceStrs = [""];
-  function spaceStr(n) {
-    while (spaceStrs.length <= n)
-      { spaceStrs.push(lst(spaceStrs) + " "); }
-    return spaceStrs[n]
-  }
-
-  function lst(arr) { return arr[arr.length-1] }
-
-  function map(array, f) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
-    return out
-  }
-
-  function insertSorted(array, value, score) {
-    var pos = 0, priority = score(value);
-    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
-    array.splice(pos, 0, value);
-  }
-
-  function nothing() {}
-
-  function createObj(base, props) {
-    var inst;
-    if (Object.create) {
-      inst = Object.create(base);
-    } else {
-      nothing.prototype = base;
-      inst = new nothing();
-    }
-    if (props) { copyObj(props, inst); }
-    return inst
-  }
-
-  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  function isWordCharBasic(ch) {
-    return /\w/.test(ch) || ch > "\x80" &&
-      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
-  }
-  function isWordChar(ch, helper) {
-    if (!helper) { return isWordCharBasic(ch) }
-    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
-    return helper.test(ch)
-  }
-
-  function isEmpty(obj) {
-    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
-    return true
-  }
-
-  // Extending unicode characters. A series of a non-extending char +
-  // any number of extending chars is treated as a single unit as far
-  // as editing and measuring is concerned. This is not fully correct,
-  // since some scripts/fonts/browsers also treat other configurations
-  // of code points as a group.
-  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]/;
-  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
-
-  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
-  function skipExtendingChars(str, pos, dir) {
-    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
-    return pos
-  }
-
-  // Returns the value from the range [`from`; `to`] that satisfies
-  // `pred` and is closest to `from`. Assumes that at least `to`
-  // satisfies `pred`. Supports `from` being greater than `to`.
-  function findFirst(pred, from, to) {
-    // At any point we are certain `to` satisfies `pred`, don't know
-    // whether `from` does.
-    var dir = from > to ? -1 : 1;
-    for (;;) {
-      if (from == to) { return from }
-      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
-      if (mid == from) { return pred(mid) ? from : to }
-      if (pred(mid)) { to = mid; }
-      else { from = mid + dir; }
-    }
-  }
-
-  // BIDI HELPERS
-
-  function iterateBidiSections(order, from, to, f) {
-    if (!order) { return f(from, to, "ltr", 0) }
-    var found = false;
-    for (var i = 0; i < order.length; ++i) {
-      var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
-        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
-        found = true;
-      }
-    }
-    if (!found) { f(from, to, "ltr"); }
-  }
-
-  var bidiOther = null;
-  function getBidiPartAt(order, ch, sticky) {
-    var found;
-    bidiOther = null;
-    for (var i = 0; i < order.length; ++i) {
-      var cur = order[i];
-      if (cur.from < ch && cur.to > ch) { return i }
-      if (cur.to == ch) {
-        if (cur.from != cur.to && sticky == "before") { found = i; }
-        else { bidiOther = i; }
-      }
-      if (cur.from == ch) {
-        if (cur.from != cur.to && sticky != "before") { found = i; }
-        else { bidiOther = i; }
-      }
-    }
-    return found != null ? found : bidiOther
-  }
-
-  // Bidirectional ordering algorithm
-  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
-  // that this (partially) implements.
-
-  // One-char codes used for character types:
-  // L (L):   Left-to-Right
-  // R (R):   Right-to-Left
-  // r (AL):  Right-to-Left Arabic
-  // 1 (EN):  European Number
-  // + (ES):  European Number Separator
-  // % (ET):  European Number Terminator
-  // n (AN):  Arabic Number
-  // , (CS):  Common Number Separator
-  // m (NSM): Non-Spacing Mark
-  // b (BN):  Boundary Neutral
-  // s (B):   Paragraph Separator
-  // t (S):   Segment Separator
-  // w (WS):  Whitespace
-  // N (ON):  Other Neutrals
-
-  // Returns null if characters are ordered as they appear
-  // (left-to-right), or an array of sections ({from, to, level}
-  // objects) in the order in which they occur visually.
-  var bidiOrdering = (function() {
-    // Character types for codepoints 0 to 0xff
-    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
-    // Character types for codepoints 0x600 to 0x6f9
-    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
-    function charType(code) {
-      if (code <= 0xf7) { return lowTypes.charAt(code) }
-      else if (0x590 <= code && code <= 0x5f4) { return "R" }
-      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
-      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
-      else if (0x2000 <= code && code <= 0x200b) { return "w" }
-      else if (code == 0x200c) { return "b" }
-      else { return "L" }
-    }
-
-    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
-    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
-
-    function BidiSpan(level, from, to) {
-      this.level = level;
-      this.from = from; this.to = to;
-    }
-
-    return function(str, direction) {
-      var outerType = direction == "ltr" ? "L" : "R";
-
-      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
-      var len = str.length, types = [];
-      for (var i = 0; i < len; ++i)
-        { types.push(charType(str.charCodeAt(i))); }
-
-      // W1. Examine each non-spacing mark (NSM) in the level run, and
-      // change the type of the NSM to the type of the previous
-      // character. If the NSM is at the start of the level run, it will
-      // get the type of sor.
-      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
-        var type = types[i$1];
-        if (type == "m") { types[i$1] = prev; }
-        else { prev = type; }
-      }
-
-      // W2. Search backwards from each instance of a European number
-      // until the first strong type (R, L, AL, or sor) is found. If an
-      // AL is found, change the type of the European number to Arabic
-      // number.
-      // W3. Change all ALs to R.
-      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
-        var type$1 = types[i$2];
-        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
-        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
-      }
-
-      // W4. A single European separator between two European numbers
-      // changes to a European number. A single common separator between
-      // two numbers of the same type changes to that type.
-      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
-        var type$2 = types[i$3];
-        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
-        else if (type$2 == "," && prev$1 == types[i$3+1] &&
-                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
-        prev$1 = type$2;
-      }
-
-      // W5. A sequence of European terminators adjacent to European
-      // numbers changes to all European numbers.
-      // W6. Otherwise, separators and terminators change to Other
-      // Neutral.
-      for (var i$4 = 0; i$4 < len; ++i$4) {
-        var type$3 = types[i$4];
-        if (type$3 == ",") { types[i$4] = "N"; }
-        else if (type$3 == "%") {
-          var end = (void 0);
-          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
-          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
-          for (var j = i$4; j < end; ++j) { types[j] = replace; }
-          i$4 = end - 1;
-        }
-      }
-
-      // W7. Search backwards from each instance of a European number
-      // until the first strong type (R, L, or sor) is found. If an L is
-      // found, then change the type of the European number to L.
-      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
-        var type$4 = types[i$5];
-        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
-        else if (isStrong.test(type$4)) { cur$1 = type$4; }
-      }
-
-      // N1. A sequence of neutrals takes the direction of the
-      // surrounding strong text if the text on both sides has the same
-      // direction. European and Arabic numbers act as if they were R in
-      // terms of their influence on neutrals. Start-of-level-run (sor)
-      // and end-of-level-run (eor) are used at level run boundaries.
-      // N2. Any remaining neutrals take the embedding direction.
-      for (var i$6 = 0; i$6 < len; ++i$6) {
-        if (isNeutral.test(types[i$6])) {
-          var end$1 = (void 0);
-          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
-          var before = (i$6 ? types[i$6-1] : outerType) == "L";
-          var after = (end$1 < len ? types[end$1] : outerType) == "L";
-          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
-          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
-          i$6 = end$1 - 1;
-        }
-      }
-
-      // Here we depart from the documented algorithm, in order to avoid
-      // building up an actual levels array. Since there are only three
-      // levels (0, 1, 2) in an implementation that doesn't take
-      // explicit embedding into account, we can build up the order on
-      // the fly, without following the level-based algorithm.
-      var order = [], m;
-      for (var i$7 = 0; i$7 < len;) {
-        if (countsAsLeft.test(types[i$7])) {
-          var start = i$7;
-          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
-          order.push(new BidiSpan(0, start, i$7));
-        } else {
-          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
-          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
-          for (var j$2 = pos; j$2 < i$7;) {
-            if (countsAsNum.test(types[j$2])) {
-              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
-              var nstart = j$2;
-              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
-              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
-              at += isRTL;
-              pos = j$2;
-            } else { ++j$2; }
-          }
-          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
-        }
-      }
-      if (direction == "ltr") {
-        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
-          order[0].from = m[0].length;
-          order.unshift(new BidiSpan(0, 0, m[0].length));
-        }
-        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
-          lst(order).to -= m[0].length;
-          order.push(new BidiSpan(0, len - m[0].length, len));
-        }
-      }
-
-      return direction == "rtl" ? order.reverse() : order
-    }
-  })();
-
-  // Get the bidi ordering for the given line (and cache it). Returns
-  // false for lines that are fully left-to-right, and an array of
-  // BidiSpan objects otherwise.
-  function getOrder(line, direction) {
-    var order = line.order;
-    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
-    return order
-  }
-
-  // EVENT HANDLING
-
-  // Lightweight event framework. on/off also work on DOM nodes,
-  // registering native DOM handlers.
-
-  var noHandlers = [];
-
-  var on = function(emitter, type, f) {
-    if (emitter.addEventListener) {
-      emitter.addEventListener(type, f, false);
-    } else if (emitter.attachEvent) {
-      emitter.attachEvent("on" + type, f);
-    } else {
-      var map$$1 = emitter._handlers || (emitter._handlers = {});
-      map$$1[type] = (map$$1[type] || noHandlers).concat(f);
-    }
-  };
-
-  function getHandlers(emitter, type) {
-    return emitter._handlers && emitter._handlers[type] || noHandlers
-  }
-
-  function off(emitter, type, f) {
-    if (emitter.removeEventListener) {
-      emitter.removeEventListener(type, f, false);
-    } else if (emitter.detachEvent) {
-      emitter.detachEvent("on" + type, f);
-    } else {
-      var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
-      if (arr) {
-        var index = indexOf(arr, f);
-        if (index > -1)
-          { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
-      }
-    }
-  }
-
-  function signal(emitter, type /*, values...*/) {
-    var handlers = getHandlers(emitter, type);
-    if (!handlers.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2);
-    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
-  }
-
-  // The DOM events that CodeMirror handles can be overridden by
-  // registering a (non-DOM) handler on the editor for the event name,
-  // and preventDefault-ing the event in that handler.
-  function signalDOMEvent(cm, e, override) {
-    if (typeof e == "string")
-      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore
-  }
-
-  function signalCursorActivity(cm) {
-    var arr = cm._handlers && cm._handlers.cursorActivity;
-    if (!arr) { return }
-    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
-    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
-      { set.push(arr[i]); } }
-  }
-
-  function hasHandler(emitter, type) {
-    return getHandlers(emitter, type).length > 0
-  }
-
-  // Add on and off methods to a constructor's prototype, to make
-  // registering events on such objects more convenient.
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
-  // Due to the fact that we still support jurassic IE versions, some
-  // compatibility wrappers are needed.
-
-  function e_preventDefault(e) {
-    if (e.preventDefault) { e.preventDefault(); }
-    else { e.returnValue = false; }
-  }
-  function e_stopPropagation(e) {
-    if (e.stopPropagation) { e.stopPropagation(); }
-    else { e.cancelBubble = true; }
-  }
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
-  }
-  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
-
-  function e_target(e) {return e.target || e.srcElement}
-  function e_button(e) {
-    var b = e.which;
-    if (b == null) {
-      if (e.button & 1) { b = 1; }
-      else if (e.button & 2) { b = 3; }
-      else if (e.button & 4) { b = 2; }
-    }
-    if (mac && e.ctrlKey && b == 1) { b = 3; }
-    return b
-  }
-
-  // Detect drag-and-drop
-  var dragAndDrop = function() {
-    // There is *some* kind of drag-and-drop support in IE6-8, but I
-    // couldn't get it to work yet.
-    if (ie && ie_version < 9) { return false }
-    var div = elt('div');
-    return "draggable" in div || "dragDrop" in div
-  }();
-
-  var zwspSupported;
-  function zeroWidthElement(measure) {
-    if (zwspSupported == null) {
-      var test = elt("span", "\u200b");
-      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
-      if (measure.firstChild.offsetHeight != 0)
-        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
-    }
-    var node = zwspSupported ? elt("span", "\u200b") :
-      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
-    node.setAttribute("cm-text", "");
-    return node
-  }
-
-  // Feature-detect IE's crummy client rect reporting for bidi text
-  var badBidiRects;
-  function hasBadBidiRects(measure) {
-    if (badBidiRects != null) { return badBidiRects }
-    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
-    var r0 = range(txt, 0, 1).getBoundingClientRect();
-    var r1 = range(txt, 1, 2).getBoundingClientRect();
-    removeChildren(measure);
-    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
-    return badBidiRects = (r1.right - r0.right < 3)
-  }
-
-  // See if "".split is the broken IE version, if so, provide an
-  // alternative way to split lines.
-  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
-    var pos = 0, result = [], l = string.length;
-    while (pos <= l) {
-      var nl = string.indexOf("\n", pos);
-      if (nl == -1) { nl = string.length; }
-      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
-      var rt = line.indexOf("\r");
-      if (rt != -1) {
-        result.push(line.slice(0, rt));
-        pos += rt + 1;
-      } else {
-        result.push(line);
-        pos = nl + 1;
-      }
-    }
-    return result
-  } : function (string) { return string.split(/\r\n?|\n/); };
-
-  var hasSelection = window.getSelection ? function (te) {
-    try { return te.selectionStart != te.selectionEnd }
-    catch(e) { return false }
-  } : function (te) {
-    var range$$1;
-    try {range$$1 = te.ownerDocument.selection.createRange();}
-    catch(e) {}
-    if (!range$$1 || range$$1.parentElement() != te) { return false }
-    return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
-  };
-
-  var hasCopyEvent = (function () {
-    var e = elt("div");
-    if ("oncopy" in e) { return true }
-    e.setAttribute("oncopy", "return;");
-    return typeof e.oncopy == "function"
-  })();
-
-  var badZoomedRects = null;
-  function hasBadZoomedRects(measure) {
-    if (badZoomedRects != null) { return badZoomedRects }
-    var node = removeChildrenAndAdd(measure, elt("span", "x"));
-    var normal = node.getBoundingClientRect();
-    var fromRange = range(node, 0, 1).getBoundingClientRect();
-    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
-  }
-
-  // Known modes, by name and by MIME
-  var modes = {}, mimeModes = {};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  function defineMode(name, mode) {
-    if (arguments.length > 2)
-      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
-    modes[name] = mode;
-  }
-
-  function defineMIME(mime, spec) {
-    mimeModes[mime] = spec;
-  }
-
-  // Given a MIME type, a {name, ...options} config object, or a name
-  // string, return a mode config object.
-  function resolveMode(spec) {
-    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
-      spec = mimeModes[spec];
-    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
-      var found = mimeModes[spec.name];
-      if (typeof found == "string") { found = {name: found}; }
-      spec = createObj(found, spec);
-      spec.name = found.name;
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
-      return resolveMode("application/xml")
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
-      return resolveMode("application/json")
-    }
-    if (typeof spec == "string") { return {name: spec} }
-    else { return spec || {name: "null"} }
-  }
-
-  // Given a mode spec (anything that resolveMode accepts), find and
-  // initialize an actual mode object.
-  function getMode(options, spec) {
-    spec = resolveMode(spec);
-    var mfactory = modes[spec.name];
-    if (!mfactory) { return getMode(options, "text/plain") }
-    var modeObj = mfactory(options, spec);
-    if (modeExtensions.hasOwnProperty(spec.name)) {
-      var exts = modeExtensions[spec.name];
-      for (var prop in exts) {
-        if (!exts.hasOwnProperty(prop)) { continue }
-        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
-        modeObj[prop] = exts[prop];
-      }
-    }
-    modeObj.name = spec.name;
-    if (spec.helperType) { modeObj.helperType = spec.helperType; }
-    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
-      { modeObj[prop$1] = spec.modeProps[prop$1]; } }
-
-    return modeObj
-  }
-
-  // This can be used to attach properties to mode objects from
-  // outside the actual mode definition.
-  var modeExtensions = {};
-  function extendMode(mode, properties) {
-    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
-    copyObj(properties, exts);
-  }
-
-  function copyState(mode, state) {
-    if (state === true) { return state }
-    if (mode.copyState) { return mode.copyState(state) }
-    var nstate = {};
-    for (var n in state) {
-      var val = state[n];
-      if (val instanceof Array) { val = val.concat([]); }
-      nstate[n] = val;
-    }
-    return nstate
-  }
-
-  // Given a mode and a state (for that mode), find the inner mode and
-  // state at the position that the state refers to.
-  function innerMode(mode, state) {
-    var info;
-    while (mode.innerMode) {
-      info = mode.innerMode(state);
-      if (!info || info.mode == mode) { break }
-      state = info.state;
-      mode = info.mode;
-    }
-    return info || {mode: mode, state: state}
-  }
-
-  function startState(mode, a1, a2) {
-    return mode.startState ? mode.startState(a1, a2) : true
-  }
-
-  // STRING STREAM
-
-  // Fed to the mode parsers, provides helper functions to make
-  // parsers more succinct.
-
-  var StringStream = function(string, tabSize, lineOracle) {
-    this.pos = this.start = 0;
-    this.string = string;
-    this.tabSize = tabSize || 8;
-    this.lastColumnPos = this.lastColumnValue = 0;
-    this.lineStart = 0;
-    this.lineOracle = lineOracle;
-  };
-
-  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
-  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
-  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
-  StringStream.prototype.next = function () {
-    if (this.pos < this.string.length)
-      { return this.string.charAt(this.pos++) }
-  };
-  StringStream.prototype.eat = function (match) {
-    var ch = this.string.charAt(this.pos);
-    var ok;
-    if (typeof match == "string") { ok = ch == match; }
-    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
-    if (ok) {++this.pos; return ch}
-  };
-  StringStream.prototype.eatWhile = function (match) {
-    var start = this.pos;
-    while (this.eat(match)){}
-    return this.pos > start
-  };
-  StringStream.prototype.eatSpace = function () {
-      var this$1 = this;
-
-    var start = this.pos;
-    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
-    return this.pos > start
-  };
-  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
-  StringStream.prototype.skipTo = function (ch) {
-    var found = this.string.indexOf(ch, this.pos);
-    if (found > -1) {this.pos = found; return true}
-  };
-  StringStream.prototype.backUp = function (n) {this.pos -= n;};
-  StringStream.prototype.column = function () {
-    if (this.lastColumnPos < this.start) {
-      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
-      this.lastColumnPos = this.start;
-    }
-    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.indentation = function () {
-    return countColumn(this.string, null, this.tabSize) -
-      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
-    if (typeof pattern == "string") {
-      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
-      var substr = this.string.substr(this.pos, pattern.length);
-      if (cased(substr) == cased(pattern)) {
-        if (consume !== false) { this.pos += pattern.length; }
-        return true
-      }
-    } else {
-      var match = this.string.slice(this.pos).match(pattern);
-      if (match && match.index > 0) { return null }
-      if (match && consume !== false) { this.pos += match[0].length; }
-      return match
-    }
-  };
-  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
-  StringStream.prototype.hideFirstChars = function (n, inner) {
-    this.lineStart += n;
-    try { return inner() }
-    finally { this.lineStart -= n; }
-  };
-  StringStream.prototype.lookAhead = function (n) {
-    var oracle = this.lineOracle;
-    return oracle && oracle.lookAhead(n)
-  };
-  StringStream.prototype.baseToken = function () {
-    var oracle = this.lineOracle;
-    return oracle && oracle.baseToken(this.pos)
-  };
-
-  // Find the line object corresponding to the given line number.
-  function getLine(doc, n) {
-    n -= doc.first;
-    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
-    var chunk = doc;
-    while (!chunk.lines) {
-      for (var i = 0;; ++i) {
-        var child = chunk.children[i], sz = child.chunkSize();
-        if (n < sz) { chunk = child; break }
-        n -= sz;
-      }
-    }
-    return chunk.lines[n]
-  }
-
-  // Get the part of a document between two positions, as an array of
-  // strings.
-  function getBetween(doc, start, end) {
-    var out = [], n = start.line;
-    doc.iter(start.line, end.line + 1, function (line) {
-      var text = line.text;
-      if (n == end.line) { text = text.slice(0, end.ch); }
-      if (n == start.line) { text = text.slice(start.ch); }
-      out.push(text);
-      ++n;
-    });
-    return out
-  }
-  // Get the lines between from and to, as array of strings.
-  function getLines(doc, from, to) {
-    var out = [];
-    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
-    return out
-  }
-
-  // Update the height of a line, propagating the height change
-  // upwards to parent nodes.
-  function updateLineHeight(line, height) {
-    var diff = height - line.height;
-    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
-  }
-
-  // Given a line object, find its line number by walking up through
-  // its parent links.
-  function lineNo(line) {
-    if (line.parent == null) { return null }
-    var cur = line.parent, no = indexOf(cur.lines, line);
-    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
-      for (var i = 0;; ++i) {
-        if (chunk.children[i] == cur) { break }
-        no += chunk.children[i].chunkSize();
-      }
-    }
-    return no + cur.first
-  }
-
-  // Find the line at the given vertical position, using the height
-  // information in the document tree.
-  function lineAtHeight(chunk, h) {
-    var n = chunk.first;
-    outer: do {
-      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
-        var child = chunk.children[i$1], ch = child.height;
-        if (h < ch) { chunk = child; continue outer }
-        h -= ch;
-        n += child.chunkSize();
-      }
-      return n
-    } while (!chunk.lines)
-    var i = 0;
-    for (; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i], lh = line.height;
-      if (h < lh) { break }
-      h -= lh;
-    }
-    return n + i
-  }
-
-  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
-
-  function lineNumberFor(options, i) {
-    return String(options.lineNumberFormatter(i + options.firstLineNumber))
-  }
-
-  // A Pos instance represents a position within the text.
-  function Pos(line, ch, sticky) {
-    if ( sticky === void 0 ) sticky = null;
-
-    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
-    this.line = line;
-    this.ch = ch;
-    this.sticky = sticky;
-  }
-
-  // Compare two positions, return 0 if they are the same, a negative
-  // number when a is less, and a positive number otherwise.
-  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
-
-  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
-
-  function copyPos(x) {return Pos(x.line, x.ch)}
-  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
-  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
-
-  // Most of the external API clips given positions to make sure they
-  // actually exist within the document.
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
-  function clipPos(doc, pos) {
-    if (pos.line < doc.first) { return Pos(doc.first, 0) }
-    var last = doc.first + doc.size - 1;
-    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
-    return clipToLen(pos, getLine(doc, pos.line).text.length)
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
-    else if (ch < 0) { return Pos(pos.line, 0) }
-    else { return pos }
-  }
-  function clipPosArray(doc, array) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
-    return out
-  }
-
-  var SavedContext = function(state, lookAhead) {
-    this.state = state;
-    this.lookAhead = lookAhead;
-  };
-
-  var Context = function(doc, state, line, lookAhead) {
-    this.state = state;
-    this.doc = doc;
-    this.line = line;
-    this.maxLookAhead = lookAhead || 0;
-    this.baseTokens = null;
-    this.baseTokenPos = 1;
-  };
-
-  Context.prototype.lookAhead = function (n) {
-    var line = this.doc.getLine(this.line + n);
-    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
-    return line
-  };
-
-  Context.prototype.baseToken = function (n) {
-      var this$1 = this;
-
-    if (!this.baseTokens) { return null }
-    while (this.baseTokens[this.baseTokenPos] <= n)
-      { this$1.baseTokenPos += 2; }
-    var type = this.baseTokens[this.baseTokenPos + 1];
-    return {type: type && type.replace(/( |^)overlay .*/, ""),
-            size: this.baseTokens[this.baseTokenPos] - n}
-  };
-
-  Context.prototype.nextLine = function () {
-    this.line++;
-    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
-  };
-
-  Context.fromSaved = function (doc, saved, line) {
-    if (saved instanceof SavedContext)
-      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
-    else
-      { return new Context(doc, copyState(doc.mode, saved), line) }
-  };
-
-  Context.prototype.save = function (copy) {
-    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
-    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
-  };
-
-
-  // Compute a style array (an array starting with a mode generation
-  // -- for invalidation -- followed by pairs of end positions and
-  // style strings), which is used to highlight the tokens on the
-  // line.
-  function highlightLine(cm, line, context, forceToEnd) {
-    // A styles array always starts with a number identifying the
-    // mode/overlays that it is based on (for easy invalidation).
-    var st = [cm.state.modeGen], lineClasses = {};
-    // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
-            lineClasses, forceToEnd);
-    var state = context.state;
-
-    // Run overlays, adjust style array.
-    var loop = function ( o ) {
-      context.baseTokens = st;
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      context.state = true;
-      runMode(cm, line.text, overlay.mode, context, function (end, style) {
-        var start = i;
-        // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            { st.splice(i, 1, end, st[i+1], i_end); }
-          i += 2;
-          at = Math.min(end, i_end);
-        }
-        if (!style) { return }
-        if (overlay.opaque) {
-          st.splice(start, i - start, end, "overlay " + style);
-          i = start + 2;
-        } else {
-          for (; start < i; start += 2) {
-            var cur = st[start+1];
-            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
-          }
-        }
-      }, lineClasses);
-      context.state = state;
-      context.baseTokens = null;
-      context.baseTokenPos = 1;
-    };
-
-    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
-
-    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
-  }
-
-  function getLineStyles(cm, line, updateFrontier) {
-    if (!line.styles || line.styles[0] != cm.state.modeGen) {
-      var context = getContextBefore(cm, lineNo(line));
-      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
-      var result = highlightLine(cm, line, context);
-      if (resetState) { context.state = resetState; }
-      line.stateAfter = context.save(!resetState);
-      line.styles = result.styles;
-      if (result.classes) { line.styleClasses = result.classes; }
-      else if (line.styleClasses) { line.styleClasses = null; }
-      if (updateFrontier === cm.doc.highlightFrontier)
-        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
-    }
-    return line.styles
-  }
-
-  function getContextBefore(cm, n, precise) {
-    var doc = cm.doc, display = cm.display;
-    if (!doc.mode.startState) { return new Context(doc, true, n) }
-    var start = findStartLine(cm, n, precise);
-    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
-    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
-
-    doc.iter(start, n, function (line) {
-      processLine(cm, line.text, context);
-      var pos = context.line;
-      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
-      context.nextLine();
-    });
-    if (precise) { doc.modeFrontier = context.line; }
-    return context
-  }
-
-  // Lightweight form of highlight -- proceed over this line and
-  // update state, but don't save a style array. Used for lines that
-  // aren't currently visible.
-  function processLine(cm, text, context, startAt) {
-    var mode = cm.doc.mode;
-    var stream = new StringStream(text, cm.options.tabSize, context);
-    stream.start = stream.pos = startAt || 0;
-    if (text == "") { callBlankLine(mode, context.state); }
-    while (!stream.eol()) {
-      readToken(mode, stream, context.state);
-      stream.start = stream.pos;
-    }
-  }
-
-  function callBlankLine(mode, state) {
-    if (mode.blankLine) { return mode.blankLine(state) }
-    if (!mode.innerMode) { return }
-    var inner = innerMode(mode, state);
-    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
-  }
-
-  function readToken(mode, stream, state, inner) {
-    for (var i = 0; i < 10; i++) {
-      if (inner) { inner[0] = innerMode(mode, state).mode; }
-      var style = mode.token(stream, state);
-      if (stream.pos > stream.start) { return style }
-    }
-    throw new Error("Mode " + mode.name + " failed to advance stream.")
-  }
-
-  var Token = function(stream, type, state) {
-    this.start = stream.start; this.end = stream.pos;
-    this.string = stream.current();
-    this.type = type || null;
-    this.state = state;
-  };
-
-  // Utility for getTokenAt and getLineTokens
-  function takeToken(cm, pos, precise, asArray) {
-    var doc = cm.doc, mode = doc.mode, style;
-    pos = clipPos(doc, pos);
-    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
-    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
-    if (asArray) { tokens = []; }
-    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
-      stream.start = stream.pos;
-      style = readToken(mode, stream, context.state);
-      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
-    }
-    return asArray ? tokens : new Token(stream, style, context.state)
-  }
-
-  function extractLineClasses(type, output) {
-    if (type) { for (;;) {
-      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
-      if (!lineClass) { break }
-      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? "bgClass" : "textClass";
-      if (output[prop] == null)
-        { output[prop] = lineClass[2]; }
-      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
-        { output[prop] += " " + lineClass[2]; }
-    } }
-    return type
-  }
-
-  // Run the given mode's parser over a line, calling f for each token.
-  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
-    var flattenSpans = mode.flattenSpans;
-    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
-    var curStart = 0, curStyle = null;
-    var stream = new StringStream(text, cm.options.tabSize, context), style;
-    var inner = cm.options.addModeClass && [null];
-    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
-    while (!stream.eol()) {
-      if (stream.pos > cm.options.maxHighlightLength) {
-        flattenSpans = false;
-        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
-        stream.pos = text.length;
-        style = null;
-      } else {
-        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
-      }
-      if (inner) {
-        var mName = inner[0].name;
-        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
-      }
-      if (!flattenSpans || curStyle != style) {
-        while (curStart < stream.start) {
-          curStart = Math.min(stream.start, curStart + 5000);
-          f(curStart, curStyle);
-        }
-        curStyle = style;
-      }
-      stream.start = stream.pos;
-    }
-    while (curStart < stream.pos) {
-      // Webkit seems to refuse to render text nodes longer than 57444
-      // characters, and returns inaccurate measurements in nodes
-      // starting around 5000 chars.
-      var pos = Math.min(stream.pos, curStart + 5000);
-      f(pos, curStyle);
-      curStart = pos;
-    }
-  }
-
-  // Finds the line to start with when starting a parse. Tries to
-  // find a line with a stateAfter, so that it can start with a
-  // valid state. If that fails, it returns the line with the
-  // smallest indentation, which tends to need the least context to
-  // parse correctly.
-  function findStartLine(cm, n, precise) {
-    var minindent, minline, doc = cm.doc;
-    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
-    for (var search = n; search > lim; --search) {
-      if (search <= doc.first) { return doc.first }
-      var line = getLine(doc, search - 1), after = line.stateAfter;
-      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
-        { return search }
-      var indented = countColumn(line.text, null, cm.options.tabSize);
-      if (minline == null || minindent > indented) {
-        minline = search - 1;
-        minindent = indented;
-      }
-    }
-    return minline
-  }
-
-  function retreatFrontier(doc, n) {
-    doc.modeFrontier = Math.min(doc.modeFrontier, n);
-    if (doc.highlightFrontier < n - 10) { return }
-    var start = doc.first;
-    for (var line = n - 1; line > start; line--) {
-      var saved = getLine(doc, line).stateAfter;
-      // change is on 3
-      // state on line 1 looked ahead 2 -- so saw 3
-      // test 1 + 2 < 3 should cover this
-      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
-        start = line + 1;
-        break
-      }
-    }
-    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
-  }
-
-  // Optimize some code when these features are not used.
-  var sawReadOnlySpans = false, sawCollapsedSpans = false;
-
-  function seeReadOnlySpans() {
-    sawReadOnlySpans = true;
-  }
-
-  function seeCollapsedSpans() {
-    sawCollapsedSpans = true;
-  }
-
-  // TEXTMARKER SPANS
-
-  function MarkedSpan(marker, from, to) {
-    this.marker = marker;
-    this.from = from; this.to = to;
-  }
-
-  // Search an array of spans for a span matching the given marker.
-  function getMarkedSpanFor(spans, marker) {
-    if (spans) { for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.marker == marker) { return span }
-    } }
-  }
-  // Remove a span from an array, returning undefined if no spans are
-  // left (we don't store arrays for lines without spans).
-  function removeMarkedSpan(spans, span) {
-    var r;
-    for (var i = 0; i < spans.length; ++i)
-      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
-    return r
-  }
-  // Add a span to a line.
-  function addMarkedSpan(line, span) {
-    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
-    span.marker.attachLine(line);
-  }
-
-  // Used for the algorithm that adjusts markers for a change in the
-  // document. These functions cut an array of spans at a given
-  // character position, returning an array of remaining chunks (or
-  // undefined if nothing remains).
-  function markedSpansBefore(old, startCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
-      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
-        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
-      }
-    } }
-    return nw
-  }
-  function markedSpansAfter(old, endCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
-      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
-        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
-                                              span.to == null ? null : span.to - endCh));
-      }
-    } }
-    return nw
-  }
-
-  // Given a change object, compute the new set of marker spans that
-  // cover the line in which the change took place. Removes spans
-  // entirely within the change, reconnects spans belonging to the
-  // same marker that appear on both sides of the change, and cuts off
-  // spans partially within the change. Returns an array of span
-  // arrays with one element for each line in (after) the change.
-  function stretchSpansOverChange(doc, change) {
-    if (change.full) { return null }
-    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
-    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
-    if (!oldFirst && !oldLast) { return null }
-
-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
-    // Get the spans that 'stick out' on both sides
-    var first = markedSpansBefore(oldFirst, startCh, isInsert);
-    var last = markedSpansAfter(oldLast, endCh, isInsert);
-
-    // Next, merge those two ends
-    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
-    if (first) {
-      // Fix up .to properties of first
-      for (var i = 0; i < first.length; ++i) {
-        var span = first[i];
-        if (span.to == null) {
-          var found = getMarkedSpanFor(last, span.marker);
-          if (!found) { span.to = startCh; }
-          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
-        }
-      }
-    }
-    if (last) {
-      // Fix up .from in last (or move them into first in case of sameLine)
-      for (var i$1 = 0; i$1 < last.length; ++i$1) {
-        var span$1 = last[i$1];
-        if (span$1.to != null) { span$1.to += offset; }
-        if (span$1.from == null) {
-          var found$1 = getMarkedSpanFor(first, span$1.marker);
-          if (!found$1) {
-            span$1.from = offset;
-            if (sameLine) { (first || (first = [])).push(span$1); }
-          }
-        } else {
-          span$1.from += offset;
-          if (sameLine) { (first || (first = [])).push(span$1); }
-        }
-      }
-    }
-    // Make sure we didn't create any zero-length spans
-    if (first) { first = clearEmptySpans(first); }
-    if (last && last != first) { last = clearEmptySpans(last); }
-
-    var newMarkers = [first];
-    if (!sameLine) {
-      // Fill gap with whole-line-spans
-      var gap = change.text.length - 2, gapMarkers;
-      if (gap > 0 && first)
-        { for (var i$2 = 0; i$2 < first.length; ++i$2)
-          { if (first[i$2].to == null)
-            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
-      for (var i$3 = 0; i$3 < gap; ++i$3)
-        { newMarkers.push(gapMarkers); }
-      newMarkers.push(last);
-    }
-    return newMarkers
-  }
-
-  // Remove spans that are empty and don't have a clearWhenEmpty
-  // option of false.
-  function clearEmptySpans(spans) {
-    for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
-        { spans.splice(i--, 1); }
-    }
-    if (!spans.length) { return null }
-    return spans
-  }
-
-  // Used to 'clip' out readOnly ranges when making a change.
-  function removeReadOnlyRanges(doc, from, to) {
-    var markers = null;
-    doc.iter(from.line, to.line + 1, function (line) {
-      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-        var mark = line.markedSpans[i].marker;
-        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
-          { (markers || (markers = [])).push(mark); }
-      } }
-    });
-    if (!markers) { return null }
-    var parts = [{from: from, to: to}];
-    for (var i = 0; i < markers.length; ++i) {
-      var mk = markers[i], m = mk.find(0);
-      for (var j = 0; j < parts.length; ++j) {
-        var p = parts[j];
-        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
-        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
-        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
-          { newParts.push({from: p.from, to: m.from}); }
-        if (dto > 0 || !mk.inclusiveRight && !dto)
-          { newParts.push({from: m.to, to: p.to}); }
-        parts.splice.apply(parts, newParts);
-        j += newParts.length - 3;
-      }
-    }
-    return parts
-  }
-
-  // Connect or disconnect spans from a line.
-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.detachLine(line); }
-    line.markedSpans = null;
-  }
-  function attachMarkedSpans(line, spans) {
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.attachLine(line); }
-    line.markedSpans = spans;
-  }
-
-  // Helpers used when computing which overlapping collapsed span
-  // counts as the larger one.
-  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
-  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
-
-  // Returns a number indicating which of two overlapping collapsed
-  // spans is larger (and thus includes the other). Falls back to
-  // comparing ids when the spans cover exactly the same range.
-  function compareCollapsedMarkers(a, b) {
-    var lenDiff = a.lines.length - b.lines.length;
-    if (lenDiff != 0) { return lenDiff }
-    var aPos = a.find(), bPos = b.find();
-    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
-    if (fromCmp) { return -fromCmp }
-    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
-    if (toCmp) { return toCmp }
-    return b.id - a.id
-  }
-
-  // Find out whether a line ends or starts in a collapsed span. If
-  // so, return the marker for that span.
-  function collapsedSpanAtSide(line, start) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
-        { found = sp.marker; }
-    } }
-    return found
-  }
-  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
-
-  function collapsedSpanAround(line, ch) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
-    } }
-    return found
-  }
-
-  // Test whether there exists a collapsed span that partially
-  // overlaps (covers the start or end, but not both) of a new span.
-  // Such overlap is not allowed.
-  function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
-    var line = getLine(doc, lineNo$$1);
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      var found = sp.marker.find(0);
-      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
-      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
-      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
-      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
-          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
-        { return true }
-    } }
-  }
-
-  // A visual line is a line as drawn on the screen. Folding, for
-  // example, can cause multiple logical lines to appear on the same
-  // visual line. This finds the start of the visual line that the
-  // given line is part of (usually that is the line itself).
-  function visualLine(line) {
-    var merged;
-    while (merged = collapsedSpanAtStart(line))
-      { line = merged.find(-1, true).line; }
-    return line
-  }
-
-  function visualLineEnd(line) {
-    var merged;
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return line
-  }
-
-  // Returns an array of logical lines that continue the visual line
-  // started by the argument, or undefined if there are no such lines.
-  function visualLineContinued(line) {
-    var merged, lines;
-    while (merged = collapsedSpanAtEnd(line)) {
-      line = merged.find(1, true).line
-      ;(lines || (lines = [])).push(line);
-    }
-    return lines
-  }
-
-  // Get the line number of the start of the visual line that the
-  // given line number is part of.
-  function visualLineNo(doc, lineN) {
-    var line = getLine(doc, lineN), vis = visualLine(line);
-    if (line == vis) { return lineN }
-    return lineNo(vis)
-  }
-
-  // Get the line number of the start of the next visual line after
-  // the given line.
-  function visualLineEndNo(doc, lineN) {
-    if (lineN > doc.lastLine()) { return lineN }
-    var line = getLine(doc, lineN), merged;
-    if (!lineIsHidden(doc, line)) { return lineN }
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return lineNo(line) + 1
-  }
-
-  // Compute whether a line is hidden. Lines count as hidden when they
-  // are part of a visual line that starts with another line, or when
-  // they are entirely covered by collapsed, non-widget span.
-  function lineIsHidden(doc, line) {
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      if (sp.from == null) { return true }
-      if (sp.marker.widgetNode) { continue }
-      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
-        { return true }
-    } }
-  }
-  function lineIsHiddenInner(doc, line, span) {
-    if (span.to == null) {
-      var end = span.marker.find(1, true);
-      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
-    }
-    if (span.marker.inclusiveRight && span.to == line.text.length)
-      { return true }
-    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
-      sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
-          (sp.to == null || sp.to != span.from) &&
-          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
-          lineIsHiddenInner(doc, line, sp)) { return true }
-    }
-  }
-
-  // Find the height above the given line.
-  function heightAtLine(lineObj) {
-    lineObj = visualLine(lineObj);
-
-    var h = 0, chunk = lineObj.parent;
-    for (var i = 0; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i];
-      if (line == lineObj) { break }
-      else { h += line.height; }
-    }
-    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
-      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
-        var cur = p.children[i$1];
-        if (cur == chunk) { break }
-        else { h += cur.height; }
-      }
-    }
-    return h
-  }
-
-  // Compute the character length of a line, taking into account
-  // collapsed ranges (see markText) that might hide parts, and join
-  // other lines onto it.
-  function lineLength(line) {
-    if (line.height == 0) { return 0 }
-    var len = line.text.length, merged, cur = line;
-    while (merged = collapsedSpanAtStart(cur)) {
-      var found = merged.find(0, true);
-      cur = found.from.line;
-      len += found.from.ch - found.to.ch;
-    }
-    cur = line;
-    while (merged = collapsedSpanAtEnd(cur)) {
-      var found$1 = merged.find(0, true);
-      len -= cur.text.length - found$1.from.ch;
-      cur = found$1.to.line;
-      len += cur.text.length - found$1.to.ch;
-    }
-    return len
-  }
-
-  // Find the longest line in the document.
-  function findMaxLine(cm) {
-    var d = cm.display, doc = cm.doc;
-    d.maxLine = getLine(doc, doc.first);
-    d.maxLineLength = lineLength(d.maxLine);
-    d.maxLineChanged = true;
-    doc.iter(function (line) {
-      var len = lineLength(line);
-      if (len > d.maxLineLength) {
-        d.maxLineLength = len;
-        d.maxLine = line;
-      }
-    });
-  }
-
-  // LINE DATA STRUCTURE
-
-  // Line objects. These hold state related to a line, including
-  // highlighting info (the styles array).
-  var Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-
-  Line.prototype.lineNo = function () { return lineNo(this) };
-  eventMixin(Line);
-
-  // Change the content (text, markers) of a line. Automatically
-  // invalidates cached information and tries to re-estimate the
-  // line's height.
-  function updateLine(line, text, markedSpans, estimateHeight) {
-    line.text = text;
-    if (line.stateAfter) { line.stateAfter = null; }
-    if (line.styles) { line.styles = null; }
-    if (line.order != null) { line.order = null; }
-    detachMarkedSpans(line);
-    attachMarkedSpans(line, markedSpans);
-    var estHeight = estimateHeight ? estimateHeight(line) : 1;
-    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-  }
-
-  // Detach a line from the document tree and its markers.
-  function cleanUpLine(line) {
-    line.parent = null;
-    detachMarkedSpans(line);
-  }
-
-  // Convert a style as returned by a mode (either null, or a string
-  // containing one or more styles) to a CSS style. This is cached,
-  // and also looks for line-wide styles.
-  var styleToClassCache = {}, styleToClassCacheWithMode = {};
-  function interpretTokenStyle(style, options) {
-    if (!style || /^\s*$/.test(style)) { return null }
-    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
-    return cache[style] ||
-      (cache[style] = style.replace(/\S+/g, "cm-$&"))
-  }
-
-  // Render the DOM representation of the text of a line. Also builds
-  // up a 'line map', which points at the DOM nodes that represent
-  // specific stretches of text, and is used by the measuring code.
-  // The returned object contains the DOM node, this map, and
-  // information about line-wide styles that were set by the mode.
-  function buildLineContent(cm, lineView) {
-    // The padding-right forces the element to have a 'border', which
-    // is needed on Webkit to be able to get line-level bounding
-    // rectangles for it (in measureChar).
-    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
-    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
-                   col: 0, pos: 0, cm: cm,
-                   trailingSpace: false,
-                   splitSpaces: cm.getOption("lineWrapping")};
-    lineView.measure = {};
-
-    // Iterate over the logical lines that make up this visual line.
-    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
-      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
-      builder.pos = 0;
-      builder.addToken = buildToken;
-      // Optionally wire in some hacks into the token-rendering
-      // algorithm, to deal with browser quirks.
-      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
-        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
-      builder.map = [];
-      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
-      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
-      if (line.styleClasses) {
-        if (line.styleClasses.bgClass)
-          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
-        if (line.styleClasses.textClass)
-          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
-      }
-
-      // Ensure at least a single node is present, for measuring.
-      if (builder.map.length == 0)
-        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
-
-      // Store the map and a cache object for the current logical line
-      if (i == 0) {
-        lineView.measure.map = builder.map;
-        lineView.measure.cache = {};
-      } else {
-  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
-        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
-      }
-    }
-
-    // See issue #2901
-    if (webkit) {
-      var last = builder.content.lastChild;
-      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
-        { builder.content.className = "cm-tab-wrap-hack"; }
-    }
-
-    signal(cm, "renderLine", cm, lineView.line, builder.pre);
-    if (builder.pre.className)
-      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
-
-    return builder
-  }
-
-  function defaultSpecialCharPlaceholder(ch) {
-    var token = elt("span", "\u2022", "cm-invalidchar");
-    token.title = "\\u" + ch.charCodeAt(0).toString(16);
-    token.setAttribute("aria-label", token.title);
-    return token
-  }
-
-  // Build up the DOM representation for a single token, and add it to
-  // the line map. Takes care to render special characters separately.
-  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
-    if (!text) { return }
-    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
-    var special = builder.cm.state.specialChars, mustWrap = false;
-    var content;
-    if (!special.test(text)) {
-      builder.col += text.length;
-      content = document.createTextNode(displayText);
-      builder.map.push(builder.pos, builder.pos + text.length, content);
-      if (ie && ie_version < 9) { mustWrap = true; }
-      builder.pos += text.length;
-    } else {
-      content = document.createDocumentFragment();
-      var pos = 0;
-      while (true) {
-        special.lastIndex = pos;
-        var m = special.exec(text);
-        var skipped = m ? m.index - pos : text.length - pos;
-        if (skipped) {
-          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
-          else { content.appendChild(txt); }
-          builder.map.push(builder.pos, builder.pos + skipped, txt);
-          builder.col += skipped;
-          builder.pos += skipped;
-        }
-        if (!m) { break }
-        pos += skipped + 1;
-        var txt$1 = (void 0);
-        if (m[0] == "\t") {
-          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
-          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
-          txt$1.setAttribute("role", "presentation");
-          txt$1.setAttribute("cm-text", "\t");
-          builder.col += tabWidth;
-        } else if (m[0] == "\r" || m[0] == "\n") {
-          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
-          txt$1.setAttribute("cm-text", m[0]);
-          builder.col += 1;
-        } else {
-          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
-          txt$1.setAttribute("cm-text", m[0]);
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
-          else { content.appendChild(txt$1); }
-          builder.col += 1;
-        }
-        builder.map.push(builder.pos, builder.pos + 1, txt$1);
-        builder.pos++;
-      }
-    }
-    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
-    if (style || startStyle || endStyle || mustWrap || css) {
-      var fullStyle = style || "";
-      if (startStyle) { fullStyle += startStyle; }
-      if (endStyle) { fullStyle += endStyle; }
-      var token = elt("span", [content], fullStyle, css);
-      if (attributes) {
-        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
-          { token.setAttribute(attr, attributes[attr]); } }
-      }
-      return builder.content.appendChild(token)
-    }
-    builder.content.appendChild(content);
-  }
-
-  // Change some spaces to NBSP to prevent the browser from collapsing
-  // trailing spaces at the end of a line when rendering text (issue #1362).
-  function splitSpaces(text, trailingBefore) {
-    if (text.length > 1 && !/  /.test(text)) { return text }
-    var spaceBefore = trailingBefore, result = "";
-    for (var i = 0; i < text.length; i++) {
-      var ch = text.charAt(i);
-      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
-        { ch = "\u00a0"; }
-      result += ch;
-      spaceBefore = ch == " ";
-    }
-    return result
-  }
-
-  // Work around nonsense dimensions being reported for stretches of
-  // right-to-left text.
-  function buildTokenBadBidi(inner, order) {
-    return function (builder, text, style, startStyle, endStyle, css, attributes) {
-      style = style ? style + " cm-force-border" : "cm-force-border";
-      var start = builder.pos, end = start + text.length;
-      for (;;) {
-        // Find the part that overlaps with the start of this text
-        var part = (void 0);
-        for (var i = 0; i < order.length; i++) {
-          part = order[i];
-          if (part.to > start && part.from <= start) { break }
-        }
-        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
-        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
-        startStyle = null;
-        text = text.slice(part.to - start);
-        start = part.to;
-      }
-    }
-  }
-
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.widgetNode;
-    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
-    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
-      if (!widget)
-        { widget = builder.content.appendChild(document.createElement("span")); }
-      widget.setAttribute("cm-marker", marker.id);
-    }
-    if (widget) {
-      builder.cm.display.input.setUneditable(widget);
-      builder.content.appendChild(widget);
-    }
-    builder.pos += size;
-    builder.trailingSpace = false;
-  }
-
-  // Outputs a number of spans to make up a line, taking highlighting
-  // and marked text into account.
-  function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
-    if (!spans) {
-      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
-        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
-      return
-    }
-
-    var len = allText.length, pos = 0, i = 1, text = "", style, css;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
-    for (;;) {
-      if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = css = "";
-        attributes = null;
-        collapsed = null; nextChange = Infinity;
-        var foundBookmarks = [], endStyles = (void 0);
-        for (var j = 0; j < spans.length; ++j) {
-          var sp = spans[j], m = sp.marker;
-          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
-            foundBookmarks.push(m);
-          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
-            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
-              nextChange = sp.to;
-              spanEndStyle = "";
-            }
-            if (m.className) { spanStyle += " " + m.className; }
-            if (m.css) { css = (css ? css + ";" : "") + m.css; }
-            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
-            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
-            // support for the old title property
-            // https://github.com/codemirror/CodeMirror/pull/5673
-            if (m.title) { (attributes || (attributes = {})).title = m.title; }
-            if (m.attributes) {
-              for (var attr in m.attributes)
-                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
-            }
-            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
-              { collapsed = sp; }
-          } else if (sp.from > pos && nextChange > sp.from) {
-            nextChange = sp.from;
-          }
-        }
-        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
-          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
-
-        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
-          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
-        if (collapsed && (collapsed.from || 0) == pos) {
-          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
-                             collapsed.marker, collapsed.from == null);
-          if (collapsed.to == null) { return }
-          if (collapsed.to == pos) { collapsed = false; }
-        }
-      }
-      if (pos >= len) { break }
-
-      var upto = Math.min(len, nextChange);
-      while (true) {
-        if (text) {
-          var end = pos + text.length;
-          if (!collapsed) {
-            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
-            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
-          }
-          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
-          pos = end;
-          spanStartStyle = "";
-        }
-        text = allText.slice(at, at = styles[i++]);
-        style = interpretTokenStyle(styles[i++], builder.cm.options);
-      }
-    }
-  }
-
-
-  // These objects are used to represent the visible (currently drawn)
-  // part of the document. A LineView may correspond to multiple
-  // logical lines, if those are connected by collapsed ranges.
-  function LineView(doc, line, lineN) {
-    // The starting line
-    this.line = line;
-    // Continuing lines, if any
-    this.rest = visualLineContinued(line);
-    // Number of logical lines in this visual line
-    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
-    this.node = this.text = null;
-    this.hidden = lineIsHidden(doc, line);
-  }
-
-  // Create a range of LineView objects for the given lines.
-  function buildViewArray(cm, from, to) {
-    var array = [], nextPos;
-    for (var pos = from; pos < to; pos = nextPos) {
-      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
-      nextPos = pos + view.size;
-      array.push(view);
-    }
-    return array
-  }
-
-  var operationGroup = null;
-
-  function pushOperation(op) {
-    if (operationGroup) {
-      operationGroup.ops.push(op);
-    } else {
-      op.ownsGroup = operationGroup = {
-        ops: [op],
-        delayedCallbacks: []
-      };
-    }
-  }
-
-  function fireCallbacksForOps(group) {
-    // Calls delayed callbacks and cursorActivity handlers until no
-    // new ones appear
-    var callbacks = group.delayedCallbacks, i = 0;
-    do {
-      for (; i < callbacks.length; i++)
-        { callbacks[i].call(null); }
-      for (var j = 0; j < group.ops.length; j++) {
-        var op = group.ops[j];
-        if (op.cursorActivityHandlers)
-          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
-            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
-      }
-    } while (i < callbacks.length)
-  }
-
-  function finishOperation(op, endCb) {
-    var group = op.ownsGroup;
-    if (!group) { return }
-
-    try { fireCallbacksForOps(group); }
-    finally {
-      operationGroup = null;
-      endCb(group);
-    }
-  }
-
-  var orphanDelayedCallbacks = null;
-
-  // Often, we want to signal events at a point where we are in the
-  // middle of some work, but don't want the handler to start calling
-  // other methods on the editor, which might be in an inconsistent
-  // state or simply not expect any other events to happen.
-  // signalLater looks whether there are any handlers, and schedules
-  // them to be executed when the last operation ends, or, if no
-  // operation is active, when a timeout fires.
-  function signalLater(emitter, type /*, values...*/) {
-    var arr = getHandlers(emitter, type);
-    if (!arr.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2), list;
-    if (operationGroup) {
-      list = operationGroup.delayedCallbacks;
-    } else if (orphanDelayedCallbacks) {
-      list = orphanDelayedCallbacks;
-    } else {
-      list = orphanDelayedCallbacks = [];
-      setTimeout(fireOrphanDelayed, 0);
-    }
-    var loop = function ( i ) {
-      list.push(function () { return arr[i].apply(null, args); });
-    };
-
-    for (var i = 0; i < arr.length; ++i)
-      loop( i );
-  }
-
-  function fireOrphanDelayed() {
-    var delayed = orphanDelayedCallbacks;
-    orphanDelayedCallbacks = null;
-    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
-  }
-
-  // When an aspect of a line changes, a string is added to
-  // lineView.changes. This updates the relevant part of the line's
-  // DOM structure.
-  function updateLineForChanges(cm, lineView, lineN, dims) {
-    for (var j = 0; j < lineView.changes.length; j++) {
-      var type = lineView.changes[j];
-      if (type == "text") { updateLineText(cm, lineView); }
-      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
-      else if (type == "class") { updateLineClasses(cm, lineView); }
-      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
-    }
-    lineView.changes = null;
-  }
-
-  // Lines with gutter elements, widgets or a background class need to
-  // be wrapped, and have the extra elements added to the wrapper div
-  function ensureLineWrapped(lineView) {
-    if (lineView.node == lineView.text) {
-      lineView.node = elt("div", null, null, "position: relative");
-      if (lineView.text.parentNode)
-        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
-      lineView.node.appendChild(lineView.text);
-      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
-    }
-    return lineView.node
-  }
-
-  function updateLineBackground(cm, lineView) {
-    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
-    if (cls) { cls += " CodeMirror-linebackground"; }
-    if (lineView.background) {
-      if (cls) { lineView.background.className = cls; }
-      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
-    } else if (cls) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
-      cm.display.input.setUneditable(lineView.background);
-    }
-  }
-
-  // Wrapper around buildLineContent which will reuse the structure
-  // in display.externalMeasured when possible.
-  function getLineContent(cm, lineView) {
-    var ext = cm.display.externalMeasured;
-    if (ext && ext.line == lineView.line) {
-      cm.display.externalMeasured = null;
-      lineView.measure = ext.measure;
-      return ext.built
-    }
-    return buildLineContent(cm, lineView)
-  }
-
-  // Redraw the line's text. Interacts with the background and text
-  // classes because the mode may output tokens that influence these
-  // classes.
-  function updateLineText(cm, lineView) {
-    var cls = lineView.text.className;
-    var built = getLineContent(cm, lineView);
-    if (lineView.text == lineView.node) { lineView.node = built.pre; }
-    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
-    lineView.text = built.pre;
-    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
-      lineView.bgClass = built.bgClass;
-      lineView.textClass = built.textClass;
-      updateLineClasses(cm, lineView);
-    } else if (cls) {
-      lineView.text.className = cls;
-    }
-  }
-
-  function updateLineClasses(cm, lineView) {
-    updateLineBackground(cm, lineView);
-    if (lineView.line.wrapClass)
-      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
-    else if (lineView.node != lineView.text)
-      { lineView.node.className = ""; }
-    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
-    lineView.text.className = textClass || "";
-  }
-
-  function updateLineGutter(cm, lineView, lineN, dims) {
-    if (lineView.gutter) {
-      lineView.node.removeChild(lineView.gutter);
-      lineView.gutter = null;
-    }
-    if (lineView.gutterBackground) {
-      lineView.node.removeChild(lineView.gutterBackground);
-      lineView.gutterBackground = null;
-    }
-    if (lineView.line.gutterClass) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
-                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(lineView.gutterBackground);
-      wrap.insertBefore(lineView.gutterBackground, lineView.text);
-    }
-    var markers = lineView.line.gutterMarkers;
-    if (cm.options.lineNumbers || markers) {
-      var wrap$1 = ensureLineWrapped(lineView);
-      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(gutterWrap);
-      wrap$1.insertBefore(gutterWrap, lineView.text);
-      if (lineView.line.gutterClass)
-        { gutterWrap.className += " " + lineView.line.gutterClass; }
-      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
-        { lineView.lineNumber = gutterWrap.appendChild(
-          elt("div", lineNumberFor(cm.options, lineN),
-              "CodeMirror-linenumber CodeMirror-gutter-elt",
-              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
-      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
-        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
-        if (found)
-          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
-                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
-      } }
-    }
-  }
-
-  function updateLineWidgets(cm, lineView, dims) {
-    if (lineView.alignable) { lineView.alignable = null; }
-    var isWidget = classTest("CodeMirror-linewidget");
-    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
-      next = node.nextSibling;
-      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
-    }
-    insertLineWidgets(cm, lineView, dims);
-  }
-
-  // Build a line's DOM representation from scratch
-  function buildLineElement(cm, lineView, lineN, dims) {
-    var built = getLineContent(cm, lineView);
-    lineView.text = lineView.node = built.pre;
-    if (built.bgClass) { lineView.bgClass = built.bgClass; }
-    if (built.textClass) { lineView.textClass = built.textClass; }
-
-    updateLineClasses(cm, lineView);
-    updateLineGutter(cm, lineView, lineN, dims);
-    insertLineWidgets(cm, lineView, dims);
-    return lineView.node
-  }
-
-  // A lineView may contain multiple logical lines (when merged by
-  // collapsed spans). The widgets for all of them need to be drawn.
-  function insertLineWidgets(cm, lineView, dims) {
-    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
-    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
-  }
-
-  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
-    if (!line.widgets) { return }
-    var wrap = ensureLineWrapped(lineView);
-    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
-      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
-      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
-      positionLineWidget(widget, node, lineView, dims);
-      cm.display.input.setUneditable(node);
-      if (allowAbove && widget.above)
-        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
-      else
-        { wrap.appendChild(node); }
-      signalLater(widget, "redraw");
-    }
-  }
-
-  function positionLineWidget(widget, node, lineView, dims) {
-    if (widget.noHScroll) {
-  (lineView.alignable || (lineView.alignable = [])).push(node);
-      var width = dims.wrapperWidth;
-      node.style.left = dims.fixedPos + "px";
-      if (!widget.coverGutter) {
-        width -= dims.gutterTotalWidth;
-        node.style.paddingLeft = dims.gutterTotalWidth + "px";
-      }
-      node.style.width = width + "px";
-    }
-    if (widget.coverGutter) {
-      node.style.zIndex = 5;
-      node.style.position = "relative";
-      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
-    }
-  }
-
-  function widgetHeight(widget) {
-    if (widget.height != null) { return widget.height }
-    var cm = widget.doc.cm;
-    if (!cm) { return 0 }
-    if (!contains(document.body, widget.node)) {
-      var parentStyle = "position: relative;";
-      if (widget.coverGutter)
-        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
-      if (widget.noHScroll)
-        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
-      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
-    }
-    return widget.height = widget.node.parentNode.offsetHeight
-  }
-
-  // Return true when the given mouse event happened in a widget
-  function eventInWidget(display, e) {
-    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
-          (n.parentNode == display.sizer && n != display.mover))
-        { return true }
-    }
-  }
-
-  // POSITION MEASUREMENT
-
-  function paddingTop(display) {return display.lineSpace.offsetTop}
-  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
-  function paddingH(display) {
-    if (display.cachedPaddingH) { return display.cachedPaddingH }
-    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
-    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
-    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
-    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
-    return data
-  }
-
-  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
-  function displayWidth(cm) {
-    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
-  }
-  function displayHeight(cm) {
-    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
-  }
-
-  // Ensure the lineView.wrapping.heights array is populated. This is
-  // an array of bottom offsets for the lines that make up a drawn
-  // line. When lineWrapping is on, there might be more than one
-  // height.
-  function ensureLineHeights(cm, lineView, rect) {
-    var wrapping = cm.options.lineWrapping;
-    var curWidth = wrapping && displayWidth(cm);
-    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
-      var heights = lineView.measure.heights = [];
-      if (wrapping) {
-        lineView.measure.width = curWidth;
-        var rects = lineView.text.firstChild.getClientRects();
-        for (var i = 0; i < rects.length - 1; i++) {
-          var cur = rects[i], next = rects[i + 1];
-          if (Math.abs(cur.bottom - next.bottom) > 2)
-            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
-        }
-      }
-      heights.push(rect.bottom - rect.top);
-    }
-  }
-
-  // Find a line map (mapping character offsets to text nodes) and a
-  // measurement cache for the given line number. (A line view might
-  // contain multiple lines when collapsed ranges are present.)
-  function mapFromLineView(lineView, line, lineN) {
-    if (lineView.line == line)
-      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
-    for (var i = 0; i < lineView.rest.length; i++)
-      { if (lineView.rest[i] == line)
-        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
-    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
-      { if (lineNo(lineView.rest[i$1]) > lineN)
-        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
-  }
-
-  // Render a line into the hidden node display.externalMeasured. Used
-  // when measurement is needed for a line that's not in the viewport.
-  function updateExternalMeasurement(cm, line) {
-    line = visualLine(line);
-    var lineN = lineNo(line);
-    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
-    view.lineN = lineN;
-    var built = view.built = buildLineContent(cm, view);
-    view.text = built.pre;
-    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
-    return view
-  }
-
-  // Get a {top, bottom, left, right} box (in line-local coordinates)
-  // for a given character.
-  function measureChar(cm, line, ch, bias) {
-    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
-  }
-
-  // Find a line view that corresponds to the given line number.
-  function findViewForLine(cm, lineN) {
-    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
-      { return cm.display.view[findViewIndex(cm, lineN)] }
-    var ext = cm.display.externalMeasured;
-    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
-      { return ext }
-  }
-
-  // Measurement can be split in two steps, the set-up work that
-  // applies to the whole line, and the measurement of the actual
-  // character. Functions like coordsChar, that need to do a lot of
-  // measurements in a row, can thus ensure that the set-up work is
-  // only done once.
-  function prepareMeasureForLine(cm, line) {
-    var lineN = lineNo(line);
-    var view = findViewForLine(cm, lineN);
-    if (view && !view.text) {
-      view = null;
-    } else if (view && view.changes) {
-      updateLineForChanges(cm, view, lineN, getDimensions(cm));
-      cm.curOp.forceUpdate = true;
-    }
-    if (!view)
-      { view = updateExternalMeasurement(cm, line); }
-
-    var info = mapFromLineView(view, line, lineN);
-    return {
-      line: line, view: view, rect: null,
-      map: info.map, cache: info.cache, before: info.before,
-      hasHeights: false
-    }
-  }
-
-  // Given a prepared measurement object, measures the position of an
-  // actual character (or fetches it from the cache).
-  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
-    if (prepared.before) { ch = -1; }
-    var key = ch + (bias || ""), found;
-    if (prepared.cache.hasOwnProperty(key)) {
-      found = prepared.cache[key];
-    } else {
-      if (!prepared.rect)
-        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
-      if (!prepared.hasHeights) {
-        ensureLineHeights(cm, prepared.view, prepared.rect);
-        prepared.hasHeights = true;
-      }
-      found = measureCharInner(cm, prepared, ch, bias);
-      if (!found.bogus) { prepared.cache[key] = found; }
-    }
-    return {left: found.left, right: found.right,
-            top: varHeight ? found.rtop : found.top,
-            bottom: varHeight ? found.rbottom : found.bottom}
-  }
-
-  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
-
-  function nodeAndOffsetInLineMap(map$$1, ch, bias) {
-    var node, start, end, collapse, mStart, mEnd;
-    // First, search the line map for the text node corresponding to,
-    // or closest to, the target character.
-    for (var i = 0; i < map$$1.length; i += 3) {
-      mStart = map$$1[i];
-      mEnd = map$$1[i + 1];
-      if (ch < mStart) {
-        start = 0; end = 1;
-        collapse = "left";
-      } else if (ch < mEnd) {
-        start = ch - mStart;
-        end = start + 1;
-      } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
-        end = mEnd - mStart;
-        start = end - 1;
-        if (ch >= mEnd) { collapse = "right"; }
-      }
-      if (start != null) {
-        node = map$$1[i + 2];
-        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
-          { collapse = bias; }
-        if (bias == "left" && start == 0)
-          { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
-            node = map$$1[(i -= 3) + 2];
-            collapse = "left";
-          } }
-        if (bias == "right" && start == mEnd - mStart)
-          { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
-            node = map$$1[(i += 3) + 2];
-            collapse = "right";
-          } }
-        break
-      }
-    }
-    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
-  }
-
-  function getUsefulRect(rects, bias) {
-    var rect = nullRect;
-    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
-      if ((rect = rects[i]).left != rect.right) { break }
-    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
-      if ((rect = rects[i$1]).left != rect.right) { break }
-    } }
-    return rect
-  }
-
-  function measureCharInner(cm, prepared, ch, bias) {
-    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
-    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
-
-    var rect;
-    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
-      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
-        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
-        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
-        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
-          { rect = node.parentNode.getBoundingClientRect(); }
-        else
-          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
-        if (rect.left || rect.right || start == 0) { break }
-        end = start;
-        start = start - 1;
-        collapse = "right";
-      }
-      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
-    } else { // If it is a widget, simply get the box for the whole widget.
-      if (start > 0) { collapse = bias = "right"; }
-      var rects;
-      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
-        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
-      else
-        { rect = node.getBoundingClientRect(); }
-    }
-    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
-      var rSpan = node.parentNode.getClientRects()[0];
-      if (rSpan)
-        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
-      else
-        { rect = nullRect; }
-    }
-
-    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
-    var mid = (rtop + rbot) / 2;
-    var heights = prepared.view.measure.heights;
-    var i = 0;
-    for (; i < heights.length - 1; i++)
-      { if (mid < heights[i]) { break } }
-    var top = i ? heights[i - 1] : 0, bot = heights[i];
-    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
-                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
-                  top: top, bottom: bot};
-    if (!rect.left && !rect.right) { result.bogus = true; }
-    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
-
-    return result
-  }
-
-  // Work around problem with bounding client rects on ranges being
-  // returned incorrectly when zoomed on IE10 and below.
-  function maybeUpdateRectForZooming(measure, rect) {
-    if (!window.screen || screen.logicalXDPI == null ||
-        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
-      { return rect }
-    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
-    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
-    return {left: rect.left * scaleX, right: rect.right * scaleX,
-            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
-  }
-
-  function clearLineMeasurementCacheFor(lineView) {
-    if (lineView.measure) {
-      lineView.measure.cache = {};
-      lineView.measure.heights = null;
-      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-        { lineView.measure.caches[i] = {}; } }
-    }
-  }
-
-  function clearLineMeasurementCache(cm) {
-    cm.display.externalMeasure = null;
-    removeChildren(cm.display.lineMeasure);
-    for (var i = 0; i < cm.display.view.length; i++)
-      { clearLineMeasurementCacheFor(cm.display.view[i]); }
-  }
-
-  function clearCaches(cm) {
-    clearLineMeasurementCache(cm);
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
-    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
-    cm.display.lineNumChars = null;
-  }
-
-  function pageScrollX() {
-    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
-    // which causes page_Offset and bounding client rects to use
-    // different reference viewports and invalidate our calculations.
-    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
-    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
-  }
-  function pageScrollY() {
-    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
-    return window.pageYOffset || (document.documentElement || document.body).scrollTop
-  }
-
-  function widgetTopHeight(lineObj) {
-    var height = 0;
-    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
-      { height += widgetHeight(lineObj.widgets[i]); } } }
-    return height
-  }
-
-  // Converts a {top, bottom, left, right} box from line-local
-  // coordinates into another coordinate system. Context may be one of
-  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
-  // or "page".
-  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
-    if (!includeWidgets) {
-      var height = widgetTopHeight(lineObj);
-      rect.top += height; rect.bottom += height;
-    }
-    if (context == "line") { return rect }
-    if (!context) { context = "local"; }
-    var yOff = heightAtLine(lineObj);
-    if (context == "local") { yOff += paddingTop(cm.display); }
-    else { yOff -= cm.display.viewOffset; }
-    if (context == "page" || context == "window") {
-      var lOff = cm.display.lineSpace.getBoundingClientRect();
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
-      rect.left += xOff; rect.right += xOff;
-    }
-    rect.top += yOff; rect.bottom += yOff;
-    return rect
-  }
-
-  // Coverts a box from "div" coords to another coordinate system.
-  // Context may be "window", "page", "div", or "local"./null.
-  function fromCoordSystem(cm, coords, context) {
-    if (context == "div") { return coords }
-    var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
-    if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = cm.display.sizer.getBoundingClientRect();
-      left += localBox.left;
-      top += localBox.top;
-    }
-
-    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
-  }
-
-  function charCoords(cm, pos, context, lineObj, bias) {
-    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
-  }
-
-  // Returns a box for a given cursor position, which may have an
-  // 'other' property containing the position of the secondary cursor
-  // on a bidi boundary.
-  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
-  // and after `char - 1` in writing order of `char - 1`
-  // A cursor Pos(line, char, "after") is on the same visual line as `char`
-  // and before `char` in writing order of `char`
-  // Examples (upper-case letters are RTL, lower-case are LTR):
-  //     Pos(0, 1, ...)
-  //     before   after
-  // ab     a|b     a|b
-  // aB     a|B     aB|
-  // Ab     |Ab     A|b
-  // AB     B|A     B|A
-  // Every position after the last character on a line is considered to stick
-  // to the last character on the line.
-  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
-    lineObj = lineObj || getLine(cm.doc, pos.line);
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    function get(ch, right) {
-      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
-      if (right) { m.left = m.right; } else { m.right = m.left; }
-      return intoCoordSystem(cm, lineObj, m, context)
-    }
-    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
-    if (ch >= lineObj.text.length) {
-      ch = lineObj.text.length;
-      sticky = "before";
-    } else if (ch <= 0) {
-      ch = 0;
-      sticky = "after";
-    }
-    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
-
-    function getBidi(ch, partPos, invert) {
-      var part = order[partPos], right = part.level == 1;
-      return get(invert ? ch - 1 : ch, right != invert)
-    }
-    var partPos = getBidiPartAt(order, ch, sticky);
-    var other = bidiOther;
-    var val = getBidi(ch, partPos, sticky == "before");
-    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
-    return val
-  }
-
-  // Used to cheaply estimate the coordinates for a position. Used for
-  // intermediate scroll updates.
-  function estimateCoords(cm, pos) {
-    var left = 0;
-    pos = clipPos(cm.doc, pos);
-    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
-    var lineObj = getLine(cm.doc, pos.line);
-    var top = heightAtLine(lineObj) + paddingTop(cm.display);
-    return {left: left, right: left, top: top, bottom: top + lineObj.height}
-  }
-
-  // Positions returned by coordsChar contain some extra information.
-  // xRel is the relative x position of the input coordinates compared
-  // to the found position (so xRel > 0 means the coordinates are to
-  // the right of the character position, for example). When outside
-  // is true, that means the coordinates lie outside the line's
-  // vertical range.
-  function PosWithInfo(line, ch, sticky, outside, xRel) {
-    var pos = Pos(line, ch, sticky);
-    pos.xRel = xRel;
-    if (outside) { pos.outside = outside; }
-    return pos
-  }
-
-  // Compute the character position closest to the given coordinates.
-  // Input must be lineSpace-local ("div" coordinate system).
-  function coordsChar(cm, x, y) {
-    var doc = cm.doc;
-    y += cm.display.viewOffset;
-    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
-    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineN > last)
-      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
-    if (x < 0) { x = 0; }
-
-    var lineObj = getLine(doc, lineN);
-    for (;;) {
-      var found = coordsCharInner(cm, lineObj, lineN, x, y);
-      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
-      if (!collapsed) { return found }
-      var rangeEnd = collapsed.find(1);
-      if (rangeEnd.line == lineN) { return rangeEnd }
-      lineObj = getLine(doc, lineN = rangeEnd.line);
-    }
-  }
-
-  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
-    y -= widgetTopHeight(lineObj);
-    var end = lineObj.text.length;
-    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
-    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
-    return {begin: begin, end: end}
-  }
-
-  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
-    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
-  }
-
-  // Returns true if the given side of a box is after the given
-  // coordinates, in top-to-bottom, left-to-right order.
-  function boxIsAfter(box, x, y, left) {
-    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
-  }
-
-  function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
-    // Move y into line-local coordinate space
-    y -= heightAtLine(lineObj);
-    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
-    // When directly calling `measureCharPrepared`, we have to adjust
-    // for the widgets at this line.
-    var widgetHeight$$1 = widgetTopHeight(lineObj);
-    var begin = 0, end = lineObj.text.length, ltr = true;
-
-    var order = getOrder(lineObj, cm.doc.direction);
-    // If the line isn't plain left-to-right text, first figure out
-    // which bidi section the coordinates fall into.
-    if (order) {
-      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
-                   (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
-      ltr = part.level != 1;
-      // The awkward -1 offsets are needed because findFirst (called
-      // on these below) will treat its first bound as inclusive,
-      // second as exclusive, but we want to actually address the
-      // characters in the part's range
-      begin = ltr ? part.from : part.to - 1;
-      end = ltr ? part.to : part.from - 1;
-    }
-
-    // A binary search to find the first character whose bounding box
-    // starts after the coordinates. If we run across any whose box wrap
-    // the coordinates, store that.
-    var chAround = null, boxAround = null;
-    var ch = findFirst(function (ch) {
-      var box = measureCharPrepared(cm, preparedMeasure, ch);
-      box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
-      if (!boxIsAfter(box, x, y, false)) { return false }
-      if (box.top <= y && box.left <= x) {
-        chAround = ch;
-        boxAround = box;
-      }
-      return true
-    }, begin, end);
-
-    var baseX, sticky, outside = false;
-    // If a box around the coordinates was found, use that
-    if (boxAround) {
-      // Distinguish coordinates nearer to the left or right side of the box
-      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
-      ch = chAround + (atStart ? 0 : 1);
-      sticky = atStart ? "after" : "before";
-      baseX = atLeft ? boxAround.left : boxAround.right;
-    } else {
-      // (Adjust for extended bound, if necessary.)
-      if (!ltr && (ch == end || ch == begin)) { ch++; }
-      // To determine which side to associate with, get the box to the
-      // left of the character and compare it's vertical position to the
-      // coordinates
-      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
-        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
-        "after" : "before";
-      // Now get accurate coordinates for this place, in order to get a
-      // base X position
-      var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
-      baseX = coords.left;
-      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
-    }
-
-    ch = skipExtendingChars(lineObj.text, ch, 1);
-    return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
-  }
-
-  function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
-    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
-    // situation, we can take this ordering to correspond to the visual
-    // ordering. This finds the first part whose end is after the given
-    // coordinates.
-    var index = findFirst(function (i) {
-      var part = order[i], ltr = part.level != 1;
-      return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
-                                     "line", lineObj, preparedMeasure), x, y, true)
-    }, 0, order.length - 1);
-    var part = order[index];
-    // If this isn't the first part, the part's start is also after
-    // the coordinates, and the coordinates aren't on the same line as
-    // that start, move one part back.
-    if (index > 0) {
-      var ltr = part.level != 1;
-      var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
-                               "line", lineObj, preparedMeasure);
-      if (boxIsAfter(start, x, y, true) && start.top > y)
-        { part = order[index - 1]; }
-    }
-    return part
-  }
-
-  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
-    // In a wrapped line, rtl text on wrapping boundaries can do things
-    // that don't correspond to the ordering in our `order` array at
-    // all, so a binary search doesn't work, and we want to return a
-    // part that only spans one line so that the binary search in
-    // coordsCharInner is safe. As such, we first find the extent of the
-    // wrapped line, and then do a flat search in which we discard any
-    // spans that aren't on the line.
-    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
-    var begin = ref.begin;
-    var end = ref.end;
-    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
-    var part = null, closestDist = null;
-    for (var i = 0; i < order.length; i++) {
-      var p = order[i];
-      if (p.from >= end || p.to <= begin) { continue }
-      var ltr = p.level != 1;
-      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
-      // Weigh against spans ending before this, so that they are only
-      // picked if nothing ends after
-      var dist = endX < x ? x - endX + 1e9 : endX - x;
-      if (!part || closestDist > dist) {
-        part = p;
-        closestDist = dist;
-      }
-    }
-    if (!part) { part = order[order.length - 1]; }
-    // Clip the part to the wrapped line.
-    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
-    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
-    return part
-  }
-
-  var measureText;
-  // Compute the default text height.
-  function textHeight(display) {
-    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
-    if (measureText == null) {
-      measureText = elt("pre", null, "CodeMirror-line-like");
-      // Measure a bunch of lines, for browsers that compute
-      // fractional heights.
-      for (var i = 0; i < 49; ++i) {
-        measureText.appendChild(document.createTextNode("x"));
-        measureText.appendChild(elt("br"));
-      }
-      measureText.appendChild(document.createTextNode("x"));
-    }
-    removeChildrenAndAdd(display.measure, measureText);
-    var height = measureText.offsetHeight / 50;
-    if (height > 3) { display.cachedTextHeight = height; }
-    removeChildren(display.measure);
-    return height || 1
-  }
-
-  // Compute the default character width.
-  function charWidth(display) {
-    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
-    var anchor = elt("span", "xxxxxxxxxx");
-    var pre = elt("pre", [anchor], "CodeMirror-line-like");
-    removeChildrenAndAdd(display.measure, pre);
-    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
-    if (width > 2) { display.cachedCharWidth = width; }
-    return width || 10
-  }
-
-  // Do a bulk-read of the DOM positions and sizes needed to draw the
-  // view, so that we don't interleave reading and writing to the DOM.
-  function getDimensions(cm) {
-    var d = cm.display, left = {}, width = {};
-    var gutterLeft = d.gutters.clientLeft;
-    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
-      var id = cm.display.gutterSpecs[i].className;
-      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
-      width[id] = n.clientWidth;
-    }
-    return {fixedPos: compensateForHScroll(d),
-            gutterTotalWidth: d.gutters.offsetWidth,
-            gutterLeft: left,
-            gutterWidth: width,
-            wrapperWidth: d.wrapper.clientWidth}
-  }
-
-  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
-  // but using getBoundingClientRect to get a sub-pixel-accurate
-  // result.
-  function compensateForHScroll(display) {
-    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
-  }
-
-  // Returns a function that estimates the height of a line, to use as
-  // first approximation until the line becomes visible (and is thus
-  // properly measurable).
-  function estimateHeight(cm) {
-    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
-    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
-    return function (line) {
-      if (lineIsHidden(cm.doc, line)) { return 0 }
-
-      var widgetsHeight = 0;
-      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
-        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
-      } }
-
-      if (wrapping)
-        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
-      else
-        { return widgetsHeight + th }
-    }
-  }
-
-  function estimateLineHeights(cm) {
-    var doc = cm.doc, est = estimateHeight(cm);
-    doc.iter(function (line) {
-      var estHeight = est(line);
-      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-    });
-  }
-
-  // Given a mouse event, find the corresponding position. If liberal
-  // is false, it checks whether a gutter or scrollbar was clicked,
-  // and returns null if it was. forRect is used by rectangular
-  // selections, and tries to estimate a character position even for
-  // coordinates beyond the right of the text.
-  function posFromMouse(cm, e, liberal, forRect) {
-    var display = cm.display;
-    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
-
-    var x, y, space = display.lineSpace.getBoundingClientRect();
-    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
-    try { x = e.clientX - space.left; y = e.clientY - space.top; }
-    catch (e) { return null }
-    var coords = coordsChar(cm, x, y), line;
-    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
-      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
-      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
-    }
-    return coords
-  }
-
-  // Find the view element corresponding to a given line. Return null
-  // when the line isn't visible.
-  function findViewIndex(cm, n) {
-    if (n >= cm.display.viewTo) { return null }
-    n -= cm.display.viewFrom;
-    if (n < 0) { return null }
-    var view = cm.display.view;
-    for (var i = 0; i < view.length; i++) {
-      n -= view[i].size;
-      if (n < 0) { return i }
-    }
-  }
-
-  // Updates the display.view data structure for a given change to the
-  // document. From and to are in pre-change coordinates. Lendiff is
-  // the amount of lines added or subtracted by the change. This is
-  // used for changes that span multiple lines, or change the way
-  // lines are divided into visual lines. regLineChange (below)
-  // registers single-line changes.
-  function regChange(cm, from, to, lendiff) {
-    if (from == null) { from = cm.doc.first; }
-    if (to == null) { to = cm.doc.first + cm.doc.size; }
-    if (!lendiff) { lendiff = 0; }
-
-    var display = cm.display;
-    if (lendiff && to < display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers > from))
-      { display.updateLineNumbers = from; }
-
-    cm.curOp.viewChanged = true;
-
-    if (from >= display.viewTo) { // Change after
-      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
-        { resetView(cm); }
-    } else if (to <= display.viewFrom) { // Change before
-      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
-        resetView(cm);
-      } else {
-        display.viewFrom += lendiff;
-        display.viewTo += lendiff;
-      }
-    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
-      resetView(cm);
-    } else if (from <= display.viewFrom) { // Top overlap
-      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cut) {
-        display.view = display.view.slice(cut.index);
-        display.viewFrom = cut.lineN;
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    } else if (to >= display.viewTo) { // Bottom overlap
-      var cut$1 = viewCuttingPoint(cm, from, from, -1);
-      if (cut$1) {
-        display.view = display.view.slice(0, cut$1.index);
-        display.viewTo = cut$1.lineN;
-      } else {
-        resetView(cm);
-      }
-    } else { // Gap in the middle
-      var cutTop = viewCuttingPoint(cm, from, from, -1);
-      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cutTop && cutBot) {
-        display.view = display.view.slice(0, cutTop.index)
-          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
-          .concat(display.view.slice(cutBot.index));
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    }
-
-    var ext = display.externalMeasured;
-    if (ext) {
-      if (to < ext.lineN)
-        { ext.lineN += lendiff; }
-      else if (from < ext.lineN + ext.size)
-        { display.externalMeasured = null; }
-    }
-  }
-
-  // Register a change to a single line. Type must be one of "text",
-  // "gutter", "class", "widget"
-  function regLineChange(cm, line, type) {
-    cm.curOp.viewChanged = true;
-    var display = cm.display, ext = cm.display.externalMeasured;
-    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
-      { display.externalMeasured = null; }
-
-    if (line < display.viewFrom || line >= display.viewTo) { return }
-    var lineView = display.view[findViewIndex(cm, line)];
-    if (lineView.node == null) { return }
-    var arr = lineView.changes || (lineView.changes = []);
-    if (indexOf(arr, type) == -1) { arr.push(type); }
-  }
-
-  // Clear the view.
-  function resetView(cm) {
-    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
-    cm.display.view = [];
-    cm.display.viewOffset = 0;
-  }
-
-  function viewCuttingPoint(cm, oldN, newN, dir) {
-    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
-    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
-      { return {index: index, lineN: newN} }
-    var n = cm.display.viewFrom;
-    for (var i = 0; i < index; i++)
-      { n += view[i].size; }
-    if (n != oldN) {
-      if (dir > 0) {
-        if (index == view.length - 1) { return null }
-        diff = (n + view[index].size) - oldN;
-        index++;
-      } else {
-        diff = n - oldN;
-      }
-      oldN += diff; newN += diff;
-    }
-    while (visualLineNo(cm.doc, newN) != newN) {
-      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
-      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
-      index += dir;
-    }
-    return {index: index, lineN: newN}
-  }
-
-  // Force the view to cover a given range, adding empty view element
-  // or clipping off existing ones as needed.
-  function adjustView(cm, from, to) {
-    var display = cm.display, view = display.view;
-    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
-      display.view = buildViewArray(cm, from, to);
-      display.viewFrom = from;
-    } else {
-      if (display.viewFrom > from)
-        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
-      else if (display.viewFrom < from)
-        { display.view = display.view.slice(findViewIndex(cm, from)); }
-      display.viewFrom = from;
-      if (display.viewTo < to)
-        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
-      else if (display.viewTo > to)
-        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
-    }
-    display.viewTo = to;
-  }
-
-  // Count the number of lines in the view whose DOM representation is
-  // out of date (or nonexistent).
-  function countDirtyView(cm) {
-    var view = cm.display.view, dirty = 0;
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
-    }
-    return dirty
-  }
-
-  function updateSelection(cm) {
-    cm.display.input.showSelection(cm.display.input.prepareSelection());
-  }
-
-  function prepareSelection(cm, primary) {
-    if ( primary === void 0 ) primary = true;
-
-    var doc = cm.doc, result = {};
-    var curFragment = result.cursors = document.createDocumentFragment();
-    var selFragment = result.selection = document.createDocumentFragment();
-
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      if (!primary && i == doc.sel.primIndex) { continue }
-      var range$$1 = doc.sel.ranges[i];
-      if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
-      var collapsed = range$$1.empty();
-      if (collapsed || cm.options.showCursorWhenSelecting)
-        { drawSelectionCursor(cm, range$$1.head, curFragment); }
-      if (!collapsed)
-        { drawSelectionRange(cm, range$$1, selFragment); }
-    }
-    return result
-  }
-
-  // Draws a cursor for the given range
-  function drawSelectionCursor(cm, head, output) {
-    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
-
-    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
-    cursor.style.left = pos.left + "px";
-    cursor.style.top = pos.top + "px";
-    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
-
-    if (pos.other) {
-      // Secondary cursor, shown when on a 'jump' in bi-directional text
-      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
-      otherCursor.style.display = "";
-      otherCursor.style.left = pos.other.left + "px";
-      otherCursor.style.top = pos.other.top + "px";
-      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
-    }
-  }
-
-  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
-
-  // Draws the given range as a highlighted selection
-  function drawSelectionRange(cm, range$$1, output) {
-    var display = cm.display, doc = cm.doc;
-    var fragment = document.createDocumentFragment();
-    var padding = paddingH(cm.display), leftSide = padding.left;
-    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
-    var docLTR = doc.direction == "ltr";
-
-    function add(left, top, width, bottom) {
-      if (top < 0) { top = 0; }
-      top = Math.round(top);
-      bottom = Math.round(bottom);
-      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")));
-    }
-
-    function drawForLine(line, fromArg, toArg) {
-      var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
-      }
-
-      function wrapX(pos, dir, side) {
-        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
-        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
-        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
-        return coords(ch, prop)[prop]
-      }
-
-      var order = getOrder(lineObj, doc.direction);
-      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
-        var ltr = dir == "ltr";
-        var fromPos = coords(from, ltr ? "left" : "right");
-        var toPos = coords(to - 1, ltr ? "right" : "left");
-
-        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
-        var first = i == 0, last = !order || i == order.length - 1;
-        if (toPos.top - fromPos.top <= 3) { // Single line
-          var openLeft = (docLTR ? openStart : openEnd) && first;
-          var openRight = (docLTR ? openEnd : openStart) && last;
-          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
-          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
-          add(left, fromPos.top, right - left, fromPos.bottom);
-        } else { // Multiple lines
-          var topLeft, topRight, botLeft, botRight;
-          if (ltr) {
-            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
-            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
-            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
-            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
-          } else {
-            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
-            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
-            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
-            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
-          }
-          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
-          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
-          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
-        }
-
-        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
-        if (cmpCoords(toPos, start) < 0) { start = toPos; }
-        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
-        if (cmpCoords(toPos, end) < 0) { end = toPos; }
-      });
-      return {start: start, end: end}
-    }
-
-    var sFrom = range$$1.from(), sTo = range$$1.to();
-    if (sFrom.line == sTo.line) {
-      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
-    } else {
-      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
-      var singleVLine = visualLine(fromLine) == visualLine(toLine);
-      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
-      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
-        }
-      }
-      if (leftEnd.bottom < rightStart.top)
-        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
-    }
-
-    output.appendChild(fragment);
-  }
-
-  // Cursor-blinking
-  function restartBlink(cm) {
-    if (!cm.state.focused) { return }
-    var display = cm.display;
-    clearInterval(display.blinker);
-    var on = true;
-    display.cursorDiv.style.visibility = "";
-    if (cm.options.cursorBlinkRate > 0)
-      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
-        cm.options.cursorBlinkRate); }
-    else if (cm.options.cursorBlinkRate < 0)
-      { display.cursorDiv.style.visibility = "hidden"; }
-  }
-
-  function ensureFocus(cm) {
-    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
-  }
-
-  function delayBlurEvent(cm) {
-    cm.state.delayingBlurEvent = true;
-    setTimeout(function () { if (cm.state.delayingBlurEvent) {
-      cm.state.delayingBlurEvent = false;
-      onBlur(cm);
-    } }, 100);
-  }
-
-  function onFocus(cm, e) {
-    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
-
-    if (cm.options.readOnly == "nocursor") { return }
-    if (!cm.state.focused) {
-      signal(cm, "focus", cm, e);
-      cm.state.focused = true;
-      addClass(cm.display.wrapper, "CodeMirror-focused");
-      // This test prevents this from firing when a context
-      // menu is closed (since the input reset would kill the
-      // select-all detection hack)
-      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
-        cm.display.input.reset();
-        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
-      }
-      cm.display.input.receivedFocus();
-    }
-    restartBlink(cm);
-  }
-  function onBlur(cm, e) {
-    if (cm.state.delayingBlurEvent) { return }
-
-    if (cm.state.focused) {
-      signal(cm, "blur", cm, e);
-      cm.state.focused = false;
-      rmClass(cm.display.wrapper, "CodeMirror-focused");
-    }
-    clearInterval(cm.display.blinker);
-    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
-  }
-
-  // Read the actual heights of the rendered lines, and update their
-  // stored heights to match.
-  function updateHeightsInViewport(cm) {
-    var display = cm.display;
-    var prevBottom = display.lineDiv.offsetTop;
-    for (var i = 0; i < display.view.length; i++) {
-      var cur = display.view[i], wrapping = cm.options.lineWrapping;
-      var height = (void 0), width = 0;
-      if (cur.hidden) { continue }
-      if (ie && ie_version < 8) {
-        var bot = cur.node.offsetTop + cur.node.offsetHeight;
-        height = bot - prevBottom;
-        prevBottom = bot;
-      } else {
-        var box = cur.node.getBoundingClientRect();
-        height = box.bottom - box.top;
-        // Check that lines don't extend past the right of the current
-        // editor width
-        if (!wrapping && cur.text.firstChild)
-          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
-      }
-      var diff = cur.line.height - height;
-      if (diff > .005 || diff < -.005) {
-        updateLineHeight(cur.line, height);
-        updateWidgetHeight(cur.line);
-        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
-          { updateWidgetHeight(cur.rest[j]); } }
-      }
-      if (width > cm.display.sizerWidth) {
-        var chWidth = Math.ceil(width / charWidth(cm.display));
-        if (chWidth > cm.display.maxLineLength) {
-          cm.display.maxLineLength = chWidth;
-          cm.display.maxLine = cur.line;
-          cm.display.maxLineChanged = true;
-        }
-      }
-    }
-  }
-
-  // Read and store the height of line widgets associated with the
-  // given line.
-  function updateWidgetHeight(line) {
-    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
-      var w = line.widgets[i], parent = w.node.parentNode;
-      if (parent) { w.height = parent.offsetHeight; }
-    } }
-  }
-
-  // Compute the lines that are visible in a given viewport (defaults
-  // the the current scroll position). viewport may contain top,
-  // height, and ensure (see op.scrollToPos) properties.
-  function visibleLines(display, doc, viewport) {
-    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
-    top = Math.floor(top - paddingTop(display));
-    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
-
-    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
-    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
-    // forces those lines into the viewport (if possible).
-    if (viewport && viewport.ensure) {
-      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
-      if (ensureFrom < from) {
-        from = ensureFrom;
-        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
-      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
-        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
-        to = ensureTo;
-      }
-    }
-    return {from: from, to: Math.max(to, from + 1)}
-  }
-
-  // SCROLLING THINGS INTO VIEW
-
-  // If an editor sits on the top or bottom of the window, partially
-  // scrolled out of view, this ensures that the cursor is visible.
-  function maybeScrollWindow(cm, rect) {
-    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
-
-    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
-    if (rect.top + box.top < 0) { doScroll = true; }
-    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
-    if (doScroll != null && !phantom) {
-      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;"));
-      cm.display.lineSpace.appendChild(scrollNode);
-      scrollNode.scrollIntoView(doScroll);
-      cm.display.lineSpace.removeChild(scrollNode);
-    }
-  }
-
-  // Scroll a given position into view (immediately), verifying that
-  // it actually became visible (as line heights are accurately
-  // measured, the position of something may 'drift' during drawing).
-  function scrollPosIntoView(cm, pos, end, margin) {
-    if (margin == null) { margin = 0; }
-    var rect;
-    if (!cm.options.lineWrapping && pos == end) {
-      // Set pos and end to the cursor positions around the character pos sticks to
-      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
-      // If pos == Pos(_, 0, "before"), pos and end are unchanged
-      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
-      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
-    }
-    for (var limit = 0; limit < 5; limit++) {
-      var changed = false;
-      var coords = cursorCoords(cm, pos);
-      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
-      rect = {left: Math.min(coords.left, endCoords.left),
-              top: Math.min(coords.top, endCoords.top) - margin,
-              right: Math.max(coords.left, endCoords.left),
-              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
-      var scrollPos = calculateScrollPos(cm, rect);
-      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
-      if (scrollPos.scrollTop != null) {
-        updateScrollTop(cm, scrollPos.scrollTop);
-        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
-      }
-      if (scrollPos.scrollLeft != null) {
-        setScrollLeft(cm, scrollPos.scrollLeft);
-        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
-      }
-      if (!changed) { break }
-    }
-    return rect
-  }
-
-  // Scroll a given set of coordinates into view (immediately).
-  function scrollIntoView(cm, rect) {
-    var scrollPos = calculateScrollPos(cm, rect);
-    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
-    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
-  }
-
-  // Calculate a new scroll position needed to scroll the given
-  // rectangle into view. Returns an object with scrollTop and
-  // scrollLeft properties. When these are undefined, the
-  // vertical/horizontal position does not need to be adjusted.
-  function calculateScrollPos(cm, rect) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
-    if (rect.top < 0) { rect.top = 0; }
-    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
-    var screen = displayHeight(cm), result = {};
-    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
-    var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
-    if (rect.top < screentop) {
-      result.scrollTop = atTop ? 0 : rect.top;
-    } else if (rect.bottom > screentop + screen) {
-      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
-      if (newTop != screentop) { result.scrollTop = newTop; }
-    }
-
-    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
-    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
-    var tooWide = rect.right - rect.left > screenw;
-    if (tooWide) { rect.right = rect.left + screenw; }
-    if (rect.left < 10)
-      { result.scrollLeft = 0; }
-    else if (rect.left < screenleft)
-      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
-    else if (rect.right > screenw + screenleft - 3)
-      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
-    return result
-  }
-
-  // Store a relative adjustment to the scroll position in the current
-  // operation (to be applied when the operation finishes).
-  function addToScrollTop(cm, top) {
-    if (top == null) { return }
-    resolveScrollToPos(cm);
-    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
-  }
-
-  // Make sure that at the end of the operation the current cursor is
-  // shown.
-  function ensureCursorVisible(cm) {
-    resolveScrollToPos(cm);
-    var cur = cm.getCursor();
-    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
-  }
-
-  function scrollToCoords(cm, x, y) {
-    if (x != null || y != null) { resolveScrollToPos(cm); }
-    if (x != null) { cm.curOp.scrollLeft = x; }
-    if (y != null) { cm.curOp.scrollTop = y; }
-  }
-
-  function scrollToRange(cm, range$$1) {
-    resolveScrollToPos(cm);
-    cm.curOp.scrollToPos = range$$1;
-  }
-
-  // When an operation has its scrollToPos property set, and another
-  // scroll action is applied before the end of the operation, this
-  // 'simulates' scrolling that position into view in a cheap way, so
-  // that the effect of intermediate scroll commands is not ignored.
-  function resolveScrollToPos(cm) {
-    var range$$1 = cm.curOp.scrollToPos;
-    if (range$$1) {
-      cm.curOp.scrollToPos = null;
-      var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
-      scrollToCoordsRange(cm, from, to, range$$1.margin);
-    }
-  }
-
-  function scrollToCoordsRange(cm, from, to, margin) {
-    var sPos = calculateScrollPos(cm, {
-      left: Math.min(from.left, to.left),
-      top: Math.min(from.top, to.top) - margin,
-      right: Math.max(from.right, to.right),
-      bottom: Math.max(from.bottom, to.bottom) + margin
-    });
-    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
-  }
-
-  // Sync the scrollable area and scrollbars, ensure the viewport
-  // covers the visible area.
-  function updateScrollTop(cm, val) {
-    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
-    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
-    setScrollTop(cm, val, true);
-    if (gecko) { updateDisplaySimple(cm); }
-    startWorker(cm, 100);
-  }
-
-  function setScrollTop(cm, val, forceScroll) {
-    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
-    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
-    cm.doc.scrollTop = val;
-    cm.display.scrollbars.setScrollTop(val);
-    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
-  }
-
-  // Sync scroller and scrollbar, ensure the gutter elements are
-  // aligned.
-  function setScrollLeft(cm, val, isScroller, forceScroll) {
-    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
-    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
-    cm.doc.scrollLeft = val;
-    alignHorizontally(cm);
-    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
-    cm.display.scrollbars.setScrollLeft(val);
-  }
-
-  // SCROLLBARS
-
-  // Prepare DOM reads needed to update the scrollbars. Done in one
-  // shot to minimize update/measure roundtrips.
-  function measureForScrollbars(cm) {
-    var d = cm.display, gutterW = d.gutters.offsetWidth;
-    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
-    return {
-      clientHeight: d.scroller.clientHeight,
-      viewHeight: d.wrapper.clientHeight,
-      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
-      viewWidth: d.wrapper.clientWidth,
-      barLeft: cm.options.fixedGutter ? gutterW : 0,
-      docHeight: docH,
-      scrollHeight: docH + scrollGap(cm) + d.barHeight,
-      nativeBarWidth: d.nativeBarWidth,
-      gutterWidth: gutterW
-    }
-  }
-
-  var NativeScrollbars = function(place, scroll, cm) {
-    this.cm = cm;
-    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
-    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
-    vert.tabIndex = horiz.tabIndex = -1;
-    place(vert); place(horiz);
-
-    on(vert, "scroll", function () {
-      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
-    });
-    on(horiz, "scroll", function () {
-      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
-    });
-
-    this.checkedZeroWidth = false;
-    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
-    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
-  };
-
-  NativeScrollbars.prototype.update = function (measure) {
-    var needsH = measure.scrollWidth > measure.clientWidth + 1;
-    var needsV = measure.scrollHeight > measure.clientHeight + 1;
-    var sWidth = measure.nativeBarWidth;
-
-    if (needsV) {
-      this.vert.style.display = "block";
-      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
-      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
-      // A bug in IE8 can cause this value to be negative, so guard it.
-      this.vert.firstChild.style.height =
-        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
-    } else {
-      this.vert.style.display = "";
-      this.vert.firstChild.style.height = "0";
-    }
-
-    if (needsH) {
-      this.horiz.style.display = "block";
-      this.horiz.style.right = needsV ? sWidth + "px" : "0";
-      this.horiz.style.left = measure.barLeft + "px";
-      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
-      this.horiz.firstChild.style.width =
-        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
-    } else {
-      this.horiz.style.display = "";
-      this.horiz.firstChild.style.width = "0";
-    }
-
-    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
-      if (sWidth == 0) { this.zeroWidthHack(); }
-      this.checkedZeroWidth = true;
-    }
-
-    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
-  };
-
-  NativeScrollbars.prototype.setScrollLeft = function (pos) {
-    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
-    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
-  };
-
-  NativeScrollbars.prototype.setScrollTop = function (pos) {
-    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
-    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
-  };
-
-  NativeScrollbars.prototype.zeroWidthHack = function () {
-    var w = mac && !mac_geMountainLion ? "12px" : "18px";
-    this.horiz.style.height = this.vert.style.width = w;
-    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
-    this.disableHoriz = new Delayed;
-    this.disableVert = new Delayed;
-  };
-
-  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
-    bar.style.pointerEvents = "auto";
-    function maybeDisable() {
-      // To find out whether the scrollbar is still visible, we
-      // check whether the element under the pixel in the bottom
-      // right corner of the scrollbar box is the scrollbar box
-      // itself (when the bar is still visible) or its filler child
-      // (when the bar is hidden). If it is still visible, we keep
-      // it enabled, if it's hidden, we disable pointer events.
-      var box = bar.getBoundingClientRect();
-      var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
-          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
-      if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
-      else { delay.set(1000, maybeDisable); }
-    }
-    delay.set(1000, maybeDisable);
-  };
-
-  NativeScrollbars.prototype.clear = function () {
-    var parent = this.horiz.parentNode;
-    parent.removeChild(this.horiz);
-    parent.removeChild(this.vert);
-  };
-
-  var NullScrollbars = function () {};
-
-  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
-  NullScrollbars.prototype.setScrollLeft = function () {};
-  NullScrollbars.prototype.setScrollTop = function () {};
-  NullScrollbars.prototype.clear = function () {};
-
-  function updateScrollbars(cm, measure) {
-    if (!measure) { measure = measureForScrollbars(cm); }
-    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
-    updateScrollbarsInner(cm, measure);
-    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
-      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
-        { updateHeightsInViewport(cm); }
-      updateScrollbarsInner(cm, measureForScrollbars(cm));
-      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
-    }
-  }
-
-  // Re-synchronize the fake scrollbars with the actual size of the
-  // content.
-  function updateScrollbarsInner(cm, measure) {
-    var d = cm.display;
-    var sizes = d.scrollbars.update(measure);
-
-    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
-    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
-    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
-
-    if (sizes.right && sizes.bottom) {
-      d.scrollbarFiller.style.display = "block";
-      d.scrollbarFiller.style.height = sizes.bottom + "px";
-      d.scrollbarFiller.style.width = sizes.right + "px";
-    } else { d.scrollbarFiller.style.display = ""; }
-    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
-      d.gutterFiller.style.display = "block";
-      d.gutterFiller.style.height = sizes.bottom + "px";
-      d.gutterFiller.style.width = measure.gutterWidth + "px";
-    } else { d.gutterFiller.style.display = ""; }
-  }
-
-  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
-
-  function initScrollbars(cm) {
-    if (cm.display.scrollbars) {
-      cm.display.scrollbars.clear();
-      if (cm.display.scrollbars.addClass)
-        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-    }
-
-    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
-      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
-      // Prevent clicks in the scrollbars from killing focus
-      on(node, "mousedown", function () {
-        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
-      });
-      node.setAttribute("cm-not-content", "true");
-    }, function (pos, axis) {
-      if (axis == "horizontal") { setScrollLeft(cm, pos); }
-      else { updateScrollTop(cm, pos); }
-    }, cm);
-    if (cm.display.scrollbars.addClass)
-      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-  }
-
-  // Operations are used to wrap a series of changes to the editor
-  // state in such a way that each change won't have to update the
-  // cursor and display (which would be awkward, slow, and
-  // error-prone). Instead, display updates are batched and then all
-  // combined and executed at once.
-
-  var nextOpId = 0;
-  // Start a new operation.
-  function startOperation(cm) {
-    cm.curOp = {
-      cm: cm,
-      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
-      startHeight: cm.doc.height, // Used to detect need to update scrollbar
-      forceUpdate: false,      // Used to force a redraw
-      updateInput: 0,       // Whether to reset the input textarea
-      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
-      changeObjs: null,        // Accumulated changes, for firing change events
-      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
-      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
-      selectionChanged: false, // Whether the selection needs to be redrawn
-      updateMaxLine: false,    // Set when the widest line needs to be determined anew
-      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
-      scrollToPos: null,       // Used to scroll to a specific position
-      focus: false,
-      id: ++nextOpId           // Unique ID
-    };
-    pushOperation(cm.curOp);
-  }
-
-  // Finish an operation, updating the display and signalling delayed events
-  function endOperation(cm) {
-    var op = cm.curOp;
-    if (op) { finishOperation(op, function (group) {
-      for (var i = 0; i < group.ops.length; i++)
-        { group.ops[i].cm.curOp = null; }
-      endOperations(group);
-    }); }
-  }
-
-  // The DOM updates done when an operation finishes are batched so
-  // that the minimum number of relayouts are required.
-  function endOperations(group) {
-    var ops = group.ops;
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      { endOperation_R1(ops[i]); }
-    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
-      { endOperation_W1(ops[i$1]); }
-    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
-      { endOperation_R2(ops[i$2]); }
-    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
-      { endOperation_W2(ops[i$3]); }
-    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
-      { endOperation_finish(ops[i$4]); }
-  }
-
-  function endOperation_R1(op) {
-    var cm = op.cm, display = cm.display;
-    maybeClipScrollbars(cm);
-    if (op.updateMaxLine) { findMaxLine(cm); }
-
-    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
-      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
-                         op.scrollToPos.to.line >= display.viewTo) ||
-      display.maxLineChanged && cm.options.lineWrapping;
-    op.update = op.mustUpdate &&
-      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
-  }
-
-  function endOperation_W1(op) {
-    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
-  }
-
-  function endOperation_R2(op) {
-    var cm = op.cm, display = cm.display;
-    if (op.updatedDisplay) { updateHeightsInViewport(cm); }
-
-    op.barMeasure = measureForScrollbars(cm);
-
-    // If the max line changed since it was last measured, measure it,
-    // and ensure the document's width matches it.
-    // updateDisplay_W2 will use these properties to do the actual resizing
-    if (display.maxLineChanged && !cm.options.lineWrapping) {
-      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
-      cm.display.sizerWidth = op.adjustWidthTo;
-      op.barMeasure.scrollWidth =
-        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
-      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
-    }
-
-    if (op.updatedDisplay || op.selectionChanged)
-      { op.preparedSelection = display.input.prepareSelection(); }
-  }
-
-  function endOperation_W2(op) {
-    var cm = op.cm;
-
-    if (op.adjustWidthTo != null) {
-      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
-      if (op.maxScrollLeft < cm.doc.scrollLeft)
-        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
-      cm.display.maxLineChanged = false;
-    }
-
-    var takeFocus = op.focus && op.focus == activeElt();
-    if (op.preparedSelection)
-      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
-    if (op.updatedDisplay || op.startHeight != cm.doc.height)
-      { updateScrollbars(cm, op.barMeasure); }
-    if (op.updatedDisplay)
-      { setDocumentHeight(cm, op.barMeasure); }
-
-    if (op.selectionChanged) { restartBlink(cm); }
-
-    if (cm.state.focused && op.updateInput)
-      { cm.display.input.reset(op.typing); }
-    if (takeFocus) { ensureFocus(op.cm); }
-  }
-
-  function endOperation_finish(op) {
-    var cm = op.cm, display = cm.display, doc = cm.doc;
-
-    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
-
-    // Abort mouse wheel delta measurement, when scrolling explicitly
-    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
-      { display.wheelStartX = display.wheelStartY = null; }
-
-    // Propagate the scroll position to the actual DOM scroller
-    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
-
-    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
-    // If we need to scroll a specific position into view, do so.
-    if (op.scrollToPos) {
-      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
-                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
-      maybeScrollWindow(cm, rect);
-    }
-
-    // Fire events for markers that are hidden/unidden by editing or
-    // undoing
-    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
-    if (hidden) { for (var i = 0; i < hidden.length; ++i)
-      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
-    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
-      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
-
-    if (display.wrapper.offsetHeight)
-      { doc.scrollTop = cm.display.scroller.scrollTop; }
-
-    // Fire change events, and delayed event handlers
-    if (op.changeObjs)
-      { signal(cm, "changes", cm, op.changeObjs); }
-    if (op.update)
-      { op.update.finish(); }
-  }
-
-  // Run the given function in an operation
-  function runInOp(cm, f) {
-    if (cm.curOp) { return f() }
-    startOperation(cm);
-    try { return f() }
-    finally { endOperation(cm); }
-  }
-  // Wraps a function in an operation. Returns the wrapped function.
-  function operation(cm, f) {
-    return function() {
-      if (cm.curOp) { return f.apply(cm, arguments) }
-      startOperation(cm);
-      try { return f.apply(cm, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-  // Used to add methods to editor and doc instances, wrapping them in
-  // operations.
-  function methodOp(f) {
-    return function() {
-      if (this.curOp) { return f.apply(this, arguments) }
-      startOperation(this);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(this); }
-    }
-  }
-  function docMethodOp(f) {
-    return function() {
-      var cm = this.cm;
-      if (!cm || cm.curOp) { return f.apply(this, arguments) }
-      startOperation(cm);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-
-  // HIGHLIGHT WORKER
-
-  function startWorker(cm, time) {
-    if (cm.doc.highlightFrontier < cm.display.viewTo)
-      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
-  }
-
-  function highlightWorker(cm) {
-    var doc = cm.doc;
-    if (doc.highlightFrontier >= cm.display.viewTo) { return }
-    var end = +new Date + cm.options.workTime;
-    var context = getContextBefore(cm, doc.highlightFrontier);
-    var changedLines = [];
-
-    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
-      if (context.line >= cm.display.viewFrom) { // Visible
-        var oldStyles = line.styles;
-        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
-        var highlighted = highlightLine(cm, line, context, true);
-        if (resetState) { context.state = resetState; }
-        line.styles = highlighted.styles;
-        var oldCls = line.styleClasses, newCls = highlighted.classes;
-        if (newCls) { line.styleClasses = newCls; }
-        else if (oldCls) { line.styleClasses = null; }
-        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
-          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
-        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
-        if (ischange) { changedLines.push(context.line); }
-        line.stateAfter = context.save();
-        context.nextLine();
-      } else {
-        if (line.text.length <= cm.options.maxHighlightLength)
-          { processLine(cm, line.text, context); }
-        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
-        context.nextLine();
-      }
-      if (+new Date > end) {
-        startWorker(cm, cm.options.workDelay);
-        return true
-      }
-    });
-    doc.highlightFrontier = context.line;
-    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
-    if (changedLines.length) { runInOp(cm, function () {
-      for (var i = 0; i < changedLines.length; i++)
-        { regLineChange(cm, changedLines[i], "text"); }
-    }); }
-  }
-
-  // DISPLAY DRAWING
-
-  var DisplayUpdate = function(cm, viewport, force) {
-    var display = cm.display;
-
-    this.viewport = viewport;
-    // Store some values that we'll need later (but don't want to force a relayout for)
-    this.visible = visibleLines(display, cm.doc, viewport);
-    this.editorIsHidden = !display.wrapper.offsetWidth;
-    this.wrapperHeight = display.wrapper.clientHeight;
-    this.wrapperWidth = display.wrapper.clientWidth;
-    this.oldDisplayWidth = displayWidth(cm);
-    this.force = force;
-    this.dims = getDimensions(cm);
-    this.events = [];
-  };
-
-  DisplayUpdate.prototype.signal = function (emitter, type) {
-    if (hasHandler(emitter, type))
-      { this.events.push(arguments); }
-  };
-  DisplayUpdate.prototype.finish = function () {
-      var this$1 = this;
-
-    for (var i = 0; i < this.events.length; i++)
-      { signal.apply(null, this$1.events[i]); }
-  };
-
-  function maybeClipScrollbars(cm) {
-    var display = cm.display;
-    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
-      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
-      display.heightForcer.style.height = scrollGap(cm) + "px";
-      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
-      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
-      display.scrollbarsClipped = true;
-    }
-  }
-
-  function selectionSnapshot(cm) {
-    if (cm.hasFocus()) { return null }
-    var active = activeElt();
-    if (!active || !contains(cm.display.lineDiv, active)) { return null }
-    var result = {activeElt: active};
-    if (window.getSelection) {
-      var sel = window.getSelection();
-      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
-        result.anchorNode = sel.anchorNode;
-        result.anchorOffset = sel.anchorOffset;
-        result.focusNode = sel.focusNode;
-        result.focusOffset = sel.focusOffset;
-      }
-    }
-    return result
-  }
-
-  function restoreSelection(snapshot) {
-    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
-    snapshot.activeElt.focus();
-    if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
-      var sel = window.getSelection(), range$$1 = document.createRange();
-      range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
-      range$$1.collapse(false);
-      sel.removeAllRanges();
-      sel.addRange(range$$1);
-      sel.extend(snapshot.focusNode, snapshot.focusOffset);
-    }
-  }
-
-  // Does the actual updating of the line display. Bails out
-  // (returning false) when there is nothing to be done and forced is
-  // false.
-  function updateDisplayIfNeeded(cm, update) {
-    var display = cm.display, doc = cm.doc;
-
-    if (update.editorIsHidden) {
-      resetView(cm);
-      return false
-    }
-
-    // Bail out if the visible area is already rendered and nothing changed.
-    if (!update.force &&
-        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
-        display.renderedView == display.view && countDirtyView(cm) == 0)
-      { return false }
-
-    if (maybeUpdateLineNumberWidth(cm)) {
-      resetView(cm);
-      update.dims = getDimensions(cm);
-    }
-
-    // Compute a suitable new viewport (from & to)
-    var end = doc.first + doc.size;
-    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
-    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
-    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
-    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
-    if (sawCollapsedSpans) {
-      from = visualLineNo(cm.doc, from);
-      to = visualLineEndNo(cm.doc, to);
-    }
-
-    var different = from != display.viewFrom || to != display.viewTo ||
-      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
-    adjustView(cm, from, to);
-
-    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
-    // Position the mover div to align with the current scroll position
-    cm.display.mover.style.top = display.viewOffset + "px";
-
-    var toUpdate = countDirtyView(cm);
-    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
-      { return false }
-
-    // For big changes, we hide the enclosing element during the
-    // update, since that speeds up the operations on most browsers.
-    var selSnapshot = selectionSnapshot(cm);
-    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
-    patchDisplay(cm, display.updateLineNumbers, update.dims);
-    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
-    display.renderedView = display.view;
-    // There might have been a widget with a focused element that got
-    // hidden or updated, if so re-focus it.
-    restoreSelection(selSnapshot);
-
-    // Prevent selection and cursors from interfering with the scroll
-    // width and height.
-    removeChildren(display.cursorDiv);
-    removeChildren(display.selectionDiv);
-    display.gutters.style.height = display.sizer.style.minHeight = 0;
-
-    if (different) {
-      display.lastWrapHeight = update.wrapperHeight;
-      display.lastWrapWidth = update.wrapperWidth;
-      startWorker(cm, 400);
-    }
-
-    display.updateLineNumbers = null;
-
-    return true
-  }
-
-  function postUpdateDisplay(cm, update) {
-    var viewport = update.viewport;
-
-    for (var first = true;; first = false) {
-      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
-        // Clip forced viewport to actual scrollable area.
-        if (viewport && viewport.top != null)
-          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
-        // Updated line heights might result in the drawn area not
-        // actually covering the viewport. Keep looping until it does.
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
-          { break }
-      }
-      if (!updateDisplayIfNeeded(cm, update)) { break }
-      updateHeightsInViewport(cm);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.force = false;
-    }
-
-    update.signal(cm, "update", cm);
-    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
-      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
-      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
-    }
-  }
-
-  function updateDisplaySimple(cm, viewport) {
-    var update = new DisplayUpdate(cm, viewport);
-    if (updateDisplayIfNeeded(cm, update)) {
-      updateHeightsInViewport(cm);
-      postUpdateDisplay(cm, update);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.finish();
-    }
-  }
-
-  // Sync the actual display DOM structure with display.view, removing
-  // nodes for lines that are no longer in view, and creating the ones
-  // that are not there yet, and updating the ones that are out of
-  // date.
-  function patchDisplay(cm, updateNumbersFrom, dims) {
-    var display = cm.display, lineNumbers = cm.options.lineNumbers;
-    var container = display.lineDiv, cur = container.firstChild;
-
-    function rm(node) {
-      var next = node.nextSibling;
-      // Works around a throw-scroll bug in OS X Webkit
-      if (webkit && mac && cm.display.currentWheelTarget == node)
-        { node.style.display = "none"; }
-      else
-        { node.parentNode.removeChild(node); }
-      return next
-    }
-
-    var view = display.view, lineN = display.viewFrom;
-    // Loop over the elements in the view, syncing cur (the DOM nodes
-    // in display.lineDiv) with the view as we go.
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
-        var node = buildLineElement(cm, lineView, lineN, dims);
-        container.insertBefore(node, cur);
-      } else { // Already drawn
-        while (cur != lineView.node) { cur = rm(cur); }
-        var updateNumber = lineNumbers && updateNumbersFrom != null &&
-          updateNumbersFrom <= lineN && lineView.lineNumber;
-        if (lineView.changes) {
-          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
-          updateLineForChanges(cm, lineView, lineN, dims);
-        }
-        if (updateNumber) {
-          removeChildren(lineView.lineNumber);
-          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
-        }
-        cur = lineView.node.nextSibling;
-      }
-      lineN += lineView.size;
-    }
-    while (cur) { cur = rm(cur); }
-  }
-
-  function updateGutterSpace(display) {
-    var width = display.gutters.offsetWidth;
-    display.sizer.style.marginLeft = width + "px";
-  }
-
-  function setDocumentHeight(cm, measure) {
-    cm.display.sizer.style.minHeight = measure.docHeight + "px";
-    cm.display.heightForcer.style.top = measure.docHeight + "px";
-    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
-  }
-
-  // Re-align line numbers and gutter marks to compensate for
-  // horizontal scrolling.
-  function alignHorizontally(cm) {
-    var display = cm.display, view = display.view;
-    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
-    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
-    var gutterW = display.gutters.offsetWidth, left = comp + "px";
-    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
-      if (cm.options.fixedGutter) {
-        if (view[i].gutter)
-          { view[i].gutter.style.left = left; }
-        if (view[i].gutterBackground)
-          { view[i].gutterBackground.style.left = left; }
-      }
-      var align = view[i].alignable;
-      if (align) { for (var j = 0; j < align.length; j++)
-        { align[j].style.left = left; } }
-    } }
-    if (cm.options.fixedGutter)
-      { display.gutters.style.left = (comp + gutterW) + "px"; }
-  }
-
-  // Used to ensure that the line number gutter is still the right
-  // size for the current document size. Returns true when an update
-  // is needed.
-  function maybeUpdateLineNumberWidth(cm) {
-    if (!cm.options.lineNumbers) { return false }
-    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
-    if (last.length != display.lineNumChars) {
-      var test = display.measure.appendChild(elt("div", [elt("div", last)],
-                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
-      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
-      display.lineGutter.style.width = "";
-      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
-      display.lineNumWidth = display.lineNumInnerWidth + padding;
-      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
-      display.lineGutter.style.width = display.lineNumWidth + "px";
-      updateGutterSpace(cm.display);
-      return true
-    }
-    return false
-  }
-
-  function getGutters(gutters, lineNumbers) {
-    var result = [], sawLineNumbers = false;
-    for (var i = 0; i < gutters.length; i++) {
-      var name = gutters[i], style = null;
-      if (typeof name != "string") { style = name.style; name = name.className; }
-      if (name == "CodeMirror-linenumbers") {
-        if (!lineNumbers) { continue }
-        else { sawLineNumbers = true; }
-      }
-      result.push({className: name, style: style});
-    }
-    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
-    return result
-  }
-
-  // Rebuild the gutter elements, ensure the margin to the left of the
-  // code matches their width.
-  function renderGutters(display) {
-    var gutters = display.gutters, specs = display.gutterSpecs;
-    removeChildren(gutters);
-    display.lineGutter = null;
-    for (var i = 0; i < specs.length; ++i) {
-      var ref = specs[i];
-      var className = ref.className;
-      var style = ref.style;
-      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
-      if (style) { gElt.style.cssText = style; }
-      if (className == "CodeMirror-linenumbers") {
-        display.lineGutter = gElt;
-        gElt.style.width = (display.lineNumWidth || 1) + "px";
-      }
-    }
-    gutters.style.display = specs.length ? "" : "none";
-    updateGutterSpace(display);
-  }
-
-  function updateGutters(cm) {
-    renderGutters(cm.display);
-    regChange(cm);
-    alignHorizontally(cm);
-  }
-
-  // The display handles the DOM integration, both for input reading
-  // and content drawing. It holds references to DOM nodes and
-  // display-related state.
-
-  function Display(place, doc, input, options) {
-    var d = this;
-    this.input = input;
-
-    // Covers bottom-right square when both scrollbars are present.
-    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
-    d.scrollbarFiller.setAttribute("cm-not-content", "true");
-    // Covers bottom of gutter when coverGutterNextToScrollbar is on
-    // and h scrollbar is present.
-    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
-    d.gutterFiller.setAttribute("cm-not-content", "true");
-    // Will contain the actual code, positioned to cover the viewport.
-    d.lineDiv = eltP("div", null, "CodeMirror-code");
-    // Elements are added to these to represent selection and cursors.
-    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
-    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
-    // A visibility: hidden element used to find the size of things.
-    d.measure = elt("div", null, "CodeMirror-measure");
-    // When lines outside of the viewport are measured, they are drawn in this.
-    d.lineMeasure = elt("div", null, "CodeMirror-measure");
-    // Wraps everything that needs to exist inside the vertically-padded coordinate system
-    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
-                      null, "position: relative; outline: none");
-    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
-    // Moved around its parent to cover visible view.
-    d.mover = elt("div", [lines], null, "position: relative");
-    // Set to the height of the document, allowing scrolling.
-    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
-    d.sizerWidth = null;
-    // Behavior of elts with overflow: auto and padding is
-    // inconsistent across browsers. This is used to ensure the
-    // scrollable area is big enough.
-    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
-    // Will contain the gutters, if any.
-    d.gutters = elt("div", null, "CodeMirror-gutters");
-    d.lineGutter = null;
-    // Actual scrollable element.
-    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
-    d.scroller.setAttribute("tabIndex", "-1");
-    // The element in which the editor lives.
-    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
-
-    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
-    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
-
-    if (place) {
-      if (place.appendChild) { place.appendChild(d.wrapper); }
-      else { place(d.wrapper); }
-    }
-
-    // Current rendered range (may be bigger than the view window).
-    d.viewFrom = d.viewTo = doc.first;
-    d.reportedViewFrom = d.reportedViewTo = doc.first;
-    // Information about the rendered lines.
-    d.view = [];
-    d.renderedView = null;
-    // Holds info about a single rendered line when it was rendered
-    // for measurement, while not in view.
-    d.externalMeasured = null;
-    // Empty space (in pixels) above the view
-    d.viewOffset = 0;
-    d.lastWrapHeight = d.lastWrapWidth = 0;
-    d.updateLineNumbers = null;
-
-    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
-    d.scrollbarsClipped = false;
-
-    // Used to only resize the line number gutter when necessary (when
-    // the amount of lines crosses a boundary that makes its width change)
-    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
-    // Set to true when a non-horizontal-scrolling line widget is
-    // added. As an optimization, line widget aligning is skipped when
-    // this is false.
-    d.alignWidgets = false;
-
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-
-    // Tracks the maximum line length so that the horizontal scrollbar
-    // can be kept static when scrolling.
-    d.maxLine = null;
-    d.maxLineLength = 0;
-    d.maxLineChanged = false;
-
-    // Used for measuring wheel scrolling granularity
-    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
-    // True when shift is held down.
-    d.shift = false;
-
-    // Used to track whether anything happened since the context menu
-    // was opened.
-    d.selForContextMenu = null;
-
-    d.activeTouch = null;
-
-    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
-    renderGutters(d);
-
-    input.init(d);
-  }
-
-  // Since the delta values reported on mouse wheel events are
-  // unstandardized between browsers and even browser versions, and
-  // generally horribly unpredictable, this code starts by measuring
-  // the scroll effect that the first few mouse wheel events have,
-  // and, from that, detects the way it can convert deltas to pixel
-  // offsets afterwards.
-  //
-  // The reason we want to know the amount a wheel event will scroll
-  // is that it gives us a chance to update the display before the
-  // actual scrolling happens, reducing flickering.
-
-  var wheelSamples = 0, wheelPixelsPerUnit = null;
-  // Fill in a browser-detected starting value on browsers where we
-  // know one. These don't have to be accurate -- the result of them
-  // being wrong would just be a slight flicker on the first wheel
-  // scroll (if it is large enough).
-  if (ie) { wheelPixelsPerUnit = -.53; }
-  else if (gecko) { wheelPixelsPerUnit = 15; }
-  else if (chrome) { wheelPixelsPerUnit = -.7; }
-  else if (safari) { wheelPixelsPerUnit = -1/3; }
-
-  function wheelEventDelta(e) {
-    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
-    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
-    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
-    else if (dy == null) { dy = e.wheelDelta; }
-    return {x: dx, y: dy}
-  }
-  function wheelEventPixels(e) {
-    var delta = wheelEventDelta(e);
-    delta.x *= wheelPixelsPerUnit;
-    delta.y *= wheelPixelsPerUnit;
-    return delta
-  }
-
-  function onScrollWheel(cm, e) {
-    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
-
-    var display = cm.display, scroll = display.scroller;
-    // Quit if there's nothing to scroll here
-    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
-    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
-    if (!(dx && canScrollX || dy && canScrollY)) { return }
-
-    // Webkit browsers on OS X abort momentum scrolls when the target
-    // of the scroll event is removed from the scrollable element.
-    // This hack (see related code in patchDisplay) makes sure the
-    // element is kept around.
-    if (dy && mac && webkit) {
-      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
-        for (var i = 0; i < view.length; i++) {
-          if (view[i].node == cur) {
-            cm.display.currentWheelTarget = cur;
-            break outer
-          }
-        }
-      }
-    }
-
-    // On some browsers, horizontal scrolling will cause redraws to
-    // happen before the gutter has been realigned, causing it to
-    // wriggle around in a most unseemly way. When we have an
-    // estimated pixels/delta value, we just handle horizontal
-    // scrolling entirely here. It'll be slightly off from native, but
-    // better than glitching out.
-    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
-      if (dy && canScrollY)
-        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
-      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
-      // Only prevent default scrolling if vertical scrolling is
-      // actually possible. Otherwise, it causes vertical scroll
-      // jitter on OSX trackpads when deltaX is small and deltaY
-      // is large (issue #3579)
-      if (!dy || (dy && canScrollY))
-        { e_preventDefault(e); }
-      display.wheelStartX = null; // Abort measurement, if in progress
-      return
-    }
-
-    // 'Project' the visible viewport to cover the area that is being
-    // scrolled into view (if we know enough to estimate it).
-    if (dy && wheelPixelsPerUnit != null) {
-      var pixels = dy * wheelPixelsPerUnit;
-      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
-      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
-      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
-      updateDisplaySimple(cm, {top: top, bottom: bot});
-    }
-
-    if (wheelSamples < 20) {
-      if (display.wheelStartX == null) {
-        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
-        display.wheelDX = dx; display.wheelDY = dy;
-        setTimeout(function () {
-          if (display.wheelStartX == null) { return }
-          var movedX = scroll.scrollLeft - display.wheelStartX;
-          var movedY = scroll.scrollTop - display.wheelStartY;
-          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
-            (movedX && display.wheelDX && movedX / display.wheelDX);
-          display.wheelStartX = display.wheelStartY = null;
-          if (!sample) { return }
-          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
-          ++wheelSamples;
-        }, 200);
-      } else {
-        display.wheelDX += dx; display.wheelDY += dy;
-      }
-    }
-  }
-
-  // Selection objects are immutable. A new one is created every time
-  // the selection changes. A selection is one or more non-overlapping
-  // (and non-touching) ranges, sorted, and an integer that indicates
-  // which one is the primary selection (the one that's scrolled into
-  // view, that getCursor returns, etc).
-  var Selection = function(ranges, primIndex) {
-    this.ranges = ranges;
-    this.primIndex = primIndex;
-  };
-
-  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
-
-  Selection.prototype.equals = function (other) {
-      var this$1 = this;
-
-    if (other == this) { return true }
-    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var here = this$1.ranges[i], there = other.ranges[i];
-      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
-    }
-    return true
-  };
-
-  Selection.prototype.deepCopy = function () {
-      var this$1 = this;
-
-    var out = [];
-    for (var i = 0; i < this.ranges.length; i++)
-      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
-    return new Selection(out, this.primIndex)
-  };
-
-  Selection.prototype.somethingSelected = function () {
-      var this$1 = this;
-
-    for (var i = 0; i < this.ranges.length; i++)
-      { if (!this$1.ranges[i].empty()) { return true } }
-    return false
-  };
-
-  Selection.prototype.contains = function (pos, end) {
-      var this$1 = this;
-
-    if (!end) { end = pos; }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var range = this$1.ranges[i];
-      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
-        { return i }
-    }
-    return -1
-  };
-
-  var Range = function(anchor, head) {
-    this.anchor = anchor; this.head = head;
-  };
-
-  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
-  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
-  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
-
-  // Take an unsorted, potentially overlapping set of ranges, and
-  // build a selection out of it. 'Consumes' ranges array (modifying
-  // it).
-  function normalizeSelection(cm, ranges, primIndex) {
-    var mayTouch = cm && cm.options.selectionsMayTouch;
-    var prim = ranges[primIndex];
-    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
-    primIndex = indexOf(ranges, prim);
-    for (var i = 1; i < ranges.length; i++) {
-      var cur = ranges[i], prev = ranges[i - 1];
-      var diff = cmp(prev.to(), cur.from());
-      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
-        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
-        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
-        if (i <= primIndex) { --primIndex; }
-        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
-      }
-    }
-    return new Selection(ranges, primIndex)
-  }
-
-  function simpleSelection(anchor, head) {
-    return new Selection([new Range(anchor, head || anchor)], 0)
-  }
-
-  // Compute the position of the end of a change (its 'to' property
-  // refers to the pre-change end).
-  function changeEnd(change) {
-    if (!change.text) { return change.to }
-    return Pos(change.from.line + change.text.length - 1,
-               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
-  }
-
-  // Adjust a position to refer to the post-change position of the
-  // same text, or the end of the change if the change covers it.
-  function adjustForChange(pos, change) {
-    if (cmp(pos, change.from) < 0) { return pos }
-    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
-
-    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
-    return Pos(line, ch)
-  }
-
-  function computeSelAfterChange(doc, change) {
-    var out = [];
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      var range = doc.sel.ranges[i];
-      out.push(new Range(adjustForChange(range.anchor, change),
-                         adjustForChange(range.head, change)));
-    }
-    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
-  }
-
-  function offsetPos(pos, old, nw) {
-    if (pos.line == old.line)
-      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
-    else
-      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
-  }
-
-  // Used by replaceSelections to allow moving the selection to the
-  // start or around the replaced test. Hint may be "start" or "around".
-  function computeReplacedSel(doc, changes, hint) {
-    var out = [];
-    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
-    for (var i = 0; i < changes.length; i++) {
-      var change = changes[i];
-      var from = offsetPos(change.from, oldPrev, newPrev);
-      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
-      oldPrev = change.to;
-      newPrev = to;
-      if (hint == "around") {
-        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
-        out[i] = new Range(inv ? to : from, inv ? from : to);
-      } else {
-        out[i] = new Range(from, from);
-      }
-    }
-    return new Selection(out, doc.sel.primIndex)
-  }
-
-  // Used to get the editor into a consistent state again when options change.
-
-  function loadMode(cm) {
-    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
-    resetModeState(cm);
-  }
-
-  function resetModeState(cm) {
-    cm.doc.iter(function (line) {
-      if (line.stateAfter) { line.stateAfter = null; }
-      if (line.styles) { line.styles = null; }
-    });
-    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
-    startWorker(cm, 100);
-    cm.state.modeGen++;
-    if (cm.curOp) { regChange(cm); }
-  }
-
-  // DOCUMENT DATA STRUCTURE
-
-  // By default, updates that start and end at the beginning of a line
-  // are treated specially, in order to make the association of line
-  // widgets and marker elements with the text behave more intuitive.
-  function isWholeLineUpdate(doc, change) {
-    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
-      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
-  }
-
-  // Perform a change on the document data structure.
-  function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
-    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
-    function update(line, text, spans) {
-      updateLine(line, text, spans, estimateHeight$$1);
-      signalLater(line, "change", line, change);
-    }
-    function linesFor(start, end) {
-      var result = [];
-      for (var i = start; i < end; ++i)
-        { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
-      return result
-    }
-
-    var from = change.from, to = change.to, text = change.text;
-    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
-    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
-
-    // Adjust the line structure
-    if (change.full) {
-      doc.insert(0, linesFor(0, text.length));
-      doc.remove(text.length, doc.size - text.length);
-    } else if (isWholeLineUpdate(doc, change)) {
-      // This is a whole-line replace. Treated specially to make
-      // sure line objects move the way they are supposed to.
-      var added = linesFor(0, text.length - 1);
-      update(lastLine, lastLine.text, lastSpans);
-      if (nlines) { doc.remove(from.line, nlines); }
-      if (added.length) { doc.insert(from.line, added); }
-    } else if (firstLine == lastLine) {
-      if (text.length == 1) {
-        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
-      } else {
-        var added$1 = linesFor(1, text.length - 1);
-        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
-        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-        doc.insert(from.line + 1, added$1);
-      }
-    } else if (text.length == 1) {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
-      doc.remove(from.line + 1, nlines);
-    } else {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
-      var added$2 = linesFor(1, text.length - 1);
-      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
-      doc.insert(from.line + 1, added$2);
-    }
-
-    signalLater(doc, "change", doc, change);
-  }
-
-  // Call f for all linked documents.
-  function linkedDocs(doc, f, sharedHistOnly) {
-    function propagate(doc, skip, sharedHist) {
-      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
-        var rel = doc.linked[i];
-        if (rel.doc == skip) { continue }
-        var shared = sharedHist && rel.sharedHist;
-        if (sharedHistOnly && !shared) { continue }
-        f(rel.doc, shared);
-        propagate(rel.doc, doc, shared);
-      } }
-    }
-    propagate(doc, null, true);
-  }
-
-  // Attach a document to an editor.
-  function attachDoc(cm, doc) {
-    if (doc.cm) { throw new Error("This document is already in use.") }
-    cm.doc = doc;
-    doc.cm = cm;
-    estimateLineHeights(cm);
-    loadMode(cm);
-    setDirectionClass(cm);
-    if (!cm.options.lineWrapping) { findMaxLine(cm); }
-    cm.options.mode = doc.modeOption;
-    regChange(cm);
-  }
-
-  function setDirectionClass(cm) {
-  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
-  }
-
-  function directionChanged(cm) {
-    runInOp(cm, function () {
-      setDirectionClass(cm);
-      regChange(cm);
-    });
-  }
-
-  function History(startGen) {
-    // Arrays of change events and selections. Doing something adds an
-    // event to done and clears undo. Undoing moves events from done
-    // to undone, redoing moves them in the other direction.
-    this.done = []; this.undone = [];
-    this.undoDepth = Infinity;
-    // Used to track when changes can be merged into a single undo
-    // event
-    this.lastModTime = this.lastSelTime = 0;
-    this.lastOp = this.lastSelOp = null;
-    this.lastOrigin = this.lastSelOrigin = null;
-    // Used by the isClean() method
-    this.generation = this.maxGeneration = startGen || 1;
-  }
-
-  // Create a history change event from an updateDoc-style change
-  // object.
-  function historyChangeFromChange(doc, change) {
-    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
-    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
-    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
-    return histChange
-  }
-
-  // Pop all selection events off the end of a history array. Stop at
-  // a change event.
-  function clearSelectionEvents(array) {
-    while (array.length) {
-      var last = lst(array);
-      if (last.ranges) { array.pop(); }
-      else { break }
-    }
-  }
-
-  // Find the top change event in the history. Pop off selection
-  // events that are in the way.
-  function lastChangeEvent(hist, force) {
-    if (force) {
-      clearSelectionEvents(hist.done);
-      return lst(hist.done)
-    } else if (hist.done.length && !lst(hist.done).ranges) {
-      return lst(hist.done)
-    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
-      hist.done.pop();
-      return lst(hist.done)
-    }
-  }
-
-  // Register a change in the history. Merges changes that are within
-  // a single operation, or are close together with an origin that
-  // allows merging (starting with "+") into a single event.
-  function addChangeToHistory(doc, change, selAfter, opId) {
-    var hist = doc.history;
-    hist.undone.length = 0;
-    var time = +new Date, cur;
-    var last;
-
-    if ((hist.lastOp == opId ||
-         hist.lastOrigin == change.origin && change.origin &&
-         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
-          change.origin.charAt(0) == "*")) &&
-        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
-      // Merge this change into the last event
-      last = lst(cur.changes);
-      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
-        // Optimized case for simple insertion -- don't want to add
-        // new changesets for every character typed
-        last.to = changeEnd(change);
-      } else {
-        // Add new sub-event
-        cur.changes.push(historyChangeFromChange(doc, change));
-      }
-    } else {
-      // Can not be merged, start a new event.
-      var before = lst(hist.done);
-      if (!before || !before.ranges)
-        { pushSelectionToHistory(doc.sel, hist.done); }
-      cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation};
-      hist.done.push(cur);
-      while (hist.done.length > hist.undoDepth) {
-        hist.done.shift();
-        if (!hist.done[0].ranges) { hist.done.shift(); }
-      }
-    }
-    hist.done.push(selAfter);
-    hist.generation = ++hist.maxGeneration;
-    hist.lastModTime = hist.lastSelTime = time;
-    hist.lastOp = hist.lastSelOp = opId;
-    hist.lastOrigin = hist.lastSelOrigin = change.origin;
-
-    if (!last) { signal(doc, "historyAdded"); }
-  }
-
-  function selectionEventCanBeMerged(doc, origin, prev, sel) {
-    var ch = origin.charAt(0);
-    return ch == "*" ||
-      ch == "+" &&
-      prev.ranges.length == sel.ranges.length &&
-      prev.somethingSelected() == sel.somethingSelected() &&
-      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
-  }
-
-  // Called whenever the selection changes, sets the new selection as
-  // the pending selection in the history, and pushes the old pending
-  // selection into the 'done' array when it was significantly
-  // different (in number of selected ranges, emptiness, or time).
-  function addSelectionToHistory(doc, sel, opId, options) {
-    var hist = doc.history, origin = options && options.origin;
-
-    // A new event is started when the previous origin does not match
-    // the current, or the origins don't allow matching. Origins
-    // starting with * are always merged, those starting with + are
-    // merged when similar and close together in time.
-    if (opId == hist.lastSelOp ||
-        (origin && hist.lastSelOrigin == origin &&
-         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
-          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
-      { hist.done[hist.done.length - 1] = sel; }
-    else
-      { pushSelectionToHistory(sel, hist.done); }
-
-    hist.lastSelTime = +new Date;
-    hist.lastSelOrigin = origin;
-    hist.lastSelOp = opId;
-    if (options && options.clearRedo !== false)
-      { clearSelectionEvents(hist.undone); }
-  }
-
-  function pushSelectionToHistory(sel, dest) {
-    var top = lst(dest);
-    if (!(top && top.ranges && top.equals(sel)))
-      { dest.push(sel); }
-  }
-
-  // Used to store marked span information in the history.
-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change["spans_" + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
-      if (line.markedSpans)
-        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
-      ++n;
-    });
-  }
-
-  // When un/re-doing restores text containing marked spans, those
-  // that have been explicitly cleared should not be restored.
-  function removeClearedSpans(spans) {
-    if (!spans) { return null }
-    var out;
-    for (var i = 0; i < spans.length; ++i) {
-      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
-      else if (out) { out.push(spans[i]); }
-    }
-    return !out ? spans : out.length ? out : null
-  }
-
-  // Retrieve and filter the old marked spans stored in a change event.
-  function getOldSpans(doc, change) {
-    var found = change["spans_" + doc.id];
-    if (!found) { return null }
-    var nw = [];
-    for (var i = 0; i < change.text.length; ++i)
-      { nw.push(removeClearedSpans(found[i])); }
-    return nw
-  }
-
-  // Used for un/re-doing changes from the history. Combines the
-  // result of computing the existing spans with the set of spans that
-  // existed in the history (so that deleting around a span and then
-  // undoing brings back the span).
-  function mergeOldSpans(doc, change) {
-    var old = getOldSpans(doc, change);
-    var stretched = stretchSpansOverChange(doc, change);
-    if (!old) { return stretched }
-    if (!stretched) { return old }
-
-    for (var i = 0; i < old.length; ++i) {
-      var oldCur = old[i], stretchCur = stretched[i];
-      if (oldCur && stretchCur) {
-        spans: for (var j = 0; j < stretchCur.length; ++j) {
-          var span = stretchCur[j];
-          for (var k = 0; k < oldCur.length; ++k)
-            { if (oldCur[k].marker == span.marker) { continue spans } }
-          oldCur.push(span);
-        }
-      } else if (stretchCur) {
-        old[i] = stretchCur;
-      }
-    }
-    return old
-  }
-
-  // Used both to provide a JSON-safe object in .getHistory, and, when
-  // detaching a document, to split the history in two
-  function copyHistoryArray(events, newGroup, instantiateSel) {
-    var copy = [];
-    for (var i = 0; i < events.length; ++i) {
-      var event = events[i];
-      if (event.ranges) {
-        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
-        continue
-      }
-      var changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges});
-      for (var j = 0; j < changes.length; ++j) {
-        var change = changes[j], m = (void 0);
-        newChanges.push({from: change.from, to: change.to, text: change.text});
-        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
-          if (indexOf(newGroup, Number(m[1])) > -1) {
-            lst(newChanges)[prop] = change[prop];
-            delete change[prop];
-          }
-        } } }
-      }
-    }
-    return copy
-  }
-
-  // The 'scroll' parameter given to many of these indicated whether
-  // the new cursor position should be scrolled into view after
-  // modifying the selection.
-
-  // If shift is held or the extend flag is set, extends a range to
-  // include a given position (and optionally a second position).
-  // Otherwise, simply returns the range between the given positions.
-  // Used for cursor motion and such.
-  function extendRange(range, head, other, extend) {
-    if (extend) {
-      var anchor = range.anchor;
-      if (other) {
-        var posBefore = cmp(head, anchor) < 0;
-        if (posBefore != (cmp(other, anchor) < 0)) {
-          anchor = head;
-          head = other;
-        } else if (posBefore != (cmp(head, other) < 0)) {
-          head = other;
-        }
-      }
-      return new Range(anchor, head)
-    } else {
-      return new Range(other || head, head)
-    }
-  }
-
-  // Extend the primary selection range, discard the rest.
-  function extendSelection(doc, head, other, options, extend) {
-    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
-    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
-  }
-
-  // Extend all selections (pos is an array of selections with length
-  // equal the number of selections)
-  function extendSelections(doc, heads, options) {
-    var out = [];
-    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
-    for (var i = 0; i < doc.sel.ranges.length; i++)
-      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
-    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
-    setSelection(doc, newSel, options);
-  }
-
-  // Updates a single range in the selection.
-  function replaceOneSelection(doc, i, range, options) {
-    var ranges = doc.sel.ranges.slice(0);
-    ranges[i] = range;
-    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
-  }
-
-  // Reset the selection to a single range.
-  function setSimpleSelection(doc, anchor, head, options) {
-    setSelection(doc, simpleSelection(anchor, head), options);
-  }
-
-  // Give beforeSelectionChange handlers a change to influence a
-  // selection update.
-  function filterSelectionChange(doc, sel, options) {
-    var obj = {
-      ranges: sel.ranges,
-      update: function(ranges) {
-        var this$1 = this;
-
-        this.ranges = [];
-        for (var i = 0; i < ranges.length; i++)
-          { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
-                                     clipPos(doc, ranges[i].head)); }
-      },
-      origin: options && options.origin
-    };
-    signal(doc, "beforeSelectionChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
-    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
-    else { return sel }
-  }
-
-  function setSelectionReplaceHistory(doc, sel, options) {
-    var done = doc.history.done, last = lst(done);
-    if (last && last.ranges) {
-      done[done.length - 1] = sel;
-      setSelectionNoUndo(doc, sel, options);
-    } else {
-      setSelection(doc, sel, options);
-    }
-  }
-
-  // Set a new selection.
-  function setSelection(doc, sel, options) {
-    setSelectionNoUndo(doc, sel, options);
-    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
-  }
-
-  function setSelectionNoUndo(doc, sel, options) {
-    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
-      { sel = filterSelectionChange(doc, sel, options); }
-
-    var bias = options && options.bias ||
-      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
-    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
-
-    if (!(options && options.scroll === false) && doc.cm)
-      { ensureCursorVisible(doc.cm); }
-  }
-
-  function setSelectionInner(doc, sel) {
-    if (sel.equals(doc.sel)) { return }
-
-    doc.sel = sel;
-
-    if (doc.cm) {
-      doc.cm.curOp.updateInput = 1;
-      doc.cm.curOp.selectionChanged = true;
-      signalCursorActivity(doc.cm);
-    }
-    signalLater(doc, "cursorActivity", doc);
-  }
-
-  // Verify that the selection does not partially select any atomic
-  // marked ranges.
-  function reCheckSelection(doc) {
-    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
-  }
-
-  // Return a selection that does not partially select any atomic
-  // ranges.
-  function skipAtomicInSelection(doc, sel, bias, mayClear) {
-    var out;
-    for (var i = 0; i < sel.ranges.length; i++) {
-      var range = sel.ranges[i];
-      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
-      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
-      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
-      if (out || newAnchor != range.anchor || newHead != range.head) {
-        if (!out) { out = sel.ranges.slice(0, i); }
-        out[i] = new Range(newAnchor, newHead);
-      }
-    }
-    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
-  }
-
-  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
-    var line = getLine(doc, pos.line);
-    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-      var sp = line.markedSpans[i], m = sp.marker;
-
-      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
-      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
-      // is with selectLeft/Right
-      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
-      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
-
-      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
-          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
-        if (mayClear) {
-          signal(m, "beforeCursorEnter");
-          if (m.explicitlyCleared) {
-            if (!line.markedSpans) { break }
-            else {--i; continue}
-          }
-        }
-        if (!m.atomic) { continue }
-
-        if (oldPos) {
-          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
-          if (dir < 0 ? preventCursorRight : preventCursorLeft)
-            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
-          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
-            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
-        }
-
-        var far = m.find(dir < 0 ? -1 : 1);
-        if (dir < 0 ? preventCursorLeft : preventCursorRight)
-          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
-        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
-      }
-    } }
-    return pos
-  }
-
-  // Ensure a given position is not inside an atomic range.
-  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
-    var dir = bias || 1;
-    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
-        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
-    if (!found) {
-      doc.cantEdit = true;
-      return Pos(doc.first, 0)
-    }
-    return found
-  }
-
-  function movePos(doc, pos, dir, line) {
-    if (dir < 0 && pos.ch == 0) {
-      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
-      else { return null }
-    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
-      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
-      else { return null }
-    } else {
-      return new Pos(pos.line, pos.ch + dir)
-    }
-  }
-
-  function selectAll(cm) {
-    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
-  }
-
-  // UPDATING
-
-  // Allow "beforeChange" event handlers to influence a change
-  function filterChange(doc, change, update) {
-    var obj = {
-      canceled: false,
-      from: change.from,
-      to: change.to,
-      text: change.text,
-      origin: change.origin,
-      cancel: function () { return obj.canceled = true; }
-    };
-    if (update) { obj.update = function (from, to, text, origin) {
-      if (from) { obj.from = clipPos(doc, from); }
-      if (to) { obj.to = clipPos(doc, to); }
-      if (text) { obj.text = text; }
-      if (origin !== undefined) { obj.origin = origin; }
-    }; }
-    signal(doc, "beforeChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
-
-    if (obj.canceled) {
-      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
-      return null
-    }
-    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
-  }
-
-  // Apply a change to a document, and add it to the document's
-  // history, and propagating it to all linked documents.
-  function makeChange(doc, change, ignoreReadOnly) {
-    if (doc.cm) {
-      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
-      if (doc.cm.state.suppressEdits) { return }
-    }
-
-    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
-      if (!change) { return }
-    }
-
-    // Possibly split or suppress the update based on the presence
-    // of read-only spans in its range.
-    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
-    if (split) {
-      for (var i = split.length - 1; i >= 0; --i)
-        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
-    } else {
-      makeChangeInner(doc, change);
-    }
-  }
-
-  function makeChangeInner(doc, change) {
-    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
-    var selAfter = computeSelAfterChange(doc, change);
-    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
-
-    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
-    var rebased = [];
-
-    linkedDocs(doc, function (doc, sharedHist) {
-      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-        rebaseHist(doc.history, change);
-        rebased.push(doc.history);
-      }
-      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
-    });
-  }
-
-  // Revert a change stored in a document's history.
-  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
-    var suppress = doc.cm && doc.cm.state.suppressEdits;
-    if (suppress && !allowSelectionOnly) { return }
-
-    var hist = doc.history, event, selAfter = doc.sel;
-    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
-
-    // Verify that there is a useable event (so that ctrl-z won't
-    // needlessly clear selection events)
-    var i = 0;
-    for (; i < source.length; i++) {
-      event = source[i];
-      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
-        { break }
-    }
-    if (i == source.length) { return }
-    hist.lastOrigin = hist.lastSelOrigin = null;
-
-    for (;;) {
-      event = source.pop();
-      if (event.ranges) {
-        pushSelectionToHistory(event, dest);
-        if (allowSelectionOnly && !event.equals(doc.sel)) {
-          setSelection(doc, event, {clearRedo: false});
-          return
-        }
-        selAfter = event;
-      } else if (suppress) {
-        source.push(event);
-        return
-      } else { break }
-    }
-
-    // Build up a reverse change object to add to the opposite history
-    // stack (redo when undoing, and vice versa).
-    var antiChanges = [];
-    pushSelectionToHistory(selAfter, dest);
-    dest.push({changes: antiChanges, generation: hist.generation});
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
-
-    var loop = function ( i ) {
-      var change = event.changes[i];
-      change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        source.length = 0;
-        return {}
-      }
-
-      antiChanges.push(historyChangeFromChange(doc, change));
-
-      var after = i ? computeSelAfterChange(doc, change) : lst(source);
-      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
-      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
-      var rebased = [];
-
-      // Propagate to the linked documents
-      linkedDocs(doc, function (doc, sharedHist) {
-        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-          rebaseHist(doc.history, change);
-          rebased.push(doc.history);
-        }
-        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
-      });
-    };
-
-    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
-      var returned = loop( i$1 );
-
-      if ( returned ) return returned.v;
-    }
-  }
-
-  // Sub-views need their line numbers shifted when text is added
-  // above or below them in the parent document.
-  function shiftDoc(doc, distance) {
-    if (distance == 0) { return }
-    doc.first += distance;
-    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
-      Pos(range.anchor.line + distance, range.anchor.ch),
-      Pos(range.head.line + distance, range.head.ch)
-    ); }), doc.sel.primIndex);
-    if (doc.cm) {
-      regChange(doc.cm, doc.first, doc.first - distance, distance);
-      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
-        { regLineChange(doc.cm, l, "gutter"); }
-    }
-  }
-
-  // More lower-level change function, handling only a single document
-  // (not linked ones).
-  function makeChangeSingleDoc(doc, change, selAfter, spans) {
-    if (doc.cm && !doc.cm.curOp)
-      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
-
-    if (change.to.line < doc.first) {
-      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
-      return
-    }
-    if (change.from.line > doc.lastLine()) { return }
-
-    // Clip the change to the size of this doc
-    if (change.from.line < doc.first) {
-      var shift = change.text.length - 1 - (doc.first - change.from.line);
-      shiftDoc(doc, shift);
-      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
-                text: [lst(change.text)], origin: change.origin};
-    }
-    var last = doc.lastLine();
-    if (change.to.line > last) {
-      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
-                text: [change.text[0]], origin: change.origin};
-    }
-
-    change.removed = getBetween(doc, change.from, change.to);
-
-    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
-    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
-    else { updateDoc(doc, change, spans); }
-    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
-
-    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
-      { doc.cantEdit = false; }
-  }
-
-  // Handle the interaction of a change to a document with the editor
-  // that this document is part of.
-  function makeChangeSingleDocInEditor(cm, change, spans) {
-    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
-
-    var recomputeMaxLength = false, checkWidthStart = from.line;
-    if (!cm.options.lineWrapping) {
-      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
-      doc.iter(checkWidthStart, to.line + 1, function (line) {
-        if (line == display.maxLine) {
-          recomputeMaxLength = true;
-          return true
-        }
-      });
-    }
-
-    if (doc.sel.contains(change.from, change.to) > -1)
-      { signalCursorActivity(cm); }
-
-    updateDoc(doc, change, spans, estimateHeight(cm));
-
-    if (!cm.options.lineWrapping) {
-      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
-        var len = lineLength(line);
-        if (len > display.maxLineLength) {
-          display.maxLine = line;
-          display.maxLineLength = len;
-          display.maxLineChanged = true;
-          recomputeMaxLength = false;
-        }
-      });
-      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
-    }
-
-    retreatFrontier(doc, from.line);
-    startWorker(cm, 400);
-
-    var lendiff = change.text.length - (to.line - from.line) - 1;
-    // Remember that these lines changed, for updating the display
-    if (change.full)
-      { regChange(cm); }
-    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
-      { regLineChange(cm, from.line, "text"); }
-    else
-      { regChange(cm, from.line, to.line + 1, lendiff); }
-
-    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
-    if (changeHandler || changesHandler) {
-      var obj = {
-        from: from, to: to,
-        text: change.text,
-        removed: change.removed,
-        origin: change.origin
-      };
-      if (changeHandler) { signalLater(cm, "change", cm, obj); }
-      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
-    }
-    cm.display.selForContextMenu = null;
-  }
-
-  function replaceRange(doc, code, from, to, origin) {
-    var assign;
-
-    if (!to) { to = from; }
-    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
-    if (typeof code == "string") { code = doc.splitLines(code); }
-    makeChange(doc, {from: from, to: to, text: code, origin: origin});
-  }
-
-  // Rebasing/resetting history to deal with externally-sourced changes
-
-  function rebaseHistSelSingle(pos, from, to, diff) {
-    if (to < pos.line) {
-      pos.line += diff;
-    } else if (from < pos.line) {
-      pos.line = from;
-      pos.ch = 0;
-    }
-  }
-
-  // Tries to rebase an array of history events given a change in the
-  // document. If the change touches the same lines as the event, the
-  // event, and everything 'behind' it, is discarded. If the change is
-  // before the event, the event's positions are updated. Uses a
-  // copy-on-write scheme for the positions, to avoid having to
-  // reallocate them all on every rebase, but also avoid problems with
-  // shared position objects being unsafely updated.
-  function rebaseHistArray(array, from, to, diff) {
-    for (var i = 0; i < array.length; ++i) {
-      var sub = array[i], ok = true;
-      if (sub.ranges) {
-        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
-        for (var j = 0; j < sub.ranges.length; j++) {
-          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
-          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
-        }
-        continue
-      }
-      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
-        var cur = sub.changes[j$1];
-        if (to < cur.from.line) {
-          cur.from = Pos(cur.from.line + diff, cur.from.ch);
-          cur.to = Pos(cur.to.line + diff, cur.to.ch);
-        } else if (from <= cur.to.line) {
-          ok = false;
-          break
-        }
-      }
-      if (!ok) {
-        array.splice(0, i + 1);
-        i = 0;
-      }
-    }
-  }
-
-  function rebaseHist(hist, change) {
-    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
-    rebaseHistArray(hist.done, from, to, diff);
-    rebaseHistArray(hist.undone, from, to, diff);
-  }
-
-  // Utility for applying a change to a line by handle or number,
-  // returning the number and optionally registering the line as
-  // changed.
-  function changeLine(doc, handle, changeType, op) {
-    var no = handle, line = handle;
-    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
-    else { no = lineNo(handle); }
-    if (no == null) { return null }
-    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
-    return line
-  }
-
-  // The document is represented as a BTree consisting of leaves, with
-  // chunk of lines in them, and branches, with up to ten leaves or
-  // other branch nodes below them. The top node is always a branch
-  // node, and is the document object itself (meaning it has
-  // additional methods and properties).
-  //
-  // All nodes have parent links. The tree is used both to go from
-  // line numbers to line objects, and to go from objects to numbers.
-  // It also indexes by height, and is used to convert between height
-  // and line object, and to find the total height of the document.
-  //
-  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
-
-  function LeafChunk(lines) {
-    var this$1 = this;
-
-    this.lines = lines;
-    this.parent = null;
-    var height = 0;
-    for (var i = 0; i < lines.length; ++i) {
-      lines[i].parent = this$1;
-      height += lines[i].height;
-    }
-    this.height = height;
-  }
-
-  LeafChunk.prototype = {
-    chunkSize: function() { return this.lines.length },
-
-    // Remove the n lines at offset 'at'.
-    removeInner: function(at, n) {
-      var this$1 = this;
-
-      for (var i = at, e = at + n; i < e; ++i) {
-        var line = this$1.lines[i];
-        this$1.height -= line.height;
-        cleanUpLine(line);
-        signalLater(line, "delete");
-      }
-      this.lines.splice(at, n);
-    },
-
-    // Helper used to collapse a small branch into a single leaf.
-    collapse: function(lines) {
-      lines.push.apply(lines, this.lines);
-    },
-
-    // Insert the given array of lines at offset 'at', count them as
-    // having the given height.
-    insertInner: function(at, lines, height) {
-      var this$1 = this;
-
-      this.height += height;
-      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
-      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
-    },
-
-    // Used to iterate over a part of the tree.
-    iterN: function(at, n, op) {
-      var this$1 = this;
-
-      for (var e = at + n; at < e; ++at)
-        { if (op(this$1.lines[at])) { return true } }
-    }
-  };
-
-  function BranchChunk(children) {
-    var this$1 = this;
-
-    this.children = children;
-    var size = 0, height = 0;
-    for (var i = 0; i < children.length; ++i) {
-      var ch = children[i];
-      size += ch.chunkSize(); height += ch.height;
-      ch.parent = this$1;
-    }
-    this.size = size;
-    this.height = height;
-    this.parent = null;
-  }
-
-  BranchChunk.prototype = {
-    chunkSize: function() { return this.size },
-
-    removeInner: function(at, n) {
-      var this$1 = this;
-
-      this.size -= n;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this$1.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var rm = Math.min(n, sz - at), oldHeight = child.height;
-          child.removeInner(at, rm);
-          this$1.height -= oldHeight - child.height;
-          if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
-          if ((n -= rm) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-      // If the result is smaller than 25 lines, ensure that it is a
-      // single leaf node.
-      if (this.size - n < 25 &&
-          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
-        var lines = [];
-        this.collapse(lines);
-        this.children = [new LeafChunk(lines)];
-        this.children[0].parent = this;
-      }
-    },
-
-    collapse: function(lines) {
-      var this$1 = this;
-
-      for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
-    },
-
-    insertInner: function(at, lines, height) {
-      var this$1 = this;
-
-      this.size += lines.length;
-      this.height += height;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this$1.children[i], sz = child.chunkSize();
-        if (at <= sz) {
-          child.insertInner(at, lines, height);
-          if (child.lines && child.lines.length > 50) {
-            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
-            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
-            var remaining = child.lines.length % 25 + 25;
-            for (var pos = remaining; pos < child.lines.length;) {
-              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
-              child.height -= leaf.height;
-              this$1.children.splice(++i, 0, leaf);
-              leaf.parent = this$1;
-            }
-            child.lines = child.lines.slice(0, remaining);
-            this$1.maybeSpill();
-          }
-          break
-        }
-        at -= sz;
-      }
-    },
-
-    // When a node has grown, check whether it should be split.
-    maybeSpill: function() {
-      if (this.children.length <= 10) { return }
-      var me = this;
-      do {
-        var spilled = me.children.splice(me.children.length - 5, 5);
-        var sibling = new BranchChunk(spilled);
-        if (!me.parent) { // Become the parent node
-          var copy = new BranchChunk(me.children);
-          copy.parent = me;
-          me.children = [copy, sibling];
-          me = copy;
-       } else {
-          me.size -= sibling.size;
-          me.height -= sibling.height;
-          var myIndex = indexOf(me.parent.children, me);
-          me.parent.children.splice(myIndex + 1, 0, sibling);
-        }
-        sibling.parent = me.parent;
-      } while (me.children.length > 10)
-      me.parent.maybeSpill();
-    },
-
-    iterN: function(at, n, op) {
-      var this$1 = this;
-
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this$1.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var used = Math.min(n, sz - at);
-          if (child.iterN(at, used, op)) { return true }
-          if ((n -= used) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-    }
-  };
-
-  // Line widgets are block elements displayed above or below a line.
-
-  var LineWidget = function(doc, node, options) {
-    var this$1 = this;
-
-    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
-      { this$1[opt] = options[opt]; } } }
-    this.doc = doc;
-    this.node = node;
-  };
-
-  LineWidget.prototype.clear = function () {
-      var this$1 = this;
-
-    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
-    if (no == null || !ws) { return }
-    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
-    if (!ws.length) { line.widgets = null; }
-    var height = widgetHeight(this);
-    updateLineHeight(line, Math.max(0, line.height - height));
-    if (cm) {
-      runInOp(cm, function () {
-        adjustScrollWhenAboveVisible(cm, line, -height);
-        regLineChange(cm, no, "widget");
-      });
-      signalLater(cm, "lineWidgetCleared", cm, this, no);
-    }
-  };
-
-  LineWidget.prototype.changed = function () {
-      var this$1 = this;
-
-    var oldH = this.height, cm = this.doc.cm, line = this.line;
-    this.height = null;
-    var diff = widgetHeight(this) - oldH;
-    if (!diff) { return }
-    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
-    if (cm) {
-      runInOp(cm, function () {
-        cm.curOp.forceUpdate = true;
-        adjustScrollWhenAboveVisible(cm, line, diff);
-        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
-      });
-    }
-  };
-  eventMixin(LineWidget);
-
-  function adjustScrollWhenAboveVisible(cm, line, diff) {
-    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
-      { addToScrollTop(cm, diff); }
-  }
-
-  function addLineWidget(doc, handle, node, options) {
-    var widget = new LineWidget(doc, node, options);
-    var cm = doc.cm;
-    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
-    changeLine(doc, handle, "widget", function (line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) { widgets.push(widget); }
-      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
-      widget.line = line;
-      if (cm && !lineIsHidden(doc, line)) {
-        var aboveVisible = heightAtLine(line) < doc.scrollTop;
-        updateLineHeight(line, line.height + widgetHeight(widget));
-        if (aboveVisible) { addToScrollTop(cm, widget.height); }
-        cm.curOp.forceUpdate = true;
-      }
-      return true
-    });
-    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
-    return widget
-  }
-
-  // TEXTMARKERS
-
-  // Created with markText and setBookmark methods. A TextMarker is a
-  // handle that can be used to clear or find a marked position in the
-  // document. Line objects hold arrays (markedSpans) containing
-  // {from, to, marker} object pointing to such marker objects, and
-  // indicating that such a marker is present on that line. Multiple
-  // lines may point to the same marker when it spans across lines.
-  // The spans will have null for their from/to properties when the
-  // marker continues beyond the start/end of the line. Markers have
-  // links back to the lines they currently touch.
-
-  // Collapsed markers have unique ids, in order to be able to order
-  // them, which is needed for uniquely determining an outer marker
-  // when they overlap (they may nest, but not partially overlap).
-  var nextMarkerId = 0;
-
-  var TextMarker = function(doc, type) {
-    this.lines = [];
-    this.type = type;
-    this.doc = doc;
-    this.id = ++nextMarkerId;
-  };
-
-  // Clear the marker.
-  TextMarker.prototype.clear = function () {
-      var this$1 = this;
-
-    if (this.explicitlyCleared) { return }
-    var cm = this.doc.cm, withOp = cm && !cm.curOp;
-    if (withOp) { startOperation(cm); }
-    if (hasHandler(this, "clear")) {
-      var found = this.find();
-      if (found) { signalLater(this, "clear", found.from, found.to); }
-    }
-    var min = null, max = null;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this$1.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this$1);
-      if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
-      else if (cm) {
-        if (span.to != null) { max = lineNo(line); }
-        if (span.from != null) { min = lineNo(line); }
-      }
-      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
-      if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
-        { updateLineHeight(line, textHeight(cm.display)); }
-    }
-    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
-      var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
-      if (len > cm.display.maxLineLength) {
-        cm.display.maxLine = visual;
-        cm.display.maxLineLength = len;
-        cm.display.maxLineChanged = true;
-      }
-    } }
-
-    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
-    this.lines.length = 0;
-    this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
-      this.doc.cantEdit = false;
-      if (cm) { reCheckSelection(cm.doc); }
-    }
-    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
-    if (withOp) { endOperation(cm); }
-    if (this.parent) { this.parent.clear(); }
-  };
-
-  // Find the position of the marker in the document. Returns a {from,
-  // to} object by default. Side can be passed to get a specific side
-  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
-  // Pos objects returned contain a line object, rather than a line
-  // number (used to prevent looking up the same line twice).
-  TextMarker.prototype.find = function (side, lineObj) {
-      var this$1 = this;
-
-    if (side == null && this.type == "bookmark") { side = 1; }
-    var from, to;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this$1.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this$1);
-      if (span.from != null) {
-        from = Pos(lineObj ? line : lineNo(line), span.from);
-        if (side == -1) { return from }
-      }
-      if (span.to != null) {
-        to = Pos(lineObj ? line : lineNo(line), span.to);
-        if (side == 1) { return to }
-      }
-    }
-    return from && {from: from, to: to}
-  };
-
-  // Signals that the marker's widget changed, and surrounding layout
-  // should be recomputed.
-  TextMarker.prototype.changed = function () {
-      var this$1 = this;
-
-    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
-    if (!pos || !cm) { return }
-    runInOp(cm, function () {
-      var line = pos.line, lineN = lineNo(pos.line);
-      var view = findViewForLine(cm, lineN);
-      if (view) {
-        clearLineMeasurementCacheFor(view);
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
-      }
-      cm.curOp.updateMaxLine = true;
-      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
-        var oldHeight = widget.height;
-        widget.height = null;
-        var dHeight = widgetHeight(widget) - oldHeight;
-        if (dHeight)
-          { updateLineHeight(line, line.height + dHeight); }
-      }
-      signalLater(cm, "markerChanged", cm, this$1);
-    });
-  };
-
-  TextMarker.prototype.attachLine = function (line) {
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp;
-      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
-        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
-    }
-    this.lines.push(line);
-  };
-
-  TextMarker.prototype.detachLine = function (line) {
-    this.lines.splice(indexOf(this.lines, line), 1);
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp
-      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
-    }
-  };
-  eventMixin(TextMarker);
-
-  // Create a marker, wire it up to the right lines, and
-  function markText(doc, from, to, options, type) {
-    // Shared markers (across linked documents) are handled separately
-    // (markTextShared will call out to this again, once per
-    // document).
-    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
-    // Ensure we are in an operation.
-    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
-
-    var marker = new TextMarker(doc, type), diff = cmp(from, to);
-    if (options) { copyObj(options, marker, false); }
-    // Don't connect empty markers unless clearWhenEmpty is false
-    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
-      { return marker }
-    if (marker.replacedWith) {
-      // Showing up as a widget implies collapsed (widget replaces text)
-      marker.collapsed = true;
-      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
-      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
-    }
-    if (marker.collapsed) {
-      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
-          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
-        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
-      seeCollapsedSpans();
-    }
-
-    if (marker.addToHistory)
-      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
-
-    var curLine = from.line, cm = doc.cm, updateMaxLine;
-    doc.iter(curLine, to.line + 1, function (line) {
-      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
-        { updateMaxLine = true; }
-      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
-      addMarkedSpan(line, new MarkedSpan(marker,
-                                         curLine == from.line ? from.ch : null,
-                                         curLine == to.line ? to.ch : null));
-      ++curLine;
-    });
-    // lineIsHidden depends on the presence of the spans, so needs a second pass
-    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
-      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
-    }); }
-
-    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
-
-    if (marker.readOnly) {
-      seeReadOnlySpans();
-      if (doc.history.done.length || doc.history.undone.length)
-        { doc.clearHistory(); }
-    }
-    if (marker.collapsed) {
-      marker.id = ++nextMarkerId;
-      marker.atomic = true;
-    }
-    if (cm) {
-      // Sync editor state
-      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
-      if (marker.collapsed)
-        { regChange(cm, from.line, to.line + 1); }
-      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
-               marker.attributes || marker.title)
-        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
-      if (marker.atomic) { reCheckSelection(cm.doc); }
-      signalLater(cm, "markerAdded", cm, marker);
-    }
-    return marker
-  }
-
-  // SHARED TEXTMARKERS
-
-  // A shared marker spans multiple linked documents. It is
-  // implemented as a meta-marker-object controlling multiple normal
-  // markers.
-  var SharedTextMarker = function(markers, primary) {
-    var this$1 = this;
-
-    this.markers = markers;
-    this.primary = primary;
-    for (var i = 0; i < markers.length; ++i)
-      { markers[i].parent = this$1; }
-  };
-
-  SharedTextMarker.prototype.clear = function () {
-      var this$1 = this;
-
-    if (this.explicitlyCleared) { return }
-    this.explicitlyCleared = true;
-    for (var i = 0; i < this.markers.length; ++i)
-      { this$1.markers[i].clear(); }
-    signalLater(this, "clear");
-  };
-
-  SharedTextMarker.prototype.find = function (side, lineObj) {
-    return this.primary.find(side, lineObj)
-  };
-  eventMixin(SharedTextMarker);
-
-  function markTextShared(doc, from, to, options, type) {
-    options = copyObj(options);
-    options.shared = false;
-    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
-    var widget = options.widgetNode;
-    linkedDocs(doc, function (doc) {
-      if (widget) { options.widgetNode = widget.cloneNode(true); }
-      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
-      for (var i = 0; i < doc.linked.length; ++i)
-        { if (doc.linked[i].isParent) { return } }
-      primary = lst(markers);
-    });
-    return new SharedTextMarker(markers, primary)
-  }
-
-  function findSharedMarkers(doc) {
-    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
-  }
-
-  function copySharedMarkers(doc, markers) {
-    for (var i = 0; i < markers.length; i++) {
-      var marker = markers[i], pos = marker.find();
-      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
-      if (cmp(mFrom, mTo)) {
-        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
-        marker.markers.push(subMark);
-        subMark.parent = marker;
-      }
-    }
-  }
-
-  function detachSharedMarkers(markers) {
-    var loop = function ( i ) {
-      var marker = markers[i], linked = [marker.primary.doc];
-      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
-      for (var j = 0; j < marker.markers.length; j++) {
-        var subMarker = marker.markers[j];
-        if (indexOf(linked, subMarker.doc) == -1) {
-          subMarker.parent = null;
-          marker.markers.splice(j--, 1);
-        }
-      }
-    };
-
-    for (var i = 0; i < markers.length; i++) loop( i );
-  }
-
-  var nextDocId = 0;
-  var Doc = function(text, mode, firstLine, lineSep, direction) {
-    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
-    if (firstLine == null) { firstLine = 0; }
-
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
-    this.first = firstLine;
-    this.scrollTop = this.scrollLeft = 0;
-    this.cantEdit = false;
-    this.cleanGeneration = 1;
-    this.modeFrontier = this.highlightFrontier = firstLine;
-    var start = Pos(firstLine, 0);
-    this.sel = simpleSelection(start);
-    this.history = new History(null);
-    this.id = ++nextDocId;
-    this.modeOption = mode;
-    this.lineSep = lineSep;
-    this.direction = (direction == "rtl") ? "rtl" : "ltr";
-    this.extend = false;
-
-    if (typeof text == "string") { text = this.splitLines(text); }
-    updateDoc(this, {from: start, to: start, text: text});
-    setSelection(this, simpleSelection(start), sel_dontScroll);
-  };
-
-  Doc.prototype = createObj(BranchChunk.prototype, {
-    constructor: Doc,
-    // Iterate over the document. Supports two forms -- with only one
-    // argument, it calls that for each line in the document. With
-    // three, it iterates over the range given by the first two (with
-    // the second being non-inclusive).
-    iter: function(from, to, op) {
-      if (op) { this.iterN(from - this.first, to - from, op); }
-      else { this.iterN(this.first, this.first + this.size, from); }
-    },
-
-    // Non-public interface for adding and removing lines.
-    insert: function(at, lines) {
-      var height = 0;
-      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
-      this.insertInner(at - this.first, lines, height);
-    },
-    remove: function(at, n) { this.removeInner(at - this.first, n); },
-
-    // From here, the methods are part of the public interface. Most
-    // are also available from CodeMirror (editor) instances.
-
-    getValue: function(lineSep) {
-      var lines = getLines(this, this.first, this.first + this.size);
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-    setValue: docMethodOp(function(code) {
-      var top = Pos(this.first, 0), last = this.first + this.size - 1;
-      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
-                        text: this.splitLines(code), origin: "setValue", full: true}, true);
-      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
-      setSelection(this, simpleSelection(top), sel_dontScroll);
-    }),
-    replaceRange: function(code, from, to, origin) {
-      from = clipPos(this, from);
-      to = to ? clipPos(this, to) : from;
-      replaceRange(this, code, from, to, origin);
-    },
-    getRange: function(from, to, lineSep) {
-      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-
-    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
-
-    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
-    getLineNumber: function(line) {return lineNo(line)},
-
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") { line = getLine(this, line); }
-      return visualLine(line)
-    },
-
-    lineCount: function() {return this.size},
-    firstLine: function() {return this.first},
-    lastLine: function() {return this.first + this.size - 1},
-
-    clipPos: function(pos) {return clipPos(this, pos)},
-
-    getCursor: function(start) {
-      var range$$1 = this.sel.primary(), pos;
-      if (start == null || start == "head") { pos = range$$1.head; }
-      else if (start == "anchor") { pos = range$$1.anchor; }
-      else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
-      else { pos = range$$1.from(); }
-      return pos
-    },
-    listSelections: function() { return this.sel.ranges },
-    somethingSelected: function() {return this.sel.somethingSelected()},
-
-    setCursor: docMethodOp(function(line, ch, options) {
-      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
-    }),
-    setSelection: docMethodOp(function(anchor, head, options) {
-      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
-    }),
-    extendSelection: docMethodOp(function(head, other, options) {
-      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
-    }),
-    extendSelections: docMethodOp(function(heads, options) {
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    extendSelectionsBy: docMethodOp(function(f, options) {
-      var heads = map(this.sel.ranges, f);
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    setSelections: docMethodOp(function(ranges, primary, options) {
-      var this$1 = this;
-
-      if (!ranges.length) { return }
-      var out = [];
-      for (var i = 0; i < ranges.length; i++)
-        { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
-                           clipPos(this$1, ranges[i].head)); }
-      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
-      setSelection(this, normalizeSelection(this.cm, out, primary), options);
-    }),
-    addSelection: docMethodOp(function(anchor, head, options) {
-      var ranges = this.sel.ranges.slice(0);
-      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
-      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
-    }),
-
-    getSelection: function(lineSep) {
-      var this$1 = this;
-
-      var ranges = this.sel.ranges, lines;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
-        lines = lines ? lines.concat(sel) : sel;
-      }
-      if (lineSep === false) { return lines }
-      else { return lines.join(lineSep || this.lineSeparator()) }
-    },
-    getSelections: function(lineSep) {
-      var this$1 = this;
-
-      var parts = [], ranges = this.sel.ranges;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
-        if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
-        parts[i] = sel;
-      }
-      return parts
-    },
-    replaceSelection: function(code, collapse, origin) {
-      var dup = [];
-      for (var i = 0; i < this.sel.ranges.length; i++)
-        { dup[i] = code; }
-      this.replaceSelections(dup, collapse, origin || "+input");
-    },
-    replaceSelections: docMethodOp(function(code, collapse, origin) {
-      var this$1 = this;
-
-      var changes = [], sel = this.sel;
-      for (var i = 0; i < sel.ranges.length; i++) {
-        var range$$1 = sel.ranges[i];
-        changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
-      }
-      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
-      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
-        { makeChange(this$1, changes[i$1]); }
-      if (newSel) { setSelectionReplaceHistory(this, newSel); }
-      else if (this.cm) { ensureCursorVisible(this.cm); }
-    }),
-    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
-    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
-    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
-    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
-
-    setExtending: function(val) {this.extend = val;},
-    getExtending: function() {return this.extend},
-
-    historySize: function() {
-      var hist = this.history, done = 0, undone = 0;
-      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
-      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
-      return {undo: done, redo: undone}
-    },
-    clearHistory: function() {
-      var this$1 = this;
-
-      this.history = new History(this.history.maxGeneration);
-      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
-    },
-
-    markClean: function() {
-      this.cleanGeneration = this.changeGeneration(true);
-    },
-    changeGeneration: function(forceSplit) {
-      if (forceSplit)
-        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
-      return this.history.generation
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration)
-    },
-
-    getHistory: function() {
-      return {done: copyHistoryArray(this.history.done),
-              undone: copyHistoryArray(this.history.undone)}
-    },
-    setHistory: function(histData) {
-      var hist = this.history = new History(this.history.maxGeneration);
-      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
-      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
-    },
-
-    setGutterMarker: docMethodOp(function(line, gutterID, value) {
-      return changeLine(this, line, "gutter", function (line) {
-        var markers = line.gutterMarkers || (line.gutterMarkers = {});
-        markers[gutterID] = value;
-        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
-        return true
-      })
-    }),
-
-    clearGutter: docMethodOp(function(gutterID) {
-      var this$1 = this;
-
-      this.iter(function (line) {
-        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
-          changeLine(this$1, line, "gutter", function () {
-            line.gutterMarkers[gutterID] = null;
-            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
-            return true
-          });
-        }
-      });
-    }),
-
-    lineInfo: function(line) {
-      var n;
-      if (typeof line == "number") {
-        if (!isLine(this, line)) { return null }
-        n = line;
-        line = getLine(this, line);
-        if (!line) { return null }
-      } else {
-        n = lineNo(line);
-        if (n == null) { return null }
-      }
-      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
-              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
-              widgets: line.widgets}
-    },
-
-    addLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        if (!line[prop]) { line[prop] = cls; }
-        else if (classTest(cls).test(line[prop])) { return false }
-        else { line[prop] += " " + cls; }
-        return true
-      })
-    }),
-    removeLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        var cur = line[prop];
-        if (!cur) { return false }
-        else if (cls == null) { line[prop] = null; }
-        else {
-          var found = cur.match(classTest(cls));
-          if (!found) { return false }
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
-        }
-        return true
-      })
-    }),
-
-    addLineWidget: docMethodOp(function(handle, node, options) {
-      return addLineWidget(this, handle, node, options)
-    }),
-    removeLineWidget: function(widget) { widget.clear(); },
-
-    markText: function(from, to, options) {
-      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
-    },
-    setBookmark: function(pos, options) {
-      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
-                      insertLeft: options && options.insertLeft,
-                      clearWhenEmpty: false, shared: options && options.shared,
-                      handleMouseEvents: options && options.handleMouseEvents};
-      pos = clipPos(this, pos);
-      return markText(this, pos, pos, realOpts, "bookmark")
-    },
-    findMarksAt: function(pos) {
-      pos = clipPos(this, pos);
-      var markers = [], spans = getLine(this, pos.line).markedSpans;
-      if (spans) { for (var i = 0; i < spans.length; ++i) {
-        var span = spans[i];
-        if ((span.from == null || span.from <= pos.ch) &&
-            (span.to == null || span.to >= pos.ch))
-          { markers.push(span.marker.parent || span.marker); }
-      } }
-      return markers
-    },
-    findMarks: function(from, to, filter) {
-      from = clipPos(this, from); to = clipPos(this, to);
-      var found = [], lineNo$$1 = from.line;
-      this.iter(from.line, to.line + 1, function (line) {
-        var spans = line.markedSpans;
-        if (spans) { for (var i = 0; i < spans.length; i++) {
-          var span = spans[i];
-          if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
-                span.from == null && lineNo$$1 != from.line ||
-                span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
-              (!filter || filter(span.marker)))
-            { found.push(span.marker.parent || span.marker); }
-        } }
-        ++lineNo$$1;
-      });
-      return found
-    },
-    getAllMarks: function() {
-      var markers = [];
-      this.iter(function (line) {
-        var sps = line.markedSpans;
-        if (sps) { for (var i = 0; i < sps.length; ++i)
-          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
-      });
-      return markers
-    },
-
-    posFromIndex: function(off) {
-      var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
-      this.iter(function (line) {
-        var sz = line.text.length + sepSize;
-        if (sz > off) { ch = off; return true }
-        off -= sz;
-        ++lineNo$$1;
-      });
-      return clipPos(this, Pos(lineNo$$1, ch))
-    },
-    indexFromPos: function (coords) {
-      coords = clipPos(this, coords);
-      var index = coords.ch;
-      if (coords.line < this.first || coords.ch < 0) { return 0 }
-      var sepSize = this.lineSeparator().length;
-      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
-        index += line.text.length + sepSize;
-      });
-      return index
-    },
-
-    copy: function(copyHistory) {
-      var doc = new Doc(getLines(this, this.first, this.first + this.size),
-                        this.modeOption, this.first, this.lineSep, this.direction);
-      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
-      doc.sel = this.sel;
-      doc.extend = false;
-      if (copyHistory) {
-        doc.history.undoDepth = this.history.undoDepth;
-        doc.setHistory(this.getHistory());
-      }
-      return doc
-    },
-
-    linkedDoc: function(options) {
-      if (!options) { options = {}; }
-      var from = this.first, to = this.first + this.size;
-      if (options.from != null && options.from > from) { from = options.from; }
-      if (options.to != null && options.to < to) { to = options.to; }
-      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
-      if (options.sharedHist) { copy.history = this.history
-      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
-      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
-      copySharedMarkers(copy, findSharedMarkers(this));
-      return copy
-    },
-    unlinkDoc: function(other) {
-      var this$1 = this;
-
-      if (other instanceof CodeMirror) { other = other.doc; }
-      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
-        var link = this$1.linked[i];
-        if (link.doc != other) { continue }
-        this$1.linked.splice(i, 1);
-        other.unlinkDoc(this$1);
-        detachSharedMarkers(findSharedMarkers(this$1));
-        break
-      } }
-      // If the histories were shared, split them again
-      if (other.history == this.history) {
-        var splitIds = [other.id];
-        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
-        other.history = new History(null);
-        other.history.done = copyHistoryArray(this.history.done, splitIds);
-        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
-      }
-    },
-    iterLinkedDocs: function(f) {linkedDocs(this, f);},
-
-    getMode: function() {return this.mode},
-    getEditor: function() {return this.cm},
-
-    splitLines: function(str) {
-      if (this.lineSep) { return str.split(this.lineSep) }
-      return splitLinesAuto(str)
-    },
-    lineSeparator: function() { return this.lineSep || "\n" },
-
-    setDirection: docMethodOp(function (dir) {
-      if (dir != "rtl") { dir = "ltr"; }
-      if (dir == this.direction) { return }
-      this.direction = dir;
-      this.iter(function (line) { return line.order = null; });
-      if (this.cm) { directionChanged(this.cm); }
-    })
-  });
-
-  // Public alias.
-  Doc.prototype.eachLine = Doc.prototype.iter;
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
-  function onDrop(e) {
-    var cm = this;
-    clearDragCursor(cm);
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
-      { return }
-    e_preventDefault(e);
-    if (ie) { lastDrop = +new Date; }
-    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
-    if (!pos || cm.isReadOnly()) { return }
-    // Might be a file drop, in which case we simply extract the text
-    // and insert it.
-    if (files && files.length && window.FileReader && window.File) {
-      var n = files.length, text = Array(n), read = 0;
-      var markAsReadAndPasteIfAllFilesAreRead = function () {
-        if (++read == n) {
-          operation(cm, function () {
-            pos = clipPos(cm.doc, pos);
-            var change = {from: pos, to: pos,
-                          text: cm.doc.splitLines(
-                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
-                          origin: "paste"};
-            makeChange(cm.doc, change);
-            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
-          })();
-        }
-      };
-      var readTextFromFile = function (file, i) {
-        if (cm.options.allowDropFileTypes &&
-            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
-          markAsReadAndPasteIfAllFilesAreRead();
-          return
-        }
-        var reader = new FileReader;
-        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
-        reader.onload = function () {
-          var content = reader.result;
-          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
-            markAsReadAndPasteIfAllFilesAreRead();
-            return
-          }
-          text[i] = content;
-          markAsReadAndPasteIfAllFilesAreRead();
-        };
-        reader.readAsText(file);
-      };
-      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
-    } else { // Normal drop
-      // Don't do a replace if the drop happened inside of the selected text.
-      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
-        cm.state.draggingText(e);
-        // Ensure the editor is re-focused
-        setTimeout(function () { return cm.display.input.focus(); }, 20);
-        return
-      }
-      try {
-        var text$1 = e.dataTransfer.getData("Text");
-        if (text$1) {
-          var selected;
-          if (cm.state.draggingText && !cm.state.draggingText.copy)
-            { selected = cm.listSelections(); }
-          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
-          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
-            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
-          cm.replaceSelection(text$1, "around", "paste");
-          cm.display.input.focus();
-        }
-      }
-      catch(e){}
-    }
-  }
-
-  function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
-
-    e.dataTransfer.setData("Text", cm.getSelection());
-    e.dataTransfer.effectAllowed = "copyMove";
-
-    // Use dummy image instead of default browsers image.
-    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
-      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
-      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-      if (presto) {
-        img.width = img.height = 1;
-        cm.display.wrapper.appendChild(img);
-        // Force a relayout, or Opera won't use our image for some obscure reason
-        img._top = img.offsetTop;
-      }
-      e.dataTransfer.setDragImage(img, 0, 0);
-      if (presto) { img.parentNode.removeChild(img); }
-    }
-  }
-
-  function onDragOver(cm, e) {
-    var pos = posFromMouse(cm, e);
-    if (!pos) { return }
-    var frag = document.createDocumentFragment();
-    drawSelectionCursor(cm, pos, frag);
-    if (!cm.display.dragCursor) {
-      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
-      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
-    }
-    removeChildrenAndAdd(cm.display.dragCursor, frag);
-  }
-
-  function clearDragCursor(cm) {
-    if (cm.display.dragCursor) {
-      cm.display.lineSpace.removeChild(cm.display.dragCursor);
-      cm.display.dragCursor = null;
-    }
-  }
-
-  // These must be handled carefully, because naively registering a
-  // handler for each editor will cause the editors to never be
-  // garbage collected.
-
-  function forEachCodeMirror(f) {
-    if (!document.getElementsByClassName) { return }
-    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
-    for (var i = 0; i < byClass.length; i++) {
-      var cm = byClass[i].CodeMirror;
-      if (cm) { editors.push(cm); }
-    }
-    if (editors.length) { editors[0].operation(function () {
-      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
-    }); }
-  }
-
-  var globalsRegistered = false;
-  function ensureGlobalHandlers() {
-    if (globalsRegistered) { return }
-    registerGlobalHandlers();
-    globalsRegistered = true;
-  }
-  function registerGlobalHandlers() {
-    // When the window resizes, we need to refresh active editors.
-    var resizeTimer;
-    on(window, "resize", function () {
-      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
-        resizeTimer = null;
-        forEachCodeMirror(onResize);
-      }, 100); }
-    });
-    // When the window loses focus, we want to show the editor as blurred
-    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
-  }
-  // Called when the window resizes
-  function onResize(cm) {
-    var d = cm.display;
-    // Might be a text scaling operation, clear size caches.
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-    d.scrollbarsClipped = false;
-    cm.setSize();
-  }
-
-  var keyNames = {
-    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
-    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
-    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
-    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
-    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
-    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
-    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
-  };
-
-  // Number keys
-  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
-  // Alphabetic keys
-  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
-  // Function keys
-  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
-
-  var keyMap = {};
-
-  keyMap.basic = {
-    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
-    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
-    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
-    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
-    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
-    "Esc": "singleSelection"
-  };
-  // Note that the save and find-related commands aren't defined by
-  // default. User code or addons can define them. Unknown commands
-  // are simply ignored.
-  keyMap.pcDefault = {
-    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
-    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
-    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
-    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
-    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
-    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
-    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
-    "fallthrough": "basic"
-  };
-  // Very basic readline/emacs-style bindings, which are standard on Mac.
-  keyMap.emacsy = {
-    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
-    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
-    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
-    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
-    "Ctrl-O": "openLine"
-  };
-  keyMap.macDefault = {
-    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
-    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
-    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
-    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
-    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
-    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
-    "fallthrough": ["basic", "emacsy"]
-  };
-  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
-
-  // KEYMAP DISPATCH
-
-  function normalizeKeyName(name) {
-    var parts = name.split(/-(?!$)/);
-    name = parts[parts.length - 1];
-    var alt, ctrl, shift, cmd;
-    for (var i = 0; i < parts.length - 1; i++) {
-      var mod = parts[i];
-      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
-      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
-      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
-      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
-      else { throw new Error("Unrecognized modifier name: " + mod) }
-    }
-    if (alt) { name = "Alt-" + name; }
-    if (ctrl) { name = "Ctrl-" + name; }
-    if (cmd) { name = "Cmd-" + name; }
-    if (shift) { name = "Shift-" + name; }
-    return name
-  }
-
-  // This is a kludge to keep keymaps mostly working as raw objects
-  // (backwards compatibility) while at the same time support features
-  // like normalization and multi-stroke key bindings. It compiles a
-  // new normalized keymap, and then updates the old object to reflect
-  // this.
-  function normalizeKeyMap(keymap) {
-    var copy = {};
-    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
-      var value = keymap[keyname];
-      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
-      if (value == "...") { delete keymap[keyname]; continue }
-
-      var keys = map(keyname.split(" "), normalizeKeyName);
-      for (var i = 0; i < keys.length; i++) {
-        var val = (void 0), name = (void 0);
-        if (i == keys.length - 1) {
-          name = keys.join(" ");
-          val = value;
-        } else {
-          name = keys.slice(0, i + 1).join(" ");
-          val = "...";
-        }
-        var prev = copy[name];
-        if (!prev) { copy[name] = val; }
-        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
-      }
-      delete keymap[keyname];
-    } }
-    for (var prop in copy) { keymap[prop] = copy[prop]; }
-    return keymap
-  }
-
-  function lookupKey(key, map$$1, handle, context) {
-    map$$1 = getKeyMap(map$$1);
-    var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
-    if (found === false) { return "nothing" }
-    if (found === "...") { return "multi" }
-    if (found != null && handle(found)) { return "handled" }
-
-    if (map$$1.fallthrough) {
-      if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
-        { return lookupKey(key, map$$1.fallthrough, handle, context) }
-      for (var i = 0; i < map$$1.fallthrough.length; i++) {
-        var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
-        if (result) { return result }
-      }
-    }
-  }
-
-  // Modifier key presses don't count as 'real' key presses for the
-  // purpose of keymap fallthrough.
-  function isModifierKey(value) {
-    var name = typeof value == "string" ? value : keyNames[value.keyCode];
-    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
-  }
-
-  function addModifierNames(name, event, noShift) {
-    var base = name;
-    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
-    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
-    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
-    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
-    return name
-  }
-
-  // Look up the name of a key as indicated by an event object.
-  function keyName(event, noShift) {
-    if (presto && event.keyCode == 34 && event["char"]) { return false }
-    var name = keyNames[event.keyCode];
-    if (name == null || event.altGraphKey) { return false }
-    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
-    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
-    if (event.keyCode == 3 && event.code) { name = event.code; }
-    return addModifierNames(name, event, noShift)
-  }
-
-  function getKeyMap(val) {
-    return typeof val == "string" ? keyMap[val] : val
-  }
-
-  // Helper for deleting text near the selection(s), used to implement
-  // backspace, delete, and similar functionality.
-  function deleteNearSelection(cm, compute) {
-    var ranges = cm.doc.sel.ranges, kill = [];
-    // Build up a set of ranges to kill first, merging overlapping
-    // ranges.
-    for (var i = 0; i < ranges.length; i++) {
-      var toKill = compute(ranges[i]);
-      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
-        var replaced = kill.pop();
-        if (cmp(replaced.from, toKill.from) < 0) {
-          toKill.from = replaced.from;
-          break
-        }
-      }
-      kill.push(toKill);
-    }
-    // Next, remove those actual ranges.
-    runInOp(cm, function () {
-      for (var i = kill.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
-      ensureCursorVisible(cm);
-    });
-  }
-
-  function moveCharLogically(line, ch, dir) {
-    var target = skipExtendingChars(line.text, ch + dir, dir);
-    return target < 0 || target > line.text.length ? null : target
-  }
-
-  function moveLogically(line, start, dir) {
-    var ch = moveCharLogically(line, start.ch, dir);
-    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
-  }
-
-  function endOfLine(visually, cm, lineObj, lineNo, dir) {
-    if (visually) {
-      if (cm.doc.direction == "rtl") { dir = -dir; }
-      var order = getOrder(lineObj, cm.doc.direction);
-      if (order) {
-        var part = dir < 0 ? lst(order) : order[0];
-        var moveInStorageOrder = (dir < 0) == (part.level == 1);
-        var sticky = moveInStorageOrder ? "after" : "before";
-        var ch;
-        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
-        // it could be that the last bidi part is not on the last visual line,
-        // since visual lines contain content order-consecutive chunks.
-        // Thus, in rtl, we are looking for the first (content-order) character
-        // in the rtl chunk that is on the last line (that is, the same line
-        // as the last (content-order) character).
-        if (part.level > 0 || cm.doc.direction == "rtl") {
-          var prep = prepareMeasureForLine(cm, lineObj);
-          ch = dir < 0 ? lineObj.text.length - 1 : 0;
-          var targetTop = measureCharPrepared(cm, prep, ch).top;
-          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
-          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
-        } else { ch = dir < 0 ? part.to : part.from; }
-        return new Pos(lineNo, ch, sticky)
-      }
-    }
-    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
-  }
-
-  function moveVisually(cm, line, start, dir) {
-    var bidi = getOrder(line, cm.doc.direction);
-    if (!bidi) { return moveLogically(line, start, dir) }
-    if (start.ch >= line.text.length) {
-      start.ch = line.text.length;
-      start.sticky = "before";
-    } else if (start.ch <= 0) {
-      start.ch = 0;
-      start.sticky = "after";
-    }
-    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
-    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
-      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
-      // nothing interesting happens.
-      return moveLogically(line, start, dir)
-    }
-
-    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
-    var prep;
-    var getWrappedLineExtent = function (ch) {
-      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
-      prep = prep || prepareMeasureForLine(cm, line);
-      return wrappedLineExtentChar(cm, line, prep, ch)
-    };
-    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
-
-    if (cm.doc.direction == "rtl" || part.level == 1) {
-      var moveInStorageOrder = (part.level == 1) == (dir < 0);
-      var ch = mv(start, moveInStorageOrder ? 1 : -1);
-      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
-        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
-        var sticky = moveInStorageOrder ? "before" : "after";
-        return new Pos(start.line, ch, sticky)
-      }
-    }
-
-    // Case 3: Could not move within this bidi part in this visual line, so leave
-    // the current bidi part
-
-    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
-      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
-        ? new Pos(start.line, mv(ch, 1), "before")
-        : new Pos(start.line, ch, "after"); };
-
-      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
-        var part = bidi[partPos];
-        var moveInStorageOrder = (dir > 0) == (part.level != 1);
-        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
-        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
-        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
-        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
-      }
-    };
-
-    // Case 3a: Look for other bidi parts on the same visual line
-    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
-    if (res) { return res }
-
-    // Case 3b: Look for other bidi parts on the next visual line
-    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
-    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
-      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
-      if (res) { return res }
-    }
-
-    // Case 4: Nowhere to move
-    return null
-  }
-
-  // Commands are parameter-less actions that can be performed on an
-  // editor, mostly used for keybindings.
-  var commands = {
-    selectAll: selectAll,
-    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
-    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
-      if (range.empty()) {
-        var len = getLine(cm.doc, range.head.line).text.length;
-        if (range.head.ch == len && range.head.line < cm.lastLine())
-          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
-        else
-          { return {from: range.head, to: Pos(range.head.line, len)} }
-      } else {
-        return {from: range.from(), to: range.to()}
-      }
-    }); },
-    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0),
-      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
-    }); }); },
-    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0), to: range.from()
-    }); }); },
-    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
-      return {from: leftPos, to: range.from()}
-    }); },
-    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
-      return {from: range.from(), to: rightPos }
-    }); },
-    undo: function (cm) { return cm.undo(); },
-    redo: function (cm) { return cm.redo(); },
-    undoSelection: function (cm) { return cm.undoSelection(); },
-    redoSelection: function (cm) { return cm.redoSelection(); },
-    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
-    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
-    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
-      {origin: "+move", bias: -1}
-    ); },
-    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
-    }, sel_move); },
-    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: 0, top: top}, "div")
-    }, sel_move); },
-    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      var pos = cm.coordsChar({left: 0, top: top}, "div");
-      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
-      return pos
-    }, sel_move); },
-    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
-    goLineDown: function (cm) { return cm.moveV(1, "line"); },
-    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
-    goPageDown: function (cm) { return cm.moveV(1, "page"); },
-    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
-    goCharRight: function (cm) { return cm.moveH(1, "char"); },
-    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
-    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
-    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
-    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
-    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
-    goWordRight: function (cm) { return cm.moveH(1, "word"); },
-    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
-    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
-    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
-    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
-    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
-    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
-    indentAuto: function (cm) { return cm.indentSelection("smart"); },
-    indentMore: function (cm) { return cm.indentSelection("add"); },
-    indentLess: function (cm) { return cm.indentSelection("subtract"); },
-    insertTab: function (cm) { return cm.replaceSelection("\t"); },
-    insertSoftTab: function (cm) {
-      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
-      for (var i = 0; i < ranges.length; i++) {
-        var pos = ranges[i].from();
-        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
-        spaces.push(spaceStr(tabSize - col % tabSize));
-      }
-      cm.replaceSelections(spaces);
-    },
-    defaultTab: function (cm) {
-      if (cm.somethingSelected()) { cm.indentSelection("add"); }
-      else { cm.execCommand("insertTab"); }
-    },
-    // Swap the two chars left and right of each selection's head.
-    // Move cursor behind the two swapped characters afterwards.
-    //
-    // Doesn't consider line feeds a character.
-    // Doesn't scan more than one line above to find a character.
-    // Doesn't do anything on an empty line.
-    // Doesn't do anything with non-empty selections.
-    transposeChars: function (cm) { return runInOp(cm, function () {
-      var ranges = cm.listSelections(), newSel = [];
-      for (var i = 0; i < ranges.length; i++) {
-        if (!ranges[i].empty()) { continue }
-        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
-        if (line) {
-          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
-          if (cur.ch > 0) {
-            cur = new Pos(cur.line, cur.ch + 1);
-            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
-                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
-          } else if (cur.line > cm.doc.first) {
-            var prev = getLine(cm.doc, cur.line - 1).text;
-            if (prev) {
-              cur = new Pos(cur.line, 1);
-              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
-                              prev.charAt(prev.length - 1),
-                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
-            }
-          }
-        }
-        newSel.push(new Range(cur, cur));
-      }
-      cm.setSelections(newSel);
-    }); },
-    newlineAndIndent: function (cm) { return runInOp(cm, function () {
-      var sels = cm.listSelections();
-      for (var i = sels.length - 1; i >= 0; i--)
-        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
-      sels = cm.listSelections();
-      for (var i$1 = 0; i$1 < sels.length; i$1++)
-        { cm.indentLine(sels[i$1].from().line, null, true); }
-      ensureCursorVisible(cm);
-    }); },
-    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
-    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
-  };
-
-
-  function lineStart(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLine(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, visual, lineN, 1)
-  }
-  function lineEnd(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLineEnd(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, line, lineN, -1)
-  }
-  function lineStartSmart(cm, pos) {
-    var start = lineStart(cm, pos.line);
-    var line = getLine(cm.doc, start.line);
-    var order = getOrder(line, cm.doc.direction);
-    if (!order || order[0].level == 0) {
-      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
-      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
-      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
-    }
-    return start
-  }
-
-  // Run a handler that was bound to a key.
-  function doHandleBinding(cm, bound, dropShift) {
-    if (typeof bound == "string") {
-      bound = commands[bound];
-      if (!bound) { return false }
-    }
-    // Ensure previous input has been read, so that the handler sees a
-    // consistent view of the document
-    cm.display.input.ensurePolled();
-    var prevShift = cm.display.shift, done = false;
-    try {
-      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-      if (dropShift) { cm.display.shift = false; }
-      done = bound(cm) != Pass;
-    } finally {
-      cm.display.shift = prevShift;
-      cm.state.suppressEdits = false;
-    }
-    return done
-  }
-
-  function lookupKeyForEditor(cm, name, handle) {
-    for (var i = 0; i < cm.state.keyMaps.length; i++) {
-      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
-      if (result) { return result }
-    }
-    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
-      || lookupKey(name, cm.options.keyMap, handle, cm)
-  }
-
-  // Note that, despite the name, this function is also used to check
-  // for bound mouse clicks.
-
-  var stopSeq = new Delayed;
-
-  function dispatchKey(cm, name, e, handle) {
-    var seq = cm.state.keySeq;
-    if (seq) {
-      if (isModifierKey(name)) { return "handled" }
-      if (/\'$/.test(name))
-        { cm.state.keySeq = null; }
-      else
-        { stopSeq.set(50, function () {
-          if (cm.state.keySeq == seq) {
-            cm.state.keySeq = null;
-            cm.display.input.reset();
-          }
-        }); }
-      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
-    }
-    return dispatchKeyInner(cm, name, e, handle)
-  }
-
-  function dispatchKeyInner(cm, name, e, handle) {
-    var result = lookupKeyForEditor(cm, name, handle);
-
-    if (result == "multi")
-      { cm.state.keySeq = name; }
-    if (result == "handled")
-      { signalLater(cm, "keyHandled", cm, name, e); }
-
-    if (result == "handled" || result == "multi") {
-      e_preventDefault(e);
-      restartBlink(cm);
-    }
-
-    return !!result
-  }
-
-  // Handle a key from the keydown event.
-  function handleKeyBinding(cm, e) {
-    var name = keyName(e, true);
-    if (!name) { return false }
-
-    if (e.shiftKey && !cm.state.keySeq) {
-      // First try to resolve full name (including 'Shift-'). Failing
-      // that, see if there is a cursor-motion command (starting with
-      // 'go') bound to the keyname without 'Shift-'.
-      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
-          || dispatchKey(cm, name, e, function (b) {
-               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                 { return doHandleBinding(cm, b) }
-             })
-    } else {
-      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
-    }
-  }
-
-  // Handle a key from the keypress event
-  function handleCharBinding(cm, e, ch) {
-    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
-  }
-
-  var lastStoppedKey = null;
-  function onKeyDown(e) {
-    var cm = this;
-    cm.curOp.focus = activeElt();
-    if (signalDOMEvent(cm, e)) { return }
-    // IE does strange things with escape.
-    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
-    var code = e.keyCode;
-    cm.display.shift = code == 16 || e.shiftKey;
-    var handled = handleKeyBinding(cm, e);
-    if (presto) {
-      lastStoppedKey = handled ? code : null;
-      // Opera has no cut event... we try to at least catch the key combo
-      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
-        { cm.replaceSelection("", null, "cut"); }
-    }
-    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
-      { document.execCommand("cut"); }
-
-    // Turn mouse into crosshair when Alt is held on Mac.
-    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
-      { showCrossHair(cm); }
-  }
-
-  function showCrossHair(cm) {
-    var lineDiv = cm.display.lineDiv;
-    addClass(lineDiv, "CodeMirror-crosshair");
-
-    function up(e) {
-      if (e.keyCode == 18 || !e.altKey) {
-        rmClass(lineDiv, "CodeMirror-crosshair");
-        off(document, "keyup", up);
-        off(document, "mouseover", up);
-      }
-    }
-    on(document, "keyup", up);
-    on(document, "mouseover", up);
-  }
-
-  function onKeyUp(e) {
-    if (e.keyCode == 16) { this.doc.sel.shift = false; }
-    signalDOMEvent(this, e);
-  }
-
-  function onKeyPress(e) {
-    var cm = this;
-    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
-    var keyCode = e.keyCode, charCode = e.charCode;
-    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
-    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
-    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
-    // Some browsers fire keypress events for backspace
-    if (ch == "\x08") { return }
-    if (handleCharBinding(cm, e, ch)) { return }
-    cm.display.input.onKeyPress(e);
-  }
-
-  var DOUBLECLICK_DELAY = 400;
-
-  var PastClick = function(time, pos, button) {
-    this.time = time;
-    this.pos = pos;
-    this.button = button;
-  };
-
-  PastClick.prototype.compare = function (time, pos, button) {
-    return this.time + DOUBLECLICK_DELAY > time &&
-      cmp(pos, this.pos) == 0 && button == this.button
-  };
-
-  var lastClick, lastDoubleClick;
-  function clickRepeat(pos, button) {
-    var now = +new Date;
-    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
-      lastClick = lastDoubleClick = null;
-      return "triple"
-    } else if (lastClick && lastClick.compare(now, pos, button)) {
-      lastDoubleClick = new PastClick(now, pos, button);
-      lastClick = null;
-      return "double"
-    } else {
-      lastClick = new PastClick(now, pos, button);
-      lastDoubleClick = null;
-      return "single"
-    }
-  }
-
-  // A mouse down can be a single click, double click, triple click,
-  // start of selection drag, start of text drag, new cursor
-  // (ctrl-click), rectangle drag (alt-drag), or xwin
-  // middle-click-paste. Or it might be a click on something we should
-  // not interfere with, such as a scrollbar or widget.
-  function onMouseDown(e) {
-    var cm = this, display = cm.display;
-    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
-    display.input.ensurePolled();
-    display.shift = e.shiftKey;
-
-    if (eventInWidget(display, e)) {
-      if (!webkit) {
-        // Briefly turn off draggability, to allow widgets to do
-        // normal dragging things.
-        display.scroller.draggable = false;
-        setTimeout(function () { return display.scroller.draggable = true; }, 100);
-      }
-      return
-    }
-    if (clickInGutter(cm, e)) { return }
-    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
-    window.focus();
-
-    // #3261: make sure, that we're not starting a second selection
-    if (button == 1 && cm.state.selectingText)
-      { cm.state.selectingText(e); }
-
-    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
-
-    if (button == 1) {
-      if (pos) { leftButtonDown(cm, pos, repeat, e); }
-      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
-    } else if (button == 2) {
-      if (pos) { extendSelection(cm.doc, pos); }
-      setTimeout(function () { return display.input.focus(); }, 20);
-    } else if (button == 3) {
-      if (captureRightClick) { cm.display.input.onContextMenu(e); }
-      else { delayBlurEvent(cm); }
-    }
-  }
-
-  function handleMappedButton(cm, button, pos, repeat, event) {
-    var name = "Click";
-    if (repeat == "double") { name = "Double" + name; }
-    else if (repeat == "triple") { name = "Triple" + name; }
-    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
-
-    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
-      if (typeof bound == "string") { bound = commands[bound]; }
-      if (!bound) { return false }
-      var done = false;
-      try {
-        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-        done = bound(cm, pos) != Pass;
-      } finally {
-        cm.state.suppressEdits = false;
-      }
-      return done
-    })
-  }
-
-  function configureMouse(cm, repeat, event) {
-    var option = cm.getOption("configureMouse");
-    var value = option ? option(cm, repeat, event) : {};
-    if (value.unit == null) {
-      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
-      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
-    }
-    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
-    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
-    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
-    return value
-  }
-
-  function leftButtonDown(cm, pos, repeat, event) {
-    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
-    else { cm.curOp.focus = activeElt(); }
-
-    var behavior = configureMouse(cm, repeat, event);
-
-    var sel = cm.doc.sel, contained;
-    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
-        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
-        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
-        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
-      { leftButtonStartDrag(cm, event, pos, behavior); }
-    else
-      { leftButtonSelect(cm, event, pos, behavior); }
-  }
-
-  // Start a text drag. When it ends, see if any dragging actually
-  // happen, and treat as a click if it didn't.
-  function leftButtonStartDrag(cm, event, pos, behavior) {
-    var display = cm.display, moved = false;
-    var dragEnd = operation(cm, function (e) {
-      if (webkit) { display.scroller.draggable = false; }
-      cm.state.draggingText = false;
-      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
-      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
-      off(display.scroller, "dragstart", dragStart);
-      off(display.scroller, "drop", dragEnd);
-      if (!moved) {
-        e_preventDefault(e);
-        if (!behavior.addNew)
-          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
-        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
-        if (webkit || ie && ie_version == 9)
-          { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
-        else
-          { display.input.focus(); }
-      }
-    });
-    var mouseMove = function(e2) {
-      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
-    };
-    var dragStart = function () { return moved = true; };
-    // Let the drag handler handle this.
-    if (webkit) { display.scroller.draggable = true; }
-    cm.state.draggingText = dragEnd;
-    dragEnd.copy = !behavior.moveOnDrag;
-    // IE's approach to draggable
-    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
-    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
-    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
-    on(display.scroller, "dragstart", dragStart);
-    on(display.scroller, "drop", dragEnd);
-
-    delayBlurEvent(cm);
-    setTimeout(function () { return display.input.focus(); }, 20);
-  }
-
-  function rangeForUnit(cm, pos, unit) {
-    if (unit == "char") { return new Range(pos, pos) }
-    if (unit == "word") { return cm.findWordAt(pos) }
-    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
-    var result = unit(cm, pos);
-    return new Range(result.from, result.to)
-  }
-
-  // Normal selection, as opposed to text dragging.
-  function leftButtonSelect(cm, event, start, behavior) {
-    var display = cm.display, doc = cm.doc;
-    e_preventDefault(event);
-
-    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
-    if (behavior.addNew && !behavior.extend) {
-      ourIndex = doc.sel.contains(start);
-      if (ourIndex > -1)
-        { ourRange = ranges[ourIndex]; }
-      else
-        { ourRange = new Range(start, start); }
-    } else {
-      ourRange = doc.sel.primary();
-      ourIndex = doc.sel.primIndex;
-    }
-
-    if (behavior.unit == "rectangle") {
-      if (!behavior.addNew) { ourRange = new Range(start, start); }
-      start = posFromMouse(cm, event, true, true);
-      ourIndex = -1;
-    } else {
-      var range$$1 = rangeForUnit(cm, start, behavior.unit);
-      if (behavior.extend)
-        { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
-      else
-        { ourRange = range$$1; }
-    }
-
-    if (!behavior.addNew) {
-      ourIndex = 0;
-      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
-      startSel = doc.sel;
-    } else if (ourIndex == -1) {
-      ourIndex = ranges.length;
-      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
-                   {scroll: false, origin: "*mouse"});
-    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
-      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
-                   {scroll: false, origin: "*mouse"});
-      startSel = doc.sel;
-    } else {
-      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
-    }
-
-    var lastPos = start;
-    function extendTo(pos) {
-      if (cmp(lastPos, pos) == 0) { return }
-      lastPos = pos;
-
-      if (behavior.unit == "rectangle") {
-        var ranges = [], tabSize = cm.options.tabSize;
-        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
-        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
-        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
-        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
-             line <= end; line++) {
-          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
-          if (left == right)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
-          else if (text.length > leftPos)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
-        }
-        if (!ranges.length) { ranges.push(new Range(start, start)); }
-        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
-                     {origin: "*mouse", scroll: false});
-        cm.scrollIntoView(pos);
-      } else {
-        var oldRange = ourRange;
-        var range$$1 = rangeForUnit(cm, pos, behavior.unit);
-        var anchor = oldRange.anchor, head;
-        if (cmp(range$$1.anchor, anchor) > 0) {
-          head = range$$1.head;
-          anchor = minPos(oldRange.from(), range$$1.anchor);
-        } else {
-          head = range$$1.anchor;
-          anchor = maxPos(oldRange.to(), range$$1.head);
-        }
-        var ranges$1 = startSel.ranges.slice(0);
-        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
-        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
-      }
-    }
-
-    var editorSize = display.wrapper.getBoundingClientRect();
-    // Used to ensure timeout re-tries don't fire when another extend
-    // happened in the meantime (clearTimeout isn't reliable -- at
-    // least on Chrome, the timeouts still happen even when cleared,
-    // if the clear happens after their scheduled firing time).
-    var counter = 0;
-
-    function extend(e) {
-      var curCount = ++counter;
-      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
-      if (!cur) { return }
-      if (cmp(cur, lastPos) != 0) {
-        cm.curOp.focus = activeElt();
-        extendTo(cur);
-        var visible = visibleLines(display, doc);
-        if (cur.line >= visible.to || cur.line < visible.from)
-          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
-      } else {
-        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
-        if (outside) { setTimeout(operation(cm, function () {
-          if (counter != curCount) { return }
-          display.scroller.scrollTop += outside;
-          extend(e);
-        }), 50); }
-      }
-    }
-
-    function done(e) {
-      cm.state.selectingText = false;
-      counter = Infinity;
-      // If e is null or undefined we interpret this as someone trying
-      // to explicitly cancel the selection rather than the user
-      // letting go of the mouse button.
-      if (e) {
-        e_preventDefault(e);
-        display.input.focus();
-      }
-      off(display.wrapper.ownerDocument, "mousemove", move);
-      off(display.wrapper.ownerDocument, "mouseup", up);
-      doc.history.lastSelOrigin = null;
-    }
-
-    var move = operation(cm, function (e) {
-      if (e.buttons === 0 || !e_button(e)) { done(e); }
-      else { extend(e); }
-    });
-    var up = operation(cm, done);
-    cm.state.selectingText = up;
-    on(display.wrapper.ownerDocument, "mousemove", move);
-    on(display.wrapper.ownerDocument, "mouseup", up);
-  }
-
-  // Used when mouse-selecting to adjust the anchor to the proper side
-  // of a bidi jump depending on the visual position of the head.
-  function bidiSimplify(cm, range$$1) {
-    var anchor = range$$1.anchor;
-    var head = range$$1.head;
-    var anchorLine = getLine(cm.doc, anchor.line);
-    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
-    var order = getOrder(anchorLine);
-    if (!order) { return range$$1 }
-    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
-    if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
-    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
-    if (boundary == 0 || boundary == order.length) { return range$$1 }
-
-    // Compute the relative visual position of the head compared to the
-    // anchor (<0 is to the left, >0 to the right)
-    var leftSide;
-    if (head.line != anchor.line) {
-      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
-    } else {
-      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
-      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
-      if (headIndex == boundary - 1 || headIndex == boundary)
-        { leftSide = dir < 0; }
-      else
-        { leftSide = dir > 0; }
-    }
-
-    var usePart = order[boundary + (leftSide ? -1 : 0)];
-    var from = leftSide == (usePart.level == 1);
-    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
-    return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
-  }
-
-
-  // Determines whether an event happened in the gutter, and fires the
-  // handlers for the corresponding event.
-  function gutterEvent(cm, e, type, prevent) {
-    var mX, mY;
-    if (e.touches) {
-      mX = e.touches[0].clientX;
-      mY = e.touches[0].clientY;
-    } else {
-      try { mX = e.clientX; mY = e.clientY; }
-      catch(e) { return false }
-    }
-    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
-    if (prevent) { e_preventDefault(e); }
-
-    var display = cm.display;
-    var lineBox = display.lineDiv.getBoundingClientRect();
-
-    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && g.getBoundingClientRect().right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.display.gutterSpecs[i];
-        signal(cm, type, cm, line, gutter.className, e);
-        return e_defaultPrevented(e)
-      }
-    }
-  }
-
-  function clickInGutter(cm, e) {
-    return gutterEvent(cm, e, "gutterClick", true)
-  }
-
-  // CONTEXT MENU HANDLING
-
-  // To make the context menu work, we need to briefly unhide the
-  // textarea (making it as unobtrusive as possible) to let the
-  // right-click take effect on it.
-  function onContextMenu(cm, e) {
-    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
-    if (signalDOMEvent(cm, e, "contextmenu")) { return }
-    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
-  }
-
-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, "gutterContextMenu")) { return false }
-    return gutterEvent(cm, e, "gutterContextMenu", false)
-  }
-
-  function themeChanged(cm) {
-    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
-      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
-    clearCaches(cm);
-  }
-
-  var Init = {toString: function(){return "CodeMirror.Init"}};
-
-  var defaults = {};
-  var optionHandlers = {};
-
-  function defineOptions(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    function option(name, deflt, handle, notOnInit) {
-      CodeMirror.defaults[name] = deflt;
-      if (handle) { optionHandlers[name] =
-        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
-    }
-
-    CodeMirror.defineOption = option;
-
-    // Passed to option handlers when there is no old value.
-    CodeMirror.Init = Init;
-
-    // These two are, on init, called from the constructor because they
-    // have to be initialized before the editor can start at all.
-    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
-    option("mode", null, function (cm, val) {
-      cm.doc.modeOption = val;
-      loadMode(cm);
-    }, true);
-
-    option("indentUnit", 2, loadMode, true);
-    option("indentWithTabs", false);
-    option("smartIndent", true);
-    option("tabSize", 4, function (cm) {
-      resetModeState(cm);
-      clearCaches(cm);
-      regChange(cm);
-    }, true);
-
-    option("lineSeparator", null, function (cm, val) {
-      cm.doc.lineSep = val;
-      if (!val) { return }
-      var newBreaks = [], lineNo = cm.doc.first;
-      cm.doc.iter(function (line) {
-        for (var pos = 0;;) {
-          var found = line.text.indexOf(val, pos);
-          if (found == -1) { break }
-          pos = found + val.length;
-          newBreaks.push(Pos(lineNo, found));
-        }
-        lineNo++;
-      });
-      for (var i = newBreaks.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
-    });
-    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
-      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
-      if (old != Init) { cm.refresh(); }
-    });
-    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
-    option("electricChars", true);
-    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
-      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
-    }, true);
-    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
-    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
-    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
-    option("rtlMoveVisually", !windows);
-    option("wholeLineUpdateBefore", true);
-
-    option("theme", "default", function (cm) {
-      themeChanged(cm);
-      updateGutters(cm);
-    }, true);
-    option("keyMap", "default", function (cm, val, old) {
-      var next = getKeyMap(val);
-      var prev = old != Init && getKeyMap(old);
-      if (prev && prev.detach) { prev.detach(cm, next); }
-      if (next.attach) { next.attach(cm, prev || null); }
-    });
-    option("extraKeys", null);
-    option("configureMouse", null);
-
-    option("lineWrapping", false, wrappingChanged, true);
-    option("gutters", [], function (cm, val) {
-      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
-      updateGutters(cm);
-    }, true);
-    option("fixedGutter", true, function (cm, val) {
-      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
-      cm.refresh();
-    }, true);
-    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
-    option("scrollbarStyle", "native", function (cm) {
-      initScrollbars(cm);
-      updateScrollbars(cm);
-      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
-      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
-    }, true);
-    option("lineNumbers", false, function (cm, val) {
-      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
-      updateGutters(cm);
-    }, true);
-    option("firstLineNumber", 1, updateGutters, true);
-    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
-    option("showCursorWhenSelecting", false, updateSelection, true);
-
-    option("resetSelectionOnContextMenu", true);
-    option("lineWiseCopyCut", true);
-    option("pasteLinesPerSelection", true);
-    option("selectionsMayTouch", false);
-
-    option("readOnly", false, function (cm, val) {
-      if (val == "nocursor") {
-        onBlur(cm);
-        cm.display.input.blur();
-      }
-      cm.display.input.readOnlyChanged(val);
-    });
-    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
-    option("dragDrop", true, dragDropChanged);
-    option("allowDropFileTypes", null);
-
-    option("cursorBlinkRate", 530);
-    option("cursorScrollMargin", 0);
-    option("cursorHeight", 1, updateSelection, true);
-    option("singleCursorHeightPerLine", true, updateSelection, true);
-    option("workTime", 100);
-    option("workDelay", 100);
-    option("flattenSpans", true, resetModeState, true);
-    option("addModeClass", false, resetModeState, true);
-    option("pollInterval", 100);
-    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
-    option("historyEventDelay", 1250);
-    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
-    option("maxHighlightLength", 10000, resetModeState, true);
-    option("moveInputWithCursor", true, function (cm, val) {
-      if (!val) { cm.display.input.resetPosition(); }
-    });
-
-    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
-    option("autofocus", null);
-    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
-    option("phrases", null);
-  }
-
-  function dragDropChanged(cm, value, old) {
-    var wasOn = old && old != Init;
-    if (!value != !wasOn) {
-      var funcs = cm.display.dragFunctions;
-      var toggle = value ? on : off;
-      toggle(cm.display.scroller, "dragstart", funcs.start);
-      toggle(cm.display.scroller, "dragenter", funcs.enter);
-      toggle(cm.display.scroller, "dragover", funcs.over);
-      toggle(cm.display.scroller, "dragleave", funcs.leave);
-      toggle(cm.display.scroller, "drop", funcs.drop);
-    }
-  }
-
-  function wrappingChanged(cm) {
-    if (cm.options.lineWrapping) {
-      addClass(cm.display.wrapper, "CodeMirror-wrap");
-      cm.display.sizer.style.minWidth = "";
-      cm.display.sizerWidth = null;
-    } else {
-      rmClass(cm.display.wrapper, "CodeMirror-wrap");
-      findMaxLine(cm);
-    }
-    estimateLineHeights(cm);
-    regChange(cm);
-    clearCaches(cm);
-    setTimeout(function () { return updateScrollbars(cm); }, 100);
-  }
-
-  // A CodeMirror instance represents an editor. This is the object
-  // that user code is usually dealing with.
-
-  function CodeMirror(place, options) {
-    var this$1 = this;
-
-    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
-
-    this.options = options = options ? copyObj(options) : {};
-    // Determine effective options based on given values and defaults.
-    copyObj(defaults, options, false);
-
-    var doc = options.value;
-    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
-    else if (options.mode) { doc.modeOption = options.mode; }
-    this.doc = doc;
-
-    var input = new CodeMirror.inputStyles[options.inputStyle](this);
-    var display = this.display = new Display(place, doc, input, options);
-    display.wrapper.CodeMirror = this;
-    themeChanged(this);
-    if (options.lineWrapping)
-      { this.display.wrapper.className += " CodeMirror-wrap"; }
-    initScrollbars(this);
-
-    this.state = {
-      keyMaps: [],  // stores maps added by addKeyMap
-      overlays: [], // highlighting overlays, as added by addOverlay
-      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
-      overwrite: false,
-      delayingBlurEvent: false,
-      focused: false,
-      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
-      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
-      selectingText: false,
-      draggingText: false,
-      highlight: new Delayed(), // stores highlight worker timeout
-      keySeq: null,  // Unfinished key sequence
-      specialChars: null
-    };
-
-    if (options.autofocus && !mobile) { display.input.focus(); }
-
-    // Override magic textarea content restore that IE sometimes does
-    // on our hidden textarea on reload
-    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
-
-    registerEventHandlers(this);
-    ensureGlobalHandlers();
-
-    startOperation(this);
-    this.curOp.forceUpdate = true;
-    attachDoc(this, doc);
-
-    if ((options.autofocus && !mobile) || this.hasFocus())
-      { setTimeout(bind(onFocus, this), 20); }
-    else
-      { onBlur(this); }
-
-    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
-      { optionHandlers[opt](this$1, options[opt], Init); } }
-    maybeUpdateLineNumberWidth(this);
-    if (options.finishInit) { options.finishInit(this); }
-    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
-    endOperation(this);
-    // Suppress optimizelegibility in Webkit, since it breaks text
-    // measuring on line wrapping boundaries.
-    if (webkit && options.lineWrapping &&
-        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
-      { display.lineDiv.style.textRendering = "auto"; }
-  }
-
-  // The default configuration options.
-  CodeMirror.defaults = defaults;
-  // Functions to run when options are changed.
-  CodeMirror.optionHandlers = optionHandlers;
-
-  // Attach the necessary event handlers when initializing the editor
-  function registerEventHandlers(cm) {
-    var d = cm.display;
-    on(d.scroller, "mousedown", operation(cm, onMouseDown));
-    // Older IE's will not fire a second mousedown for a double click
-    if (ie && ie_version < 11)
-      { on(d.scroller, "dblclick", operation(cm, function (e) {
-        if (signalDOMEvent(cm, e)) { return }
-        var pos = posFromMouse(cm, e);
-        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
-        e_preventDefault(e);
-        var word = cm.findWordAt(pos);
-        extendSelection(cm.doc, word.anchor, word.head);
-      })); }
-    else
-      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
-    // Some browsers fire contextmenu *after* opening the menu, at
-    // which point we can't mess with it anymore. Context menu is
-    // handled in onMouseDown for these browsers.
-    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
-    on(d.input.getField(), "contextmenu", function (e) {
-      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
-    });
-
-    // Used to suppress mouse event handling when a touch happens
-    var touchFinished, prevTouch = {end: 0};
-    function finishTouch() {
-      if (d.activeTouch) {
-        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
-        prevTouch = d.activeTouch;
-        prevTouch.end = +new Date;
-      }
-    }
-    function isMouseLikeTouchEvent(e) {
-      if (e.touches.length != 1) { return false }
-      var touch = e.touches[0];
-      return touch.radiusX <= 1 && touch.radiusY <= 1
-    }
-    function farAway(touch, other) {
-      if (other.left == null) { return true }
-      var dx = other.left - touch.left, dy = other.top - touch.top;
-      return dx * dx + dy * dy > 20 * 20
-    }
-    on(d.scroller, "touchstart", function (e) {
-      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
-        d.input.ensurePolled();
-        clearTimeout(touchFinished);
-        var now = +new Date;
-        d.activeTouch = {start: now, moved: false,
-                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
-        if (e.touches.length == 1) {
-          d.activeTouch.left = e.touches[0].pageX;
-          d.activeTouch.top = e.touches[0].pageY;
-        }
-      }
-    });
-    on(d.scroller, "touchmove", function () {
-      if (d.activeTouch) { d.activeTouch.moved = true; }
-    });
-    on(d.scroller, "touchend", function (e) {
-      var touch = d.activeTouch;
-      if (touch && !eventInWidget(d, e) && touch.left != null &&
-          !touch.moved && new Date - touch.start < 300) {
-        var pos = cm.coordsChar(d.activeTouch, "page"), range;
-        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
-          { range = new Range(pos, pos); }
-        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
-          { range = cm.findWordAt(pos); }
-        else // Triple tap
-          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
-        cm.setSelection(range.anchor, range.head);
-        cm.focus();
-        e_preventDefault(e);
-      }
-      finishTouch();
-    });
-    on(d.scroller, "touchcancel", finishTouch);
-
-    // Sync scrolling between fake scrollbars and real scrollable
-    // area, ensure viewport is updated when scrolling.
-    on(d.scroller, "scroll", function () {
-      if (d.scroller.clientHeight) {
-        updateScrollTop(cm, d.scroller.scrollTop);
-        setScrollLeft(cm, d.scroller.scrollLeft, true);
-        signal(cm, "scroll", cm);
-      }
-    });
-
-    // Listen to wheel events in order to try and update the viewport on time.
-    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
-    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
-
-    // Prevent wrapper from ever scrolling
-    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
-
-    d.dragFunctions = {
-      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
-      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
-      start: function (e) { return onDragStart(cm, e); },
-      drop: operation(cm, onDrop),
-      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
-    };
-
-    var inp = d.input.getField();
-    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
-    on(inp, "keydown", operation(cm, onKeyDown));
-    on(inp, "keypress", operation(cm, onKeyPress));
-    on(inp, "focus", function (e) { return onFocus(cm, e); });
-    on(inp, "blur", function (e) { return onBlur(cm, e); });
-  }
-
-  var initHooks = [];
-  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
-
-  // Indent the given line. The how parameter can be "smart",
-  // "add"/null, "subtract", or "prev". When aggressive is false
-  // (typically set to true for forced single-line indents), empty
-  // lines are not indented, and places where the mode returns Pass
-  // are left alone.
-  function indentLine(cm, n, how, aggressive) {
-    var doc = cm.doc, state;
-    if (how == null) { how = "add"; }
-    if (how == "smart") {
-      // Fall back to "prev" when the mode doesn't have an indentation
-      // method.
-      if (!doc.mode.indent) { how = "prev"; }
-      else { state = getContextBefore(cm, n).state; }
-    }
-
-    var tabSize = cm.options.tabSize;
-    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
-    if (line.stateAfter) { line.stateAfter = null; }
-    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
-    if (!aggressive && !/\S/.test(line.text)) {
-      indentation = 0;
-      how = "not";
-    } else if (how == "smart") {
-      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
-      if (indentation == Pass || indentation > 150) {
-        if (!aggressive) { return }
-        how = "prev";
-      }
-    }
-    if (how == "prev") {
-      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
-      else { indentation = 0; }
-    } else if (how == "add") {
-      indentation = curSpace + cm.options.indentUnit;
-    } else if (how == "subtract") {
-      indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
-    }
-    indentation = Math.max(0, indentation);
-
-    var indentString = "", pos = 0;
-    if (cm.options.indentWithTabs)
-      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
-    if (pos < indentation) { indentString += spaceStr(indentation - pos); }
-
-    if (indentString != curSpaceString) {
-      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
-      line.stateAfter = null;
-      return true
-    } else {
-      // Ensure that, if the cursor was in the whitespace at the start
-      // of the line, it is moved to the end of that space.
-      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
-        var range = doc.sel.ranges[i$1];
-        if (range.head.line == n && range.head.ch < curSpaceString.length) {
-          var pos$1 = Pos(n, curSpaceString.length);
-          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
-          break
-        }
-      }
-    }
-  }
-
-  // This will be set to a {lineWise: bool, text: [string]} object, so
-  // that, when pasting, we know what kind of selections the copied
-  // text was made out of.
-  var lastCopied = null;
-
-  function setLastCopied(newLastCopied) {
-    lastCopied = newLastCopied;
-  }
-
-  function applyTextInput(cm, inserted, deleted, sel, origin) {
-    var doc = cm.doc;
-    cm.display.shift = false;
-    if (!sel) { sel = doc.sel; }
-
-    var recent = +new Date - 200;
-    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
-    var textLines = splitLinesAuto(inserted), multiPaste = null;
-    // When pasting N lines into N selections, insert one line per selection
-    if (paste && sel.ranges.length > 1) {
-      if (lastCopied && lastCopied.text.join("\n") == inserted) {
-        if (sel.ranges.length % lastCopied.text.length == 0) {
-          multiPaste = [];
-          for (var i = 0; i < lastCopied.text.length; i++)
-            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
-        }
-      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
-        multiPaste = map(textLines, function (l) { return [l]; });
-      }
-    }
-
-    var updateInput = cm.curOp.updateInput;
-    // Normal behavior is to insert the new text into every selection
-    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
-      var range$$1 = sel.ranges[i$1];
-      var from = range$$1.from(), to = range$$1.to();
-      if (range$$1.empty()) {
-        if (deleted && deleted > 0) // Handle deletion
-          { from = Pos(from.line, from.ch - deleted); }
-        else if (cm.state.overwrite && !paste) // Handle overwrite
-          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
-        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
-          { from = to = Pos(from.line, 0); }
-      }
-      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
-                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
-      makeChange(cm.doc, changeEvent);
-      signalLater(cm, "inputRead", cm, changeEvent);
-    }
-    if (inserted && !paste)
-      { triggerElectric(cm, inserted); }
-
-    ensureCursorVisible(cm);
-    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
-    cm.curOp.typing = true;
-    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
-  }
-
-  function handlePaste(e, cm) {
-    var pasted = e.clipboardData && e.clipboardData.getData("Text");
-    if (pasted) {
-      e.preventDefault();
-      if (!cm.isReadOnly() && !cm.options.disableInput)
-        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
-      return true
-    }
-  }
-
-  function triggerElectric(cm, inserted) {
-    // When an 'electric' character is inserted, immediately trigger a reindent
-    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
-    var sel = cm.doc.sel;
-
-    for (var i = sel.ranges.length - 1; i >= 0; i--) {
-      var range$$1 = sel.ranges[i];
-      if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
-      var mode = cm.getModeAt(range$$1.head);
-      var indented = false;
-      if (mode.electricChars) {
-        for (var j = 0; j < mode.electricChars.length; j++)
-          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
-            indented = indentLine(cm, range$$1.head.line, "smart");
-            break
-          } }
-      } else if (mode.electricInput) {
-        if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
-          { indented = indentLine(cm, range$$1.head.line, "smart"); }
-      }
-      if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
-    }
-  }
-
-  function copyableRanges(cm) {
-    var text = [], ranges = [];
-    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
-      var line = cm.doc.sel.ranges[i].head.line;
-      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
-      ranges.push(lineRange);
-      text.push(cm.getRange(lineRange.anchor, lineRange.head));
-    }
-    return {text: text, ranges: ranges}
-  }
-
-  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
-    field.setAttribute("autocorrect", autocorrect ? "" : "off");
-    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
-    field.setAttribute("spellcheck", !!spellcheck);
-  }
-
-  function hiddenTextarea() {
-    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
-    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
-    // The textarea is kept positioned near the cursor to prevent the
-    // fact that it'll be scrolled into view on input from scrolling
-    // our fake cursor out of view. On webkit, when wrap=off, paste is
-    // very slow. So make the area wide instead.
-    if (webkit) { te.style.width = "1000px"; }
-    else { te.setAttribute("wrap", "off"); }
-    // If border: 0; -- iOS fails to open keyboard (issue #1287)
-    if (ios) { te.style.border = "1px solid black"; }
-    disableBrowserMagic(te);
-    return div
-  }
-
-  // The publicly visible API. Note that methodOp(f) means
-  // 'wrap f in an operation, performed on its `this` parameter'.
-
-  // This is not the complete set of editor methods. Most of the
-  // methods defined on the Doc type are also injected into
-  // CodeMirror.prototype, for backwards compatibility and
-  // convenience.
-
-  function addEditorMethods(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    var helpers = CodeMirror.helpers = {};
-
-    CodeMirror.prototype = {
-      constructor: CodeMirror,
-      focus: function(){window.focus(); this.display.input.focus();},
-
-      setOption: function(option, value) {
-        var options = this.options, old = options[option];
-        if (options[option] == value && option != "mode") { return }
-        options[option] = value;
-        if (optionHandlers.hasOwnProperty(option))
-          { operation(this, optionHandlers[option])(this, value, old); }
-        signal(this, "optionChange", this, option);
-      },
-
-      getOption: function(option) {return this.options[option]},
-      getDoc: function() {return this.doc},
-
-      addKeyMap: function(map$$1, bottom) {
-        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
-      },
-      removeKeyMap: function(map$$1) {
-        var maps = this.state.keyMaps;
-        for (var i = 0; i < maps.length; ++i)
-          { if (maps[i] == map$$1 || maps[i].name == map$$1) {
-            maps.splice(i, 1);
-            return true
-          } }
-      },
-
-      addOverlay: methodOp(function(spec, options) {
-        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
-        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
-        insertSorted(this.state.overlays,
-                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
-                      priority: (options && options.priority) || 0},
-                     function (overlay) { return overlay.priority; });
-        this.state.modeGen++;
-        regChange(this);
-      }),
-      removeOverlay: methodOp(function(spec) {
-        var this$1 = this;
-
-        var overlays = this.state.overlays;
-        for (var i = 0; i < overlays.length; ++i) {
-          var cur = overlays[i].modeSpec;
-          if (cur == spec || typeof spec == "string" && cur.name == spec) {
-            overlays.splice(i, 1);
-            this$1.state.modeGen++;
-            regChange(this$1);
-            return
-          }
-        }
-      }),
-
-      indentLine: methodOp(function(n, dir, aggressive) {
-        if (typeof dir != "string" && typeof dir != "number") {
-          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
-          else { dir = dir ? "add" : "subtract"; }
-        }
-        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
-      }),
-      indentSelection: methodOp(function(how) {
-        var this$1 = this;
-
-        var ranges = this.doc.sel.ranges, end = -1;
-        for (var i = 0; i < ranges.length; i++) {
-          var range$$1 = ranges[i];
-          if (!range$$1.empty()) {
-            var from = range$$1.from(), to = range$$1.to();
-            var start = Math.max(end, from.line);
-            end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
-            for (var j = start; j < end; ++j)
-              { indentLine(this$1, j, how); }
-            var newRanges = this$1.doc.sel.ranges;
-            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
-              { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
-          } else if (range$$1.head.line > end) {
-            indentLine(this$1, range$$1.head.line, how, true);
-            end = range$$1.head.line;
-            if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
-          }
-        }
-      }),
-
-      // Fetch the parser token for a given character. Useful for hacks
-      // that want to inspect the mode state (say, for completion).
-      getTokenAt: function(pos, precise) {
-        return takeToken(this, pos, precise)
-      },
-
-      getLineTokens: function(line, precise) {
-        return takeToken(this, Pos(line), precise, true)
-      },
-
-      getTokenTypeAt: function(pos) {
-        pos = clipPos(this.doc, pos);
-        var styles = getLineStyles(this, getLine(this.doc, pos.line));
-        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-        var type;
-        if (ch == 0) { type = styles[2]; }
-        else { for (;;) {
-          var mid = (before + after) >> 1;
-          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
-          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
-          else { type = styles[mid * 2 + 2]; break }
-        } }
-        var cut = type ? type.indexOf("overlay ") : -1;
-        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
-      },
-
-      getModeAt: function(pos) {
-        var mode = this.doc.mode;
-        if (!mode.innerMode) { return mode }
-        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
-      },
-
-      getHelper: function(pos, type) {
-        return this.getHelpers(pos, type)[0]
-      },
-
-      getHelpers: function(pos, type) {
-        var this$1 = this;
-
-        var found = [];
-        if (!helpers.hasOwnProperty(type)) { return found }
-        var help = helpers[type], mode = this.getModeAt(pos);
-        if (typeof mode[type] == "string") {
-          if (help[mode[type]]) { found.push(help[mode[type]]); }
-        } else if (mode[type]) {
-          for (var i = 0; i < mode[type].length; i++) {
-            var val = help[mode[type][i]];
-            if (val) { found.push(val); }
-          }
-        } else if (mode.helperType && help[mode.helperType]) {
-          found.push(help[mode.helperType]);
-        } else if (help[mode.name]) {
-          found.push(help[mode.name]);
-        }
-        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
-          var cur = help._global[i$1];
-          if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
-            { found.push(cur.val); }
-        }
-        return found
-      },
-
-      getStateAfter: function(line, precise) {
-        var doc = this.doc;
-        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-        return getContextBefore(this, line + 1, precise).state
-      },
-
-      cursorCoords: function(start, mode) {
-        var pos, range$$1 = this.doc.sel.primary();
-        if (start == null) { pos = range$$1.head; }
-        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
-        else { pos = start ? range$$1.from() : range$$1.to(); }
-        return cursorCoords(this, pos, mode || "page")
-      },
-
-      charCoords: function(pos, mode) {
-        return charCoords(this, clipPos(this.doc, pos), mode || "page")
-      },
-
-      coordsChar: function(coords, mode) {
-        coords = fromCoordSystem(this, coords, mode || "page");
-        return coordsChar(this, coords.left, coords.top)
-      },
-
-      lineAtHeight: function(height, mode) {
-        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-        return lineAtHeight(this.doc, height + this.display.viewOffset)
-      },
-      heightAtLine: function(line, mode, includeWidgets) {
-        var end = false, lineObj;
-        if (typeof line == "number") {
-          var last = this.doc.first + this.doc.size - 1;
-          if (line < this.doc.first) { line = this.doc.first; }
-          else if (line > last) { line = last; end = true; }
-          lineObj = getLine(this.doc, line);
-        } else {
-          lineObj = line;
-        }
-        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
-          (end ? this.doc.height - heightAtLine(lineObj) : 0)
-      },
-
-      defaultTextHeight: function() { return textHeight(this.display) },
-      defaultCharWidth: function() { return charWidth(this.display) },
-
-      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
-
-      addWidget: function(pos, node, scroll, vert, horiz) {
-        var display = this.display;
-        pos = cursorCoords(this, clipPos(this.doc, pos));
-        var top = pos.bottom, left = pos.left;
-        node.style.position = "absolute";
-        node.setAttribute("cm-ignore-events", "true");
-        this.display.input.setUneditable(node);
-        display.sizer.appendChild(node);
-        if (vert == "over") {
-          top = pos.top;
-        } else if (vert == "above" || vert == "near") {
-          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
-          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
-          // Default to positioning above (if specified and possible); otherwise default to positioning below
-          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
-            { top = pos.top - node.offsetHeight; }
-          else if (pos.bottom + node.offsetHeight <= vspace)
-            { top = pos.bottom; }
-          if (left + node.offsetWidth > hspace)
-            { left = hspace - node.offsetWidth; }
-        }
-        node.style.top = top + "px";
-        node.style.left = node.style.right = "";
-        if (horiz == "right") {
-          left = display.sizer.clientWidth - node.offsetWidth;
-          node.style.right = "0px";
-        } else {
-          if (horiz == "left") { left = 0; }
-          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
-          node.style.left = left + "px";
-        }
-        if (scroll)
-          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
-      },
-
-      triggerOnKeyDown: methodOp(onKeyDown),
-      triggerOnKeyPress: methodOp(onKeyPress),
-      triggerOnKeyUp: onKeyUp,
-      triggerOnMouseDown: methodOp(onMouseDown),
-
-      execCommand: function(cmd) {
-        if (commands.hasOwnProperty(cmd))
-          { return commands[cmd].call(null, this) }
-      },
-
-      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
-
-      findPosH: function(from, amount, unit, visually) {
-        var this$1 = this;
-
-        var dir = 1;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          cur = findPosH(this$1.doc, cur, dir, unit, visually);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveH: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        this.extendSelectionsBy(function (range$$1) {
-          if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
-            { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
-          else
-            { return dir < 0 ? range$$1.from() : range$$1.to() }
-        }, sel_move);
-      }),
-
-      deleteH: methodOp(function(dir, unit) {
-        var sel = this.doc.sel, doc = this.doc;
-        if (sel.somethingSelected())
-          { doc.replaceSelection("", null, "+delete"); }
-        else
-          { deleteNearSelection(this, function (range$$1) {
-            var other = findPosH(doc, range$$1.head, dir, unit, false);
-            return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
-          }); }
-      }),
-
-      findPosV: function(from, amount, unit, goalColumn) {
-        var this$1 = this;
-
-        var dir = 1, x = goalColumn;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          var coords = cursorCoords(this$1, cur, "div");
-          if (x == null) { x = coords.left; }
-          else { coords.left = x; }
-          cur = findPosV(this$1, coords, dir, unit);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveV: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        var doc = this.doc, goals = [];
-        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
-        doc.extendSelectionsBy(function (range$$1) {
-          if (collapse)
-            { return dir < 0 ? range$$1.from() : range$$1.to() }
-          var headPos = cursorCoords(this$1, range$$1.head, "div");
-          if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
-          goals.push(headPos.left);
-          var pos = findPosV(this$1, headPos, dir, unit);
-          if (unit == "page" && range$$1 == doc.sel.primary())
-            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
-          return pos
-        }, sel_move);
-        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
-          { doc.sel.ranges[i].goalColumn = goals[i]; } }
-      }),
-
-      // Find the word at the given position (as returned by coordsChar).
-      findWordAt: function(pos) {
-        var doc = this.doc, line = getLine(doc, pos.line).text;
-        var start = pos.ch, end = pos.ch;
-        if (line) {
-          var helper = this.getHelper(pos, "wordChars");
-          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
-          var startChar = line.charAt(start);
-          var check = isWordChar(startChar, helper)
-            ? function (ch) { return isWordChar(ch, helper); }
-            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
-            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
-          while (start > 0 && check(line.charAt(start - 1))) { --start; }
-          while (end < line.length && check(line.charAt(end))) { ++end; }
-        }
-        return new Range(Pos(pos.line, start), Pos(pos.line, end))
-      },
-
-      toggleOverwrite: function(value) {
-        if (value != null && value == this.state.overwrite) { return }
-        if (this.state.overwrite = !this.state.overwrite)
-          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-        else
-          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-
-        signal(this, "overwriteToggle", this, this.state.overwrite);
-      },
-      hasFocus: function() { return this.display.input.getField() == activeElt() },
-      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
-
-      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
-      getScrollInfo: function() {
-        var scroller = this.display.scroller;
-        return {left: scroller.scrollLeft, top: scroller.scrollTop,
-                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
-                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
-                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
-      },
-
-      scrollIntoView: methodOp(function(range$$1, margin) {
-        if (range$$1 == null) {
-          range$$1 = {from: this.doc.sel.primary().head, to: null};
-          if (margin == null) { margin = this.options.cursorScrollMargin; }
-        } else if (typeof range$$1 == "number") {
-          range$$1 = {from: Pos(range$$1, 0), to: null};
-        } else if (range$$1.from == null) {
-          range$$1 = {from: range$$1, to: null};
-        }
-        if (!range$$1.to) { range$$1.to = range$$1.from; }
-        range$$1.margin = margin || 0;
-
-        if (range$$1.from.line != null) {
-          scrollToRange(this, range$$1);
-        } else {
-          scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
-        }
-      }),
-
-      setSize: methodOp(function(width, height) {
-        var this$1 = this;
-
-        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
-        if (width != null) { this.display.wrapper.style.width = interpret(width); }
-        if (height != null) { this.display.wrapper.style.height = interpret(height); }
-        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
-        var lineNo$$1 = this.display.viewFrom;
-        this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
-          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
-            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
-          ++lineNo$$1;
-        });
-        this.curOp.forceUpdate = true;
-        signal(this, "refresh", this);
-      }),
-
-      operation: function(f){return runInOp(this, f)},
-      startOperation: function(){return startOperation(this)},
-      endOperation: function(){return endOperation(this)},
-
-      refresh: methodOp(function() {
-        var oldHeight = this.display.cachedTextHeight;
-        regChange(this);
-        this.curOp.forceUpdate = true;
-        clearCaches(this);
-        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
-        updateGutterSpace(this.display);
-        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
-          { estimateLineHeights(this); }
-        signal(this, "refresh", this);
-      }),
-
-      swapDoc: methodOp(function(doc) {
-        var old = this.doc;
-        old.cm = null;
-        // Cancel the current text selection if any (#5821)
-        if (this.state.selectingText) { this.state.selectingText(); }
-        attachDoc(this, doc);
-        clearCaches(this);
-        this.display.input.reset();
-        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
-        this.curOp.forceScroll = true;
-        signalLater(this, "swapDoc", this, old);
-        return old
-      }),
-
-      phrase: function(phraseText) {
-        var phrases = this.options.phrases;
-        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
-      },
-
-      getInputField: function(){return this.display.input.getField()},
-      getWrapperElement: function(){return this.display.wrapper},
-      getScrollerElement: function(){return this.display.scroller},
-      getGutterElement: function(){return this.display.gutters}
-    };
-    eventMixin(CodeMirror);
-
-    CodeMirror.registerHelper = function(type, name, value) {
-      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
-      helpers[type][name] = value;
-    };
-    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
-      CodeMirror.registerHelper(type, name, value);
-      helpers[type]._global.push({pred: predicate, val: value});
-    };
-  }
-
-  // Used for horizontal relative motion. Dir is -1 or 1 (left or
-  // right), unit can be "char", "column" (like char, but doesn't
-  // cross line boundaries), "word" (across next word), or "group" (to
-  // the start of next group of word or non-word-non-whitespace
-  // chars). The visually param controls whether, in right-to-left
-  // text, direction 1 means to move towards the next index in the
-  // string, or towards the character to the right of the current
-  // position. The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosH(doc, pos, dir, unit, visually) {
-    var oldPos = pos;
-    var origDir = dir;
-    var lineObj = getLine(doc, pos.line);
-    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
-    function findNextLine() {
-      var l = pos.line + lineDir;
-      if (l < doc.first || l >= doc.first + doc.size) { return false }
-      pos = new Pos(l, pos.ch, pos.sticky);
-      return lineObj = getLine(doc, l)
-    }
-    function moveOnce(boundToLine) {
-      var next;
-      if (visually) {
-        next = moveVisually(doc.cm, lineObj, pos, dir);
-      } else {
-        next = moveLogically(lineObj, pos, dir);
-      }
-      if (next == null) {
-        if (!boundToLine && findNextLine())
-          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
-        else
-          { return false }
-      } else {
-        pos = next;
-      }
-      return true
-    }
-
-    if (unit == "char") {
-      moveOnce();
-    } else if (unit == "column") {
-      moveOnce(true);
-    } else if (unit == "word" || unit == "group") {
-      var sawType = null, group = unit == "group";
-      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
-      for (var first = true;; first = false) {
-        if (dir < 0 && !moveOnce(!first)) { break }
-        var cur = lineObj.text.charAt(pos.ch) || "\n";
-        var type = isWordChar(cur, helper) ? "w"
-          : group && cur == "\n" ? "n"
-          : !group || /\s/.test(cur) ? null
-          : "p";
-        if (group && !first && !type) { type = "s"; }
-        if (sawType && sawType != type) {
-          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
-          break
-        }
-
-        if (type) { sawType = type; }
-        if (dir > 0 && !moveOnce(!first)) { break }
-      }
-    }
-    var result = skipAtomic(doc, pos, oldPos, origDir, true);
-    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
-    return result
-  }
-
-  // For relative vertical movement. Dir may be -1 or 1. Unit can be
-  // "page" or "line". The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosV(cm, pos, dir, unit) {
-    var doc = cm.doc, x = pos.left, y;
-    if (unit == "page") {
-      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
-      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
-      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
-
-    } else if (unit == "line") {
-      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
-    }
-    var target;
-    for (;;) {
-      target = coordsChar(cm, x, y);
-      if (!target.outside) { break }
-      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
-      y += dir * 5;
-    }
-    return target
-  }
-
-  // CONTENTEDITABLE INPUT STYLE
-
-  var ContentEditableInput = function(cm) {
-    this.cm = cm;
-    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
-    this.polling = new Delayed();
-    this.composing = null;
-    this.gracePeriod = false;
-    this.readDOMTimeout = null;
-  };
-
-  ContentEditableInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = input.cm;
-    var div = input.div = display.lineDiv;
-    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
-
-    on(div, "paste", function (e) {
-      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
-      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
-    });
-
-    on(div, "compositionstart", function (e) {
-      this$1.composing = {data: e.data, done: false};
-    });
-    on(div, "compositionupdate", function (e) {
-      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
-    });
-    on(div, "compositionend", function (e) {
-      if (this$1.composing) {
-        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
-        this$1.composing.done = true;
-      }
-    });
-
-    on(div, "touchstart", function () { return input.forceCompositionEnd(); });
-
-    on(div, "input", function () {
-      if (!this$1.composing) { this$1.readFromDOMSoon(); }
-    });
-
-    function onCopyCut(e) {
-      if (signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.operation(function () {
-            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
-            cm.replaceSelection("", null, "cut");
-          });
-        }
-      }
-      if (e.clipboardData) {
-        e.clipboardData.clearData();
-        var content = lastCopied.text.join("\n");
-        // iOS exposes the clipboard API, but seems to discard content inserted into it
-        e.clipboardData.setData("Text", content);
-        if (e.clipboardData.getData("Text") == content) {
-          e.preventDefault();
-          return
-        }
-      }
-      // Old-fashioned briefly-focus-a-textarea hack
-      var kludge = hiddenTextarea(), te = kludge.firstChild;
-      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
-      te.value = lastCopied.text.join("\n");
-      var hadFocus = document.activeElement;
-      selectInput(te);
-      setTimeout(function () {
-        cm.display.lineSpace.removeChild(kludge);
-        hadFocus.focus();
-        if (hadFocus == div) { input.showPrimarySelection(); }
-      }, 50);
-    }
-    on(div, "copy", onCopyCut);
-    on(div, "cut", onCopyCut);
-  };
-
-  ContentEditableInput.prototype.prepareSelection = function () {
-    var result = prepareSelection(this.cm, false);
-    result.focus = this.cm.state.focused;
-    return result
-  };
-
-  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
-    if (!info || !this.cm.display.view.length) { return }
-    if (info.focus || takeFocus) { this.showPrimarySelection(); }
-    this.showMultipleSelections(info);
-  };
-
-  ContentEditableInput.prototype.getSelection = function () {
-    return this.cm.display.wrapper.ownerDocument.getSelection()
-  };
-
-  ContentEditableInput.prototype.showPrimarySelection = function () {
-    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
-    var from = prim.from(), to = prim.to();
-
-    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
-        cmp(minPos(curAnchor, curFocus), from) == 0 &&
-        cmp(maxPos(curAnchor, curFocus), to) == 0)
-      { return }
-
-    var view = cm.display.view;
-    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
-        {node: view[0].measure.map[2], offset: 0};
-    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
-    if (!end) {
-      var measure = view[view.length - 1].measure;
-      var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
-      end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
-    }
-
-    if (!start || !end) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var old = sel.rangeCount && sel.getRangeAt(0), rng;
-    try { rng = range(start.node, start.offset, end.offset, end.node); }
-    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
-    if (rng) {
-      if (!gecko && cm.state.focused) {
-        sel.collapse(start.node, start.offset);
-        if (!rng.collapsed) {
-          sel.removeAllRanges();
-          sel.addRange(rng);
-        }
-      } else {
-        sel.removeAllRanges();
-        sel.addRange(rng);
-      }
-      if (old && sel.anchorNode == null) { sel.addRange(old); }
-      else if (gecko) { this.startGracePeriod(); }
-    }
-    this.rememberSelection();
-  };
-
-  ContentEditableInput.prototype.startGracePeriod = function () {
-      var this$1 = this;
-
-    clearTimeout(this.gracePeriod);
-    this.gracePeriod = setTimeout(function () {
-      this$1.gracePeriod = false;
-      if (this$1.selectionChanged())
-        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
-    }, 20);
-  };
-
-  ContentEditableInput.prototype.showMultipleSelections = function (info) {
-    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
-    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
-  };
-
-  ContentEditableInput.prototype.rememberSelection = function () {
-    var sel = this.getSelection();
-    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
-    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
-  };
-
-  ContentEditableInput.prototype.selectionInEditor = function () {
-    var sel = this.getSelection();
-    if (!sel.rangeCount) { return false }
-    var node = sel.getRangeAt(0).commonAncestorContainer;
-    return contains(this.div, node)
-  };
-
-  ContentEditableInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor") {
-      if (!this.selectionInEditor())
-        { this.showSelection(this.prepareSelection(), true); }
-      this.div.focus();
-    }
-  };
-  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
-  ContentEditableInput.prototype.getField = function () { return this.div };
-
-  ContentEditableInput.prototype.supportsTouch = function () { return true };
-
-  ContentEditableInput.prototype.receivedFocus = function () {
-    var input = this;
-    if (this.selectionInEditor())
-      { this.pollSelection(); }
-    else
-      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
-
-    function poll() {
-      if (input.cm.state.focused) {
-        input.pollSelection();
-        input.polling.set(input.cm.options.pollInterval, poll);
-      }
-    }
-    this.polling.set(this.cm.options.pollInterval, poll);
-  };
-
-  ContentEditableInput.prototype.selectionChanged = function () {
-    var sel = this.getSelection();
-    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
-      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
-  };
-
-  ContentEditableInput.prototype.pollSelection = function () {
-    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
-    var sel = this.getSelection(), cm = this.cm;
-    // On Android Chrome (version 56, at least), backspacing into an
-    // uneditable block element will put the cursor in that element,
-    // and then, because it's not editable, hide the virtual keyboard.
-    // Because Android doesn't allow us to actually detect backspace
-    // presses in a sane way, this code checks for when that happens
-    // and simulates a backspace press in this case.
-    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
-      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
-      this.blur();
-      this.focus();
-      return
-    }
-    if (this.composing) { return }
-    this.rememberSelection();
-    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (anchor && head) { runInOp(cm, function () {
-      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
-      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
-    }); }
-  };
-
-  ContentEditableInput.prototype.pollContent = function () {
-    if (this.readDOMTimeout != null) {
-      clearTimeout(this.readDOMTimeout);
-      this.readDOMTimeout = null;
-    }
-
-    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
-    var from = sel.from(), to = sel.to();
-    if (from.ch == 0 && from.line > cm.firstLine())
-      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
-    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
-      { to = Pos(to.line + 1, 0); }
-    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
-
-    var fromIndex, fromLine, fromNode;
-    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
-      fromLine = lineNo(display.view[0].line);
-      fromNode = display.view[0].node;
-    } else {
-      fromLine = lineNo(display.view[fromIndex].line);
-      fromNode = display.view[fromIndex - 1].node.nextSibling;
-    }
-    var toIndex = findViewIndex(cm, to.line);
-    var toLine, toNode;
-    if (toIndex == display.view.length - 1) {
-      toLine = display.viewTo - 1;
-      toNode = display.lineDiv.lastChild;
-    } else {
-      toLine = lineNo(display.view[toIndex + 1].line) - 1;
-      toNode = display.view[toIndex + 1].node.previousSibling;
-    }
-
-    if (!fromNode) { return false }
-    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
-    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
-    while (newText.length > 1 && oldText.length > 1) {
-      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
-      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
-      else { break }
-    }
-
-    var cutFront = 0, cutEnd = 0;
-    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
-    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
-      { ++cutFront; }
-    var newBot = lst(newText), oldBot = lst(oldText);
-    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
-                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
-    while (cutEnd < maxCutEnd &&
-           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
-      { ++cutEnd; }
-    // Try to move start of change to start of selection if ambiguous
-    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
-      while (cutFront && cutFront > from.ch &&
-             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
-        cutFront--;
-        cutEnd++;
-      }
-    }
-
-    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
-    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
-
-    var chFrom = Pos(fromLine, cutFront);
-    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
-    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
-      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
-      return true
-    }
-  };
-
-  ContentEditableInput.prototype.ensurePolled = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.reset = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.forceCompositionEnd = function () {
-    if (!this.composing) { return }
-    clearTimeout(this.readDOMTimeout);
-    this.composing = null;
-    this.updateFromDOM();
-    this.div.blur();
-    this.div.focus();
-  };
-  ContentEditableInput.prototype.readFromDOMSoon = function () {
-      var this$1 = this;
-
-    if (this.readDOMTimeout != null) { return }
-    this.readDOMTimeout = setTimeout(function () {
-      this$1.readDOMTimeout = null;
-      if (this$1.composing) {
-        if (this$1.composing.done) { this$1.composing = null; }
-        else { return }
-      }
-      this$1.updateFromDOM();
-    }, 80);
-  };
-
-  ContentEditableInput.prototype.updateFromDOM = function () {
-      var this$1 = this;
-
-    if (this.cm.isReadOnly() || !this.pollContent())
-      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
-  };
-
-  ContentEditableInput.prototype.setUneditable = function (node) {
-    node.contentEditable = "false";
-  };
-
-  ContentEditableInput.prototype.onKeyPress = function (e) {
-    if (e.charCode == 0 || this.composing) { return }
-    e.preventDefault();
-    if (!this.cm.isReadOnly())
-      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
-  };
-
-  ContentEditableInput.prototype.readOnlyChanged = function (val) {
-    this.div.contentEditable = String(val != "nocursor");
-  };
-
-  ContentEditableInput.prototype.onContextMenu = function () {};
-  ContentEditableInput.prototype.resetPosition = function () {};
-
-  ContentEditableInput.prototype.needsContentAttribute = true;
-
-  function posToDOM(cm, pos) {
-    var view = findViewForLine(cm, pos.line);
-    if (!view || view.hidden) { return null }
-    var line = getLine(cm.doc, pos.line);
-    var info = mapFromLineView(view, line, pos.line);
-
-    var order = getOrder(line, cm.doc.direction), side = "left";
-    if (order) {
-      var partPos = getBidiPartAt(order, pos.ch);
-      side = partPos % 2 ? "right" : "left";
-    }
-    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
-    result.offset = result.collapse == "right" ? result.end : result.start;
-    return result
-  }
-
-  function isInGutter(node) {
-    for (var scan = node; scan; scan = scan.parentNode)
-      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
-    return false
-  }
-
-  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
-
-  function domTextBetween(cm, from, to, fromLine, toLine) {
-    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
-    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
-    function close() {
-      if (closing) {
-        text += lineSep;
-        if (extraLinebreak) { text += lineSep; }
-        closing = extraLinebreak = false;
-      }
-    }
-    function addText(str) {
-      if (str) {
-        close();
-        text += str;
-      }
-    }
-    function walk(node) {
-      if (node.nodeType == 1) {
-        var cmText = node.getAttribute("cm-text");
-        if (cmText) {
-          addText(cmText);
-          return
-        }
-        var markerID = node.getAttribute("cm-marker"), range$$1;
-        if (markerID) {
-          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
-          if (found.length && (range$$1 = found[0].find(0)))
-            { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
-          return
-        }
-        if (node.getAttribute("contenteditable") == "false") { return }
-        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
-        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
-
-        if (isBlock) { close(); }
-        for (var i = 0; i < node.childNodes.length; i++)
-          { walk(node.childNodes[i]); }
-
-        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
-        if (isBlock) { closing = true; }
-      } else if (node.nodeType == 3) {
-        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
-      }
-    }
-    for (;;) {
-      walk(from);
-      if (from == to) { break }
-      from = from.nextSibling;
-      extraLinebreak = false;
-    }
-    return text
-  }
-
-  function domToPos(cm, node, offset) {
-    var lineNode;
-    if (node == cm.display.lineDiv) {
-      lineNode = cm.display.lineDiv.childNodes[offset];
-      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
-      node = null; offset = 0;
-    } else {
-      for (lineNode = node;; lineNode = lineNode.parentNode) {
-        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
-        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
-      }
-    }
-    for (var i = 0; i < cm.display.view.length; i++) {
-      var lineView = cm.display.view[i];
-      if (lineView.node == lineNode)
-        { return locateNodeInLineView(lineView, node, offset) }
-    }
-  }
-
-  function locateNodeInLineView(lineView, node, offset) {
-    var wrapper = lineView.text.firstChild, bad = false;
-    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
-    if (node == wrapper) {
-      bad = true;
-      node = wrapper.childNodes[offset];
-      offset = 0;
-      if (!node) {
-        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
-        return badPos(Pos(lineNo(line), line.text.length), bad)
-      }
-    }
-
-    var textNode = node.nodeType == 3 ? node : null, topNode = node;
-    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
-      textNode = node.firstChild;
-      if (offset) { offset = textNode.nodeValue.length; }
-    }
-    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
-    var measure = lineView.measure, maps = measure.maps;
-
-    function find(textNode, topNode, offset) {
-      for (var i = -1; i < (maps ? maps.length : 0); i++) {
-        var map$$1 = i < 0 ? measure.map : maps[i];
-        for (var j = 0; j < map$$1.length; j += 3) {
-          var curNode = map$$1[j + 2];
-          if (curNode == textNode || curNode == topNode) {
-            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
-            var ch = map$$1[j] + offset;
-            if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
-            return Pos(line, ch)
-          }
-        }
-      }
-    }
-    var found = find(textNode, topNode, offset);
-    if (found) { return badPos(found, bad) }
-
-    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
-    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
-      found = find(after, after.firstChild, 0);
-      if (found)
-        { return badPos(Pos(found.line, found.ch - dist), bad) }
-      else
-        { dist += after.textContent.length; }
-    }
-    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
-      found = find(before, before.firstChild, -1);
-      if (found)
-        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
-      else
-        { dist$1 += before.textContent.length; }
-    }
-  }
-
-  // TEXTAREA INPUT STYLE
-
-  var TextareaInput = function(cm) {
-    this.cm = cm;
-    // See input.poll and input.reset
-    this.prevInput = "";
-
-    // Flag that indicates whether we expect input to appear real soon
-    // now (after some event like 'keypress' or 'input') and are
-    // polling intensively.
-    this.pollingFast = false;
-    // Self-resetting timeout for the poller
-    this.polling = new Delayed();
-    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
-    this.hasSelection = false;
-    this.composing = null;
-  };
-
-  TextareaInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = this.cm;
-    this.createField(display);
-    var te = this.textarea;
-
-    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
-
-    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
-    if (ios) { te.style.width = "0px"; }
-
-    on(te, "input", function () {
-      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
-      input.poll();
-    });
-
-    on(te, "paste", function (e) {
-      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-
-      cm.state.pasteIncoming = +new Date;
-      input.fastPoll();
-    });
-
-    function prepareCopyCut(e) {
-      if (signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.setSelections(ranges.ranges, null, sel_dontScroll);
-        } else {
-          input.prevInput = "";
-          te.value = ranges.text.join("\n");
-          selectInput(te);
-        }
-      }
-      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
-    }
-    on(te, "cut", prepareCopyCut);
-    on(te, "copy", prepareCopyCut);
-
-    on(display.scroller, "paste", function (e) {
-      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
-      if (!te.dispatchEvent) {
-        cm.state.pasteIncoming = +new Date;
-        input.focus();
-        return
-      }
-
-      // Pass the `paste` event to the textarea so it's handled by its event listener.
-      var event = new Event("paste");
-      event.clipboardData = e.clipboardData;
-      te.dispatchEvent(event);
-    });
-
-    // Prevent normal selection in the editor (we handle our own)
-    on(display.lineSpace, "selectstart", function (e) {
-      if (!eventInWidget(display, e)) { e_preventDefault(e); }
-    });
-
-    on(te, "compositionstart", function () {
-      var start = cm.getCursor("from");
-      if (input.composing) { input.composing.range.clear(); }
-      input.composing = {
-        start: start,
-        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
-      };
-    });
-    on(te, "compositionend", function () {
-      if (input.composing) {
-        input.poll();
-        input.composing.range.clear();
-        input.composing = null;
-      }
-    });
-  };
-
-  TextareaInput.prototype.createField = function (_display) {
-    // Wraps and hides input textarea
-    this.wrapper = hiddenTextarea();
-    // The semihidden textarea that is focused when the editor is
-    // focused, and receives input.
-    this.textarea = this.wrapper.firstChild;
-  };
-
-  TextareaInput.prototype.prepareSelection = function () {
-    // Redraw the selection and/or cursor
-    var cm = this.cm, display = cm.display, doc = cm.doc;
-    var result = prepareSelection(cm);
-
-    // Move the hidden textarea near the cursor to prevent scrolling artifacts
-    if (cm.options.moveInputWithCursor) {
-      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
-      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
-      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                          headPos.top + lineOff.top - wrapOff.top));
-      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                           headPos.left + lineOff.left - wrapOff.left));
-    }
-
-    return result
-  };
-
-  TextareaInput.prototype.showSelection = function (drawn) {
-    var cm = this.cm, display = cm.display;
-    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
-    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
-    if (drawn.teTop != null) {
-      this.wrapper.style.top = drawn.teTop + "px";
-      this.wrapper.style.left = drawn.teLeft + "px";
-    }
-  };
-
-  // Reset the input to correspond to the selection (or to be empty,
-  // when not typing and nothing is selected)
-  TextareaInput.prototype.reset = function (typing) {
-    if (this.contextMenuPending || this.composing) { return }
-    var cm = this.cm;
-    if (cm.somethingSelected()) {
-      this.prevInput = "";
-      var content = cm.getSelection();
-      this.textarea.value = content;
-      if (cm.state.focused) { selectInput(this.textarea); }
-      if (ie && ie_version >= 9) { this.hasSelection = content; }
-    } else if (!typing) {
-      this.prevInput = this.textarea.value = "";
-      if (ie && ie_version >= 9) { this.hasSelection = null; }
-    }
-  };
-
-  TextareaInput.prototype.getField = function () { return this.textarea };
-
-  TextareaInput.prototype.supportsTouch = function () { return false };
-
-  TextareaInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
-      try { this.textarea.focus(); }
-      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
-    }
-  };
-
-  TextareaInput.prototype.blur = function () { this.textarea.blur(); };
-
-  TextareaInput.prototype.resetPosition = function () {
-    this.wrapper.style.top = this.wrapper.style.left = 0;
-  };
-
-  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
-
-  // Poll for input changes, using the normal rate of polling. This
-  // runs as long as the editor is focused.
-  TextareaInput.prototype.slowPoll = function () {
-      var this$1 = this;
-
-    if (this.pollingFast) { return }
-    this.polling.set(this.cm.options.pollInterval, function () {
-      this$1.poll();
-      if (this$1.cm.state.focused) { this$1.slowPoll(); }
-    });
-  };
-
-  // When an event has just come in that is likely to add or change
-  // something in the input textarea, we poll faster, to ensure that
-  // the change appears on the screen quickly.
-  TextareaInput.prototype.fastPoll = function () {
-    var missed = false, input = this;
-    input.pollingFast = true;
-    function p() {
-      var changed = input.poll();
-      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
-      else {input.pollingFast = false; input.slowPoll();}
-    }
-    input.polling.set(20, p);
-  };
-
-  // Read input from the textarea, and update the document to match.
-  // When something is selected, it is present in the textarea, and
-  // selected (unless it is huge, in which case a placeholder is
-  // used). When nothing is selected, the cursor sits after previously
-  // seen text (can be empty), which is stored in prevInput (we must
-  // not reset the textarea when typing, because that breaks IME).
-  TextareaInput.prototype.poll = function () {
-      var this$1 = this;
-
-    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
-    // Since this is called a *lot*, try to bail out as cheaply as
-    // possible when it is clear that nothing happened. hasSelection
-    // will be the case when there is a lot of text in the textarea,
-    // in which case reading its value would be expensive.
-    if (this.contextMenuPending || !cm.state.focused ||
-        (hasSelection(input) && !prevInput && !this.composing) ||
-        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
-      { return false }
-
-    var text = input.value;
-    // If nothing changed, bail.
-    if (text == prevInput && !cm.somethingSelected()) { return false }
-    // Work around nonsensical selection resetting in IE9/10, and
-    // inexplicable appearance of private area unicode characters on
-    // some key combos in Mac (#2689).
-    if (ie && ie_version >= 9 && this.hasSelection === text ||
-        mac && /[\uf700-\uf7ff]/.test(text)) {
-      cm.display.input.reset();
-      return false
-    }
-
-    if (cm.doc.sel == cm.display.selForContextMenu) {
-      var first = text.charCodeAt(0);
-      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
-      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
-    }
-    // Find the part of the input that is actually new
-    var same = 0, l = Math.min(prevInput.length, text.length);
-    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
-
-    runInOp(cm, function () {
-      applyTextInput(cm, text.slice(same), prevInput.length - same,
-                     null, this$1.composing ? "*compose" : null);
-
-      // Don't leave long text in the textarea, since it makes further polling slow
-      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
-      else { this$1.prevInput = text; }
-
-      if (this$1.composing) {
-        this$1.composing.range.clear();
-        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
-                                           {className: "CodeMirror-composing"});
-      }
-    });
-    return true
-  };
-
-  TextareaInput.prototype.ensurePolled = function () {
-    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
-  };
-
-  TextareaInput.prototype.onKeyPress = function () {
-    if (ie && ie_version >= 9) { this.hasSelection = null; }
-    this.fastPoll();
-  };
-
-  TextareaInput.prototype.onContextMenu = function (e) {
-    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
-    if (input.contextMenuPending) { input.contextMenuPending(); }
-    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
-    if (!pos || presto) { return } // Opera is difficult.
-
-    // Reset the current text selection only if the click is done outside of the selection
-    // and 'resetSelectionOnContextMenu' option is true.
-    var reset = cm.options.resetSelectionOnContextMenu;
-    if (reset && cm.doc.sel.contains(pos) == -1)
-      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
-
-    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
-    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
-    input.wrapper.style.cssText = "position: static";
-    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);";
-    var oldScrollY;
-    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
-    display.input.focus();
-    if (webkit) { window.scrollTo(null, oldScrollY); }
-    display.input.reset();
-    // Adds "Select all" to context menu in FF
-    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
-    input.contextMenuPending = rehide;
-    display.selForContextMenu = cm.doc.sel;
-    clearTimeout(display.detectingSelectAll);
-
-    // Select-all will be greyed out if there's nothing to select, so
-    // this adds a zero-width space so that we can later check whether
-    // it got selected.
-    function prepareSelectAllHack() {
-      if (te.selectionStart != null) {
-        var selected = cm.somethingSelected();
-        var extval = "\u200b" + (selected ? te.value : "");
-        te.value = "\u21da"; // Used to catch context-menu undo
-        te.value = extval;
-        input.prevInput = selected ? "" : "\u200b";
-        te.selectionStart = 1; te.selectionEnd = extval.length;
-        // Re-set this, in case some other handler touched the
-        // selection in the meantime.
-        display.selForContextMenu = cm.doc.sel;
-      }
-    }
-    function rehide() {
-      if (input.contextMenuPending != rehide) { return }
-      input.contextMenuPending = false;
-      input.wrapper.style.cssText = oldWrapperCSS;
-      te.style.cssText = oldCSS;
-      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
-
-      // Try to detect the user choosing select-all
-      if (te.selectionStart != null) {
-        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
-        var i = 0, poll = function () {
-          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
-              te.selectionEnd > 0 && input.prevInput == "\u200b") {
-            operation(cm, selectAll)(cm);
-          } else if (i++ < 10) {
-            display.detectingSelectAll = setTimeout(poll, 500);
-          } else {
-            display.selForContextMenu = null;
-            display.input.reset();
-          }
-        };
-        display.detectingSelectAll = setTimeout(poll, 200);
-      }
-    }
-
-    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
-    if (captureRightClick) {
-      e_stop(e);
-      var mouseup = function () {
-        off(window, "mouseup", mouseup);
-        setTimeout(rehide, 20);
-      };
-      on(window, "mouseup", mouseup);
-    } else {
-      setTimeout(rehide, 50);
-    }
-  };
-
-  TextareaInput.prototype.readOnlyChanged = function (val) {
-    if (!val) { this.reset(); }
-    this.textarea.disabled = val == "nocursor";
-  };
-
-  TextareaInput.prototype.setUneditable = function () {};
-
-  TextareaInput.prototype.needsContentAttribute = false;
-
-  function fromTextArea(textarea, options) {
-    options = options ? copyObj(options) : {};
-    options.value = textarea.value;
-    if (!options.tabindex && textarea.tabIndex)
-      { options.tabindex = textarea.tabIndex; }
-    if (!options.placeholder && textarea.placeholder)
-      { options.placeholder = textarea.placeholder; }
-    // Set autofocus to true if this textarea is focused, or if it has
-    // autofocus and no other element is focused.
-    if (options.autofocus == null) {
-      var hasFocus = activeElt();
-      options.autofocus = hasFocus == textarea ||
-        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
-    }
-
-    function save() {textarea.value = cm.getValue();}
-
-    var realSubmit;
-    if (textarea.form) {
-      on(textarea.form, "submit", save);
-      // Deplorable hack to make the submit method do the right thing.
-      if (!options.leaveSubmitMethodAlone) {
-        var form = textarea.form;
-        realSubmit = form.submit;
-        try {
-          var wrappedSubmit = form.submit = function () {
-            save();
-            form.submit = realSubmit;
-            form.submit();
-            form.submit = wrappedSubmit;
-          };
-        } catch(e) {}
-      }
-    }
-
-    options.finishInit = function (cm) {
-      cm.save = save;
-      cm.getTextArea = function () { return textarea; };
-      cm.toTextArea = function () {
-        cm.toTextArea = isNaN; // Prevent this from being ran twice
-        save();
-        textarea.parentNode.removeChild(cm.getWrapperElement());
-        textarea.style.display = "";
-        if (textarea.form) {
-          off(textarea.form, "submit", save);
-          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
-            { textarea.form.submit = realSubmit; }
-        }
-      };
-    };
-
-    textarea.style.display = "none";
-    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
-      options);
-    return cm
-  }
-
-  function addLegacyProps(CodeMirror) {
-    CodeMirror.off = off;
-    CodeMirror.on = on;
-    CodeMirror.wheelEventPixels = wheelEventPixels;
-    CodeMirror.Doc = Doc;
-    CodeMirror.splitLines = splitLinesAuto;
-    CodeMirror.countColumn = countColumn;
-    CodeMirror.findColumn = findColumn;
-    CodeMirror.isWordChar = isWordCharBasic;
-    CodeMirror.Pass = Pass;
-    CodeMirror.signal = signal;
-    CodeMirror.Line = Line;
-    CodeMirror.changeEnd = changeEnd;
-    CodeMirror.scrollbarModel = scrollbarModel;
-    CodeMirror.Pos = Pos;
-    CodeMirror.cmpPos = cmp;
-    CodeMirror.modes = modes;
-    CodeMirror.mimeModes = mimeModes;
-    CodeMirror.resolveMode = resolveMode;
-    CodeMirror.getMode = getMode;
-    CodeMirror.modeExtensions = modeExtensions;
-    CodeMirror.extendMode = extendMode;
-    CodeMirror.copyState = copyState;
-    CodeMirror.startState = startState;
-    CodeMirror.innerMode = innerMode;
-    CodeMirror.commands = commands;
-    CodeMirror.keyMap = keyMap;
-    CodeMirror.keyName = keyName;
-    CodeMirror.isModifierKey = isModifierKey;
-    CodeMirror.lookupKey = lookupKey;
-    CodeMirror.normalizeKeyMap = normalizeKeyMap;
-    CodeMirror.StringStream = StringStream;
-    CodeMirror.SharedTextMarker = SharedTextMarker;
-    CodeMirror.TextMarker = TextMarker;
-    CodeMirror.LineWidget = LineWidget;
-    CodeMirror.e_preventDefault = e_preventDefault;
-    CodeMirror.e_stopPropagation = e_stopPropagation;
-    CodeMirror.e_stop = e_stop;
-    CodeMirror.addClass = addClass;
-    CodeMirror.contains = contains;
-    CodeMirror.rmClass = rmClass;
-    CodeMirror.keyNames = keyNames;
-  }
-
-  // EDITOR CONSTRUCTOR
-
-  defineOptions(CodeMirror);
-
-  addEditorMethods(CodeMirror);
-
-  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
-  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
-  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
-    { CodeMirror.prototype[prop] = (function(method) {
-      return function() {return method.apply(this.doc, arguments)}
-    })(Doc.prototype[prop]); } }
-
-  eventMixin(Doc);
-  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  CodeMirror.defineMode = function(name/*, mode, …*/) {
-    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
-    defineMode.apply(this, arguments);
-  };
-
-  CodeMirror.defineMIME = defineMIME;
-
-  // Minimal default mode.
-  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
-  CodeMirror.defineMIME("text/plain", "null");
-
-  // EXTENSIONS
-
-  CodeMirror.defineExtension = function (name, func) {
-    CodeMirror.prototype[name] = func;
-  };
-  CodeMirror.defineDocExtension = function (name, func) {
-    Doc.prototype[name] = func;
-  };
-
-  CodeMirror.fromTextArea = fromTextArea;
-
-  addLegacyProps(CodeMirror);
-
-  CodeMirror.version = "5.52.0";
-
-  return CodeMirror;
-
-})));
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).CodeMirror=t()}(this,function(){"use strict";var e=navigator.userAgent,t=navigator.platform,d=/gecko\/\d/i.test(e),n=/MSIE \d/.test(e),r=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(e),i=/Edge\/(\d+)/.exec(e),w=n||r||i,v=w&&(n?document.documentMode||6:+(i||r)[1]),f=!i&&/WebKit\//.test(e),r=f&&/Qt\/\d+\.\d+/.test(e),o=!i&&/Chrome\//.test(e),p=/Opera\//.test(e),c=/Apple Computer/.test(navigator.vendor),l=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(e),u=/PhantomJS/.test(e),s=c&&(/Mobile\/\w+/.test(e)||2<navigator.maxTouchPoints),a=/Android/.test(e),h=s||a||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(e),g=s||/Mac/.test(t),m=/\bCrOS\b/.test(e),y=/win/i.test(t),e=p&&e.match(/Version\/(\d*\.\d*)/);(e=e&&Number(e[1]))&&15<=e&&(f=!(p=!1));var b=g&&(r||p&&(null==e||e<12.11)),x=d||w&&9<=v;function C(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}var S,L=function(e,t){var n=e.className,r=C(t).exec(n);r&&(t=n.slice(r.index+r[0].length),e.className=n.slice(0,r.index)+(t?r[1]+t:""))};function k(e){for(var t=e.childNodes.length;0<t;--t)e.removeChild(e.firstChild);return e}function T(e,t){return k(e).appendChild(t)}function M(e,t,n,r){var i=document.createElement(e);if(n&&(i.className=n),r&&(i.style.cssText=r),"string"==typeof t)i.appendChild(document.createTextNode(t));else if(t)for(var o=0;o<t.length;++o)i.appendChild(t[o]);return i}function N(e,t,n,r){r=M(e,t,n,r);return r.setAttribute("role","presentation"),r}function O(e,t){if(3==t.nodeType&&(t=t.parentNode),e.contains)return e.contains(t);do{if(11==t.nodeType&&(t=t.host),t==e)return!0}while(t=t.parentNode)}function A(){var t;try{t=document.activeElement}catch(e){t=document.body||null}for(;t&&t.shadowRoot&&t.shadowRoot.activeElement;)t=t.shadowRoot.activeElement;return t}function D(e,t){var n=e.className;C(t).test(n)||(e.className+=(n?" ":"")+t)}function W(e,t){for(var n=e.split(" "),r=0;r<n.length;r++)n[r]&&!C(n[r]).test(t)&&(t+=" "+n[r]);return t}S=document.createRange?function(e,t,n,r){var i=document.createRange();return i.setEnd(r||e,n),i.setStart(e,t),i}:function(e,t,n){var r=document.body.createTextRange();try{r.moveToElementText(e.parentNode)}catch(e){return r}return r.collapse(!0),r.moveEnd("character",n),r.moveStart("character",t),r};var H=function(e){e.select()};function F(e){var t=Array.prototype.slice.call(arguments,1);return function(){return e.apply(null,t)}}function P(e,t,n){for(var r in t=t||{},e)!e.hasOwnProperty(r)||!1===n&&t.hasOwnProperty(r)||(t[r]=e[r]);return t}function E(e,t,n,r,i){null==t&&-1==(t=e.search(/[^\s\u00a0]/))&&(t=e.length);for(var o=r||0,l=i||0;;){var s=e.indexOf("\t",o);if(s<0||t<=s)return l+(t-o);l+=s-o,l+=n-l%n,o=s+1}}s?H=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:w&&(H=function(e){try{e.select()}catch(e){}});var I=function(){this.id=null,this.f=null,this.time=0,this.handler=F(this.onTimeout,this)};function R(e,t){for(var n=0;n<e.length;++n)if(e[n]==t)return n;return-1}I.prototype.onTimeout=function(e){e.id=0,e.time<=+new Date?e.f():setTimeout(e.handler,e.time-+new Date)},I.prototype.set=function(e,t){this.f=t;t=+new Date+e;(!this.id||t<this.time)&&(clearTimeout(this.id),this.id=setTimeout(this.handler,e),this.time=t)};var z=50,B={toString:function(){return"CodeMirror.Pass"}},G={scroll:!1},U={origin:"*mouse"},V={origin:"+move"};function K(e,t,n){for(var r=0,i=0;;){var o=e.indexOf("\t",r);-1==o&&(o=e.length);var l=o-r;if(o==e.length||t<=i+l)return r+Math.min(l,t-i);if(i+=o-r,r=o+1,t<=(i+=n-i%n))return r}}var j=[""];function X(e){for(;j.length<=e;)j.push(Y(j)+" ");return j[e]}function Y(e){return e[e.length-1]}function _(e,t){for(var n=[],r=0;r<e.length;r++)n[r]=t(e[r],r);return n}function $(){}function q(e,t){e=Object.create?Object.create(e):($.prototype=e,new $);return t&&P(t,e),e}var Z=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;function Q(e){return/\w/.test(e)||"\80"<e&&(e.toUpperCase()!=e.toLowerCase()||Z.test(e))}function J(e,t){return t?!!(-1<t.source.indexOf("\\w")&&Q(e))||t.test(e):Q(e)}function ee(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return;return 1}var te=/[\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]/;function ne(e){return 768<=e.charCodeAt(0)&&te.test(e)}function re(e,t,n){for(;(n<0?0<t:t<e.length)&&ne(e.charAt(t));)t+=n;return t}function ie(e,t,n){for(var r=n<t?-1:1;;){if(t==n)return t;var i=(t+n)/2,i=r<0?Math.ceil(i):Math.floor(i);if(i==t)return e(i)?t:n;e(i)?n=i:t=i+r}}var oe=null;function le(e,t,n){var r;oe=null;for(var i=0;i<e.length;++i){var o=e[i];if(o.from<t&&o.to>t)return i;o.to==t&&(o.from!=o.to&&"before"==n?r=i:oe=i),o.from==t&&(o.from!=o.to&&"before"!=n?r=i:oe=i)}return null!=r?r:oe}var se,ae,ue,ce,he,de,fe,pe=(se="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN",ae="nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111",ue=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,ce=/[stwN]/,he=/[LRr]/,de=/[Lb1n]/,fe=/[1n]/,function(e,t){var n="ltr"==t?"L":"R";if(0==e.length||"ltr"==t&&!ue.test(e))return!1;for(var r,i=e.length,o=[],l=0;l<i;++l)o.push((r=e.charCodeAt(l))<=247?se.charAt(r):1424<=r&&r<=1524?"R":1536<=r&&r<=1785?ae.charAt(r-1536):1774<=r&&r<=2220?"r":8192<=r&&r<=8203?"w":8204==r?"b":"L");for(var s=0,a=n;s<i;++s){var u=o[s];"m"==u?o[s]=a:a=u}for(var c=0,h=n;c<i;++c){var d=o[c];"1"==d&&"r"==h?o[c]="n":he.test(d)&&"r"==(h=d)&&(o[c]="R")}for(var f=1,p=o[0];f<i-1;++f){var g=o[f];"+"==g&&"1"==p&&"1"==o[f+1]?o[f]="1":","!=g||p!=o[f+1]||"1"!=p&&"n"!=p||(o[f]=p),p=g}for(var m=0;m<i;++m){var v=o[m];if(","==v)o[m]="N";else if("%"==v){for(var y=void 0,y=m+1;y<i&&"%"==o[y];++y);for(var b=m&&"!"==o[m-1]||y<i&&"1"==o[y]?"1":"N",w=m;w<y;++w)o[w]=b;m=y-1}}for(var x=0,C=n;x<i;++x){var S=o[x];"L"==C&&"1"==S?o[x]="L":he.test(S)&&(C=S)}for(var L=0;L<i;++L)if(ce.test(o[L])){for(var k=void 0,k=L+1;k<i&&ce.test(o[k]);++k);for(var T="L"==(L?o[L-1]:n),M=T==("L"==(k<i?o[k]:n))?T?"L":"R":n,N=L;N<k;++N)o[N]=M;L=k-1}for(var O,A=[],D=0;D<i;)if(de.test(o[D])){var W=D;for(++D;D<i&&de.test(o[D]);++D);A.push(new ge(0,W,D))}else{var H=D,F=A.length,P="rtl"==t?1:0;for(++D;D<i&&"L"!=o[D];++D);for(var E=H;E<D;)if(fe.test(o[E])){H<E&&(A.splice(F,0,new ge(1,H,E)),F+=P);var I=E;for(++E;E<D&&fe.test(o[E]);++E);A.splice(F,0,new ge(2,I,E)),F+=P,H=E}else++E;H<D&&A.splice(F,0,new ge(1,H,D))}return"ltr"==t&&(1==A[0].level&&(O=e.match(/^\s+/))&&(A[0].from=O[0].length,A.unshift(new ge(0,0,O[0].length))),1==Y(A).level&&(O=e.match(/\s+$/))&&(Y(A).to-=O[0].length,A.push(new ge(0,i-O[0].length,i)))),"rtl"==t?A.reverse():A});function ge(e,t,n){this.level=e,this.from=t,this.to=n}function me(e,t){var n=e.order;return null==n&&(n=e.order=pe(e.text,t)),n}var ve=[],ye=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent?e.attachEvent("on"+t,n):(e=e._handlers||(e._handlers={}))[t]=(e[t]||ve).concat(n)};function be(e,t){return e._handlers&&e._handlers[t]||ve}function we(e,t,n){var r;e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent?e.detachEvent("on"+t,n):!(e=(r=e._handlers)&&r[t])||-1<(n=R(e,n))&&(r[t]=e.slice(0,n).concat(e.slice(n+1)))}function xe(e,t){var n=be(e,t);if(n.length)for(var r=Array.prototype.slice.call(arguments,2),i=0;i<n.length;++i)n[i].apply(null,r)}function Ce(e,t,n){return"string"==typeof t&&(t={type:t,preventDefault:function(){this.defaultPrevented=!0}}),xe(e,n||t.type,e,t),Ne(t)||t.codemirrorIgnore}function Se(e){var t=e._handlers&&e._handlers.cursorActivity;if(t)for(var n=e.curOp.cursorActivityHandlers||(e.curOp.cursorActivityHandlers=[]),r=0;r<t.length;++r)-1==R(n,t[r])&&n.push(t[r])}function Le(e,t){return 0<be(e,t).length}function ke(e){e.prototype.on=function(e,t){ye(this,e,t)},e.prototype.off=function(e,t){we(this,e,t)}}function Te(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function Me(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function Ne(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function Oe(e){Te(e),Me(e)}function Ae(e){return e.target||e.srcElement}function De(e){var t=e.which;return null==t&&(1&e.button?t=1:2&e.button?t=3:4&e.button&&(t=2)),g&&e.ctrlKey&&1==t&&(t=3),t}var We,He,Fe=function(){if(w&&v<9)return!1;var e=M("div");return"draggable"in e||"dragDrop"in e}();var Pe=3!="\n\nb".split(/\n/).length?function(e){for(var t=0,n=[],r=e.length;t<=r;){var i=e.indexOf("\n",t);-1==i&&(i=e.length);var o=e.slice(t,"\r"==e.charAt(i-1)?i-1:i),l=o.indexOf("\r");-1!=l?(n.push(o.slice(0,l)),t+=l+1):(n.push(o),t=i+1)}return n}:function(e){return e.split(/\r\n?|\n/)},Ee=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(e){return!1}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(e){}return!(!t||t.parentElement()!=e)&&0!=t.compareEndPoints("StartToEnd",t)},Ie="oncopy"in(r=M("div"))||(r.setAttribute("oncopy","return;"),"function"==typeof r.oncopy),Re=null;var ze={},Be={};function Ge(e){if("string"==typeof e&&Be.hasOwnProperty(e))e=Be[e];else if(e&&"string"==typeof e.name&&Be.hasOwnProperty(e.name)){var t=Be[e.name];"string"==typeof t&&(t={name:t}),(e=q(t,e)).name=t.name}else{if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e))return Ge("application/xml");if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+json$/.test(e))return Ge("application/json")}return"string"==typeof e?{name:e}:e||{name:"null"}}function Ue(e,t){t=Ge(t);var n=ze[t.name];if(!n)return Ue(e,"text/plain");var r=n(e,t);if(Ve.hasOwnProperty(t.name)){var i,o=Ve[t.name];for(i in o)o.hasOwnProperty(i)&&(r.hasOwnProperty(i)&&(r["_"+i]=r[i]),r[i]=o[i])}if(r.name=t.name,t.helperType&&(r.helperType=t.helperType),t.modeProps)for(var l in t.modeProps)r[l]=t.modeProps[l];return r}var Ve={};function Ke(e,t){P(t,Ve.hasOwnProperty(e)?Ve[e]:Ve[e]={})}function je(e,t){if(!0===t)return t;if(e.copyState)return e.copyState(t);var n,r={};for(n in t){var i=t[n];i instanceof Array&&(i=i.concat([])),r[n]=i}return r}function Xe(e,t){for(var n;e.innerMode&&(n=e.innerMode(t))&&n.mode!=e;)t=n.state,e=n.mode;return n||{mode:e,state:t}}function Ye(e,t,n){return!e.startState||e.startState(t,n)}var _e=function(e,t,n){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=n};function $e(e,t){if((t-=e.first)<0||t>=e.size)throw new Error("There is no line "+(t+e.first)+" in the document.");for(var n=e;!n.lines;)for(var r=0;;++r){var i=n.children[r],o=i.chunkSize();if(t<o){n=i;break}t-=o}return n.lines[t]}function qe(e,t,n){var r=[],i=t.line;return e.iter(t.line,n.line+1,function(e){e=e.text;i==n.line&&(e=e.slice(0,n.ch)),i==t.line&&(e=e.slice(t.ch)),r.push(e),++i}),r}function Ze(e,t,n){var r=[];return e.iter(t,n,function(e){r.push(e.text)}),r}function Qe(e,t){var n=t-e.height;if(n)for(var r=e;r;r=r.parent)r.height+=n}function Je(e){if(null==e.parent)return null;for(var t=e.parent,n=R(t.lines,e),r=t.parent;r;r=(t=r).parent)for(var i=0;r.children[i]!=t;++i)n+=r.children[i].chunkSize();return n+t.first}function et(e,t){var n=e.first;e:do{for(var r=0;r<e.children.length;++r){var i=e.children[r],o=i.height;if(t<o){e=i;continue e}t-=o,n+=i.chunkSize()}return n}while(!e.lines);for(var l=0;l<e.lines.length;++l){var s=e.lines[l].height;if(t<s)break;t-=s}return n+l}function tt(e,t){return t>=e.first&&t<e.first+e.size}function nt(e,t){return String(e.lineNumberFormatter(t+e.firstLineNumber))}function rt(e,t,n){if(void 0===n&&(n=null),!(this instanceof rt))return new rt(e,t,n);this.line=e,this.ch=t,this.sticky=n}function it(e,t){return e.line-t.line||e.ch-t.ch}function ot(e,t){return e.sticky==t.sticky&&0==it(e,t)}function lt(e){return rt(e.line,e.ch)}function st(e,t){return it(e,t)<0?t:e}function at(e,t){return it(e,t)<0?e:t}function ut(e,t){return Math.max(e.first,Math.min(t,e.first+e.size-1))}function ct(e,t){if(t.line<e.first)return rt(e.first,0);var n=e.first+e.size-1;return t.line>n?rt(n,$e(e,n).text.length):(e=$e(e,(n=t).line).text.length,null==(t=n.ch)||e<t?rt(n.line,e):t<0?rt(n.line,0):n)}function ht(e,t){for(var n=[],r=0;r<t.length;r++)n[r]=ct(e,t[r]);return n}_e.prototype.eol=function(){return this.pos>=this.string.length},_e.prototype.sol=function(){return this.pos==this.lineStart},_e.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},_e.prototype.next=function(){if(this.pos<this.string.length)return this.string.charAt(this.pos++)},_e.prototype.eat=function(e){var t=this.string.charAt(this.pos),e="string"==typeof e?t==e:t&&(e.test?e.test(t):e(t));if(e)return++this.pos,t},_e.prototype.eatWhile=function(e){for(var t=this.pos;this.eat(e););return this.pos>t},_e.prototype.eatSpace=function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},_e.prototype.skipToEnd=function(){this.pos=this.string.length},_e.prototype.skipTo=function(e){e=this.string.indexOf(e,this.pos);if(-1<e)return this.pos=e,!0},_e.prototype.backUp=function(e){this.pos-=e},_e.prototype.column=function(){return this.lastColumnPos<this.start&&(this.lastColumnValue=E(this.string,this.start,this.tabSize,this.lastColumnPos,this.lastColumnValue),this.lastColumnPos=this.start),this.lastColumnValue-(this.lineStart?E(this.string,this.lineStart,this.tabSize):0)},_e.prototype.indentation=function(){return E(this.string,null,this.tabSize)-(this.lineStart?E(this.string,this.lineStart,this.tabSize):0)},_e.prototype.match=function(e,t,n){if("string"!=typeof e){var r=this.string.slice(this.pos).match(e);return r&&0<r.index?null:(r&&!1!==t&&(this.pos+=r[0].length),r)}r=function(e){return n?e.toLowerCase():e};if(r(this.string.substr(this.pos,e.length))==r(e))return!1!==t&&(this.pos+=e.length),!0},_e.prototype.current=function(){return this.string.slice(this.start,this.pos)},_e.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}},_e.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)},_e.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};var dt=function(e,t){this.state=e,this.lookAhead=t},ft=function(e,t,n,r){this.state=t,this.doc=e,this.line=n,this.maxLookAhead=r||0,this.baseTokens=null,this.baseTokenPos=1};function pt(t,n,r,e){var a=[t.state.modeGen],i={};St(t,n.text,t.doc.mode,r,function(e,t){return a.push(e,t)},i,e);for(var u=r.state,o=0;o<t.state.overlays.length;++o)!function(e){r.baseTokens=a;var o=t.state.overlays[e],l=1,s=0;r.state=!0,St(t,n.text,o.mode,r,function(e,t){for(var n=l;s<e;){var r=a[l];e<r&&a.splice(l,1,e,a[l+1],r),l+=2,s=Math.min(e,r)}if(t)if(o.opaque)a.splice(n,l-n,e,"overlay "+t),l=n+2;else for(;n<l;n+=2){var i=a[n+1];a[n+1]=(i?i+" ":"")+"overlay "+t}},i),r.state=u,r.baseTokens=null,r.baseTokenPos=1}(o);return{styles:a,classes:i.bgClass||i.textClass?i:null}}function gt(e,t,n){var r,i,o;return t.styles&&t.styles[0]==e.state.modeGen||(r=mt(e,Je(t)),i=t.text.length>e.options.maxHighlightLength&&je(e.doc.mode,r.state),o=pt(e,t,r),i&&(r.state=i),t.stateAfter=r.save(!i),t.styles=o.styles,o.classes?t.styleClasses=o.classes:t.styleClasses&&(t.styleClasses=null),n===e.doc.highlightFrontier&&(e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier))),t.styles}function mt(n,r,e){var t=n.doc,i=n.display;if(!t.mode.startState)return new ft(t,!0,r);var o=function(e,t,n){for(var r,i,o=e.doc,l=n?-1:t-(e.doc.mode.innerMode?1e3:100),s=t;l<s;--s){if(s<=o.first)return o.first;var a=$e(o,s-1),u=a.stateAfter;if(u&&(!n||s+(u instanceof dt?u.lookAhead:0)<=o.modeFrontier))return s;a=E(a.text,null,e.options.tabSize);(null==i||a<r)&&(i=s-1,r=a)}return i}(n,r,e),l=o>t.first&&$e(t,o-1).stateAfter,s=l?ft.fromSaved(t,l,o):new ft(t,Ye(t.mode),o);return t.iter(o,r,function(e){vt(n,e.text,s);var t=s.line;e.stateAfter=t==r-1||t%5==0||t>=i.viewFrom&&t<i.viewTo?s.save():null,s.nextLine()}),e&&(t.modeFrontier=s.line),s}function vt(e,t,n,r){var i=e.doc.mode,o=new _e(t,e.options.tabSize,n);for(o.start=o.pos=r||0,""==t&&yt(i,n.state);!o.eol();)bt(i,o,n.state),o.start=o.pos}function yt(e,t){if(e.blankLine)return e.blankLine(t);if(e.innerMode){t=Xe(e,t);return t.mode.blankLine?t.mode.blankLine(t.state):void 0}}function bt(e,t,n,r){for(var i=0;i<10;i++){r&&(r[0]=Xe(e,n).mode);var o=e.token(t,n);if(t.pos>t.start)return o}throw new Error("Mode "+e.name+" failed to advance stream.")}ft.prototype.lookAhead=function(e){var t=this.doc.getLine(this.line+e);return null!=t&&e>this.maxLookAhead&&(this.maxLookAhead=e),t},ft.prototype.baseToken=function(e){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=e;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-e}},ft.prototype.nextLine=function(){this.line++,0<this.maxLookAhead&&this.maxLookAhead--},ft.fromSaved=function(e,t,n){return t instanceof dt?new ft(e,je(e.mode,t.state),n,t.lookAhead):new ft(e,je(e.mode,t),n)},ft.prototype.save=function(e){e=!1!==e?je(this.doc.mode,this.state):this.state;return 0<this.maxLookAhead?new dt(e,this.maxLookAhead):e};var wt=function(e,t,n){this.start=e.start,this.end=e.pos,this.string=e.current(),this.type=t||null,this.state=n};function xt(e,t,n,r){var i,o,l=e.doc,s=l.mode,a=$e(l,(t=ct(l,t)).line),u=mt(e,t.line,n),c=new _e(a.text,e.options.tabSize,u);for(r&&(o=[]);(r||c.pos<t.ch)&&!c.eol();)c.start=c.pos,i=bt(s,c,u.state),r&&o.push(new wt(c,i,je(l.mode,u.state)));return r?o:new wt(c,i,u.state)}function Ct(e,t){if(e)for(;;){var n=e.match(/(?:^|\s+)line-(background-)?(\S+)/);if(!n)break;e=e.slice(0,n.index)+e.slice(n.index+n[0].length);var r=n[1]?"bgClass":"textClass";null==t[r]?t[r]=n[2]:new RegExp("(?:^|\\s)"+n[2]+"(?:$|\\s)").test(t[r])||(t[r]+=" "+n[2])}return e}function St(e,t,n,r,i,o,l){var s=n.flattenSpans;null==s&&(s=e.options.flattenSpans);var a=0,u=null,c=new _e(t,e.options.tabSize,r),h=e.options.addModeClass&&[null];for(""==t&&Ct(yt(n,r.state),o);!c.eol();){var d,f=c.pos>e.options.maxHighlightLength?(s=!1,l&&vt(e,t,r,c.pos),c.pos=t.length,null):Ct(bt(n,c,r.state,h),o);if(!h||(d=h[0].name)&&(f="m-"+(f?d+" "+f:d)),!s||u!=f){for(;a<c.start;)i(a=Math.min(c.start,a+5e3),u);u=f}c.start=c.pos}for(;a<c.pos;){var p=Math.min(c.pos,a+5e3);i(p,u),a=p}}var Lt=!1,kt=!1;function Tt(e,t,n){this.marker=e,this.from=t,this.to=n}function Mt(e,t){if(e)for(var n=0;n<e.length;++n){var r=e[n];if(r.marker==t)return r}}function Nt(e,t){if(t.full)return null;var n=tt(e,t.from.line)&&$e(e,t.from.line).markedSpans,r=tt(e,t.to.line)&&$e(e,t.to.line).markedSpans;if(!n&&!r)return null;var i=t.from.ch,o=t.to.ch,e=0==it(t.from,t.to),l=function(e,t,n){var r;if(e)for(var i=0;i<e.length;++i){var o,l=e[i],s=l.marker;!(null==l.from||(s.inclusiveLeft?l.from<=t:l.from<t))&&(l.from!=t||"bookmark"!=s.type||n&&l.marker.insertLeft)||(o=null==l.to||(s.inclusiveRight?l.to>=t:l.to>t),(r=r||[]).push(new Tt(s,l.from,o?null:l.to)))}return r}(n,i,e),s=function(e,t,n){var r;if(e)for(var i=0;i<e.length;++i){var o,l=e[i],s=l.marker;!(null==l.to||(s.inclusiveRight?l.to>=t:l.to>t))&&(l.from!=t||"bookmark"!=s.type||n&&!l.marker.insertLeft)||(o=null==l.from||(s.inclusiveLeft?l.from<=t:l.from<t),(r=r||[]).push(new Tt(s,o?null:l.from-t,null==l.to?null:l.to-t)))}return r}(r,o,e),a=1==t.text.length,u=Y(t.text).length+(a?i:0);if(l)for(var c=0;c<l.length;++c){var h,d=l[c];null==d.to&&((h=Mt(s,d.marker))?a&&(d.to=null==h.to?null:h.to+u):d.to=i)}if(s)for(var f=0;f<s.length;++f){var p=s[f];null!=p.to&&(p.to+=u),null==p.from?Mt(l,p.marker)||(p.from=u,a&&(l=l||[]).push(p)):(p.from+=u,a&&(l=l||[]).push(p))}l=l&&Ot(l),s&&s!=l&&(s=Ot(s));var g=[l];if(!a){var m,v=t.text.length-2;if(0<v&&l)for(var y=0;y<l.length;++y)null==l[y].to&&(m=m||[]).push(new Tt(l[y].marker,null,null));for(var b=0;b<v;++b)g.push(m);g.push(s)}return g}function Ot(e){for(var t=0;t<e.length;++t){var n=e[t];null!=n.from&&n.from==n.to&&!1!==n.marker.clearWhenEmpty&&e.splice(t--,1)}return e.length?e:null}function At(e){var t=e.markedSpans;if(t){for(var n=0;n<t.length;++n)t[n].marker.detachLine(e);e.markedSpans=null}}function Dt(e,t){if(t){for(var n=0;n<t.length;++n)t[n].marker.attachLine(e);e.markedSpans=t}}function Wt(e){return e.inclusiveLeft?-1:0}function Ht(e){return e.inclusiveRight?1:0}function Ft(e,t){var n=e.lines.length-t.lines.length;if(0!=n)return n;var r=e.find(),i=t.find(),n=it(r.from,i.from)||Wt(e)-Wt(t);if(n)return-n;i=it(r.to,i.to)||Ht(e)-Ht(t);return i||t.id-e.id}function Pt(e,t){var n,r=kt&&e.markedSpans;if(r)for(var i,o=0;o<r.length;++o)(i=r[o]).marker.collapsed&&null==(t?i.from:i.to)&&(!n||Ft(n,i.marker)<0)&&(n=i.marker);return n}function Et(e){return Pt(e,!0)}function It(e){return Pt(e,!1)}function Rt(e,t,n,r,i){var t=$e(e,t),o=kt&&t.markedSpans;if(o)for(var l=0;l<o.length;++l){var s=o[l];if(s.marker.collapsed){var a=s.marker.find(0),u=it(a.from,n)||Wt(s.marker)-Wt(i),c=it(a.to,r)||Ht(s.marker)-Ht(i);if(!(0<=u&&c<=0||u<=0&&0<=c)&&(u<=0&&(s.marker.inclusiveRight&&i.inclusiveLeft?0<=it(a.to,n):0<it(a.to,n))||0<=u&&(s.marker.inclusiveRight&&i.inclusiveLeft?it(a.from,r)<=0:it(a.from,r)<0)))return 1}}}function zt(e){for(var t;t=Et(e);)e=t.find(-1,!0).line;return e}function Bt(e,t){var n=$e(e,t),e=zt(n);return n==e?t:Je(e)}function Gt(e,t){if(t>e.lastLine())return t;var n,r=$e(e,t);if(!Ut(e,r))return t;for(;n=It(r);)r=n.find(1,!0).line;return Je(r)+1}function Ut(e,t){var n=kt&&t.markedSpans;if(n)for(var r,i=0;i<n.length;++i)if((r=n[i]).marker.collapsed){if(null==r.from)return!0;if(!r.marker.widgetNode&&0==r.from&&r.marker.inclusiveLeft&&function e(t,n,r){if(null==r.to){var i=r.marker.find(1,!0);return e(t,i.line,Mt(i.line.markedSpans,r.marker))}if(r.marker.inclusiveRight&&r.to==n.text.length)return!0;for(var o=void 0,l=0;l<n.markedSpans.length;++l)if((o=n.markedSpans[l]).marker.collapsed&&!o.marker.widgetNode&&o.from==r.to&&(null==o.to||o.to!=r.from)&&(o.marker.inclusiveLeft||r.marker.inclusiveRight)&&e(t,n,o))return!0}(e,t,r))return!0}}function Vt(e){for(var t=0,n=(e=zt(e)).parent,r=0;r<n.lines.length;++r){var i=n.lines[r];if(i==e)break;t+=i.height}for(var o=n.parent;o;o=(n=o).parent)for(var l=0;l<o.children.length;++l){var s=o.children[l];if(s==n)break;t+=s.height}return t}function Kt(e){if(0==e.height)return 0;for(var t,n=e.text.length,r=e;t=Et(r);){var i=t.find(0,!0),r=i.from.line;n+=i.from.ch-i.to.ch}for(r=e;t=It(r);){var o=t.find(0,!0);n-=r.text.length-o.from.ch,n+=(r=o.to.line).text.length-o.to.ch}return n}function jt(e){var n=e.display,e=e.doc;n.maxLine=$e(e,e.first),n.maxLineLength=Kt(n.maxLine),n.maxLineChanged=!0,e.iter(function(e){var t=Kt(e);t>n.maxLineLength&&(n.maxLineLength=t,n.maxLine=e)})}var Xt=function(e,t,n){this.text=e,Dt(this,t),this.height=n?n(this):1};Xt.prototype.lineNo=function(){return Je(this)},ke(Xt);var Yt={},_t={};function $t(e,t){if(!e||/^\s*$/.test(e))return null;t=t.addModeClass?_t:Yt;return t[e]||(t[e]=e.replace(/\S+/g,"cm-$&"))}function qt(e,t){var n=N("span",null,null,f?"padding-right: .1px":null),r={pre:N("pre",[n],"CodeMirror-line"),content:n,col:0,pos:0,cm:e,trailingSpace:!1,splitSpaces:e.getOption("lineWrapping")};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var o=i?t.rest[i-1]:t.line,l=void 0;r.pos=0,r.addToken=Qt,function(e){if(null!=He)return He;var t=T(e,document.createTextNode("AخA")),n=S(t,0,1).getBoundingClientRect(),t=S(t,1,2).getBoundingClientRect();return k(e),n&&n.left!=n.right&&(He=t.right-n.right<3)}(e.display.measure)&&(l=me(o,e.doc.direction))&&(r.addToken=function(h,d){return function(e,t,n,r,i,o,l){n=n?n+" cm-force-border":"cm-force-border";for(var s=e.pos,a=s+t.length;;){for(var u=void 0,c=0;c<d.length&&!((u=d[c]).to>s&&u.from<=s);c++);if(u.to>=a)return h(e,t,n,r,i,o,l);h(e,t.slice(0,u.to-s),n,r,null,o,l),r=null,t=t.slice(u.to-s),s=u.to}}}(r.addToken,l)),r.map=[],function(e,t,n){var r=e.markedSpans,i=e.text,o=0;if(!r){for(var l=1;l<n.length;l+=2)t.addToken(t,i.slice(o,o=n[l]),$t(n[l+1],t.cm.options));return}for(var s,a,u,c,h,d,f,p=i.length,g=0,m=1,v="",y=0;;){if(y==g){u=c=h=a="",d=f=null,y=1/0;for(var b=[],w=void 0,x=0;x<r.length;++x){var C=r[x],S=C.marker;if("bookmark"==S.type&&C.from==g&&S.widgetNode)b.push(S);else if(C.from<=g&&(null==C.to||C.to>g||S.collapsed&&C.to==g&&C.from==g)){if(null!=C.to&&C.to!=g&&y>C.to&&(y=C.to,c=""),S.className&&(u+=" "+S.className),S.css&&(a=(a?a+";":"")+S.css),S.startStyle&&C.from==g&&(h+=" "+S.startStyle),S.endStyle&&C.to==y&&(w=w||[]).push(S.endStyle,C.to),S.title&&((f=f||{}).title=S.title),S.attributes)for(var L in S.attributes)(f=f||{})[L]=S.attributes[L];S.collapsed&&(!d||Ft(d.marker,S)<0)&&(d=C)}else C.from>g&&y>C.from&&(y=C.from)}if(w)for(var k=0;k<w.length;k+=2)w[k+1]==y&&(c+=" "+w[k]);if(!d||d.from==g)for(var T=0;T<b.length;++T)Jt(t,0,b[T]);if(d&&(d.from||0)==g){if(Jt(t,(null==d.to?p+1:d.to)-g,d.marker,null==d.from),null==d.to)return;d.to==g&&(d=!1)}}if(p<=g)break;for(var M=Math.min(p,y);;){if(v){var N,O=g+v.length;if(d||(N=M<O?v.slice(0,M-g):v,t.addToken(t,N,s?s+u:u,h,g+N.length==y?c:"",a,f)),M<=O){v=v.slice(M-g),g=M;break}g=O,h=""}v=i.slice(o,o=n[m++]),s=$t(n[m++],t.cm.options)}}}(o,r,gt(e,o,t!=e.display.externalMeasured&&Je(o))),o.styleClasses&&(o.styleClasses.bgClass&&(r.bgClass=W(o.styleClasses.bgClass,r.bgClass||"")),o.styleClasses.textClass&&(r.textClass=W(o.styleClasses.textClass,r.textClass||""))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(function(e){null==We&&(t=M("span","​"),T(e,M("span",[t,document.createTextNode("x")])),0!=e.firstChild.offsetHeight&&(We=t.offsetWidth<=1&&2<t.offsetHeight&&!(w&&v<8)));var t=We?M("span","​"):M("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return t.setAttribute("cm-text",""),t}(e.display.measure))),0==i?(t.measure.map=r.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(r.map),(t.measure.caches||(t.measure.caches=[])).push({}))}return f&&(n=r.content.lastChild,(/\bcm-tab\b/.test(n.className)||n.querySelector&&n.querySelector(".cm-tab"))&&(r.content.className="cm-tab-wrap-hack")),xe(e,"renderLine",e,t.line,r.pre),r.pre.className&&(r.textClass=W(r.pre.className,r.textClass||"")),r}function Zt(e){var t=M("span","•","cm-invalidchar");return t.title="\\u"+e.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function Qt(e,t,n,r,i,o,l){if(t){var s,a=e.splitSpaces?function(e,t){if(1<e.length&&!/  /.test(e))return e;for(var n=t,r="",i=0;i<e.length;i++){var o=e.charAt(i);" "!=o||!n||i!=e.length-1&&32!=e.charCodeAt(i+1)||(o=" "),r+=o,n=" "==o}return r}(t,e.trailingSpace):t,u=e.cm.state.specialChars,c=!1;if(u.test(t)){s=document.createDocumentFragment();for(var h=0;;){u.lastIndex=h;var d=u.exec(t),f=d?d.index-h:t.length-h;if(f&&(p=document.createTextNode(a.slice(h,h+f)),w&&v<9?s.appendChild(M("span",[p])):s.appendChild(p),e.map.push(e.pos,e.pos+f,p),e.col+=f,e.pos+=f),!d)break;h+=1+f;var p=void 0;"\t"==d[0]?(f=(f=e.cm.options.tabSize)-e.col%f,(p=s.appendChild(M("span",X(f),"cm-tab"))).setAttribute("role","presentation"),p.setAttribute("cm-text","\t"),e.col+=f):("\r"==d[0]||"\n"==d[0]?(p=s.appendChild(M("span","\r"==d[0]?"␍":"␤","cm-invalidchar"))).setAttribute("cm-text",d[0]):((p=e.cm.options.specialCharPlaceholder(d[0])).setAttribute("cm-text",d[0]),w&&v<9?s.appendChild(M("span",[p])):s.appendChild(p)),e.col+=1),e.map.push(e.pos,e.pos+1,p),e.pos++}}else e.col+=t.length,s=document.createTextNode(a),e.map.push(e.pos,e.pos+t.length,s),w&&v<9&&(c=!0),e.pos+=t.length;if(e.trailingSpace=32==a.charCodeAt(t.length-1),n||r||i||c||o||l){n=n||"";r&&(n+=r),i&&(n+=i);var g=M("span",[s],n,o);if(l)for(var m in l)l.hasOwnProperty(m)&&"style"!=m&&"class"!=m&&g.setAttribute(m,l[m]);return e.content.appendChild(g)}e.content.appendChild(s)}}function Jt(e,t,n,r){var i=!r&&n.widgetNode;i&&e.map.push(e.pos,e.pos+t,i),!r&&e.cm.display.input.needsContentAttribute&&(i=i||e.content.appendChild(document.createElement("span"))).setAttribute("cm-marker",n.id),i&&(e.cm.display.input.setUneditable(i),e.content.appendChild(i)),e.pos+=t,e.trailingSpace=!1}function en(e,t,n){this.line=t,this.rest=function(e){for(var t,n;t=It(e);)e=t.find(1,!0).line,(n=n||[]).push(e);return n}(t),this.size=this.rest?Je(Y(this.rest))-n+1:1,this.node=this.text=null,this.hidden=Ut(e,t)}function tn(e,t,n){for(var r=[],i=t;i<n;i=l){var o=new en(e.doc,$e(e.doc,i),i),l=i+o.size;r.push(o)}return r}var nn=null;function rn(e,t){var n=e.ownsGroup;if(n)try{!function(e){var t=e.delayedCallbacks,n=0;do{for(;n<t.length;n++)t[n].call(null);for(var r=0;r<e.ops.length;r++){var i=e.ops[r];if(i.cursorActivityHandlers)for(;i.cursorActivityCalled<i.cursorActivityHandlers.length;)i.cursorActivityHandlers[i.cursorActivityCalled++].call(null,i.cm)}}while(n<t.length)}(n)}finally{nn=null,t(n)}}var on=null;function ln(e,t){var n=be(e,t);if(n.length){var r,i=Array.prototype.slice.call(arguments,2);nn?r=nn.delayedCallbacks:on?r=on:(r=on=[],setTimeout(sn,0));for(var o=0;o<n.length;++o)!function(e){r.push(function(){return n[e].apply(null,i)})}(o)}}function sn(){var e=on;on=null;for(var t=0;t<e.length;++t)e[t]()}function an(e,t,n,r){for(var i=0;i<t.changes.length;i++){var o=t.changes[i];"text"==o?function(e,t){var n=t.text.className,r=cn(e,t);t.text==t.node&&(t.node=r.pre);t.text.parentNode.replaceChild(r.pre,t.text),t.text=r.pre,r.bgClass!=t.bgClass||r.textClass!=t.textClass?(t.bgClass=r.bgClass,t.textClass=r.textClass,hn(e,t)):n&&(t.text.className=n)}(e,t):"gutter"==o?dn(e,t,n,r):"class"==o?hn(e,t):"widget"==o&&function(e,t,n){t.alignable&&(t.alignable=null);for(var r=C("CodeMirror-linewidget"),i=t.node.firstChild,o=void 0;i;i=o)o=i.nextSibling,r.test(i.className)&&t.node.removeChild(i);fn(e,t,n)}(e,t,r)}t.changes=null}function un(e){return e.node==e.text&&(e.node=M("div",null,null,"position: relative"),e.text.parentNode&&e.text.parentNode.replaceChild(e.node,e.text),e.node.appendChild(e.text),w&&v<8&&(e.node.style.zIndex=2)),e.node}function cn(e,t){var n=e.display.externalMeasured;return n&&n.line==t.line?(e.display.externalMeasured=null,t.measure=n.measure,n.built):qt(e,t)}function hn(e,t){var n,r;n=e,(r=(i=t).bgClass?i.bgClass+" "+(i.line.bgClass||""):i.line.bgClass)&&(r+=" CodeMirror-linebackground"),i.background?r?i.background.className=r:(i.background.parentNode.removeChild(i.background),i.background=null):r&&(e=un(i),i.background=e.insertBefore(M("div",null,r),e.firstChild),n.display.input.setUneditable(i.background)),t.line.wrapClass?un(t).className=t.line.wrapClass:t.node!=t.text&&(t.node.className="");var i=t.textClass?t.textClass+" "+(t.line.textClass||""):t.line.textClass;t.text.className=i||""}function dn(e,t,n,r){t.gutter&&(t.node.removeChild(t.gutter),t.gutter=null),t.gutterBackground&&(t.node.removeChild(t.gutterBackground),t.gutterBackground=null),t.line.gutterClass&&(o=un(t),t.gutterBackground=M("div",null,"CodeMirror-gutter-background "+t.line.gutterClass,"left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px; width: "+r.gutterTotalWidth+"px"),e.display.input.setUneditable(t.gutterBackground),o.insertBefore(t.gutterBackground,t.text));var i=t.line.gutterMarkers;if(e.options.lineNumbers||i){var o=un(t),l=t.gutter=M("div",null,"CodeMirror-gutter-wrapper","left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px");if(e.display.input.setUneditable(l),o.insertBefore(l,t.text),t.line.gutterClass&&(l.className+=" "+t.line.gutterClass),!e.options.lineNumbers||i&&i["CodeMirror-linenumbers"]||(t.lineNumber=l.appendChild(M("div",nt(e.options,n),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+r.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+e.display.lineNumInnerWidth+"px"))),i)for(var s=0;s<e.display.gutterSpecs.length;++s){var a=e.display.gutterSpecs[s].className,u=i.hasOwnProperty(a)&&i[a];u&&l.appendChild(M("div",[u],"CodeMirror-gutter-elt","left: "+r.gutterLeft[a]+"px; width: "+r.gutterWidth[a]+"px"))}}}function fn(e,t,n){if(pn(e,t.line,t,n,!0),t.rest)for(var r=0;r<t.rest.length;r++)pn(e,t.rest[r],t,n,!1)}function pn(e,t,n,r,i){if(t.widgets)for(var o=un(n),l=0,s=t.widgets;l<s.length;++l){var a=s[l],u=M("div",[a.node],"CodeMirror-linewidget"+(a.className?" "+a.className:""));a.handleMouseEvents||u.setAttribute("cm-ignore-events","true"),function(e,t,n,r){e.noHScroll&&((n.alignable||(n.alignable=[])).push(t),n=r.wrapperWidth,t.style.left=r.fixedPos+"px",e.coverGutter||(n-=r.gutterTotalWidth,t.style.paddingLeft=r.gutterTotalWidth+"px"),t.style.width=n+"px");e.coverGutter&&(t.style.zIndex=5,t.style.position="relative",e.noHScroll||(t.style.marginLeft=-r.gutterTotalWidth+"px"))}(a,u,n,r),e.display.input.setUneditable(u),i&&a.above?o.insertBefore(u,n.gutter||n.text):o.appendChild(u),ln(a,"redraw")}}function gn(e){if(null!=e.height)return e.height;var t,n=e.doc.cm;return n?(O(document.body,e.node)||(t="position: relative;",e.coverGutter&&(t+="margin-left: -"+n.display.gutters.offsetWidth+"px;"),e.noHScroll&&(t+="width: "+n.display.wrapper.clientWidth+"px;"),T(n.display.measure,M("div",[e.node],null,t))),e.height=e.node.parentNode.offsetHeight):0}function mn(e,t){for(var n=Ae(t);n!=e.wrapper;n=n.parentNode)if(!n||1==n.nodeType&&"true"==n.getAttribute("cm-ignore-events")||n.parentNode==e.sizer&&n!=e.mover)return 1}function vn(e){return e.lineSpace.offsetTop}function yn(e){return e.mover.offsetHeight-e.lineSpace.offsetHeight}function bn(e){if(e.cachedPaddingH)return e.cachedPaddingH;var t=T(e.measure,M("pre","x","CodeMirror-line-like")),t=window.getComputedStyle?window.getComputedStyle(t):t.currentStyle,t={left:parseInt(t.paddingLeft),right:parseInt(t.paddingRight)};return isNaN(t.left)||isNaN(t.right)||(e.cachedPaddingH=t),t}function wn(e){return z-e.display.nativeBarWidth}function xn(e){return e.display.scroller.clientWidth-wn(e)-e.display.barWidth}function Cn(e){return e.display.scroller.clientHeight-wn(e)-e.display.barHeight}function Sn(e,t,n){if(e.line==t)return{map:e.measure.map,cache:e.measure.cache};for(var r=0;r<e.rest.length;r++)if(e.rest[r]==t)return{map:e.measure.maps[r],cache:e.measure.caches[r]};for(var i=0;i<e.rest.length;i++)if(Je(e.rest[i])>n)return{map:e.measure.maps[i],cache:e.measure.caches[i],before:!0}}function Ln(e,t,n,r){return Mn(e,Tn(e,t),n,r)}function kn(e,t){if(t>=e.display.viewFrom&&t<e.display.viewTo)return e.display.view[er(e,t)];e=e.display.externalMeasured;return e&&t>=e.lineN&&t<e.lineN+e.size?e:void 0}function Tn(e,t){var n,r,i=Je(t),o=kn(e,i);o&&!o.text?o=null:o&&o.changes&&(an(e,o,i,$n(e)),e.curOp.forceUpdate=!0),o||(n=e,e=Je(r=zt(r=t)),(r=n.display.externalMeasured=new en(n.doc,r,e)).lineN=e,e=r.built=qt(n,r),r.text=e.pre,T(n.display.lineMeasure,e.pre),o=r);i=Sn(o,t,i);return{line:t,view:o,rect:null,map:i.map,cache:i.cache,before:i.before,hasHeights:!1}}function Mn(e,t,n,r,i){t.before&&(n=-1);var o,l=n+(r||"");return t.cache.hasOwnProperty(l)?o=t.cache[l]:(t.rect||(t.rect=t.view.text.getBoundingClientRect()),t.hasHeights||(function(e,t,n){var r=e.options.lineWrapping,e=r&&xn(e);if(!t.measure.heights||r&&t.measure.width!=e){var i=t.measure.heights=[];if(r){t.measure.width=e;for(var o=t.text.firstChild.getClientRects(),l=0;l<o.length-1;l++){var s=o[l],a=o[l+1];2<Math.abs(s.bottom-a.bottom)&&i.push((s.bottom+a.top)/2-n.top)}}i.push(n.bottom-n.top)}}(e,t.view,t.rect),t.hasHeights=!0),(o=function(e,t,n,r){var i,o=An(t.map,n,r),l=o.node,s=o.start,a=o.end,u=o.collapse;if(3==l.nodeType){for(var c=0;c<4;c++){for(;s&&ne(t.line.text.charAt(o.coverStart+s));)--s;for(;o.coverStart+a<o.coverEnd&&ne(t.line.text.charAt(o.coverStart+a));)++a;if((i=w&&v<9&&0==s&&a==o.coverEnd-o.coverStart?l.parentNode.getBoundingClientRect():function(e,t){var n=On;if("left"==t)for(var r=0;r<e.length&&(n=e[r]).left==n.right;r++);else for(var i=e.length-1;0<=i&&(n=e[i]).left==n.right;i--);return n}(S(l,s,a).getClientRects(),r)).left||i.right||0==s)break;a=s,--s,u="right"}w&&v<11&&(i=function(e,t){if(!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI||!function(e){if(null!=Re)return Re;var e=(t=T(e,M("span","x"))).getBoundingClientRect(),t=S(t,0,1).getBoundingClientRect();return Re=1<Math.abs(e.left-t.left)}(e))return t;var n=screen.logicalXDPI/screen.deviceXDPI,e=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*n,right:t.right*n,top:t.top*e,bottom:t.bottom*e}}(e.display.measure,i))}else 0<s&&(u=r="right"),i=e.options.lineWrapping&&1<(g=l.getClientRects()).length?g["right"==r?g.length-1:0]:l.getBoundingClientRect();!(w&&v<9)||s||i&&(i.left||i.right)||(m=l.parentNode.getClientRects()[0],i=m?{left:m.left,right:m.left+_n(e.display),top:m.top,bottom:m.bottom}:On);for(var h=i.top-t.rect.top,n=i.bottom-t.rect.top,d=(h+n)/2,f=t.view.measure.heights,p=0;p<f.length-1&&!(d<f[p]);p++);var g=p?f[p-1]:0,m=f[p],m={left:("right"==u?i.right:i.left)-t.rect.left,right:("left"==u?i.left:i.right)-t.rect.left,top:g,bottom:m};i.left||i.right||(m.bogus=!0);e.options.singleCursorHeightPerLine||(m.rtop=h,m.rbottom=n);return m}(e,t,n,r)).bogus||(t.cache[l]=o)),{left:o.left,right:o.right,top:i?o.rtop:o.top,bottom:i?o.rbottom:o.bottom}}var Nn,On={left:0,right:0,top:0,bottom:0};function An(e,t,n){for(var r,i,o,l,s,a,u=0;u<e.length;u+=3)if(s=e[u],a=e[u+1],t<s?(i=0,o=1,l="left"):t<a?o=(i=t-s)+1:(u==e.length-3||t==a&&e[u+3]>t)&&(i=(o=a-s)-1,a<=t&&(l="right")),null!=i){if(r=e[u+2],s==a&&n==(r.insertLeft?"left":"right")&&(l=n),"left"==n&&0==i)for(;u&&e[u-2]==e[u-3]&&e[u-1].insertLeft;)r=e[2+(u-=3)],l="left";if("right"==n&&i==a-s)for(;u<e.length-3&&e[u+3]==e[u+4]&&!e[u+5].insertLeft;)r=e[(u+=3)+2],l="right";break}return{node:r,start:i,end:o,collapse:l,coverStart:s,coverEnd:a}}function Dn(e){if(e.measure&&(e.measure.cache={},e.measure.heights=null,e.rest))for(var t=0;t<e.rest.length;t++)e.measure.caches[t]={}}function Wn(e){e.display.externalMeasure=null,k(e.display.lineMeasure);for(var t=0;t<e.display.view.length;t++)Dn(e.display.view[t])}function Hn(e){Wn(e),e.display.cachedCharWidth=e.display.cachedTextHeight=e.display.cachedPaddingH=null,e.options.lineWrapping||(e.display.maxLineChanged=!0),e.display.lineNumChars=null}function Fn(){return o&&a?-(document.body.getBoundingClientRect().left-parseInt(getComputedStyle(document.body).marginLeft)):window.pageXOffset||(document.documentElement||document.body).scrollLeft}function Pn(){return o&&a?-(document.body.getBoundingClientRect().top-parseInt(getComputedStyle(document.body).marginTop)):window.pageYOffset||(document.documentElement||document.body).scrollTop}function En(e){var t=0;if(e.widgets)for(var n=0;n<e.widgets.length;++n)e.widgets[n].above&&(t+=gn(e.widgets[n]));return t}function In(e,t,n,r,i){if(i||(i=En(t),n.top+=i,n.bottom+=i),"line"==r)return n;r=r||"local";t=Vt(t);return"local"==r?t+=vn(e.display):t-=e.display.viewOffset,"page"!=r&&"window"!=r||(t+=(e=e.display.lineSpace.getBoundingClientRect()).top+("window"==r?0:Pn()),r=e.left+("window"==r?0:Fn()),n.left+=r,n.right+=r),n.top+=t,n.bottom+=t,n}function Rn(e,t,n){if("div"==n)return t;var r=t.left,t=t.top;"page"==n?(r-=Fn(),t-=Pn()):"local"!=n&&n||(r+=(n=e.display.sizer.getBoundingClientRect()).left,t+=n.top);e=e.display.lineSpace.getBoundingClientRect();return{left:r-e.left,top:t-e.top}}function zn(e,t,n,r,i){return In(e,r=r||$e(e.doc,t.line),Ln(e,r,t.ch,i),n)}function Bn(n,e,r,i,o,l){function s(e,t){e=Mn(n,o,e,t?"right":"left",l);return t?e.left=e.right:e.right=e.left,In(n,i,e,r)}i=i||$e(n.doc,e.line),o=o||Tn(n,i);var a=me(i,n.doc.direction),t=e.ch,u=e.sticky;if(t>=i.text.length?(t=i.text.length,u="before"):t<=0&&(t=0,u="after"),!a)return s("before"==u?t-1:t,"before"==u);function c(e,t,n){return s(n?e-1:e,1==a[t].level!=n)}var h=le(a,t,u),e=oe,h=c(t,h,"before"==u);return null!=e&&(h.other=c(t,e,"before"!=u)),h}function Gn(e,t){var n=0;t=ct(e.doc,t),e.options.lineWrapping||(n=_n(e.display)*t.ch);t=$e(e.doc,t.line),e=Vt(t)+vn(e.display);return{left:n,right:n,top:e,bottom:e+t.height}}function Un(e,t,n,r,i){n=rt(e,t,n);return n.xRel=i,r&&(n.outside=r),n}function Vn(e,t,n){var r=e.doc;if((n+=e.display.viewOffset)<0)return Un(r.first,0,null,-1,-1);var i=et(r,n),o=r.first+r.size-1;if(o<i)return Un(r.first+r.size-1,$e(r,o).text.length,null,1,1);t<0&&(t=0);for(var l=$e(r,i);;){var s=function(n,e,t,r,i){i-=Vt(e);var o=Tn(n,e),l=En(e),s=0,a=e.text.length,u=!0,c=me(e,n.doc.direction);c&&(f=(n.options.lineWrapping?function(e,t,n,r,i,o,l){var l=Kn(e,t,r,l),s=l.begin,a=l.end;/\s/.test(t.text.charAt(a-1))&&a--;for(var u=null,c=null,h=0;h<i.length;h++){var d,f=i[h];f.from>=a||f.to<=s||(d=1!=f.level,d=Mn(e,r,d?Math.min(a,f.to)-1:Math.max(s,f.from)).right,d=d<o?o-d+1e9:d-o,(!u||d<c)&&(u=f,c=d))}u=u||i[i.length-1];u.from<s&&(u={from:s,to:u.to,level:u.level});u.to>a&&(u={from:u.from,to:a,level:u.level});return u}:function(n,r,i,o,l,s,a){var e=ie(function(e){var t=l[e],e=1!=t.level;return Xn(Bn(n,rt(i,e?t.to:t.from,e?"before":"after"),"line",r,o),s,a,!0)},0,l.length-1),t=l[e];{var u;0<e&&(u=1!=t.level,Xn(u=Bn(n,rt(i,u?t.from:t.to,u?"after":"before"),"line",r,o),s,a,!0)&&u.top>a&&(t=l[e-1]))}return t})(n,e,t,o,c,r,i),u=1!=f.level,s=u?f.from:f.to-1,a=u?f.to:f.from-1);var h=null,d=null,c=ie(function(e){var t=Mn(n,o,e);return t.top+=l,t.bottom+=l,Xn(t,r,i,!1)&&(t.top<=i&&t.left<=r&&(h=e,d=t),1)},s,a),f=!1;{var p,g;d?(p=r-d.left<d.right-r,c=h+((g=p==u)?0:1),g=g?"after":"before",p=p?d.left:d.right):(u||c!=a&&c!=s||c++,g=0==c||c!=e.text.length&&Mn(n,o,c-(u?1:0)).bottom+l<=i==u?"after":"before",u=Bn(n,rt(t,c,g),"line",e,o),p=u.left,f=i<u.top?-1:i>=u.bottom?1:0)}return c=re(e.text,c,1),Un(t,c,g,f,r-p)}(e,l,i,t,n),a=function(e,t){var n,r=kt&&e.markedSpans;if(r)for(var i=0;i<r.length;++i){var o=r[i];o.marker.collapsed&&(null==o.from||o.from<t)&&(null==o.to||o.to>t)&&(!n||Ft(n,o.marker)<0)&&(n=o.marker)}return n}(l,s.ch+(0<s.xRel||0<s.outside?1:0));if(!a)return s;a=a.find(1);if(a.line==i)return a;l=$e(r,i=a.line)}}function Kn(t,e,n,r){r-=En(e);var i=e.text.length,e=ie(function(e){return Mn(t,n,e-1).bottom<=r},i,0);return{begin:e,end:i=ie(function(e){return Mn(t,n,e).top>r},e,i)}}function jn(e,t,n,r){return Kn(e,t,n=n||Tn(e,t),In(e,t,Mn(e,n,r),"line").top)}function Xn(e,t,n,r){return!(e.bottom<=n)&&(e.top>n||(r?e.left:e.right)>t)}function Yn(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Nn){Nn=M("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t)Nn.appendChild(document.createTextNode("x")),Nn.appendChild(M("br"));Nn.appendChild(document.createTextNode("x"))}T(e.measure,Nn);var n=Nn.offsetHeight/50;return 3<n&&(e.cachedTextHeight=n),k(e.measure),n||1}function _n(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=M("span","xxxxxxxxxx"),n=M("pre",[t],"CodeMirror-line-like");T(e.measure,n);t=t.getBoundingClientRect(),t=(t.right-t.left)/10;return 2<t&&(e.cachedCharWidth=t),t||10}function $n(e){for(var t=e.display,n={},r={},i=t.gutters.clientLeft,o=t.gutters.firstChild,l=0;o;o=o.nextSibling,++l){var s=e.display.gutterSpecs[l].className;n[s]=o.offsetLeft+o.clientLeft+i,r[s]=o.clientWidth}return{fixedPos:qn(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:n,gutterWidth:r,wrapperWidth:t.wrapper.clientWidth}}function qn(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function Zn(r){var i=Yn(r.display),o=r.options.lineWrapping,l=o&&Math.max(5,r.display.scroller.clientWidth/_n(r.display)-3);return function(e){if(Ut(r.doc,e))return 0;var t=0;if(e.widgets)for(var n=0;n<e.widgets.length;n++)e.widgets[n].height&&(t+=e.widgets[n].height);return o?t+(Math.ceil(e.text.length/l)||1)*i:t+i}}function Qn(e){var t=e.doc,n=Zn(e);t.iter(function(e){var t=n(e);t!=e.height&&Qe(e,t)})}function Jn(e,t,n,r){var i=e.display;if(!n&&"true"==Ae(t).getAttribute("cm-not-content"))return null;var o,l,s=i.lineSpace.getBoundingClientRect();try{o=t.clientX-s.left,l=t.clientY-s.top}catch(e){return null}var a,i=Vn(e,o,l);return r&&0<i.xRel&&(a=$e(e.doc,i.line).text).length==i.ch&&(a=E(a,a.length,e.options.tabSize)-a.length,i=rt(i.line,Math.max(0,Math.round((o-bn(e.display).left)/_n(e.display))-a))),i}function er(e,t){if(t>=e.display.viewTo)return null;if((t-=e.display.viewFrom)<0)return null;for(var n=e.display.view,r=0;r<n.length;r++)if((t-=n[r].size)<0)return r}function tr(e,t,n,r){null==t&&(t=e.doc.first),null==n&&(n=e.doc.first+e.doc.size),r=r||0;var i,o,l=e.display;r&&n<l.viewTo&&(null==l.updateLineNumbers||l.updateLineNumbers>t)&&(l.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=l.viewTo?kt&&Bt(e.doc,t)<l.viewTo&&rr(e):n<=l.viewFrom?kt&&Gt(e.doc,n+r)>l.viewFrom?rr(e):(l.viewFrom+=r,l.viewTo+=r):t<=l.viewFrom&&n>=l.viewTo?rr(e):t<=l.viewFrom?(i=ir(e,n,n+r,1))?(l.view=l.view.slice(i.index),l.viewFrom=i.lineN,l.viewTo+=r):rr(e):n>=l.viewTo?(o=ir(e,t,t,-1))?(l.view=l.view.slice(0,o.index),l.viewTo=o.lineN):rr(e):(i=ir(e,t,t,-1),o=ir(e,n,n+r,1),i&&o?(l.view=l.view.slice(0,i.index).concat(tn(e,i.lineN,o.lineN)).concat(l.view.slice(o.index)),l.viewTo+=r):rr(e));e=l.externalMeasured;e&&(n<e.lineN?e.lineN+=r:t<e.lineN+e.size&&(l.externalMeasured=null))}function nr(e,t,n){e.curOp.viewChanged=!0;var r=e.display,i=e.display.externalMeasured;i&&t>=i.lineN&&t<i.lineN+i.size&&(r.externalMeasured=null),t<r.viewFrom||t>=r.viewTo||(null==(t=r.view[er(e,t)]).node||-1==R(t=t.changes||(t.changes=[]),n)&&t.push(n))}function rr(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function ir(e,t,n,r){var i,o=er(e,t),l=e.display.view;if(!kt||n==e.doc.first+e.doc.size)return{index:o,lineN:n};for(var s=e.display.viewFrom,a=0;a<o;a++)s+=l[a].size;if(s!=t){if(0<r){if(o==l.length-1)return null;i=s+l[o].size-t,o++}else i=s-t;t+=i,n+=i}for(;Bt(e.doc,n)!=n;){if(o==(r<0?0:l.length-1))return null;n+=r*l[o-(r<0?1:0)].size,o+=r}return{index:o,lineN:n}}function or(e){for(var t=e.display.view,n=0,r=0;r<t.length;r++){var i=t[r];i.hidden||i.node&&!i.changes||++n}return n}function lr(e){e.display.input.showSelection(e.display.input.prepareSelection())}function sr(e,t){void 0===t&&(t=!0);for(var n,r,i=e.doc,o={},l=o.cursors=document.createDocumentFragment(),s=o.selection=document.createDocumentFragment(),a=0;a<i.sel.ranges.length;a++)!t&&a==i.sel.primIndex||((n=i.sel.ranges[a]).from().line>=e.display.viewTo||n.to().line<e.display.viewFrom||(((r=n.empty())||e.options.showCursorWhenSelecting)&&ar(e,n.head,l),r||function(i,e,t){var n=i.display,o=i.doc,l=document.createDocumentFragment(),r=bn(i.display),S=r.left,L=Math.max(n.sizerWidth,xn(i)-n.sizer.offsetLeft)-r.right,k="ltr"==o.direction;function T(e,t,n,r){t<0&&(t=0),t=Math.round(t),r=Math.round(r),l.appendChild(M("div",null,"CodeMirror-selected","position: absolute; left: "+e+"px;\n                             top: "+t+"px; width: "+(null==n?L-e:n)+"px;\n                             height: "+(r-t)+"px"))}function s(n,g,m){var v,y,r=$e(o,n),b=r.text.length;function w(e,t){return zn(i,rt(n,e),"div",r,t)}function x(e,t,n){e=jn(i,r,null,e),t="ltr"==t==("after"==n)?"left":"right";return w("after"==n?e.begin:e.end-(/\s/.test(r.text.charAt(e.end-1))?2:1),t)[t]}var C=me(r,o.direction);return function(e,t,n,r){if(!e)return r(t,n,"ltr",0);for(var i=!1,o=0;o<e.length;++o){var l=e[o];(l.from<n&&l.to>t||t==n&&l.to==t)&&(r(Math.max(l.from,t),Math.min(l.to,n),1==l.level?"rtl":"ltr",o),i=!0)}i||r(t,n,"ltr")}(C,g||0,null==m?b:m,function(e,t,n,r){var i,o,l,s,a="ltr"==n,u=w(e,a?"left":"right"),c=w(t-1,a?"right":"left"),h=null==g&&0==e,d=null==m&&t==b,f=0==r,p=!C||r==C.length-1;c.top-u.top<=3?(i=(k?d:h)&&p,r=(k?h:d)&&f?S:(a?u:c).left,i=i?L:(a?c:u).right,T(r,u.top,i-r,u.bottom)):(n=a?(o=k&&h&&f?S:u.left,l=k?L:x(e,n,"before"),s=k?S:x(t,n,"after"),k&&d&&p?L:c.right):(o=k?x(e,n,"before"):S,l=!k&&h&&f?L:u.right,s=!k&&d&&p?S:c.left,k?x(t,n,"after"):L),T(o,u.top,l-o,u.bottom),u.bottom<c.top&&T(S,u.bottom,null,c.top),T(s,c.top,n-s,c.bottom)),(!v||ur(u,v)<0)&&(v=u),ur(c,v)<0&&(v=c),(!y||ur(u,y)<0)&&(y=u),ur(c,y)<0&&(y=c)}),{start:v,end:y}}var a=e.from(),n=e.to();a.line==n.line?s(a.line,a.ch,n.ch):(r=$e(o,a.line),e=$e(o,n.line),e=zt(r)==zt(e),r=s(a.line,a.ch,e?r.text.length+1:null).end,n=s(n.line,e?0:null,n.ch).start,e&&(r.top<n.top-2?(T(r.right,r.top,null,r.bottom),T(S,n.top,n.left,n.bottom)):T(r.right,r.top,n.left-r.right,r.bottom)),r.bottom<n.top&&T(S,r.bottom,null,n.top));t.appendChild(l)}(e,n,s)));return o}function ar(e,t,n){var r=Bn(e,t,"div",null,null,!e.options.singleCursorHeightPerLine),t=n.appendChild(M("div"," ","CodeMirror-cursor"));t.style.left=r.left+"px",t.style.top=r.top+"px",t.style.height=Math.max(0,r.bottom-r.top)*e.options.cursorHeight+"px",r.other&&((n=n.appendChild(M("div"," ","CodeMirror-cursor CodeMirror-secondarycursor"))).style.display="",n.style.left=r.other.left+"px",n.style.top=r.other.top+"px",n.style.height=.85*(r.other.bottom-r.other.top)+"px")}function ur(e,t){return e.top-t.top||e.left-t.left}function cr(e){var t,n;e.state.focused&&(t=e.display,clearInterval(t.blinker),n=!0,t.cursorDiv.style.visibility="",0<e.options.cursorBlinkRate?t.blinker=setInterval(function(){e.hasFocus()||pr(e),t.cursorDiv.style.visibility=(n=!n)?"":"hidden"},e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden"))}function hr(e){e.hasFocus()||(e.display.input.focus(),e.state.focused||fr(e))}function dr(e){e.state.delayingBlurEvent=!0,setTimeout(function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,e.state.focused&&pr(e))},100)}function fr(e,t){e.state.delayingBlurEvent&&!e.state.draggingText&&(e.state.delayingBlurEvent=!1),"nocursor"!=e.options.readOnly&&(e.state.focused||(xe(e,"focus",e,t),e.state.focused=!0,D(e.display.wrapper,"CodeMirror-focused"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),f&&setTimeout(function(){return e.display.input.reset(!0)},20)),e.display.input.receivedFocus()),cr(e))}function pr(e,t){e.state.delayingBlurEvent||(e.state.focused&&(xe(e,"blur",e,t),e.state.focused=!1,L(e.display.wrapper,"CodeMirror-focused")),clearInterval(e.display.blinker),setTimeout(function(){e.state.focused||(e.display.shift=!1)},150))}function gr(e){for(var t=e.display,n=t.lineDiv.offsetTop,r=0;r<t.view.length;r++){var i,o=t.view[r],l=e.options.lineWrapping,s=void 0,a=0;if(!o.hidden){w&&v<8?(s=(i=o.node.offsetTop+o.node.offsetHeight)-n,n=i):(s=(u=o.node.getBoundingClientRect()).bottom-u.top,!l&&o.text.firstChild&&(a=o.text.firstChild.getBoundingClientRect().right-u.left-1));var u=o.line.height-s;if((.005<u||u<-.005)&&(Qe(o.line,s),mr(o.line),o.rest))for(var c=0;c<o.rest.length;c++)mr(o.rest[c]);a>e.display.sizerWidth&&((a=Math.ceil(a/_n(e.display)))>e.display.maxLineLength&&(e.display.maxLineLength=a,e.display.maxLine=o.line,e.display.maxLineChanged=!0))}}}function mr(e){if(e.widgets)for(var t=0;t<e.widgets.length;++t){var n=e.widgets[t],r=n.node.parentNode;r&&(n.height=r.offsetHeight)}}function vr(e,t,n){var r=n&&null!=n.top?Math.max(0,n.top):e.scroller.scrollTop,r=Math.floor(r-vn(e)),i=n&&null!=n.bottom?n.bottom:r+e.wrapper.clientHeight,o=et(t,r),r=et(t,i);return n&&n.ensure&&(i=n.ensure.from.line,n=n.ensure.to.line,i<o?r=et(t,Vt($e(t,o=i))+e.wrapper.clientHeight):Math.min(n,t.lastLine())>=r&&(o=et(t,Vt($e(t,n))-e.wrapper.clientHeight),r=n)),{from:o,to:Math.max(r,o+1)}}function yr(e,t){var n=e.display,r=Yn(e.display);t.top<0&&(t.top=0);var i=(e.curOp&&null!=e.curOp.scrollTop?e.curOp:n.scroller).scrollTop,o=Cn(e),l={};t.bottom-t.top>o&&(t.bottom=t.top+o);var s=e.doc.height+yn(n),a=t.top<r,r=t.bottom>s-r;t.top<i?l.scrollTop=a?0:t.top:t.bottom>i+o&&((u=Math.min(t.top,(r?s:t.bottom)-o))!=i&&(l.scrollTop=u));var i=e.options.fixedGutter?0:n.gutters.offsetWidth,u=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:n.scroller.scrollLeft-i,e=xn(e)-n.gutters.offsetWidth,n=t.right-t.left>e;return n&&(t.right=t.left+e),t.left<10?l.scrollLeft=0:t.left<u?l.scrollLeft=Math.max(0,t.left+i-(n?0:10)):t.right>e+u-3&&(l.scrollLeft=t.right+(n?0:10)-e),l}function br(e,t){null!=t&&(Cr(e),e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc:e.curOp).scrollTop+t)}function wr(e){Cr(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function xr(e,t,n){null==t&&null==n||Cr(e),null!=t&&(e.curOp.scrollLeft=t),null!=n&&(e.curOp.scrollTop=n)}function Cr(e){var t=e.curOp.scrollToPos;t&&(e.curOp.scrollToPos=null,Sr(e,Gn(e,t.from),Gn(e,t.to),t.margin))}function Sr(e,t,n,r){r=yr(e,{left:Math.min(t.left,n.left),top:Math.min(t.top,n.top)-r,right:Math.max(t.right,n.right),bottom:Math.max(t.bottom,n.bottom)+r});xr(e,r.scrollLeft,r.scrollTop)}function Lr(e,t){Math.abs(e.doc.scrollTop-t)<2||(d||Kr(e,{top:t}),kr(e,t,!0),d&&Kr(e),zr(e,100))}function kr(e,t,n){t=Math.max(0,Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t)),e.display.scroller.scrollTop==t&&!n||(e.doc.scrollTop=t,e.display.scrollbars.setScrollTop(t),e.display.scroller.scrollTop!=t&&(e.display.scroller.scrollTop=t))}function Tr(e,t,n,r){t=Math.max(0,Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth)),(n?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)&&!r||(e.doc.scrollLeft=t,Yr(e),e.display.scroller.scrollLeft!=t&&(e.display.scroller.scrollLeft=t),e.display.scrollbars.setScrollLeft(t))}function Mr(e){var t=e.display,n=t.gutters.offsetWidth,r=Math.round(e.doc.height+yn(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+wn(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:n}}e=function(e,t,n){this.cm=n;var r=this.vert=M("div",[M("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=M("div",[M("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");r.tabIndex=i.tabIndex=-1,e(r),e(i),ye(r,"scroll",function(){r.clientHeight&&t(r.scrollTop,"vertical")}),ye(i,"scroll",function(){i.clientWidth&&t(i.scrollLeft,"horizontal")}),this.checkedZeroWidth=!1,w&&v<8&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};e.prototype.update=function(e){var t,n=e.scrollWidth>e.clientWidth+1,r=e.scrollHeight>e.clientHeight+1,i=e.nativeBarWidth;return r?(this.vert.style.display="block",this.vert.style.bottom=n?i+"px":"0",t=e.viewHeight-(n?i:0),this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+t)+"px"):(this.vert.style.display="",this.vert.firstChild.style.height="0"),n?(this.horiz.style.display="block",this.horiz.style.right=r?i+"px":"0",this.horiz.style.left=e.barLeft+"px",t=e.viewWidth-e.barLeft-(r?i:0),this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+t)+"px"):(this.horiz.style.display="",this.horiz.firstChild.style.width="0"),!this.checkedZeroWidth&&0<e.clientHeight&&(0==i&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:r?i:0,bottom:n?i:0}},e.prototype.setScrollLeft=function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")},e.prototype.setScrollTop=function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,"vert")},e.prototype.zeroWidthHack=function(){var e=g&&!l?"12px":"18px";this.horiz.style.height=this.vert.style.width=e,this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none",this.disableHoriz=new I,this.disableVert=new I},e.prototype.enableZeroWidthBar=function(n,r,i){n.style.pointerEvents="auto",r.set(1e3,function e(){var t=n.getBoundingClientRect();("vert"==i?document.elementFromPoint(t.right-1,(t.top+t.bottom)/2):document.elementFromPoint((t.right+t.left)/2,t.bottom-1))!=n?n.style.pointerEvents="none":r.set(1e3,e)})},e.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)};r=function(){};function Nr(e,t){t=t||Mr(e);var n=e.display.barWidth,r=e.display.barHeight;Or(e,t);for(var i=0;i<4&&n!=e.display.barWidth||r!=e.display.barHeight;i++)n!=e.display.barWidth&&e.options.lineWrapping&&gr(e),Or(e,Mr(e)),n=e.display.barWidth,r=e.display.barHeight}function Or(e,t){var n=e.display,r=n.scrollbars.update(t);n.sizer.style.paddingRight=(n.barWidth=r.right)+"px",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+"px",n.heightForcer.style.borderBottom=r.bottom+"px solid transparent",r.right&&r.bottom?(n.scrollbarFiller.style.display="block",n.scrollbarFiller.style.height=r.bottom+"px",n.scrollbarFiller.style.width=r.right+"px"):n.scrollbarFiller.style.display="",r.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(n.gutterFiller.style.display="block",n.gutterFiller.style.height=r.bottom+"px",n.gutterFiller.style.width=t.gutterWidth+"px"):n.gutterFiller.style.display=""}r.prototype.update=function(){return{bottom:0,right:0}},r.prototype.setScrollLeft=function(){},r.prototype.setScrollTop=function(){},r.prototype.clear=function(){};var Ar={native:e,null:r};function Dr(n){n.display.scrollbars&&(n.display.scrollbars.clear(),n.display.scrollbars.addClass&&L(n.display.wrapper,n.display.scrollbars.addClass)),n.display.scrollbars=new Ar[n.options.scrollbarStyle](function(e){n.display.wrapper.insertBefore(e,n.display.scrollbarFiller),ye(e,"mousedown",function(){n.state.focused&&setTimeout(function(){return n.display.input.focus()},0)}),e.setAttribute("cm-not-content","true")},function(e,t){("horizontal"==t?Tr:Lr)(n,e)},n),n.display.scrollbars.addClass&&D(n.display.wrapper,n.display.scrollbars.addClass)}var Wr=0;function Hr(e){e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Wr},e=e.curOp,nn?nn.ops.push(e):e.ownsGroup=nn={ops:[e],delayedCallbacks:[]}}function Fr(e){e=e.curOp;e&&rn(e,function(e){for(var t=0;t<e.ops.length;t++)e.ops[t].cm.curOp=null;!function(e){for(var t=e.ops,n=0;n<t.length;n++)!function(e){var t=e.cm,n=t.display;(function(e){var t=e.display;!t.scrollbarsClipped&&t.scroller.offsetWidth&&(t.nativeBarWidth=t.scroller.offsetWidth-t.scroller.clientWidth,t.heightForcer.style.height=wn(e)+"px",t.sizer.style.marginBottom=-t.nativeBarWidth+"px",t.sizer.style.borderRightWidth=wn(e)+"px",t.scrollbarsClipped=!0)})(t),e.updateMaxLine&&jt(t);e.mustUpdate=e.viewChanged||e.forceUpdate||null!=e.scrollTop||e.scrollToPos&&(e.scrollToPos.from.line<n.viewFrom||e.scrollToPos.to.line>=n.viewTo)||n.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new Gr(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}(t[n]);for(var r=0;r<t.length;r++)!function(e){e.updatedDisplay=e.mustUpdate&&Ur(e.cm,e.update)}(t[r]);for(var i=0;i<t.length;i++)!function(e){var t=e.cm,n=t.display;e.updatedDisplay&&gr(t);e.barMeasure=Mr(t),n.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=Ln(t,n.maxLine,n.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+e.adjustWidthTo+wn(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+e.adjustWidthTo-xn(t)));(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=n.input.prepareSelection())}(t[i]);for(var o=0;o<t.length;o++)!function(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+"px",e.maxScrollLeft<t.doc.scrollLeft&&Tr(t,Math.min(t.display.scroller.scrollLeft,e.maxScrollLeft),!0),t.display.maxLineChanged=!1);var n=e.focus&&e.focus==A();e.preparedSelection&&t.display.input.showSelection(e.preparedSelection,n);!e.updatedDisplay&&e.startHeight==t.doc.height||Nr(t,e.barMeasure);e.updatedDisplay&&Xr(t,e.barMeasure);e.selectionChanged&&cr(t);t.state.focused&&e.updateInput&&t.display.input.reset(e.typing);n&&hr(e.cm)}(t[o]);for(var l=0;l<t.length;l++)!function(e){var t=e.cm,n=t.display,r=t.doc;e.updatedDisplay&&Vr(t,e.update);null==n.wheelStartX||null==e.scrollTop&&null==e.scrollLeft&&!e.scrollToPos||(n.wheelStartX=n.wheelStartY=null);null!=e.scrollTop&&kr(t,e.scrollTop,e.forceScroll);null!=e.scrollLeft&&Tr(t,e.scrollLeft,!0,!0);{var i;e.scrollToPos&&(i=function(e,t,n,r){null==r&&(r=0),e.options.lineWrapping||t!=n||(n="before"==(t=t.ch?rt(t.line,"before"==t.sticky?t.ch-1:t.ch,"after"):t).sticky?rt(t.line,t.ch+1,"before"):t);for(var i=0;i<5;i++){var o,l=!1,s=Bn(e,t),a=n&&n!=t?Bn(e,n):s,u=yr(e,o={left:Math.min(s.left,a.left),top:Math.min(s.top,a.top)-r,right:Math.max(s.left,a.left),bottom:Math.max(s.bottom,a.bottom)+r}),s=e.doc.scrollTop,a=e.doc.scrollLeft;if(null!=u.scrollTop&&(Lr(e,u.scrollTop),1<Math.abs(e.doc.scrollTop-s)&&(l=!0)),null!=u.scrollLeft&&(Tr(e,u.scrollLeft),1<Math.abs(e.doc.scrollLeft-a)&&(l=!0)),!l)break}return o}(t,ct(r,e.scrollToPos.from),ct(r,e.scrollToPos.to),e.scrollToPos.margin),function(e,t){var n,r,i;Ce(e,"scrollCursorIntoView")||(r=(n=e.display).sizer.getBoundingClientRect(),i=null,t.top+r.top<0?i=!0:t.bottom+r.top>(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null==i||u||(t=M("div","​",null,"position: absolute;\n                         top: "+(t.top-n.viewOffset-vn(e.display))+"px;\n                         height: "+(t.bottom-t.top+wn(e)+n.barHeight)+"px;\n                         left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;"),e.display.lineSpace.appendChild(t),t.scrollIntoView(i),e.display.lineSpace.removeChild(t)))}(t,i))}var o=e.maybeHiddenMarkers,l=e.maybeUnhiddenMarkers;if(o)for(var s=0;s<o.length;++s)o[s].lines.length||xe(o[s],"hide");if(l)for(var a=0;a<l.length;++a)l[a].lines.length&&xe(l[a],"unhide");n.wrapper.offsetHeight&&(r.scrollTop=t.display.scroller.scrollTop);e.changeObjs&&xe(t,"changes",t,e.changeObjs);e.update&&e.update.finish()}(t[l])}(e)})}function Pr(e,t){if(e.curOp)return t();Hr(e);try{return t()}finally{Fr(e)}}function Er(e,t){return function(){if(e.curOp)return t.apply(e,arguments);Hr(e);try{return t.apply(e,arguments)}finally{Fr(e)}}}function Ir(e){return function(){if(this.curOp)return e.apply(this,arguments);Hr(this);try{return e.apply(this,arguments)}finally{Fr(this)}}}function Rr(t){return function(){var e=this.cm;if(!e||e.curOp)return t.apply(this,arguments);Hr(e);try{return t.apply(this,arguments)}finally{Fr(e)}}}function zr(e,t){e.doc.highlightFrontier<e.display.viewTo&&e.state.highlight.set(t,F(Br,e))}function Br(l){var s,a,u,c=l.doc;c.highlightFrontier>=l.display.viewTo||(s=+new Date+l.options.workTime,a=mt(l,c.highlightFrontier),u=[],c.iter(a.line,Math.min(c.first+c.size,l.display.viewTo+500),function(e){if(a.line>=l.display.viewFrom){var t=e.styles,n=e.text.length>l.options.maxHighlightLength?je(c.mode,a.state):null,r=pt(l,e,a,!0);n&&(a.state=n),e.styles=r.styles;n=e.styleClasses,r=r.classes;r?e.styleClasses=r:n&&(e.styleClasses=null);for(var i=!t||t.length!=e.styles.length||n!=r&&(!n||!r||n.bgClass!=r.bgClass||n.textClass!=r.textClass),o=0;!i&&o<t.length;++o)i=t[o]!=e.styles[o];i&&u.push(a.line),e.stateAfter=a.save(),a.nextLine()}else e.text.length<=l.options.maxHighlightLength&&vt(l,e.text,a),e.stateAfter=a.line%5==0?a.save():null,a.nextLine();if(+new Date>s)return zr(l,l.options.workDelay),!0}),c.highlightFrontier=a.line,c.modeFrontier=Math.max(c.modeFrontier,a.line),u.length&&Pr(l,function(){for(var e=0;e<u.length;e++)nr(l,u[e],"text")}))}var Gr=function(e,t,n){var r=e.display;this.viewport=t,this.visible=vr(r,e.doc,t),this.editorIsHidden=!r.wrapper.offsetWidth,this.wrapperHeight=r.wrapper.clientHeight,this.wrapperWidth=r.wrapper.clientWidth,this.oldDisplayWidth=xn(e),this.force=n,this.dims=$n(e),this.events=[]};function Ur(e,t){var n=e.display,r=e.doc;if(t.editorIsHidden)return rr(e),!1;if(!t.force&&t.visible.from>=n.viewFrom&&t.visible.to<=n.viewTo&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo)&&n.renderedView==n.view&&0==or(e))return!1;_r(e)&&(rr(e),t.dims=$n(e));var i=r.first+r.size,o=Math.max(t.visible.from-e.options.viewportMargin,r.first),l=Math.min(i,t.visible.to+e.options.viewportMargin);n.viewFrom<o&&o-n.viewFrom<20&&(o=Math.max(r.first,n.viewFrom)),n.viewTo>l&&n.viewTo-l<20&&(l=Math.min(i,n.viewTo)),kt&&(o=Bt(e.doc,o),l=Gt(e.doc,l));var s=o!=n.viewFrom||l!=n.viewTo||n.lastWrapHeight!=t.wrapperHeight||n.lastWrapWidth!=t.wrapperWidth;r=o,i=l,0==(l=(o=e).display).view.length||r>=l.viewTo||i<=l.viewFrom?(l.view=tn(o,r,i),l.viewFrom=r):(l.viewFrom>r?l.view=tn(o,r,l.viewFrom).concat(l.view):l.viewFrom<r&&(l.view=l.view.slice(er(o,r))),l.viewFrom=r,l.viewTo<i?l.view=l.view.concat(tn(o,l.viewTo,i)):l.viewTo>i&&(l.view=l.view.slice(0,er(o,i)))),l.viewTo=i,n.viewOffset=Vt($e(e.doc,n.viewFrom)),e.display.mover.style.top=n.viewOffset+"px";o=or(e);if(!s&&0==o&&!t.force&&n.renderedView==n.view&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo))return!1;l=function(e){if(e.hasFocus())return null;var t=A();if(!t||!O(e.display.lineDiv,t))return null;var n={activeElt:t};return!window.getSelection||(t=window.getSelection()).anchorNode&&t.extend&&O(e.display.lineDiv,t.anchorNode)&&(n.anchorNode=t.anchorNode,n.anchorOffset=t.anchorOffset,n.focusNode=t.focusNode,n.focusOffset=t.focusOffset),n}(e);return 4<o&&(n.lineDiv.style.display="none"),function(n,e,t){var r=n.display,i=n.options.lineNumbers,o=r.lineDiv,l=o.firstChild;function s(e){var t=e.nextSibling;return f&&g&&n.display.currentWheelTarget==e?e.style.display="none":e.parentNode.removeChild(e),t}for(var a=r.view,u=r.viewFrom,c=0;c<a.length;c++){var h=a[c];if(!h.hidden)if(h.node&&h.node.parentNode==o){for(;l!=h.node;)l=s(l);var d=i&&null!=e&&e<=u&&h.lineNumber;h.changes&&(-1<R(h.changes,"gutter")&&(d=!1),an(n,h,u,t)),d&&(k(h.lineNumber),h.lineNumber.appendChild(document.createTextNode(nt(n.options,u)))),l=h.node.nextSibling}else{d=function(e,t,n,r){var i=cn(e,t);return t.text=t.node=i.pre,i.bgClass&&(t.bgClass=i.bgClass),i.textClass&&(t.textClass=i.textClass),hn(e,t),dn(e,t,n,r),fn(e,t,r),t.node}(n,h,u,t);o.insertBefore(d,l)}u+=h.size}for(;l;)l=s(l)}(e,n.updateLineNumbers,t.dims),4<o&&(n.lineDiv.style.display=""),n.renderedView=n.view,(i=l)&&i.activeElt&&i.activeElt!=A()&&(i.activeElt.focus(),!/^(INPUT|TEXTAREA)$/.test(i.activeElt.nodeName)&&i.anchorNode&&O(document.body,i.anchorNode)&&O(document.body,i.focusNode)&&(o=window.getSelection(),(l=document.createRange()).setEnd(i.anchorNode,i.anchorOffset),l.collapse(!1),o.removeAllRanges(),o.addRange(l),o.extend(i.focusNode,i.focusOffset))),k(n.cursorDiv),k(n.selectionDiv),n.gutters.style.height=n.sizer.style.minHeight=0,s&&(n.lastWrapHeight=t.wrapperHeight,n.lastWrapWidth=t.wrapperWidth,zr(e,400)),!(n.updateLineNumbers=null)}function Vr(e,t){for(var n=t.viewport,r=!0;;r=!1){if(r&&e.options.lineWrapping&&t.oldDisplayWidth!=xn(e))r&&(t.visible=vr(e.display,e.doc,n));else if(n&&null!=n.top&&(n={top:Math.min(e.doc.height+yn(e.display)-Cn(e),n.top)}),t.visible=vr(e.display,e.doc,n),t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)break;if(!Ur(e,t))break;gr(e);var i=Mr(e);lr(e),Nr(e,i),Xr(e,i),t.force=!1}t.signal(e,"update",e),e.display.viewFrom==e.display.reportedViewFrom&&e.display.viewTo==e.display.reportedViewTo||(t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function Kr(e,t){var n=new Gr(e,t);Ur(e,n)&&(gr(e),Vr(e,n),t=Mr(e),lr(e),Nr(e,t),Xr(e,t),n.finish())}function jr(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+"px"}function Xr(e,t){e.display.sizer.style.minHeight=t.docHeight+"px",e.display.heightForcer.style.top=t.docHeight+"px",e.display.gutters.style.height=t.docHeight+e.display.barHeight+wn(e)+"px"}function Yr(e){var t=e.display,n=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var r=qn(t)-t.scroller.scrollLeft+e.doc.scrollLeft,i=t.gutters.offsetWidth,o=r+"px",l=0;l<n.length;l++)if(!n[l].hidden){e.options.fixedGutter&&(n[l].gutter&&(n[l].gutter.style.left=o),n[l].gutterBackground&&(n[l].gutterBackground.style.left=o));var s=n[l].alignable;if(s)for(var a=0;a<s.length;a++)s[a].style.left=o}e.options.fixedGutter&&(t.gutters.style.left=r+i+"px")}}function _r(e){if(e.options.lineNumbers){var t=e.doc,n=nt(e.options,t.first+t.size-1),r=e.display;if(n.length!=r.lineNumChars){var i=r.measure.appendChild(M("div",[M("div",n)],"CodeMirror-linenumber CodeMirror-gutter-elt")),t=i.firstChild.offsetWidth,i=i.offsetWidth-t;return r.lineGutter.style.width="",r.lineNumInnerWidth=Math.max(t,r.lineGutter.offsetWidth-i)+1,r.lineNumWidth=r.lineNumInnerWidth+i,r.lineNumChars=r.lineNumInnerWidth?n.length:-1,r.lineGutter.style.width=r.lineNumWidth+"px",jr(e.display),1}}}function $r(e,t){for(var n=[],r=!1,i=0;i<e.length;i++){var o=e[i],l=null;if("string"!=typeof o&&(l=o.style,o=o.className),"CodeMirror-linenumbers"==o){if(!t)continue;r=!0}n.push({className:o,style:l})}return t&&!r&&n.push({className:"CodeMirror-linenumbers",style:null}),n}function qr(e){var t=e.gutters,n=e.gutterSpecs;k(t),e.lineGutter=null;for(var r=0;r<n.length;++r){var i=n[r],o=i.className,l=i.style,i=t.appendChild(M("div",null,"CodeMirror-gutter "+o));l&&(i.style.cssText=l),"CodeMirror-linenumbers"==o&&((e.lineGutter=i).style.width=(e.lineNumWidth||1)+"px")}t.style.display=n.length?"":"none",jr(e)}function Zr(e){qr(e.display),tr(e),Yr(e)}function Qr(e,t,n,r){var i=this;this.input=n,i.scrollbarFiller=M("div",null,"CodeMirror-scrollbar-filler"),i.scrollbarFiller.setAttribute("cm-not-content","true"),i.gutterFiller=M("div",null,"CodeMirror-gutter-filler"),i.gutterFiller.setAttribute("cm-not-content","true"),i.lineDiv=N("div",null,"CodeMirror-code"),i.selectionDiv=M("div",null,null,"position: relative; z-index: 1"),i.cursorDiv=M("div",null,"CodeMirror-cursors"),i.measure=M("div",null,"CodeMirror-measure"),i.lineMeasure=M("div",null,"CodeMirror-measure"),i.lineSpace=N("div",[i.measure,i.lineMeasure,i.selectionDiv,i.cursorDiv,i.lineDiv],null,"position: relative; outline: none");var o=N("div",[i.lineSpace],"CodeMirror-lines");i.mover=M("div",[o],null,"position: relative"),i.sizer=M("div",[i.mover],"CodeMirror-sizer"),i.sizerWidth=null,i.heightForcer=M("div",null,null,"position: absolute; height: "+z+"px; width: 1px;"),i.gutters=M("div",null,"CodeMirror-gutters"),i.lineGutter=null,i.scroller=M("div",[i.sizer,i.heightForcer,i.gutters],"CodeMirror-scroll"),i.scroller.setAttribute("tabIndex","-1"),i.wrapper=M("div",[i.scrollbarFiller,i.gutterFiller,i.scroller],"CodeMirror"),w&&v<8&&(i.gutters.style.zIndex=-1,i.scroller.style.paddingRight=0),f||d&&h||(i.scroller.draggable=!0),e&&(e.appendChild?e.appendChild(i.wrapper):e(i.wrapper)),i.viewFrom=i.viewTo=t.first,i.reportedViewFrom=i.reportedViewTo=t.first,i.view=[],i.renderedView=null,i.externalMeasured=null,i.viewOffset=0,i.lastWrapHeight=i.lastWrapWidth=0,i.updateLineNumbers=null,i.nativeBarWidth=i.barHeight=i.barWidth=0,i.scrollbarsClipped=!1,i.lineNumWidth=i.lineNumInnerWidth=i.lineNumChars=null,i.alignWidgets=!1,i.cachedCharWidth=i.cachedTextHeight=i.cachedPaddingH=null,i.maxLine=null,i.maxLineLength=0,i.maxLineChanged=!1,i.wheelDX=i.wheelDY=i.wheelStartX=i.wheelStartY=null,i.shift=!1,i.selForContextMenu=null,i.activeTouch=null,i.gutterSpecs=$r(r.gutters,r.lineNumbers),qr(i),n.init(i)}Gr.prototype.signal=function(e,t){Le(e,t)&&this.events.push(arguments)},Gr.prototype.finish=function(){for(var e=0;e<this.events.length;e++)xe.apply(null,this.events[e])};var Jr=0,ei=null;function ti(e){var t=e.wheelDeltaX,n=e.wheelDeltaY;return null==t&&e.detail&&e.axis==e.HORIZONTAL_AXIS&&(t=e.detail),null==n&&e.detail&&e.axis==e.VERTICAL_AXIS?n=e.detail:null==n&&(n=e.wheelDelta),{x:t,y:n}}function ni(e){e=ti(e);return e.x*=ei,e.y*=ei,e}function ri(e,t){var n=ti(t),r=n.x,i=n.y,o=e.display,l=o.scroller,s=l.scrollWidth>l.clientWidth,a=l.scrollHeight>l.clientHeight;if(r&&s||i&&a){if(i&&g&&f)e:for(var u=t.target,c=o.view;u!=l;u=u.parentNode)for(var h=0;h<c.length;h++)if(c[h].node==u){e.display.currentWheelTarget=u;break e}if(r&&!d&&!p&&null!=ei)return i&&a&&Lr(e,Math.max(0,l.scrollTop+i*ei)),Tr(e,Math.max(0,l.scrollLeft+r*ei)),(!i||i&&a)&&Te(t),void(o.wheelStartX=null);i&&null!=ei&&(n=i*ei,a=(s=e.doc.scrollTop)+o.wrapper.clientHeight,n<0?s=Math.max(0,s+n-50):a=Math.min(e.doc.height,a+n+50),Kr(e,{top:s,bottom:a})),Jr<20&&(null==o.wheelStartX?(o.wheelStartX=l.scrollLeft,o.wheelStartY=l.scrollTop,o.wheelDX=r,o.wheelDY=i,setTimeout(function(){var e,t;null!=o.wheelStartX&&(t=l.scrollLeft-o.wheelStartX,t=(e=l.scrollTop-o.wheelStartY)&&o.wheelDY&&e/o.wheelDY||t&&o.wheelDX&&t/o.wheelDX,o.wheelStartX=o.wheelStartY=null,t&&(ei=(ei*Jr+t)/(Jr+1),++Jr))},200)):(o.wheelDX+=r,o.wheelDY+=i))}}w?ei=-.53:d?ei=15:o?ei=-.7:c&&(ei=-1/3);var ii=function(e,t){this.ranges=e,this.primIndex=t};ii.prototype.primary=function(){return this.ranges[this.primIndex]},ii.prototype.equals=function(e){if(e==this)return!0;if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length)return!1;for(var t=0;t<this.ranges.length;t++){var n=this.ranges[t],r=e.ranges[t];if(!ot(n.anchor,r.anchor)||!ot(n.head,r.head))return!1}return!0},ii.prototype.deepCopy=function(){for(var e=[],t=0;t<this.ranges.length;t++)e[t]=new oi(lt(this.ranges[t].anchor),lt(this.ranges[t].head));return new ii(e,this.primIndex)},ii.prototype.somethingSelected=function(){for(var e=0;e<this.ranges.length;e++)if(!this.ranges[e].empty())return!0;return!1},ii.prototype.contains=function(e,t){t=t||e;for(var n=0;n<this.ranges.length;n++){var r=this.ranges[n];if(0<=it(t,r.from())&&it(e,r.to())<=0)return n}return-1};var oi=function(e,t){this.anchor=e,this.head=t};function li(e,t,n){var r=e&&e.options.selectionsMayTouch,e=t[n];t.sort(function(e,t){return it(e.from(),t.from())}),n=R(t,e);for(var i=1;i<t.length;i++){var o,l=t[i],s=t[i-1],a=it(s.to(),l.from());(r&&!l.empty()?0<a:0<=a)&&(o=at(s.from(),l.from()),a=st(s.to(),l.to()),s=s.empty()?l.from()==l.head:s.from()==s.head,i<=n&&--n,t.splice(--i,2,new oi(s?a:o,s?o:a)))}return new ii(t,n)}function si(e,t){return new ii([new oi(e,t||e)],0)}function ai(e){return e.text?rt(e.from.line+e.text.length-1,Y(e.text).length+(1==e.text.length?e.from.ch:0)):e.to}function ui(e,t){if(it(e,t.from)<0)return e;if(it(e,t.to)<=0)return ai(t);var n=e.line+t.text.length-(t.to.line-t.from.line)-1,r=e.ch;return e.line==t.to.line&&(r+=ai(t).ch-t.to.ch),rt(n,r)}function ci(e,t){for(var n=[],r=0;r<e.sel.ranges.length;r++){var i=e.sel.ranges[r];n.push(new oi(ui(i.anchor,t),ui(i.head,t)))}return li(e.cm,n,e.sel.primIndex)}function hi(e,t,n){return e.line==t.line?rt(n.line,e.ch-t.ch+n.ch):rt(n.line+(e.line-t.line),e.ch)}function di(e){e.doc.mode=Ue(e.options,e.doc.modeOption),fi(e)}function fi(e){e.doc.iter(function(e){e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null)}),e.doc.modeFrontier=e.doc.highlightFrontier=e.doc.first,zr(e,100),e.state.modeGen++,e.curOp&&tr(e)}function pi(e,t){return 0==t.from.ch&&0==t.to.ch&&""==Y(t.text)&&(!e.cm||e.cm.options.wholeLineUpdateBefore)}function gi(e,o,t,l){function i(e){return t?t[e]:null}function n(e,t,n){var r,i;r=t,i=n,t=l,(n=e).text=r,n.stateAfter&&(n.stateAfter=null),n.styles&&(n.styles=null),null!=n.order&&(n.order=null),At(n),Dt(n,i),(t=t?t(n):1)!=n.height&&Qe(n,t),ln(e,"change",e,o)}function r(e,t){for(var n=[],r=e;r<t;++r)n.push(new Xt(c[r],i(r),l));return n}var s,a=o.from,u=o.to,c=o.text,h=$e(e,a.line),d=$e(e,u.line),f=Y(c),p=i(c.length-1),g=u.line-a.line;o.full?(e.insert(0,r(0,c.length)),e.remove(c.length,e.size-c.length)):pi(e,o)?(s=r(0,c.length-1),n(d,d.text,p),g&&e.remove(a.line,g),s.length&&e.insert(a.line,s)):h==d?1==c.length?n(h,h.text.slice(0,a.ch)+f+h.text.slice(u.ch),p):((s=r(1,c.length-1)).push(new Xt(f+h.text.slice(u.ch),p,l)),n(h,h.text.slice(0,a.ch)+c[0],i(0)),e.insert(a.line+1,s)):1==c.length?(n(h,h.text.slice(0,a.ch)+c[0]+d.text.slice(u.ch),i(0)),e.remove(a.line+1,g)):(n(h,h.text.slice(0,a.ch)+c[0],i(0)),n(d,f+d.text.slice(u.ch),p),p=r(1,c.length-1),1<g&&e.remove(a.line+1,g-1),e.insert(a.line+1,p)),ln(e,"change",e,o)}function mi(e,s,a){!function e(t,n,r){if(t.linked)for(var i=0;i<t.linked.length;++i){var o,l=t.linked[i];l.doc!=n&&(o=r&&l.sharedHist,a&&!o||(s(l.doc,o),e(l.doc,t,o)))}}(e,null,!0)}function vi(e,t){if(t.cm)throw new Error("This document is already in use.");Qn((e.doc=t).cm=e),di(e),yi(e),e.options.lineWrapping||jt(e),e.options.mode=t.modeOption,tr(e)}function yi(e){("rtl"==e.doc.direction?D:L)(e.display.lineDiv,"CodeMirror-rtl")}function bi(e){this.done=[],this.undone=[],this.undoDepth=e?e.undoDepth:1/0,this.lastModTime=this.lastSelTime=0,this.lastOp=this.lastSelOp=null,this.lastOrigin=this.lastSelOrigin=null,this.generation=this.maxGeneration=e?e.maxGeneration:1}function wi(e,t){var n={from:lt(t.from),to:ai(t),text:qe(e,t.from,t.to)};return ki(e,n,t.from.line,t.to.line+1),mi(e,function(e){return ki(e,n,t.from.line,t.to.line+1),0},!0),n}function xi(e){for(;e.length;){if(!Y(e).ranges)break;e.pop()}}function Ci(e,t,n,r){var i=e.history;i.undone.length=0;var o,l,s=+new Date;if((i.lastOp==r||i.lastOrigin==t.origin&&t.origin&&("+"==t.origin.charAt(0)&&i.lastModTime>s-(e.cm?e.cm.options.historyEventDelay:500)||"*"==t.origin.charAt(0)))&&(o=(a=i).lastOp==r?(xi(a.done),Y(a.done)):a.done.length&&!Y(a.done).ranges?Y(a.done):1<a.done.length&&!a.done[a.done.length-2].ranges?(a.done.pop(),Y(a.done)):void 0))l=Y(o.changes),0==it(t.from,t.to)&&0==it(t.from,l.to)?l.to=ai(t):o.changes.push(wi(e,t));else{var a=Y(i.done);for(a&&a.ranges||Li(e.sel,i.done),o={changes:[wi(e,t)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(n),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=s,i.lastOp=i.lastSelOp=r,i.lastOrigin=i.lastSelOrigin=t.origin,l||xe(e,"historyAdded")}function Si(e,t,n,r){var i,o,l,s=e.history,a=r&&r.origin;n==s.lastSelOp||a&&s.lastSelOrigin==a&&(s.lastModTime==s.lastSelTime&&s.lastOrigin==a||(i=e,o=a,l=Y(s.done),e=t,"*"==(o=o.charAt(0))||"+"==o&&l.ranges.length==e.ranges.length&&l.somethingSelected()==e.somethingSelected()&&new Date-i.history.lastSelTime<=(i.cm?i.cm.options.historyEventDelay:500)))?s.done[s.done.length-1]=t:Li(t,s.done),s.lastSelTime=+new Date,s.lastSelOrigin=a,s.lastSelOp=n,r&&!1!==r.clearRedo&&xi(s.undone)}function Li(e,t){var n=Y(t);n&&n.ranges&&n.equals(e)||t.push(e)}function ki(t,n,e,r){var i=n["spans_"+t.id],o=0;t.iter(Math.max(t.first,e),Math.min(t.first+t.size,r),function(e){e.markedSpans&&((i=i||(n["spans_"+t.id]={}))[o]=e.markedSpans),++o})}function Ti(e,t){var n=t["spans_"+e.id];if(!n)return null;for(var r=[],i=0;i<t.text.length;++i)r.push(function(e){if(!e)return null;for(var t,n=0;n<e.length;++n)e[n].marker.explicitlyCleared?t=t||e.slice(0,n):t&&t.push(e[n]);return t?t.length?t:null:e}(n[i]));return r}function Mi(e,t){var n=Ti(e,t),r=Nt(e,t);if(!n)return r;if(!r)return n;for(var i=0;i<n.length;++i){var o=n[i],l=r[i];if(o&&l)e:for(var s=0;s<l.length;++s){for(var a=l[s],u=0;u<o.length;++u)if(o[u].marker==a.marker)continue e;o.push(a)}else l&&(n[i]=l)}return n}function Ni(e,t,n){for(var r=[],i=0;i<e.length;++i){var o=e[i];if(o.ranges)r.push(n?ii.prototype.deepCopy.call(o):o);else{var l=o.changes,s=[];r.push({changes:s});for(var a=0;a<l.length;++a){var u,c=l[a];if(s.push({from:c.from,to:c.to,text:c.text}),t)for(var h in c)(u=h.match(/^spans_(\d+)$/))&&-1<R(t,Number(u[1]))&&(Y(s)[h]=c[h],delete c[h])}}}return r}function Oi(e,t,n,r){if(r){r=e.anchor;return n&&((e=it(t,r)<0)!=it(n,r)<0?(r=t,t=n):e!=it(t,n)<0&&(t=n)),new oi(r,t)}return new oi(n||t,t)}function Ai(e,t,n,r,i){null==i&&(i=e.cm&&(e.cm.display.shift||e.extend)),Pi(e,new ii([Oi(e.sel.primary(),t,n,i)],0),r)}function Di(e,t,n){for(var r=[],i=e.cm&&(e.cm.display.shift||e.extend),o=0;o<e.sel.ranges.length;o++)r[o]=Oi(e.sel.ranges[o],t[o],null,i);Pi(e,li(e.cm,r,e.sel.primIndex),n)}function Wi(e,t,n,r){var i=e.sel.ranges.slice(0);i[t]=n,Pi(e,li(e.cm,i,e.sel.primIndex),r)}function Hi(e,t,n,r){Pi(e,si(t,n),r)}function Fi(e,t,n){var r=e.history.done,i=Y(r);i&&i.ranges?Ei(e,r[r.length-1]=t,n):Pi(e,t,n)}function Pi(e,t,n){Ei(e,t,n),Si(e,e.sel,e.cm?e.cm.curOp.id:NaN,n)}function Ei(e,t,n){var r,i;(Le(e,"beforeSelectionChange")||e.cm&&Le(e.cm,"beforeSelectionChange"))&&(r=e,i=n,i={ranges:(o=t).ranges,update:function(e){this.ranges=[];for(var t=0;t<e.length;t++)this.ranges[t]=new oi(ct(r,e[t].anchor),ct(r,e[t].head))},origin:i&&i.origin},xe(r,"beforeSelectionChange",r,i),r.cm&&xe(r.cm,"beforeSelectionChange",r.cm,i),t=i.ranges!=o.ranges?li(r.cm,i.ranges,i.ranges.length-1):o);var o=n&&n.bias||(it(t.primary().head,e.sel.primary().head)<0?-1:1);Ii(e,zi(e,t,o,!0)),n&&!1===n.scroll||!e.cm||"nocursor"==e.cm.getOption("readOnly")||wr(e.cm)}function Ii(e,t){t.equals(e.sel)||(e.sel=t,e.cm&&(e.cm.curOp.updateInput=1,e.cm.curOp.selectionChanged=!0,Se(e.cm)),ln(e,"cursorActivity",e))}function Ri(e){Ii(e,zi(e,e.sel,null,!1))}function zi(e,t,n,r){for(var i,o=0;o<t.ranges.length;o++){var l=t.ranges[o],s=t.ranges.length==e.sel.ranges.length&&e.sel.ranges[o],a=Gi(e,l.anchor,s&&s.anchor,n,r),s=Gi(e,l.head,s&&s.head,n,r);!i&&a==l.anchor&&s==l.head||((i=i||t.ranges.slice(0,o))[o]=new oi(a,s))}return i?li(e.cm,i,t.primIndex):t}function Bi(e,t,n,r,i){var o=$e(e,t.line);if(o.markedSpans)for(var l=0;l<o.markedSpans.length;++l){var s=o.markedSpans[l],a=s.marker,u="selectLeft"in a?!a.selectLeft:a.inclusiveLeft,c="selectRight"in a?!a.selectRight:a.inclusiveRight;if((null==s.from||(u?s.from<=t.ch:s.from<t.ch))&&(null==s.to||(c?s.to>=t.ch:s.to>t.ch))){if(i&&(xe(a,"beforeCursorEnter"),a.explicitlyCleared)){if(o.markedSpans){--l;continue}break}if(a.atomic){if(n){var h=a.find(r<0?1:-1),s=void 0;if((r<0?c:u)&&(h=Ui(e,h,-r,h&&h.line==t.line?o:null)),h&&h.line==t.line&&(s=it(h,n))&&(r<0?s<0:0<s))return Bi(e,h,t,r,i)}a=a.find(r<0?-1:1);return(r<0?u:c)&&(a=Ui(e,a,r,a.line==t.line?o:null)),a?Bi(e,a,t,r,i):null}}}return t}function Gi(e,t,n,r,i){r=r||1,r=Bi(e,t,n,r,i)||!i&&Bi(e,t,n,r,!0)||Bi(e,t,n,-r,i)||!i&&Bi(e,t,n,-r,!0);return r||(e.cantEdit=!0,rt(e.first,0))}function Ui(e,t,n,r){return n<0&&0==t.ch?t.line>e.first?ct(e,rt(t.line-1)):null:0<n&&t.ch==(r||$e(e,t.line)).text.length?t.line<e.first+e.size-1?rt(t.line+1,0):null:new rt(t.line,t.ch+n)}function Vi(e){e.setSelection(rt(e.firstLine(),0),rt(e.lastLine()),G)}function Ki(i,e,t){var o={canceled:!1,from:e.from,to:e.to,text:e.text,origin:e.origin,cancel:function(){return o.canceled=!0}};return t&&(o.update=function(e,t,n,r){e&&(o.from=ct(i,e)),t&&(o.to=ct(i,t)),n&&(o.text=n),void 0!==r&&(o.origin=r)}),xe(i,"beforeChange",i,o),i.cm&&xe(i.cm,"beforeChange",i.cm,o),o.canceled?(i.cm&&(i.cm.curOp.updateInput=2),null):{from:o.from,to:o.to,text:o.text,origin:o.origin}}function ji(e,t,n){if(e.cm){if(!e.cm.curOp)return Er(e.cm,ji)(e,t,n);if(e.cm.state.suppressEdits)return}if(!(Le(e,"beforeChange")||e.cm&&Le(e.cm,"beforeChange"))||(t=Ki(e,t,!0))){var r=Lt&&!n&&function(e,t,n){var r=null;if(e.iter(t.line,n.line+1,function(e){if(e.markedSpans)for(var t=0;t<e.markedSpans.length;++t){var n=e.markedSpans[t].marker;!n.readOnly||r&&-1!=R(r,n)||(r=r||[]).push(n)}}),!r)return null;for(var i=[{from:t,to:n}],o=0;o<r.length;++o)for(var l=r[o],s=l.find(0),a=0;a<i.length;++a){var u,c,h,d=i[a];it(d.to,s.from)<0||0<it(d.from,s.to)||(u=[a,1],c=it(d.from,s.from),h=it(d.to,s.to),(c<0||!l.inclusiveLeft&&!c)&&u.push({from:d.from,to:s.from}),(0<h||!l.inclusiveRight&&!h)&&u.push({from:s.to,to:d.to}),i.splice.apply(i,u),a+=u.length-3)}return i}(e,t.from,t.to);if(r)for(var i=r.length-1;0<=i;--i)Xi(e,{from:r[i].from,to:r[i].to,text:i?[""]:t.text,origin:t.origin});else Xi(e,t)}}function Xi(e,n){var t,r;1==n.text.length&&""==n.text[0]&&0==it(n.from,n.to)||(t=ci(e,n),Ci(e,n,t,e.cm?e.cm.curOp.id:NaN),$i(e,n,t,Nt(e,n)),r=[],mi(e,function(e,t){t||-1!=R(r,e.history)||(Ji(e.history,n),r.push(e.history)),$i(e,n,null,Nt(e,n))}))}function Yi(i,o,e){var t=i.cm&&i.cm.state.suppressEdits;if(!t||e){for(var l,n=i.history,r=i.sel,s="undo"==o?n.done:n.undone,a="undo"==o?n.undone:n.done,u=0;u<s.length&&(l=s[u],e?!l.ranges||l.equals(i.sel):l.ranges);u++);if(u!=s.length){for(n.lastOrigin=n.lastSelOrigin=null;;){if(!(l=s.pop()).ranges){if(t)return void s.push(l);break}if(Li(l,a),e&&!l.equals(i.sel))return void Pi(i,l,{clearRedo:!1});r=l}var c=[];Li(r,a),a.push({changes:c,generation:n.generation}),n.generation=l.generation||++n.maxGeneration;for(var h=Le(i,"beforeChange")||i.cm&&Le(i.cm,"beforeChange"),d=l.changes.length-1;0<=d;--d){var f=function(e){var n=l.changes[e];if(n.origin=o,h&&!Ki(i,n,!1))return s.length=0,{};c.push(wi(i,n));var t=e?ci(i,n):Y(s);$i(i,n,t,Mi(i,n)),!e&&i.cm&&i.cm.scrollIntoView({from:n.from,to:ai(n)});var r=[];mi(i,function(e,t){t||-1!=R(r,e.history)||(Ji(e.history,n),r.push(e.history)),$i(e,n,null,Mi(e,n))})}(d);if(f)return f.v}}}}function _i(e,t){if(0!=t&&(e.first+=t,e.sel=new ii(_(e.sel.ranges,function(e){return new oi(rt(e.anchor.line+t,e.anchor.ch),rt(e.head.line+t,e.head.ch))}),e.sel.primIndex),e.cm)){tr(e.cm,e.first,e.first-t,t);for(var n=e.cm.display,r=n.viewFrom;r<n.viewTo;r++)nr(e.cm,r,"gutter")}}function $i(e,t,n,r){if(e.cm&&!e.cm.curOp)return Er(e.cm,$i)(e,t,n,r);var i;t.to.line<e.first?_i(e,t.text.length-1-(t.to.line-t.from.line)):t.from.line>e.lastLine()||(t.from.line<e.first&&(_i(e,i=t.text.length-1-(e.first-t.from.line)),t={from:rt(e.first,0),to:rt(t.to.line+i,t.to.ch),text:[Y(t.text)],origin:t.origin}),i=e.lastLine(),t.to.line>i&&(t={from:t.from,to:rt(i,$e(e,i).text.length),text:[t.text[0]],origin:t.origin}),t.removed=qe(e,t.from,t.to),n=n||ci(e,t),e.cm?function(e,t,n){var r=e.doc,i=e.display,o=t.from,l=t.to,s=!1,a=o.line;e.options.lineWrapping||(a=Je(zt($e(r,o.line))),r.iter(a,l.line+1,function(e){if(e==i.maxLine)return s=!0}));-1<r.sel.contains(t.from,t.to)&&Se(e);gi(r,t,n,Zn(e)),e.options.lineWrapping||(r.iter(a,o.line+t.text.length,function(e){var t=Kt(e);t>i.maxLineLength&&(i.maxLine=e,i.maxLineLength=t,i.maxLineChanged=!0,s=!1)}),s&&(e.curOp.updateMaxLine=!0));(function(e,t){if(e.modeFrontier=Math.min(e.modeFrontier,t),!(e.highlightFrontier<t-10)){for(var n=e.first,r=t-1;n<r;r--){var i=$e(e,r).stateAfter;if(i&&(!(i instanceof dt)||r+i.lookAhead<t)){n=r+1;break}}e.highlightFrontier=Math.min(e.highlightFrontier,n)}})(r,o.line),zr(e,400);a=t.text.length-(l.line-o.line)-1;t.full?tr(e):o.line!=l.line||1!=t.text.length||pi(e.doc,t)?tr(e,o.line,l.line+1,a):nr(e,o.line,"text");r=Le(e,"changes"),a=Le(e,"change");(a||r)&&(t={from:o,to:l,text:t.text,removed:t.removed,origin:t.origin},a&&ln(e,"change",e,t),r&&(e.curOp.changeObjs||(e.curOp.changeObjs=[])).push(t));e.display.selForContextMenu=null}(e.cm,t,r):gi(e,t,r),Ei(e,n,G),e.cantEdit&&Gi(e,rt(e.firstLine(),0))&&(e.cantEdit=!1))}function qi(e,t,n,r,i){var o;it(r=r||n,n)<0&&(n=(o=[r,n])[0],r=o[1]),"string"==typeof t&&(t=e.splitLines(t)),ji(e,{from:n,to:r,text:t,origin:i})}function Zi(e,t,n,r){n<e.line?e.line+=r:t<e.line&&(e.line=t,e.ch=0)}function Qi(e,t,n,r){for(var i=0;i<e.length;++i){var o=e[i],l=!0;if(o.ranges){o.copied||((o=e[i]=o.deepCopy()).copied=!0);for(var s=0;s<o.ranges.length;s++)Zi(o.ranges[s].anchor,t,n,r),Zi(o.ranges[s].head,t,n,r)}else{for(var a=0;a<o.changes.length;++a){var u=o.changes[a];if(n<u.from.line)u.from=rt(u.from.line+r,u.from.ch),u.to=rt(u.to.line+r,u.to.ch);else if(t<=u.to.line){l=!1;break}}l||(e.splice(0,i+1),i=0)}}}function Ji(e,t){var n=t.from.line,r=t.to.line,t=t.text.length-(r-n)-1;Qi(e.done,n,r,t),Qi(e.undone,n,r,t)}function eo(e,t,n,r){var i=t,o=t;return"number"==typeof t?o=$e(e,ut(e,t)):i=Je(t),null==i?null:(r(o,i)&&e.cm&&nr(e.cm,i,n),o)}function to(e){this.lines=e,this.parent=null;for(var t=0,n=0;n<e.length;++n)e[n].parent=this,t+=e[n].height;this.height=t}function no(e){this.children=e;for(var t=0,n=0,r=0;r<e.length;++r){var i=e[r];t+=i.chunkSize(),n+=i.height,i.parent=this}this.size=t,this.height=n,this.parent=null}oi.prototype.from=function(){return at(this.anchor,this.head)},oi.prototype.to=function(){return st(this.anchor,this.head)},oi.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch},to.prototype={chunkSize:function(){return this.lines.length},removeInner:function(e,t){for(var n,r=e,i=e+t;r<i;++r){var o=this.lines[r];this.height-=o.height,(n=o).parent=null,At(n),ln(o,"delete")}this.lines.splice(e,t)},collapse:function(e){e.push.apply(e,this.lines)},insertInner:function(e,t,n){this.height+=n,this.lines=this.lines.slice(0,e).concat(t).concat(this.lines.slice(e));for(var r=0;r<t.length;++r)t[r].parent=this},iterN:function(e,t,n){for(var r=e+t;e<r;++e)if(n(this.lines[e]))return!0}},no.prototype={chunkSize:function(){return this.size},removeInner:function(e,t){this.size-=t;for(var n,r=0;r<this.children.length;++r){var i=this.children[r],o=i.chunkSize();if(e<o){var l=Math.min(t,o-e),s=i.height;if(i.removeInner(e,l),this.height-=s-i.height,o==l&&(this.children.splice(r--,1),i.parent=null),0==(t-=l))break;e=0}else e-=o}this.size-t<25&&(1<this.children.length||!(this.children[0]instanceof to))&&(n=[],this.collapse(n),this.children=[new to(n)],this.children[0].parent=this)},collapse:function(e){for(var t=0;t<this.children.length;++t)this.children[t].collapse(e)},insertInner:function(e,t,n){this.size+=t.length,this.height+=n;for(var r=0;r<this.children.length;++r){var i=this.children[r],o=i.chunkSize();if(e<=o){if(i.insertInner(e,t,n),i.lines&&50<i.lines.length){for(var l=i.lines.length%25+25,s=l;s<i.lines.length;){var a=new to(i.lines.slice(s,s+=25));i.height-=a.height,this.children.splice(++r,0,a),a.parent=this}i.lines=i.lines.slice(0,l),this.maybeSpill()}break}e-=o}},maybeSpill:function(){if(!(this.children.length<=10)){var e=this;do{var t,n=new no(e.children.splice(e.children.length-5,5))}while(e.parent?(e.size-=n.size,e.height-=n.height,t=R(e.parent.children,e),e.parent.children.splice(t+1,0,n)):(((t=new no(e.children)).parent=e).children=[t,n],e=t),n.parent=e.parent,10<e.children.length);e.parent.maybeSpill()}},iterN:function(e,t,n){for(var r=0;r<this.children.length;++r){var i=this.children[r],o=i.chunkSize();if(e<o){var l=Math.min(t,o-e);if(i.iterN(e,l,n))return!0;if(0==(t-=l))break;e=0}else e-=o}}};function ro(e,t,n){if(n)for(var r in n)n.hasOwnProperty(r)&&(this[r]=n[r]);this.doc=e,this.node=t}function io(e,t,n){Vt(t)<(e.curOp&&e.curOp.scrollTop||e.doc.scrollTop)&&br(e,n)}ro.prototype.clear=function(){var e=this.doc.cm,t=this.line.widgets,n=this.line,r=Je(n);if(null!=r&&t){for(var i=0;i<t.length;++i)t[i]==this&&t.splice(i--,1);t.length||(n.widgets=null);var o=gn(this);Qe(n,Math.max(0,n.height-o)),e&&(Pr(e,function(){io(e,n,-o),nr(e,r,"widget")}),ln(e,"lineWidgetCleared",e,this,r))}},ro.prototype.changed=function(){var e=this,t=this.height,n=this.doc.cm,r=this.line;this.height=null;var i=gn(this)-t;i&&(Ut(this.doc,r)||Qe(r,r.height+i),n&&Pr(n,function(){n.curOp.forceUpdate=!0,io(n,r,i),ln(n,"lineWidgetChanged",n,e,Je(r))}))},ke(ro);var oo=0,lo=function(e,t){this.lines=[],this.type=t,this.doc=e,this.id=++oo};function so(t,n,r,e,i){if(e&&e.shared)return function(e,n,r,i,o){(i=P(i)).shared=!1;var l=[so(e,n,r,i,o)],s=l[0],a=i.widgetNode;return mi(e,function(e){a&&(i.widgetNode=a.cloneNode(!0)),l.push(so(e,ct(e,n),ct(e,r),i,o));for(var t=0;t<e.linked.length;++t)if(e.linked[t].isParent)return;s=Y(l)}),new ao(l,s)}(t,n,r,e,i);if(t.cm&&!t.cm.curOp)return Er(t.cm,so)(t,n,r,e,i);var o=new lo(t,i),i=it(n,r);if(e&&P(e,o,!1),0<i||0==i&&!1!==o.clearWhenEmpty)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=N("span",[o.replacedWith],"CodeMirror-widget"),e.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),e.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(Rt(t,n.line,n,r,o)||n.line!=r.line&&Rt(t,r.line,n,r,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");kt=!0}o.addToHistory&&Ci(t,{from:n,to:r,origin:"markText"},t.sel,NaN);var l,s=n.line,a=t.cm;if(t.iter(s,r.line+1,function(e){var t;a&&o.collapsed&&!a.options.lineWrapping&&zt(e)==a.display.maxLine&&(l=!0),o.collapsed&&s!=n.line&&Qe(e,0),t=e,e=new Tt(o,s==n.line?n.ch:null,s==r.line?r.ch:null),t.markedSpans=t.markedSpans?t.markedSpans.concat([e]):[e],e.marker.attachLine(t),++s}),o.collapsed&&t.iter(n.line,r.line+1,function(e){Ut(t,e)&&Qe(e,0)}),o.clearOnEnter&&ye(o,"beforeCursorEnter",function(){return o.clear()}),o.readOnly&&(Lt=!0,(t.history.done.length||t.history.undone.length)&&t.clearHistory()),o.collapsed&&(o.id=++oo,o.atomic=!0),a){if(l&&(a.curOp.updateMaxLine=!0),o.collapsed)tr(a,n.line,r.line+1);else if(o.className||o.startStyle||o.endStyle||o.css||o.attributes||o.title)for(var u=n.line;u<=r.line;u++)nr(a,u,"text");o.atomic&&Ri(a.doc),ln(a,"markerAdded",a,o)}return o}lo.prototype.clear=function(){if(!this.explicitlyCleared){var e,t=this.doc.cm,n=t&&!t.curOp;n&&Hr(t),!Le(this,"clear")||(e=this.find())&&ln(this,"clear",e.from,e.to);for(var r=null,i=null,o=0;o<this.lines.length;++o){var l=this.lines[o],s=Mt(l.markedSpans,this);t&&!this.collapsed?nr(t,Je(l),"text"):t&&(null!=s.to&&(i=Je(l)),null!=s.from&&(r=Je(l))),l.markedSpans=function(e,t){for(var n,r=0;r<e.length;++r)e[r]!=t&&(n=n||[]).push(e[r]);return n}(l.markedSpans,s),null==s.from&&this.collapsed&&!Ut(this.doc,l)&&t&&Qe(l,Yn(t.display))}if(t&&this.collapsed&&!t.options.lineWrapping)for(var a=0;a<this.lines.length;++a){var u=zt(this.lines[a]),c=Kt(u);c>t.display.maxLineLength&&(t.display.maxLine=u,t.display.maxLineLength=c,t.display.maxLineChanged=!0)}null!=r&&t&&this.collapsed&&tr(t,r,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,t&&Ri(t.doc)),t&&ln(t,"markerCleared",t,this,r,i),n&&Fr(t),this.parent&&this.parent.clear()}},lo.prototype.find=function(e,t){var n,r;null==e&&"bookmark"==this.type&&(e=1);for(var i=0;i<this.lines.length;++i){var o=this.lines[i],l=Mt(o.markedSpans,this);if(null!=l.from&&(n=rt(t?o:Je(o),l.from),-1==e))return n;if(null!=l.to&&(r=rt(t?o:Je(o),l.to),1==e))return r}return n&&{from:n,to:r}},lo.prototype.changed=function(){var n=this,r=this.find(-1,!0),i=this,o=this.doc.cm;r&&o&&Pr(o,function(){var e=r.line,t=Je(r.line),t=kn(o,t);t&&(Dn(t),o.curOp.selectionChanged=o.curOp.forceUpdate=!0),o.curOp.updateMaxLine=!0,Ut(i.doc,e)||null==i.height||(t=i.height,i.height=null,(t=gn(i)-t)&&Qe(e,e.height+t)),ln(o,"markerChanged",o,n)})},lo.prototype.attachLine=function(e){var t;!this.lines.length&&this.doc.cm&&((t=this.doc.cm.curOp).maybeHiddenMarkers&&-1!=R(t.maybeHiddenMarkers,this)||(t.maybeUnhiddenMarkers||(t.maybeUnhiddenMarkers=[])).push(this)),this.lines.push(e)},lo.prototype.detachLine=function(e){this.lines.splice(R(this.lines,e),1),!this.lines.length&&this.doc.cm&&((e=this.doc.cm.curOp).maybeHiddenMarkers||(e.maybeHiddenMarkers=[])).push(this)},ke(lo);var ao=function(e,t){this.markers=e,this.primary=t;for(var n=0;n<e.length;++n)e[n].parent=this};function uo(e){return e.findMarks(rt(e.first,0),e.clipPos(rt(e.lastLine())),function(e){return e.parent})}ao.prototype.clear=function(){if(!this.explicitlyCleared){this.explicitlyCleared=!0;for(var e=0;e<this.markers.length;++e)this.markers[e].clear();ln(this,"clear")}},ao.prototype.find=function(e,t){return this.primary.find(e,t)},ke(ao);var co=0,ho=function(e,t,n,r,i){if(!(this instanceof ho))return new ho(e,t,n,r,i);null==n&&(n=0),no.call(this,[new to([new Xt("",null)])]),this.first=n,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.cleanGeneration=1;n=rt(this.modeFrontier=this.highlightFrontier=n,0);this.sel=si(n),this.history=new bi(null),this.id=++co,this.modeOption=t,this.lineSep=r,this.direction="rtl"==i?"rtl":"ltr",this.extend=!1,"string"==typeof e&&(e=this.splitLines(e)),gi(this,{from:n,to:n,text:e}),Pi(this,si(n),G)};ho.prototype=q(no.prototype,{constructor:ho,iter:function(e,t,n){n?this.iterN(e-this.first,t-e,n):this.iterN(this.first,this.first+this.size,e)},insert:function(e,t){for(var n=0,r=0;r<t.length;++r)n+=t[r].height;this.insertInner(e-this.first,t,n)},remove:function(e,t){this.removeInner(e-this.first,t)},getValue:function(e){var t=Ze(this,this.first,this.first+this.size);return!1===e?t:t.join(e||this.lineSeparator())},setValue:Rr(function(e){var t=rt(this.first,0),n=this.first+this.size-1;ji(this,{from:t,to:rt(n,$e(this,n).text.length),text:this.splitLines(e),origin:"setValue",full:!0},!0),this.cm&&xr(this.cm,0,0),Pi(this,si(t),G)}),replaceRange:function(e,t,n,r){qi(this,e,t=ct(this,t),n=n?ct(this,n):t,r)},getRange:function(e,t,n){t=qe(this,ct(this,e),ct(this,t));return!1===n?t:t.join(n||this.lineSeparator())},getLine:function(e){e=this.getLineHandle(e);return e&&e.text},getLineHandle:function(e){if(tt(this,e))return $e(this,e)},getLineNumber:Je,getLineHandleVisualStart:function(e){return"number"==typeof e&&(e=$e(this,e)),zt(e)},lineCount:function(){return this.size},firstLine:function(){return this.first},lastLine:function(){return this.first+this.size-1},clipPos:function(e){return ct(this,e)},getCursor:function(e){var t=this.sel.primary(),t=null==e||"head"==e?t.head:"anchor"==e?t.anchor:"end"==e||"to"==e||!1===e?t.to():t.from();return t},listSelections:function(){return this.sel.ranges},somethingSelected:function(){return this.sel.somethingSelected()},setCursor:Rr(function(e,t,n){Hi(this,ct(this,"number"==typeof e?rt(e,t||0):e),null,n)}),setSelection:Rr(function(e,t,n){Hi(this,ct(this,e),ct(this,t||e),n)}),extendSelection:Rr(function(e,t,n){Ai(this,ct(this,e),t&&ct(this,t),n)}),extendSelections:Rr(function(e,t){Di(this,ht(this,e),t)}),extendSelectionsBy:Rr(function(e,t){Di(this,ht(this,_(this.sel.ranges,e)),t)}),setSelections:Rr(function(e,t,n){if(e.length){for(var r=[],i=0;i<e.length;i++)r[i]=new oi(ct(this,e[i].anchor),ct(this,e[i].head));null==t&&(t=Math.min(e.length-1,this.sel.primIndex)),Pi(this,li(this.cm,r,t),n)}}),addSelection:Rr(function(e,t,n){var r=this.sel.ranges.slice(0);r.push(new oi(ct(this,e),ct(this,t||e))),Pi(this,li(this.cm,r,r.length-1),n)}),getSelection:function(e){for(var t=this.sel.ranges,n=0;n<t.length;n++)var r=qe(this,t[n].from(),t[n].to()),i=i?i.concat(r):r;return!1===e?i:i.join(e||this.lineSeparator())},getSelections:function(e){for(var t=[],n=this.sel.ranges,r=0;r<n.length;r++){var i=qe(this,n[r].from(),n[r].to());!1!==e&&(i=i.join(e||this.lineSeparator())),t[r]=i}return t},replaceSelection:function(e,t,n){for(var r=[],i=0;i<this.sel.ranges.length;i++)r[i]=e;this.replaceSelections(r,t,n||"+input")},replaceSelections:Rr(function(e,t,n){for(var r=[],i=this.sel,o=0;o<i.ranges.length;o++){var l=i.ranges[o];r[o]={from:l.from(),to:l.to(),text:this.splitLines(e[o]),origin:n}}for(var t=t&&"end"!=t&&function(e,t,n){for(var r=[],i=u=rt(e.first,0),o=0;o<t.length;o++){var l=t[o],s=hi(l.from,u,i),a=hi(ai(l),u,i),u=l.to,i=a;"around"==n?(l=it((l=e.sel.ranges[o]).head,l.anchor)<0,r[o]=new oi(l?a:s,l?s:a)):r[o]=new oi(s,s)}return new ii(r,e.sel.primIndex)}(this,r,t),s=r.length-1;0<=s;s--)ji(this,r[s]);t?Fi(this,t):this.cm&&wr(this.cm)}),undo:Rr(function(){Yi(this,"undo")}),redo:Rr(function(){Yi(this,"redo")}),undoSelection:Rr(function(){Yi(this,"undo",!0)}),redoSelection:Rr(function(){Yi(this,"redo",!0)}),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,n=0,r=0;r<e.done.length;r++)e.done[r].ranges||++t;for(var i=0;i<e.undone.length;i++)e.undone[i].ranges||++n;return{undo:t,redo:n}},clearHistory:function(){var t=this;this.history=new bi(this.history),mi(this,function(e){return e.history=t.history},!0)},markClean:function(){this.cleanGeneration=this.changeGeneration(!0)},changeGeneration:function(e){return e&&(this.history.lastOp=this.history.lastSelOp=this.history.lastOrigin=null),this.history.generation},isClean:function(e){return this.history.generation==(e||this.cleanGeneration)},getHistory:function(){return{done:Ni(this.history.done),undone:Ni(this.history.undone)}},setHistory:function(e){var t=this.history=new bi(this.history);t.done=Ni(e.done.slice(0),null,!0),t.undone=Ni(e.undone.slice(0),null,!0)},setGutterMarker:Rr(function(e,n,r){return eo(this,e,"gutter",function(e){var t=e.gutterMarkers||(e.gutterMarkers={});return!(t[n]=r)&&ee(t)&&(e.gutterMarkers=null),1})}),clearGutter:Rr(function(t){var n=this;this.iter(function(e){e.gutterMarkers&&e.gutterMarkers[t]&&eo(n,e,"gutter",function(){return e.gutterMarkers[t]=null,ee(e.gutterMarkers)&&(e.gutterMarkers=null),1})})}),lineInfo:function(e){var t;if("number"==typeof e){if(!tt(this,e))return null;if(!(e=$e(this,t=e)))return null}else if(null==(t=Je(e)))return null;return{line:t,handle:e,text:e.text,gutterMarkers:e.gutterMarkers,textClass:e.textClass,bgClass:e.bgClass,wrapClass:e.wrapClass,widgets:e.widgets}},addLineClass:Rr(function(e,n,r){return eo(this,e,"gutter"==n?"gutter":"class",function(e){var t="text"==n?"textClass":"background"==n?"bgClass":"gutter"==n?"gutterClass":"wrapClass";if(e[t]){if(C(r).test(e[t]))return;e[t]+=" "+r}else e[t]=r;return 1})}),removeLineClass:Rr(function(e,o,l){return eo(this,e,"gutter"==o?"gutter":"class",function(e){var t="text"==o?"textClass":"background"==o?"bgClass":"gutter"==o?"gutterClass":"wrapClass",n=e[t];if(n){if(null==l)e[t]=null;else{var r=n.match(C(l));if(!r)return;var i=r.index+r[0].length;e[t]=n.slice(0,r.index)+(r.index&&i!=n.length?" ":"")+n.slice(i)||null}return 1}})}),addLineWidget:Rr(function(e,t,n){return e=e,i=new ro(r=this,t,n),(o=r.cm)&&i.noHScroll&&(o.display.alignWidgets=!0),eo(r,e,"widget",function(e){var t=e.widgets||(e.widgets=[]);return null==i.insertAt?t.push(i):t.splice(Math.min(t.length,Math.max(0,i.insertAt)),0,i),i.line=e,o&&!Ut(r,e)&&(t=Vt(e)<r.scrollTop,Qe(e,e.height+gn(i)),t&&br(o,i.height),o.curOp.forceUpdate=!0),1}),o&&ln(o,"lineWidgetAdded",o,i,"number"==typeof e?e:Je(e)),i;var r,i,o}),removeLineWidget:function(e){e.clear()},markText:function(e,t,n){return so(this,ct(this,e),ct(this,t),n,n&&n.type||"range")},setBookmark:function(e,t){t={replacedWith:t&&(null==t.nodeType?t.widget:t),insertLeft:t&&t.insertLeft,clearWhenEmpty:!1,shared:t&&t.shared,handleMouseEvents:t&&t.handleMouseEvents};return so(this,e=ct(this,e),e,t,"bookmark")},findMarksAt:function(e){var t=[],n=$e(this,(e=ct(this,e)).line).markedSpans;if(n)for(var r=0;r<n.length;++r){var i=n[r];(null==i.from||i.from<=e.ch)&&(null==i.to||i.to>=e.ch)&&t.push(i.marker.parent||i.marker)}return t},findMarks:function(i,o,l){i=ct(this,i),o=ct(this,o);var s=[],a=i.line;return this.iter(i.line,o.line+1,function(e){var t=e.markedSpans;if(t)for(var n=0;n<t.length;n++){var r=t[n];null!=r.to&&a==i.line&&i.ch>=r.to||null==r.from&&a!=i.line||null!=r.from&&a==o.line&&r.from>=o.ch||l&&!l(r.marker)||s.push(r.marker.parent||r.marker)}++a}),s},getAllMarks:function(){var r=[];return this.iter(function(e){var t=e.markedSpans;if(t)for(var n=0;n<t.length;++n)null!=t[n].from&&r.push(t[n].marker)}),r},posFromIndex:function(t){var n,r=this.first,i=this.lineSeparator().length;return this.iter(function(e){e=e.text.length+i;if(t<e)return n=t,!0;t-=e,++r}),ct(this,rt(r,n))},indexFromPos:function(e){var t=(e=ct(this,e)).ch;if(e.line<this.first||e.ch<0)return 0;var n=this.lineSeparator().length;return this.iter(this.first,e.line,function(e){t+=e.text.length+n}),t},copy:function(e){var t=new ho(Ze(this,this.first,this.first+this.size),this.modeOption,this.first,this.lineSep,this.direction);return t.scrollTop=this.scrollTop,t.scrollLeft=this.scrollLeft,t.sel=this.sel,t.extend=!1,e&&(t.history.undoDepth=this.history.undoDepth,t.setHistory(this.getHistory())),t},linkedDoc:function(e){e=e||{};var t=this.first,n=this.first+this.size;null!=e.from&&e.from>t&&(t=e.from),null!=e.to&&e.to<n&&(n=e.to);t=new ho(Ze(this,t,n),e.mode||this.modeOption,t,this.lineSep,this.direction);return e.sharedHist&&(t.history=this.history),(this.linked||(this.linked=[])).push({doc:t,sharedHist:e.sharedHist}),t.linked=[{doc:this,isParent:!0,sharedHist:e.sharedHist}],function(e,t){for(var n=0;n<t.length;n++){var r=t[n],i=r.find(),o=e.clipPos(i.from),i=e.clipPos(i.to);it(o,i)&&(i=so(e,o,i,r.primary,r.primary.type),r.markers.push(i),i.parent=r)}}(t,uo(this)),t},unlinkDoc:function(e){if(e instanceof ul&&(e=e.doc),this.linked)for(var t=0;t<this.linked.length;++t)if(this.linked[t].doc==e){this.linked.splice(t,1),e.unlinkDoc(this),function(o){for(var e=0;e<o.length;e++)!function(e){var t=o[e],n=[t.primary.doc];mi(t.primary.doc,function(e){return n.push(e)});for(var r=0;r<t.markers.length;r++){var i=t.markers[r];-1==R(n,i.doc)&&(i.parent=null,t.markers.splice(r--,1))}}(e)}(uo(this));break}var n;e.history==this.history&&(n=[e.id],mi(e,function(e){return n.push(e.id)},!0),e.history=new bi(null),e.history.done=Ni(this.history.done,n),e.history.undone=Ni(this.history.undone,n))},iterLinkedDocs:function(e){mi(this,e)},getMode:function(){return this.mode},getEditor:function(){return this.cm},splitLines:function(e){return this.lineSep?e.split(this.lineSep):Pe(e)},lineSeparator:function(){return this.lineSep||"\n"},setDirection:Rr(function(e){var t;"rtl"!=e&&(e="ltr"),e!=this.direction&&(this.direction=e,this.iter(function(e){return e.order=null}),this.cm&&Pr(t=this.cm,function(){yi(t),tr(t)}))})}),ho.prototype.eachLine=ho.prototype.iter;var fo=0;function po(e){var r=this;if(go(r),!Ce(r,e)&&!mn(r.display,e)){Te(e),w&&(fo=+new Date);var t=Jn(r,e,!0),n=e.dataTransfer.files;if(t&&!r.isReadOnly())if(n&&n.length&&window.FileReader&&window.File)for(var i=n.length,o=Array(i),l=0,s=function(){++l==i&&Er(r,function(){var e={from:t=ct(r.doc,t),to:t,text:r.doc.splitLines(o.filter(function(e){return null!=e}).join(r.doc.lineSeparator())),origin:"paste"};ji(r.doc,e),Fi(r.doc,si(ct(r.doc,t),ct(r.doc,ai(e))))})()},a=0;a<n.length;a++)!function(e,t){var n;r.options.allowDropFileTypes&&-1==R(r.options.allowDropFileTypes,e.type)?s():((n=new FileReader).onerror=s,n.onload=function(){var e=n.result;/[\x00-\x08\x0e-\x1f]{2}/.test(e)||(o[t]=e),s()},n.readAsText(e))}(n[a],a);else{if(r.state.draggingText&&-1<r.doc.sel.contains(t))return r.state.draggingText(e),void setTimeout(function(){return r.display.input.focus()},20);try{var u,c=e.dataTransfer.getData("Text");if(c){if(r.state.draggingText&&!r.state.draggingText.copy&&(u=r.listSelections()),Ei(r.doc,si(t,t)),u)for(var h=0;h<u.length;++h)qi(r.doc,"",u[h].anchor,u[h].head,"drag");r.replaceSelection(c,"around","paste"),r.display.input.focus()}}catch(e){}}}}function go(e){e.display.dragCursor&&(e.display.lineSpace.removeChild(e.display.dragCursor),e.display.dragCursor=null)}function mo(t){if(document.getElementsByClassName){for(var e=document.getElementsByClassName("CodeMirror"),n=[],r=0;r<e.length;r++){var i=e[r].CodeMirror;i&&n.push(i)}n.length&&n[0].operation(function(){for(var e=0;e<n.length;e++)t(n[e])})}}var vo=!1;function yo(){var e;vo||(ye(window,"resize",function(){null==e&&(e=setTimeout(function(){e=null,mo(bo)},100))}),ye(window,"blur",function(){return mo(pr)}),vo=!0)}function bo(e){var t=e.display;t.cachedCharWidth=t.cachedTextHeight=t.cachedPaddingH=null,t.scrollbarsClipped=!1,e.setSize()}for(var wo={3:"Pause",8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"PrintScrn",45:"Insert",46:"Delete",59:";",61:"=",91:"Mod",92:"Mod",93:"Mod",106:"*",107:"=",109:"-",110:".",111:"/",145:"ScrollLock",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",224:"Mod",63232:"Up",63233:"Down",63234:"Left",63235:"Right",63272:"Delete",63273:"Home",63275:"End",63276:"PageUp",63277:"PageDown",63302:"Insert"},xo=0;xo<10;xo++)wo[xo+48]=wo[xo+96]=String(xo);for(var Co=65;Co<=90;Co++)wo[Co]=String.fromCharCode(Co);for(var So=1;So<=12;So++)wo[So+111]=wo[So+63235]="F"+So;var Lo={};function ko(e){var t,n,r,i,o=e.split(/-(?!$)/);e=o[o.length-1];for(var l=0;l<o.length-1;l++){var s=o[l];if(/^(cmd|meta|m)$/i.test(s))i=!0;else if(/^a(lt)?$/i.test(s))t=!0;else if(/^(c|ctrl|control)$/i.test(s))n=!0;else{if(!/^s(hift)?$/i.test(s))throw new Error("Unrecognized modifier name: "+s);r=!0}}return t&&(e="Alt-"+e),n&&(e="Ctrl-"+e),i&&(e="Cmd-"+e),r&&(e="Shift-"+e),e}function To(e){var t,n,r={};for(t in e)if(e.hasOwnProperty(t)){var i=e[t];if(!/^(name|fallthrough|(de|at)tach)$/.test(t))if("..."!=i){for(var o=_(t.split(" "),ko),l=0;l<o.length;l++){var s=void 0,a=void 0,s=l==o.length-1?(a=o.join(" "),i):(a=o.slice(0,l+1).join(" "),"..."),u=r[a];if(u){if(u!=s)throw new Error("Inconsistent bindings for "+a)}else r[a]=s}delete e[t]}else delete e[t]}for(n in r)e[n]=r[n];return e}function Mo(e,t,n,r){var i=(t=Do(t)).call?t.call(e,r):t[e];if(!1===i)return"nothing";if("..."===i)return"multi";if(null!=i&&n(i))return"handled";if(t.fallthrough){if("[object Array]"!=Object.prototype.toString.call(t.fallthrough))return Mo(e,t.fallthrough,n,r);for(var o=0;o<t.fallthrough.length;o++){var l=Mo(e,t.fallthrough[o],n,r);if(l)return l}}}function No(e){e="string"==typeof e?e:wo[e.keyCode];return"Ctrl"==e||"Alt"==e||"Shift"==e||"Mod"==e}function Oo(e,t,n){var r=e;return t.altKey&&"Alt"!=r&&(e="Alt-"+e),(b?t.metaKey:t.ctrlKey)&&"Ctrl"!=r&&(e="Ctrl-"+e),(b?t.ctrlKey:t.metaKey)&&"Mod"!=r&&(e="Cmd-"+e),!n&&t.shiftKey&&"Shift"!=r&&(e="Shift-"+e),e}function Ao(e,t){if(p&&34==e.keyCode&&e.char)return!1;var n=wo[e.keyCode];return null!=n&&!e.altGraphKey&&(3==e.keyCode&&e.code&&(n=e.code),Oo(n,e,t))}function Do(e){return"string"==typeof e?Lo[e]:e}function Wo(t,e){for(var n=t.doc.sel.ranges,r=[],i=0;i<n.length;i++){for(var o=e(n[i]);r.length&&it(o.from,Y(r).to)<=0;){var l=r.pop();if(it(l.from,o.from)<0){o.from=l.from;break}}r.push(o)}Pr(t,function(){for(var e=r.length-1;0<=e;e--)qi(t.doc,"",r[e].from,r[e].to,"+delete");wr(t)})}function Ho(e,t,n){n=re(e.text,t+n,n);return n<0||n>e.text.length?null:n}function Fo(e,t,n){e=Ho(e,t.ch,n);return null==e?null:new rt(t.line,e,n<0?"after":"before")}function Po(e,t,n,r,i){if(e){"rtl"==t.doc.direction&&(i=-i);var o=me(n,t.doc.direction);if(o){var l,s,a,e=i<0?Y(o):o[0],o=i<0==(1==e.level)?"after":"before";return 0<e.level||"rtl"==t.doc.direction?(l=Tn(t,n),s=i<0?n.text.length-1:0,a=Mn(t,l,s).top,s=ie(function(e){return Mn(t,l,e).top==a},i<0==(1==e.level)?e.from:e.to-1,s),"before"==o&&(s=Ho(n,s,1))):s=i<0?e.to:e.from,new rt(r,s,o)}}return new rt(r,i<0?n.text.length:0,i<0?"before":"after")}function Eo(t,n,s,e){var a=me(n,t.doc.direction);if(!a)return Fo(n,s,e);s.ch>=n.text.length?(s.ch=n.text.length,s.sticky="before"):s.ch<=0&&(s.ch=0,s.sticky="after");var r=le(a,s.ch,s.sticky),i=a[r];if("ltr"==t.doc.direction&&i.level%2==0&&(0<e?i.to>s.ch:i.from<s.ch))return Fo(n,s,e);function u(e,t){return Ho(n,e instanceof rt?e.ch:e,t)}function o(e){return t.options.lineWrapping?(l=l||Tn(t,n),jn(t,n,l,e)):{begin:0,end:n.text.length}}var l,c=o("before"==s.sticky?u(s,-1):s.ch);if("rtl"==t.doc.direction||1==i.level){var h=1==i.level==e<0,d=u(s,h?1:-1);if(null!=d&&(h?d<=i.to&&d<=c.end:d>=i.from&&d>=c.begin)){var f=h?"before":"after";return new rt(s.line,d,f)}}f=function(e,t,n){for(var r=function(e,t){return t?new rt(s.line,u(e,1),"before"):new rt(s.line,e,"after")};0<=e&&e<a.length;e+=t){var i=a[e],o=0<t==(1!=i.level),l=o?n.begin:u(n.end,-1);if(i.from<=l&&l<i.to)return r(l,o);if(l=o?i.from:u(i.to,-1),n.begin<=l&&l<n.end)return r(l,o)}},r=f(r+e,e,c);if(r)return r;c=0<e?c.end:u(c.begin,-1);return null==c||0<e&&c==n.text.length||!(r=f(0<e?0:a.length-1,e,o(c)))?null:r}Lo.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},Lo.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},Lo.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},Lo.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},Lo.default=g?Lo.macDefault:Lo.pcDefault;var Io={selectAll:Vi,singleSelection:function(e){return e.setSelection(e.getCursor("anchor"),e.getCursor("head"),G)},killLine:function(n){return Wo(n,function(e){if(e.empty()){var t=$e(n.doc,e.head.line).text.length;return e.head.ch==t&&e.head.line<n.lastLine()?{from:e.head,to:rt(e.head.line+1,0)}:{from:e.head,to:rt(e.head.line,t)}}return{from:e.from(),to:e.to()}})},deleteLine:function(t){return Wo(t,function(e){return{from:rt(e.from().line,0),to:ct(t.doc,rt(e.to().line+1,0))}})},delLineLeft:function(e){return Wo(e,function(e){return{from:rt(e.from().line,0),to:e.from()}})},delWrappedLineLeft:function(n){return Wo(n,function(e){var t=n.charCoords(e.head,"div").top+5;return{from:n.coordsChar({left:0,top:t},"div"),to:e.from()}})},delWrappedLineRight:function(n){return Wo(n,function(e){var t=n.charCoords(e.head,"div").top+5,t=n.coordsChar({left:n.display.lineDiv.offsetWidth+100,top:t},"div");return{from:e.from(),to:t}})},undo:function(e){return e.undo()},redo:function(e){return e.redo()},undoSelection:function(e){return e.undoSelection()},redoSelection:function(e){return e.redoSelection()},goDocStart:function(e){return e.extendSelection(rt(e.firstLine(),0))},goDocEnd:function(e){return e.extendSelection(rt(e.lastLine()))},goLineStart:function(t){return t.extendSelectionsBy(function(e){return Ro(t,e.head.line)},{origin:"+move",bias:1})},goLineStartSmart:function(t){return t.extendSelectionsBy(function(e){return zo(t,e.head)},{origin:"+move",bias:1})},goLineEnd:function(t){return t.extendSelectionsBy(function(e){return function(e,t){var n=$e(e.doc,t),r=function(e){for(var t;t=It(e);)e=t.find(1,!0).line;return e}(n);r!=n&&(t=Je(r));return Po(!0,e,n,t,-1)}(t,e.head.line)},{origin:"+move",bias:-1})},goLineRight:function(t){return t.extendSelectionsBy(function(e){e=t.cursorCoords(e.head,"div").top+5;return t.coordsChar({left:t.display.lineDiv.offsetWidth+100,top:e},"div")},V)},goLineLeft:function(t){return t.extendSelectionsBy(function(e){e=t.cursorCoords(e.head,"div").top+5;return t.coordsChar({left:0,top:e},"div")},V)},goLineLeftSmart:function(n){return n.extendSelectionsBy(function(e){var t=n.cursorCoords(e.head,"div").top+5,t=n.coordsChar({left:0,top:t},"div");return t.ch<n.getLine(t.line).search(/\S/)?zo(n,e.head):t},V)},goLineUp:function(e){return e.moveV(-1,"line")},goLineDown:function(e){return e.moveV(1,"line")},goPageUp:function(e){return e.moveV(-1,"page")},goPageDown:function(e){return e.moveV(1,"page")},goCharLeft:function(e){return e.moveH(-1,"char")},goCharRight:function(e){return e.moveH(1,"char")},goColumnLeft:function(e){return e.moveH(-1,"column")},goColumnRight:function(e){return e.moveH(1,"column")},goWordLeft:function(e){return e.moveH(-1,"word")},goGroupRight:function(e){return e.moveH(1,"group")},goGroupLeft:function(e){return e.moveH(-1,"group")},goWordRight:function(e){return e.moveH(1,"word")},delCharBefore:function(e){return e.deleteH(-1,"codepoint")},delCharAfter:function(e){return e.deleteH(1,"char")},delWordBefore:function(e){return e.deleteH(-1,"word")},delWordAfter:function(e){return e.deleteH(1,"word")},delGroupBefore:function(e){return e.deleteH(-1,"group")},delGroupAfter:function(e){return e.deleteH(1,"group")},indentAuto:function(e){return e.indentSelection("smart")},indentMore:function(e){return e.indentSelection("add")},indentLess:function(e){return e.indentSelection("subtract")},insertTab:function(e){return e.replaceSelection("\t")},insertSoftTab:function(e){for(var t=[],n=e.listSelections(),r=e.options.tabSize,i=0;i<n.length;i++){var o=n[i].from(),o=E(e.getLine(o.line),o.ch,r);t.push(X(r-o%r))}e.replaceSelections(t)},defaultTab:function(e){e.somethingSelected()?e.indentSelection("add"):e.execCommand("insertTab")},transposeChars:function(l){return Pr(l,function(){for(var e,t,n,r=l.listSelections(),i=[],o=0;o<r.length;o++)r[o].empty()&&(e=r[o].head,(t=$e(l.doc,e.line).text)&&(e.ch==t.length&&(e=new rt(e.line,e.ch-1)),0<e.ch?(e=new rt(e.line,e.ch+1),l.replaceRange(t.charAt(e.ch-1)+t.charAt(e.ch-2),rt(e.line,e.ch-2),e,"+transpose")):e.line>l.doc.first&&((n=$e(l.doc,e.line-1).text)&&(e=new rt(e.line,1),l.replaceRange(t.charAt(0)+l.doc.lineSeparator()+n.charAt(n.length-1),rt(e.line-1,n.length-1),e,"+transpose")))),i.push(new oi(e,e)));l.setSelections(i)})},newlineAndIndent:function(r){return Pr(r,function(){for(var e=r.listSelections(),t=e.length-1;0<=t;t--)r.replaceRange(r.doc.lineSeparator(),e[t].anchor,e[t].head,"+input");e=r.listSelections();for(var n=0;n<e.length;n++)r.indentLine(e[n].from().line,null,!0);wr(r)})},openLine:function(e){return e.replaceSelection("\n","start")},toggleOverwrite:function(e){return e.toggleOverwrite()}};function Ro(e,t){var n=$e(e.doc,t),r=zt(n);return r!=n&&(t=Je(r)),Po(!0,e,r,t,1)}function zo(e,t){var n=Ro(e,t.line),r=$e(e.doc,n.line),e=me(r,e.doc.direction);if(e&&0!=e[0].level)return n;r=Math.max(n.ch,r.text.search(/\S/)),t=t.line==n.line&&t.ch<=r&&t.ch;return rt(n.line,t?0:r,n.sticky)}function Bo(e,t,n){if("string"==typeof t&&!(t=Io[t]))return!1;e.display.input.ensurePolled();var r=e.display.shift,i=!1;try{e.isReadOnly()&&(e.state.suppressEdits=!0),n&&(e.display.shift=!1),i=t(e)!=B}finally{e.display.shift=r,e.state.suppressEdits=!1}return i}var Go=new I;function Uo(e,t,n,r){var i=e.state.keySeq;if(i){if(No(t))return"handled";if(/\'$/.test(t)?e.state.keySeq=null:Go.set(50,function(){e.state.keySeq==i&&(e.state.keySeq=null,e.display.input.reset())}),Vo(e,i+" "+t,n,r))return!0}return Vo(e,t,n,r)}function Vo(e,t,n,r){r=function(e,t,n){for(var r=0;r<e.state.keyMaps.length;r++){var i=Mo(t,e.state.keyMaps[r],n,e);if(i)return i}return e.options.extraKeys&&Mo(t,e.options.extraKeys,n,e)||Mo(t,e.options.keyMap,n,e)}(e,t,r);return"multi"==r&&(e.state.keySeq=t),"handled"==r&&ln(e,"keyHandled",e,t,n),"handled"!=r&&"multi"!=r||(Te(n),cr(e)),!!r}function Ko(t,e){var n=Ao(e,!0);return!!n&&(e.shiftKey&&!t.state.keySeq?Uo(t,"Shift-"+n,e,function(e){return Bo(t,e,!0)})||Uo(t,n,e,function(e){if("string"==typeof e?/^go[A-Z]/.test(e):e.motion)return Bo(t,e)}):Uo(t,n,e,function(e){return Bo(t,e)}))}var jo=null;function Xo(e){var t,n,r,i=this;function o(e){18!=e.keyCode&&e.altKey||(L(r,"CodeMirror-crosshair"),we(document,"keyup",o),we(document,"mouseover",o))}e.target&&e.target!=i.display.input.getField()||(i.curOp.focus=A(),Ce(i,e)||(w&&v<11&&27==e.keyCode&&(e.returnValue=!1),t=e.keyCode,i.display.shift=16==t||e.shiftKey,n=Ko(i,e),p&&(jo=n?t:null,!n&&88==t&&!Ie&&(g?e.metaKey:e.ctrlKey)&&i.replaceSelection("",null,"cut")),d&&!g&&!n&&46==t&&e.shiftKey&&!e.ctrlKey&&document.execCommand&&document.execCommand("cut"),18!=t||/\bCodeMirror-crosshair\b/.test(i.display.lineDiv.className)||(D(r=i.display.lineDiv,"CodeMirror-crosshair"),ye(document,"keyup",o),ye(document,"mouseover",o))))}function Yo(e){16==e.keyCode&&(this.doc.sel.shift=!1),Ce(this,e)}function _o(e){var t=this;if(!(e.target&&e.target!=t.display.input.getField()||mn(t.display,e)||Ce(t,e)||e.ctrlKey&&!e.altKey||g&&e.metaKey)){var n,r=e.keyCode,i=e.charCode;if(p&&r==jo)return jo=null,void Te(e);p&&(!e.which||e.which<10)&&Ko(t,e)||"\b"!=(i=String.fromCharCode(null==i?r:i))&&(Uo(n=t,"'"+i+"'",e,function(e){return Bo(n,e,!0)})||t.display.input.onKeyPress(e))}}var $o,qo,Zo=function(e,t,n){this.time=e,this.pos=t,this.button=n};function Qo(e){var t,n,r,i,o,l=this,s=l.display;Ce(l,e)||s.activeTouch&&s.input.supportsTouch()||(s.input.ensurePolled(),s.shift=e.shiftKey,mn(s,e)?f||(s.scroller.draggable=!1,setTimeout(function(){return s.scroller.draggable=!0},100)):tl(l,e)||(t=Jn(l,e),n=De(e),i=t?(r=t,i=n,o=+new Date,qo&&qo.compare(o,r,i)?($o=qo=null,"triple"):$o&&$o.compare(o,r,i)?(qo=new Zo(o,r,i),$o=null,"double"):($o=new Zo(o,r,i),qo=null,"single")):"single",window.focus(),1==n&&l.state.selectingText&&l.state.selectingText(e),t&&function(n,e,r,t,i){var o="Click";"double"==t?o="Double"+o:"triple"==t&&(o="Triple"+o);return Uo(n,Oo(o=(1==e?"Left":2==e?"Middle":"Right")+o,i),i,function(e){if("string"==typeof e&&(e=Io[e]),!e)return!1;var t=!1;try{n.isReadOnly()&&(n.state.suppressEdits=!0),t=e(n,r)!=B}finally{n.state.suppressEdits=!1}return t})}(l,n,t,i,e)||(1==n?t?function(e,t,n,r){w?setTimeout(F(hr,e),0):e.curOp.focus=A();var i,o=function(e,t,n){var r=e.getOption("configureMouse"),i=r?r(e,t,n):{};null==i.unit&&(r=m?n.shiftKey&&n.metaKey:n.altKey,i.unit=r?"rectangle":"single"==t?"char":"double"==t?"word":"line");null!=i.extend&&!e.doc.extend||(i.extend=e.doc.extend||n.shiftKey);null==i.addNew&&(i.addNew=g?n.metaKey:n.ctrlKey);null==i.moveOnDrag&&(i.moveOnDrag=!(g?n.altKey:n.ctrlKey));return i}(e,n,r),l=e.doc.sel;(e.options.dragDrop&&Fe&&!e.isReadOnly()&&"single"==n&&-1<(i=l.contains(t))&&(it((i=l.ranges[i]).from(),t)<0||0<t.xRel)&&(0<it(i.to(),t)||t.xRel<0)?function(t,n,r,i){var o=t.display,l=!1,s=Er(t,function(e){f&&(o.scroller.draggable=!1),t.state.draggingText=!1,t.state.delayingBlurEvent&&(t.hasFocus()?t.state.delayingBlurEvent=!1:dr(t)),we(o.wrapper.ownerDocument,"mouseup",s),we(o.wrapper.ownerDocument,"mousemove",a),we(o.scroller,"dragstart",u),we(o.scroller,"drop",s),l||(Te(e),i.addNew||Ai(t.doc,r,null,null,i.extend),f&&!c||w&&9==v?setTimeout(function(){o.wrapper.ownerDocument.body.focus({preventScroll:!0}),o.input.focus()},20):o.input.focus())}),a=function(e){l=l||10<=Math.abs(n.clientX-e.clientX)+Math.abs(n.clientY-e.clientY)},u=function(){return l=!0};f&&(o.scroller.draggable=!0);(t.state.draggingText=s).copy=!i.moveOnDrag,ye(o.wrapper.ownerDocument,"mouseup",s),ye(o.wrapper.ownerDocument,"mousemove",a),ye(o.scroller,"dragstart",u),ye(o.scroller,"drop",s),t.state.delayingBlurEvent=!0,setTimeout(function(){return o.input.focus()},20),o.scroller.dragDrop&&o.scroller.dragDrop()}:function(d,e,f,p){w&&dr(d);var l=d.display,g=d.doc;Te(e);var m,v,y=g.sel,t=y.ranges;p.addNew&&!p.extend?(v=g.sel.contains(f),m=-1<v?t[v]:new oi(f,f)):(m=g.sel.primary(),v=g.sel.primIndex);"rectangle"==p.unit?(p.addNew||(m=new oi(f,f)),f=Jn(d,e,!0,!0),v=-1):(e=Jo(d,f,p.unit),m=p.extend?Oi(m,e.anchor,e.head,p.extend):e);p.addNew?-1==v?(v=t.length,Pi(g,li(d,t.concat([m]),v),{scroll:!1,origin:"*mouse"})):1<t.length&&t[v].empty()&&"char"==p.unit&&!p.extend?(Pi(g,li(d,t.slice(0,v).concat(t.slice(v+1)),0),{scroll:!1,origin:"*mouse"}),y=g.sel):Wi(g,v,m,U):(Pi(g,new ii([m],v=0),U),y=g.sel);var b=f;function s(e){if(0!=it(b,e))if(b=e,"rectangle"==p.unit){for(var t=[],n=d.options.tabSize,r=E($e(g,f.line).text,f.ch,n),i=E($e(g,e.line).text,e.ch,n),o=Math.min(r,i),l=Math.max(r,i),s=Math.min(f.line,e.line),a=Math.min(d.lastLine(),Math.max(f.line,e.line));s<=a;s++){var u=$e(g,s).text,c=K(u,o,n);o==l?t.push(new oi(rt(s,c),rt(s,c))):u.length>c&&t.push(new oi(rt(s,c),rt(s,K(u,l,n))))}t.length||t.push(new oi(f,f)),Pi(g,li(d,y.ranges.slice(0,v).concat(t),v),{origin:"*mouse",scroll:!1}),d.scrollIntoView(e)}else{var h,r=m,i=Jo(d,e,p.unit),e=r.anchor,e=0<it(i.anchor,e)?(h=i.head,at(r.from(),i.anchor)):(h=i.anchor,st(r.to(),i.head)),i=y.ranges.slice(0);i[v]=function(e,t){var n=t.anchor,r=t.head,i=$e(e.doc,n.line);if(0==it(n,r)&&n.sticky==r.sticky)return t;var o=me(i);if(!o)return t;var l=le(o,n.ch,n.sticky),s=o[l];if(s.from!=n.ch&&s.to!=n.ch)return t;i=l+(s.from==n.ch==(1!=s.level)?0:1);if(0==i||i==o.length)return t;a=r.line!=n.line?0<(r.line-n.line)*("ltr"==e.doc.direction?1:-1):(e=le(o,r.ch,r.sticky),a=e-l||(r.ch-n.ch)*(1==s.level?-1:1),e==i-1||e==i?a<0:0<a);var i=o[i+(a?-1:0)],a=a==(1==i.level),i=a?i.from:i.to,a=a?"after":"before";return n.ch==i&&n.sticky==a?t:new oi(new rt(n.line,i,a),r)}(d,new oi(ct(g,e),h)),Pi(g,li(d,i,v),U)}}var a=l.wrapper.getBoundingClientRect(),u=0;function n(e){d.state.selectingText=!1,u=1/0,e&&(Te(e),l.input.focus()),we(l.wrapper.ownerDocument,"mousemove",r),we(l.wrapper.ownerDocument,"mouseup",i),g.history.lastSelOrigin=null}var r=Er(d,function(e){(0!==e.buttons&&De(e)?function e(t){var n,r,i=++u,o=Jn(d,t,!0,"rectangle"==p.unit);o&&(0!=it(o,b)?(d.curOp.focus=A(),s(o),n=vr(l,g),(o.line>=n.to||o.line<n.from)&&setTimeout(Er(d,function(){u==i&&e(t)}),150)):(r=t.clientY<a.top?-20:t.clientY>a.bottom?20:0)&&setTimeout(Er(d,function(){u==i&&(l.scroller.scrollTop+=r,e(t))}),50))}:n)(e)}),i=Er(d,n);d.state.selectingText=i,ye(l.wrapper.ownerDocument,"mousemove",r),ye(l.wrapper.ownerDocument,"mouseup",i)})(e,r,t,o)}(l,t,i,e):Ae(e)==s.scroller&&Te(e):2==n?(t&&Ai(l.doc,t),setTimeout(function(){return s.input.focus()},20)):3==n&&(x?l.display.input.onContextMenu(e):dr(l)))))}function Jo(e,t,n){if("char"==n)return new oi(t,t);if("word"==n)return e.findWordAt(t);if("line"==n)return new oi(rt(t.line,0),ct(e.doc,rt(t.line+1,0)));t=n(e,t);return new oi(t.from,t.to)}function el(e,t,n,r){var i,o;if(t.touches)i=t.touches[0].clientX,o=t.touches[0].clientY;else try{i=t.clientX,o=t.clientY}catch(e){return!1}if(i>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;r&&Te(t);var l=e.display,r=l.lineDiv.getBoundingClientRect();if(o>r.bottom||!Le(e,n))return Ne(t);o-=r.top-l.viewOffset;for(var s=0;s<e.display.gutterSpecs.length;++s){var a=l.gutters.childNodes[s];if(a&&a.getBoundingClientRect().right>=i)return xe(e,n,e,et(e.doc,o),e.display.gutterSpecs[s].className,t),Ne(t)}}function tl(e,t){return el(e,t,"gutterClick",!0)}function nl(e,t){var n,r;mn(e.display,t)||(r=t,Le(n=e,"gutterContextMenu")&&el(n,r,"gutterContextMenu",!1))||Ce(e,t,"contextmenu")||x||e.display.input.onContextMenu(t)}function rl(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+e.options.theme.replace(/(^|\s)\s*/g," cm-s-"),Hn(e)}Zo.prototype.compare=function(e,t,n){return this.time+400>e&&0==it(t,this.pos)&&n==this.button};var il={toString:function(){return"CodeMirror.Init"}},ol={},ll={};function sl(e,t,n){!t!=!(n&&n!=il)&&(n=e.display.dragFunctions,(t=t?ye:we)(e.display.scroller,"dragstart",n.start),t(e.display.scroller,"dragenter",n.enter),t(e.display.scroller,"dragover",n.over),t(e.display.scroller,"dragleave",n.leave),t(e.display.scroller,"drop",n.drop))}function al(e){e.options.lineWrapping?(D(e.display.wrapper,"CodeMirror-wrap"),e.display.sizer.style.minWidth="",e.display.sizerWidth=null):(L(e.display.wrapper,"CodeMirror-wrap"),jt(e)),Qn(e),tr(e),Hn(e),setTimeout(function(){return Nr(e)},100)}function ul(e,t){var n=this;if(!(this instanceof ul))return new ul(e,t);this.options=t=t?P(t):{},P(ol,t,!1);var r=t.value;"string"==typeof r?r=new ho(r,t.mode,null,t.lineSeparator,t.direction):t.mode&&(r.modeOption=t.mode),this.doc=r;var i,o=new ul.inputStyles[t.inputStyle](this),o=this.display=new Qr(e,r,o,t);for(i in rl(o.wrapper.CodeMirror=this),t.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),Dr(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new I,keySeq:null,specialChars:null},t.autofocus&&!h&&o.input.focus(),w&&v<11&&setTimeout(function(){return n.display.input.reset(!0)},20),function(r){var i=r.display;ye(i.scroller,"mousedown",Er(r,Qo)),ye(i.scroller,"dblclick",w&&v<11?Er(r,function(e){var t;Ce(r,e)||(!(t=Jn(r,e))||tl(r,e)||mn(r.display,e)||(Te(e),t=r.findWordAt(t),Ai(r.doc,t.anchor,t.head)))}):function(e){return Ce(r,e)||Te(e)});ye(i.scroller,"contextmenu",function(e){return nl(r,e)}),ye(i.input.getField(),"contextmenu",function(e){i.scroller.contains(e.target)||nl(r,e)});var n,o={end:0};function l(){i.activeTouch&&(n=setTimeout(function(){return i.activeTouch=null},1e3),(o=i.activeTouch).end=+new Date)}function s(e,t){if(null==t.left)return 1;var n=t.left-e.left,e=t.top-e.top;return 400<n*n+e*e}ye(i.scroller,"touchstart",function(e){var t;Ce(r,e)||function(e){if(1==e.touches.length){e=e.touches[0];return e.radiusX<=1&&e.radiusY<=1}}(e)||tl(r,e)||(i.input.ensurePolled(),clearTimeout(n),t=+new Date,i.activeTouch={start:t,moved:!1,prev:t-o.end<=300?o:null},1==e.touches.length&&(i.activeTouch.left=e.touches[0].pageX,i.activeTouch.top=e.touches[0].pageY))}),ye(i.scroller,"touchmove",function(){i.activeTouch&&(i.activeTouch.moved=!0)}),ye(i.scroller,"touchend",function(e){var t,n=i.activeTouch;n&&!mn(i,e)&&null!=n.left&&!n.moved&&new Date-n.start<300&&(t=r.coordsChar(i.activeTouch,"page"),t=!n.prev||s(n,n.prev)?new oi(t,t):!n.prev.prev||s(n,n.prev.prev)?r.findWordAt(t):new oi(rt(t.line,0),ct(r.doc,rt(t.line+1,0))),r.setSelection(t.anchor,t.head),r.focus(),Te(e)),l()}),ye(i.scroller,"touchcancel",l),ye(i.scroller,"scroll",function(){i.scroller.clientHeight&&(Lr(r,i.scroller.scrollTop),Tr(r,i.scroller.scrollLeft,!0),xe(r,"scroll",r))}),ye(i.scroller,"mousewheel",function(e){return ri(r,e)}),ye(i.scroller,"DOMMouseScroll",function(e){return ri(r,e)}),ye(i.wrapper,"scroll",function(){return i.wrapper.scrollTop=i.wrapper.scrollLeft=0}),i.dragFunctions={enter:function(e){Ce(r,e)||Oe(e)},over:function(e){var t,n;Ce(r,e)||((n=Jn(t=r,n=e))&&(ar(t,n,n=document.createDocumentFragment()),t.display.dragCursor||(t.display.dragCursor=M("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),t.display.lineSpace.insertBefore(t.display.dragCursor,t.display.cursorDiv)),T(t.display.dragCursor,n)),Oe(e))},start:function(e){return t=r,n=e,void(w&&(!t.state.draggingText||+new Date-fo<100)?Oe(n):Ce(t,n)||mn(t.display,n)||(n.dataTransfer.setData("Text",t.getSelection()),n.dataTransfer.effectAllowed="copyMove",n.dataTransfer.setDragImage&&!c&&((e=M("img",null,null,"position: fixed; left: 0; top: 0;")).src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",p&&(e.width=e.height=1,t.display.wrapper.appendChild(e),e._top=e.offsetTop),n.dataTransfer.setDragImage(e,0,0),p&&e.parentNode.removeChild(e))));var t,n},drop:Er(r,po),leave:function(e){Ce(r,e)||go(r)}};var e=i.input.getField();ye(e,"keyup",function(e){return Yo.call(r,e)}),ye(e,"keydown",Er(r,Xo)),ye(e,"keypress",Er(r,_o)),ye(e,"focus",function(e){return fr(r,e)}),ye(e,"blur",function(e){return pr(r,e)})}(this),yo(),Hr(this),this.curOp.forceUpdate=!0,vi(this,r),t.autofocus&&!h||this.hasFocus()?setTimeout(function(){n.hasFocus()&&!n.state.focused&&fr(n)},20):pr(this),ll)ll.hasOwnProperty(i)&&ll[i](this,t[i],il);_r(this),t.finishInit&&t.finishInit(this);for(var l=0;l<cl.length;++l)cl[l](this);Fr(this),f&&t.lineWrapping&&"optimizelegibility"==getComputedStyle(o.lineDiv).textRendering&&(o.lineDiv.style.textRendering="auto")}ul.defaults=ol,ul.optionHandlers=ll;var cl=[];function hl(e,t,n,r){var i,o=e.doc;null==n&&(n="add"),"smart"==n&&(o.mode.indent?i=mt(e,t).state:n="prev");var l=e.options.tabSize,s=$e(o,t),a=E(s.text,null,l);s.stateAfter&&(s.stateAfter=null);var u,c=s.text.match(/^\s*/)[0];if(r||/\S/.test(s.text)){if("smart"==n&&((u=o.mode.indent(i,s.text.slice(c.length),s.text))==B||150<u)){if(!r)return;n="prev"}}else u=0,n="not";"prev"==n?u=t>o.first?E($e(o,t-1).text,null,l):0:"add"==n?u=a+e.options.indentUnit:"subtract"==n?u=a-e.options.indentUnit:"number"==typeof n&&(u=a+n),u=Math.max(0,u);var h="",d=0;if(e.options.indentWithTabs)for(var f=Math.floor(u/l);f;--f)d+=l,h+="\t";if(d<u&&(h+=X(u-d)),h!=c)return qi(o,h,rt(t,0),rt(t,c.length),"+input"),!(s.stateAfter=null);for(var p=0;p<o.sel.ranges.length;p++){var g=o.sel.ranges[p];if(g.head.line==t&&g.head.ch<c.length){g=rt(t,c.length);Wi(o,p,new oi(g,g));break}}}ul.defineInitHook=function(e){return cl.push(e)};var dl=null;function fl(e){dl=e}function pl(e,t,n,r,i){var o=e.doc;e.display.shift=!1,r=r||o.sel;var l=+new Date-200,s="paste"==i||e.state.pasteIncoming>l,a=Pe(t),u=null;if(s&&1<r.ranges.length)if(dl&&dl.text.join("\n")==t){if(r.ranges.length%dl.text.length==0){u=[];for(var c=0;c<dl.text.length;c++)u.push(o.splitLines(dl.text[c]))}}else a.length==r.ranges.length&&e.options.pasteLinesPerSelection&&(u=_(a,function(e){return[e]}));for(var h=e.curOp.updateInput,d=r.ranges.length-1;0<=d;d--){var f=r.ranges[d],p=f.from(),g=f.to();f.empty()&&(n&&0<n?p=rt(p.line,p.ch-n):e.state.overwrite&&!s?g=rt(g.line,Math.min($e(o,g.line).text.length,g.ch+Y(a).length)):s&&dl&&dl.lineWise&&dl.text.join("\n")==a.join("\n")&&(p=g=rt(p.line,0)));g={from:p,to:g,text:u?u[d%u.length]:a,origin:i||(s?"paste":e.state.cutIncoming>l?"cut":"+input")};ji(e.doc,g),ln(e,"inputRead",e,g)}t&&!s&&ml(e,t),wr(e),e.curOp.updateInput<2&&(e.curOp.updateInput=h),e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=-1}function gl(e,t){var n=e.clipboardData&&e.clipboardData.getData("Text");return n&&(e.preventDefault(),t.isReadOnly()||t.options.disableInput||Pr(t,function(){return pl(t,n,0,null,"paste")}),1)}function ml(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var n=e.doc.sel,r=n.ranges.length-1;0<=r;r--){var i=n.ranges[r];if(!(100<i.head.ch||r&&n.ranges[r-1].head.line==i.head.line)){var o=e.getModeAt(i.head),l=!1;if(o.electricChars){for(var s=0;s<o.electricChars.length;s++)if(-1<t.indexOf(o.electricChars.charAt(s))){l=hl(e,i.head.line,"smart");break}}else o.electricInput&&o.electricInput.test($e(e.doc,i.head.line).text.slice(0,i.head.ch))&&(l=hl(e,i.head.line,"smart"));l&&ln(e,"electricInput",e,i.head.line)}}}function vl(e){for(var t=[],n=[],r=0;r<e.doc.sel.ranges.length;r++){var i=e.doc.sel.ranges[r].head.line,i={anchor:rt(i,0),head:rt(i+1,0)};n.push(i),t.push(e.getRange(i.anchor,i.head))}return{text:t,ranges:n}}function yl(e,t,n,r){e.setAttribute("autocorrect",n?"":"off"),e.setAttribute("autocapitalize",r?"":"off"),e.setAttribute("spellcheck",!!t)}function bl(){var e=M("textarea",null,null,"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"),t=M("div",[e],null,"overflow: hidden; position: relative; width: 3px; height: 0px;");return f?e.style.width="1000px":e.setAttribute("wrap","off"),s&&(e.style.border="1px solid black"),yl(e),t}function wl(i,o,l,s,a){var e=o,t=l,u=$e(i,o.line),c=a&&"rtl"==i.direction?-l:l;function n(e){var t,n,r;if(null==(n="codepoint"==s?(t=u.text.charCodeAt(o.ch+(0<l?0:-1)),isNaN(t)?null:(n=0<l?55296<=t&&t<56320:56320<=t&&t<57343,new rt(o.line,Math.max(0,Math.min(u.text.length,o.ch+l*(n?2:1))),-l))):a?Eo(i.cm,u,o,l):Fo(u,o,l))){if(e||(r=o.line+c)<i.first||r>=i.first+i.size||(o=new rt(r,o.ch,o.sticky),!(u=$e(i,r))))return;o=Po(a,i.cm,u,o.line,c)}else o=n;return 1}if("char"==s||"codepoint"==s)n();else if("column"==s)n(!0);else if("word"==s||"group"==s)for(var r=null,h="group"==s,d=i.cm&&i.cm.getHelper(o,"wordChars"),f=!0;!(l<0)||n(!f);f=!1){var p=u.text.charAt(o.ch)||"\n",p=J(p,d)?"w":h&&"\n"==p?"n":!h||/\s/.test(p)?null:"p";if(!h||f||p||(p="s"),r&&r!=p){l<0&&(l=1,n(),o.sticky="after");break}if(p&&(r=p),0<l&&!n(!f))break}t=Gi(i,o,e,t,!0);return ot(e,t)&&(t.hitSide=!0),t}function xl(e,t,n,r){var i,o,l,s=e.doc,a=t.left;for("page"==r?(i=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight),i=Math.max(i-.5*Yn(e.display),3),o=(0<n?t.bottom:t.top)+n*i):"line"==r&&(o=0<n?t.bottom+3:t.top-3);(l=Vn(e,a,o)).outside;){if(n<0?o<=0:o>=s.height){l.hitSide=!0;break}o+=5*n}return l}e=function(e){this.cm=e,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new I,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};function Cl(e,t){var n=kn(e,t.line);if(!n||n.hidden)return null;var r=$e(e.doc,t.line),n=Sn(n,r,t.line),r=me(r,e.doc.direction),e="left";r&&(e=le(r,t.ch)%2?"right":"left");e=An(n.map,t.ch,e);return e.offset="right"==e.collapse?e.end:e.start,e}function Sl(e,t){return t&&(e.bad=!0),e}function Ll(e,t,n){var r;if(t==e.display.lineDiv){if(!(r=e.display.lineDiv.childNodes[n]))return Sl(e.clipPos(rt(e.display.viewTo-1)),!0);t=null,n=0}else for(r=t;;r=r.parentNode){if(!r||r==e.display.lineDiv)return null;if(r.parentNode&&r.parentNode==e.display.lineDiv)break}for(var i=0;i<e.display.view.length;i++){var o=e.display.view[i];if(o.node==r)return function(u,e,t){var n=u.text.firstChild,r=!1;if(!e||!O(n,e))return Sl(rt(Je(u.line),0),!0);if(e==n&&(r=!0,e=n.childNodes[t],t=0,!e)){var i=u.rest?Y(u.rest):u.line;return Sl(rt(Je(i),i.text.length),r)}var i=3==e.nodeType?e:null,o=e;i||1!=e.childNodes.length||3!=e.firstChild.nodeType||(i=e.firstChild,t=t&&i.nodeValue.length);for(;o.parentNode!=n;)o=o.parentNode;var c=u.measure,h=c.maps;function l(e,t,n){for(var r=-1;r<(h?h.length:0);r++)for(var i=r<0?c.map:h[r],o=0;o<i.length;o+=3){var l=i[o+2];if(l==e||l==t){var s=Je(r<0?u.line:u.rest[r]),a=i[o]+n;return(n<0||l!=e)&&(a=i[o+(n?1:0)]),rt(s,a)}}}var s=l(i,o,t);if(s)return Sl(s,r);for(var a=o.nextSibling,d=i?i.nodeValue.length-t:0;a;a=a.nextSibling){if(s=l(a,a.firstChild,0))return Sl(rt(s.line,s.ch-d),r);d+=a.textContent.length}for(var f=o.previousSibling,p=t;f;f=f.previousSibling){if(s=l(f,f.firstChild,-1))return Sl(rt(s.line,s.ch+p),r);p+=f.textContent.length}}(o,t,n)}}e.prototype.init=function(e){var t=this,o=this,l=o.cm,s=o.div=e.lineDiv;function a(e){for(var t=e.target;t;t=t.parentNode){if(t==s)return 1;if(/\bCodeMirror-(?:line)?widget\b/.test(t.className))break}}function n(e){if(a(e)&&!Ce(l,e)){if(l.somethingSelected())fl({lineWise:!1,text:l.getSelections()}),"cut"==e.type&&l.replaceSelection("",null,"cut");else{if(!l.options.lineWiseCopyCut)return;var t=vl(l);fl({lineWise:!0,text:t.text}),"cut"==e.type&&l.operation(function(){l.setSelections(t.ranges,0,G),l.replaceSelection("",null,"cut")})}if(e.clipboardData){e.clipboardData.clearData();var n=dl.text.join("\n");if(e.clipboardData.setData("Text",n),e.clipboardData.getData("Text")==n)return void e.preventDefault()}var r=bl(),e=r.firstChild;l.display.lineSpace.insertBefore(r,l.display.lineSpace.firstChild),e.value=dl.text.join("\n");var i=document.activeElement;H(e),setTimeout(function(){l.display.lineSpace.removeChild(r),i.focus(),i==s&&o.showPrimarySelection()},50)}}yl(s,l.options.spellcheck,l.options.autocorrect,l.options.autocapitalize),ye(s,"paste",function(e){!a(e)||Ce(l,e)||gl(e,l)||v<=11&&setTimeout(Er(l,function(){return t.updateFromDOM()}),20)}),ye(s,"compositionstart",function(e){t.composing={data:e.data,done:!1}}),ye(s,"compositionupdate",function(e){t.composing||(t.composing={data:e.data,done:!1})}),ye(s,"compositionend",function(e){t.composing&&(e.data!=t.composing.data&&t.readFromDOMSoon(),t.composing.done=!0)}),ye(s,"touchstart",function(){return o.forceCompositionEnd()}),ye(s,"input",function(){t.composing||t.readFromDOMSoon()}),ye(s,"copy",n),ye(s,"cut",n)},e.prototype.screenReaderLabelChanged=function(e){e?this.div.setAttribute("aria-label",e):this.div.removeAttribute("aria-label")},e.prototype.prepareSelection=function(){var e=sr(this.cm,!1);return e.focus=document.activeElement==this.div,e},e.prototype.showSelection=function(e,t){e&&this.cm.display.view.length&&((e.focus||t)&&this.showPrimarySelection(),this.showMultipleSelections(e))},e.prototype.getSelection=function(){return this.cm.display.wrapper.ownerDocument.getSelection()},e.prototype.showPrimarySelection=function(){var e=this.getSelection(),t=this.cm,n=t.doc.sel.primary(),r=n.from(),i=n.to();if(t.display.viewTo==t.display.viewFrom||r.line>=t.display.viewTo||i.line<t.display.viewFrom)e.removeAllRanges();else{var o=Ll(t,e.anchorNode,e.anchorOffset),n=Ll(t,e.focusNode,e.focusOffset);if(!o||o.bad||!n||n.bad||0!=it(at(o,n),r)||0!=it(st(o,n),i)){var n=t.display.view,l=r.line>=t.display.viewFrom&&Cl(t,r)||{node:n[0].measure.map[2],offset:0},s=i.line<t.display.viewTo&&Cl(t,i);if(s||(s={node:(u=(u=n[n.length-1].measure).maps?u.maps[u.maps.length-1]:u.map)[u.length-1],offset:u[u.length-2]-u[u.length-3]}),l&&s){var a,u=e.rangeCount&&e.getRangeAt(0);try{a=S(l.node,l.offset,s.offset,s.node)}catch(e){}a&&(!d&&t.state.focused?(e.collapse(l.node,l.offset),a.collapsed||(e.removeAllRanges(),e.addRange(a))):(e.removeAllRanges(),e.addRange(a)),u&&null==e.anchorNode?e.addRange(u):d&&this.startGracePeriod()),this.rememberSelection()}else e.removeAllRanges()}}},e.prototype.startGracePeriod=function(){var e=this;clearTimeout(this.gracePeriod),this.gracePeriod=setTimeout(function(){e.gracePeriod=!1,e.selectionChanged()&&e.cm.operation(function(){return e.cm.curOp.selectionChanged=!0})},20)},e.prototype.showMultipleSelections=function(e){T(this.cm.display.cursorDiv,e.cursors),T(this.cm.display.selectionDiv,e.selection)},e.prototype.rememberSelection=function(){var e=this.getSelection();this.lastAnchorNode=e.anchorNode,this.lastAnchorOffset=e.anchorOffset,this.lastFocusNode=e.focusNode,this.lastFocusOffset=e.focusOffset},e.prototype.selectionInEditor=function(){var e=this.getSelection();if(!e.rangeCount)return!1;e=e.getRangeAt(0).commonAncestorContainer;return O(this.div,e)},e.prototype.focus=function(){"nocursor"!=this.cm.options.readOnly&&(this.selectionInEditor()&&document.activeElement==this.div||this.showSelection(this.prepareSelection(),!0),this.div.focus())},e.prototype.blur=function(){this.div.blur()},e.prototype.getField=function(){return this.div},e.prototype.supportsTouch=function(){return!0},e.prototype.receivedFocus=function(){var t=this;this.selectionInEditor()?this.pollSelection():Pr(this.cm,function(){return t.cm.curOp.selectionChanged=!0}),this.polling.set(this.cm.options.pollInterval,function e(){t.cm.state.focused&&(t.pollSelection(),t.polling.set(t.cm.options.pollInterval,e))})},e.prototype.selectionChanged=function(){var e=this.getSelection();return e.anchorNode!=this.lastAnchorNode||e.anchorOffset!=this.lastAnchorOffset||e.focusNode!=this.lastFocusNode||e.focusOffset!=this.lastFocusOffset},e.prototype.pollSelection=function(){if(null==this.readDOMTimeout&&!this.gracePeriod&&this.selectionChanged()){var e,t,n=this.getSelection(),r=this.cm;if(a&&o&&this.cm.display.gutterSpecs.length&&function(e){for(var t=e;t;t=t.parentNode)if(/CodeMirror-gutter-wrapper/.test(t.className))return!0;return!1}(n.anchorNode))return this.cm.triggerOnKeyDown({type:"keydown",keyCode:8,preventDefault:Math.abs}),this.blur(),void this.focus();this.composing||(this.rememberSelection(),e=Ll(r,n.anchorNode,n.anchorOffset),t=Ll(r,n.focusNode,n.focusOffset),e&&t&&Pr(r,function(){Pi(r.doc,si(e,t),G),(e.bad||t.bad)&&(r.curOp.selectionChanged=!0)}))}},e.prototype.pollContent=function(){null!=this.readDOMTimeout&&(clearTimeout(this.readDOMTimeout),this.readDOMTimeout=null);var e,t=this.cm,n=t.display,r=t.doc.sel.primary(),i=r.from(),r=r.to();if(0==i.ch&&i.line>t.firstLine()&&(i=rt(i.line-1,$e(t.doc,i.line-1).length)),r.ch==$e(t.doc,r.line).text.length&&r.line<t.lastLine()&&(r=rt(r.line+1,0)),i.line<n.viewFrom||r.line>n.viewTo-1)return!1;m=i.line==n.viewFrom||0==(m=er(t,i.line))?(e=Je(n.view[0].line),n.view[0].node):(e=Je(n.view[m].line),n.view[m-1].node.nextSibling);var o,r=er(t,r.line),r=r==n.view.length-1?(o=n.viewTo-1,n.lineDiv.lastChild):(o=Je(n.view[r+1].line)-1,n.view[r+1].node.previousSibling);if(!m)return!1;for(var l=t.doc.splitLines(function(l,e,t,s,a){var n="",u=!1,c=l.doc.lineSeparator(),h=!1;function d(){u&&(n+=c,h&&(n+=c),u=h=!1)}function f(e){e&&(d(),n+=e)}for(;!function e(t){if(1==t.nodeType){var n=t.getAttribute("cm-text");if(n)f(n);else if(n=t.getAttribute("cm-marker"))(n=l.findMarks(rt(s,0),rt(a+1,0),(o=+n,function(e){return e.id==o}))).length&&(r=n[0].find(0))&&f(qe(l.doc,r.from,r.to).join(c));else if("false"!=t.getAttribute("contenteditable")){var r=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(/^br$/i.test(t.nodeName)||0!=t.textContent.length){r&&d();for(var i=0;i<t.childNodes.length;i++)e(t.childNodes[i]);/^(pre|p)$/i.test(t.nodeName)&&(h=!0),r&&(u=!0)}}}else 3==t.nodeType&&f(t.nodeValue.replace(/\u200b/g,"").replace(/\u00a0/g," "));var o}(e),e!=t;)e=e.nextSibling,h=!1;return n}(t,m,r,e,o)),s=qe(t.doc,rt(e,0),rt(o,$e(t.doc,o).text.length));1<l.length&&1<s.length;)if(Y(l)==Y(s))l.pop(),s.pop(),o--;else{if(l[0]!=s[0])break;l.shift(),s.shift(),e++}for(var a=0,u=0,c=l[0],h=s[0],d=Math.min(c.length,h.length);a<d&&c.charCodeAt(a)==h.charCodeAt(a);)++a;for(var f=Y(l),p=Y(s),g=Math.min(f.length-(1==l.length?a:0),p.length-(1==s.length?a:0));u<g&&f.charCodeAt(f.length-u-1)==p.charCodeAt(p.length-u-1);)++u;if(1==l.length&&1==s.length&&e==i.line)for(;a&&a>i.ch&&f.charCodeAt(f.length-u-1)==p.charCodeAt(p.length-u-1);)a--,u++;l[l.length-1]=f.slice(0,f.length-u).replace(/^\u200b+/,""),l[0]=l[0].slice(a).replace(/\u200b+$/,"");var m=rt(e,a),r=rt(o,s.length?Y(s).length-u:0);return 1<l.length||l[0]||it(m,r)?(qi(t.doc,l,m,r,"+input"),!0):void 0},e.prototype.ensurePolled=function(){this.forceCompositionEnd()},e.prototype.reset=function(){this.forceCompositionEnd()},e.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},e.prototype.readFromDOMSoon=function(){var e=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout(function(){if(e.readDOMTimeout=null,e.composing){if(!e.composing.done)return;e.composing=null}e.updateFromDOM()},80))},e.prototype.updateFromDOM=function(){var e=this;!this.cm.isReadOnly()&&this.pollContent()||Pr(this.cm,function(){return tr(e.cm)})},e.prototype.setUneditable=function(e){e.contentEditable="false"},e.prototype.onKeyPress=function(e){0==e.charCode||this.composing||(e.preventDefault(),this.cm.isReadOnly()||Er(this.cm,pl)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0))},e.prototype.readOnlyChanged=function(e){this.div.contentEditable=String("nocursor"!=e)},e.prototype.onContextMenu=function(){},e.prototype.resetPosition=function(){},e.prototype.needsContentAttribute=!0;var kl,Tl,Ml,Nl,Ol,r=function(e){this.cm=e,this.prevInput="",this.pollingFast=!1,this.polling=new I,this.hasSelection=!1,this.composing=null};function Al(e,t,r,n){kl.defaults[e]=t,r&&(Tl[e]=n?function(e,t,n){n!=il&&r(e,t,n)}:r)}r.prototype.init=function(n){var e=this,r=this,i=this.cm;this.createField(n);var o=this.textarea;function t(e){if(!Ce(i,e)){if(i.somethingSelected())fl({lineWise:!1,text:i.getSelections()});else{if(!i.options.lineWiseCopyCut)return;var t=vl(i);fl({lineWise:!0,text:t.text}),"cut"==e.type?i.setSelections(t.ranges,null,G):(r.prevInput="",o.value=t.text.join("\n"),H(o))}"cut"==e.type&&(i.state.cutIncoming=+new Date)}}n.wrapper.insertBefore(this.wrapper,n.wrapper.firstChild),s&&(o.style.width="0px"),ye(o,"input",function(){w&&9<=v&&e.hasSelection&&(e.hasSelection=null),r.poll()}),ye(o,"paste",function(e){Ce(i,e)||gl(e,i)||(i.state.pasteIncoming=+new Date,r.fastPoll())}),ye(o,"cut",t),ye(o,"copy",t),ye(n.scroller,"paste",function(e){if(!mn(n,e)&&!Ce(i,e)){if(!o.dispatchEvent)return i.state.pasteIncoming=+new Date,void r.focus();var t=new Event("paste");t.clipboardData=e.clipboardData,o.dispatchEvent(t)}}),ye(n.lineSpace,"selectstart",function(e){mn(n,e)||Te(e)}),ye(o,"compositionstart",function(){var e=i.getCursor("from");r.composing&&r.composing.range.clear(),r.composing={start:e,range:i.markText(e,i.getCursor("to"),{className:"CodeMirror-composing"})}}),ye(o,"compositionend",function(){r.composing&&(r.poll(),r.composing.range.clear(),r.composing=null)})},r.prototype.createField=function(e){this.wrapper=bl(),this.textarea=this.wrapper.firstChild},r.prototype.screenReaderLabelChanged=function(e){e?this.textarea.setAttribute("aria-label",e):this.textarea.removeAttribute("aria-label")},r.prototype.prepareSelection=function(){var e,t=this.cm,n=t.display,r=t.doc,i=sr(t);return t.options.moveInputWithCursor&&(e=Bn(t,r.sel.primary().head,"div"),t=n.wrapper.getBoundingClientRect(),r=n.lineDiv.getBoundingClientRect(),i.teTop=Math.max(0,Math.min(n.wrapper.clientHeight-10,e.top+r.top-t.top)),i.teLeft=Math.max(0,Math.min(n.wrapper.clientWidth-10,e.left+r.left-t.left))),i},r.prototype.showSelection=function(e){var t=this.cm.display;T(t.cursorDiv,e.cursors),T(t.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+"px",this.wrapper.style.left=e.teLeft+"px")},r.prototype.reset=function(e){var t,n;this.contextMenuPending||this.composing||((t=this.cm).somethingSelected()?(this.prevInput="",n=t.getSelection(),this.textarea.value=n,t.state.focused&&H(this.textarea),w&&9<=v&&(this.hasSelection=n)):e||(this.prevInput=this.textarea.value="",w&&9<=v&&(this.hasSelection=null)))},r.prototype.getField=function(){return this.textarea},r.prototype.supportsTouch=function(){return!1},r.prototype.focus=function(){if("nocursor"!=this.cm.options.readOnly&&(!h||A()!=this.textarea))try{this.textarea.focus()}catch(e){}},r.prototype.blur=function(){this.textarea.blur()},r.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},r.prototype.receivedFocus=function(){this.slowPoll()},r.prototype.slowPoll=function(){var e=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,function(){e.poll(),e.cm.state.focused&&e.slowPoll()})},r.prototype.fastPoll=function(){var t=!1,n=this;n.pollingFast=!0,n.polling.set(20,function e(){n.poll()||t?(n.pollingFast=!1,n.slowPoll()):(t=!0,n.polling.set(60,e))})},r.prototype.poll=function(){var e=this,t=this.cm,n=this.textarea,r=this.prevInput;if(this.contextMenuPending||!t.state.focused||Ee(n)&&!r&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var i=n.value;if(i==r&&!t.somethingSelected())return!1;if(w&&9<=v&&this.hasSelection===i||g&&/[\uf700-\uf7ff]/.test(i))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var o=i.charCodeAt(0);if(8203!=o||r||(r="​"),8666==o)return this.reset(),this.cm.execCommand("undo")}for(var l=0,s=Math.min(r.length,i.length);l<s&&r.charCodeAt(l)==i.charCodeAt(l);)++l;return Pr(t,function(){pl(t,i.slice(l),r.length-l,null,e.composing?"*compose":null),1e3<i.length||-1<i.indexOf("\n")?n.value=e.prevInput="":e.prevInput=i,e.composing&&(e.composing.range.clear(),e.composing.range=t.markText(e.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"}))}),!0},r.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},r.prototype.onKeyPress=function(){w&&9<=v&&(this.hasSelection=null),this.fastPoll()},r.prototype.onContextMenu=function(e){var n=this,r=n.cm,i=r.display,o=n.textarea;n.contextMenuPending&&n.contextMenuPending();var l,s,t,a,u=Jn(r,e),c=i.scroller.scrollTop;function h(){var e,t;null!=o.selectionStart&&(t="​"+((e=r.somethingSelected())?o.value:""),o.value="⇚",o.value=t,n.prevInput=e?"":"​",o.selectionStart=1,o.selectionEnd=t.length,i.selForContextMenu=r.doc.sel)}function d(){var e,t;n.contextMenuPending==d&&(n.contextMenuPending=!1,n.wrapper.style.cssText=s,o.style.cssText=l,w&&v<9&&i.scrollbars.setScrollTop(i.scroller.scrollTop=c),null!=o.selectionStart&&((!w||w&&v<9)&&h(),e=0,t=function(){i.selForContextMenu==r.doc.sel&&0==o.selectionStart&&0<o.selectionEnd&&"​"==n.prevInput?Er(r,Vi)(r):e++<10?i.detectingSelectAll=setTimeout(t,500):(i.selForContextMenu=null,i.input.reset())},i.detectingSelectAll=setTimeout(t,200)))}u&&!p&&(r.options.resetSelectionOnContextMenu&&-1==r.doc.sel.contains(u)&&Er(r,Pi)(r.doc,si(u),G),l=o.style.cssText,s=n.wrapper.style.cssText,u=n.wrapper.offsetParent.getBoundingClientRect(),n.wrapper.style.cssText="position: static",o.style.cssText="position: absolute; width: 30px; height: 30px;\n      top: "+(e.clientY-u.top-5)+"px; left: "+(e.clientX-u.left-5)+"px;\n      z-index: 1000; background: "+(w?"rgba(255, 255, 255, .05)":"transparent")+";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",f&&(t=window.scrollY),i.input.focus(),f&&window.scrollTo(null,t),i.input.reset(),r.somethingSelected()||(o.value=n.prevInput=" "),n.contextMenuPending=d,i.selForContextMenu=r.doc.sel,clearTimeout(i.detectingSelectAll),w&&9<=v&&h(),x?(Oe(e),a=function(){we(window,"mouseup",a),setTimeout(d,20)},ye(window,"mouseup",a)):setTimeout(d,50))},r.prototype.readOnlyChanged=function(e){e||this.reset(),this.textarea.disabled="nocursor"==e,this.textarea.readOnly=!!e},r.prototype.setUneditable=function(){},r.prototype.needsContentAttribute=!1,Tl=(kl=ul).optionHandlers,kl.defineOption=Al,kl.Init=il,Al("value","",function(e,t){return e.setValue(t)},!0),Al("mode",null,function(e,t){e.doc.modeOption=t,di(e)},!0),Al("indentUnit",2,di,!0),Al("indentWithTabs",!1),Al("smartIndent",!0),Al("tabSize",4,function(e){fi(e),Hn(e),tr(e)},!0),Al("lineSeparator",null,function(e,r){if(e.doc.lineSep=r){var i=[],o=e.doc.first;e.doc.iter(function(e){for(var t=0;;){var n=e.text.indexOf(r,t);if(-1==n)break;t=n+r.length,i.push(rt(o,n))}o++});for(var t=i.length-1;0<=t;t--)qi(e.doc,r,i[t],rt(i[t].line,i[t].ch+r.length))}}),Al("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,function(e,t,n){e.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g"),n!=il&&e.refresh()}),Al("specialCharPlaceholder",Zt,function(e){return e.refresh()},!0),Al("electricChars",!0),Al("inputStyle",h?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},!0),Al("spellcheck",!1,function(e,t){return e.getInputField().spellcheck=t},!0),Al("autocorrect",!1,function(e,t){return e.getInputField().autocorrect=t},!0),Al("autocapitalize",!1,function(e,t){return e.getInputField().autocapitalize=t},!0),Al("rtlMoveVisually",!y),Al("wholeLineUpdateBefore",!0),Al("theme","default",function(e){rl(e),Zr(e)},!0),Al("keyMap","default",function(e,t,n){t=Do(t),n=n!=il&&Do(n);n&&n.detach&&n.detach(e,t),t.attach&&t.attach(e,n||null)}),Al("extraKeys",null),Al("configureMouse",null),Al("lineWrapping",!1,al,!0),Al("gutters",[],function(e,t){e.display.gutterSpecs=$r(t,e.options.lineNumbers),Zr(e)},!0),Al("fixedGutter",!0,function(e,t){e.display.gutters.style.left=t?qn(e.display)+"px":"0",e.refresh()},!0),Al("coverGutterNextToScrollbar",!1,function(e){return Nr(e)},!0),Al("scrollbarStyle","native",function(e){Dr(e),Nr(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)},!0),Al("lineNumbers",!1,function(e,t){e.display.gutterSpecs=$r(e.options.gutters,t),Zr(e)},!0),Al("firstLineNumber",1,Zr,!0),Al("lineNumberFormatter",function(e){return e},Zr,!0),Al("showCursorWhenSelecting",!1,lr,!0),Al("resetSelectionOnContextMenu",!0),Al("lineWiseCopyCut",!0),Al("pasteLinesPerSelection",!0),Al("selectionsMayTouch",!1),Al("readOnly",!1,function(e,t){"nocursor"==t&&(pr(e),e.display.input.blur()),e.display.input.readOnlyChanged(t)}),Al("screenReaderLabel",null,function(e,t){t=""===t?null:t,e.display.input.screenReaderLabelChanged(t)}),Al("disableInput",!1,function(e,t){t||e.display.input.reset()},!0),Al("dragDrop",!0,sl),Al("allowDropFileTypes",null),Al("cursorBlinkRate",530),Al("cursorScrollMargin",0),Al("cursorHeight",1,lr,!0),Al("singleCursorHeightPerLine",!0,lr,!0),Al("workTime",100),Al("workDelay",100),Al("flattenSpans",!0,fi,!0),Al("addModeClass",!1,fi,!0),Al("pollInterval",100),Al("undoDepth",200,function(e,t){return e.doc.history.undoDepth=t}),Al("historyEventDelay",1250),Al("viewportMargin",10,function(e){return e.refresh()},!0),Al("maxHighlightLength",1e4,fi,!0),Al("moveInputWithCursor",!0,function(e,t){t||e.display.input.resetPosition()}),Al("tabindex",null,function(e,t){return e.display.input.getField().tabIndex=t||""}),Al("autofocus",null),Al("direction","ltr",function(e,t){return e.doc.setDirection(t)},!0),Al("phrases",null),Nl=(Ml=ul).optionHandlers,Ol=Ml.helpers={},Ml.prototype={constructor:Ml,focus:function(){window.focus(),this.display.input.focus()},setOption:function(e,t){var n=this.options,r=n[e];n[e]==t&&"mode"!=e||(n[e]=t,Nl.hasOwnProperty(e)&&Er(this,Nl[e])(this,t,r),xe(this,"optionChange",this,e))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?"push":"unshift"](Do(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,n=0;n<t.length;++n)if(t[n]==e||t[n].name==e)return t.splice(n,1),!0},addOverlay:Ir(function(e,t){var n=e.token?e:Ml.getMode(this.options,e);if(n.startState)throw new Error("Overlays may not be stateful.");!function(e,t,n){for(var r=0,i=n(t);r<e.length&&n(e[r])<=i;)r++;e.splice(r,0,t)}(this.state.overlays,{mode:n,modeSpec:e,opaque:t&&t.opaque,priority:t&&t.priority||0},function(e){return e.priority}),this.state.modeGen++,tr(this)}),removeOverlay:Ir(function(e){for(var t=this.state.overlays,n=0;n<t.length;++n){var r=t[n].modeSpec;if(r==e||"string"==typeof e&&r.name==e)return t.splice(n,1),this.state.modeGen++,void tr(this)}}),indentLine:Ir(function(e,t,n){"string"!=typeof t&&"number"!=typeof t&&(t=null==t?this.options.smartIndent?"smart":"prev":t?"add":"subtract"),tt(this.doc,e)&&hl(this,e,t,n)}),indentSelection:Ir(function(e){for(var t=this.doc.sel.ranges,n=-1,r=0;r<t.length;r++){var i=t[r];if(i.empty())i.head.line>n&&(hl(this,i.head.line,e,!0),n=i.head.line,r==this.doc.sel.primIndex&&wr(this));else{for(var o=i.from(),l=i.to(),i=Math.max(n,o.line),n=Math.min(this.lastLine(),l.line-(l.ch?0:1))+1,s=i;s<n;++s)hl(this,s,e);i=this.doc.sel.ranges;0==o.ch&&t.length==i.length&&0<i[r].from().ch&&Wi(this.doc,r,new oi(o,i[r].to()),G)}}}),getTokenAt:function(e,t){return xt(this,e,t)},getLineTokens:function(e,t){return xt(this,rt(e),t,!0)},getTokenTypeAt:function(e){e=ct(this.doc,e);var t,n=gt(this,$e(this.doc,e.line)),r=0,i=(n.length-1)/2,o=e.ch;if(0==o)t=n[2];else for(;;){var l=r+i>>1;if((l?n[2*l-1]:0)>=o)i=l;else{if(!(n[2*l+1]<o)){t=n[2*l+2];break}r=1+l}}e=t?t.indexOf("overlay "):-1;return e<0?t:0==e?null:t.slice(0,e-1)},getModeAt:function(e){var t=this.doc.mode;return t.innerMode?Ml.innerMode(t,this.getTokenAt(e).state).mode:t},getHelper:function(e,t){return this.getHelpers(e,t)[0]},getHelpers:function(e,t){var n=[];if(!Ol.hasOwnProperty(t))return n;var r=Ol[t],i=this.getModeAt(e);if("string"==typeof i[t])r[i[t]]&&n.push(r[i[t]]);else if(i[t])for(var o=0;o<i[t].length;o++){var l=r[i[t][o]];l&&n.push(l)}else i.helperType&&r[i.helperType]?n.push(r[i.helperType]):r[i.name]&&n.push(r[i.name]);for(var s=0;s<r._global.length;s++){var a=r._global[s];a.pred(i,this)&&-1==R(n,a.val)&&n.push(a.val)}return n},getStateAfter:function(e,t){var n=this.doc;return mt(this,(e=ut(n,null==e?n.first+n.size-1:e))+1,t).state},cursorCoords:function(e,t){var n=this.doc.sel.primary(),n=null==e?n.head:"object"==typeof e?ct(this.doc,e):e?n.from():n.to();return Bn(this,n,t||"page")},charCoords:function(e,t){return zn(this,ct(this.doc,e),t||"page")},coordsChar:function(e,t){return Vn(this,(e=Rn(this,e,t||"page")).left,e.top)},lineAtHeight:function(e,t){return e=Rn(this,{top:e,left:0},t||"page").top,et(this.doc,e+this.display.viewOffset)},heightAtLine:function(e,t,n){var r,i=!1,e="number"==typeof e?(r=this.doc.first+this.doc.size-1,e<this.doc.first?e=this.doc.first:r<e&&(e=r,i=!0),$e(this.doc,e)):e;return In(this,e,{top:0,left:0},t||"page",n||i).top+(i?this.doc.height-Vt(e):0)},defaultTextHeight:function(){return Yn(this.display)},defaultCharWidth:function(){return _n(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,n,r,i){var o,l,s=this.display,a=(e=Bn(this,ct(this.doc,e))).bottom,u=e.left;t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),s.sizer.appendChild(t),"over"==r?a=e.top:"above"!=r&&"near"!=r||(o=Math.max(s.wrapper.clientHeight,this.doc.height),l=Math.max(s.sizer.clientWidth,s.lineSpace.clientWidth),("above"==r||e.bottom+t.offsetHeight>o)&&e.top>t.offsetHeight?a=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=o&&(a=e.bottom),u+t.offsetWidth>l&&(u=l-t.offsetWidth)),t.style.top=a+"px",t.style.left=t.style.right="","right"==i?(u=s.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==i?u=0:"middle"==i&&(u=(s.sizer.clientWidth-t.offsetWidth)/2),t.style.left=u+"px"),n&&(n=this,t={left:u,top:a,right:u+t.offsetWidth,bottom:a+t.offsetHeight},null!=(t=yr(n,t)).scrollTop&&Lr(n,t.scrollTop),null!=t.scrollLeft&&Tr(n,t.scrollLeft))},triggerOnKeyDown:Ir(Xo),triggerOnKeyPress:Ir(_o),triggerOnKeyUp:Yo,triggerOnMouseDown:Ir(Qo),execCommand:function(e){if(Io.hasOwnProperty(e))return Io[e].call(null,this)},triggerElectric:Ir(function(e){ml(this,e)}),findPosH:function(e,t,n,r){var i=1;t<0&&(i=-1,t=-t);for(var o=ct(this.doc,e),l=0;l<t&&!(o=wl(this.doc,o,i,n,r)).hitSide;++l);return o},moveH:Ir(function(t,n){var r=this;this.extendSelectionsBy(function(e){return r.display.shift||r.doc.extend||e.empty()?wl(r.doc,e.head,t,n,r.options.rtlMoveVisually):t<0?e.from():e.to()},V)}),deleteH:Ir(function(n,r){var e=this.doc.sel,i=this.doc;e.somethingSelected()?i.replaceSelection("",null,"+delete"):Wo(this,function(e){var t=wl(i,e.head,n,r,!1);return n<0?{from:t,to:e.head}:{from:e.head,to:t}})}),findPosV:function(e,t,n,r){var i=1,o=r;t<0&&(i=-1,t=-t);for(var l=ct(this.doc,e),s=0;s<t;++s){var a=Bn(this,l,"div");if(null==o?o=a.left:a.left=o,(l=xl(this,a,i,n)).hitSide)break}return l},moveV:Ir(function(r,i){var o=this,l=this.doc,s=[],a=!this.display.shift&&!l.extend&&l.sel.somethingSelected();if(l.extendSelectionsBy(function(e){if(a)return r<0?e.from():e.to();var t=Bn(o,e.head,"div");null!=e.goalColumn&&(t.left=e.goalColumn),s.push(t.left);var n=xl(o,t,r,i);return"page"==i&&e==l.sel.primary()&&br(o,zn(o,n,"div").top-t.top),n},V),s.length)for(var e=0;e<l.sel.ranges.length;e++)l.sel.ranges[e].goalColumn=s[e]}),findWordAt:function(e){var t=$e(this.doc,e.line).text,n=e.ch,r=e.ch;if(t){var i=this.getHelper(e,"wordChars");"before"!=e.sticky&&r!=t.length||!n?++r:--n;for(var o=t.charAt(n),l=J(o,i)?function(e){return J(e,i)}:/\s/.test(o)?function(e){return/\s/.test(e)}:function(e){return!/\s/.test(e)&&!J(e)};0<n&&l(t.charAt(n-1));)--n;for(;r<t.length&&l(t.charAt(r));)++r}return new oi(rt(e.line,n),rt(e.line,r))},toggleOverwrite:function(e){null!=e&&e==this.state.overwrite||(((this.state.overwrite=!this.state.overwrite)?D:L)(this.display.cursorDiv,"CodeMirror-overwrite"),xe(this,"overwriteToggle",this,this.state.overwrite))},hasFocus:function(){return this.display.input.getField()==A()},isReadOnly:function(){return!(!this.options.readOnly&&!this.doc.cantEdit)},scrollTo:Ir(function(e,t){xr(this,e,t)}),getScrollInfo:function(){var e=this.display.scroller;return{left:e.scrollLeft,top:e.scrollTop,height:e.scrollHeight-wn(this)-this.display.barHeight,width:e.scrollWidth-wn(this)-this.display.barWidth,clientHeight:Cn(this),clientWidth:xn(this)}},scrollIntoView:Ir(function(e,t){var n;null==e?(e={from:this.doc.sel.primary().head,to:null},null==t&&(t=this.options.cursorScrollMargin)):"number"==typeof e?e={from:rt(e,0),to:null}:null==e.from&&(e={from:e,to:null}),e.to||(e.to=e.from),e.margin=t||0,null!=e.from.line?(n=e,Cr(t=this),t.curOp.scrollToPos=n):Sr(this,e.from,e.to,e.margin)}),setSize:Ir(function(e,t){function n(e){return"number"==typeof e||/^\d+$/.test(String(e))?e+"px":e}var r=this;null!=e&&(this.display.wrapper.style.width=n(e)),null!=t&&(this.display.wrapper.style.height=n(t)),this.options.lineWrapping&&Wn(this);var i=this.display.viewFrom;this.doc.iter(i,this.display.viewTo,function(e){if(e.widgets)for(var t=0;t<e.widgets.length;t++)if(e.widgets[t].noHScroll){nr(r,i,"widget");break}++i}),this.curOp.forceUpdate=!0,xe(this,"refresh",this)}),operation:function(e){return Pr(this,e)},startOperation:function(){return Hr(this)},endOperation:function(){return Fr(this)},refresh:Ir(function(){var e=this.display.cachedTextHeight;tr(this),this.curOp.forceUpdate=!0,Hn(this),xr(this,this.doc.scrollLeft,this.doc.scrollTop),jr(this.display),(null==e||.5<Math.abs(e-Yn(this.display))||this.options.lineWrapping)&&Qn(this),xe(this,"refresh",this)}),swapDoc:Ir(function(e){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),vi(this,e),Hn(this),this.display.input.reset(),xr(this,e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,ln(this,"swapDoc",this,t),t}),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},ke(Ml),Ml.registerHelper=function(e,t,n){Ol.hasOwnProperty(e)||(Ol[e]=Ml[e]={_global:[]}),Ol[e][t]=n},Ml.registerGlobalHelper=function(e,t,n,r){Ml.registerHelper(e,t,r),Ol[e]._global.push({pred:n,val:r})};var Dl,Wl,Hl="iter insert remove copy getEditor constructor".split(" ");for(Dl in ho.prototype)ho.prototype.hasOwnProperty(Dl)&&R(Hl,Dl)<0&&(ul.prototype[Dl]=function(e){return function(){return e.apply(this.doc,arguments)}}(ho.prototype[Dl]));return ke(ho),ul.inputStyles={textarea:r,contenteditable:e},ul.defineMode=function(e){ul.defaults.mode||"null"==e||(ul.defaults.mode=e),function(e,t){2<arguments.length&&(t.dependencies=Array.prototype.slice.call(arguments,2)),ze[e]=t}.apply(this,arguments)},ul.defineMIME=function(e,t){Be[e]=t},ul.defineMode("null",function(){return{token:function(e){return e.skipToEnd()}}}),ul.defineMIME("text/plain","null"),ul.defineExtension=function(e,t){ul.prototype[e]=t},ul.defineDocExtension=function(e,t){ho.prototype[e]=t},ul.fromTextArea=function(t,n){var e;function r(){t.value=s.getValue()}if(n=n?P(n):{},n.value=t.value,!n.tabindex&&t.tabIndex&&(n.tabindex=t.tabIndex),!n.placeholder&&t.placeholder&&(n.placeholder=t.placeholder),null==n.autofocus&&(e=A(),n.autofocus=e==t||null!=t.getAttribute("autofocus")&&e==document.body),t.form&&(ye(t.form,"submit",r),!n.leaveSubmitMethodAlone)){var i=t.form,o=i.submit;try{var l=i.submit=function(){r(),i.submit=o,i.submit(),i.submit=l}}catch(e){}}n.finishInit=function(e){e.save=r,e.getTextArea=function(){return t},e.toTextArea=function(){e.toTextArea=isNaN,r(),t.parentNode.removeChild(e.getWrapperElement()),t.style.display="",t.form&&(we(t.form,"submit",r),n.leaveSubmitMethodAlone||"function"!=typeof t.form.submit||(t.form.submit=o))}},t.style.display="none";var s=ul(function(e){return t.parentNode.insertBefore(e,t.nextSibling)},n);return s},(Wl=ul).off=we,Wl.on=ye,Wl.wheelEventPixels=ni,Wl.Doc=ho,Wl.splitLines=Pe,Wl.countColumn=E,Wl.findColumn=K,Wl.isWordChar=Q,Wl.Pass=B,Wl.signal=xe,Wl.Line=Xt,Wl.changeEnd=ai,Wl.scrollbarModel=Ar,Wl.Pos=rt,Wl.cmpPos=it,Wl.modes=ze,Wl.mimeModes=Be,Wl.resolveMode=Ge,Wl.getMode=Ue,Wl.modeExtensions=Ve,Wl.extendMode=Ke,Wl.copyState=je,Wl.startState=Ye,Wl.innerMode=Xe,Wl.commands=Io,Wl.keyMap=Lo,Wl.keyName=Ao,Wl.isModifierKey=No,Wl.lookupKey=Mo,Wl.normalizeKeyMap=To,Wl.StringStream=_e,Wl.SharedTextMarker=ao,Wl.TextMarker=lo,Wl.LineWidget=ro,Wl.e_preventDefault=Te,Wl.e_stopPropagation=Me,Wl.e_stop=Oe,Wl.addClass=D,Wl.contains=O,Wl.rmClass=L,Wl.keyNames=wo,ul.version="5.59.4",ul});
\ No newline at end of file
diff --git a/src/static/js/socket.io.js b/src/static/js/socket.io.js
deleted file mode 100644 (file)
index e43c4ad..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(_dereq_,module,exports){module.exports=_dereq_("./lib/")},{"./lib/":2}],2:[function(_dereq_,module,exports){var url=_dereq_("./url");var parser=_dereq_("socket.io-parser");var Manager=_dereq_("./manager");var debug=_dereq_("debug")("socket.io-client");module.exports=exports=lookup;var cache=exports.managers={};function lookup(uri,opts){if(typeof uri=="object"){opts=uri;uri=undefined}opts=opts||{};var parsed=url(uri);var source=parsed.source;var id=parsed.id;var io;if(opts.forceNew||opts["force new connection"]||false===opts.multiplex){debug("ignoring socket cache for %s",source);io=Manager(source,opts)}else{if(!cache[id]){debug("new io instance for %s",source);cache[id]=Manager(source,opts)}io=cache[id]}return io.socket(parsed.path)}exports.protocol=parser.protocol;exports.connect=lookup;exports.Manager=_dereq_("./manager");exports.Socket=_dereq_("./socket")},{"./manager":3,"./socket":5,"./url":6,debug:10,"socket.io-parser":44}],3:[function(_dereq_,module,exports){var url=_dereq_("./url");var eio=_dereq_("engine.io-client");var Socket=_dereq_("./socket");var Emitter=_dereq_("component-emitter");var parser=_dereq_("socket.io-parser");var on=_dereq_("./on");var bind=_dereq_("component-bind");var object=_dereq_("object-component");var debug=_dereq_("debug")("socket.io-client:manager");var indexOf=_dereq_("indexof");var Backoff=_dereq_("backo2");module.exports=Manager;function Manager(uri,opts){if(!(this instanceof Manager))return new Manager(uri,opts);if(uri&&"object"==typeof uri){opts=uri;uri=undefined}opts=opts||{};opts.path=opts.path||"/socket.io";this.nsps={};this.subs=[];this.opts=opts;this.reconnection(opts.reconnection!==false);this.reconnectionAttempts(opts.reconnectionAttempts||Infinity);this.reconnectionDelay(opts.reconnectionDelay||1e3);this.reconnectionDelayMax(opts.reconnectionDelayMax||5e3);this.randomizationFactor(opts.randomizationFactor||.5);this.backoff=new Backoff({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()});this.timeout(null==opts.timeout?2e4:opts.timeout);this.readyState="closed";this.uri=uri;this.connected=[];this.encoding=false;this.packetBuffer=[];this.encoder=new parser.Encoder;this.decoder=new parser.Decoder;this.autoConnect=opts.autoConnect!==false;if(this.autoConnect)this.open()}Manager.prototype.emitAll=function(){this.emit.apply(this,arguments);for(var nsp in this.nsps){this.nsps[nsp].emit.apply(this.nsps[nsp],arguments)}};Manager.prototype.updateSocketIds=function(){for(var nsp in this.nsps){this.nsps[nsp].id=this.engine.id}};Emitter(Manager.prototype);Manager.prototype.reconnection=function(v){if(!arguments.length)return this._reconnection;this._reconnection=!!v;return this};Manager.prototype.reconnectionAttempts=function(v){if(!arguments.length)return this._reconnectionAttempts;this._reconnectionAttempts=v;return this};Manager.prototype.reconnectionDelay=function(v){if(!arguments.length)return this._reconnectionDelay;this._reconnectionDelay=v;this.backoff&&this.backoff.setMin(v);return this};Manager.prototype.randomizationFactor=function(v){if(!arguments.length)return this._randomizationFactor;this._randomizationFactor=v;this.backoff&&this.backoff.setJitter(v);return this};Manager.prototype.reconnectionDelayMax=function(v){if(!arguments.length)return this._reconnectionDelayMax;this._reconnectionDelayMax=v;this.backoff&&this.backoff.setMax(v);return this};Manager.prototype.timeout=function(v){if(!arguments.length)return this._timeout;this._timeout=v;return this};Manager.prototype.maybeReconnectOnOpen=function(){if(!this.reconnecting&&this._reconnection&&this.backoff.attempts===0){this.reconnect()}};Manager.prototype.open=Manager.prototype.connect=function(fn){debug("readyState %s",this.readyState);if(~this.readyState.indexOf("open"))return this;debug("opening %s",this.uri);this.engine=eio(this.uri,this.opts);var socket=this.engine;var self=this;this.readyState="opening";this.skipReconnect=false;var openSub=on(socket,"open",function(){self.onopen();fn&&fn()});var errorSub=on(socket,"error",function(data){debug("connect_error");self.cleanup();self.readyState="closed";self.emitAll("connect_error",data);if(fn){var err=new Error("Connection error");err.data=data;fn(err)}else{self.maybeReconnectOnOpen()}});if(false!==this._timeout){var timeout=this._timeout;debug("connect attempt will timeout after %d",timeout);var timer=setTimeout(function(){debug("connect attempt timed out after %d",timeout);openSub.destroy();socket.close();socket.emit("error","timeout");self.emitAll("connect_timeout",timeout)},timeout);this.subs.push({destroy:function(){clearTimeout(timer)}})}this.subs.push(openSub);this.subs.push(errorSub);return this};Manager.prototype.onopen=function(){debug("open");this.cleanup();this.readyState="open";this.emit("open");var socket=this.engine;this.subs.push(on(socket,"data",bind(this,"ondata")));this.subs.push(on(this.decoder,"decoded",bind(this,"ondecoded")));this.subs.push(on(socket,"error",bind(this,"onerror")));this.subs.push(on(socket,"close",bind(this,"onclose")))};Manager.prototype.ondata=function(data){this.decoder.add(data)};Manager.prototype.ondecoded=function(packet){this.emit("packet",packet)};Manager.prototype.onerror=function(err){debug("error",err);this.emitAll("error",err)};Manager.prototype.socket=function(nsp){var socket=this.nsps[nsp];if(!socket){socket=new Socket(this,nsp);this.nsps[nsp]=socket;var self=this;socket.on("connect",function(){socket.id=self.engine.id;if(!~indexOf(self.connected,socket)){self.connected.push(socket)}})}return socket};Manager.prototype.destroy=function(socket){var index=indexOf(this.connected,socket);if(~index)this.connected.splice(index,1);if(this.connected.length)return;this.close()};Manager.prototype.packet=function(packet){debug("writing packet %j",packet);var self=this;if(!self.encoding){self.encoding=true;this.encoder.encode(packet,function(encodedPackets){for(var i=0;i<encodedPackets.length;i++){self.engine.write(encodedPackets[i])}self.encoding=false;self.processPacketQueue()})}else{self.packetBuffer.push(packet)}};Manager.prototype.processPacketQueue=function(){if(this.packetBuffer.length>0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){this.skipReconnect=true;this.backoff.reset();this.readyState="closed";this.engine&&this.engine.close()};Manager.prototype.onclose=function(reason){debug("close");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":4,"./socket":5,"./url":6,backo2:7,"component-bind":8,"component-emitter":9,debug:10,"engine.io-client":11,indexof:40,"object-component":41,"socket.io-parser":44}],4:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],5:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};if(this.io.autoConnect)this.open();this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){debug("calling ack %s with %j",packet.id,packet.data);var fn=this.acks[packet.id];fn.apply(this,packet.data);delete this.acks[packet.id]};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i<this.receiveBuffer.length;i++){emit.apply(this,this.receiveBuffer[i])}this.receiveBuffer=[];for(i=0;i<this.sendBuffer.length;i++){this.packet(this.sendBuffer[i])}this.sendBuffer=[]};Socket.prototype.ondisconnect=function(){debug("server disconnect (%s)",this.nsp);this.destroy();this.onclose("io server disconnect")};Socket.prototype.destroy=function(){if(this.subs){for(var i=0;i<this.subs.length;i++){this.subs[i].destroy()}this.subs=null}this.io.destroy(this)};Socket.prototype.close=Socket.prototype.disconnect=function(){if(this.connected){debug("performing disconnect (%s)",this.nsp);this.packet({type:parser.DISCONNECT})}this.destroy();if(this.connected){this.onclose("io client disconnect")}return this}},{"./on":4,"component-bind":8,"component-emitter":9,debug:10,"has-binary":36,"socket.io-parser":44,"to-array":48}],6:[function(_dereq_,module,exports){(function(global){var parseuri=_dereq_("parseuri");var debug=_dereq_("debug")("socket.io-client:url");module.exports=url;function url(uri,loc){var obj=uri;var loc=loc||global.location;if(null==uri)uri=loc.protocol+"//"+loc.host;if("string"==typeof uri){if("/"==uri.charAt(0)){if("/"==uri.charAt(1)){uri=loc.protocol+uri}else{uri=loc.hostname+uri}}if(!/^(https?|wss?):\/\//.test(uri)){debug("protocol-less url %s",uri);if("undefined"!=typeof loc){uri=loc.protocol+"//"+uri}else{uri="https://"+uri}}debug("parse %s",uri);obj=parseuri(uri)}if(!obj.port){if(/^(http|ws)$/.test(obj.protocol)){obj.port="80"}else if(/^(http|ws)s$/.test(obj.protocol)){obj.port="443"}}obj.path=obj.path||"/";obj.id=obj.protocol+"://"+obj.host+":"+obj.port;obj.href=obj.protocol+"://"+obj.host+(loc&&loc.port==obj.port?"":":"+obj.port);return obj}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{debug:10,parseuri:42}],7:[function(_dereq_,module,exports){module.exports=Backoff;function Backoff(opts){opts=opts||{};this.ms=opts.min||100;this.max=opts.max||1e4;this.factor=opts.factor||2;this.jitter=opts.jitter>0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],8:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],9:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i<callbacks.length;i++){cb=callbacks[i];if(cb===fn||cb.fn===fn){callbacks.splice(i,1);break}}return this};Emitter.prototype.emit=function(event){this._callbacks=this._callbacks||{};var args=[].slice.call(arguments,1),callbacks=this._callbacks[event];if(callbacks){callbacks=callbacks.slice(0);for(var i=0,len=callbacks.length;i<len;++i){callbacks[i].apply(this,args)}}return this};Emitter.prototype.listeners=function(event){this._callbacks=this._callbacks||{};return this._callbacks[event]||[]};Emitter.prototype.hasListeners=function(event){return!!this.listeners(event).length}},{}],10:[function(_dereq_,module,exports){module.exports=debug;function debug(name){if(!debug.enabled(name))return function(){};return function(fmt){fmt=coerce(fmt);var curr=new Date;var ms=curr-(debug[name]||curr);debug[name]=curr;fmt=name+" "+fmt+" +"+debug.humanize(ms);window.console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}}debug.names=[];debug.skips=[];debug.enable=function(name){try{localStorage.debug=name}catch(e){}var split=(name||"").split(/[\s,]+/),len=split.length;for(var i=0;i<len;i++){name=split[i].replace("*",".*?");if(name[0]==="-"){debug.skips.push(new RegExp("^"+name.substr(1)+"$"))}else{debug.names.push(new RegExp("^"+name+"$"))}}};debug.disable=function(){debug.enable("")};debug.humanize=function(ms){var sec=1e3,min=60*1e3,hour=60*min;if(ms>=hour)return(ms/hour).toFixed(1)+"h";if(ms>=min)return(ms/min).toFixed(1)+"m";if(ms>=sec)return(ms/sec|0)+"s";return ms+"ms"};debug.enabled=function(name){for(var i=0,len=debug.skips.length;i<len;i++){if(debug.skips[i].test(name)){return false}}for(var i=0,len=debug.names.length;i<len;i++){if(debug.names[i].test(name)){return true}}return false};function coerce(val){if(val instanceof Error)return val.stack||val.message;return val}try{if(window.localStorage)debug.enable(localStorage.debug)}catch(e){}},{}],11:[function(_dereq_,module,exports){module.exports=_dereq_("./lib/")},{"./lib/":12}],12:[function(_dereq_,module,exports){module.exports=_dereq_("./socket");module.exports.parser=_dereq_("engine.io-parser")},{"./socket":13,"engine.io-parser":25}],13:[function(_dereq_,module,exports){(function(global){var transports=_dereq_("./transports");var Emitter=_dereq_("component-emitter");var debug=_dereq_("debug")("engine.io-client:socket");var index=_dereq_("indexof");var parser=_dereq_("engine.io-parser");var parseuri=_dereq_("parseuri");var parsejson=_dereq_("parsejson");var parseqs=_dereq_("parseqs");module.exports=Socket;function noop(){}function Socket(uri,opts){if(!(this instanceof Socket))return new Socket(uri,opts);opts=opts||{};if(uri&&"object"==typeof uri){opts=uri;uri=null}if(uri){uri=parseuri(uri);opts.host=uri.host;opts.secure=uri.protocol=="https"||uri.protocol=="wss";opts.port=uri.port;if(uri.query)opts.query=uri.query}this.secure=null!=opts.secure?opts.secure:global.location&&"https:"==location.protocol;if(opts.host){var pieces=opts.host.split(":");opts.hostname=pieces.shift();if(pieces.length){opts.port=pieces.pop()}else if(!opts.port){opts.port=this.secure?"443":"80"}}this.agent=opts.agent||false;this.hostname=opts.hostname||(global.location?location.hostname:"localhost");this.port=opts.port||(global.location&&location.port?location.port:this.secure?443:80);this.query=opts.query||{};if("string"==typeof this.query)this.query=parseqs.decode(this.query);this.upgrade=false!==opts.upgrade;this.path=(opts.path||"/engine.io").replace(/\/$/,"")+"/";this.forceJSONP=!!opts.forceJSONP;this.jsonp=false!==opts.jsonp;this.forceBase64=!!opts.forceBase64;this.enablesXDR=!!opts.enablesXDR;this.timestampParam=opts.timestampParam||"t";this.timestampRequests=opts.timestampRequests;this.transports=opts.transports||["polling","websocket"];this.readyState="";this.writeBuffer=[];this.callbackBuffer=[];this.policyPort=opts.policyPort||843;this.rememberUpgrade=opts.rememberUpgrade||false;this.binaryType=null;this.onlyBinaryUpgrades=opts.onlyBinaryUpgrades;this.pfx=opts.pfx||null;this.key=opts.key||null;this.passphrase=opts.passphrase||null;this.cert=opts.cert||null;this.ca=opts.ca||null;this.ciphers=opts.ciphers||null;this.rejectUnauthorized=opts.rejectUnauthorized||null;this.open()}Socket.priorWebsocketSuccess=false;Emitter(Socket.prototype);Socket.protocol=parser.protocol;Socket.Socket=Socket;Socket.Transport=_dereq_("./transport");Socket.transports=_dereq_("./transports");Socket.parser=_dereq_("engine.io-parser");Socket.prototype.createTransport=function(name){debug('creating transport "%s"',name);var query=clone(this.query);query.EIO=parser.protocol;query.transport=name;if(this.id)query.sid=this.id;var transport=new transports[name]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:query,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized});return transport};function clone(obj){var o={};for(var i in obj){if(obj.hasOwnProperty(i)){o[i]=obj[i]}}return o}Socket.prototype.open=function(){var transport;if(this.rememberUpgrade&&Socket.priorWebsocketSuccess&&this.transports.indexOf("websocket")!=-1){transport="websocket"}else if(0==this.transports.length){var self=this;setTimeout(function(){self.emit("error","No transports available")},0);return}else{transport=this.transports[0]}this.readyState="opening";var transport;try{transport=this.createTransport(transport)}catch(e){this.transports.shift();this.open();return}transport.open();this.setTransport(transport)};Socket.prototype.setTransport=function(transport){debug("setting transport %s",transport.name);var self=this;if(this.transport){debug("clearing existing transport %s",this.transport.name);this.transport.removeAllListeners()}this.transport=transport;transport.on("drain",function(){self.onDrain()}).on("packet",function(packet){self.onPacket(packet)}).on("error",function(e){self.onError(e)}).on("close",function(){self.onClose("transport close")})};Socket.prototype.probe=function(name){debug('probing transport "%s"',name);var transport=this.createTransport(name,{probe:1}),failed=false,self=this;Socket.priorWebsocketSuccess=false;function onTransportOpen(){if(self.onlyBinaryUpgrades){var upgradeLosesBinary=!this.supportsBinary&&self.transport.supportsBinary;failed=failed||upgradeLosesBinary}if(failed)return;debug('probe transport "%s" opened',name);transport.send([{type:"ping",data:"probe"}]);transport.once("packet",function(msg){if(failed)return;if("pong"==msg.type&&"probe"==msg.data){debug('probe transport "%s" pong',name);self.upgrading=true;self.emit("upgrading",transport);if(!transport)return;Socket.priorWebsocketSuccess="websocket"==transport.name;debug('pausing current transport "%s"',self.transport.name);self.transport.pause(function(){if(failed)return;if("closed"==self.readyState)return;debug("changing transport and sending upgrade packet");cleanup();self.setTransport(transport);transport.send([{type:"upgrade"}]);self.emit("upgrade",transport);transport=null;self.upgrading=false;self.flush()})}else{debug('probe transport "%s" failed',name);var err=new Error("probe error");err.transport=transport.name;self.emit("upgradeError",err)}})}function freezeTransport(){if(failed)return;failed=true;cleanup();transport.close();transport=null}function onerror(err){var error=new Error("probe error: "+err);error.transport=transport.name;freezeTransport();debug('probe transport "%s" failed because of error: %s',name,err);self.emit("upgradeError",error)}function onTransportClose(){onerror("transport closed")}function onclose(){onerror("socket closed")}function onupgrade(to){if(transport&&to.name!=transport.name){debug('"%s" works - aborting "%s"',to.name,transport.name);freezeTransport()}}function cleanup(){transport.removeListener("open",onTransportOpen);transport.removeListener("error",onerror);transport.removeListener("close",onTransportClose);self.removeListener("close",onclose);self.removeListener("upgrading",onupgrade)}transport.once("open",onTransportOpen);transport.once("error",onerror);transport.once("close",onTransportClose);this.once("close",onclose);this.once("upgrading",onupgrade);transport.open()};Socket.prototype.onOpen=function(){debug("socket open");this.readyState="open";Socket.priorWebsocketSuccess="websocket"==this.transport.name;this.emit("open");this.flush();if("open"==this.readyState&&this.upgrade&&this.transport.pause){debug("starting upgrade probes");for(var i=0,l=this.upgrades.length;i<l;i++){this.probe(this.upgrades[i])}}};Socket.prototype.onPacket=function(packet){if("opening"==this.readyState||"open"==this.readyState){debug('socket receive: type "%s", data "%s"',packet.type,packet.data);this.emit("packet",packet);this.emit("heartbeat");switch(packet.type){case"open":this.onHandshake(parsejson(packet.data));break;case"pong":this.setPing();break;case"error":var err=new Error("server error");err.code=packet.data;this.emit("error",err);break;case"message":this.emit("data",packet.data);this.emit("message",packet.data);break}}else{debug('packet received with socket readyState "%s"',this.readyState)}};Socket.prototype.onHandshake=function(data){this.emit("handshake",data);this.id=data.sid;this.transport.query.sid=data.sid;this.upgrades=this.filterUpgrades(data.upgrades);this.pingInterval=data.pingInterval;this.pingTimeout=data.pingTimeout;this.onOpen();if("closed"==this.readyState)return;this.setPing();this.removeListener("heartbeat",this.onHeartbeat);this.on("heartbeat",this.onHeartbeat)};Socket.prototype.onHeartbeat=function(timeout){clearTimeout(this.pingTimeoutTimer);var self=this;self.pingTimeoutTimer=setTimeout(function(){if("closed"==self.readyState)return;self.onClose("ping timeout")},timeout||self.pingInterval+self.pingTimeout)};Socket.prototype.setPing=function(){var self=this;clearTimeout(self.pingIntervalTimer);self.pingIntervalTimer=setTimeout(function(){debug("writing ping packet - expecting pong within %sms",self.pingTimeout);self.ping();self.onHeartbeat(self.pingTimeout)},self.pingInterval)};Socket.prototype.ping=function(){this.sendPacket("ping")};Socket.prototype.onDrain=function(){for(var i=0;i<this.prevBufferLen;i++){if(this.callbackBuffer[i]){this.callbackBuffer[i]()}}this.writeBuffer.splice(0,this.prevBufferLen);this.callbackBuffer.splice(0,this.prevBufferLen);this.prevBufferLen=0;if(this.writeBuffer.length==0){this.emit("drain")}else{this.flush()}};Socket.prototype.flush=function(){if("closed"!=this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){debug("flushing %d packets in socket",this.writeBuffer.length);this.transport.send(this.writeBuffer);this.prevBufferLen=this.writeBuffer.length;this.emit("flush")}};Socket.prototype.write=Socket.prototype.send=function(msg,fn){this.sendPacket("message",msg,fn);return this};Socket.prototype.sendPacket=function(type,data,fn){if("closing"==this.readyState||"closed"==this.readyState){return}var packet={type:type,data:data};this.emit("packetCreate",packet);this.writeBuffer.push(packet);this.callbackBuffer.push(fn);this.flush()};Socket.prototype.close=function(){if("opening"==this.readyState||"open"==this.readyState){this.readyState="closing";var self=this;function close(){self.onClose("forced close");debug("socket closing - telling transport to close");self.transport.close()}function cleanupAndClose(){self.removeListener("upgrade",cleanupAndClose);self.removeListener("upgradeError",cleanupAndClose);close()}function waitForUpgrade(){self.once("upgrade",cleanupAndClose);self.once("upgradeError",cleanupAndClose)}if(this.writeBuffer.length){this.once("drain",function(){if(this.upgrading){waitForUpgrade()}else{close()}})}else if(this.upgrading){waitForUpgrade()}else{close()}}return this};Socket.prototype.onError=function(err){debug("socket error %j",err);Socket.priorWebsocketSuccess=false;this.emit("error",err);this.onClose("transport error",err)};Socket.prototype.onClose=function(reason,desc){if("opening"==this.readyState||"open"==this.readyState||"closing"==this.readyState){debug('socket close with reason: "%s"',reason);var self=this;clearTimeout(this.pingIntervalTimer);clearTimeout(this.pingTimeoutTimer);setTimeout(function(){self.writeBuffer=[];self.callbackBuffer=[];self.prevBufferLen=0},0);this.transport.removeAllListeners("close");this.transport.close();this.transport.removeAllListeners();this.readyState="closed";this.id=null;this.emit("close",reason,desc)}};Socket.prototype.filterUpgrades=function(upgrades){var filteredUpgrades=[];for(var i=0,j=upgrades.length;i<j;i++){if(~index(this.transports,upgrades[i]))filteredUpgrades.push(upgrades[i])}return filteredUpgrades}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./transport":14,"./transports":15,"component-emitter":9,debug:22,"engine.io-parser":25,indexof:40,parsejson:32,parseqs:33,parseuri:34}],14:[function(_dereq_,module,exports){var parser=_dereq_("engine.io-parser");var Emitter=_dereq_("component-emitter");module.exports=Transport;function Transport(opts){this.path=opts.path;this.hostname=opts.hostname;this.port=opts.port;this.secure=opts.secure;this.query=opts.query;this.timestampParam=opts.timestampParam;this.timestampRequests=opts.timestampRequests;this.readyState="";this.agent=opts.agent||false;this.socket=opts.socket;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized}Emitter(Transport.prototype);Transport.timestamps=0;Transport.prototype.onError=function(msg,desc){var err=new Error(msg);err.type="TransportError";err.description=desc;this.emit("error",err);return this};Transport.prototype.open=function(){if("closed"==this.readyState||""==this.readyState){this.readyState="opening";this.doOpen()}return this};Transport.prototype.close=function(){if("opening"==this.readyState||"open"==this.readyState){this.doClose();this.onClose()}return this};Transport.prototype.send=function(packets){if("open"==this.readyState){this.write(packets)}else{throw new Error("Transport not open")}};Transport.prototype.onOpen=function(){this.readyState="open";this.writable=true;this.emit("open")};Transport.prototype.onData=function(data){var packet=parser.decodePacket(data,this.socket.binaryType);this.onPacket(packet)};Transport.prototype.onPacket=function(packet){this.emit("packet",packet)};Transport.prototype.onClose=function(){this.readyState="closed";this.emit("close")}},{"component-emitter":9,"engine.io-parser":25}],15:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest");var XHR=_dereq_("./polling-xhr");var JSONP=_dereq_("./polling-jsonp");var websocket=_dereq_("./websocket");exports.polling=polling;exports.websocket=websocket;function polling(opts){var xhr;var xd=false;var xs=false;var jsonp=false!==opts.jsonp;if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}xd=opts.hostname!=location.hostname||port!=opts.port;xs=opts.secure!=isSSL}opts.xdomain=xd;opts.xscheme=xs;xhr=new XMLHttpRequest(opts);if("open"in xhr&&!opts.forceJSONP){return new XHR(opts)}else{if(!jsonp)throw new Error("JSONP disabled");return new JSONP(opts)}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling-jsonp":16,"./polling-xhr":17,"./websocket":19,xmlhttprequest:20}],16:[function(_dereq_,module,exports){(function(global){var Polling=_dereq_("./polling");var inherit=_dereq_("component-inherit");module.exports=JSONPPolling;var rNewline=/\n/g;var rEscapedNewline=/\\n/g;var callbacks;var index=0;function empty(){}function JSONPPolling(opts){Polling.call(this,opts);
-this.query=this.query||{};if(!callbacks){if(!global.___eio)global.___eio=[];callbacks=global.___eio}this.index=callbacks.length;var self=this;callbacks.push(function(msg){self.onData(msg)});this.query.j=this.index;if(global.document&&global.addEventListener){global.addEventListener("beforeunload",function(){if(self.script)self.script.onerror=empty},false)}}inherit(JSONPPolling,Polling);JSONPPolling.prototype.supportsBinary=false;JSONPPolling.prototype.doClose=function(){if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}if(this.form){this.form.parentNode.removeChild(this.form);this.form=null;this.iframe=null}Polling.prototype.doClose.call(this)};JSONPPolling.prototype.doPoll=function(){var self=this;var script=document.createElement("script");if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}script.async=true;script.src=this.uri();script.onerror=function(e){self.onError("jsonp poll error",e)};var insertAt=document.getElementsByTagName("script")[0];insertAt.parentNode.insertBefore(script,insertAt);this.script=script;var isUAgecko="undefined"!=typeof navigator&&/gecko/i.test(navigator.userAgent);if(isUAgecko){setTimeout(function(){var iframe=document.createElement("iframe");document.body.appendChild(iframe);document.body.removeChild(iframe)},100)}};JSONPPolling.prototype.doWrite=function(data,fn){var self=this;if(!this.form){var form=document.createElement("form");var area=document.createElement("textarea");var id=this.iframeId="eio_iframe_"+this.index;var iframe;form.className="socketio";form.style.position="absolute";form.style.top="-1000px";form.style.left="-1000px";form.target=id;form.method="POST";form.setAttribute("accept-charset","utf-8");area.name="d";form.appendChild(area);document.body.appendChild(form);this.form=form;this.area=area}this.form.action=this.uri();function complete(){initIframe();fn()}function initIframe(){if(self.iframe){try{self.form.removeChild(self.iframe)}catch(e){self.onError("jsonp polling iframe removal error",e)}}try{var html='<iframe src="javascript:0" name="'+self.iframeId+'">';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-inherit":21}],17:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{data="ok"}}}catch(e){this.onError(e)}if(null!=data){this.onData(data)}};Request.prototype.hasXDR=function(){return"undefined"!==typeof global.XDomainRequest&&!this.xs&&this.enablesXDR};Request.prototype.abort=function(){this.cleanup()};if(global.document){Request.requestsCount=0;Request.requests={};if(global.attachEvent){global.attachEvent("onunload",unloadHandler)}else if(global.addEventListener){global.addEventListener("beforeunload",unloadHandler,false)}}function unloadHandler(){for(var i in Request.requests){if(Request.requests.hasOwnProperty(i)){Request.requests[i].abort()}}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-emitter":9,"component-inherit":21,debug:22,xmlhttprequest:20}],18:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parseqs=_dereq_("parseqs");var parser=_dereq_("engine.io-parser");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling");module.exports=Polling;var hasXHR2=function(){var XMLHttpRequest=_dereq_("xmlhttprequest");var xhr=new XMLHttpRequest({xdomain:false});return null!=xhr.responseType}();function Polling(opts){var forceBase64=opts&&opts.forceBase64;if(!hasXHR2||forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(Polling,Transport);Polling.prototype.name="polling";Polling.prototype.doOpen=function(){this.poll()};Polling.prototype.pause=function(onPause){var pending=0;var self=this;this.readyState="pausing";function pause(){debug("paused");self.readyState="paused";onPause()}if(this.polling||!this.writable){var total=0;if(this.polling){debug("we are currently polling - waiting to pause");total++;this.once("pollComplete",function(){debug("pre-pause polling complete");--total||pause()})}if(!this.writable){debug("we are currently writing - waiting to pause");total++;this.once("drain",function(){debug("pre-pause writing complete");--total||pause()})}}else{pause()}};Polling.prototype.poll=function(){debug("polling");this.polling=true;this.doPoll();this.emit("poll")};Polling.prototype.onData=function(data){var self=this;debug("polling got data %s",data);var callback=function(packet,index,total){if("opening"==self.readyState){self.onOpen()}if("close"==packet.type){self.onClose();return false}self.onPacket(packet)};parser.decodePayload(data,this.socket.binaryType,callback);if("closed"!=this.readyState){this.polling=false;this.emit("pollComplete");if("open"==this.readyState){this.poll()}else{debug('ignoring poll - transport state "%s"',this.readyState)}}};Polling.prototype.doClose=function(){var self=this;function close(){debug("writing close packet");self.write([{type:"close"}])}if("open"==this.readyState){debug("transport open - closing");close()}else{debug("transport not open - deferring close");this.once("open",close)}};Polling.prototype.write=function(packets){var self=this;this.writable=false;var callbackfn=function(){self.writable=true;self.emit("drain")};var self=this;parser.encodePayload(packets,this.supportsBinary,function(data){self.doWrite(data,callbackfn)})};Polling.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"https":"http";var port="";if(false!==this.timestampRequests){query[this.timestampParam]=+new Date+"-"+Transport.timestamps++}if(!this.supportsBinary&&!query.sid){query.b64=1}query=parseqs.encode(query);if(this.port&&("https"==schema&&this.port!=443||"http"==schema&&this.port!=80)){port=":"+this.port}if(query.length){query="?"+query}return schema+"://"+this.hostname+port+this.path+query}},{"../transport":14,"component-inherit":21,debug:22,"engine.io-parser":25,parseqs:33,xmlhttprequest:20}],19:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parser=_dereq_("engine.io-parser");var parseqs=_dereq_("parseqs");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:websocket");var WebSocket=_dereq_("ws");module.exports=WS;function WS(opts){var forceBase64=opts&&opts.forceBase64;if(forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(WS,Transport);WS.prototype.name="websocket";WS.prototype.supportsBinary=true;WS.prototype.doOpen=function(){if(!this.check()){return}var self=this;var uri=this.uri();var protocols=void 0;var opts={agent:this.agent};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;this.ws=new WebSocket(uri,protocols,opts);if(this.ws.binaryType===undefined){this.supportsBinary=false}this.ws.binaryType="arraybuffer";this.addEventListeners()};WS.prototype.addEventListeners=function(){var self=this;this.ws.onopen=function(){self.onOpen()};this.ws.onclose=function(){self.onClose()};this.ws.onmessage=function(ev){self.onData(ev.data)};this.ws.onerror=function(e){self.onError("websocket error",e)}};if("undefined"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)){WS.prototype.onData=function(data){var self=this;setTimeout(function(){Transport.prototype.onData.call(self,data)},0)}}WS.prototype.write=function(packets){var self=this;this.writable=false;for(var i=0,l=packets.length;i<l;i++){parser.encodePacket(packets[i],this.supportsBinary,function(data){try{self.ws.send(data)}catch(e){debug("websocket closed before onclose event")}})}function ondrain(){self.writable=true;self.emit("drain")}setTimeout(ondrain,0)};WS.prototype.onClose=function(){Transport.prototype.onClose.call(this)};WS.prototype.doClose=function(){if(typeof this.ws!=="undefined"){this.ws.close()}};WS.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"wss":"ws";var port="";if(this.port&&("wss"==schema&&this.port!=443||"ws"==schema&&this.port!=80)){port=":"+this.port}if(this.timestampRequests){query[this.timestampParam]=+new Date}if(!this.supportsBinary){query.b64=1}query=parseqs.encode(query);if(query.length){query="?"+query}return schema+"://"+this.hostname+port+this.path+query};WS.prototype.check=function(){return!!WebSocket&&!("__initialize"in WebSocket&&this.name===WS.prototype.name)}},{"../transport":14,"component-inherit":21,debug:22,"engine.io-parser":25,parseqs:33,ws:35}],20:[function(_dereq_,module,exports){var hasCORS=_dereq_("has-cors");module.exports=function(opts){var xdomain=opts.xdomain;var xscheme=opts.xscheme;var enablesXDR=opts.enablesXDR;try{if("undefined"!=typeof XMLHttpRequest&&(!xdomain||hasCORS)){return new XMLHttpRequest}}catch(e){}try{if("undefined"!=typeof XDomainRequest&&!xscheme&&enablesXDR){return new XDomainRequest}}catch(e){}if(!xdomain){try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}}},{"has-cors":38}],21:[function(_dereq_,module,exports){module.exports=function(a,b){var fn=function(){};fn.prototype=b.prototype;a.prototype=new fn;a.prototype.constructor=a}},{}],22:[function(_dereq_,module,exports){exports=module.exports=_dereq_("./debug");exports.log=log;exports.formatArgs=formatArgs;exports.save=save;exports.load=load;exports.useColors=useColors;exports.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"];function useColors(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"==typeof console&&"function"==typeof console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){localStorage.removeItem("debug")}else{localStorage.debug=namespaces}}catch(e){}}function load(){var r;try{r=localStorage.debug}catch(e){}return r}exports.enable(load())},{"./debug":23}],23:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i<len;i++){if(!split[i])continue;namespaces=split[i].replace(/\*/g,".*?");if(namespaces[0]==="-"){exports.skips.push(new RegExp("^"+namespaces.substr(1)+"$"))}else{exports.names.push(new RegExp("^"+namespaces+"$"))}}}function disable(){exports.enable("")}function enabled(name){var i,len;for(i=0,len=exports.skips.length;i<len;i++){if(exports.skips[i].test(name)){return false}}for(i=0,len=exports.names.length;i<len;i++){if(exports.names[i].test(name)){return true}}return false}function coerce(val){if(val instanceof Error)return val.stack||val.message;return val}},{ms:24}],24:[function(_dereq_,module,exports){var s=1e3;var m=s*60;var h=m*60;var d=h*24;var y=d*365.25;module.exports=function(val,options){options=options||{};if("string"==typeof val)return parse(val);return options.long?long(val):short(val)};function parse(str){var match=/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);if(!match)return;var n=parseFloat(match[1]);var type=(match[2]||"ms").toLowerCase();switch(type){case"years":case"year":case"y":return n*y;case"days":case"day":case"d":return n*d;case"hours":case"hour":case"h":return n*h;case"minutes":case"minute":case"m":return n*m;case"seconds":case"second":case"s":return n*s;case"ms":return n}}function short(ms){if(ms>=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms<n)return;if(ms<n*1.5)return Math.floor(ms/n)+" "+name;return Math.ceil(ms/n)+" "+name+"s"}},{}],25:[function(_dereq_,module,exports){(function(global){var keys=_dereq_("./keys");var hasBinary=_dereq_("has-binary");var sliceBuffer=_dereq_("arraybuffer.slice");var base64encoder=_dereq_("base64-arraybuffer");var after=_dereq_("after");var utf8=_dereq_("utf8");var isAndroid=navigator.userAgent.match(/Android/i);var isPhantomJS=/PhantomJS/i.test(navigator.userAgent);var dontSendBlobs=isAndroid||isPhantomJS;exports.protocol=3;var packets=exports.packets={open:0,close:1,ping:2,pong:3,message:4,upgrade:5,noop:6};var packetslist=keys(packets);var err={type:"error",data:"parser error"};var Blob=_dereq_("blob");exports.encodePacket=function(packet,supportsBinary,utf8encode,callback){if("function"==typeof supportsBinary){callback=supportsBinary;supportsBinary=false}if("function"==typeof utf8encode){callback=utf8encode;utf8encode=null}var data=packet.data===undefined?undefined:packet.data.buffer||packet.data;if(global.ArrayBuffer&&data instanceof ArrayBuffer){return encodeArrayBuffer(packet,supportsBinary,callback)}else if(Blob&&data instanceof global.Blob){return encodeBlob(packet,supportsBinary,callback)}if(data&&data.base64){return encodeBase64Object(packet,callback)}var encoded=packets[packet.type];if(undefined!==packet.data){encoded+=utf8encode?utf8.encode(String(packet.data)):String(packet.data)}return callback(""+encoded)};function encodeBase64Object(packet,callback){var message="b"+exports.packets[packet.type]+packet.data.data;return callback(message)}function encodeArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var data=packet.data;var contentArray=new Uint8Array(data);var resultBuffer=new Uint8Array(1+data.byteLength);resultBuffer[0]=packets[packet.type];for(var i=0;i<contentArray.length;i++){resultBuffer[i+1]=contentArray[i]}return callback(resultBuffer.buffer)}function encodeBlobAsArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var fr=new FileReader;fr.onload=function(){packet.data=fr.result;exports.encodePacket(packet,supportsBinary,true,callback)};return fr.readAsArrayBuffer(packet.data)}function encodeBlob(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}if(dontSendBlobs){return encodeBlobAsArrayBuffer(packet,supportsBinary,callback)}var length=new Uint8Array(1);length[0]=packets[packet.type];var blob=new Blob([length.buffer,packet.data]);return callback(blob)}exports.encodeBase64Packet=function(packet,callback){var message="b"+exports.packets[packet.type];if(Blob&&packet.data instanceof Blob){var fr=new FileReader;fr.onload=function(){var b64=fr.result.split(",")[1];callback(message+b64)};return fr.readAsDataURL(packet.data)}var b64data;try{b64data=String.fromCharCode.apply(null,new Uint8Array(packet.data))}catch(e){var typed=new Uint8Array(packet.data);var basic=new Array(typed.length);for(var i=0;i<typed.length;i++){basic[i]=typed[i]}b64data=String.fromCharCode.apply(null,basic)}message+=global.btoa(b64data);return callback(message)};exports.decodePacket=function(data,binaryType,utf8decode){if(typeof data=="string"||data===undefined){if(data.charAt(0)=="b"){return exports.decodeBase64Packet(data.substr(1),binaryType)}if(utf8decode){try{data=utf8.decode(data)}catch(e){return err}}var type=data.charAt(0);if(Number(type)!=type||!packetslist[type]){return err}if(data.length>1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i<ary.length;i++){eachWithIndex(i,ary[i],next)}}exports.decodePayload=function(data,binaryType,callback){if(typeof data!="string"){return exports.decodePayloadAsBinary(data,binaryType,callback)}if(typeof binaryType==="function"){callback=binaryType;binaryType=null}var packet;if(data==""){return callback(err,0,1)}var length="",n,msg;for(var i=0,l=data.length;i<l;i++){var chr=data.charAt(i);if(":"!=chr){length+=chr}else{if(""==length||length!=(n=Number(length))){return callback(err,0,1)}msg=data.substr(i+1,n);if(length!=msg.length){return callback(err,0,1)}if(msg.length){packet=exports.decodePacket(msg,binaryType,true);if(err.type==packet.type&&err.data==packet.data){return callback(err,0,1)}var ret=callback(packet,i+n,l);if(false===ret)return}i+=n;length=""}}if(length!=""){return callback(err,0,1)}};exports.encodePayloadAsArrayBuffer=function(packets,callback){if(!packets.length){return callback(new ArrayBuffer(0))}function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(data){return doneCallback(null,data)})}map(packets,encodeOne,function(err,encodedPackets){var totalLength=encodedPackets.reduce(function(acc,p){var len;if(typeof p==="string"){len=p.length}else{len=p.byteLength}return acc+len.toString().length+len+2},0);var resultArray=new Uint8Array(totalLength);var bufferIndex=0;encodedPackets.forEach(function(p){var isString=typeof p==="string";var ab=p;if(isString){var view=new Uint8Array(p.length);for(var i=0;i<p.length;i++){view[i]=p.charCodeAt(i)}ab=view.buffer}if(isString){resultArray[bufferIndex++]=0}else{resultArray[bufferIndex++]=1}var lenStr=ab.byteLength.toString();for(var i=0;i<lenStr.length;i++){resultArray[bufferIndex++]=parseInt(lenStr[i])}resultArray[bufferIndex++]=255;var view=new Uint8Array(ab);for(var i=0;i<view.length;i++){resultArray[bufferIndex++]=view[i]}});return callback(resultArray.buffer)})};exports.encodePayloadAsBlob=function(packets,callback){function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(encoded){var binaryIdentifier=new Uint8Array(1);binaryIdentifier[0]=1;if(typeof encoded==="string"){var view=new Uint8Array(encoded.length);for(var i=0;i<encoded.length;i++){view[i]=encoded.charCodeAt(i)}encoded=view.buffer;binaryIdentifier[0]=0}var len=encoded instanceof ArrayBuffer?encoded.byteLength:encoded.size;var lenStr=len.toString();var lengthAry=new Uint8Array(lenStr.length+1);for(var i=0;i<lenStr.length;i++){lengthAry[i]=parseInt(lenStr[i])}lengthAry[lenStr.length]=255;if(Blob){var blob=new Blob([binaryIdentifier.buffer,lengthAry.buffer,encoded]);doneCallback(null,blob)}})}map(packets,encodeOne,function(err,results){return callback(new Blob(results))})};exports.decodePayloadAsBinary=function(data,binaryType,callback){if(typeof binaryType==="function"){callback=binaryType;binaryType=null}var bufferTail=data;var buffers=[];var numberTooLong=false;while(bufferTail.byteLength>0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;i<typed.length;i++){msg+=String.fromCharCode(typed[i])}}}buffers.push(msg);bufferTail=sliceBuffer(bufferTail,msgLength)}var total=buffers.length;buffers.forEach(function(buffer,i){callback(exports.decodePacket(buffer,binaryType,true),i,total)})}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./keys":26,after:27,"arraybuffer.slice":28,"base64-arraybuffer":29,blob:30,"has-binary":36,utf8:31}],26:[function(_dereq_,module,exports){module.exports=Object.keys||function keys(obj){var arr=[];var has=Object.prototype.hasOwnProperty;for(var i in obj){if(has.call(obj,i)){arr.push(i)}}return arr}},{}],27:[function(_dereq_,module,exports){module.exports=after;function after(count,callback,err_cb){var bail=false;err_cb=err_cb||noop;proxy.count=count;return count===0?callback():proxy;function proxy(err,result){if(proxy.count<=0){throw new Error("after called too many times")}--proxy.count;if(err){bail=true;callback(err);callback=err_cb}else if(proxy.count===0&&!bail){callback(null,result)}}}function noop(){}},{}],28:[function(_dereq_,module,exports){module.exports=function(arraybuffer,start,end){var bytes=arraybuffer.byteLength;start=start||0;end=end||bytes;if(arraybuffer.slice){return arraybuffer.slice(start,end)}if(start<0){start+=bytes}if(end<0){end+=bytes}if(end>bytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i<end;i++,ii++){result[ii]=abv[i]}return result.buffer}},{}],29:[function(_dereq_,module,exports){(function(chars){"use strict";exports.encode=function(arraybuffer){var bytes=new Uint8Array(arraybuffer),i,len=bytes.length,base64="";for(i=0;i<len;i+=3){base64+=chars[bytes[i]>>2];base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i<len;i+=4){encoded1=chars.indexOf(base64[i]);encoded2=chars.indexOf(base64[i+1]);encoded3=chars.indexOf(base64[i+2]);encoded4=chars.indexOf(base64[i+3]);bytes[p++]=encoded1<<2|encoded2>>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],30:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var a=new Blob(["hi"]);return a.size===2}catch(e){return false}}();var blobSupportsArrayBufferView=blobSupported&&function(){try{var b=new Blob([new Uint8Array([1,2])]);return b.size===2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function mapArrayBufferViews(ary){for(var i=0;i<ary.length;i++){var chunk=ary[i];if(chunk.buffer instanceof ArrayBuffer){var buf=chunk.buffer;if(chunk.byteLength!==buf.byteLength){var copy=new Uint8Array(chunk.byteLength);copy.set(new Uint8Array(buf,chunk.byteOffset,chunk.byteLength));buf=copy.buffer}ary[i]=buf}}}function BlobBuilderConstructor(ary,options){options=options||{};var bb=new BlobBuilder;mapArrayBufferViews(ary);for(var i=0;i<ary.length;i++){bb.append(ary[i])}return options.type?bb.getBlob(options.type):bb.getBlob()}function BlobConstructor(ary,options){mapArrayBufferViews(ary);return new Blob(ary,options||{})}module.exports=function(){if(blobSupported){return blobSupportsArrayBufferView?global.Blob:BlobConstructor}else if(blobBuilderSupported){return BlobBuilderConstructor}else{return undefined}}()}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],31:[function(_dereq_,module,exports){(function(global){(function(root){var freeExports=typeof exports=="object"&&exports;var freeModule=typeof module=="object"&&module&&module.exports==freeExports&&module;var freeGlobal=typeof global=="object"&&global;if(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal){root=freeGlobal}var stringFromCharCode=String.fromCharCode;function ucs2decode(string){var output=[];var counter=0;var length=string.length;var value;var extra;while(counter<length){value=string.charCodeAt(counter++);if(value>=55296&&value<=56319&&counter<length){extra=string.charCodeAt(counter++);if((extra&64512)==56320){output.push(((value&1023)<<10)+(extra&1023)+65536)}else{output.push(value);counter--}}else{output.push(value)}}return output}function ucs2encode(array){var length=array.length;var index=-1;var value;var output="";while(++index<length){value=array[index];if(value>65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function checkScalarValue(codePoint){if(codePoint>=55296&&codePoint<=57343){throw Error("Lone surrogate U+"+codePoint.toString(16).toUpperCase()+" is not a scalar value")
-}}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){checkScalarValue(codePoint);symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index<length){codePoint=codePoints[index];byteString+=encodeCodePoint(codePoint)}return byteString}function readContinuationByte(){if(byteIndex>=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){checkScalarValue(codePoint);return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],32:[function(_dereq_,module,exports){(function(global){var rvalidchars=/^[\],:{}\s]*$/;var rvalidescape=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rvalidtokens=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;var rvalidbraces=/(?:^|:|,)(?:\s*\[)+/g;var rtrimLeft=/^\s+/;var rtrimRight=/\s+$/;module.exports=function parsejson(data){if("string"!=typeof data||!data){return null}data=data.replace(rtrimLeft,"").replace(rtrimRight,"");if(global.JSON&&JSON.parse){return JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,"@").replace(rvalidtokens,"]").replace(rvalidbraces,""))){return new Function("return "+data)()}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],33:[function(_dereq_,module,exports){exports.encode=function(obj){var str="";for(var i in obj){if(obj.hasOwnProperty(i)){if(str.length)str+="&";str+=encodeURIComponent(i)+"="+encodeURIComponent(obj[i])}}return str};exports.decode=function(qs){var qry={};var pairs=qs.split("&");for(var i=0,l=pairs.length;i<l;i++){var pair=pairs[i].split("=");qry[decodeURIComponent(pair[0])]=decodeURIComponent(pair[1])}return qry}},{}],34:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;var parts=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];module.exports=function parseuri(str){var src=str,b=str.indexOf("["),e=str.indexOf("]");if(b!=-1&&e!=-1){str=str.substring(0,b)+str.substring(b,e).replace(/:/g,";")+str.substring(e,str.length)}var m=re.exec(str||""),uri={},i=14;while(i--){uri[parts[i]]=m[i]||""}if(b!=-1&&e!=-1){uri.source=src;uri.host=uri.host.substring(1,uri.host.length-1).replace(/;/g,":");uri.authority=uri.authority.replace("[","").replace("]","").replace(/;/g,":");uri.ipv6uri=true}return uri}},{}],35:[function(_dereq_,module,exports){var global=function(){return this}();var WebSocket=global.WebSocket||global.MozWebSocket;module.exports=WebSocket?ws:null;function ws(uri,protocols,opts){var instance;if(protocols){instance=new WebSocket(uri,protocols)}else{instance=new WebSocket(uri)}return instance}if(WebSocket)ws.prototype=WebSocket.prototype},{}],36:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&"object"==typeof obj){if(obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{isarray:37}],37:[function(_dereq_,module,exports){module.exports=Array.isArray||function(arr){return Object.prototype.toString.call(arr)=="[object Array]"}},{}],38:[function(_dereq_,module,exports){var global=_dereq_("global");try{module.exports="XMLHttpRequest"in global&&"withCredentials"in new global.XMLHttpRequest}catch(err){module.exports=false}},{global:39}],39:[function(_dereq_,module,exports){module.exports=function(){return this}()},{}],40:[function(_dereq_,module,exports){var indexOf=[].indexOf;module.exports=function(arr,obj){if(indexOf)return arr.indexOf(obj);for(var i=0;i<arr.length;++i){if(arr[i]===obj)return i}return-1}},{}],41:[function(_dereq_,module,exports){var has=Object.prototype.hasOwnProperty;exports.keys=Object.keys||function(obj){var keys=[];for(var key in obj){if(has.call(obj,key)){keys.push(key)}}return keys};exports.values=function(obj){var vals=[];for(var key in obj){if(has.call(obj,key)){vals.push(obj[key])}}return vals};exports.merge=function(a,b){for(var key in b){if(has.call(b,key)){a[key]=b[key]}}return a};exports.length=function(obj){return exports.keys(obj).length};exports.isEmpty=function(obj){return 0==exports.length(obj)}},{}],42:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;var parts=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];module.exports=function parseuri(str){var m=re.exec(str||""),uri={},i=14;while(i--){uri[parts[i]]=m[i]||""}return uri}},{}],43:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");var isBuf=_dereq_("./is-buffer");exports.deconstructPacket=function(packet){var buffers=[];var packetData=packet.data;function _deconstructPacket(data){if(!data)return data;if(isBuf(data)){var placeholder={_placeholder:true,num:buffers.length};buffers.push(data);return placeholder}else if(isArray(data)){var newData=new Array(data.length);for(var i=0;i<data.length;i++){newData[i]=_deconstructPacket(data[i])}return newData}else if("object"==typeof data&&!(data instanceof Date)){var newData={};for(var key in data){newData[key]=_deconstructPacket(data[key])}return newData}return data}var pack=packet;pack.data=_deconstructPacket(packetData);pack.attachments=buffers.length;return{packet:pack,buffers:buffers}};exports.reconstructPacket=function(packet,buffers){var curPlaceHolder=0;function _reconstructPacket(data){if(data&&data._placeholder){var buf=buffers[data.num];return buf}else if(isArray(data)){for(var i=0;i<data.length;i++){data[i]=_reconstructPacket(data[i])}return data}else if(data&&"object"==typeof data){for(var key in data){data[key]=_reconstructPacket(data[key])}return data}return data}packet.data=_reconstructPacket(packet.data);packet.attachments=undefined;return packet};exports.removeBlobs=function(data,callback){function _removeBlobs(obj,curKey,containingObject){if(!obj)return obj;if(global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){pendingBlobs++;var fileReader=new FileReader;fileReader.onload=function(){if(containingObject){containingObject[curKey]=this.result}else{bloblessData=this.result}if(!--pendingBlobs){callback(bloblessData)}};fileReader.readAsArrayBuffer(obj)}else if(isArray(obj)){for(var i=0;i<obj.length;i++){_removeBlobs(obj[i],i,obj)}}else if(obj&&"object"==typeof obj&&!isBuf(obj)){for(var key in obj){_removeBlobs(obj[key],key,obj)}}}var pendingBlobs=0;var bloblessData=data;_removeBlobs(bloblessData);if(!pendingBlobs){callback(bloblessData)}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./is-buffer":45,isarray:46}],44:[function(_dereq_,module,exports){var debug=_dereq_("debug")("socket.io-parser");var json=_dereq_("json3");var isArray=_dereq_("isarray");var Emitter=_dereq_("component-emitter");var binary=_dereq_("./binary");var isBuf=_dereq_("./is-buffer");exports.protocol=4;exports.types=["CONNECT","DISCONNECT","EVENT","BINARY_EVENT","ACK","BINARY_ACK","ERROR"];exports.CONNECT=0;exports.DISCONNECT=1;exports.EVENT=2;exports.ACK=3;exports.ERROR=4;exports.BINARY_EVENT=5;exports.BINARY_ACK=6;exports.Encoder=Encoder;exports.Decoder=Decoder;function Encoder(){}Encoder.prototype.encode=function(obj,callback){debug("encoding packet %j",obj);if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){encodeAsBinary(obj,callback)}else{var encoding=encodeAsString(obj);callback([encoding])}};function encodeAsString(obj){var str="";var nsp=false;str+=obj.type;if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){str+=obj.attachments;str+="-"}if(obj.nsp&&"/"!=obj.nsp){nsp=true;str+=obj.nsp}if(null!=obj.id){if(nsp){str+=",";nsp=false}str+=obj.id}if(null!=obj.data){if(nsp)str+=",";str+=json.stringify(obj.data)}debug("encoded %j as %s",obj,str);return str}function encodeAsBinary(obj,callback){function writeEncoding(bloblessData){var deconstruction=binary.deconstructPacket(bloblessData);var pack=encodeAsString(deconstruction.packet);var buffers=deconstruction.buffers;buffers.unshift(pack);callback(buffers)}binary.removeBlobs(obj,writeEncoding)}function Decoder(){this.reconstructor=null}Emitter(Decoder.prototype);Decoder.prototype.add=function(obj){var packet;if("string"==typeof obj){packet=decodeString(obj);if(exports.BINARY_EVENT==packet.type||exports.BINARY_ACK==packet.type){this.reconstructor=new BinaryReconstructor(packet);if(this.reconstructor.reconPack.attachments===0){this.emit("decoded",packet)}}else{this.emit("decoded",packet)}}else if(isBuf(obj)||obj.base64){if(!this.reconstructor){throw new Error("got binary data when not reconstructing a packet")}else{packet=this.reconstructor.takeBinaryData(obj);if(packet){this.reconstructor=null;this.emit("decoded",packet)}}}else{throw new Error("Unknown type: "+obj)}};function decodeString(str){var p={};var i=0;p.type=Number(str.charAt(0));if(null==exports.types[p.type])return error();if(exports.BINARY_EVENT==p.type||exports.BINARY_ACK==p.type){var buf="";while(str.charAt(++i)!="-"){buf+=str.charAt(i);if(i==str.length)break}if(buf!=Number(buf)||str.charAt(i)!="-"){throw new Error("Illegal attachments")}p.attachments=Number(buf)}if("/"==str.charAt(i+1)){p.nsp="";while(++i){var c=str.charAt(i);if(","==c)break;p.nsp+=c;if(i==str.length)break}}else{p.nsp="/"}var next=str.charAt(i+1);if(""!==next&&Number(next)==next){p.id="";while(++i){var c=str.charAt(i);if(null==c||Number(c)!=c){--i;break}p.id+=str.charAt(i);if(i==str.length)break}p.id=Number(p.id)}if(str.charAt(++i)){try{p.data=json.parse(str.substr(i))}catch(e){return error()}}debug("decoded %s as %j",str,p);return p}Decoder.prototype.destroy=function(){if(this.reconstructor){this.reconstructor.finishedReconstruction()}};function BinaryReconstructor(packet){this.reconPack=packet;this.buffers=[]}BinaryReconstructor.prototype.takeBinaryData=function(binData){this.buffers.push(binData);if(this.buffers.length==this.reconPack.attachments){var packet=binary.reconstructPacket(this.reconPack,this.buffers);this.finishedReconstruction();return packet}return null};BinaryReconstructor.prototype.finishedReconstruction=function(){this.reconPack=null;this.buffers=[]};function error(data){return{type:exports.ERROR,data:"parser error"}}},{"./binary":43,"./is-buffer":45,"component-emitter":9,debug:10,isarray:46,json3:47}],45:[function(_dereq_,module,exports){(function(global){module.exports=isBuf;function isBuf(obj){return global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],46:[function(_dereq_,module,exports){module.exports=_dereq_(37)},{}],47:[function(_dereq_,module,exports){(function(window){var getClass={}.toString,isProperty,forEach,undef;var isLoader=typeof define==="function"&&define.amd;var nativeJSON=typeof JSON=="object"&&JSON;var JSON3=typeof exports=="object"&&exports&&!exports.nodeType&&exports;if(JSON3&&nativeJSON){JSON3.stringify=nativeJSON.stringify;JSON3.parse=nativeJSON.parse}else{JSON3=window.JSON=nativeJSON||{}}var isExtended=new Date(-0xc782b5b800cec);try{isExtended=isExtended.getUTCFullYear()==-109252&&isExtended.getUTCMonth()===0&&isExtended.getUTCDate()===1&&isExtended.getUTCHours()==10&&isExtended.getUTCMinutes()==37&&isExtended.getUTCSeconds()==6&&isExtended.getUTCMilliseconds()==708}catch(exception){}function has(name){if(has[name]!==undef){return has[name]}var isSupported;if(name=="bug-string-char-index"){isSupported="a"[0]!="a"}else if(name=="json"){isSupported=has("json-stringify")&&has("json-parse")}else{var value,serialized='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if(name=="json-stringify"){var stringify=JSON3.stringify,stringifySupported=typeof stringify=="function"&&isExtended;if(stringifySupported){(value=function(){return 1}).toJSON=value;try{stringifySupported=stringify(0)==="0"&&stringify(new Number)==="0"&&stringify(new String)=='""'&&stringify(getClass)===undef&&stringify(undef)===undef&&stringify()===undef&&stringify(value)==="1"&&stringify([value])=="[1]"&&stringify([undef])=="[null]"&&stringify(null)=="null"&&stringify([undef,getClass,null])=="[null,null,null]"&&stringify({a:[value,true,false,null,"\x00\b\n\f\r  "]})==serialized&&stringify(null,value)==="1"&&stringify([1,2],null,1)=="[\n 1,\n 2\n]"&&stringify(new Date(-864e13))=='"-271821-04-20T00:00:00.000Z"'&&stringify(new Date(864e13))=='"+275760-09-13T00:00:00.000Z"'&&stringify(new Date(-621987552e5))=='"-000001-01-01T00:00:00.000Z"'&&stringify(new Date(-1))=='"1969-12-31T23:59:59.999Z"'}catch(exception){stringifySupported=false}}isSupported=stringifySupported}if(name=="json-parse"){var parse=JSON3.parse;if(typeof parse=="function"){try{if(parse("0")===0&&!parse(false)){value=parse(serialized);var parseSupported=value["a"].length==5&&value["a"][0]===1;if(parseSupported){try{parseSupported=!parse('"    "')}catch(exception){}if(parseSupported){try{parseSupported=parse("01")!==1}catch(exception){}}if(parseSupported){try{parseSupported=parse("1.")!==1}catch(exception){}}}}}catch(exception){parseSupported=false}}isSupported=parseSupported}}return has[name]=!!isSupported}if(!has("json")){var functionClass="[object Function]";var dateClass="[object Date]";var numberClass="[object Number]";var stringClass="[object String]";var arrayClass="[object Array]";var booleanClass="[object Boolean]";var charIndexBuggy=has("bug-string-char-index");if(!isExtended){var floor=Math.floor;var Months=[0,31,59,90,120,151,181,212,243,273,304,334];var getDay=function(year,month){return Months[month]+365*(year-1970)+floor((year-1969+(month=+(month>1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty={}.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}var PrimitiveTypes={"boolean":1,number:1,string:1,undefined:1};var isHostType=function(object,property){var type=typeof object[property];return type=="object"?!!object[property]:!PrimitiveTypes[type]};forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&isHostType(object,"hasOwnProperty")?object.hasOwnProperty:isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,isLarge=length>10&&charIndexBuggy,symbols;if(isLarge){symbols=value.split("")}for(;index<length;index++){var charCode=value.charCodeAt(index);switch(charCode){case 8:case 9:case 10:case 12:case 13:case 34:case 92:result+=Escapes[charCode];break;default:if(charCode<32){result+=unicodePrefix+toPaddedString(2,charCode.toString(16));break}result+=isLarge?symbols[index]:charIndexBuggy?value.charAt(index):value[index]}}return result+'"'};var serialize=function(property,object,callback,properties,whitespace,indentation,stack){var value,className,year,month,date,time,hours,minutes,seconds,milliseconds,results,element,index,length,prefix,result;try{value=object[property]}catch(exception){}if(typeof value=="object"&&value){className=getClass.call(value);if(className==dateClass&&!isProperty.call(value,"toJSON")){if(value>-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index<length;index++){element=serialize(index,value,callback,properties,whitespace,indentation,stack);results.push(element===undef?"null":element)}result=results.length?whitespace?"[\n"+indentation+results.join(",\n"+indentation)+"\n"+prefix+"]":"["+results.join(",")+"]":"[]"}else{forEach(properties||value,function(property){var element=serialize(property,value,callback,properties,whitespace,indentation,stack);if(element!==undef){results.push(quote(property)+":"+(whitespace?" ":"")+element)}});result=results.length?whitespace?"{\n"+indentation+results.join(",\n"+indentation)+"\n"+prefix+"}":"{"+results.join(",")+"}":"{}"}stack.pop();return result}};JSON3.stringify=function(source,filter,width){var whitespace,callback,properties,className;if(typeof filter=="function"||typeof filter=="object"&&filter){if((className=getClass.call(filter))==functionClass){callback=filter}else if(className==arrayClass){properties={};for(var index=0,length=filter.length,value;index<length;value=filter[index++],(className=getClass.call(value),className==stringClass||className==numberClass)&&(properties[value]=1));}}if(width){if((className=getClass.call(width))==numberClass){if((width-=width%1)>0){for(whitespace="",width>10&&(width=10);whitespace.length<width;whitespace+=" ");}}else if(className==stringClass){whitespace=width.length<=10?width:width.slice(0,10)}}return serialize("",(value={},value[""]=source,value),callback,properties,whitespace,"",[])}}if(!has("json-parse")){var fromCharCode=String.fromCharCode;var Unescapes={92:"\\",34:'"',47:"/",98:"\b",116:"  ",110:"\n",102:"\f",114:"\r"};var Index,Source;var abort=function(){Index=Source=null;throw SyntaxError()};var lex=function(){var source=Source,length=source.length,value,begin,position,isSigned,charCode;while(Index<length){charCode=source.charCodeAt(Index);switch(charCode){case 9:case 10:case 13:case 32:Index++;break;case 123:case 125:case 91:case 93:case 58:case 44:value=charIndexBuggy?source.charAt(Index):source[Index];Index++;return value;case 34:for(value="@",Index++;Index<length;){charCode=source.charCodeAt(Index);if(charCode<32){abort()}else if(charCode==92){charCode=source.charCodeAt(++Index);switch(charCode){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:value+=Unescapes[charCode];Index++;break;case 117:begin=++Index;for(position=Index+4;Index<position;Index++){charCode=source.charCodeAt(Index);if(!(charCode>=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index<length&&(charCode=source.charCodeAt(Index),charCode>=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex())}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};JSON3.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}if(isLoader){define(function(){return JSON3})}})(this)},{}],48:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i<list.length;i++){array[i-index]=list[i]}return array}},{}]},{},[1])(1)});
\ No newline at end of file
index a19dc77210d06a2793b677bf0315bfe8159074e0..d1e2e036dc275cf2d7a4130356483280f55ad353 100644 (file)
@@ -1,6 +1,4 @@
 .cheat-sheet
-  padding 1em
-
   table
     width 100%
     border-collapse collapse
diff --git a/src/stylus/dragbar.styl b/src/stylus/dragbar.styl
new file mode 100644 (file)
index 0000000..d652550
--- /dev/null
@@ -0,0 +1,20 @@
+.dragbar
+  position absolute
+  background #aaa
+  border-top 3px solid #888
+  border-bottom 3px solid #888
+
+  &:hover
+    background #ccc
+
+.dragbar-vertical
+  width 100%
+  height 4px
+  cursor row-resize
+  margin-top -10px
+
+.dragbar-horizontal
+  width 4px
+  height 100%
+  cursor col-resize
+  margin-right -10px
index af2013350b0f1d5c9f7725e9fbe792afb4e93279..11d2b63185fdd6568a049c47ad6588b0152bafd3 100644 (file)
@@ -1,5 +1,5 @@
-.files .new-folder .modal-body input
-  width calc(100% - 18px)
+.files .new-folder input
+  width calc(100% - 8px)
 
 .files-name
   display flex
     background #ddd
     color #444
 
-  &.files-upload
-    color #fff
-    background #0078e7
-
-    &:hover
-      background-image \
-        linear-gradient(transparent, rgba(0, 0, 0, .05) 40%, rgba(0, 0, 0, .1))
-
-
 .files-body
   flex-direction row
 
index 4e06537347e1807babee43c1bb810021c845c480..0b36af3ce88a70866b2bf637a69276b5cf661c49 100644 (file)
@@ -1,10 +1,11 @@
 .header
-  padding 0
+  width calc(100% - 2em)
+  padding 0 1em
   border 0
 
   .header-content
-    max-width 800px
-    margin auto
+    max-width limit-width
+    margin auto
     text-align left
     display flex
     flex-wrap wrap
@@ -17,6 +18,7 @@
   .banner
     white-space nowrap
     flex 1
+    margin 0
 
     img
       vertical-align top
 
   .estop
     margin 0 5px
+    height 140px
 
-@media only screen and (max-width 985px)
-  .header .header-content > *
-    margin-left 70px
+@media only screen and (max-width collapse-width)
+  .header .header-content
+    margin-left 50px
+
+    .video
+      display none
index 7b82353d475f17cad81502e6808257d532fe89af..70a731ca7448350f8e317f6a9481c002fb517581 100644 (file)
@@ -1,13 +1,15 @@
 .indicators
   padding 1em 0
   text-align center
+  display grid
+  grid-template-columns 1fr 1fr
 
   table
-    display inline-block
+    height 100%
     vertical-align top
-    margin 0.5em
     empty-cells show
-    border 3px solid #bbb
+    border 2px solid #bbb
+    border-collapse separate
 
     td, th
       padding 4px
@@ -34,7 +36,7 @@
     th.separator
       background #ccc
       border 1px solid #ccc
-      padding 1px
+      padding 0
 
     &.motor_fault
       td, th
@@ -46,3 +48,8 @@
 
         &:hover
           opacity 0.5
+
+
+@media only screen and (max-width 1000px)
+  .indicators
+    grid-template-columns 1fr
index f7a8aafe3917e1fa5cf18b33b335f28a61b5ac03..c51193c39736b8f45e6f98e9eb1b5600f1e0f965 100644 (file)
@@ -4,7 +4,7 @@
   width 300px
   min-height 100px
   margin-top -50px
-  margin-left -150px
+  margin-left -(sidebar-width)
   top 50%
   left 50%
   padding 20px 30px
index fc84380492ef27f7516743f99fc749855556b88c..7608aa3f4fe9b0b2c4b30703b90a0f3382e228dd 100644 (file)
@@ -1,12 +1,8 @@
 #main
   overflow-y scroll
-  position fixed
-  top 0
-  right 0
-  left 150px
-  bottom 0
   display flex
   flex-direction column
+  flex 1
 
   .content
     width 100%
@@ -33,7 +29,3 @@
         padding 0.7em 1em
         border-radius 3px
         display inline-block
-
-@media (max-width: 48em)
-  #main
-    left 0
index 46c01c590bff5137710e82160da522cc462bbd59..f9bd487256efa1c395861dbb37573772d266f557 100644 (file)
@@ -1,16 +1,32 @@
+#menu, #menuLink
+  transition: all 0.2s ease-out;
+
 #menu
-  z-index 40
+  width sidebar-width
+  background #222
+  overflow hidden
+
+  a
+    color #999
+    padding: 0.6em 0 0.6em 0.6em
 
   .save
     display block
     margin 0.25em 0.6em
 
+  .pure-menu li
+    a:hover, a:focus
+      background #333
+
   .pure-menu-list
     border 0
 
   .pure-menu-heading
     background inherit
+    margin 0
     padding 0
+    font-size 110%
+    color #fff
 
     .pure-menu-link
       padding 0.6em
 
   .pure-menu-item .pure-menu-link
     padding-left 1.5em
+
+#menuLink
+  position absolute
+  opacity 0.9
+  background #222
+  display none
+  font-size 10px
+  width 2em
+  height auto
+  padding 2.1em 1.6em
+
+  &:hover
+    background #333
+
+  span
+    position relative
+    display block
+
+  span, span:before, span:after
+    background-color #fff
+    width 100%
+    height 0.2em
+
+  span:before, span:after
+    position absolute
+    content " "
+    margin-top -0.6em
+
+  span:after
+    margin-top 0.6em
+
+
+@media (max-width: collapse-width)
+  #menu
+    position absolute
+    z-index 100
+    height 100vh
+    width 0
+    opacity 0.9
+
+  #menuLink
+    display block
+    z-index 100
+
+  #layout.active
+    #menu
+      width sidebar-width
+
+    #menuLink
+      margin-left sidebar-width
index cdcad241a1b974de66c1d6de1af2b15600dc0651..05f2b297069b8c7df3a1d36e28a981c6e1a1fbc0 100644 (file)
 
 .modal-body
   overflow-y auto
+  padding 0.125em 0
+
+.modal-footer
+  display flex
 
 .modal-footer > div
   display flex
   gap 0.5em
   margin-top 1em
-  justify-content right
+  margin-left auto
 
 .modal-enter, .modal-leave
   opacity 0
index 26566aafe7f53437a00ea1be28705dc7b0fabfaa..4c6e0c2e8b861d5d047e02c04a416d4c736a9011 100644 (file)
@@ -1,45 +1,45 @@
 .path-viewer
+  display flex
+  flex-direction column
+  overflow hidden
+
+  canvas
+    outline none
+    border none
+
   table
-    margin 0.25em
     width 100%
 
-  .path-viewer-toolbar
-    > *
-      margin 0.25em
+  .path-viewer-content
+    flex 1
+    margin 0
+    overflow hidden
+    background #eee
 
-    .tool-button
-      display inline-block
-      cursor pointer
-      border 2px solid transparent
-      border-radius 2px
-      text-align center
+  .path-viewer-messages
+    margin 0
+    max-height 30%
+    overflow-y auto
 
-      &:hover
-        opacity 0.7
-        border 2px inset #eee
+    tr:nth-child(odd)
+      background #f3f3f3
 
-      &.active
-        border 2px inset #888
-        background #ddd
+    th
+      background #ddd
+      color #444
+      padding 0.25em 0.5em
 
-      img
-        max-height 32px
-        vertical-align bottom
+    td
+      padding 0.125em 0.5em
 
-      .fa
-        font-size 28px
-        display inline-block
-        width 32px
+    th, td
+      text-align left
 
-  .path-viewer-messages
-    margin 0.125em 0
+      &.location
+        white-space nowrap
 
-    th, td
-      padding 0.125em
+      &.message
+        width 100%
 
       &.level
-        text-transform capitalize
-
-  .path-viewer-content
-    margin-bottom 0.5em
-    background #eee
+        text-transform uppercase
index f7f12710b6354548e27a81dc959e7f5e68fea1cd..b5bae053774767466057f04e5046d8fdd573e695 100644 (file)
@@ -29,8 +29,8 @@ body, #layout
   overflow hidden
   height 100vh
 
-#layout.active
-  width calc(100vw - 150px)
+#layout
+  display flex
 
 [v-cloak]
   display none
@@ -43,11 +43,20 @@ tt
 .content
   padding 0
 
-.file-upload
-  display none
+button
+  white-space nowrap
+
+  .fa
+    margin-right 0.25em
+
+button-hover = linear-gradient(transparent, #0000000d 40%, #00000019)
+limit-width = 1200px
+sidebar-width = 110px
+collapse-width = 810px
 
 @import('status-colors')
 @import('color-picker')
+@import('dragbar')
 @import('attention')
 @import('status')
 @import('header')
@@ -60,6 +69,7 @@ tt
 @import('navbar')
 @import('video')
 @import('files')
+@import('upload-dialog')
 @import('code-mirror')
 @import('view-control')
 @import('view-settings')
@@ -67,7 +77,7 @@ tt
 @import('view-viewer')
 @import('view-files')
 @import('view-camera')
-@import('view-help')
+@import('view-docs')
 @import('path-viewer')
 @import('console')
 @import('log')
index e3e3714bd7fae5ff8faba581b37cea2c2d09e04b..c5017d242ab9c20f529d7b312282e2f3ea10a2dd 100644 (file)
     cursor pointer
     text-decoration none
     text-align center
-    background #f6f6f6
+    background #eee
     border-top-left-radius 5px
     border-top-right-radius 5px
     border-top 2px solid #f6f6f6
     border-left 1px solid #f6f6f6
     border-right 1px solid #f6f6f6
     margin-right 2px
+    padding 0.5em
+
+    &:hover
+      background-image button-hover
 
   > section
     display none
diff --git a/src/stylus/upload-dialog.styl b/src/stylus/upload-dialog.styl
new file mode 100644 (file)
index 0000000..c6cc47d
--- /dev/null
@@ -0,0 +1,13 @@
+.upload-dialog
+  > form
+    display none
+
+  .progress
+    height 1.75em
+
+    .bar
+      height 100%
+      background #f2ac45
+      line-height 1.75em
+      padding 0 0.5em
+      text-align right
index 1eb939f9be63778313ade565b0a2ffc768c0bba2..edcfb85e2a6cd1ce8ba01ca3a2654aab33eea47a 100644 (file)
@@ -1,13 +1,18 @@
-.view-camera-page #main > .header .video
-  display none
+.view-camera-page #main
+  > .header .video
+    display none
 
 .view-camera
-  max-width inherit
   margin-bottom 0
   background #000
+  height 100%
+  overflow hidden
+
+  > div
+    height 100%
 
   .video
-    height calc(100vh - 177px)
+    height calc(100% - 40px)
     overflow hidden
 
     img
index 750d6a75ce8747b3e6ba4a48b74af7632becad52..cd274bdb66f6ad921299e64caae7386082c42ad8 100644 (file)
@@ -1,16 +1,27 @@
 .view-control
-  table
-    border-collapse collapse
+  max-width limit-width
+  margin auto
+
+  #control
+    display flex
+    flex-direction column
 
-    &:first-child
+    > *
       margin 0.5em 0
 
+    .info-blocks
+      display grid
+      grid-template-columns auto auto auto
+      column-gap 4px
+      row-gap 4px
+
+  table
+    border-collapse collapse
+
     td, th
       border 1px solid #ddd
 
   .axes
-    width 100%
-
     .axis-x .name
       color #f00
 
 
   .macros
     display grid
-    grid-template-columns repeat(8, 11.625%)
+    grid-template-columns repeat(8, 1fr)
     column-gap 1%
     row-gap 8px
-    margin-bottom 10px
+    padding 0 0.25em
 
     .macro
       text-align left
       padding 0.5em
 
       &:hover
-        opacity 0.9
+        background-image button-hover
+
+      .fa
+        margin-right 0.25em
 
   .info
     empty-cells show
-    width 32.9%
-    display inline-block
 
     th, td
       height 1.75em
   .gcode-view, .history
     font-family courier
     overflow auto
-    width 100%
     height 450px
     white-space nowrap
 
   color #000
 
 
+@media only screen and (min-width 1350px)
+  .view-control #control
+    .macros
+      grid-template-columns repeat(12, 1fr)
+
 @media only screen and (max-width 970px)
   .view-control #control
     .path-viewer.small .path-viewer-content
       .absolute, .offset
         display none
 
-    > *:nth-of-type(n)
-      float none
-      clear both
-      width 99%
+    .macros
+      grid-template-columns repeat(6, 1fr)
+
+    .info-blocks
+      grid-template-columns auto
+
+@media only screen and (max-width 570px)
+  .view-control #control
+    .macros
+      grid-template-columns repeat(4, 1fr)
diff --git a/src/stylus/view-docs.styl b/src/stylus/view-docs.styl
new file mode 100644 (file)
index 0000000..75d014a
--- /dev/null
@@ -0,0 +1,6 @@
+.view-docs
+  .docs-view
+    padding 0 1em
+    width calc(100% - 2em)
+    max-width limit-width
+    margin 0 auto
index 727fa759f904c29f31fc115cd0c3ca085603949b..ae0689d66577487b2a961c7cc697e40a9df6b90c 100644 (file)
@@ -1,5 +1,4 @@
 .view-editor
-  max-width inherit
   margin-bottom 0
 
   .navbar .filename
index 8990bd242a5192df7614d9cf3e5f5f7bf1924e49..676b3a9f837a3dd117c845765a03e26793a228f7 100644 (file)
@@ -1,7 +1,6 @@
 .view-files
-  max-width inherit
   margin-bottom 0
 
   .files-path-bar
-    .new-folder
+    .files-upload, .new-folder
       display none
diff --git a/src/stylus/view-help.styl b/src/stylus/view-help.styl
deleted file mode 100644 (file)
index c517a67..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#main .view-help
-  padding 0 1em
-  width calc(100% - 2em)
index 52c03e67ed42d80113206669b6df75ed9b10ee71..f0839bd87fb8b53b06d23bc4ba71d1c97d61153e 100644 (file)
@@ -1,12 +1,17 @@
 .view-settings
-  max-width inherit
   margin-bottom 0
 
   .navbar button
     margin-left auto
+    padding 0.25em 1em
+    border-radius 0
+    height 40px
 
   .settings-view
     margin 0 auto
-    max-width 800px
+    max-width limit-width
     margin-bottom 50px
     padding 0 1em
+
+#settings-admin a
+  text-decoration none
index d13eed038e31e00720722b2e250a799d98c0a37c..1c22668b8d9e70f04e6914878a13c54f9a9abd3e 100644 (file)
@@ -1,7 +1,20 @@
+.view-viewer-page #main
+  height 100vh
+
 .view-viewer
-  max-width inherit
+  height 100%
+  overflow hidden
   margin-bottom 0
 
+  #viewer
+    display flex
+    flex-direction column
+    height 100%
+    overflow hidden
+
+  .path-viewer
+    height 100%
+
   .navbar
     .filename
       flex 1
     .nav-item
       align-items center
 
-  .path-viewer
-    height calc(100vh - 180px)
-
-    .path-viewer-content
-      height 100%
-      margin 0
-      overflow hidden
-
 .viewer-help-dialog
   .modal-container
     width 600px