diff --git a/.gitignore b/.gitignore
index bfa0621..3175dd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
.idea/*
+
+node_modules/
diff --git a/Jakefile b/Jakefile
deleted file mode 100644
index 5001cb7..0000000
--- a/Jakefile
+++ /dev/null
@@ -1,31 +0,0 @@
-var Handlebars=require("handlebars"),
- fs = require("fs"),
- path = require ("path"),
- UglifyJS = require("uglify-js"),
- distPath='dist/';
-
-Handlebars.registerHelper('include', function(context) {
- return fs.readFileSync(context,'utf8');
-});
-
-function keepComment(node,comment){
- return comment.type === "comment2";
-}
-
-task('clean',function(){
- fs.rmdir(distPath)
-});
-
-task('default',['clean'], function (params) {
- fs.mkdir(distPath,0755);
-
- var options = JSON.parse(fs.readFileSync('plugin.json','utf8'))
- options.Year=new Date().getFullYear()
-
- var template = Handlebars.compile(fs.readFileSync('templates/jquery.maskedinput.template','utf8'));
- var debugFile = path.join(distPath,'jquery.maskedinput.js');
- fs.writeFileSync(debugFile,template(options));
-
- compressed = UglifyJS.minify(debugFile,{output:{comments:keepComment}});
- fs.writeFileSync(path.join(distPath,'jquery.maskedinput.min.js'), compressed.code);
-});
\ No newline at end of file
diff --git a/README.md b/README.md
index ef6f39b..c95df69 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,17 @@ If your requirements aren't met by the predefined placeholders, you can always a
By design, this plugin will reject input which doesn't complete the mask. You can bypass this by using a '?' character at the position where you would like to consider input optional. For example, a mask of "(999) 999-9999? x99999" would require only the first 10 digits of a phone number with extension being optional.
+Setting up your Developer Environment
+-------------------------------------
+jQuery Masked Input uses [NodeJS](http://www.nodejs.org) and [GruntJS](http://www.gruntjs.com) as it's developer platform and build automation tool.
+
+To get your environment setup correctly, you'll need nodejs version 0.8.25 or greater installed. You'll also need to install the grunt command line tool:
+
+ $ sudo npm install -g grunt-cli
+
+Once node is installed on your system all that you need to do is install the developer dependencies and run the grunt build:
+
+ $ npm install
+ $ grunt
+
+All of the tests for jQuery Masked Input are run using the [jasmine](http://pivotal.github.io/jasmine/) test runner.
diff --git a/dist/jquery.maskedinput.js b/dist/jquery.maskedinput.js
index 89ce020..74e2a41 100644
--- a/dist/jquery.maskedinput.js
+++ b/dist/jquery.maskedinput.js
@@ -1,338 +1,143 @@
/*
- Masked Input plugin for jQuery
- Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
- Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
- Version: 1.3.1
+ jQuery Masked Input Plugin
+ Copyright (c) 2007 - 2013 Josh Bush (digitalbush.com)
+ Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
+ Version: 1.3.1
*/
-(function($) {
- function getPasteEvent() {
- var el = document.createElement('input'),
- name = 'onpaste';
- el.setAttribute(name, '');
- return (typeof el[name] === 'function')?'paste':'input';
-}
-
-var pasteEventName = getPasteEvent() + ".mask",
- ua = navigator.userAgent,
- iPhone = /iphone/i.test(ua),
- android=/android/i.test(ua),
- caretTimeoutId;
-
-$.mask = {
- //Predefined character definitions
- definitions: {
- '9': "[0-9]",
- 'a': "[A-Za-z]",
- '*': "[A-Za-z0-9]"
- },
- dataName: "rawMaskFn",
- placeholder: '_'
-};
-
-$.fn.extend({
- //Helper Function for Caret positioning
- caret: function(begin, end) {
- var range;
-
- if (this.length === 0 || this.is(":hidden")) {
- return;
- }
-
- if (typeof begin == 'number') {
- end = (typeof end === 'number') ? end : begin;
- return this.each(function() {
- if (this.setSelectionRange) {
- this.setSelectionRange(begin, end);
- } else if (this.createTextRange) {
- range = this.createTextRange();
- range.collapse(true);
- range.moveEnd('character', end);
- range.moveStart('character', begin);
- range.select();
- }
- });
- } else {
- if (this[0].setSelectionRange) {
- begin = this[0].selectionStart;
- end = this[0].selectionEnd;
- } else if (document.selection && document.selection.createRange) {
- range = document.selection.createRange();
- begin = 0 - range.duplicate().moveStart('character', -100000);
- end = begin + range.text.length;
- }
- return { begin: begin, end: end };
- }
- },
- unmask: function() {
- return this.trigger("unmask");
- },
- mask: function(mask, settings) {
- var input,
- defs,
- tests,
- partialPosition,
- firstNonMaskPos,
- len;
-
- if (!mask && this.length > 0) {
- input = $(this[0]);
- return input.data($.mask.dataName)();
- }
- settings = $.extend({
- placeholder: $.mask.placeholder, // Load default placeholder
- completed: null
- }, settings);
-
-
- defs = $.mask.definitions;
- tests = [];
- partialPosition = len = mask.length;
- firstNonMaskPos = null;
-
- $.each(mask.split(""), function(i, c) {
- if (c == '?') {
- len--;
- partialPosition = i;
- } else if (defs[c]) {
- tests.push(new RegExp(defs[c]));
- if (firstNonMaskPos === null) {
- firstNonMaskPos = tests.length - 1;
- }
- } else {
- tests.push(null);
- }
- });
-
- return this.trigger("unmask").each(function() {
- var input = $(this),
- buffer = $.map(
- mask.split(""),
- function(c, i) {
- if (c != '?') {
- return defs[c] ? settings.placeholder : c;
- }
- }),
- focusText = input.val();
-
- function seekNext(pos) {
- while (++pos < len && !tests[pos]);
- return pos;
- }
-
- function seekPrev(pos) {
- while (--pos >= 0 && !tests[pos]);
- return pos;
- }
-
- function shiftL(begin,end) {
- var i,
- j;
-
- if (begin<0) {
- return;
- }
-
- for (i = begin, j = seekNext(end); i < len; i++) {
- if (tests[i]) {
- if (j < len && tests[i].test(buffer[j])) {
- buffer[i] = buffer[j];
- buffer[j] = settings.placeholder;
- } else {
- break;
- }
-
- j = seekNext(j);
- }
- }
- writeBuffer();
- input.caret(Math.max(firstNonMaskPos, begin));
- }
-
- function shiftR(pos) {
- var i,
- c,
- j,
- t;
-
- for (i = pos, c = settings.placeholder; i < len; i++) {
- if (tests[i]) {
- j = seekNext(i);
- t = buffer[i];
- buffer[i] = c;
- if (j < len && tests[j].test(t)) {
- c = t;
- } else {
- break;
- }
- }
- }
- }
-
- function keydownEvent(e) {
- var k = e.which,
- pos,
- begin,
- end;
-
- //backspace, delete, and escape get special treatment
- if (k === 8 || k === 46 || (iPhone && k === 127)) {
- pos = input.caret();
- begin = pos.begin;
- end = pos.end;
-
- if (end - begin === 0) {
- begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
- end=k===46?seekNext(end):end;
- }
- clearBuffer(begin, end);
- shiftL(begin, end - 1);
-
- e.preventDefault();
- } else if (k == 27) {//escape
- input.val(focusText);
- input.caret(0, checkVal());
- e.preventDefault();
- }
- }
-
- function keypressEvent(e) {
- var k = e.which,
- pos = input.caret(),
- p,
- c,
- next;
-
- if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
- return;
- } else if (k) {
- if (pos.end - pos.begin !== 0){
- clearBuffer(pos.begin, pos.end);
- shiftL(pos.begin, pos.end-1);
- }
-
- p = seekNext(pos.begin - 1);
- if (p < len) {
- c = String.fromCharCode(k);
- if (tests[p].test(c)) {
- shiftR(p);
-
- buffer[p] = c;
- writeBuffer();
- next = seekNext(p);
-
- if(android){
- setTimeout($.proxy($.fn.caret,input,next),0);
- }else{
- input.caret(next);
- }
-
- if (settings.completed && next >= len) {
- settings.completed.call(input);
- }
- }
- }
- e.preventDefault();
- }
- }
-
- function clearBuffer(start, end) {
- var i;
- for (i = start; i < end && i < len; i++) {
- if (tests[i]) {
- buffer[i] = settings.placeholder;
- }
- }
- }
-
- function writeBuffer() { input.val(buffer.join('')); }
-
- function checkVal(allow) {
- //try to place characters where they belong
- var test = input.val(),
- lastMatch = -1,
- i,
- c;
-
- for (i = 0, pos = 0; i < len; i++) {
- if (tests[i]) {
- buffer[i] = settings.placeholder;
- while (pos++ < test.length) {
- c = test.charAt(pos - 1);
- if (tests[i].test(c)) {
- buffer[i] = c;
- lastMatch = i;
- break;
- }
- }
- if (pos > test.length) {
- break;
- }
- } else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
- pos++;
- lastMatch = i;
- }
- }
- if (allow) {
- writeBuffer();
- } else if (lastMatch + 1 < partialPosition) {
- input.val("");
- clearBuffer(0, len);
- } else {
- writeBuffer();
- input.val(input.val().substring(0, lastMatch + 1));
- }
- return (partialPosition ? i : firstNonMaskPos);
- }
-
- input.data($.mask.dataName,function(){
- return $.map(buffer, function(c, i) {
- return tests[i]&&c!=settings.placeholder ? c : null;
- }).join('');
- });
-
- if (!input.attr("readonly"))
- input
- .one("unmask", function() {
- input
- .unbind(".mask")
- .removeData($.mask.dataName);
- })
- .bind("focus.mask", function() {
- clearTimeout(caretTimeoutId);
- var pos,
- moveCaret;
-
- focusText = input.val();
- pos = checkVal();
-
- caretTimeoutId = setTimeout(function(){
- writeBuffer();
- if (pos == mask.length) {
- input.caret(0, pos);
- } else {
- input.caret(pos);
- }
- }, 10);
- })
- .bind("blur.mask", function() {
- checkVal();
- if (input.val() != focusText)
- input.change();
- })
- .bind("keydown.mask", keydownEvent)
- .bind("keypress.mask", keypressEvent)
- .bind(pasteEventName, function() {
- setTimeout(function() {
- var pos=checkVal(true);
- input.caret(pos);
- if (settings.completed && pos == input.val().length)
- settings.completed.call(input);
- }, 0);
- });
- checkVal(); //Perform initial check for existing values
- });
- }
-});
-
-
-})(jQuery);
+!function($) {
+ function getPasteEvent() {
+ var el = document.createElement("input"), name = "onpaste";
+ return el.setAttribute(name, ""), "function" == typeof el[name] ? "paste" : "input";
+ }
+ var caretTimeoutId, pasteEventName = getPasteEvent() + ".mask", ua = navigator.userAgent, iPhone = /iphone/i.test(ua), chrome = /chrome/i.test(ua), android = /android/i.test(ua);
+ $.mask = {
+ definitions: {
+ "9": "[0-9]",
+ a: "[A-Za-z]",
+ "*": "[A-Za-z0-9]"
+ },
+ dataName: "rawMaskFn",
+ placeholder: "_"
+ }, $.fn.extend({
+ caret: function(begin, end) {
+ var range;
+ if (0 !== this.length && !this.is(":hidden")) return "number" == typeof begin ? (end = "number" == typeof end ? end : begin,
+ this.each(function() {
+ this.setSelectionRange ? this.setSelectionRange(begin, end) : this.createTextRange && (range = this.createTextRange(),
+ range.collapse(!0), range.moveEnd("character", end), range.moveStart("character", begin),
+ range.select());
+ })) : (this[0].setSelectionRange ? (begin = this[0].selectionStart, end = this[0].selectionEnd) : document.selection && document.selection.createRange && (range = document.selection.createRange(),
+ begin = 0 - range.duplicate().moveStart("character", -1e5), end = begin + range.text.length),
+ {
+ begin: begin,
+ end: end
+ });
+ },
+ unmask: function() {
+ return this.trigger("unmask");
+ },
+ mask: function(mask, settings) {
+ var input, defs, tests, partialPosition, firstNonMaskPos, len;
+ return !mask && this.length > 0 ? (input = $(this[0]), input.data($.mask.dataName)()) : (settings = $.extend({
+ placeholder: $.mask.placeholder,
+ completed: null
+ }, settings), defs = $.mask.definitions, tests = [], partialPosition = len = mask.length,
+ firstNonMaskPos = null, $.each(mask.split(""), function(i, c) {
+ "?" == c ? (len--, partialPosition = i) : defs[c] ? (tests.push(new RegExp(defs[c])),
+ null === firstNonMaskPos && (firstNonMaskPos = tests.length - 1)) : tests.push(null);
+ }), this.trigger("unmask").each(function() {
+ function seekNext(pos) {
+ for (;++pos < len && !tests[pos]; ) ;
+ return pos;
+ }
+ function seekPrev(pos) {
+ for (;--pos >= 0 && !tests[pos]; ) ;
+ return pos;
+ }
+ function shiftL(begin, end) {
+ var i, j;
+ if (!(0 > begin)) {
+ for (i = begin, j = seekNext(end); len > i; i++) if (tests[i]) {
+ if (!(len > j && tests[i].test(buffer[j]))) break;
+ buffer[i] = buffer[j], buffer[j] = settings.placeholder, j = seekNext(j);
+ }
+ writeBuffer(), input.caret(Math.max(firstNonMaskPos, begin));
+ }
+ }
+ function shiftR(pos) {
+ var i, c, j, t;
+ for (i = pos, c = settings.placeholder; len > i; i++) if (tests[i]) {
+ if (j = seekNext(i), t = buffer[i], buffer[i] = c, !(len > j && tests[j].test(t))) break;
+ c = t;
+ }
+ }
+ function keydownEvent(e) {
+ var pos, begin, end, k = e.which;
+ 8 === k || 46 === k || iPhone && 127 === k ? (pos = input.caret(), begin = pos.begin,
+ end = pos.end, 0 === end - begin && (begin = 46 !== k ? seekPrev(begin) : end = seekNext(begin - 1),
+ end = 46 === k ? seekNext(end) : end), clearBuffer(begin, end), shiftL(begin, end - 1),
+ e.preventDefault()) : 27 == k && (input.val(focusText), input.caret(0, checkVal()),
+ e.preventDefault());
+ }
+ function keypressEvent(e) {
+ var p, c, next, k = e.which, pos = input.caret();
+ if (0 == k) {
+ if (pos.begin >= len) return input.val(input.val().substr(0, len)), e.preventDefault(),
+ !1;
+ pos.begin == pos.end && (k = input.val().charCodeAt(pos.begin - 1), pos.begin--,
+ pos.end--);
+ }
+ e.ctrlKey || e.altKey || e.metaKey || 32 > k || k && (0 !== pos.end - pos.begin && (clearBuffer(pos.begin, pos.end),
+ shiftL(pos.begin, pos.end - 1)), p = seekNext(pos.begin - 1), len > p && (c = String.fromCharCode(k),
+ tests[p].test(c) && (shiftR(p), buffer[p] = c, writeBuffer(), next = seekNext(p),
+ android ? setTimeout($.proxy($.fn.caret, input, next), 0) : input.caret(next), settings.completed && next >= len && settings.completed.call(input))),
+ e.preventDefault());
+ }
+ function clearBuffer(start, end) {
+ var i;
+ for (i = start; end > i && len > i; i++) tests[i] && (buffer[i] = settings.placeholder);
+ }
+ function writeBuffer() {
+ input.val(buffer.join(""));
+ }
+ function checkVal(allow) {
+ var i, c, pos, test = input.val(), lastMatch = -1;
+ for (i = 0, pos = 0; len > i; i++) if (tests[i]) {
+ for (buffer[i] = settings.placeholder; pos++ < test.length; ) if (c = test.charAt(pos - 1),
+ tests[i].test(c)) {
+ buffer[i] = c, lastMatch = i;
+ break;
+ }
+ if (pos > test.length) break;
+ } else buffer[i] === test.charAt(pos) && i !== partialPosition && (pos++, lastMatch = i);
+ return allow ? writeBuffer() : partialPosition > lastMatch + 1 ? (input.val(""),
+ clearBuffer(0, len)) : (writeBuffer(), input.val(input.val().substring(0, lastMatch + 1))),
+ partialPosition ? i : firstNonMaskPos;
+ }
+ var input = $(this), buffer = $.map(mask.split(""), function(c) {
+ return "?" != c ? defs[c] ? settings.placeholder : c : void 0;
+ }), focusText = input.val();
+ input.data($.mask.dataName, function() {
+ return $.map(buffer, function(c, i) {
+ return tests[i] && c != settings.placeholder ? c : null;
+ }).join("");
+ }), input.attr("readonly") || input.one("unmask", function() {
+ input.unbind(".mask").removeData($.mask.dataName);
+ }).bind("focus.mask", function() {
+ clearTimeout(caretTimeoutId);
+ var pos;
+ focusText = input.val(), pos = checkVal(), caretTimeoutId = setTimeout(function() {
+ writeBuffer(), pos == mask.length ? input.caret(0, pos) : input.caret(pos);
+ }, 10);
+ }).bind("blur.mask", function() {
+ checkVal(), input.val() != focusText && input.change();
+ }).bind("keydown.mask", keydownEvent).bind("keypress.mask", keypressEvent).bind(pasteEventName, function() {
+ setTimeout(function() {
+ var pos = checkVal(!0);
+ input.caret(pos), settings.completed && pos == input.val().length && settings.completed.call(input);
+ }, 0);
+ }), chrome && android && input.bind("keyup.mask", keypressEvent), checkVal();
+ }));
+ }
+ });
+}(jQuery);
\ No newline at end of file
diff --git a/dist/jquery.maskedinput.min.js b/dist/jquery.maskedinput.min.js
index 0d9ce6e..683f0e1 100644
--- a/dist/jquery.maskedinput.min.js
+++ b/dist/jquery.maskedinput.min.js
@@ -1,7 +1,7 @@
/*
- Masked Input plugin for jQuery
- Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
- Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
- Version: 1.3.1
+ jQuery Masked Input Plugin
+ Copyright (c) 2007 - 2013 Josh Bush (digitalbush.com)
+ Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
+ Version: 1.3.1
*/
-(function(e){function t(){var e=document.createElement("input"),t="onpaste";return e.setAttribute(t,""),"function"==typeof e[t]?"paste":"input"}var n,a=t()+".mask",r=navigator.userAgent,i=/iphone/i.test(r),o=/android/i.test(r);e.mask={definitions:{9:"[0-9]",a:"[A-Za-z]","*":"[A-Za-z0-9]"},dataName:"rawMaskFn",placeholder:"_"},e.fn.extend({caret:function(e,t){var n;if(0!==this.length&&!this.is(":hidden"))return"number"==typeof e?(t="number"==typeof t?t:e,this.each(function(){this.setSelectionRange?this.setSelectionRange(e,t):this.createTextRange&&(n=this.createTextRange(),n.collapse(!0),n.moveEnd("character",t),n.moveStart("character",e),n.select())})):(this[0].setSelectionRange?(e=this[0].selectionStart,t=this[0].selectionEnd):document.selection&&document.selection.createRange&&(n=document.selection.createRange(),e=0-n.duplicate().moveStart("character",-1e5),t=e+n.text.length),{begin:e,end:t})},unmask:function(){return this.trigger("unmask")},mask:function(t,r){var c,l,s,u,f,h;return!t&&this.length>0?(c=e(this[0]),c.data(e.mask.dataName)()):(r=e.extend({placeholder:e.mask.placeholder,completed:null},r),l=e.mask.definitions,s=[],u=h=t.length,f=null,e.each(t.split(""),function(e,t){"?"==t?(h--,u=e):l[t]?(s.push(RegExp(l[t])),null===f&&(f=s.length-1)):s.push(null)}),this.trigger("unmask").each(function(){function c(e){for(;h>++e&&!s[e];);return e}function d(e){for(;--e>=0&&!s[e];);return e}function m(e,t){var n,a;if(!(0>e)){for(n=e,a=c(t);h>n;n++)if(s[n]){if(!(h>a&&s[n].test(R[a])))break;R[n]=R[a],R[a]=r.placeholder,a=c(a)}b(),x.caret(Math.max(f,e))}}function p(e){var t,n,a,i;for(t=e,n=r.placeholder;h>t;t++)if(s[t]){if(a=c(t),i=R[t],R[t]=n,!(h>a&&s[a].test(i)))break;n=i}}function g(e){var t,n,a,r=e.which;8===r||46===r||i&&127===r?(t=x.caret(),n=t.begin,a=t.end,0===a-n&&(n=46!==r?d(n):a=c(n-1),a=46===r?c(a):a),k(n,a),m(n,a-1),e.preventDefault()):27==r&&(x.val(S),x.caret(0,y()),e.preventDefault())}function v(t){var n,a,i,l=t.which,u=x.caret();t.ctrlKey||t.altKey||t.metaKey||32>l||l&&(0!==u.end-u.begin&&(k(u.begin,u.end),m(u.begin,u.end-1)),n=c(u.begin-1),h>n&&(a=String.fromCharCode(l),s[n].test(a)&&(p(n),R[n]=a,b(),i=c(n),o?setTimeout(e.proxy(e.fn.caret,x,i),0):x.caret(i),r.completed&&i>=h&&r.completed.call(x))),t.preventDefault())}function k(e,t){var n;for(n=e;t>n&&h>n;n++)s[n]&&(R[n]=r.placeholder)}function b(){x.val(R.join(""))}function y(e){var t,n,a=x.val(),i=-1;for(t=0,pos=0;h>t;t++)if(s[t]){for(R[t]=r.placeholder;pos++