From 4fdeff9ab874e148f0e74dc5dc0c6318ed3feda1 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Tue, 18 Mar 2014 22:48:43 -0500
Subject: [PATCH 01/18] Removing caret from jQuery namespace.
This could be more concise later, just wanted it out now.
---
gruntfile.js | 2 +-
spec/lib/setup.js | 33 ++++++++++++++++
src/jquery.maskedinput.js | 82 +++++++++++++++++++--------------------
3 files changed, 73 insertions(+), 44 deletions(-)
diff --git a/gruntfile.js b/gruntfile.js
index 5896f86..b54a2b3 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -35,10 +35,10 @@ module.exports = function( grunt ) {
options: {
specs: "spec/*[S|s]pec.js",
vendor: [
+ "lib/jquery-1.9.0.min.js",
"spec/lib/matchers.js",
"spec/lib/jasmine-species/jasmine-grammar.js",
"spec/lib/setup.js",
- "lib/jquery-1.9.0.min.js",
"spec/lib/jquery.keymasher.js"
]
}
diff --git a/spec/lib/setup.js b/spec/lib/setup.js
index 9539215..14d79fb 100644
--- a/spec/lib/setup.js
+++ b/spec/lib/setup.js
@@ -9,6 +9,39 @@ function importGrammar(g){
importGrammar(jasmine.grammar.FeatureStory);
importGrammar(jasmine.grammar.GWT);
+
+$.fn.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 };
+ }
+ };
var input;
beforeEach(function(){ input = $("").appendTo("body").focus(); });
afterEach(function(){ input.remove();});
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index cd226c8..c6b3a7a 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -1,5 +1,32 @@
(function($) {
+ function setCaret(elm,begin,end){
+ end = (typeof end === 'number') ? end : begin;
+
+ if (elm.setSelectionRange) {
+ elm.setSelectionRange(begin, end);
+ } else if (elm.createTextRange) {
+ var range = elm.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', end);
+ range.moveStart('character', begin);
+ range.select();
+ }
+ }
+
+ function getCaret(elm){
+ var begin,end;
+ if (elm.setSelectionRange) {
+ begin = elm.selectionStart;
+ end = elm.selectionEnd;
+ } else if (document.selection && document.selection.createRange) {
+ var range = document.selection.createRange();
+ begin = 0 - range.duplicate().moveStart('character', -100000);
+ end = begin + range.text.length;
+ }
+ return { begin: begin, end: end };
+ }
+
function getPasteEvent() {
var el = document.createElement('input'),
name = 'onpaste';
@@ -27,39 +54,6 @@ $.mask = {
};
$.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");
},
@@ -102,7 +96,8 @@ $.fn.extend({
});
return this.trigger("unmask").each(function() {
- var input = $(this),
+ var elm = this,
+ input = $(this),
buffer = $.map(
mask.split(""),
function(c, i) {
@@ -144,7 +139,7 @@ $.fn.extend({
}
}
writeBuffer();
- input.caret(Math.max(firstNonMaskPos, begin));
+ setCaret(elm,Math.max(firstNonMaskPos, begin))
}
function shiftR(pos) {
@@ -182,7 +177,7 @@ $.fn.extend({
//backspace, delete, and escape get special treatment
if (k === 8 || k === 46 || (iPhone && k === 127)) {
- pos = input.caret();
+ pos = getCaret(elm);
begin = pos.begin;
end = pos.end;
@@ -198,14 +193,14 @@ $.fn.extend({
blurEvent.call(this, e);
} else if (k === 27) { // escape
input.val(focusText);
- input.caret(0, checkVal());
+ setCaret(elm,0,checkVal());
e.preventDefault();
}
}
function keypressEvent(e) {
var k = e.which,
- pos = input.caret(),
+ pos = getCaret(elm),
p,
c,
next;
@@ -249,12 +244,13 @@ $.fn.extend({
//Path for CSP Violation on FireFox OS 1.1
var proxy = function()
{
- $.proxy($.fn.caret,input,next);
+
+ $.proxy(setCaret,elm,next);
}
setTimeout(proxy,0);
}else{
- input.caret(next);
+ setCaret(elm,next);
}
if (settings.completed && next >= len) {
@@ -348,9 +344,9 @@ $.fn.extend({
caretTimeoutId = setTimeout(function(){
writeBuffer();
if (pos == mask.replace("?","").length) {
- input.caret(0, pos);
+ setCaret(elm,0,pos);
} else {
- input.caret(pos);
+ setCaret(elm,pos);
}
}, 10);
})
@@ -360,7 +356,7 @@ $.fn.extend({
.on(pasteEventName, function() {
setTimeout(function() {
var pos=checkVal(true);
- input.caret(pos);
+ setCaret(elm,pos);
if (settings.completed && pos == input.val().length)
settings.completed.call(input);
}, 0);
From 990de2eecd5a38788333aecc8b65efc70773088c Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Thu, 20 Mar 2014 22:41:07 -0500
Subject: [PATCH 02/18] Prying apart masking logic from DOM manipulation.
Still rough with 18 test failures.
---
src/jquery.maskedinput.js | 622 ++++++++++++++++++--------------------
1 file changed, 288 insertions(+), 334 deletions(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index c6b3a7a..74d3736 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -27,345 +27,299 @@
return { begin: begin, end: end };
}
-function getPasteEvent() {
- var el = document.createElement('input'),
+ $.mask = {
+ masks:[],
+ //Predefined character definitions
+ //TODO: Move these to the mask def itself.
+ definitions: {
+ '9': "[0-9]",
+ 'a': "[A-Za-z]",
+ '*': "[A-Za-z0-9]"
+ },
+ autoclear: true,
+ dataName: "maskedinput",
+ placeholder: '_'
+ };
+
+ function FixedWidthMask (mask, settings){
+ var self=this;
+ //Build up structures necessary to quickly apply masking
+ this.settings = settings;
+
+ this.tests = [];
+ this.partialPosition = mask.length;
+ this.length = mask.length;
+
+ var firstNonMaskPos = null;
+
+ $.each(mask.split(""), function(i, c) {
+ if (c == '?') {
+ self.length--;
+ self.partialPosition = i;
+ } else if (settings.definitions[c]) {
+ self.tests.push(new RegExp(settings.definitions[c]));
+ if (firstNonMaskPos === null) {
+ firstNonMaskPos = self.tests.length - 1;
+ }
+ } else {
+ self.tests.push(c);
+ }
+ });
+ }
+
+ FixedWidthMask.prototype.applyBackspace = function(input, pos){
+ var i, buffer = input.split('');
+ for(i = pos - 1; i >= 0; i--){
+ if(this.tests[i].test)
+ break
+ }
+ buffer.splice(i, 1);
+ return this.apply(buffer.join(''), i);
+ }
+
+ FixedWidthMask.prototype.applyDelete = function(input, pos){
+ var i, buffer = input.split('');
+ for(i = pos; i < buffer.length; i++){
+ if(this.tests[i].test)
+ break
+ }
+ buffer.splice(i, 1);
+ var result=this.apply(buffer.join(''), i);
+ result.pos=i;
+ return result;
+ }
+
+ FixedWidthMask.prototype.apply = function(input, caretPosition){
+ if(caretPosition == null)
+ caretPosition = 0;
+ var buffer=[],
+ raw=[],
+ lastMatch = -1,
+ i,
+ action,
+ pos;
+
+ for (i = 0, pos = 0; i < this.length; i++) {
+ var action=this.tests[i];
+
+ if (action.test) {
+ buffer.push(this.settings.placeholder);
+
+ while (pos++ < input.length) {
+ c = input.charAt(pos - 1);
+ if (action.test(c)) {
+ buffer[i] = c;
+ raw.push(c)
+ lastMatch = i;
+ break;
+ }
+ }
+ } else {
+ buffer.push(action);
+ if(action === input.charAt(pos) && i !== this.partialPosition) {
+ pos++;
+ lastMatch=i;
+ }
+ }
+ }
+
+ //Find the next spot waiting for input
+ var maxCaret=Math.min(caretPosition,this.length);
+ for(i=Math.max(lastMatch+1,maxCaret);i= this.partialPosition
+ };
+ console.log(result);
+ return result;
+ }
+
+ 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),
- chrome = /chrome/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]"
- },
- autoclear: true,
- dataName: "rawMaskFn",
- placeholder: '_'
-};
-
-$.fn.extend({
- 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({
- autoclear: $.mask.autoclear,
- 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 elm = this,
+ el.setAttribute(name, '');
+ return (typeof el[name] === 'function')?'paste':'input';
+ }
+
+ var pasteEventName = getPasteEvent() + ".mask",
+ ua = navigator.userAgent,
+ iPhone = /iphone/i.test(ua),
+ chrome = /chrome/i.test(ua),
+ android=/android/i.test(ua),
+ caretTimeoutId;
+
+
+
+ $.fn.extend({
+ //TODO: Be a good citizen and get this down to only .mask()
+ unmask: function() {
+ return this.trigger("unmask");
+ },
+ //TODO: we need a conflict thing here, maybe only use maskedinput(or alias?)
+ mask: function(mask, settings) {
+ var input,
+ defs,
+ tests,
+ partialPosition,
+ firstNonMaskPos,
+ len;
+
+ //TODO: make these more in line with newer plugin interaction guidelines.
+ if (!mask && this.length > 0) {
+ input = $(this[0]);
+ return input.data($.mask.dataName).apply(input.val()).raw;
+ }
+
+ settings = $.extend({
+ definitions: $.mask.definitions,
+ autoclear: $.mask.autoclear,
+ placeholder: $.mask.placeholder,
+ completed: null
+ }, settings);
+
+ var mask=new FixedWidthMask(mask,settings);
+
+
+ return this.trigger("unmask").each(function() {
+ var elm = this,
input = $(this),
- buffer = $.map(
- mask.split(""),
- function(c, i) {
- if (c != '?') {
- return defs[c] ? settings.placeholder : c;
- }
- }),
- defaultBuffer = buffer.join(''),
- 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();
- setCaret(elm,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 blurEvent(e) {
- checkVal();
-
- if (input.val() != focusText)
- input.change();
- }
-
- 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 = getCaret(elm);
- 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 === 13 ) { // enter
- blurEvent.call(this, e);
- } else if (k === 27) { // escape
- input.val(focusText);
- setCaret(elm,0,checkVal());
- e.preventDefault();
- }
- }
-
- function keypressEvent(e) {
- var k = e.which,
- pos = getCaret(elm),
- p,
- c,
- next;
-
- if (k == 0) {
- // unable to detect key pressed. Grab it from pos and adjust
- // this is a failsafe for mobile chrome
- // which can't detect keypress events
- // reliably
- if (pos.begin >= len) {
- input.val(input.val().substr(0, len));
- e.preventDefault();
- return false;
+ focusText = elm.value;
+
+ function blurEvent(e) {
+ if(settings.autoclear){
+ var result = mask.apply(elm.value, 0);
+ if(!result.isComplete){
+ elm.value = "";
}
- if (pos.begin == pos.end) {
- k = input.val().charCodeAt(pos.begin - 1);
- pos.begin--;
- pos.end--;
+ }
+
+ if (elm.value != focusText){
+ input.change();
+ }
+ }
+
+ function keydownEvent(e) {
+ var k = e.which;
+
+ //backspace, delete, enter, and escape get special treatment
+ if (k === 8 || k === 46) {
+ var pos = getCaret(elm);
+ var result;
+
+ if(pos.begin != pos.end){
+ var buffer = elm.value.split('');
+ buffer.splice(pos.begin,pos.end - pos.begin);
+ result = mask.apply(buffer.join(''), pos.begin+1);
+ result.pos = pos.begin;
+ }else{
+ if(k==8){
+ result = mask.applyBackspace(elm.value, pos.begin);
+ }else{
+ result = mask.applyDelete(elm.value, pos.begin);
+ }
}
+
+ elm.value = result.value;
+ setCaret(elm, result.pos);
+ e.preventDefault();
+ } else if(k === 13) { // enter
+ blurEvent.call(this, e);
+ } else if (k === 27) { // escape
+ elm.value = focusText;
+ setCaret(elm, 0, focusText.length);
+ e.preventDefault();
}
+ }
- if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
- return;
- } else if ( k && k !== 13 ) {
- 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){
- //Path for CSP Violation on FireFox OS 1.1
- var proxy = function()
- {
-
- $.proxy(setCaret,elm,next);
- }
-
- setTimeout(proxy,0);
- }else{
- setCaret(elm,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,
- pos;
-
- 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) {
- if (settings.autoclear || buffer.join('') === defaultBuffer) {
- // Invalid value. Remove it and replace it with the
- // mask, which is the default behavior.
- input.val("");
- clearBuffer(0, len);
- } else {
- // Invalid value, but we opt to show the value to the
- // user and allow them to correct their mistake.
- writeBuffer();
- }
- } 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
- .off(".mask")
- .removeData($.mask.dataName);
- })
- .on("focus.mask", function() {
- clearTimeout(caretTimeoutId);
- var pos;
-
- focusText = input.val();
-
- pos = checkVal();
-
- caretTimeoutId = setTimeout(function(){
- writeBuffer();
- if (pos == mask.replace("?","").length) {
- setCaret(elm,0,pos);
- } else {
- setCaret(elm,pos);
- }
- }, 10);
- })
- .on("blur.mask", blurEvent)
- .on("keydown.mask", keydownEvent)
- .on("keypress.mask", keypressEvent)
- .on(pasteEventName, function() {
- setTimeout(function() {
- var pos=checkVal(true);
- setCaret(elm,pos);
- if (settings.completed && pos == input.val().length)
- settings.completed.call(input);
- }, 0);
- });
- if (chrome && android) {
- input.on("keyup.mask", keypressEvent);
+ function keypressEvent(e) {
+ var k = e.which,
+ pos = getCaret(elm);
+
+ if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
+ return;
+ } else if (k && k !== 13) {
+ var buffer = elm.value.split('');
+ buffer.splice(pos.begin, pos.end - pos.begin, String.fromCharCode(k));
+ var result = mask.apply(buffer.join(''), pos.begin+1);
+
+ elm.value = result.value;
+ setCaret(elm, result.pos);
+ if(result.isComplete && settings.completed)
+ settings.completed.call(input); //TODO: Raise event instead.
+ e.preventDefault();
+
+ // if(android){
+ // //Path for CSP Violation on FireFox OS 1.1
+ // var proxy = function()
+ // {
+ //
+ // $.proxy(setCaret,elm,next);
+ // }
+ //
+ // setTimeout(proxy,0);
+ // }else{
+ // setCaret(elm,next);
+ // }
+ }
+ }
+
+ var caretTimeoutId;
+ function focusEvent(e){
+ clearTimeout(caretTimeoutId);
+ var result = mask.apply(elm.value, 0);
+ focusText = elm.value;
+
+ caretTimeoutId = setTimeout(function(){
+ elm.value = result.value;
+ if (result.isComplete) {
+ setCaret(elm, 0, result.pos);
+ } else {
+ setCaret(elm, result.pos);
+ }
+ }, 10);
}
- checkVal(); //Perform initial check for existing values
- });
- }
-});
+
+ function pasteEvent(e){
+ setTimeout(function() {
+ var pos = getCaret(elm);
+ var result = mask.apply(elm.value, pos.end);
+ elm.value = result.value;
+ setCaret(elm, result.pos);
+ if(result.isComplete && settings.completed)
+ settings.completed.call(input); //TODO: Raise event instead.
+ }, 0);
+ }
+ input.data($.mask.dataName,mask);
+
+ if (!input.attr("readonly"))
+ input
+ .one("unmask", function() {
+ input
+ .off(".mask")
+ .removeData($.mask.dataName);
+ })
+ .on("focus.mask",focusEvent)
+ .on("blur.mask", blurEvent)
+ .on("keydown.mask", keydownEvent)
+ .on("keypress.mask", keypressEvent)
+ .on(pasteEventName, pasteEvent);
+
+ // if (chrome && android) {
+ // input.on("keyup.mask", keypressEvent);
+ // }
+
+ //Apply initital mask
+ if(elm.value.length){
+ var result=mask.apply(elm.value, 0);
+ elm.value = result.value;
+ }
+ });
+ }
+ });
})(jQuery);
From 34d579fadc9da7978dabc49165d4c3526237009d Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Thu, 20 Mar 2014 23:12:53 -0500
Subject: [PATCH 03/18] Fixing issue with blur leaving placeholders for
optional values.
---
src/jquery.maskedinput.js | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 74d3736..a318c1d 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -118,7 +118,6 @@
buffer.push(action);
if(action === input.charAt(pos) && i !== this.partialPosition) {
pos++;
- lastMatch=i;
}
}
}
@@ -130,9 +129,16 @@
break
}
+ var trimmed=buffer;
+ if(this.partialPosition < this.length){
+ trimmed=buffer.slice(0, Math.max(this.partialPosition,lastMatch+1))
+ }
+
+ //TODO: better names for these props
var result={
- value: buffer.join(''),
- raw: raw.join(''), //TODO: separate unmask call?
+ value: buffer.join(''), //Prompt Value
+ trimmed: trimmed.join(''), //Display Value
+ raw: raw.join(''), //Raw Value, TODO: separate unmask call?
pos: i , //(partialPosition ? i : firstNonMaskPos)
isComplete: (lastMatch + 1) >= this.partialPosition
};
@@ -192,11 +198,10 @@
focusText = elm.value;
function blurEvent(e) {
- if(settings.autoclear){
- var result = mask.apply(elm.value, 0);
- if(!result.isComplete){
- elm.value = "";
- }
+ var result = mask.apply(elm.value, 0);
+ elm.value = result.trimmed;
+ if(settings.autoclear && !result.isComplete){
+ elm.value = "";
}
if (elm.value != focusText){
From 2fae445e0af3ab225a14522ef560f6aa3d26f670 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Thu, 20 Mar 2014 23:43:27 -0500
Subject: [PATCH 04/18] Fix backspace caret position.
---
src/jquery.maskedinput.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index a318c1d..e45a080 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -74,7 +74,9 @@
break
}
buffer.splice(i, 1);
- return this.apply(buffer.join(''), i);
+ var result= this.apply(buffer.join(''), i);
+ result.pos=i;
+ return result;
}
FixedWidthMask.prototype.applyDelete = function(input, pos){
From 36243cb2661d4d0cdfc261474dd71618dd6ac218 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Fri, 21 Mar 2014 21:41:59 -0500
Subject: [PATCH 05/18] Fixing caret position when typing in the middle of the
string.
---
src/jquery.maskedinput.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index e45a080..7db0db5 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -126,7 +126,7 @@
//Find the next spot waiting for input
var maxCaret=Math.min(caretPosition,this.length);
- for(i=Math.max(lastMatch+1,maxCaret);i
Date: Fri, 21 Mar 2014 21:52:33 -0500
Subject: [PATCH 06/18] Fixing caret position on focus.
---
src/jquery.maskedinput.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 7db0db5..f29abdd 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -93,7 +93,7 @@
FixedWidthMask.prototype.apply = function(input, caretPosition){
if(caretPosition == null)
- caretPosition = 0;
+ caretPosition = this.length;
var buffer=[],
raw=[],
lastMatch = -1,
@@ -200,7 +200,7 @@
focusText = elm.value;
function blurEvent(e) {
- var result = mask.apply(elm.value, 0);
+ var result = mask.apply(elm.value);
elm.value = result.trimmed;
if(settings.autoclear && !result.isComplete){
elm.value = "";
@@ -279,7 +279,7 @@
var caretTimeoutId;
function focusEvent(e){
clearTimeout(caretTimeoutId);
- var result = mask.apply(elm.value, 0);
+ var result = mask.apply(elm.value);
focusText = elm.value;
caretTimeoutId = setTimeout(function(){
@@ -323,7 +323,7 @@
//Apply initital mask
if(elm.value.length){
- var result=mask.apply(elm.value, 0);
+ var result=mask.apply(elm.value);
elm.value = result.value;
}
});
From 26da479cbea786e43704248b10d5354aa87ac4d6 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Fri, 21 Mar 2014 22:10:06 -0500
Subject: [PATCH 07/18] Fixed bug and adjusted specs for around autoclear =
false.
---
spec/Enter.Spec.js | 4 ++--
spec/Focus.Spec.js | 4 ++--
src/jquery.maskedinput.js | 12 ++++++++++--
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/spec/Enter.Spec.js b/spec/Enter.Spec.js
index 025e21e..636aa09 100644
--- a/spec/Enter.Spec.js
+++ b/spec/Enter.Spec.js
@@ -37,7 +37,7 @@ feature("Enter Key", function() {
input.trigger(enterKeyEvent);
});
then("value should remain visible with placeholders",function(){
- expect(input).toHaveValue("1_");
+ expect(input).toHaveValue("1");
});
});
});
@@ -63,7 +63,7 @@ feature("Enter Key", function() {
input.mashKeys("1").trigger(enterKeyEvent);
});
then("value should be empty",function(){
- expect(input).toHaveValue("1___");
+ expect(input).toHaveValue("1");
});
});
diff --git a/spec/Focus.Spec.js b/spec/Focus.Spec.js
index 9964893..91c6c25 100644
--- a/spec/Focus.Spec.js
+++ b/spec/Focus.Spec.js
@@ -123,7 +123,7 @@ feature("Leaving A Masked Input",function(){
input.blur();
});
then("value should remain visible with placeholders",function(){
- expect(input).toHaveValue("1_");
+ expect(input).toHaveValue("1");
});
});
});
@@ -149,7 +149,7 @@ feature("Optional marker",function(){
input.mashKeys("1").blur();
});
then("value should be empty",function(){
- expect(input).toHaveValue("1___");
+ expect(input).toHaveValue("1");
});
});
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index f29abdd..4da121f 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -133,7 +133,11 @@
var trimmed=buffer;
if(this.partialPosition < this.length){
- trimmed=buffer.slice(0, Math.max(this.partialPosition,lastMatch+1))
+ trimmed = buffer.slice(0, Math.max(this.partialPosition,lastMatch+1))
+ }
+
+ if(!this.settings.autoclear){
+ trimmed = buffer.slice(0,i);
}
//TODO: better names for these props
@@ -324,7 +328,11 @@
//Apply initital mask
if(elm.value.length){
var result=mask.apply(elm.value);
- elm.value = result.value;
+ if(!settings.autoclear || result.isComplete){
+ elm.value = result.value;
+ }else{
+ elm.value="";
+ }
}
});
}
From 86cff48f29d4575b51c4f1375d0b96d35e67f64f Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Sun, 23 Mar 2014 23:36:05 -0500
Subject: [PATCH 08/18] Fixed last of failing tests around shifting with some
awful code.
---
src/jquery.maskedinput.js | 49 +++++++++++++++++++++++++++++++++------
1 file changed, 42 insertions(+), 7 deletions(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 4da121f..3a12f2c 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -74,7 +74,7 @@
break
}
buffer.splice(i, 1);
- var result= this.apply(buffer.join(''), i);
+ var result= this.apply(buffer.join(''), i, true);
result.pos=i;
return result;
}
@@ -86,15 +86,17 @@
break
}
buffer.splice(i, 1);
- var result=this.apply(buffer.join(''), i);
+ var result=this.apply(buffer.join(''), i, true);
result.pos=i;
return result;
}
- FixedWidthMask.prototype.apply = function(input, caretPosition){
+ FixedWidthMask.prototype.apply = function(inputString, caretPosition, doShift){
if(caretPosition == null)
caretPosition = this.length;
- var buffer=[],
+
+ var input=inputString.split(''),
+ buffer=[],
raw=[],
lastMatch = -1,
i,
@@ -108,17 +110,50 @@
buffer.push(this.settings.placeholder);
while (pos++ < input.length) {
- c = input.charAt(pos - 1);
+ c = input[pos - 1];
if (action.test(c)) {
buffer[i] = c;
- raw.push(c)
+ raw.push(c);
lastMatch = i;
break;
+ }else if(doShift){
+ //TODO: The following is awful and needs to be refactored.
+ var tests=$.map(this.tests.slice(i + 1),function(test, offset){
+ var index = pos - 1 + offset;
+ if(test.test && input[index] != null){
+ return {regex:test,char:input[index]};
+ }
+ });
+
+ if(tests.length){
+ var newInput = [],canShift = true;
+ tests.unshift({regex: action});
+ for(var j = 1; j < tests.length; j++){
+ if(!tests[j-1].regex.test(tests[j].char)){
+ canShift = false;
+ break;
+ }
+ newInput.push(tests[j].char);
+ }
+ }
+
+ if(canShift){
+ //Everything to the right can shift left and still match.
+ input = newInput;
+ buffer[i] = input[0];
+ pos = 1;
+ }else{
+ //Retry current char at next position leaving a blank.
+ pos--;
+ }
+ //Only allow shift attempt to happen once.
+ doShift = false;
+ break;
}
}
} else {
buffer.push(action);
- if(action === input.charAt(pos) && i !== this.partialPosition) {
+ if(action === input[pos] && i !== this.partialPosition) {
pos++;
}
}
From c1b07803b5eb5e8cc3b1f68c33c6467a5ff70075 Mon Sep 17 00:00:00 2001
From: Jared Barboza
Date: Mon, 24 Mar 2014 00:53:58 -0400
Subject: [PATCH 09/18] fix global var leak in FixedWidthMask#apply
---
src/jquery.maskedinput.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 3a12f2c..011b4d6 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -110,7 +110,7 @@
buffer.push(this.settings.placeholder);
while (pos++ < input.length) {
- c = input[pos - 1];
+ var c = input[pos - 1];
if (action.test(c)) {
buffer[i] = c;
raw.push(c);
From 6f552731fdce574b81d600d70a94a86cb639c35e Mon Sep 17 00:00:00 2001
From: Jared Barboza
Date: Mon, 24 Mar 2014 00:54:54 -0400
Subject: [PATCH 10/18] change applyDelete/Backspace nested if/else to else
if/else
---
src/jquery.maskedinput.js | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 011b4d6..d0d51f8 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -258,17 +258,15 @@
var pos = getCaret(elm);
var result;
- if(pos.begin != pos.end){
+ if (pos.begin != pos.end) {
var buffer = elm.value.split('');
buffer.splice(pos.begin,pos.end - pos.begin);
result = mask.apply(buffer.join(''), pos.begin+1);
result.pos = pos.begin;
- }else{
- if(k==8){
- result = mask.applyBackspace(elm.value, pos.begin);
- }else{
- result = mask.applyDelete(elm.value, pos.begin);
- }
+ } else if( k == 8 ) {
+ result = mask.applyBackspace(elm.value, pos.begin);
+ } else {
+ result = mask.applyDelete(elm.value, pos.begin);
}
elm.value = result.value;
From 70ccc51f62f32295a9365f980834b932f727a57e Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 21:27:59 -0500
Subject: [PATCH 11/18] Adding concat and watch grunt tasks.
---
gruntfile.js | 88 +++++++++++++++++++++++++++++++---------------------
package.json | 11 ++++---
2 files changed, 58 insertions(+), 41 deletions(-)
diff --git a/gruntfile.js b/gruntfile.js
index b54a2b3..997d1fc 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -1,54 +1,70 @@
-
"use strict";
+module.exports = function(grunt) {
-module.exports = function( grunt ) {
grunt.initConfig({
- // TODO: change to read component.json
- pkg: require('./package.json'),
-
- uglify: {
- options: {
- banner: '/*\n <%= pkg.description %>\n Copyright (c) 2007 - <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)\n Version: <%= pkg.version %>\n*/\n'
- },
-
- dev: {
+ pkg: grunt.file.readJSON('package.json'),
+ banner: '/*\n'+
+ ' <%= pkg.description %>\n'+
+ ' Copyright (c) 2007 - <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n'+
+ ' Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)\n'+
+ ' Version: <%= pkg.version %>\n'+
+ '*/\n\n',
+ concat: {
options: {
- beautify: true,
- mangle: false
+ banner: '<%= banner %>',
+ stripBanners: false
},
-
- files: {
- 'dist/jquery.maskedinput.js': ['src/jquery.maskedinput.js']
+ dist: {
+ src: ['src/jquery.maskedinput.js'],
+ dest: 'dist/<%= pkg.name %>.js'
}
- },
-
- min: {
+ },
+ uglify: {
+ options: {
+ banner: '<%= banner %>'
+ },
+ dist: {
files: {
- 'dist/jquery.maskedinput.min.js': ['src/jquery.maskedinput.js']
+ 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
-
jasmine: {
- full: {
- src: "src/**/*.js",
- options: {
- specs: "spec/*[S|s]pec.js",
- vendor: [
- "lib/jquery-1.9.0.min.js",
- "spec/lib/matchers.js",
- "spec/lib/jasmine-species/jasmine-grammar.js",
- "spec/lib/setup.js",
- "spec/lib/jquery.keymasher.js"
- ]
+ full: {
+ src: '<%= concat.dist.src %>',
+ options: {
+ specs: 'spec/*[S|s]pec.js',
+ vendor: [
+ 'lib/jquery-1.9.0.min.js',
+ 'spec/lib/matchers.js',
+ 'spec/lib/jasmine-species/jasmine-grammar.js',
+ 'spec/lib/setup.js',
+ 'spec/lib/jquery.keymasher.js'
+ ]
+ }
+ }
+ },
+ jshint: {
+ files: ['gruntfile.js', '<%= concat.dist.src %>', '<%= jasmine.full.options.specs %>'],
+ options: {
+ // options here to override JSHint defaults
+ globals: {
+ jQuery: true
}
}
+ },
+ watch: {
+ files: ['<%= jshint.files %>'],
+ tasks: [/*'jshint',*/ 'jasmine']
}
});
- grunt.loadNpmTasks("grunt-contrib-jasmine");
- grunt.loadNpmTasks("grunt-contrib-uglify");
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-concat');
- grunt.registerTask('test', ['jasmine']);
- grunt.registerTask('default', ['test', 'uglify']);
+ grunt.registerTask('test', [/*'jshint',*/ 'jasmine']);
+ grunt.registerTask('default', [/*'jshint',*/ 'jasmine', 'concat', 'uglify']);
};
diff --git a/package.json b/package.json
index 2f8f240..561b68a 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,13 @@
"version": "1.3.1",
"author": "Josh Bush (digitalbush.com)",
"description": "jQuery Masked Input Plugin",
-
"devDependencies": {
- "grunt": "0.4.x",
- "grunt-contrib-jasmine": "0.5.x",
- "grunt-contrib-watch": "0.5.x",
- "grunt-contrib-uglify": "0.2.x"
+ "grunt": "~0.4.4",
+ "grunt-contrib-jasmine": "0.5.x",
+ "grunt-contrib-watch": "~0.6.1",
+ "grunt-contrib-uglify": "0.2.x",
+ "grunt-contrib-jshint": "~0.9.2",
+ "grunt-contrib-concat": "~0.3.0"
},
"scripts": {
"test": "grunt test"
From 4b0c1c50d4937b4be5f7bddc58c39313041eccac Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 22:04:58 -0500
Subject: [PATCH 12/18] Adding jshint
---
gruntfile.js | 119 ++++++++++++++++++++++++--------------------------
package.json | 2 +-
src/.jshintrc | 5 +++
3 files changed, 64 insertions(+), 62 deletions(-)
create mode 100644 src/.jshintrc
diff --git a/gruntfile.js b/gruntfile.js
index 997d1fc..c8701fb 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -1,70 +1,67 @@
-"use strict";
-module.exports = function(grunt) {
- grunt.initConfig({
- pkg: grunt.file.readJSON('package.json'),
- banner: '/*\n'+
- ' <%= pkg.description %>\n'+
- ' Copyright (c) 2007 - <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n'+
- ' Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)\n'+
- ' Version: <%= pkg.version %>\n'+
- '*/\n\n',
- concat: {
- options: {
- banner: '<%= banner %>',
- stripBanners: false
+module.exports = function(grunt) {
+ "use strict";
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ banner: '/*\n'+
+ ' <%= pkg.description %>\n'+
+ ' Copyright (c) 2007 - <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n'+
+ ' Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)\n'+
+ ' Version: <%= pkg.version %>\n'+
+ '*/\n\n',
+ concat: {
+ options: {
+ banner: '<%= banner %>',
+ stripBanners: false
+ },
+ dist: {
+ src: ['src/jquery.maskedinput.js'],
+ dest: 'dist/<%= pkg.name %>.js'
+ }
},
- dist: {
- src: ['src/jquery.maskedinput.js'],
- dest: 'dist/<%= pkg.name %>.js'
- }
- },
- uglify: {
- options: {
- banner: '<%= banner %>'
+ uglify: {
+ options: {
+ banner: '<%= banner %>'
+ },
+ dist: {
+ files: {
+ 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
+ }
+ }
},
- dist: {
- files: {
- 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
- }
- }
- },
- jasmine: {
- full: {
- src: '<%= concat.dist.src %>',
+ jasmine: {
+ full: {
+ src: '<%= concat.dist.src %>',
+ options: {
+ specs: 'spec/*[S|s]pec.js',
+ vendor: [
+ 'lib/jquery-1.9.0.min.js',
+ 'spec/lib/matchers.js',
+ 'spec/lib/jasmine-species/jasmine-grammar.js',
+ 'spec/lib/setup.js',
+ 'spec/lib/jquery.keymasher.js'
+ ]
+ }
+ }
+ },
+ jshint: {
+ files: ['gruntfile.js', '<%= concat.dist.src %>', '<%= jasmine.full.options.specs %>'],
options: {
- specs: 'spec/*[S|s]pec.js',
- vendor: [
- 'lib/jquery-1.9.0.min.js',
- 'spec/lib/matchers.js',
- 'spec/lib/jasmine-species/jasmine-grammar.js',
- 'spec/lib/setup.js',
- 'spec/lib/jquery.keymasher.js'
- ]
+ jshintrc: 'src/.jshintrc'
}
+ },
+ watch: {
+ files: ['<%= jshint.files %>'],
+ tasks: ['jshint', 'jasmine']
}
- },
- jshint: {
- files: ['gruntfile.js', '<%= concat.dist.src %>', '<%= jasmine.full.options.specs %>'],
- options: {
- // options here to override JSHint defaults
- globals: {
- jQuery: true
- }
- }
- },
- watch: {
- files: ['<%= jshint.files %>'],
- tasks: [/*'jshint',*/ 'jasmine']
- }
- });
+ });
- grunt.loadNpmTasks('grunt-contrib-uglify');
- grunt.loadNpmTasks('grunt-contrib-jshint');
- grunt.loadNpmTasks('grunt-contrib-jasmine');
- grunt.loadNpmTasks('grunt-contrib-watch');
- grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-concat');
- grunt.registerTask('test', [/*'jshint',*/ 'jasmine']);
- grunt.registerTask('default', [/*'jshint',*/ 'jasmine', 'concat', 'uglify']);
+ grunt.registerTask('test', ['jshint', 'jasmine']);
+ grunt.registerTask('default', ['jshint', 'jasmine', 'concat', 'uglify']);
};
diff --git a/package.json b/package.json
index 561b68a..7d80b3f 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"description": "jQuery Masked Input Plugin",
"devDependencies": {
"grunt": "~0.4.4",
- "grunt-contrib-jasmine": "0.5.x",
+ "grunt-contrib-jasmine": "0.5.x",
"grunt-contrib-watch": "~0.6.1",
"grunt-contrib-uglify": "0.2.x",
"grunt-contrib-jshint": "~0.9.2",
diff --git a/src/.jshintrc b/src/.jshintrc
new file mode 100644
index 0000000..b4f03e6
--- /dev/null
+++ b/src/.jshintrc
@@ -0,0 +1,5 @@
+{
+ "asi" : true,
+ "jquery" : true,
+ "eqnull" : true
+}
From 9099e814be9273d1aa443a39b9f643a979a83a78 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 22:50:33 -0500
Subject: [PATCH 13/18] Making jshint happy with current source.
---
gruntfile.js | 22 ++++++----
spec/.jshintrc | 3 ++
src/.jshintrc | 10 +++--
src/jquery.maskedinput.js | 85 ++++++++++++++++++---------------------
4 files changed, 64 insertions(+), 56 deletions(-)
create mode 100644 spec/.jshintrc
diff --git a/gruntfile.js b/gruntfile.js
index c8701fb..a507209 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -23,11 +23,11 @@ module.exports = function(grunt) {
options: {
banner: '<%= banner %>'
},
- dist: {
- files: {
- 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
+ dist: {
+ files: {
+ 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
+ }
}
- }
},
jasmine: {
full: {
@@ -45,9 +45,17 @@ module.exports = function(grunt) {
}
},
jshint: {
- files: ['gruntfile.js', '<%= concat.dist.src %>', '<%= jasmine.full.options.specs %>'],
- options: {
- jshintrc: 'src/.jshintrc'
+ src: {
+ options: {
+ jshintrc: 'src/.jshintrc'
+ },
+ src: '<%= concat.dist.src %>'
+ },
+ specs: {
+ options: {
+ jshintrc: 'spec/.jshintrc'
+ },
+ src: ['<%= jasmine.full.options.specs %>']
}
},
watch: {
diff --git a/spec/.jshintrc b/spec/.jshintrc
new file mode 100644
index 0000000..a6649b9
--- /dev/null
+++ b/spec/.jshintrc
@@ -0,0 +1,3 @@
+{
+ "asi" : true
+}
diff --git a/src/.jshintrc b/src/.jshintrc
index b4f03e6..97efc48 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -1,5 +1,9 @@
{
- "asi" : true,
- "jquery" : true,
- "eqnull" : true
+ "browser" : true,
+ "jquery" : true,
+ "eqnull" : true,
+ "newcap" : true,
+ "indent" : 4,
+ "undef" : true,
+ "unused" : true
}
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index d0d51f8..0af047e 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -70,26 +70,26 @@
FixedWidthMask.prototype.applyBackspace = function(input, pos){
var i, buffer = input.split('');
for(i = pos - 1; i >= 0; i--){
- if(this.tests[i].test)
- break
+ if(this.tests[i].test)
+ break;
}
buffer.splice(i, 1);
var result= this.apply(buffer.join(''), i, true);
result.pos=i;
return result;
- }
+ };
FixedWidthMask.prototype.applyDelete = function(input, pos){
var i, buffer = input.split('');
for(i = pos; i < buffer.length; i++){
- if(this.tests[i].test)
- break
+ if(this.tests[i].test)
+ break;
}
buffer.splice(i, 1);
var result=this.apply(buffer.join(''), i, true);
result.pos=i;
return result;
- }
+ };
FixedWidthMask.prototype.apply = function(inputString, caretPosition, doShift){
if(caretPosition == null)
@@ -104,7 +104,7 @@
pos;
for (i = 0, pos = 0; i < this.length; i++) {
- var action=this.tests[i];
+ action=this.tests[i];
if (action.test) {
buffer.push(this.settings.placeholder);
@@ -118,15 +118,20 @@
break;
}else if(doShift){
//TODO: The following is awful and needs to be refactored.
- var tests=$.map(this.tests.slice(i + 1),function(test, offset){
+ var tests;
+ /* jshint ignore:start */
+ tests=$.map(this.tests.slice(i + 1), function(test, offset){
var index = pos - 1 + offset;
if(test.test && input[index] != null){
return {regex:test,char:input[index]};
}
});
+ /* jshint ignore:end */
+
+ var newInput = [];
+ var canShift = tests.length > 0;
if(tests.length){
- var newInput = [],canShift = true;
tests.unshift({regex: action});
for(var j = 1; j < tests.length; j++){
if(!tests[j-1].regex.test(tests[j].char)){
@@ -163,12 +168,12 @@
var maxCaret=Math.min(caretPosition,this.length);
for(i=Math.min(lastMatch+1,maxCaret);i< this.length){
- trimmed = buffer.slice(0, Math.max(this.partialPosition,lastMatch+1))
+ trimmed = buffer.slice(0, Math.max(this.partialPosition,lastMatch+1));
}
if(!this.settings.autoclear){
@@ -183,9 +188,8 @@
pos: i , //(partialPosition ? i : firstNonMaskPos)
isComplete: (lastMatch + 1) >= this.partialPosition
};
- console.log(result);
return result;
- }
+ };
function getPasteEvent() {
var el = document.createElement('input'),
@@ -194,14 +198,7 @@
return (typeof el[name] === 'function')?'paste':'input';
}
- var pasteEventName = getPasteEvent() + ".mask",
- ua = navigator.userAgent,
- iPhone = /iphone/i.test(ua),
- chrome = /chrome/i.test(ua),
- android=/android/i.test(ua),
- caretTimeoutId;
-
-
+ var pasteEventName = getPasteEvent() + ".mask";
$.fn.extend({
//TODO: Be a good citizen and get this down to only .mask()
@@ -209,18 +206,13 @@
return this.trigger("unmask");
},
//TODO: we need a conflict thing here, maybe only use maskedinput(or alias?)
- mask: function(mask, settings) {
- var input,
- defs,
- tests,
- partialPosition,
- firstNonMaskPos,
- len;
+ mask: function(format, settings) {
+ var input;
//TODO: make these more in line with newer plugin interaction guidelines.
- if (!mask && this.length > 0) {
- input = $(this[0]);
- return input.data($.mask.dataName).apply(input.val()).raw;
+ if (!format && this.length > 0) {
+ input = $(this[0]);
+ return input.data($.mask.dataName).apply(input.val()).raw;
}
settings = $.extend({
@@ -230,7 +222,7 @@
completed: null
}, settings);
- var mask=new FixedWidthMask(mask,settings);
+ var mask=new FixedWidthMask(format,settings);
return this.trigger("unmask").each(function() {
@@ -238,7 +230,7 @@
input = $(this),
focusText = elm.value;
- function blurEvent(e) {
+ function blurEvent() {
var result = mask.apply(elm.value);
elm.value = result.trimmed;
if(settings.autoclear && !result.isComplete){
@@ -314,7 +306,7 @@
}
var caretTimeoutId;
- function focusEvent(e){
+ function focusEvent(){
clearTimeout(caretTimeoutId);
var result = mask.apply(elm.value);
focusText = elm.value;
@@ -329,7 +321,7 @@
}, 10);
}
- function pasteEvent(e){
+ function pasteEvent(){
setTimeout(function() {
var pos = getCaret(elm);
var result = mask.apply(elm.value, pos.end);
@@ -341,18 +333,19 @@
}
input.data($.mask.dataName,mask);
- if (!input.attr("readonly"))
- input
- .one("unmask", function() {
+ if (!input.attr("readonly")){
input
- .off(".mask")
- .removeData($.mask.dataName);
- })
- .on("focus.mask",focusEvent)
- .on("blur.mask", blurEvent)
- .on("keydown.mask", keydownEvent)
- .on("keypress.mask", keypressEvent)
- .on(pasteEventName, pasteEvent);
+ .one("unmask", function() {
+ input
+ .off(".mask")
+ .removeData($.mask.dataName);
+ })
+ .on("focus.mask",focusEvent)
+ .on("blur.mask", blurEvent)
+ .on("keydown.mask", keydownEvent)
+ .on("keypress.mask", keypressEvent)
+ .on(pasteEventName, pasteEvent);
+ }
// if (chrome && android) {
// input.on("keyup.mask", keypressEvent);
From 7fe007591cb49ff9a06bf8ee26d481528e7ec072 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 23:00:21 -0500
Subject: [PATCH 14/18] Breaking out mask implementation into separate file.
---
gruntfile.js | 2 +-
src/jquery.maskedinput.js | 157 +-----------------------------------
src/masks/FixedWidthMask.js | 152 ++++++++++++++++++++++++++++++++++
3 files changed, 157 insertions(+), 154 deletions(-)
create mode 100644 src/masks/FixedWidthMask.js
diff --git a/gruntfile.js b/gruntfile.js
index a507209..aa76f29 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -15,7 +15,7 @@ module.exports = function(grunt) {
stripBanners: false
},
dist: {
- src: ['src/jquery.maskedinput.js'],
+ src: ['src/jquery.maskedinput.js','src/masks/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 0af047e..44cfdea 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -27,8 +27,9 @@
return { begin: begin, end: end };
}
+ //TODO: alias as maskedinput?
$.mask = {
- masks:[],
+ masks:{},
//Predefined character definitions
//TODO: Move these to the mask def itself.
definitions: {
@@ -41,156 +42,6 @@
placeholder: '_'
};
- function FixedWidthMask (mask, settings){
- var self=this;
- //Build up structures necessary to quickly apply masking
- this.settings = settings;
-
- this.tests = [];
- this.partialPosition = mask.length;
- this.length = mask.length;
-
- var firstNonMaskPos = null;
-
- $.each(mask.split(""), function(i, c) {
- if (c == '?') {
- self.length--;
- self.partialPosition = i;
- } else if (settings.definitions[c]) {
- self.tests.push(new RegExp(settings.definitions[c]));
- if (firstNonMaskPos === null) {
- firstNonMaskPos = self.tests.length - 1;
- }
- } else {
- self.tests.push(c);
- }
- });
- }
-
- FixedWidthMask.prototype.applyBackspace = function(input, pos){
- var i, buffer = input.split('');
- for(i = pos - 1; i >= 0; i--){
- if(this.tests[i].test)
- break;
- }
- buffer.splice(i, 1);
- var result= this.apply(buffer.join(''), i, true);
- result.pos=i;
- return result;
- };
-
- FixedWidthMask.prototype.applyDelete = function(input, pos){
- var i, buffer = input.split('');
- for(i = pos; i < buffer.length; i++){
- if(this.tests[i].test)
- break;
- }
- buffer.splice(i, 1);
- var result=this.apply(buffer.join(''), i, true);
- result.pos=i;
- return result;
- };
-
- FixedWidthMask.prototype.apply = function(inputString, caretPosition, doShift){
- if(caretPosition == null)
- caretPosition = this.length;
-
- var input=inputString.split(''),
- buffer=[],
- raw=[],
- lastMatch = -1,
- i,
- action,
- pos;
-
- for (i = 0, pos = 0; i < this.length; i++) {
- action=this.tests[i];
-
- if (action.test) {
- buffer.push(this.settings.placeholder);
-
- while (pos++ < input.length) {
- var c = input[pos - 1];
- if (action.test(c)) {
- buffer[i] = c;
- raw.push(c);
- lastMatch = i;
- break;
- }else if(doShift){
- //TODO: The following is awful and needs to be refactored.
- var tests;
- /* jshint ignore:start */
- tests=$.map(this.tests.slice(i + 1), function(test, offset){
- var index = pos - 1 + offset;
- if(test.test && input[index] != null){
- return {regex:test,char:input[index]};
- }
- });
- /* jshint ignore:end */
-
- var newInput = [];
- var canShift = tests.length > 0;
-
- if(tests.length){
- tests.unshift({regex: action});
- for(var j = 1; j < tests.length; j++){
- if(!tests[j-1].regex.test(tests[j].char)){
- canShift = false;
- break;
- }
- newInput.push(tests[j].char);
- }
- }
-
- if(canShift){
- //Everything to the right can shift left and still match.
- input = newInput;
- buffer[i] = input[0];
- pos = 1;
- }else{
- //Retry current char at next position leaving a blank.
- pos--;
- }
- //Only allow shift attempt to happen once.
- doShift = false;
- break;
- }
- }
- } else {
- buffer.push(action);
- if(action === input[pos] && i !== this.partialPosition) {
- pos++;
- }
- }
- }
-
- //Find the next spot waiting for input
- var maxCaret=Math.min(caretPosition,this.length);
- for(i=Math.min(lastMatch+1,maxCaret);i< this.length){
- trimmed = buffer.slice(0, Math.max(this.partialPosition,lastMatch+1));
- }
-
- if(!this.settings.autoclear){
- trimmed = buffer.slice(0,i);
- }
-
- //TODO: better names for these props
- var result={
- value: buffer.join(''), //Prompt Value
- trimmed: trimmed.join(''), //Display Value
- raw: raw.join(''), //Raw Value, TODO: separate unmask call?
- pos: i , //(partialPosition ? i : firstNonMaskPos)
- isComplete: (lastMatch + 1) >= this.partialPosition
- };
- return result;
- };
-
function getPasteEvent() {
var el = document.createElement('input'),
name = 'onpaste';
@@ -222,8 +73,8 @@
completed: null
}, settings);
- var mask=new FixedWidthMask(format,settings);
-
+ //Hardcoded as fixed for now.
+ var mask=new $.mask.masks.fixed(format, settings);
return this.trigger("unmask").each(function() {
var elm = this,
diff --git a/src/masks/FixedWidthMask.js b/src/masks/FixedWidthMask.js
new file mode 100644
index 0000000..7e21f09
--- /dev/null
+++ b/src/masks/FixedWidthMask.js
@@ -0,0 +1,152 @@
+(function(){
+ function FixedWidthMask (mask, settings){
+ var self=this;
+ //Build up structures necessary to quickly apply masking
+ this.settings = settings;
+
+ this.tests = [];
+ this.partialPosition = mask.length;
+ this.length = mask.length;
+
+ var firstNonMaskPos = null;
+
+ $.each(mask.split(""), function(i, c) {
+ if (c == '?') {
+ self.length--;
+ self.partialPosition = i;
+ } else if (settings.definitions[c]) {
+ self.tests.push(new RegExp(settings.definitions[c]));
+ if (firstNonMaskPos === null) {
+ firstNonMaskPos = self.tests.length - 1;
+ }
+ } else {
+ self.tests.push(c);
+ }
+ });
+ }
+
+ FixedWidthMask.prototype.applyBackspace = function(input, pos){
+ var i, buffer = input.split('');
+ for(i = pos - 1; i >= 0; i--){
+ if(this.tests[i].test)
+ break;
+ }
+ buffer.splice(i, 1);
+ var result= this.apply(buffer.join(''), i, true);
+ result.pos=i;
+ return result;
+ };
+
+ FixedWidthMask.prototype.applyDelete = function(input, pos){
+ var i, buffer = input.split('');
+ for(i = pos; i < buffer.length; i++){
+ if(this.tests[i].test)
+ break;
+ }
+ buffer.splice(i, 1);
+ var result=this.apply(buffer.join(''), i, true);
+ result.pos=i;
+ return result;
+ };
+
+ FixedWidthMask.prototype.apply = function(inputString, caretPosition, doShift){
+ if(caretPosition == null)
+ caretPosition = this.length;
+
+ var input=inputString.split(''),
+ buffer=[],
+ raw=[],
+ lastMatch = -1,
+ i,
+ action,
+ pos;
+
+ for (i = 0, pos = 0; i < this.length; i++) {
+ action=this.tests[i];
+
+ if (action.test) {
+ buffer.push(this.settings.placeholder);
+
+ while (pos++ < input.length) {
+ var c = input[pos - 1];
+ if (action.test(c)) {
+ buffer[i] = c;
+ raw.push(c);
+ lastMatch = i;
+ break;
+ }else if(doShift){
+ //TODO: The following is awful and needs to be refactored.
+ var tests;
+ /* jshint ignore:start */
+ tests=$.map(this.tests.slice(i + 1), function(test, offset){
+ var index = pos - 1 + offset;
+ if(test.test && input[index] != null){
+ return {regex:test,char:input[index]};
+ }
+ });
+ /* jshint ignore:end */
+
+ var newInput = [];
+ var canShift = tests.length > 0;
+
+ if(tests.length){
+ tests.unshift({regex: action});
+ for(var j = 1; j < tests.length; j++){
+ if(!tests[j-1].regex.test(tests[j].char)){
+ canShift = false;
+ break;
+ }
+ newInput.push(tests[j].char);
+ }
+ }
+
+ if(canShift){
+ //Everything to the right can shift left and still match.
+ input = newInput;
+ buffer[i] = input[0];
+ pos = 1;
+ }else{
+ //Retry current char at next position leaving a blank.
+ pos--;
+ }
+ //Only allow shift attempt to happen once.
+ doShift = false;
+ break;
+ }
+ }
+ } else {
+ buffer.push(action);
+ if(action === input[pos] && i !== this.partialPosition) {
+ pos++;
+ }
+ }
+ }
+
+ //Find the next spot waiting for input
+ var maxCaret=Math.min(caretPosition,this.length);
+ for(i=Math.min(lastMatch+1,maxCaret);i< this.length){
+ trimmed = buffer.slice(0, Math.max(this.partialPosition,lastMatch+1));
+ }
+
+ if(!this.settings.autoclear){
+ trimmed = buffer.slice(0,i);
+ }
+
+ //TODO: better names for these props
+ var result={
+ value: buffer.join(''), //Prompt Value
+ trimmed: trimmed.join(''), //Display Value
+ raw: raw.join(''), //Raw Value, TODO: separate unmask call?
+ pos: i , //(partialPosition ? i : firstNonMaskPos)
+ isComplete: (lastMatch + 1) >= this.partialPosition
+ };
+ return result;
+ };
+ $.mask.masks.fixed = FixedWidthMask;
+})(jQuery);
From df370e44e311f7842411b3d8c9832a91fdfe2239 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 23:15:57 -0500
Subject: [PATCH 15/18] Breaking: Replacing completed callback with proper
event.
---
demo/index.html | 5 ++++-
spec/Paste.Spec.js | 6 +++---
src/jquery.maskedinput.js | 11 ++++++-----
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/demo/index.html b/demo/index.html
index d297b1e..c57914e 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -6,7 +6,10 @@
$(function() {
$.mask.definitions['~'] = "[+-]";
- $("#date").mask("99/99/9999",{completed:function(){alert("completed!");}});
+ $("#date").mask("99/99/9999")
+ .on("completed.mask",function(){
+ alert("completed with value: "+this.value);
+ });
$("#phone").mask("(999) 999-9999");
$("#phoneExt").mask("(999) 999-9999? x99999");
$("#iphone").mask("+33 999 999 999");
diff --git a/spec/Paste.Spec.js b/spec/Paste.Spec.js
index a9ae291..0997f33 100644
--- a/spec/Paste.Spec.js
+++ b/spec/Paste.Spec.js
@@ -1,8 +1,8 @@
-feature("Pasting", function() {
+feature("Pasting", function() {
scenario('When pasting a value',function(){
var completed=false;
given("an input with a completed callback", function(){
- input.mask("99",{completed:function(){completed=true;}});
+ input.mask("99").on("completed.mask",function(){completed=true;});
});
when("pasting",function(){
@@ -13,4 +13,4 @@ feature("Pasting", function() {
expect(completed).toBeTruthy();
});
});
-});
\ No newline at end of file
+});
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index 44cfdea..a8c3df7 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -73,7 +73,7 @@
completed: null
}, settings);
- //Hardcoded as fixed for now.
+ //Hardcoded as fixed for now.
var mask=new $.mask.masks.fixed(format, settings);
return this.trigger("unmask").each(function() {
@@ -137,8 +137,9 @@
elm.value = result.value;
setCaret(elm, result.pos);
- if(result.isComplete && settings.completed)
- settings.completed.call(input); //TODO: Raise event instead.
+ if(result.isComplete)
+ input.trigger("completed.mask");
+
e.preventDefault();
// if(android){
@@ -178,8 +179,8 @@
var result = mask.apply(elm.value, pos.end);
elm.value = result.value;
setCaret(elm, result.pos);
- if(result.isComplete && settings.completed)
- settings.completed.call(input); //TODO: Raise event instead.
+ if(result.isComplete)
+ input.trigger("completed.mask");
}, 0);
}
input.data($.mask.dataName,mask);
From e46b100430f777495c8dfed0c1c736f52855f1c6 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 23:25:37 -0500
Subject: [PATCH 16/18] Force curly braces for single line blocks.
---
src/.jshintrc | 4 +++-
src/jquery.maskedinput.js | 6 ++++--
src/masks/FixedWidthMask.js | 12 ++++++++----
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/.jshintrc b/src/.jshintrc
index 97efc48..c07d7d2 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -5,5 +5,7 @@
"newcap" : true,
"indent" : 4,
"undef" : true,
- "unused" : true
+ "unused" : true,
+ "trailing" : true,
+ "curly" : true
}
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index a8c3df7..cd4b983 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -137,8 +137,9 @@
elm.value = result.value;
setCaret(elm, result.pos);
- if(result.isComplete)
+ if(result.isComplete){
input.trigger("completed.mask");
+ }
e.preventDefault();
@@ -179,8 +180,9 @@
var result = mask.apply(elm.value, pos.end);
elm.value = result.value;
setCaret(elm, result.pos);
- if(result.isComplete)
+ if(result.isComplete){
input.trigger("completed.mask");
+ }
}, 0);
}
input.data($.mask.dataName,mask);
diff --git a/src/masks/FixedWidthMask.js b/src/masks/FixedWidthMask.js
index 7e21f09..d61192d 100644
--- a/src/masks/FixedWidthMask.js
+++ b/src/masks/FixedWidthMask.js
@@ -28,8 +28,9 @@
FixedWidthMask.prototype.applyBackspace = function(input, pos){
var i, buffer = input.split('');
for(i = pos - 1; i >= 0; i--){
- if(this.tests[i].test)
+ if(this.tests[i].test){
break;
+ }
}
buffer.splice(i, 1);
var result= this.apply(buffer.join(''), i, true);
@@ -40,8 +41,9 @@
FixedWidthMask.prototype.applyDelete = function(input, pos){
var i, buffer = input.split('');
for(i = pos; i < buffer.length; i++){
- if(this.tests[i].test)
+ if(this.tests[i].test){
break;
+ }
}
buffer.splice(i, 1);
var result=this.apply(buffer.join(''), i, true);
@@ -50,8 +52,9 @@
};
FixedWidthMask.prototype.apply = function(inputString, caretPosition, doShift){
- if(caretPosition == null)
+ if(caretPosition == null){
caretPosition = this.length;
+ }
var input=inputString.split(''),
buffer=[],
@@ -125,8 +128,9 @@
//Find the next spot waiting for input
var maxCaret=Math.min(caretPosition,this.length);
for(i=Math.min(lastMatch+1,maxCaret);i
Date: Mon, 24 Mar 2014 23:31:23 -0500
Subject: [PATCH 17/18] Enforcing strict mode.
---
src/.jshintrc | 3 ++-
src/jquery.maskedinput.js | 4 ++--
src/masks/FixedWidthMask.js | 1 +
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/.jshintrc b/src/.jshintrc
index c07d7d2..a26251b 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -7,5 +7,6 @@
"undef" : true,
"unused" : true,
"trailing" : true,
- "curly" : true
+ "curly" : true,
+ "strict" : true
}
diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js
index cd4b983..d291665 100644
--- a/src/jquery.maskedinput.js
+++ b/src/jquery.maskedinput.js
@@ -1,5 +1,5 @@
(function($) {
-
+ "use strict";
function setCaret(elm,begin,end){
end = (typeof end === 'number') ? end : begin;
@@ -116,7 +116,7 @@
setCaret(elm, result.pos);
e.preventDefault();
} else if(k === 13) { // enter
- blurEvent.call(this, e);
+ blurEvent.call(elm, e);
} else if (k === 27) { // escape
elm.value = focusText;
setCaret(elm, 0, focusText.length);
diff --git a/src/masks/FixedWidthMask.js b/src/masks/FixedWidthMask.js
index d61192d..e7ad0a1 100644
--- a/src/masks/FixedWidthMask.js
+++ b/src/masks/FixedWidthMask.js
@@ -1,4 +1,5 @@
(function(){
+ "use strict";
function FixedWidthMask (mask, settings){
var self=this;
//Build up structures necessary to quickly apply masking
From 4bde7fe2bb4d2f3e47fa6e4d2e2d1a286f1d53d3 Mon Sep 17 00:00:00 2001
From: Josh Bush
Date: Mon, 24 Mar 2014 23:43:30 -0500
Subject: [PATCH 18/18] plugin.json is an artifact from an old build system.
---
plugin.json | 5 -----
1 file changed, 5 deletions(-)
delete mode 100644 plugin.json
diff --git a/plugin.json b/plugin.json
deleted file mode 100644
index e2aa63d..0000000
--- a/plugin.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name" : "jquery.maskedinput",
- "author" : "Josh Bush (digitalbush.com)",
- "version" : "1.3.1"
-}