From dff4d24bacd32187060995f3ce29f32d9df191fb Mon Sep 17 00:00:00 2001
From: Jarid Margolin <jaridmargolin@gmail.com>
Date: Mon, 21 Oct 2013 16:50:02 -0700
Subject: [PATCH] Add support for jquery

---
 Gruntfile.js                       |  39 ++-
 README.md                          |  12 +-
 _site/demos.html                   |  88 ------
 _site/index.html                   | 154 ----------
 _site/javascripts/formatter.js     | 612 -------------------------------------
 _site/javascripts/scale.fix.js     |  17 --
 _site/stylesheets/pygment_trac.css |  69 -----
 _site/stylesheets/styles.css       | 343 ---------------------
 lib/formatter.js                   |   4 +-
 lib/formatter.min.js               |   2 +-
 lib/jquery.formatter.js            | 558 +++++++++++++++++++++++++++++++++
 lib/jquery.formatter.min.js        |   9 +
 src/formatter.js                   |   3 -
 src/tmpls/intro.js                 |   4 +
 src/tmpls/jquery.intro.js          |   1 +
 src/tmpls/jquery.outro.js          |  13 +
 src/tmpls/outro.js                 |   2 +
 17 files changed, 630 insertions(+), 1300 deletions(-)
 delete mode 100644 _site/demos.html
 delete mode 100644 _site/index.html
 delete mode 100644 _site/javascripts/formatter.js
 delete mode 100644 _site/javascripts/scale.fix.js
 delete mode 100644 _site/stylesheets/pygment_trac.css
 delete mode 100644 _site/stylesheets/styles.css
 create mode 100644 lib/jquery.formatter.js
 create mode 100644 lib/jquery.formatter.min.js
 create mode 100644 src/tmpls/intro.js
 create mode 100644 src/tmpls/jquery.intro.js
 create mode 100644 src/tmpls/jquery.outro.js
 create mode 100644 src/tmpls/outro.js

diff --git a/Gruntfile.js b/Gruntfile.js
index e09c554..d27e164 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -3,14 +3,24 @@ module.exports = function(grunt) {
   grunt.initConfig({
     pkg: grunt.file.readJSON('package.json'),
     jshint: {
-      all: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js', 'test/**/**/*.js'],
+      all: [
+        'Gruntfile.js',
+        'src/**/*.js',
+        'test/**/*.js',
+        'test/**/**/*.js'
+      ],
       options: {
+        ignores: [
+          'src/tmpls/intro.js',
+          'src/tmpls/outro.js',
+          'src/tmpls/jquery.intro.js',
+          'src/tmpls/jquery.outro.js'
+        ],
         force: true,
         // Bad line breaking before '?'.
         '-W014': true,
         // Expected a conditional expression and instead saw an assignment.
-        '-W084': true,
-        ignores: ['src/intro.js', 'src/outro.js']
+        '-W084': true
       }
     },
     concat: {
@@ -22,9 +32,7 @@ module.exports = function(grunt) {
           ' *\n' +
           ' * thanks to digitalBush/jquery.maskedinput for some of the trickier\n' +
           ' * keycode handling\n' +
-          ' */ \n\n' +
-          ' (function () {\n\n',
-        footer: '\n\n})();',
+          ' */ \n\n',
         process: function(src, filepath) {
           // Remove contents between Exclude Start and Exclude End
           src = src.replace( /\/\*\s*ExcludeStart\s*\*\/[\w\W]*?\/\*\s*ExcludeEnd\s*\*\//ig, '');
@@ -37,12 +45,25 @@ module.exports = function(grunt) {
       },
       vanilla: {
         src: [
+          'src/tmpls/intro.js',
           'src/formatter.js',
           'src/pattern.js',
           'src/inpt-sel.js',
-          'src/utils.js'
+          'src/utils.js',
+          'src/tmpls/outro.js'
         ],
         dest: 'lib/formatter.js'
+      },
+      jquery: {
+        src: [
+          'src/tmpls/jquery.intro.js',
+          'src/formatter.js',
+          'src/pattern.js',
+          'src/inpt-sel.js',
+          'src/utils.js',
+          'src/tmpls/jquery.outro.js'
+        ],
+        dest: 'lib/jquery.formatter.js'
       }
     },
     uglify: {
@@ -52,6 +73,10 @@ module.exports = function(grunt) {
       vanilla: {
         src: 'lib/formatter.js',
         dest: 'lib/formatter.min.js'
+      },
+      jquery: {
+        src: 'lib/jquery.formatter.js',
+        dest: 'lib/jquery.formatter.min.js'
       }
     }
   });
diff --git a/README.md b/README.md
index 158564b..732e664 100644
--- a/README.md
+++ b/README.md
@@ -30,19 +30,23 @@ Usage
 
 **Include script tag:**
 
-    <script src="bower_components/formatter/formatter.js">  
+    <script src="bower_components/formatter/lib/formatter.js">
+
+or
+
+  <script src="bower_components/formatter/lib/formatter.min.js"> 
     
 
 ### new Formatter(el, opts)
 
     new Formatter(document.getElementById('credit-input'), {
-        'str': '{{XXXX}}-{{XXXX}}-{{XXXX}}-{{XXXX}}'
+        'pattern': '{{999}}-{{999}}-{{999}}-{{9999}}'
+        'persistent': true
     });
 
 * **el** (required): Input element to manipulate
 * **opts** (required): An object holding instance specific options.
-  * **pattern** (required): String representing the pattern of your formatted input. User input areas begin with `{{` and end with `}}`. For example, a phone number may be represented: `({{XXX}}) {{XXX}}-{{XXXX}}`
-
+  * **pattern** (required): String representing the pattern of your formatted input. User input areas begin with `{{` and end with `}}`. For example, a phone number may be represented: `({{999}}) {{999}}-{{999}}`
 
 
 Examples / Demos
diff --git a/_site/demos.html b/_site/demos.html
deleted file mode 100644
index 6d468dd..0000000
--- a/_site/demos.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="chrome=1">
-    <title>formatter.js by firstopinion</title>
-
-    <link rel="stylesheet" href="stylesheets/styles.css">
-    <link rel="stylesheet" href="stylesheets/pygment_trac.css">
-    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
-    <!--[if lt IE 9]>
-    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-    <![endif]-->
-    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-  </head>
-  <body>
-    <div class="wrapper">
-      <header>
-        <h1>formatter.js</h1>
-        <p>Format user input to match a specified pattern</p>
-
-        <p class="view"><a href="https://github.com/firstopinion/formatter.js">View the Project on GitHub <small>firstopinion/formatter.js</small></a></p>
-        
-        <div class="menu">
-          <p class="menu-item"><a href="index.html">+ Docs</a></p>
-          <p class="menu-item"><a href="demos.html">+ Demos</a></p>
-        </div>
-
-        <ul>
-          <li><a href="https://github.com/firstopinion/formatter.js/zipball/master">Download <strong>ZIP File</strong></a></li>
-          <li><a href="https://github.com/firstopinion/formatter.js/tarball/master">Download <strong>TAR Ball</strong></a></li>
-          <li><a href="https://github.com/firstopinion/formatter.js">View On <strong>GitHub</strong></a></li>
-        </ul>
-      </header>
-      <section>
-
-        
-
-<h1>Demos</h1>
-
-<h2 id="credit">Credit Card</h2>
-<p><strong>Ex</strong>: 4242-4242-4242-4242</p>
-<div class="demo-input">
-  <div class="input-wrap input-s1 input-white">
-    <input type="text" class="input" id="credit-input" maxlength="19" pattern="\d*">
-  </div>
-</div>
-<pre><code>new Formatter(document.getElementById('credit-input'), {
-    'pattern': '{{9999}}-{{9999}}-{{9999}}-{{9999}}'
-});</code></pre>
-
-<h2 id="phone">Phone Number</h2>
-<p><strong>Ex</strong>: (802) 415-3411</p>
-<div class="demo-input">
-  <div class="input-wrap input-s1 input-white">
-    <input type="text" class="input" id="phone-input" maxlength="14" pattern="\d*">
-  </div>
-</div>
-<pre><code>new Formatter(document.getElementById('phone-input'), {
-    'pattern': '({{999}}) {{999}}.{{9999}}',
-    'persistent': true
-});</code></pre>
-
-
-
-      </section>
-    </div>
-    <script src="javascripts/scale.fix.js"></script>
-    <script src="javascripts/formatter.js"></script>
-    
-    <script>
-      var creditInput = document.getElementById('credit-input');
-      if (creditInput) {
-        new Formatter(creditInput, {
-            'pattern': '{{9999}}-{{9999}}-{{9999}}-{{9999}}'
-        });
-      }
-      var phoneInput = document.getElementById('phone-input');
-      if (creditInput) {
-        new Formatter(phoneInput, {
-            'pattern': '({{999}}) {{999}}.{{9999}}',
-            'persistent': true
-        });
-      }
-    </script>
-    
-  </body>
-</html>
\ No newline at end of file
diff --git a/_site/index.html b/_site/index.html
deleted file mode 100644
index 80766d3..0000000
--- a/_site/index.html
+++ /dev/null
@@ -1,154 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="chrome=1">
-    <title>formatter.js by firstopinion</title>
-
-    <link rel="stylesheet" href="stylesheets/styles.css">
-    <link rel="stylesheet" href="stylesheets/pygment_trac.css">
-    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
-    <!--[if lt IE 9]>
-    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-    <![endif]-->
-    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-  </head>
-  <body>
-    <div class="wrapper">
-      <header>
-        <h1>formatter.js</h1>
-        <p>Format user input to match a specified pattern</p>
-
-        <p class="view"><a href="https://github.com/firstopinion/formatter.js">View the Project on GitHub <small>firstopinion/formatter.js</small></a></p>
-        
-        <div class="menu">
-          <p class="menu-item"><a href="index.html">+ Docs</a></p>
-          <p class="menu-item"><a href="demos.html">+ Demos</a></p>
-        </div>
-
-        <ul>
-          <li><a href="https://github.com/firstopinion/formatter.js/zipball/master">Download <strong>ZIP File</strong></a></li>
-          <li><a href="https://github.com/firstopinion/formatter.js/tarball/master">Download <strong>TAR Ball</strong></a></li>
-          <li><a href="https://github.com/firstopinion/formatter.js">View On <strong>GitHub</strong></a></li>
-        </ul>
-      </header>
-      <section>
-
-        <h1 id='formatterjs_build_status'>formatter.js <a href='https://travis-ci.org/firstopinion/formatter.js'><img alt='Build Status' src='https://travis-ci.org/firstopinion/formatter.js.png' /></a></h1>
-
-<pre><code>   ___                    __  __              _   
-  / _/__  ______ _  ___ _/ /_/ /____ ____    (_)__
- / _/ _ \/ __/  &#39; \/ _ `/ __/ __/ -_) __/   / (_-&lt;
-/_/ \___/_/ /_/_/_/\_,_/\__/\__/\__/_/ (_)_/ /___/
-                                        |___/     </code></pre>
-
-<p>Format user input to match a specified pattern</p>
-
-<h2 id='why'>Why?</h2>
-
-<p>Sometimes it is useful to format user input as they type. Existing libraries lacked proper functionality / felxibility. Formatter was built from the ground up with no dependencies.</p>
-
-<h2 id='on_bower'>On Bower</h2>
-
-<pre><code>bower install formatter</code></pre>
-
-<h2 id='usage'>Usage</h2>
-
-<p><strong>Include script tag:</strong></p>
-
-<pre><code>&lt;script src=&quot;bower_components/formatter/formatter.js&quot;&gt;  </code></pre>
-
-<h3 id='new_formatterel_opts'>new Formatter(el, opts)</h3>
-
-<ul>
-<li>
-<p><strong>el</strong> (required): Input element to manipulate</p>
-</li>
-
-<li>
-<p><strong>opts</strong> (required): An object holding instance specific options.</p>
-
-<ul>
-<li><strong>pattern</strong> (required): String representing the pattern of your formatted input. User input areas begin with <code>{{</code> and end with <code>}}</code>. For example, a phone number may be represented: <code>({{XXX}}) {{XXX}}-{{XXXX}}</code></li>
-
-<li><strong>persistent</strong> : [False] Boolean representing if the formatted characters are always visible (persistent), or if they appear as you type.</li>
-</ul>
-</li>
-</ul>
-
-<h2 id='examples__demos'>Examples / Demos</h2>
-
-<p><strong>Credit Card</strong>: 4242-4242-4242-4242</p>
-
-<pre><code>new Formatter(document.getElementById(&#39;credit-input&#39;), {
-    &#39;pattern&#39;: &#39;{{9999}}-{{9999}}-{{9999}}-{{9999}}&#39;
-});</code></pre>
-
-<p><a href='http://firstopinion.github.io/formatter.js/demos.html#credit'>view demo</a></p>
-
-<p><strong>Phone Number</strong>: (802) 415-3411</p>
-
-<pre><code>new Formatter(document.getElementById(&#39;phone-input&#39;), {
-    &#39;pattern&#39;: &#39;({{999}}) {{999}}.{{9999}}&#39;
-    &#39;persistent&#39;: true
-});</code></pre>
-
-<p><a href='http://firstopinion.github.io/formatter.js/demos.html#phone'>view demo</a></p>
-
-<h2 id='tests'>Tests</h2>
-
-<p>Install Dependencies:</p>
-
-<pre><code>npm install</code></pre>
-
-<p>Run Tests:</p>
-
-<pre><code>npm test</code></pre>
-
-<h2 id='todos'>Todos</h2>
-
-<ul>
-<li>Selenium Tests (browserstack or saucelabs)</li>
-
-<li>Break out into src files &amp; create build script</li>
-</ul>
-
-<h2 id='known_issues'>Known Issues</h2>
-
-<ul>
-<li>Some patterns break the regex (should escape str before running)</li>
-</ul>
-
-<h2 id='license'>License</h2>
-
-<p>The MIT License (MIT) Copyright (c) 2013 First Opinion</p>
-
-<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8220;Software&#8221;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
-
-<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
-
-<p>THE SOFTWARE IS PROVIDED &#8220;AS IS&#8221;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
-
-      </section>
-    </div>
-    <script src="javascripts/scale.fix.js"></script>
-    <script src="javascripts/formatter.js"></script>
-    
-    <script>
-      var creditInput = document.getElementById('credit-input');
-      if (creditInput) {
-        new Formatter(creditInput, {
-            'pattern': '{{9999}}-{{9999}}-{{9999}}-{{9999}}'
-        });
-      }
-      var phoneInput = document.getElementById('phone-input');
-      if (creditInput) {
-        new Formatter(phoneInput, {
-            'pattern': '({{999}}) {{999}}.{{9999}}',
-            'persistent': true
-        });
-      }
-    </script>
-    
-  </body>
-</html>
\ No newline at end of file
diff --git a/_site/javascripts/formatter.js b/_site/javascripts/formatter.js
deleted file mode 100644
index 0db444a..0000000
--- a/_site/javascripts/formatter.js
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- *    ___                    __  __              _   
- *   / _/__  ______ _  ___ _/ /_/ /____ ____    (_)__
- *  / _/ _ \/ __/  ' \/ _ `/ __/ __/ -_) __/   / (_-<
- * /_/ \___/_/ /_/_/_/\_,_/\__/\__/\__/_/ (_)_/ /___/
- *                                           |___/  
- * v0.0.1
- * Copyright (c) 2013 First Opinion
- * formatter.js is open sourced under the MIT license.
- *
- * thanks to digitalBush/jquery.maskedinput for some of the trickier
- * keycode handling
- */
-
-
-
-(function() {
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Setup
-  // - inspiration from underscore.js
-  /////////////////////////////////////////////////////////////////////////////
-  
-  // Establish the root object, window in the browser,
-  // or exports on the server.
-  var root = this;
-
-  if (typeof module !== 'undefined' && module.exports) {
-    exports.Formatter = Formatter;
-  } else {
-    root.Formatter = Formatter;
-  }
-
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Scope Vars
-  /////////////////////////////////////////////////////////////////////////////
-
-  // Defaults
-  var defaults = {
-    persistent: false,
-    repeat: false,
-    placeholder: ' '
-  };
-  
-  // Regexs for input validation
-  var inptRegs = {
-    '9': new RegExp('[0-9]'),
-    'a': new RegExp('[A-Za-z]'),
-    '*': new RegExp('[A-Za-z0-9]')
-  };
-
-  // Useragent info for keycode handling
-  var iPhone = /iphone/i.test(navigator.userAgent);
-
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Class
-  /////////////////////////////////////////////////////////////////////////////
-
-  //
-  // @public
-  // Class Constructor - Called with new Formatter(el, opts)
-  // Responsible for setting up required instance variables, and
-  // attaching the event listener to the element
-  //
-  function Formatter(el, opts) {
-    // The element to FORMAT
-    this.el = el;
-
-    // Make sure we have an element
-    if (!this.el) { 
-      throw new TypeError('Must provide an existing element');
-    }
-
-    // Merge opts with defaults
-    this.opts = extend({}, defaults, opts);
-
-    // Make sure we have valid opts
-    if (typeof this.opts.pattern === 'undefined') {
-      throw new TypeError('Must provide a pattern');
-    } else if (this.opts.persistent && this.opts.repeat) {
-      throw new TypeError('Cannot persist and repeat');
-    }
-
-    // Get info about the given pattern
-    var patternInfo = getPatternInfo(this.opts.pattern);
-    this.maxLength  = patternInfo.maxLength;
-    this.chars      = patternInfo.chars;
-    this.inpts      = patternInfo.inpts;
-
-    // Init values
-    this.focusStart = 0;
-    this.hldrs      = {};
-
-    // If persistence, format on start
-    if (this.opts.persistent) {
-      this._processKey(null, true);
-      this.el.blur();
-    }
-
-    // Attach all listeners
-    this._addEventListeners();
-  };
-
-  //
-  // @private
-  //
-  Formatter.prototype._addEventListeners = function () {
-    // Cache this
-    var self = this;
-
-    // Handlers
-    addEventListener(this.el, 'keydown', function (evt) {
-      self._keyDown(evt);
-    });
-    addEventListener(this.el, 'keypress', function (evt) {
-      self._keyPress(evt);
-    });
-    addEventListener(this.el, 'paste', function (evt) {
-      self._paste(evt);
-    });
-
-    // Persistence handlers for focus
-    if (this.opts.persistent) {
-      addEventListener(this.el, 'focus', function (evt) {
-        self._focus(evt);
-      });
-      addEventListener(this.el, 'mousedown', function (evt) {
-        self._focus(evt);
-      });
-    }
-  };
-
-  //
-  // @private
-  // This is the handler called on all keyDown strokes. All keys fire this
-  // handler. We use this handler to deal with special keys. Only process
-  // delete keys.
-  // 
-  //
-  Formatter.prototype._keyDown = function (evt) {
-    // The first thing we need is the character code
-    var k = evt.which || evt.keyCode;
-
-    // If delete key
-    if (k && isDelKey(k)) {
-      // Process the keyCode and prevent default
-      this._processKey(null, true);
-      return preventDefault(evt);
-    }
-  };
-
-  //
-  // @private
-  // This is the handler called on all keyPress strokes. This handler
-  // is used to process characters.
-  //
-  Formatter.prototype._keyPress = function (evt) {
-    // The first thing we need is the character code
-    var k = evt.which || evt.keyCode;
-
-    // Process the keyCode and prevent default
-    if (!isDelKey(k) && !isModifier(evt)) {
-      this._processKey(String.fromCharCode(k), false);
-      return preventDefault(evt);
-    }
-  };
-
-  //
-  // @private
-  // Handle paste events.
-  //
-  Formatter.prototype._paste = function (evt) {
-    // Process the clipboard paste and prevent default
-    this._processKey(getClipBoardData(evt), false);
-    return preventDefault(evt);
-  };
-
-  //
-  // @private
-  // Handle paste events.
-  //
-  Formatter.prototype._focus = function (evt) {
-    // Wrapped in timeout so that we can grab input selection
-    var self = this;
-    setTimeout(function () {
-      // Grab selection
-      var selection = getInputSelection(self.el);
-      // Char check
-      var isAfterStart = selection.end > self.focusStart;
-          isFirstChar  = selection.end == 0;
-      // If clicked in front of start, refocus to start
-      if (isAfterStart || isFirstChar) {
-        setInputSelection(self.el, self.focusStart);
-      }
-    }, 0);
-  };
-
-  //
-  // @private
-  // Using the provided key information, alter the value of the
-  // instance element.
-  //
-  Formatter.prototype._processKey = function (chars, isDelKey) {
-    // Our old newPos is now our oldPos
-    // String manipulation depends on current selection/position
-    this.sel = getInputSelection(this.el);
-
-    // Get the el vlaue (prior to processing current key)
-    this.val = this.el.value;
-    // We will record the delta of our actions so that we can
-    // properly account for it during formatting
-    this.delta = 0;
-
-    // If characters were highlighted, we will need to remove them
-    if (this.sel.start !== this.sel.end) {
-      this.val = removeCharsFrom(this.val, this.sel.start, this.sel.end);
-      this.delta = -Math.abs(this.sel.start - this.sel.end);
-
-    // If pressed key is a del key, and the caret pos is not at start,
-    // remove at caret pos
-    } else if (isDelKey && this.sel.start-1 >= 0) {
-      this.val = removeCharsFrom(this.val, this.sel.end -1, this.sel.end);
-      this.delta = -1;
-    }
-
-    // If the key is not a del key, it should convert to a str
-    if (!isDelKey) {
-      // Add char at position and increment delta
-      this.val = addCharsAtPos(this.val, chars, this.sel.start);
-      this.delta += chars.length;
-    }
-
-    // Format el.value (also handles updating caret position)
-    this._formatValue();
-  };
-
-  //
-  // @private
-  // Alter element value to display characters matching the provided
-  // instance pattern. Also responsible for updatin
-  //
-  Formatter.prototype._formatValue = function () {
-    // Set caret pos
-    this.curPos = this.sel.end;
-    this.newPos = this.curPos + this.delta;
-
-    // Remove all formatted chars from val
-    this._removeChars();
-
-    // Validate inpts
-    this._validateInpts();
-
-    // Add formatted characters
-    if (this.opts.persistent) { 
-      this._addCharsPersist();
-    } else {
-      this._addChars();
-    }
-
-    // Adhere to maxLength
-    this.el.value = this.val.substr(0, this.maxLength);
-
-    // Set new caret position
-    setInputSelection(this.el, this.newPos);
-  };
-
-  //
-  // @private
-  // Loop over val and add formatted chars as necessary
-  //
-  Formatter.prototype._addChars = function () {
-    // Avoid caching val.length, as it changes during manipulations
-    for (var i = 0; i <= this.val.length; i++) {
-      this._addChar(i);
-    }
-  };
-
-  //
-  // @private
-  // Loop over vals and add formatted chars as necessary.
-  //
-  Formatter.prototype._addCharsPersist = function () {
-    for (var i = 0; i <= this.maxLength; i++) {
-      if (!this.val[i]) {
-        // Add placeholder at pos
-        this.val = addCharsAtPos(this.val, this.opts.placeholder, i);
-        this.hldrs[i] = this.opts.placeholder;
-      }
-      this._addChar(i);
-    }
-    // Adjust focusStart to make sure its not on a formatted char
-    while (this.chars[this.focusStart]) { this.focusStart++ }
-  };
-
-  //
-  // @private
-  // Add formattted char at position
-  //
-  Formatter.prototype._addChar = function (i) {
-    // If char exists at position
-    var char = this.chars[i];
-    if (!char) { return true }
-
-    // If chars are added in between the old pos and new pos
-    // we need to increment pos and delta
-    if (isBetween(i, [this.sel.start -1, this.newPos +1])) {
-      this.newPos ++;
-      this.delta ++;
-    }
-
-    // If character added before focusStart, incr
-    if (i <= this.focusStart) {
-      this.focusStart++;
-    }
-
-    // When moving backwards there are some race conditions where we
-    // dont want to add the character
-    if (this.delta < 0 && (this.val[i] == char )) { return true }
-
-    // Updateholder
-    if (this.hldrs[i]) {
-      delete this.hldrs[i]
-      this.hldrs[i + 1] = this.opts.placeholder;
-    }
-
-    // Update value
-    this.val = addCharsAtPos(this.val, char, i)
-  };
-
-  //
-  // @private
-  // Remove all formatted before and after a specified pos
-  //
-  Formatter.prototype._removeChars = function () {
-    // Delta shouldn't include placeholders
-    if (this.sel.end > this.focusStart) {
-      this.delta += this.sel.end - this.focusStart
-    }
-
-    // Account for shifts during removal
-    var shift = 0;
-    // Loop through all possible char positions
-    for (var i = 0; i <= this.maxLength; i++) {
-
-      // If no char or holder at position, skip current incr
-      if (!this.chars[i] && !this.hldrs[i]) { continue }
-
-      // Transformed position accounts for shift from removal, as well
-      // as delta from user input (after pos)
-      var transPos = (i >= this.sel.start) ? i + this.delta + shift : i + shift;
-
-      // If we can't remove a char, skip current incr
-      var isChar = this.chars[i] && this.chars[i] == this.val[transPos];
-          isHldr = this.hldrs[i] && this.hldrs[i]== this.val[transPos];
-      if (!isChar && !isHldr) { continue }
-
-      // Remove and account for shift
-      this.val = removeCharsFrom(this.val, transPos, transPos + 1);
-      shift--;
-    }
-
-    // All hldrs should be removed now
-    this.hldrs = {}
-    // Set focusStart to last character
-    this.focusStart = this.val.length;
-  };
-
-  //
-  // @private
-  // Make sure all inpts are valid, else remove and update delta
-  //
-  Formatter.prototype._validateInpts = function () {
-    for (var i = 0; i < this.val.length; i++) {
-      var inptType = this.inpts[i];
-      if (!inptRegs[inptType] || !inptRegs[inptType].test(this.val[i])) {
-        this.val = removeCharsFrom(this.val, i, i + 1);
-        this.delta--;
-        this.newPos--;
-        this.focusStart--;
-        i--;
-      }
-    }
-  };
-
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Class Utils
-  /////////////////////////////////////////////////////////////////////////////
-
-  //
-  // @private
-  // Create an object holding all formatted characters
-  // with corresponding positions
-  //
-  var getPatternInfo = function (pattern) {
-    // Account for delim characters
-    var DELIM_SIZE = 4;
-
-    // Object to return
-    var info = { inpts: {}, chars: {} };
-
-    // Pattern matches
-    var matches = [],
-        regexp  = new RegExp('{{([^}]+)}}', 'g');
-
-    while(match = regexp.exec(pattern)) {
-      matches.push(match)
-    }
-
-    // Loop over all chars in pattern.
-    var mCount = iCount = i = 0,
-        pLength = pattern.length;
-    
-    for (i; i < pLength; i++) {
-      // The current match object
-      var match = matches[mCount];
-
-      // If the current char is the start of a match
-      if (i == match.index) {
-        // Cache match value
-        var val = match[1];
-        // Add to inpt
-        var valLength = val.length;
-        for (var j = 0; j < valLength; j++) {
-          info.inpts[iCount] = val[j];
-          iCount++;
-        }
-        // Incr i in order to skip over the rest of these chars
-        i += (match[1].length + DELIM_SIZE - 1);
-        // Move onto next match
-        mCount ++;
-
-      // If not we must add to chars obj
-      } else {
-        info.chars[i - (mCount * DELIM_SIZE)] = pattern[i];
-      }
-    }
-
-    // Set maxLength
-    info.maxLength = i - (mCount * DELIM_SIZE);
-
-    // Return the chars object and the length of the pattern
-    return info;
-  };
-
-  //
-  // @private
-  // Return array filled with match objects
-  //
-  var getPatternMatch = function (pattern) {
-    // Populate array of matches
-    var matches = [],
-        regexp  = new RegExp('{{([^}]+)}}', 'g');
-
-    while(match = regexp.exec(pattern)) {
-      matches.push(match)
-    }
-
-    return matches
-  }
-
-  //
-  // @private
-  // Add a given character to a string at a defined pos
-  //
-  var addCharsAtPos = function (str, chars, pos) {
-    return str.substr(0, pos) + chars + str.substr(pos, str.length);
-  };
-
-  //
-  // @private
-  // Remove a span of characters
-  //
-  var removeCharsFrom = function (str, start, end) {
-    return str.substr(0, start) + str.substr(end, str.length);
-  };
-
-  //
-  // @private
-  // Shallow copy properties from n objects to destObj
-  //
-  var extend = function (destObj) {
-    for (var i = 1; i < arguments.length; i++) {
-      for (var key in arguments[i]) {
-        destObj[key] = arguments[i][key];
-      }
-    }
-    return destObj;
-  };
-
-  //
-  // @private
-  // Return true/false is num false between bounds
-  //
-  var isBetween = function (num, bounds) {
-    bounds.sort(function(a,b) { return a-b });
-    return (num > bounds[0] && num < bounds[1])
-  };
-
-  // @private
-  // Returns true/false if k is a del key
-  //
-  var isDelKey = function (k) {
-    return k === 8 || k === 46 || (iPhone && k === 127)
-  };
-
-  //
-  // @private
-  // Returns true/false if modifier key is held down
-  //
-  var isModifier = function (evt) {
-    return evt.ctrlKey || evt.altKey || evt.metaKey
-  };
-
-  //
-  // @private
-  // Helper method for cross browser event listeners
-  //
-  var addEventListener = function (el, evt, handler) {
-    return (typeof el.addEventListener != "undefined")
-      ? el.addEventListener(evt, handler, false)
-      : el.attachEvent('on' + evt, handler);
-  };
-
-  //
-  // @private
-  // Helper method for cross browser implementation of preventDefault
-  //
-  var preventDefault = function (evt) {
-    (evt.preventDefault) ? evt.preventDefault() : (evt.returnValue = false);
-  };
-
-  //
-  // @private
-  // Helper method for cross browser implementation for grabbing
-  // clipboard data
-  //
-  var getClipBoardData = function (evt) {
-    if (evt.clipboardData) { return evt.clipboardData.getData('Text') }
-    if (window.clipboardData) { return window.clipboardData.getData('Text') }
-  };
-
-  //
-  // @private
-  // Get start and end positions of selected input. Return 0's
-  // if there is no selectiion data
-  //
-  var getInputSelection = function (el) {
-    // If normal browser return with result
-    if (typeof el.selectionStart == "number") {
-      return { 
-        start: el.selectionStart,
-        end: el.selectionEnd
-      }
-    }
-
-    // Uh-Oh. We must be IE. Fun with TextRange!!
-    var range = document.selection.createRange();
-    // Determine if there is a selection
-    if (range && range.parentElement() == el) {
-      var inputRange = el.createTextRange(),
-          endRange   = el.createTextRange(),
-          length     = el.value.length;
-
-      // Create a working TextRange for the input selection
-      inputRange.moveToBookmark(range.getBookmark());
-
-      // Move endRange start pos to end pos (hence endRange)
-      endRange.collapse(false);
-      
-      // If we are at the very end of the input, start and end
-      // must both be the length of the el.value
-      if (inputRange.compareEndPoints("StartToEnd", endRange) > -1) {
-        return { start: length, end: length }
-      }
-
-      // Note: moveStart usually returns the units moved, which one may
-      // think is -length, however, it will stop when it gets to the
-      // start of the range, thus giving us the negative value of the pos.
-      return {
-        start: -inputRange.moveStart("character", -length),
-        end: -inputRange.moveEnd("character", -length)
-      }
-    }
-
-    //Return 0's on no selection data
-    return { start: 0, end: 0 }
-  };
-
-  //
-  // @private
-  // Set the caret position at a specified location
-  //
-  var setInputSelection = function (el, pos) {
-    // If normal browser
-    if (el.setSelectionRange) {
-      el.focus();
-      el.setSelectionRange(pos,pos);
-
-    // IE = TextRange fun
-    } else if (el.createTextRange) {
-      var range = el.createTextRange();
-      range.collapse(true);
-      range.moveEnd('character', pos);
-      range.moveStart('character', pos);
-      range.select();
-    }
-  };
-
-})();
\ No newline at end of file
diff --git a/_site/javascripts/scale.fix.js b/_site/javascripts/scale.fix.js
deleted file mode 100644
index 87a40ca..0000000
--- a/_site/javascripts/scale.fix.js
+++ /dev/null
@@ -1,17 +0,0 @@
-var metas = document.getElementsByTagName('meta');
-var i;
-if (navigator.userAgent.match(/iPhone/i)) {
-  for (i=0; i<metas.length; i++) {
-    if (metas[i].name == "viewport") {
-      metas[i].content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
-    }
-  }
-  document.addEventListener("gesturestart", gestureStart, false);
-}
-function gestureStart() {
-  for (i=0; i<metas.length; i++) {
-    if (metas[i].name == "viewport") {
-      metas[i].content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
-    }
-  }
-}
\ No newline at end of file
diff --git a/_site/stylesheets/pygment_trac.css b/_site/stylesheets/pygment_trac.css
deleted file mode 100644
index c6a6452..0000000
--- a/_site/stylesheets/pygment_trac.css
+++ /dev/null
@@ -1,69 +0,0 @@
-.highlight  { background: #ffffff; }
-.highlight .c { color: #999988; font-style: italic } /* Comment */
-.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
-.highlight .k { font-weight: bold } /* Keyword */
-.highlight .o { font-weight: bold } /* Operator */
-.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
-.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
-.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
-.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
-.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
-.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
-.highlight .ge { font-style: italic } /* Generic.Emph */
-.highlight .gr { color: #aa0000 } /* Generic.Error */
-.highlight .gh { color: #999999 } /* Generic.Heading */
-.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
-.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
-.highlight .go { color: #888888 } /* Generic.Output */
-.highlight .gp { color: #555555 } /* Generic.Prompt */
-.highlight .gs { font-weight: bold } /* Generic.Strong */
-.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
-.highlight .gt { color: #aa0000 } /* Generic.Traceback */
-.highlight .kc { font-weight: bold } /* Keyword.Constant */
-.highlight .kd { font-weight: bold } /* Keyword.Declaration */
-.highlight .kn { font-weight: bold } /* Keyword.Namespace */
-.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
-.highlight .kr { font-weight: bold } /* Keyword.Reserved */
-.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
-.highlight .m { color: #009999 } /* Literal.Number */
-.highlight .s { color: #d14 } /* Literal.String */
-.highlight .na { color: #008080 } /* Name.Attribute */
-.highlight .nb { color: #0086B3 } /* Name.Builtin */
-.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
-.highlight .no { color: #008080 } /* Name.Constant */
-.highlight .ni { color: #800080 } /* Name.Entity */
-.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
-.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
-.highlight .nn { color: #555555 } /* Name.Namespace */
-.highlight .nt { color: #000080 } /* Name.Tag */
-.highlight .nv { color: #008080 } /* Name.Variable */
-.highlight .ow { font-weight: bold } /* Operator.Word */
-.highlight .w { color: #bbbbbb } /* Text.Whitespace */
-.highlight .mf { color: #009999 } /* Literal.Number.Float */
-.highlight .mh { color: #009999 } /* Literal.Number.Hex */
-.highlight .mi { color: #009999 } /* Literal.Number.Integer */
-.highlight .mo { color: #009999 } /* Literal.Number.Oct */
-.highlight .sb { color: #d14 } /* Literal.String.Backtick */
-.highlight .sc { color: #d14 } /* Literal.String.Char */
-.highlight .sd { color: #d14 } /* Literal.String.Doc */
-.highlight .s2 { color: #d14 } /* Literal.String.Double */
-.highlight .se { color: #d14 } /* Literal.String.Escape */
-.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
-.highlight .si { color: #d14 } /* Literal.String.Interpol */
-.highlight .sx { color: #d14 } /* Literal.String.Other */
-.highlight .sr { color: #009926 } /* Literal.String.Regex */
-.highlight .s1 { color: #d14 } /* Literal.String.Single */
-.highlight .ss { color: #990073 } /* Literal.String.Symbol */
-.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
-.highlight .vc { color: #008080 } /* Name.Variable.Class */
-.highlight .vg { color: #008080 } /* Name.Variable.Global */
-.highlight .vi { color: #008080 } /* Name.Variable.Instance */
-.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
-
-.type-csharp .highlight .k { color: #0000FF }
-.type-csharp .highlight .kt { color: #0000FF }
-.type-csharp .highlight .nf { color: #000000; font-weight: normal }
-.type-csharp .highlight .nc { color: #2B91AF }
-.type-csharp .highlight .nn { color: #000000 }
-.type-csharp .highlight .s { color: #A31515 }
-.type-csharp .highlight .sc { color: #A31515 }
diff --git a/_site/stylesheets/styles.css b/_site/stylesheets/styles.css
deleted file mode 100644
index 20d2502..0000000
--- a/_site/stylesheets/styles.css
+++ /dev/null
@@ -1,343 +0,0 @@
-/* 1. theme
-----------------------------------------------------------------------------------------------------*/
-@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);
-
-body {
-  padding:50px;
-  font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
-  color:#777;
-  font-weight:300;
-}
-
-h1, h2, h3, h4, h5, h6 {
-  color:#222;
-  margin:0 0 20px;
-}
-
-p, ul, ol, table, pre, dl {
-  margin:0 0 20px;
-}
-
-h1, h2, h3 {
-  line-height:1.1;
-}
-
-h1 {
-  font-size:28px;
-}
-
-h2 {
-  color:#393939;
-}
-
-h3, h4, h5, h6 {
-  color:#494949;
-}
-
-a {
-  color:#39c;
-  font-weight:400;
-  text-decoration:none;
-}
-
-a small {
-  font-size:11px;
-  color:#777;
-  margin-top:-0.6em;
-  display:block;
-}
-
-.wrapper {
-  width:860px;
-  margin:0 auto;
-}
-
-blockquote {
-  border-left:1px solid #e5e5e5;
-  margin:0;
-  padding:0 0 0 20px;
-  font-style:italic;
-}
-
-code, pre {
-  font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
-  color:#333;
-  font-size:12px;
-}
-
-pre {
-  padding:8px 15px;
-  background: #f8f8f8;  
-  border-radius:5px;
-  border:1px solid #e5e5e5;
-  overflow-x: auto;
-}
-
-table {
-  width:100%;
-  border-collapse:collapse;
-}
-
-th, td {
-  text-align:left;
-  padding:5px 10px;
-  border-bottom:1px solid #e5e5e5;
-}
-
-dt {
-  color:#444;
-  font-weight:700;
-}
-
-th {
-  color:#444;
-}
-
-img {
-  max-width:100%;
-}
-
-header {
-  width:270px;
-  float:left;
-  position:fixed;
-}
-
-header ul {
-  list-style:none;
-  height:40px;
-  
-  padding:0;
-  
-  background: #eee;
-  background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
-  background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
-  background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
-  background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
-  background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
-  
-  border-radius:5px;
-  border:1px solid #d2d2d2;
-  box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
-  width:270px;
-}
-
-header li {
-  width:89px;
-  float:left;
-  border-right:1px solid #d2d2d2;
-  height:40px;
-}
-
-header ul a {
-  line-height:1;
-  font-size:11px;
-  color:#999;
-  display:block;
-  text-align:center;
-  padding-top:6px;
-  height:40px;
-}
-
-strong {
-  color:#222;
-  font-weight:700;
-}
-
-header ul li + li {
-  width:88px;
-  border-left:1px solid #fff;
-}
-
-header ul li + li + li {
-  border-right:none;
-  width:89px;
-}
-
-header ul a strong {
-  font-size:14px;
-  display:block;
-  color:#222;
-}
-
-section {
-  width:500px;
-  float:right;
-  padding-bottom:50px;
-}
-
-small {
-  font-size:11px;
-}
-
-hr {
-  border:0;
-  background:#e5e5e5;
-  height:1px;
-  margin:0 0 20px;
-}
-
-footer {
-  width:270px;
-  float:left;
-  position:fixed;
-  bottom:50px;
-}
-
-@media print, screen and (max-width: 960px) {
-  
-  div.wrapper {
-    width:auto;
-    margin:0;
-  }
-  
-  header, section, footer {
-    float:none;
-    position:static;
-    width:auto;
-  }
-  
-  header {
-    padding-right:320px;
-  }
-  
-  section {
-    border:1px solid #e5e5e5;
-    border-width:1px 0;
-    padding:20px 0;
-    margin:0 0 20px;
-  }
-  
-  header a small {
-    display:inline;
-  }
-  
-  header ul {
-    position:absolute;
-    right:50px;
-    top:52px;
-  }
-}
-
-@media print, screen and (max-width: 720px) {
-  body {
-    word-wrap:break-word;
-  }
-  
-  header {
-    padding:0;
-  }
-  
-  header ul, header p.view {
-    position:static;
-  }
-  
-  pre, code {
-    word-wrap:normal;
-  }
-}
-
-@media print, screen and (max-width: 480px) {
-  body {
-    padding:15px;
-  }
-  
-  header ul {
-    display:none;
-  }
-}
-
-@media print {
-  body {
-    padding:0.4in;
-    font-size:12pt;
-    color:#444;
-  }
-}
-
-.menu {
-  margin: 0 0 20px 0;
-  padding: 0;
-  border-top: 1px solid #ddd;
-}
-.menu-item {
-  margin: 0;
-  border-bottom: 1px solid #ddd;
-}
-.menu-item > a {
-  display: block;
-  padding: 10px 0;
-}
-.demo-input {
-  margin-bottom: 20px;
-  padding: 15px;
-  border-radius: 4px;
-  background: #fafafa;
-}
-/* 2. form elements
-----------------------------------------------------------------------------------------------------*/
-input {
-  display: block;
-  margin: 0;
-  padding: 0;
-  border: none;
-  outline: none;
-  border: 0;
-  outline: none;
-  background: none;
-  font-family: "Lucida Console", Monaco, monospace;
-}
-input[type=file] {
-  width: 1px;
-}
-input[type=number]::-webkit-inner-spin-button,
-input[type=number]::-webkit-outer-spin-button {
-    -webkit-appearance: none;
-    margin: 0;
-}
-
-/* 3. input
-----------------------------------------------------------------------------------------------------*/
-.input-wrap {
-  display: block;
-  position: relative;
-  *position: static;
-  overflow: hidden;
-  line-height: 0;
-  vertical-align: middle;
-}
-.input {
-  width: 100%;
-  border: medium none;
-}
-
-.input-s1 {
-  padding: 21px 21px 17px 21px;
-  height: 16px;
-  line-height: 1;
-  font-size: 16px;
-  font-weight: light;
-  border-radius: 4px;
-  border: 1px solid rgb(250, 250, 250);
-}
-.input-s1 > .input {
-  margin: -19px -21px -17px -21px;
-  padding: 19px 21px 17px 21px;
-  height: 16px;
-  border-radius: 4px;
-}
-.input-white {
-  background: #d3d3ce; /* Old browsers */
-  background: linear-gradient(to bottom, #d3d3ce 0%,#fff 100%); /* W3C */
-}
-.input-white > .input {
-  color: #676a6c;
-  background: #fff;
-}
-.input-white > .input::-webkit-input-placeholder {
-  color: #bcbec0;
-}
-.input-white > .input:-moz-placeholder {  
-  color: #bcbec0;
-}
\ No newline at end of file
diff --git a/lib/formatter.js b/lib/formatter.js
index b032c13..fb0471d 100644
--- a/lib/formatter.js
+++ b/lib/formatter.js
@@ -7,7 +7,7 @@
  * keycode handling
  */ 
 
- (function () {
+;(function (window, document, undefined) {
 
 // Expose to window
 if (typeof window !== 'undefined') { window.Formatter = Formatter; }
@@ -547,4 +547,4 @@ utils.isModifier = function (evt) {
   return evt.ctrlKey || evt.altKey || evt.metaKey;
 };
 
-})();
\ No newline at end of file
+})(window, document);
\ No newline at end of file
diff --git a/lib/formatter.min.js b/lib/formatter.min.js
index 6e6f0c3..f8a48ff 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(){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(null,!0),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)}))}"undefined"!=typeof window&&(window.Formatter=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._keyDown=function(a){var b=a.which||a.keyCode;return b&&i.isDelKey(b)?(this._processKey(null,!0),i.preventDefault(a)):void 0},a.prototype._keyPress=function(a){var b=a.which||a.keyCode;return i.isDelKey(b)||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){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)):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._formatValue=function(){this.curPos=this.sel.end,this.newPos=this.curPos+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[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[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[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[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[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[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);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.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey}}();
\ No newline at end of file
+!function(a,b){function c(a,b){var c=this;if(c.el=a,!c.el)throw new TypeError("Must provide an existing element");if(c.opts=k.extend({},d,b),"undefined"==typeof c.opts.pattern)throw new TypeError("Must provide a pattern");var e=f.parse(c.opts.pattern);c.mLength=e.mLength,c.chars=e.chars,c.inpts=e.inpts,c.hldrs={},c.focus=0,k.addListener(c.el,"keydown",function(a){c._keyDown(a)}),k.addListener(c.el,"keypress",function(a){c._keyPress(a)}),k.addListener(c.el,"paste",function(a){c._paste(a)}),c.opts.persistent&&(c._processKey(null,!0),c.el.blur(),k.addListener(c.el,"focus",function(a){c._focus(a)}),k.addListener(c.el,"click",function(a){c._focus(a)}),k.addListener(c.el,"touchstart",function(a){c._focus(a)}))}"undefined"!=typeof a&&(a.Formatter=c);var d={persistent:!1,repeat:!1,placeholder:" "},e={9:new RegExp("[0-9]"),a:new RegExp("[A-Za-z]"),"*":new RegExp("[A-Za-z0-9]")};c.prototype._keyDown=function(a){var b=a.which||a.keyCode;return b&&k.isDelKey(b)?(this._processKey(null,!0),k.preventDefault(a)):void 0},c.prototype._keyPress=function(a){var b=a.which||a.keyCode;return k.isDelKey(b)||k.isModifier(a)?void 0:(this._processKey(String.fromCharCode(b),!1),k.preventDefault(a))},c.prototype._paste=function(a){return this._processKey(k.getClip(a),!1),k.preventDefault(a)},c.prototype._focus=function(){var a=this;setTimeout(function(){var b=j.get(a.el),c=b.end>a.focus;isFirstChar=0===b.end,(c||isFirstChar)&&j.set(a.el,a.focus)},0)},c.prototype._processKey=function(a,b){this.sel=j.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=k.removeChars(this.val,this.sel.begin,this.sel.end)):b&&this.sel.begin-1>=0&&(this.val=k.removeChars(this.val,this.sel.end-1,this.sel.end),this.delta=-1),b||(this.val=k.addChars(this.val,a,this.sel.begin),this.delta+=a.length),this._formatValue()},c.prototype._formatValue=function(){this.curPos=this.sel.end,this.newPos=this.curPos+this.delta,this._removeChars(),this._validateInpts(),this._addChars(),this.el.value=this.val.substr(0,this.mLength),j.set(this.el,this.newPos)},c.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[f],(d&&d==c||e&&e==c)&&(this.val=k.removeChars(this.val,f,f+1),a--)}this.hldrs={},this.focus=this.val.length},c.prototype._validateInpts=function(){for(var a=0;a<this.val.length;a++){var b=this.inpts[a];e[b]&&e[b].test(this.val[a])||this.inpts[a]&&(this.val=k.removeChars(this.val,a,a+1),this.focusStart--,this.newPos--,this.delta--,a--)}},c.prototype._addChars=function(){if(this.opts.persistent){for(var a=0;a<=this.mLength;a++)this.val[a]||(this.val=k.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)},c.prototype._addChar=function(a){var b=this.chars[a];return b?(k.isBetween(a,[this.sel.begin-1,this.newPos+1])&&(this.newPos++,this.delta++),a<=this.focus&&this.focus++,this.delta<0&&this.val[a]==b?!0:(this.hldrs[a]&&(delete this.hldrs[a],this.hldrs[a+1]=this.opts.placeholder),this.val=k.addChars(this.val,b,a),void 0)):!0};var f={},g=4,h=new RegExp("{{([^}]+)}}","g"),i=function(a){for(var b,c=[];b=h.exec(a);)c.push(b);return c};f.parse=function(a){var b={inpts:{},chars:{}},c=i(a),d=a.length,e=0,f=0,h=0,j=function(a){for(var c=a.length,d=0;c>d;d++)b.inpts[f]=a[d],f++;e++,h+=a.length+g-1};for(h;d>h;h++)h==c[e].index?j(c[e][1]):b.chars[h-e*g]=a[h];return b.mLength=h-e*g,b};var j={};j.get=function(a){if("number"==typeof a.selectionStart)return{begin:a.selectionStart,end:a.selectionEnd};var c=b.selection.createRange();if(c&&c.parentElement()==a){var d=a.createTextRange(),e=a.createTextRange(),f=a.value.length;return d.moveToBookmark(c.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}},j.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 k={},l="undefined"!=typeof navigator?navigator.userAgent:null,m=/iphone/i.test(l);k.extend=function(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])a[c]=arguments[b][c];return a},k.addChars=function(a,b,c){return a.substr(0,c)+b+a.substr(c,a.length)},k.removeChars=function(a,b,c){return a.substr(0,b)+a.substr(c,a.length)},k.isBetween=function(a,b){return b.sort(function(a,b){return a-b}),a>b[0]&&a<b[1]},k.addListener=function(a,b,c){return"undefined"!=typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},k.preventDefault=function(a){return a.preventDefault?a.preventDefault():a.returnValue=!1},k.getClip=function(b){return b.clipboardData?b.clipboardData.getData("Text"):a.clipboardData?a.clipboardData.getData("Text"):void 0},k.isDelKey=function(a){return 8===a||46===a||m&&127===a},k.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey}}(window,document);
\ No newline at end of file
diff --git a/lib/jquery.formatter.js b/lib/jquery.formatter.js
new file mode 100644
index 0000000..af78fb9
--- /dev/null
+++ b/lib/jquery.formatter.js
@@ -0,0 +1,558 @@
+/*!
+ * v0.0.3
+ * Copyright (c) 2013 First Opinion
+ * formatter.js is open sourced under the MIT license.
+ *
+ * thanks to digitalBush/jquery.maskedinput for some of the trickier
+ * keycode handling
+ */ 
+
+;(function ($, window, document, undefined) {
+
+// Defaults
+var defaults = {
+  persistent: false,
+  repeat: false,
+  placeholder: ' '
+};
+
+// Regexs for input validation
+var inptRegs = {
+  '9': new RegExp('[0-9]'),
+  'a': new RegExp('[A-Za-z]'),
+  '*': new RegExp('[A-Za-z0-9]')
+};
+
+//
+// Class Constructor - Called with new Formatter(el, opts)
+// Responsible for setting up required instance variables, and
+// attaching the event listener to the element.
+//
+function Formatter(el, opts) {
+  // Cache this
+  var self = this;
+
+  // Make sure we have an element. Make accesible to instance
+  self.el = el;
+  if (!self.el) { 
+    throw new TypeError('Must provide an existing element');
+  }
+
+  // Merge opts with defaults
+  self.opts = utils.extend({}, defaults, opts);
+
+  // Make sure we have valid opts
+  if (typeof self.opts.pattern === 'undefined') {
+    throw new TypeError('Must provide a pattern');
+  }
+
+  // Get info about the given pattern
+  var parsed   = pattern.parse(self.opts.pattern);
+  self.mLength = parsed.mLength;
+  self.chars   = parsed.chars;
+  self.inpts   = parsed.inpts;
+
+  // Init values
+  self.hldrs = {};
+  self.focus = 0;
+
+  // Add Listeners
+  utils.addListener(self.el, 'keydown', function (evt) {
+    self._keyDown(evt);
+  });
+  utils.addListener(self.el, 'keypress', function (evt) {
+    self._keyPress(evt);
+  });
+  utils.addListener(self.el, 'paste', function (evt) {
+    self._paste(evt);
+  });
+
+  // Persistence
+  if (self.opts.persistent) {
+    // Format on start
+    self._processKey(null, true);
+    self.el.blur();
+
+    // Add Listeners
+    utils.addListener(self.el, 'focus', function (evt) {
+      self._focus(evt);
+    });
+    utils.addListener(self.el, 'click', function (evt) {
+      self._focus(evt);
+    });
+    utils.addListener(self.el, 'touchstart', function (evt) {
+      self._focus(evt);
+    });
+  }
+}
+
+//
+// @private
+// Handler called on all keyDown strokes. All keys trigger
+// this handler. Only process delete keys.
+//
+Formatter.prototype._keyDown = function (evt) {
+  // The first thing we need is the character code
+  var k = evt.which || evt.keyCode;
+
+  // If delete key
+  if (k && utils.isDelKey(k)) {
+    // Process the keyCode and prevent default
+    this._processKey(null, true);
+    return utils.preventDefault(evt);
+  }
+};
+
+//
+// @private
+// Handler called on all keyPress strokes. Only processes
+// character keys (as long as no modifier key is in use).
+//
+Formatter.prototype._keyPress = function (evt) {
+  // The first thing we need is the character code
+  var k = evt.which || evt.keyCode;
+
+  // Process the keyCode and prevent default
+  if (!utils.isDelKey(k) && !utils.isModifier(evt)) {
+    this._processKey(String.fromCharCode(k), false);
+    return utils.preventDefault(evt);
+  }
+};
+
+//
+// @private
+// Handler called on paste event.
+//
+Formatter.prototype._paste = function (evt) {
+  // Process the clipboard paste and prevent default
+  this._processKey(utils.getClip(evt), false);
+  return utils.preventDefault(evt);
+};
+
+//
+// @private
+// Handle called on focus event.
+//
+Formatter.prototype._focus = function (evt) {
+  // Wrapped in timeout so that we can grab input selection
+  var self = this;
+  setTimeout(function () {
+    // Grab selection
+    var selection = inptSel.get(self.el);
+    // Char check
+    var isAfterStart = selection.end > self.focus;
+        isFirstChar  = selection.end === 0;
+    // If clicked in front of start, refocus to start
+    if (isAfterStart || isFirstChar) {
+      inptSel.set(self.el, self.focus);
+    }
+  }, 0);
+};
+
+//
+// @private
+// Using the provided key information, alter el value.
+//
+Formatter.prototype._processKey = function (chars, isDelKey) {
+  // Get current state
+  this.sel = inptSel.get(this.el);
+  this.val = this.el.value;
+
+  // Init values
+  this.delta = 0;
+
+  // If chars were highlighted, we need to remove them
+  if (this.sel.begin !== this.sel.end) {
+    this.delta = (-1) * Math.abs(this.sel.begin - this.sel.end);
+    this.val   = utils.removeChars(this.val, this.sel.begin, this.sel.end);
+
+  // If pressed key is a del key, and the caret pos is not at start,
+  // remove at caret pos
+  } else if (isDelKey && this.sel.begin - 1 >= 0) {
+    this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
+    this.delta = -1;
+  }
+
+  // If the key is not a del key, it should convert to a str
+  if (!isDelKey) {
+    // Add char at position and increment delta
+    this.val = utils.addChars(this.val, chars, this.sel.begin);
+    this.delta += chars.length;
+  }
+
+  // Format el.value (also handles updating caret position)
+  this._formatValue();
+};
+
+//
+// @private
+// Alter element value to display characters matching the provided
+// instance pattern. Also responsible for updatin
+//
+Formatter.prototype._formatValue = function () {
+  // Set caret pos
+  this.curPos = this.sel.end;
+  this.newPos = this.curPos + this.delta;
+
+  // Remove all formatted chars from val
+  this._removeChars();
+  // Validate inpts
+  this._validateInpts();
+  // Add formatted characters
+  this._addChars();
+
+  // Set vakye and adhere to maxLength 
+  this.el.value = this.val.substr(0, this.mLength);
+
+  // Set new caret position
+  inptSel.set(this.el, this.newPos);
+};
+
+//
+// @private
+// Remove all formatted before and after a specified pos
+//
+Formatter.prototype._removeChars = function () {
+  // Delta shouldn't include placeholders
+  if (this.sel.end > this.focus) {
+    this.delta += this.sel.end - this.focus;
+  }
+
+  // Account for shifts during removal
+  var shift = 0;
+
+  // Loop through all possible char positions
+  for (var i = 0; i <= this.mLength; i++) {
+    // Get transformed position
+    var curChar = this.chars[i],
+        curHldr = this.hldrs[i],
+        pos = i + shift,
+        val;
+
+    // If after selection we need to account for delta
+    pos = (i >= this.sel.begin) ? pos + this.delta : pos;
+    val = this.val[pos];
+    // Remove char and account for shift
+    if (curChar && curChar == val || curHldr && curHldr == val) {
+      this.val = utils.removeChars(this.val, pos, pos + 1);
+      shift--;
+    }
+  }
+
+  // All hldrs should be removed now
+  this.hldrs = {};
+
+  // Set focus to last character
+  this.focus = this.val.length;
+};
+
+//
+// @private
+// Make sure all inpts are valid, else remove and update delta
+//
+Formatter.prototype._validateInpts = function () {
+  // Loop over each char and validate
+  for (var i = 0; i < this.val.length; i++) {
+    // 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[i])) {
+      // Check bounds
+      if (this.inpts[i]) {
+        this.val = utils.removeChars(this.val, i, i + 1);
+        this.focusStart--;
+        this.newPos--;
+        this.delta--;
+        i--;
+      }
+    }
+  }
+};
+
+//
+// @private
+// Loop over val and add formatted chars as necessary
+//
+Formatter.prototype._addChars = function () {
+  if (this.opts.persistent) { 
+    // Loop over all possible characters
+    for (var i = 0; i <= this.mLength; i++) {
+      if (!this.val[i]) {
+        // Add placeholder at pos
+        this.val = utils.addChars(this.val, this.opts.placeholder, i);
+        this.hldrs[i] = this.opts.placeholder;
+      }
+      this._addChar(i);
+    }
+
+    // Adjust focus to make sure its not on a formatted char
+    while (this.chars[this.focus]) {
+      this.focus++;
+    }
+  } else {
+    // Avoid caching val.length, as it changes during manipulations
+    for (var j = 0; j <= this.val.length; j++) {
+      this._addChar(j);
+    }
+  }
+};
+
+//
+// @private
+// Add formattted char at position
+//
+Formatter.prototype._addChar = function (i) {
+  // If char exists at position
+  var char = this.chars[i];
+  if (!char) { return true; }
+
+  // If chars are added in between the old pos and new pos
+  // we need to increment pos and delta
+  if (utils.isBetween(i, [this.sel.begin -1, this.newPos +1])) {
+    this.newPos ++;
+    this.delta ++;
+  }
+
+  // If character added before focus, incr
+  if (i <= this.focus) {
+    this.focus++;
+  }
+
+  // When moving backwards there are some race conditions where we
+  // dont want to add the character
+  if (this.delta < 0 && (this.val[i] == char )) { return true; }
+
+  // Updateholder
+  if (this.hldrs[i]) {
+    delete this.hldrs[i];
+    this.hldrs[i + 1] = this.opts.placeholder;
+  }
+
+  // Update value
+  this.val = utils.addChars(this.val, char, i);
+};
+// Define module
+var pattern = {};
+
+// Match information
+var DELIM_SIZE = 4;
+
+// Our regex used to parse
+var regexp  = new RegExp('{{([^}]+)}}', 'g');
+
+//
+// Helper method to parse pattern str
+//
+var getMatches = function (pattern) {
+  // Populate array of matches
+  var matches = [],
+      match;
+  while(match = regexp.exec(pattern)) {
+    matches.push(match);
+  }
+
+  return matches;
+};
+
+//
+// Create an object holding all formatted characters
+// with corresponding positions
+//
+pattern.parse = function (pattern) {
+  // Our obj to populate
+  var info = { inpts: {}, chars: {} };
+
+  // Pattern information
+  var matches = getMatches(pattern),
+      pLength = pattern.length;
+
+  // Counters
+  var mCount = 0,
+      iCount = 0,
+      i = 0;
+
+  // Add inpts, move to end of match, and process
+  var processMatch = function (val) {
+    var valLength = val.length;
+    for (var j = 0; j < valLength; j++) {
+      info.inpts[iCount] = val[j];
+      iCount++;
+    }
+    mCount ++;
+    i += (val.length + DELIM_SIZE - 1);
+  };
+
+  // Process match or add chars
+  for (i; i < pLength; i++) {
+    if (i == matches[mCount].index) {
+      processMatch(matches[mCount][1]);
+    } else {
+      info.chars[i - (mCount * DELIM_SIZE)] = pattern[i];
+    }
+  }
+
+  // Set mLength and return
+  info.mLength = i - (mCount * DELIM_SIZE);
+  return info;
+};
+// Define module
+var inptSel = {};
+
+//
+// Get begin and end positions of selected input. Return 0's
+// if there is no selectiion data
+//
+inptSel.get = function (el) {
+  // If normal browser return with result
+  if (typeof el.selectionStart == "number") {
+    return { 
+      begin: el.selectionStart,
+      end: el.selectionEnd
+    };
+  }
+
+  // Uh-Oh. We must be IE. Fun with TextRange!!
+  var range = document.selection.createRange();
+  // Determine if there is a selection
+  if (range && range.parentElement() == el) {
+    var inputRange = el.createTextRange(),
+        endRange   = el.createTextRange(),
+        length     = el.value.length;
+
+    // Create a working TextRange for the input selection
+    inputRange.moveToBookmark(range.getBookmark());
+
+    // Move endRange begin pos to end pos (hence endRange)
+    endRange.collapse(false);
+    
+    // If we are at the very end of the input, begin and end
+    // must both be the length of the el.value
+    if (inputRange.compareEndPoints("StartToEnd", endRange) > -1) {
+      return { begin: length, end: length };
+    }
+
+    // Note: moveStart usually returns the units moved, which 
+    // one may think is -length, however, it will stop when it
+    // gets to the begin of the range, thus giving us the
+    // negative value of the pos.
+    return {
+      begin: -inputRange.moveStart("character", -length),
+      end: -inputRange.moveEnd("character", -length)
+    };
+  }
+
+  //Return 0's on no selection data
+  return { begin: 0, end: 0 };
+};
+
+//
+// Set the caret position at a specified location
+//
+inptSel.set = function (el, pos) {
+  // If normal browser
+  if (el.setSelectionRange) {
+    el.focus();
+    el.setSelectionRange(pos,pos);
+
+  // IE = TextRange fun
+  } else if (el.createTextRange) {
+    var range = el.createTextRange();
+    range.collapse(true);
+    range.moveEnd('character', pos);
+    range.moveStart('character', pos);
+    range.select();
+  }
+};
+// Define module
+var utils = {};
+
+// Useragent info for keycode handling
+var uAgent = (typeof navigator !== 'undefined') ? navigator.userAgent : null,
+    iPhone = /iphone/i.test(uAgent);
+
+//
+// Shallow copy properties from n objects to destObj
+//
+utils.extend = function (destObj) {
+  for (var i = 1; i < arguments.length; i++) {
+    for (var key in arguments[i]) {
+      destObj[key] = arguments[i][key];
+    }
+  }
+  return destObj;
+};
+
+//
+// Add a given character to a string at a defined pos
+//
+utils.addChars = function (str, chars, pos) {
+  return str.substr(0, pos) + chars + str.substr(pos, str.length);
+};
+
+//
+// Remove a span of characters
+//
+utils.removeChars = function (str, start, end) {
+  return str.substr(0, start) + str.substr(end, str.length);
+};
+
+//
+// Return true/false is num false between bounds
+//
+utils.isBetween = function (num, bounds) {
+  bounds.sort(function(a,b) { return a-b; });
+  return (num > bounds[0] && num < bounds[1]);
+};
+
+//
+// Helper method for cross browser event listeners
+//
+utils.addListener = function (el, evt, handler) {
+  return (typeof el.addEventListener != "undefined")
+    ? el.addEventListener(evt, handler, false)
+    : el.attachEvent('on' + evt, handler);
+};
+
+//
+// Helper method for cross browser implementation of preventDefault
+//
+utils.preventDefault = function (evt) {
+  return (evt.preventDefault) ? evt.preventDefault() : (evt.returnValue = false);
+};
+
+//
+// Helper method for cross browser implementation for grabbing
+// clipboard data
+//
+utils.getClip = function (evt) {
+  if (evt.clipboardData) { return evt.clipboardData.getData('Text'); }
+  if (window.clipboardData) { return window.clipboardData.getData('Text'); }
+};
+
+//
+// Returns true/false if k is a del key
+//
+utils.isDelKey = function (k) {
+  return k === 8 || k === 46 || (iPhone && k === 127);
+};
+
+//
+// Returns true/false if modifier key is held down
+//
+utils.isModifier = function (evt) {
+  return evt.ctrlKey || evt.altKey || evt.metaKey;
+};
+// A really lightweight plugin wrapper around the constructor, 
+// preventing against multiple instantiations
+var pluginName = 'formatter';
+$.fn[pluginName] = function (options) {
+  return this.each(function () {
+    if (!$.data(this, 'plugin_' + pluginName)) {
+      $.data(this, 'plugin_' + pluginName, 
+      new Formatter(this[0], options));
+    }
+  });
+};
+
+})( jQuery, window, document);
\ No newline at end of file
diff --git a/lib/jquery.formatter.min.js b/lib/jquery.formatter.min.js
new file mode 100644
index 0000000..080392b
--- /dev/null
+++ b/lib/jquery.formatter.min.js
@@ -0,0 +1,9 @@
+/*!
+ * v0.0.3
+ * Copyright (c) 2013 First Opinion
+ * formatter.js is open sourced under the MIT license.
+ *
+ * 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(null,!0),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._keyDown=function(a){var b=a.which||a.keyCode;return b&&l.isDelKey(b)?(this._processKey(null,!0),l.preventDefault(a)):void 0},d.prototype._keyPress=function(a){var b=a.which||a.keyCode;return l.isDelKey(b)||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){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)):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._formatValue=function(){this.curPos=this.sel.end,this.newPos=this.curPos+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[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[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[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[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[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[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.isModifier=function(a){return a.ctrlKey||a.altKey||a.metaKey};var o="formatter";a.fn[o]=function(b){return this.each(function(){a.data(this,"plugin_"+o)||a.data(this,"plugin_"+o,new d(this[0],b))})}}(jQuery,window,document);
\ No newline at end of file
diff --git a/src/formatter.js b/src/formatter.js
index dc8ca41..bfaa5ef 100644
--- a/src/formatter.js
+++ b/src/formatter.js
@@ -14,9 +14,6 @@ var pattern = require('./pattern'),
 module.exports = Formatter;
 /* ExcludeEnd */
 
-// Expose to window
-if (typeof window !== 'undefined') { window.Formatter = Formatter; }
-
 // Defaults
 var defaults = {
   persistent: false,
diff --git a/src/tmpls/intro.js b/src/tmpls/intro.js
new file mode 100644
index 0000000..5e31044
--- /dev/null
+++ b/src/tmpls/intro.js
@@ -0,0 +1,4 @@
+;(function (window, document, undefined) {
+
+// Expose to window
+if (typeof window !== 'undefined') { window.Formatter = Formatter; }
diff --git a/src/tmpls/jquery.intro.js b/src/tmpls/jquery.intro.js
new file mode 100644
index 0000000..31520cd
--- /dev/null
+++ b/src/tmpls/jquery.intro.js
@@ -0,0 +1 @@
+;(function ($, window, document, undefined) {
diff --git a/src/tmpls/jquery.outro.js b/src/tmpls/jquery.outro.js
new file mode 100644
index 0000000..234201c
--- /dev/null
+++ b/src/tmpls/jquery.outro.js
@@ -0,0 +1,13 @@
+// A really lightweight plugin wrapper around the constructor, 
+// preventing against multiple instantiations
+var pluginName = 'formatter';
+$.fn[pluginName] = function (options) {
+  return this.each(function () {
+    if (!$.data(this, 'plugin_' + pluginName)) {
+      $.data(this, 'plugin_' + pluginName, 
+      new Formatter(this[0], options));
+    }
+  });
+};
+
+})( jQuery, window, document);
\ No newline at end of file
diff --git a/src/tmpls/outro.js b/src/tmpls/outro.js
new file mode 100644
index 0000000..c4a4a66
--- /dev/null
+++ b/src/tmpls/outro.js
@@ -0,0 +1,2 @@
+
+})(window, document);
\ No newline at end of file
