#767: Fix #709: typing '\text{' barfs

diff --git a/circle.yml b/circle.yml
index 331b639..7fd97e5 100644
--- a/circle.yml
+++ b/circle.yml
@@ -44,19 +44,27 @@
 
         mkdir -p ~/sauce-connect
         cd ~/sauce-connect
+
         if [ -x sc-*-linux/bin/sc ]; then
           echo Using cached sc-*-linux/bin/sc
         else
           time wget https://saucelabs.com/downloads/sc-latest-linux.tar.gz
           time tar -xzf sc-latest-linux.tar.gz
         fi
-        # Sauce Connect randomly fails so try twice https://git.io/vPN8v
+
         time sc-*-linux/bin/sc --user $SAUCE_USERNAME --api-key $SAUCE_ACCESS_KEY \
           --readyfile ~/sauce_is_ready
         test -e ~/sauce_was_ready && exit
+
+        echo 'Sauce Connect failed, try redownloading (https://git.io/vSxsJ)'
+        rm -rf *
+        time wget https://saucelabs.com/downloads/sc-latest-linux.tar.gz
+        time tar -xzf sc-latest-linux.tar.gz
+
         time sc-*-linux/bin/sc --user $SAUCE_USERNAME --api-key $SAUCE_ACCESS_KEY \
           --readyfile ~/sauce_is_ready
         test -e ~/sauce_was_ready && exit
+
         echo 'ERROR: Exited twice without creating readyfile' \
           | tee /dev/stderr > ~/sauce_is_ready
         exit 1
@@ -65,6 +73,17 @@
 
 test:
   pre:
+    - |-
+      # Generate link to Many-Worlds build and add to GitHub Commit Status
+      curl -i -X POST https://api.github.com/repos/mathquill/mathquill/statuses/$CIRCLE_SHA1 \
+           -u MathQuillBot:$GITHUB_STATUS_API_KEY \
+           -d '{
+                 "context": "ci/many-worlds",
+                 "state": "success",
+                 "description": "Try the tests on the Many-Worlds build of this commit:",
+                 "target_url": "http://many-worlds.glitch.me/mathquill/mathquill/commit/'$CIRCLE_SHA1'/test/"
+               }'
+
     # Safari on Sauce can only connect to port 3000, 4000, 7000, or 8000. Edge needs port 7000 or 8000.
     # https://david263a.wordpress.com/2015/04/18/fixing-safari-cant-connect-to-localhost-issue-when-using-sauce-labs-connect-tunnel/
     # https://support.saucelabs.com/customer/portal/questions/14368823-requests-to-localhost-on-microsoft-edge-are-failing-over-sauce-connect
@@ -148,7 +167,8 @@
                  "framework": "mocha",
                  "url": "http://localhost:8000/test/unit.html?post_xunit_to=http://localhost:9000",
                  "platforms": [["", "Chrome", ""]]
-      }' | tee /dev/stderr | tail -1 > js-tests.json
+               }' \
+      | tee /dev/stderr | tail -1 > js-tests.json
 
       echo '2. Wait for tests to finish:'
       echo
diff --git a/docs/Api_Methods.md b/docs/Api_Methods.md
index b7db00c..a776774 100644
--- a/docs/Api_Methods.md
+++ b/docs/Api_Methods.md
@@ -190,7 +190,7 @@
 
 Move the cursor to the left/right end of the editable field, respectively. These are shorthand for [`.moveToDirEnd(L/R)`](#movetodirenddirection), respectively.
 
-## .movetoDirEnd(direction)
+## .moveToDirEnd(direction)
 
 Moves the cursor to the end of the mathfield in the direction specified. The direction can be one of `MQ.L` or `MQ.R`. These are constants, where `MQ.L === -MQ.R` and vice versa. This function may be easier to use than [moveToLeftEnd or moveToRightEnd](#movetoleftend-movetorightend) if used in the [`moveOutOf` handler](Config.md#outof-handlers).
 
diff --git a/src/commands/math/commands.js b/src/commands/math/commands.js
index 53fb861..68838bb 100644
--- a/src/commands/math/commands.js
+++ b/src/commands/math/commands.js
@@ -81,6 +81,8 @@
 LatexCmds.overline = LatexCmds.bar = bind(Style, '\\overline', 'span', 'class="mq-non-leaf mq-overline"');
 LatexCmds.overrightarrow = bind(Style, '\\overrightarrow', 'span', 'class="mq-non-leaf mq-overarrow mq-arrow-right"');
 LatexCmds.overleftarrow = bind(Style, '\\overleftarrow', 'span', 'class="mq-non-leaf mq-overarrow mq-arrow-left"');
+LatexCmds.overleftrightarrow = bind(Style, '\\overleftrightarrow', 'span', 'class="mq-non-leaf mq-overarrow mq-arrow-both"');
+LatexCmds.overarc = bind(Style, '\\overarc', 'span', 'class="mq-non-leaf mq-overarc"');
 LatexCmds.dot = P(MathCommand, function(_, super_) {
     _.init = function() {
         super_.init.call(this, '\\dot', '<span class="mq-non-leaf"><span class="mq-dot-recurring-inner">'
@@ -328,6 +330,15 @@
     this.sup.downOutOf = insLeftOfMeUnlessAtEnd;
     super_.finalizeTree.call(this);
   };
+  _.reflow = function() {
+     var $block = this.jQ;//mq-supsub
+
+     var h = $block.prev().innerHeight() ;
+     h *= 0.6 ;
+
+     $block.css( 'vertical-align',  h + 'px' ) ;
+
+  } ;
 });
 
 var SummationNotation = P(MathCommand, function(_, super_) {
diff --git a/src/css/math.less b/src/css/math.less
index 743f7ca..dfa53e2 100644
--- a/src/css/math.less
+++ b/src/css/math.less
@@ -50,7 +50,9 @@
 
   .mq-text-mode {
     display: inline-block;
+    white-space: pre;  
   }
+
   .mq-text-mode.mq-hasCursor {
     box-shadow: inset darkgray 0 .1em .2em;
     padding: 0 .1em;
@@ -348,10 +350,24 @@
     font-family: @symbola;
   }
 
+  .mq-overarc {
+    border-top: 1px solid black;
+    -webkit-border-top-right-radius: 50% .3em;
+    -moz-border-radius-topright: 50% .3em;
+    border-top-right-radius: 50% .3em;
+    -webkit-border-top-left-radius: 50% .3em;
+    -moz-border-radius-topleft: 50% .3em;
+    border-top-left-radius: 50% .3em;
+    margin-top: 1px;
+    padding-top: 0.15em;
+  }
+
   .mq-overarrow {
+    min-width: .5em;
     border-top: 1px solid black;
     margin-top: 1px;
     padding-top: 0.2em;
+    text-align: center;
 
     &:before {
       display: block;
@@ -370,5 +386,34 @@
       filter: FlipH;
       -ms-filter: "FlipH";
     }
+    &.mq-arrow-both {
+      vertical-align: text-bottom;
+
+      &.mq-empty {
+        min-height: 1.23em;
+
+        &:after {
+          top: -0.34em;
+        }
+      }
+      &:before{
+        -moz-transform: scaleX(-1);
+        -o-transform: scaleX(-1);
+        -webkit-transform: scaleX(-1);
+        transform: scaleX(-1);
+        filter: FlipH;
+        -ms-filter: "FlipH";
+      }
+      &:after {
+        display: block;
+        position: relative;
+        top: -2.3em;
+        font-size: 0.5em;
+        line-height: 0em;
+        content: '\27A4';
+        visibility: visible; //must override .mq-editable-field.mq-empty:after
+        text-align: right;
+      }
+    }
   }
 }
diff --git a/src/publicapi.js b/src/publicapi.js
index 84b13db..63cd46f 100644
--- a/src/publicapi.js
+++ b/src/publicapi.js
@@ -160,6 +160,15 @@
       if (this.__controller.blurred) this.__controller.cursor.hide().parent.blur();
       return this;
     };
+    _.empty = function() {
+      var root = this.__controller.root, cursor = this.__controller.cursor;
+      root.eachChild('postOrder', 'dispose');
+      root.ends[L] = root.ends[R] = 0;
+      root.jQ.empty();
+      delete cursor.selection;
+      cursor.insAtRightEnd(root);
+      return this;
+    };
     _.cmd = function(cmd) {
       var ctrlr = this.__controller.notify(), cursor = ctrlr.cursor;
       if (/^\\[a-z]+$/i.test(cmd)) {
diff --git a/src/services/keystroke.js b/src/services/keystroke.js
index fc3fc8e..a53cb5a 100644
--- a/src/services/keystroke.js
+++ b/src/services/keystroke.js
@@ -241,11 +241,15 @@
   _.ctrlDeleteDir = function(dir) {
     prayDirection(dir);
     var cursor = this.cursor;
-    if (!cursor[L] || cursor.selection) return this.deleteDir();
+    if (!cursor[dir] || cursor.selection) return this.deleteDir(dir);
 
     this.notify('edit');
-    Fragment(cursor.parent.ends[L], cursor[L]).remove();
-    cursor.insAtDirEnd(L, cursor.parent);
+    if (dir === L) {
+      Fragment(cursor.parent.ends[L], cursor[L]).remove();
+    } else {
+      Fragment(cursor[R], cursor.parent.ends[R]).remove();
+    };
+    cursor.insAtDirEnd(dir, cursor.parent);
 
     if (cursor[L].siblingDeleted) cursor[L].siblingDeleted(cursor.options, R);
     if (cursor[R].siblingDeleted) cursor[R].siblingDeleted(cursor.options, L);
diff --git a/src/services/textarea.js b/src/services/textarea.js
index eaed10c..8782e26 100644
--- a/src/services/textarea.js
+++ b/src/services/textarea.js
@@ -48,7 +48,8 @@
     var ctrlr = this, root = ctrlr.root, cursor = ctrlr.cursor,
       textarea = ctrlr.textarea, textareaSpan = ctrlr.textareaSpan;
 
-    this.container.prepend('<span class="mq-selectable">$'+ctrlr.exportLatex()+'$</span>');
+    this.container.prepend(jQuery('<span class="mq-selectable">')
+      .text('$'+ctrlr.exportLatex()+'$'));
     ctrlr.blurred = true;
     textarea.bind('cut paste', false)
     .bind('copy', function() { ctrlr.setTextareaSelection(); })
diff --git a/test/unit/latex.test.js b/test/unit/latex.test.js
index 7baa156..e6e4096 100644
--- a/test/unit/latex.test.js
+++ b/test/unit/latex.test.js
@@ -329,4 +329,18 @@
     testCantParse('langlerfish/ranglerfish (checking for confusion with langle/rangle)',
 		    '\\left\\langlerfish 123\\right\\ranglerfish)');
   });
+
+  suite('selectable span', function() {
+    setup(function() {
+      MQ.StaticMath($('<span>2&lt;x</span>').appendTo('#mock')[0]);
+    });
+
+    function selectableContent() {
+      return document.querySelector('#mock .mq-selectable').textContent;
+    }
+
+    test('escapes < in textContent', function () {
+      assert.equal(selectableContent(), '$2<x$');
+    });
+  });
 });
diff --git a/test/unit/publicapi.test.js b/test/unit/publicapi.test.js
index 4b71904..f980855 100644
--- a/test/unit/publicapi.test.js
+++ b/test/unit/publicapi.test.js
@@ -108,7 +108,7 @@
       mq.latex('x+y');
       assert.equal(mq.html(), '<var>x</var><span class="mq-binary-operator">+</span><var>y</var>');
     });
-    
+
     test('.text() with incomplete commands', function() {
       assert.equal(mq.text(), '');
       mq.typedText('\\');
@@ -157,6 +157,12 @@
       assert.equal(mq.__controller.cursor[L].ctrlSeq, '0');
       assert.equal(mq.__controller.cursor[R], 0);
     });
+
+    test('.empty()', function() {
+      mq.latex('xyz');
+      mq.empty();
+      assert.equal(mq.latex(), '');
+    });
   });
 
   test('edit handler interface versioning', function() {
diff --git a/test/unit/typing.test.js b/test/unit/typing.test.js
index a3f2253..c77fc90 100644
--- a/test/unit/typing.test.js
+++ b/test/unit/typing.test.js
@@ -477,6 +477,26 @@
         assertLatex('\\left(1+2\\right)+3+4+5');
       });
 
+      test('typing Ctrl-Backspace deletes everything to the left of the cursor', function () {
+        mq.typedText('12345');
+        assertLatex('12345');
+        mq.keystroke('Left Left');
+        mq.keystroke('Ctrl-Backspace');
+        assertLatex('45');
+        mq.keystroke('Ctrl-Backspace');
+        assertLatex('45');
+      });
+
+      test('typing Ctrl-Del deletes everything to the right of the cursor', function () {
+        mq.typedText('12345');
+        assertLatex('12345');
+        mq.keystroke('Left Left');
+        mq.keystroke('Ctrl-Del');
+        assertLatex('123');
+        mq.keystroke('Ctrl-Del');
+        assertLatex('123');
+      });
+
       suite('pipes', function() {
         test('typing then backspacing a pipe in the middle of 1+2+3+4', function() {
           mq.typedText('1+2+3+4');
diff --git a/test/visual.html b/test/visual.html
index a2207d1..3d091f5 100644
--- a/test/visual.html
+++ b/test/visual.html
@@ -212,6 +212,11 @@
 <tr><td><span class="mathquill-static-math">\vec x + \tilde x + \vec A + \tilde A + \vec{abcd} + \tilde{abcd}</span><td><span>\vec x + \tilde x + \vec A + \tilde A + \vec{abcd} + \tilde{abcd}</span>^M

 <tr><td><span class="mathquill-static-math">\int _{\phi =0}^{2\pi }\int _{\theta =0}^{\pi }\int _{r=0}^{\infty }f(r,\theta ,\phi )r^2\sin \theta drd\theta d\phi </span><td><span>\int _{\phi =0}^{2\pi }\int _{\theta =0}^{\pi }\int _{r=0}^{\infty }f(r,\theta ,\phi )r^2\sin \theta drd\theta d\phi </span>

 <tr><td><span class="mathquill-static-math">\int_0^{\frac{\frac{1}{2}}{3}} \int_0^{\frac{1}{\frac{2}{3}}} \int_0^{\frac{1}{\frac{2}{\frac{3}{\frac{4}{5}}}}}</span><td><span>\int_0^{\frac{\frac{1}{2}}{3}} \int_0^{\frac{1}{\frac{2}{3}}} \int_0^{\frac{1}{\frac{2}{\frac{3}{\frac{4}{5}}}}}</span>

+<tr><td><span class="mathquill-static-math">\overline{abc}</span><td><span>\overline{abc}</span>

+<tr><td><span class="mathquill-static-math">\overleftarrow{abc}</span><td><span>\overleftarrow{abc}</span>

+<tr><td><span class="mathquill-static-math">\overrightarrow{abc}</span><td><span>\overrightarrow{abc}</span>

+<tr><td><span class="mathquill-static-math">\overleftrightarrow{abc}</span><td><span>\overleftrightarrow{abc}</span>

+<tr><td><span class="mathquill-static-math">\overarc{abc}</span><td><span>\overarc{abc}</span>

 <tr><td colspan=2><span id="sixes"></span>

 <script>

 $(function() {

@@ -282,6 +287,12 @@
 

 <p>Should wrap anything you type in '&lt;&gt;': <span id="wrap-typing">1+2+3</span></p>

 

+<h3>Text mode</h3>

+

+<p>Spaces at the beginning and end of text mode blocks should be visible: <span class="mathquill-static-math">1\text{ And }2</span></p>

+

+<p>Mutiple consecutive spaces in the middle of a text mode block should not collapse into one space: <span class="mathquill-static-math">\text{three   spaces}</span></p>

+

 </div>

 <script type="text/javascript">

 window.onerror = function(err) {