From 04507e7ea25f59a5e4813b63e942f8b52373631b Mon Sep 17 00:00:00 2001
From: Jarid Margolin <jaridmargolin@gmail.com>
Date: Wed, 13 Nov 2013 19:40:34 -0800
Subject: [PATCH] Close #18

---
 lib/formatter.js            |  83 ++++++++------
 lib/formatter.min.js        |   2 +-
 lib/jquery.formatter.js     |  83 ++++++++------
 lib/jquery.formatter.min.js |   2 +-
 src/formatter.js            |  83 ++++++++------
 test/formatter.js           | 268 +++++++++++++++++++++++++++++++++-----------
 6 files changed, 350 insertions(+), 171 deletions(-)

diff --git a/lib/formatter.js b/lib/formatter.js
index a051786..e0d049c 100644
--- a/lib/formatter.js
+++ b/lib/formatter.js
@@ -203,27 +203,19 @@ Formatter.prototype._processKey = function (chars, delKey) {
     this.delta = (-1) * Math.abs(this.sel.begin - this.sel.end);
     this.val   = utils.removeChars(this.val, this.sel.begin, this.sel.end);
   }
-  // If delKey
-  else if (delKey) {
-    // Delete
-    if (delKey && delKey == 46) {
-      // Adjust focus to make sure its not on a formatted char
-      while (this.chars[this.sel.begin]) {
-        this._nextPos();
-      }
-      // As long as we are not at the end
-      if (this.sel.begin < this.val.length) {
-        // We will simulate a delete by moving the caret to the next char
-        // and then deleting
-        this._nextPos();
-        this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-        this.delta = -1;
-      }
-    // or Backspace and not at start
-    } else if (delKey && this.sel.begin - 1 >= 0) {
-      this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-      this.delta = -1;
-    }
+
+  // Delete key (moves opposite direction)
+  else if (delKey && delKey == 46) {
+    this._delete();
+
+  // or Backspace and not at start
+  } else if (delKey && this.sel.begin - 1 >= 0) {
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+
+  // or Backspace and at start - exit
+  } else if (delKey) {
+    return true;
   }
 
   // If the key is not a del key, it should convert to a str
@@ -239,6 +231,26 @@ Formatter.prototype._processKey = function (chars, delKey) {
 
 //
 // @private
+// Deletes the character in front of it
+//
+Formatter.prototype._delete = function () {
+  // Adjust focus to make sure its not on a formatted char
+  while (this.chars[this.sel.begin]) {
+    this._nextPos();
+  }
+
+  // As long as we are not at the end
+  if (this.sel.begin < this.val.length) {
+    // We will simulate a delete by moving the caret to the next char
+    // and then deleting
+    this._nextPos();
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+  }
+};
+
+//
+// @private
 // Quick helper method to move the caret to the next pos
 //
 Formatter.prototype._nextPos = function () {
@@ -317,16 +329,18 @@ Formatter.prototype._validateInpts = function () {
     // Get char inpt type
     var inptType = this.inpts[i];
 
-    // If improper type, or char doesnt match, remove
-    if (!inptRegs[inptType] || !inptRegs[inptType].test(this.val.charAt(i))) {
-      // Check bounds
-      if (this.inpts[i]) {
-        this.val = utils.removeChars(this.val, i, i + 1);
-        this.focusStart--;
-        this.newPos--;
-        this.delta--;
-        i--;
-      }
+    // Checks
+    var isBadType = !inptRegs[inptType],
+        isInvalid = !inptRegs[inptType].test(this.val.charAt(i)),
+        inBounds  = this.inpts[i];
+
+    // Remove if incorrect and inbounds
+    if ((isBadType || isInvalid) && inBounds) {
+      this.val = utils.removeChars(this.val, i, i + 1);
+      this.focusStart--;
+      this.newPos--;
+      this.delta--;
+      i--;
     }
   }
 };
@@ -354,6 +368,9 @@ Formatter.prototype._addChars = function () {
   } else {
     // Avoid caching val.length, as it changes during manipulations
     for (var j = 0; j <= this.val.length; j++) {
+      // When moving backwards there are some race conditions where we
+      // dont want to add the character
+      if (this.delta <= 0 && (j == this.focus)) { return true; }
       this._addChar(j);
     }
   }
@@ -380,10 +397,6 @@ Formatter.prototype._addChar = function (i) {
     this.focus++;
   }
 
-  // When moving backwards there are some race conditions where we
-  // dont want to add the character
-  if (this.delta < 0 && (this.val.charAt(i) == char )) { return true; }
-
   // Updateholder
   if (this.hldrs[i]) {
     delete this.hldrs[i];
diff --git a/lib/formatter.min.js b/lib/formatter.min.js
index 09ef747..06246d1 100644
--- a/lib/formatter.min.js
+++ b/lib/formatter.min.js
@@ -6,4 +6,4 @@
  * thanks to digitalBush/jquery.maskedinput for some of the trickier
  * keycode handling
  */
-!function(a,b,c){"undefined"!=typeof module&&module.exports?module.exports=c():"function"==typeof define&&define.amd?define(c):b[a]=c()}("Formatter",this,function(){function a(a,c){var e=this;if(e.el=a,!e.el)throw new TypeError("Must provide an existing element");if(e.opts=i.extend({},b,c),"undefined"==typeof e.opts.pattern)throw new TypeError("Must provide a pattern");var f=d.parse(e.opts.pattern);e.mLength=f.mLength,e.chars=f.chars,e.inpts=f.inpts,e.hldrs={},e.focus=0,i.addListener(e.el,"keydown",function(a){e._keyDown(a)}),i.addListener(e.el,"keypress",function(a){e._keyPress(a)}),i.addListener(e.el,"paste",function(a){e._paste(a)}),e.opts.persistent&&(e._processKey("",!1),e.el.blur(),i.addListener(e.el,"focus",function(a){e._focus(a)}),i.addListener(e.el,"click",function(a){e._focus(a)}),i.addListener(e.el,"touchstart",function(a){e._focus(a)}))}var b={persistent:!1,repeat:!1,placeholder:" "},c={9:new RegExp("[0-9]"),a:new RegExp("[A-Za-z]"),"*":new RegExp("[A-Za-z0-9]")};a.prototype.resetPattern=function(a){this.sel=h.get(this.el),this.val=this.el.value,this.delta=0,this._removeChars();var b=d.parse(a);this.mLength=b.mLength,this.chars=b.chars,this.inpts=b.inpts,this._processKey("",!1)},a.prototype._keyDown=function(a){var b=a.which||a.keyCode;return b&&i.isDelKey(b)?(this._processKey(null,b),i.preventDefault(a)):void 0},a.prototype._keyPress=function(a){var b,c;return a.which?b=a.which:(b=a.keyCode,c=i.isSpecialKey(b)),i.isDelKey(b)||c||i.isModifier(a)?void 0:(this._processKey(String.fromCharCode(b),!1),i.preventDefault(a))},a.prototype._paste=function(a){return this._processKey(i.getClip(a),!1),i.preventDefault(a)},a.prototype._focus=function(){var a=this;setTimeout(function(){var b=h.get(a.el),c=b.end>a.focus;isFirstChar=0===b.end,(c||isFirstChar)&&h.set(a.el,a.focus)},0)},a.prototype._processKey=function(a,b){if(this.sel=h.get(this.el),this.val=this.el.value,this.delta=0,this.sel.begin!==this.sel.end)this.delta=-1*Math.abs(this.sel.begin-this.sel.end),this.val=i.removeChars(this.val,this.sel.begin,this.sel.end);else if(b)if(b&&46==b){for(;this.chars[this.sel.begin];)this._nextPos();this.sel.begin<this.val.length&&(this._nextPos(),this.val=i.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1)}else b&&this.sel.begin-1>=0&&(this.val=i.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1);b||(this.val=i.addChars(this.val,a,this.sel.begin),this.delta+=a.length),this._formatValue()},a.prototype._nextPos=function(){this.sel.end++,this.sel.begin++},a.prototype._formatValue=function(){this.newPos=this.sel.end+this.delta,this._removeChars(),this._validateInpts(),this._addChars(),this.el.value=this.val.substr(0,this.mLength),h.set(this.el,this.newPos)},a.prototype._removeChars=function(){this.sel.end>this.focus&&(this.delta+=this.sel.end-this.focus);for(var a=0,b=0;b<=this.mLength;b++){var c,d=this.chars[b],e=this.hldrs[b],f=b+a;f=b>=this.sel.begin?f+this.delta:f,c=this.val.charAt(f),(d&&d==c||e&&e==c)&&(this.val=i.removeChars(this.val,f,f+1),a--)}this.hldrs={},this.focus=this.val.length},a.prototype._validateInpts=function(){for(var a=0;a<this.val.length;a++){var b=this.inpts[a];c[b]&&c[b].test(this.val.charAt(a))||this.inpts[a]&&(this.val=i.removeChars(this.val,a,a+1),this.focusStart--,this.newPos--,this.delta--,a--)}},a.prototype._addChars=function(){if(this.opts.persistent){for(var a=0;a<=this.mLength;a++)this.val.charAt(a)||(this.val=i.addChars(this.val,this.opts.placeholder,a),this.hldrs[a]=this.opts.placeholder),this._addChar(a);for(;this.chars[this.focus];)this.focus++}else for(var b=0;b<=this.val.length;b++)this._addChar(b)},a.prototype._addChar=function(a){var b=this.chars[a];return b?(i.isBetween(a,[this.sel.begin-1,this.newPos+1])&&(this.newPos++,this.delta++),a<=this.focus&&this.focus++,this.delta<0&&this.val.charAt(a)==b?!0:(this.hldrs[a]&&(delete this.hldrs[a],this.hldrs[a+1]=this.opts.placeholder),this.val=i.addChars(this.val,b,a),void 0)):!0};var d={},e=4,f=new RegExp("{{([^}]+)}}","g"),g=function(a){for(var b,c=[];b=f.exec(a);)c.push(b);return c};d.parse=function(a){var b={inpts:{},chars:{}},c=g(a),d=a.length,f=0,h=0,i=0,j=function(a){for(var c=a.length,d=0;c>d;d++)b.inpts[h]=a.charAt(d),h++;f++,i+=a.length+e-1};for(i;d>i;i++)i==c[f].index?j(c[f][1]):b.chars[i-f*e]=a.charAt(i);return b.mLength=i-f*e,b};var h={};h.get=function(a){if("number"==typeof a.selectionStart)return{begin:a.selectionStart,end:a.selectionEnd};var b=document.selection.createRange();if(b&&b.parentElement()==a){var c=a.createTextRange(),d=a.createTextRange(),e=a.value.length;return c.moveToBookmark(b.getBookmark()),d.collapse(!1),c.compareEndPoints("StartToEnd",d)>-1?{begin:e,end:e}:{begin:-c.moveStart("character",-e),end:-c.moveEnd("character",-e)}}return{begin:0,end:0}},h.set=function(a,b){if(a.setSelectionRange)a.focus(),a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}};var i={},j="undefined"!=typeof navigator?navigator.userAgent:null,k=/iphone/i.test(j);return i.extend=function(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])a[c]=arguments[b][c];return a},i.addChars=function(a,b,c){return a.substr(0,c)+b+a.substr(c,a.length)},i.removeChars=function(a,b,c){return a.substr(0,b)+a.substr(c,a.length)},i.isBetween=function(a,b){return b.sort(function(a,b){return a-b}),a>b[0]&&a<b[1]},i.addListener=function(a,b,c){return"undefined"!=typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},i.preventDefault=function(a){return a.preventDefault?a.preventDefault():a.returnValue=!1},i.getClip=function(a){return a.clipboardData?a.clipboardData.getData("Text"):window.clipboardData?window.clipboardData.getData("Text"):void 0},i.isDelKey=function(a){return 8===a||46===a||k&&127===a},i.isSpecialKey=function(a){var b={35:"end",36:"home",37:"leftarrow",38:"uparrow",39:"rightarrow",40:"downarrow"};return b[a]},i.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey},a});
\ No newline at end of file
+!function(a,b,c){"undefined"!=typeof module&&module.exports?module.exports=c():"function"==typeof define&&define.amd?define(c):b[a]=c()}("Formatter",this,function(){function a(a,c){var e=this;if(e.el=a,!e.el)throw new TypeError("Must provide an existing element");if(e.opts=i.extend({},b,c),"undefined"==typeof e.opts.pattern)throw new TypeError("Must provide a pattern");var f=d.parse(e.opts.pattern);e.mLength=f.mLength,e.chars=f.chars,e.inpts=f.inpts,e.hldrs={},e.focus=0,i.addListener(e.el,"keydown",function(a){e._keyDown(a)}),i.addListener(e.el,"keypress",function(a){e._keyPress(a)}),i.addListener(e.el,"paste",function(a){e._paste(a)}),e.opts.persistent&&(e._processKey("",!1),e.el.blur(),i.addListener(e.el,"focus",function(a){e._focus(a)}),i.addListener(e.el,"click",function(a){e._focus(a)}),i.addListener(e.el,"touchstart",function(a){e._focus(a)}))}var b={persistent:!1,repeat:!1,placeholder:" "},c={9:new RegExp("[0-9]"),a:new RegExp("[A-Za-z]"),"*":new RegExp("[A-Za-z0-9]")};a.prototype.resetPattern=function(a){this.sel=h.get(this.el),this.val=this.el.value,this.delta=0,this._removeChars();var b=d.parse(a);this.mLength=b.mLength,this.chars=b.chars,this.inpts=b.inpts,this._processKey("",!1)},a.prototype._keyDown=function(a){var b=a.which||a.keyCode;return b&&i.isDelKey(b)?(this._processKey(null,b),i.preventDefault(a)):void 0},a.prototype._keyPress=function(a){var b,c;return a.which?b=a.which:(b=a.keyCode,c=i.isSpecialKey(b)),i.isDelKey(b)||c||i.isModifier(a)?void 0:(this._processKey(String.fromCharCode(b),!1),i.preventDefault(a))},a.prototype._paste=function(a){return this._processKey(i.getClip(a),!1),i.preventDefault(a)},a.prototype._focus=function(){var a=this;setTimeout(function(){var b=h.get(a.el),c=b.end>a.focus;isFirstChar=0===b.end,(c||isFirstChar)&&h.set(a.el,a.focus)},0)},a.prototype._processKey=function(a,b){if(this.sel=h.get(this.el),this.val=this.el.value,this.delta=0,this.sel.begin!==this.sel.end)this.delta=-1*Math.abs(this.sel.begin-this.sel.end),this.val=i.removeChars(this.val,this.sel.begin,this.sel.end);else if(b&&46==b)this._delete();else if(b&&this.sel.begin-1>=0)this.val=i.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1;else if(b)return!0;b||(this.val=i.addChars(this.val,a,this.sel.begin),this.delta+=a.length),this._formatValue()},a.prototype._delete=function(){for(;this.chars[this.sel.begin];)this._nextPos();this.sel.begin<this.val.length&&(this._nextPos(),this.val=i.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1)},a.prototype._nextPos=function(){this.sel.end++,this.sel.begin++},a.prototype._formatValue=function(){this.newPos=this.sel.end+this.delta,this._removeChars(),this._validateInpts(),this._addChars(),this.el.value=this.val.substr(0,this.mLength),h.set(this.el,this.newPos)},a.prototype._removeChars=function(){this.sel.end>this.focus&&(this.delta+=this.sel.end-this.focus);for(var a=0,b=0;b<=this.mLength;b++){var c,d=this.chars[b],e=this.hldrs[b],f=b+a;f=b>=this.sel.begin?f+this.delta:f,c=this.val.charAt(f),(d&&d==c||e&&e==c)&&(this.val=i.removeChars(this.val,f,f+1),a--)}this.hldrs={},this.focus=this.val.length},a.prototype._validateInpts=function(){for(var a=0;a<this.val.length;a++){var b=this.inpts[a],d=!c[b],e=!c[b].test(this.val.charAt(a)),f=this.inpts[a];(d||e)&&f&&(this.val=i.removeChars(this.val,a,a+1),this.focusStart--,this.newPos--,this.delta--,a--)}},a.prototype._addChars=function(){if(this.opts.persistent){for(var a=0;a<=this.mLength;a++)this.val.charAt(a)||(this.val=i.addChars(this.val,this.opts.placeholder,a),this.hldrs[a]=this.opts.placeholder),this._addChar(a);for(;this.chars[this.focus];)this.focus++}else for(var b=0;b<=this.val.length;b++){if(this.delta<=0&&b==this.focus)return!0;this._addChar(b)}},a.prototype._addChar=function(a){var b=this.chars[a];return b?(i.isBetween(a,[this.sel.begin-1,this.newPos+1])&&(this.newPos++,this.delta++),a<=this.focus&&this.focus++,this.hldrs[a]&&(delete this.hldrs[a],this.hldrs[a+1]=this.opts.placeholder),this.val=i.addChars(this.val,b,a),void 0):!0};var d={},e=4,f=new RegExp("{{([^}]+)}}","g"),g=function(a){for(var b,c=[];b=f.exec(a);)c.push(b);return c};d.parse=function(a){var b={inpts:{},chars:{}},c=g(a),d=a.length,f=0,h=0,i=0,j=function(a){for(var c=a.length,d=0;c>d;d++)b.inpts[h]=a.charAt(d),h++;f++,i+=a.length+e-1};for(i;d>i;i++)i==c[f].index?j(c[f][1]):b.chars[i-f*e]=a.charAt(i);return b.mLength=i-f*e,b};var h={};h.get=function(a){if("number"==typeof a.selectionStart)return{begin:a.selectionStart,end:a.selectionEnd};var b=document.selection.createRange();if(b&&b.parentElement()==a){var c=a.createTextRange(),d=a.createTextRange(),e=a.value.length;return c.moveToBookmark(b.getBookmark()),d.collapse(!1),c.compareEndPoints("StartToEnd",d)>-1?{begin:e,end:e}:{begin:-c.moveStart("character",-e),end:-c.moveEnd("character",-e)}}return{begin:0,end:0}},h.set=function(a,b){if(a.setSelectionRange)a.focus(),a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}};var i={},j="undefined"!=typeof navigator?navigator.userAgent:null,k=/iphone/i.test(j);return i.extend=function(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])a[c]=arguments[b][c];return a},i.addChars=function(a,b,c){return a.substr(0,c)+b+a.substr(c,a.length)},i.removeChars=function(a,b,c){return a.substr(0,b)+a.substr(c,a.length)},i.isBetween=function(a,b){return b.sort(function(a,b){return a-b}),a>b[0]&&a<b[1]},i.addListener=function(a,b,c){return"undefined"!=typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},i.preventDefault=function(a){return a.preventDefault?a.preventDefault():a.returnValue=!1},i.getClip=function(a){return a.clipboardData?a.clipboardData.getData("Text"):window.clipboardData?window.clipboardData.getData("Text"):void 0},i.isDelKey=function(a){return 8===a||46===a||k&&127===a},i.isSpecialKey=function(a){var b={35:"end",36:"home",37:"leftarrow",38:"uparrow",39:"rightarrow",40:"downarrow"};return b[a]},i.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey},a});
\ No newline at end of file
diff --git a/lib/jquery.formatter.js b/lib/jquery.formatter.js
index 15f8b5e..d7f2b67 100644
--- a/lib/jquery.formatter.js
+++ b/lib/jquery.formatter.js
@@ -199,27 +199,19 @@ Formatter.prototype._processKey = function (chars, delKey) {
     this.delta = (-1) * Math.abs(this.sel.begin - this.sel.end);
     this.val   = utils.removeChars(this.val, this.sel.begin, this.sel.end);
   }
-  // If delKey
-  else if (delKey) {
-    // Delete
-    if (delKey && delKey == 46) {
-      // Adjust focus to make sure its not on a formatted char
-      while (this.chars[this.sel.begin]) {
-        this._nextPos();
-      }
-      // As long as we are not at the end
-      if (this.sel.begin < this.val.length) {
-        // We will simulate a delete by moving the caret to the next char
-        // and then deleting
-        this._nextPos();
-        this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-        this.delta = -1;
-      }
-    // or Backspace and not at start
-    } else if (delKey && this.sel.begin - 1 >= 0) {
-      this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-      this.delta = -1;
-    }
+
+  // Delete key (moves opposite direction)
+  else if (delKey && delKey == 46) {
+    this._delete();
+
+  // or Backspace and not at start
+  } else if (delKey && this.sel.begin - 1 >= 0) {
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+
+  // or Backspace and at start - exit
+  } else if (delKey) {
+    return true;
   }
 
   // If the key is not a del key, it should convert to a str
@@ -235,6 +227,26 @@ Formatter.prototype._processKey = function (chars, delKey) {
 
 //
 // @private
+// Deletes the character in front of it
+//
+Formatter.prototype._delete = function () {
+  // Adjust focus to make sure its not on a formatted char
+  while (this.chars[this.sel.begin]) {
+    this._nextPos();
+  }
+
+  // As long as we are not at the end
+  if (this.sel.begin < this.val.length) {
+    // We will simulate a delete by moving the caret to the next char
+    // and then deleting
+    this._nextPos();
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+  }
+};
+
+//
+// @private
 // Quick helper method to move the caret to the next pos
 //
 Formatter.prototype._nextPos = function () {
@@ -313,16 +325,18 @@ Formatter.prototype._validateInpts = function () {
     // Get char inpt type
     var inptType = this.inpts[i];
 
-    // If improper type, or char doesnt match, remove
-    if (!inptRegs[inptType] || !inptRegs[inptType].test(this.val.charAt(i))) {
-      // Check bounds
-      if (this.inpts[i]) {
-        this.val = utils.removeChars(this.val, i, i + 1);
-        this.focusStart--;
-        this.newPos--;
-        this.delta--;
-        i--;
-      }
+    // Checks
+    var isBadType = !inptRegs[inptType],
+        isInvalid = !inptRegs[inptType].test(this.val.charAt(i)),
+        inBounds  = this.inpts[i];
+
+    // Remove if incorrect and inbounds
+    if ((isBadType || isInvalid) && inBounds) {
+      this.val = utils.removeChars(this.val, i, i + 1);
+      this.focusStart--;
+      this.newPos--;
+      this.delta--;
+      i--;
     }
   }
 };
@@ -350,6 +364,9 @@ Formatter.prototype._addChars = function () {
   } else {
     // Avoid caching val.length, as it changes during manipulations
     for (var j = 0; j <= this.val.length; j++) {
+      // When moving backwards there are some race conditions where we
+      // dont want to add the character
+      if (this.delta <= 0 && (j == this.focus)) { return true; }
       this._addChar(j);
     }
   }
@@ -376,10 +393,6 @@ Formatter.prototype._addChar = function (i) {
     this.focus++;
   }
 
-  // When moving backwards there are some race conditions where we
-  // dont want to add the character
-  if (this.delta < 0 && (this.val.charAt(i) == char )) { return true; }
-
   // Updateholder
   if (this.hldrs[i]) {
     delete this.hldrs[i];
diff --git a/lib/jquery.formatter.min.js b/lib/jquery.formatter.min.js
index fbb206c..a013a86 100644
--- a/lib/jquery.formatter.min.js
+++ b/lib/jquery.formatter.min.js
@@ -6,4 +6,4 @@
  * thanks to digitalBush/jquery.maskedinput for some of the trickier
  * keycode handling
  */
-!function(a,b,c){function d(a,b){var c=this;if(c.el=a,!c.el)throw new TypeError("Must provide an existing element");if(c.opts=l.extend({},e,b),"undefined"==typeof c.opts.pattern)throw new TypeError("Must provide a pattern");var d=g.parse(c.opts.pattern);c.mLength=d.mLength,c.chars=d.chars,c.inpts=d.inpts,c.hldrs={},c.focus=0,l.addListener(c.el,"keydown",function(a){c._keyDown(a)}),l.addListener(c.el,"keypress",function(a){c._keyPress(a)}),l.addListener(c.el,"paste",function(a){c._paste(a)}),c.opts.persistent&&(c._processKey("",!1),c.el.blur(),l.addListener(c.el,"focus",function(a){c._focus(a)}),l.addListener(c.el,"click",function(a){c._focus(a)}),l.addListener(c.el,"touchstart",function(a){c._focus(a)}))}var e={persistent:!1,repeat:!1,placeholder:" "},f={9:new RegExp("[0-9]"),a:new RegExp("[A-Za-z]"),"*":new RegExp("[A-Za-z0-9]")};d.prototype.resetPattern=function(a){this.sel=k.get(this.el),this.val=this.el.value,this.delta=0,this._removeChars();var b=g.parse(a);this.mLength=b.mLength,this.chars=b.chars,this.inpts=b.inpts,this._processKey("",!1)},d.prototype._keyDown=function(a){var b=a.which||a.keyCode;return b&&l.isDelKey(b)?(this._processKey(null,b),l.preventDefault(a)):void 0},d.prototype._keyPress=function(a){var b,c;return a.which?b=a.which:(b=a.keyCode,c=l.isSpecialKey(b)),l.isDelKey(b)||c||l.isModifier(a)?void 0:(this._processKey(String.fromCharCode(b),!1),l.preventDefault(a))},d.prototype._paste=function(a){return this._processKey(l.getClip(a),!1),l.preventDefault(a)},d.prototype._focus=function(){var a=this;setTimeout(function(){var b=k.get(a.el),c=b.end>a.focus;isFirstChar=0===b.end,(c||isFirstChar)&&k.set(a.el,a.focus)},0)},d.prototype._processKey=function(a,b){if(this.sel=k.get(this.el),this.val=this.el.value,this.delta=0,this.sel.begin!==this.sel.end)this.delta=-1*Math.abs(this.sel.begin-this.sel.end),this.val=l.removeChars(this.val,this.sel.begin,this.sel.end);else if(b)if(b&&46==b){for(;this.chars[this.sel.begin];)this._nextPos();this.sel.begin<this.val.length&&(this._nextPos(),this.val=l.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1)}else b&&this.sel.begin-1>=0&&(this.val=l.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1);b||(this.val=l.addChars(this.val,a,this.sel.begin),this.delta+=a.length),this._formatValue()},d.prototype._nextPos=function(){this.sel.end++,this.sel.begin++},d.prototype._formatValue=function(){this.newPos=this.sel.end+this.delta,this._removeChars(),this._validateInpts(),this._addChars(),this.el.value=this.val.substr(0,this.mLength),k.set(this.el,this.newPos)},d.prototype._removeChars=function(){this.sel.end>this.focus&&(this.delta+=this.sel.end-this.focus);for(var a=0,b=0;b<=this.mLength;b++){var c,d=this.chars[b],e=this.hldrs[b],f=b+a;f=b>=this.sel.begin?f+this.delta:f,c=this.val.charAt(f),(d&&d==c||e&&e==c)&&(this.val=l.removeChars(this.val,f,f+1),a--)}this.hldrs={},this.focus=this.val.length},d.prototype._validateInpts=function(){for(var a=0;a<this.val.length;a++){var b=this.inpts[a];f[b]&&f[b].test(this.val.charAt(a))||this.inpts[a]&&(this.val=l.removeChars(this.val,a,a+1),this.focusStart--,this.newPos--,this.delta--,a--)}},d.prototype._addChars=function(){if(this.opts.persistent){for(var a=0;a<=this.mLength;a++)this.val.charAt(a)||(this.val=l.addChars(this.val,this.opts.placeholder,a),this.hldrs[a]=this.opts.placeholder),this._addChar(a);for(;this.chars[this.focus];)this.focus++}else for(var b=0;b<=this.val.length;b++)this._addChar(b)},d.prototype._addChar=function(a){var b=this.chars[a];return b?(l.isBetween(a,[this.sel.begin-1,this.newPos+1])&&(this.newPos++,this.delta++),a<=this.focus&&this.focus++,this.delta<0&&this.val.charAt(a)==b?!0:(this.hldrs[a]&&(delete this.hldrs[a],this.hldrs[a+1]=this.opts.placeholder),this.val=l.addChars(this.val,b,a),void 0)):!0};var g={},h=4,i=new RegExp("{{([^}]+)}}","g"),j=function(a){for(var b,c=[];b=i.exec(a);)c.push(b);return c};g.parse=function(a){var b={inpts:{},chars:{}},c=j(a),d=a.length,e=0,f=0,g=0,i=function(a){for(var c=a.length,d=0;c>d;d++)b.inpts[f]=a.charAt(d),f++;e++,g+=a.length+h-1};for(g;d>g;g++)g==c[e].index?i(c[e][1]):b.chars[g-e*h]=a.charAt(g);return b.mLength=g-e*h,b};var k={};k.get=function(a){if("number"==typeof a.selectionStart)return{begin:a.selectionStart,end:a.selectionEnd};var b=c.selection.createRange();if(b&&b.parentElement()==a){var d=a.createTextRange(),e=a.createTextRange(),f=a.value.length;return d.moveToBookmark(b.getBookmark()),e.collapse(!1),d.compareEndPoints("StartToEnd",e)>-1?{begin:f,end:f}:{begin:-d.moveStart("character",-f),end:-d.moveEnd("character",-f)}}return{begin:0,end:0}},k.set=function(a,b){if(a.setSelectionRange)a.focus(),a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}};var l={},m="undefined"!=typeof navigator?navigator.userAgent:null,n=/iphone/i.test(m);l.extend=function(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])a[c]=arguments[b][c];return a},l.addChars=function(a,b,c){return a.substr(0,c)+b+a.substr(c,a.length)},l.removeChars=function(a,b,c){return a.substr(0,b)+a.substr(c,a.length)},l.isBetween=function(a,b){return b.sort(function(a,b){return a-b}),a>b[0]&&a<b[1]},l.addListener=function(a,b,c){return"undefined"!=typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},l.preventDefault=function(a){return a.preventDefault?a.preventDefault():a.returnValue=!1},l.getClip=function(a){return a.clipboardData?a.clipboardData.getData("Text"):b.clipboardData?b.clipboardData.getData("Text"):void 0},l.isDelKey=function(a){return 8===a||46===a||n&&127===a},l.isSpecialKey=function(a){var b={35:"end",36:"home",37:"leftarrow",38:"uparrow",39:"rightarrow",40:"downarrow"};return b[a]},l.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey};var o="formatter";a.fn[o]=function(b){return"object"==typeof b&&this.each(function(){a.data(this,"plugin_"+o)||a.data(this,"plugin_"+o,new d(this,b))}),this.resetPattern=function(b){return this.each(function(){var c=a.data(this,"plugin_"+o);c&&c.resetPattern(b)}),this},this}}(jQuery,window,document);
\ No newline at end of file
+!function(a,b,c){function d(a,b){var c=this;if(c.el=a,!c.el)throw new TypeError("Must provide an existing element");if(c.opts=l.extend({},e,b),"undefined"==typeof c.opts.pattern)throw new TypeError("Must provide a pattern");var d=g.parse(c.opts.pattern);c.mLength=d.mLength,c.chars=d.chars,c.inpts=d.inpts,c.hldrs={},c.focus=0,l.addListener(c.el,"keydown",function(a){c._keyDown(a)}),l.addListener(c.el,"keypress",function(a){c._keyPress(a)}),l.addListener(c.el,"paste",function(a){c._paste(a)}),c.opts.persistent&&(c._processKey("",!1),c.el.blur(),l.addListener(c.el,"focus",function(a){c._focus(a)}),l.addListener(c.el,"click",function(a){c._focus(a)}),l.addListener(c.el,"touchstart",function(a){c._focus(a)}))}var e={persistent:!1,repeat:!1,placeholder:" "},f={9:new RegExp("[0-9]"),a:new RegExp("[A-Za-z]"),"*":new RegExp("[A-Za-z0-9]")};d.prototype.resetPattern=function(a){this.sel=k.get(this.el),this.val=this.el.value,this.delta=0,this._removeChars();var b=g.parse(a);this.mLength=b.mLength,this.chars=b.chars,this.inpts=b.inpts,this._processKey("",!1)},d.prototype._keyDown=function(a){var b=a.which||a.keyCode;return b&&l.isDelKey(b)?(this._processKey(null,b),l.preventDefault(a)):void 0},d.prototype._keyPress=function(a){var b,c;return a.which?b=a.which:(b=a.keyCode,c=l.isSpecialKey(b)),l.isDelKey(b)||c||l.isModifier(a)?void 0:(this._processKey(String.fromCharCode(b),!1),l.preventDefault(a))},d.prototype._paste=function(a){return this._processKey(l.getClip(a),!1),l.preventDefault(a)},d.prototype._focus=function(){var a=this;setTimeout(function(){var b=k.get(a.el),c=b.end>a.focus;isFirstChar=0===b.end,(c||isFirstChar)&&k.set(a.el,a.focus)},0)},d.prototype._processKey=function(a,b){if(this.sel=k.get(this.el),this.val=this.el.value,this.delta=0,this.sel.begin!==this.sel.end)this.delta=-1*Math.abs(this.sel.begin-this.sel.end),this.val=l.removeChars(this.val,this.sel.begin,this.sel.end);else if(b&&46==b)this._delete();else if(b&&this.sel.begin-1>=0)this.val=l.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1;else if(b)return!0;b||(this.val=l.addChars(this.val,a,this.sel.begin),this.delta+=a.length),this._formatValue()},d.prototype._delete=function(){for(;this.chars[this.sel.begin];)this._nextPos();this.sel.begin<this.val.length&&(this._nextPos(),this.val=l.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1)},d.prototype._nextPos=function(){this.sel.end++,this.sel.begin++},d.prototype._formatValue=function(){this.newPos=this.sel.end+this.delta,this._removeChars(),this._validateInpts(),this._addChars(),this.el.value=this.val.substr(0,this.mLength),k.set(this.el,this.newPos)},d.prototype._removeChars=function(){this.sel.end>this.focus&&(this.delta+=this.sel.end-this.focus);for(var a=0,b=0;b<=this.mLength;b++){var c,d=this.chars[b],e=this.hldrs[b],f=b+a;f=b>=this.sel.begin?f+this.delta:f,c=this.val.charAt(f),(d&&d==c||e&&e==c)&&(this.val=l.removeChars(this.val,f,f+1),a--)}this.hldrs={},this.focus=this.val.length},d.prototype._validateInpts=function(){for(var a=0;a<this.val.length;a++){var b=this.inpts[a],c=!f[b],d=!f[b].test(this.val.charAt(a)),e=this.inpts[a];(c||d)&&e&&(this.val=l.removeChars(this.val,a,a+1),this.focusStart--,this.newPos--,this.delta--,a--)}},d.prototype._addChars=function(){if(this.opts.persistent){for(var a=0;a<=this.mLength;a++)this.val.charAt(a)||(this.val=l.addChars(this.val,this.opts.placeholder,a),this.hldrs[a]=this.opts.placeholder),this._addChar(a);for(;this.chars[this.focus];)this.focus++}else for(var b=0;b<=this.val.length;b++){if(this.delta<=0&&b==this.focus)return!0;this._addChar(b)}},d.prototype._addChar=function(a){var b=this.chars[a];return b?(l.isBetween(a,[this.sel.begin-1,this.newPos+1])&&(this.newPos++,this.delta++),a<=this.focus&&this.focus++,this.hldrs[a]&&(delete this.hldrs[a],this.hldrs[a+1]=this.opts.placeholder),this.val=l.addChars(this.val,b,a),void 0):!0};var g={},h=4,i=new RegExp("{{([^}]+)}}","g"),j=function(a){for(var b,c=[];b=i.exec(a);)c.push(b);return c};g.parse=function(a){var b={inpts:{},chars:{}},c=j(a),d=a.length,e=0,f=0,g=0,i=function(a){for(var c=a.length,d=0;c>d;d++)b.inpts[f]=a.charAt(d),f++;e++,g+=a.length+h-1};for(g;d>g;g++)g==c[e].index?i(c[e][1]):b.chars[g-e*h]=a.charAt(g);return b.mLength=g-e*h,b};var k={};k.get=function(a){if("number"==typeof a.selectionStart)return{begin:a.selectionStart,end:a.selectionEnd};var b=c.selection.createRange();if(b&&b.parentElement()==a){var d=a.createTextRange(),e=a.createTextRange(),f=a.value.length;return d.moveToBookmark(b.getBookmark()),e.collapse(!1),d.compareEndPoints("StartToEnd",e)>-1?{begin:f,end:f}:{begin:-d.moveStart("character",-f),end:-d.moveEnd("character",-f)}}return{begin:0,end:0}},k.set=function(a,b){if(a.setSelectionRange)a.focus(),a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}};var l={},m="undefined"!=typeof navigator?navigator.userAgent:null,n=/iphone/i.test(m);l.extend=function(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])a[c]=arguments[b][c];return a},l.addChars=function(a,b,c){return a.substr(0,c)+b+a.substr(c,a.length)},l.removeChars=function(a,b,c){return a.substr(0,b)+a.substr(c,a.length)},l.isBetween=function(a,b){return b.sort(function(a,b){return a-b}),a>b[0]&&a<b[1]},l.addListener=function(a,b,c){return"undefined"!=typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},l.preventDefault=function(a){return a.preventDefault?a.preventDefault():a.returnValue=!1},l.getClip=function(a){return a.clipboardData?a.clipboardData.getData("Text"):b.clipboardData?b.clipboardData.getData("Text"):void 0},l.isDelKey=function(a){return 8===a||46===a||n&&127===a},l.isSpecialKey=function(a){var b={35:"end",36:"home",37:"leftarrow",38:"uparrow",39:"rightarrow",40:"downarrow"};return b[a]},l.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey};var o="formatter";a.fn[o]=function(b){return"object"==typeof b&&this.each(function(){a.data(this,"plugin_"+o)||a.data(this,"plugin_"+o,new d(this,b))}),this.resetPattern=function(b){return this.each(function(){var c=a.data(this,"plugin_"+o);c&&c.resetPattern(b)}),this},this}}(jQuery,window,document);
\ No newline at end of file
diff --git a/src/formatter.js b/src/formatter.js
index ad701dd..75d4c63 100644
--- a/src/formatter.js
+++ b/src/formatter.js
@@ -204,27 +204,19 @@ Formatter.prototype._processKey = function (chars, delKey) {
     this.delta = (-1) * Math.abs(this.sel.begin - this.sel.end);
     this.val   = utils.removeChars(this.val, this.sel.begin, this.sel.end);
   }
-  // If delKey
-  else if (delKey) {
-    // Delete
-    if (delKey && delKey == 46) {
-      // Adjust focus to make sure its not on a formatted char
-      while (this.chars[this.sel.begin]) {
-        this._nextPos();
-      }
-      // As long as we are not at the end
-      if (this.sel.begin < this.val.length) {
-        // We will simulate a delete by moving the caret to the next char
-        // and then deleting
-        this._nextPos();
-        this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-        this.delta = -1;
-      }
-    // or Backspace and not at start
-    } else if (delKey && this.sel.begin - 1 >= 0) {
-      this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
-      this.delta = -1;
-    }
+
+  // Delete key (moves opposite direction)
+  else if (delKey && delKey == 46) {
+    this._delete();
+
+  // or Backspace and not at start
+  } else if (delKey && this.sel.begin - 1 >= 0) {
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+
+  // or Backspace and at start - exit
+  } else if (delKey) {
+    return true;
   }
 
   // If the key is not a del key, it should convert to a str
@@ -240,6 +232,26 @@ Formatter.prototype._processKey = function (chars, delKey) {
 
 //
 // @private
+// Deletes the character in front of it
+//
+Formatter.prototype._delete = function () {
+  // Adjust focus to make sure its not on a formatted char
+  while (this.chars[this.sel.begin]) {
+    this._nextPos();
+  }
+
+  // As long as we are not at the end
+  if (this.sel.begin < this.val.length) {
+    // We will simulate a delete by moving the caret to the next char
+    // and then deleting
+    this._nextPos();
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+  }
+};
+
+//
+// @private
 // Quick helper method to move the caret to the next pos
 //
 Formatter.prototype._nextPos = function () {
@@ -318,16 +330,18 @@ Formatter.prototype._validateInpts = function () {
     // Get char inpt type
     var inptType = this.inpts[i];
 
-    // If improper type, or char doesnt match, remove
-    if (!inptRegs[inptType] || !inptRegs[inptType].test(this.val.charAt(i))) {
-      // Check bounds
-      if (this.inpts[i]) {
-        this.val = utils.removeChars(this.val, i, i + 1);
-        this.focusStart--;
-        this.newPos--;
-        this.delta--;
-        i--;
-      }
+    // Checks
+    var isBadType = !inptRegs[inptType],
+        isInvalid = !inptRegs[inptType].test(this.val.charAt(i)),
+        inBounds  = this.inpts[i];
+
+    // Remove if incorrect and inbounds
+    if ((isBadType || isInvalid) && inBounds) {
+      this.val = utils.removeChars(this.val, i, i + 1);
+      this.focusStart--;
+      this.newPos--;
+      this.delta--;
+      i--;
     }
   }
 };
@@ -355,6 +369,9 @@ Formatter.prototype._addChars = function () {
   } else {
     // Avoid caching val.length, as it changes during manipulations
     for (var j = 0; j <= this.val.length; j++) {
+      // When moving backwards there are some race conditions where we
+      // dont want to add the character
+      if (this.delta <= 0 && (j == this.focus)) { return true; }
       this._addChar(j);
     }
   }
@@ -381,10 +398,6 @@ Formatter.prototype._addChar = function (i) {
     this.focus++;
   }
 
-  // When moving backwards there are some race conditions where we
-  // dont want to add the character
-  if (this.delta < 0 && (this.val.charAt(i) == char )) { return true; }
-
   // Updateholder
   if (this.hldrs[i]) {
     delete this.hldrs[i];
diff --git a/test/formatter.js b/test/formatter.js
index 9acef78..8b4df7b 100644
--- a/test/formatter.js
+++ b/test/formatter.js
@@ -29,7 +29,7 @@ describe('formatter.js', function () {
   // Scope vars
   var formatted, user, sel, el;
 
-  before(function () {
+  beforeEach(function () {
     // Fake user to carry out keyboard and mouse events
     user = new User();
     // Set init el
@@ -46,89 +46,229 @@ describe('formatter.js', function () {
     stub(inptSel, 'set', function (el, pos) {
       sel.begin = sel.end = pos;
     });
-    // New instance
-    formatted = new Formatter(el, {
-      pattern: '({{999}}) {{999}}-{{9999}}',
-      persistent: true
-    });
   });
 
-  after(function () {
+  afterEach(function () {
     utils.addListener.restore();
     inptSel.get.restore();
     inptSel.set.restore();
   });
 
-  it('Should set init values and merge defaults', function () {
-    // Check opts
-    assert.equal(formatted.opts.pattern, '({{999}}) {{999}}-{{9999}}');
-    assert.isTrue(formatted.opts.persistent);
-    // Check pattern
-    assert.isObject(formatted.chars);
-    assert.isObject(formatted.inpts);
-    assert.isNumber(formatted.mLength);
-    // Check init values
-    assert.isObject(formatted.hldrs);
-    assert.isNumber(formatted.focus);
-  });
+  //
+  // Formatter global tests
+  //
+  describe('global', function () {
+    beforeEach(function () {
+      // New instance
+      formatted = new Formatter(el, {
+        pattern: '({{999}}) {{999}}-{{9999}}',
+        persistent: true
+      });
+    });
 
-  it('Should format chars as they are entered', function (done) {
-    user.keySeq('1237890', function () {
-      assert.equal(formatted.el.value, '(123) 789-0   ');
-      done();
+    it('Should set init values and merge defaults', function () {
+      // Check opts
+      assert.equal(formatted.opts.pattern, '({{999}}) {{999}}-{{9999}}');
+      assert.isTrue(formatted.opts.persistent);
+      // Check pattern
+      assert.isObject(formatted.chars);
+      assert.isObject(formatted.inpts);
+      assert.isNumber(formatted.mLength);
+      // Check init values
+      assert.isObject(formatted.hldrs);
+      assert.isNumber(formatted.focus);
     });
-  });
 
-  it('Should focus to the next available inpt position', function () {
-    assert.equal(formatted.focus, 11);
-  });
+    it('Should natively handle home, end, and arrow keys', function () {
+      user.key('leftarrow');
+      user.key('rightarrow');
+      user.key('uparrow');
+      user.key('downarrow');
+      user.key('home');
+      user.key('end');
+      assert.equal(formatted.focus, 1);
+    });
 
-  it('Should natively handle home, end, and arrow keys', function () {
-    user.key('leftarrow');
-    user.key('rightarrow');
-    user.key('uparrow');
-    user.key('downarrow');
-    user.key('home');
-    user.key('end');
-    assert.equal(formatted.focus, 11);
-  });
+    it('Should update value when resetPattern method is called', function (done) {
+      user.keySeq('24567890', function () {
+        formatted.resetPattern('{{999}}.{{999}}.{{9999}}');
+        assert.equal(formatted.el.value, '245.678.90  ');
+        done();
+      });
+    });
 
-  it('Should fromat chars entered mid str', function (done) {
-    sel = { begin: 6, end: 6 };
-    user.keySeq('456', function () {
-      assert.equal(formatted.el.value, '(123) 456-7890');
-      done();
+    describe('input focus', function () {
+      it('Should focus to the next available inpt position', function (done) {
+        user.keySeq('1237890', function () {
+          assert.equal(formatted.focus, 11);
+          done();
+        });
+      });
+      it('Should not focus on a formatted char', function (done) {
+        user.keySeq('123', function () {
+          assert.equal(formatted.focus, 6);
+          done();
+        });
+      });
     });
   });
 
-  it('Should delete chars when highlighted', function () {
-    sel = { begin: 2, end: 8 };
-    user.key('backspace');
-    assert.equal(formatted.el.value, '(167) 890-    ');
-  });
 
-  it('Should handle pasting multiple characters', function (done) {
-    sel = { begin: 2, end: 2 };
-    user.paste('2345', function () {
-      assert.equal(formatted.el.value, '(123) 456-7890');
-      done();
+  //
+  // Formatter with persistence
+  //
+  describe('persistent: true', function () {
+    beforeEach(function () {
+      // New instance
+      formatted = new Formatter(el, {
+        pattern: '({{999}}) {{999}}-{{9999}}',
+        persistent: true
+      });
     });
-  });
 
-  it('Should remove previous character on backspace key', function () {
-    sel = { begin: 2, end: 2 };
-    user.key('backspace');
-    assert.equal(formatted.el.value, '(234) 567-890 ');
-  });
+    it('Should format chars as they are entered', function (done) {
+      user.keySeq('1237890', function () {
+        assert.equal(formatted.el.value, '(123) 789-0   ');
+        done();
+      });
+    });
+
+    it('Should fromat chars entered mid str', function (done) {
+      user.keySeq('1237890', function () {
+        sel = { begin: 6, end: 6 };
+        user.keySeq('456', function () {
+          assert.equal(formatted.el.value, '(123) 456-7890');
+          done();
+        });
+      });
+    });
+
+    it('Should delete chars when highlighted', function (done) {
+      user.keySeq('1234567890', function () {
+        sel = { begin: 2, end: 8 };
+        user.key('backspace');
+        assert.equal(formatted.el.value, '(167) 890-    ');
+        done();
+      });
+    });
+
+    it('Should handle pasting multiple characters', function (done) {
+      user.keySeq('167890', function () {
+        sel = { begin: 2, end: 2 };
+        user.paste('2345', function () {
+          assert.equal(formatted.el.value, '(123) 456-7890');
+          done();
+        });
+      });
+    });
+
+    it('Should remove previous character on backspace key', function (done) {
+      user.keySeq('1234567890', function () {
+        sel = { begin: 2, end: 2 };
+        user.key('backspace');
+        assert.equal(formatted.el.value, '(234) 567-890 ');
+        done();
+      });
+    });
 
-  it('Should remove next character on delete key', function () {
-    sel = { begin: 2, end: 2 };
-    user.key('delete');
-    assert.equal(formatted.el.value, '(245) 678-90  ');
+    it('Should remove next character on delete key', function (done) {
+      user.keySeq('234567890', function () {
+        sel = { begin: 2, end: 2 };
+        user.key('delete');
+        assert.equal(formatted.el.value, '(245) 678-90  ');
+        done();
+      });
+    });
   });
 
-  it('Should update value when resetPattern method is called', function () {
-    formatted.resetPattern('{{999}}.{{999}}.{{9999}}');
-    assert.equal(formatted.el.value, '245.678.90  ');
+  //
+  // Formatter without persistence
+  //
+  describe('persistent: false', function () {
+    beforeEach(function () {
+      // New instance
+      formatted = new Formatter(el, {
+        pattern: '({{999}}) {{999}}-{{9999}}'
+      });
+    });
+
+    it('Should format chars as they are entered', function (done) {
+      user.keySeq('1237890', function () {
+        assert.equal(formatted.el.value, '(123) 789-0');
+        done();
+      });
+    });
+
+    it('Should fromat chars entered mid str', function (done) {
+      user.keySeq('1237890', function () {
+        sel = { begin: 6, end: 6 };
+        user.keySeq('456', function () {
+          assert.equal(formatted.el.value, '(123) 456-7890');
+          done();
+        });
+      });
+    });
+
+    it('Should delete chars when highlighted', function (done) {
+      user.keySeq('1234567890', function () {
+        sel = { begin: 2, end: 8 };
+        user.key('backspace');
+        assert.equal(formatted.el.value, '(167) 890');
+        done();
+      });
+    });
+
+    it('Should handle pasting multiple characters', function (done) {
+      user.keySeq('167890', function () {
+        sel = { begin: 2, end: 2 };
+        user.paste('2345', function () {
+          assert.equal(formatted.el.value, '(123) 456-7890');
+          done();
+        });
+      });
+    });
+
+    it('Should remove previous character on backspace key', function (done) {
+      user.keySeq('1234567890', function () {
+        sel = { begin: 2, end: 2 };
+        user.key('backspace');
+        assert.equal(formatted.el.value, '(234) 567-890');
+        done();
+      });
+    });
+
+    it('Should completely empty field', function (done) {
+      user.keySeq('1', function () {
+        user.key('backspace');
+        assert.equal(formatted.el.value, '');
+        done();
+      });
+    });
+
+    it('Should not add chars past focus location if deleting', function (done) {
+      user.keySeq('1234567', function () {
+        user.key('backspace');
+        assert.equal(formatted.el.value, '(123) 456');
+        done();
+      });
+    });
+
+    it('Should remove next character on delete key', function (done) {
+      user.keySeq('234567890', function () {
+        sel = { begin: 2, end: 2 };
+        user.key('delete');
+        assert.equal(formatted.el.value, '(245) 678-90');
+        done();
+      });
+    });
+
+    it('Should update value when resetPattern method is called', function (done) {
+      user.keySeq('24567890', function () {
+        formatted.resetPattern('{{999}}.{{999}}.{{9999}}');
+        assert.equal(formatted.el.value, '245.678.90');
+        done();
+      });
+    });
   });
+  
 });
\ No newline at end of file
