From eb1e27f1b2885cc07c39abf4ff354627e713b616 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Tue, 2 Aug 2011 16:51:21 -0500 Subject: [PATCH 01/33] first pass at auto-completion. Currently breaks 'completed' setting. --- .gitignore | 2 ++ demo/index.html | 21 ++++++++++++++++++++- src/jquery.maskedinput.js | 27 ++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3260eff..44140a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/* dist/* +# vim undo files +*~ diff --git a/demo/index.html b/demo/index.html index 4beeaab..d41c970 100644 --- a/demo/index.html +++ b/demo/index.html @@ -6,7 +6,26 @@ + + + + + + + + + + + + + + + +
Date99/99/9999
Phone(999) 999-9999
Phone + Ext(999) 999-9999? x99999
Int'l Phone+44 (999) 999-9999
Tax ID99-9999999
SSN999-99-9999
Product Keya*-999-a999
Eye Script~9.99 ~9.99 999
Purchase Orderaaa-999-***
Percent99%
+
+ + diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index e1419dd..af67604 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -192,7 +192,7 @@ buffer[i] = settings.placeholder; while (pos++ < test.length) { var c = test.charAt(pos - 1); - if (tests[pos - 1] && tests[pos - 1].test(c)) { + if (tests[i].test(c)) { buffer[i] = c; lastMatch = i; break; diff --git a/src/jquery.maskedinput.js~ b/src/jquery.maskedinput.js~ new file mode 100644 index 0000000..66cbb07 --- /dev/null +++ b/src/jquery.maskedinput.js~ @@ -0,0 +1,287 @@ +/* + Masked Input plugin for jQuery + Copyright (c) 2007-@Year Josh Bush (digitalbush.com) + Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) + Version: @version +*/ +(function($) { + var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask"; + var iPhone = (window.orientation != undefined); + + $.mask = { + //Predefined character definitions + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + 'm': "[0-9\/]", + 'd': "[0-9\/]", + 'y': "[0-9\/]", + '*': "[A-Za-z0-9]" + }, + dataName:"rawMaskFn" + }; + + $.fn.extend({ + //Helper Function for Caret positioning + caret: function(begin, end) { + if (this.length == 0) 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) { + var 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) { + var 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) { + if (!mask && this.length > 0) { + var input = $(this[0]); + return input.data($.mask.dataName)(); + } + settings = $.extend({ + placeholder: "_", + completed: null, + autocomplete: [ + /* an array of objects of the following format, which will be + * applied via a call to String.replace() after each update of the input + + { + pattern: /^([2-9])(.)/, + replacement: "0$1" + } + + */ + ] + + }, settings); + + var defs = $.mask.definitions; + var tests = []; + var partialPosition = mask.length; + var firstNonMaskPos = null; + var len = mask.length; + + $.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); + var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c }); + var 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) { + if(begin<0) + return; + for (var 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) { + for (var i = pos, c = settings.placeholder; i < len; i++) { + if (tests[i]) { + var j = seekNext(i); + var t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) + c = t; + else + break; + } + } + }; + + function keydownEvent(e) { + var k=e.which; + + //backspace, delete, and escape get special treatment + if(k == 8 || k == 46 || (iPhone && k == 127)){ + var 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); + + return false; + } else if (k == 27) {//escape + input.val(focusText); + input.caret(0, checkVal()); + return false; + } + }; + + function keypressEvent(e) { + var k = e.which, + rule = null, + i = 0, + startLength = input.mask().length, + pos = input.caret(); + if (e.ctrlKey || e.altKey || e.metaKey || k<32) {//Ignore + return true; + } else if (k) { + if(pos.end-pos.begin!=0){ + clearBuffer(pos.begin, pos.end); + shiftL(pos.begin, pos.end-1); + } + + var p = seekNext(pos.begin - 1); + if (p < len) { + var c = String.fromCharCode(k); + if (tests[p].test(c)) { + shiftR(p); + buffer[p] = c; + writeBuffer(); + var next = seekNext(p); + input.caret(next); + + if (settings.autocomplete.length > 0) { + for(i=0; i < settings.autocomplete.length; i++){ + rule = settings.autocomplete[i]; + input.val(input.val().replace(rule.pattern, rule.replacement)); + clearBuffer(0, RegExp.lastMatch.length); + input.caret(checkVal(true)); + } + } + next += input.mask().length - startLength - 1; + if (settings.completed && next >= len) + settings.completed.call(input); + } + + } + return false; + } + }; + + function clearBuffer(start, end) { + for (var i = start; i < end && i < len; i++) { + if (tests[i]) + buffer[i] = settings.placeholder; + } + }; + + function writeBuffer() { return input.val(buffer.join('')).val(); }; + + function checkVal(allow) { + //try to place characters where they belong + var test = input.val(); + var lastMatch = -1; + for (var i = 0, pos = 0; i < len; i++) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + var c = test.charAt(pos - 1); + if (tests[pos - 1] && tests[pos - 1].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 && lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else if (allow || lastMatch + 1 >= partialPosition) { + writeBuffer(); + if (!allow) 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() { + focusText = input.val(); + var pos = checkVal(); + writeBuffer(); + var moveCaret=function(){ + if (pos == mask.length) + input.caret(0, pos); + else + input.caret(pos); + }; + ($.browser.msie ? moveCaret:function(){setTimeout(moveCaret,0)})(); + }) + .bind("blur.mask", function() { + checkVal(); + if (input.val() != focusText) + input.change(); + }) + .bind("keydown.mask", keydownEvent) + .bind("keypress.mask", keypressEvent) + .bind(pasteEventName, function() { + setTimeout(function() { input.caret(checkVal(true)); }, 0); + }); + + checkVal(); //Perform initial check for existing values + }); + } + }); +})(jQuery); From 9052a720f9730b6b5911e19c9ed37bf244d3f916 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Wed, 3 Aug 2011 14:25:55 -0500 Subject: [PATCH 08/33] inadvertent adds of ignored files --- .gitignore~ | 4 - demo/index.html~ | 44 ------ src/jquery.maskedinput.js~ | 287 ------------------------------------- 3 files changed, 335 deletions(-) delete mode 100644 .gitignore~ delete mode 100644 demo/index.html~ delete mode 100644 src/jquery.maskedinput.js~ diff --git a/.gitignore~ b/.gitignore~ deleted file mode 100644 index 3ea1db9..0000000 --- a/.gitignore~ +++ /dev/null @@ -1,4 +0,0 @@ - -.idea/* -dist/* -*~ diff --git a/demo/index.html~ b/demo/index.html~ deleted file mode 100644 index 4beeaab..0000000 --- a/demo/index.html~ +++ /dev/null @@ -1,44 +0,0 @@ - - - jQuery Mask Test - - - - - - - - - - - - - - - - -
Date99/99/9999
Phone(999) 999-9999
Phone + Ext(999) 999-9999? x99999
Int'l Phone+44 (999) 999-9999
Tax ID99-9999999
SSN999-99-9999
Product Keya*-999-a999
Eye Script~9.99 ~9.99 999
Purchase Orderaaa-999-***
Percent99%
-
- - diff --git a/src/jquery.maskedinput.js~ b/src/jquery.maskedinput.js~ deleted file mode 100644 index 66cbb07..0000000 --- a/src/jquery.maskedinput.js~ +++ /dev/null @@ -1,287 +0,0 @@ -/* - Masked Input plugin for jQuery - Copyright (c) 2007-@Year Josh Bush (digitalbush.com) - Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) - Version: @version -*/ -(function($) { - var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask"; - var iPhone = (window.orientation != undefined); - - $.mask = { - //Predefined character definitions - definitions: { - '9': "[0-9]", - 'a': "[A-Za-z]", - 'm': "[0-9\/]", - 'd': "[0-9\/]", - 'y': "[0-9\/]", - '*': "[A-Za-z0-9]" - }, - dataName:"rawMaskFn" - }; - - $.fn.extend({ - //Helper Function for Caret positioning - caret: function(begin, end) { - if (this.length == 0) 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) { - var 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) { - var 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) { - if (!mask && this.length > 0) { - var input = $(this[0]); - return input.data($.mask.dataName)(); - } - settings = $.extend({ - placeholder: "_", - completed: null, - autocomplete: [ - /* an array of objects of the following format, which will be - * applied via a call to String.replace() after each update of the input - - { - pattern: /^([2-9])(.)/, - replacement: "0$1" - } - - */ - ] - - }, settings); - - var defs = $.mask.definitions; - var tests = []; - var partialPosition = mask.length; - var firstNonMaskPos = null; - var len = mask.length; - - $.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); - var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c }); - var 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) { - if(begin<0) - return; - for (var 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) { - for (var i = pos, c = settings.placeholder; i < len; i++) { - if (tests[i]) { - var j = seekNext(i); - var t = buffer[i]; - buffer[i] = c; - if (j < len && tests[j].test(t)) - c = t; - else - break; - } - } - }; - - function keydownEvent(e) { - var k=e.which; - - //backspace, delete, and escape get special treatment - if(k == 8 || k == 46 || (iPhone && k == 127)){ - var 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); - - return false; - } else if (k == 27) {//escape - input.val(focusText); - input.caret(0, checkVal()); - return false; - } - }; - - function keypressEvent(e) { - var k = e.which, - rule = null, - i = 0, - startLength = input.mask().length, - pos = input.caret(); - if (e.ctrlKey || e.altKey || e.metaKey || k<32) {//Ignore - return true; - } else if (k) { - if(pos.end-pos.begin!=0){ - clearBuffer(pos.begin, pos.end); - shiftL(pos.begin, pos.end-1); - } - - var p = seekNext(pos.begin - 1); - if (p < len) { - var c = String.fromCharCode(k); - if (tests[p].test(c)) { - shiftR(p); - buffer[p] = c; - writeBuffer(); - var next = seekNext(p); - input.caret(next); - - if (settings.autocomplete.length > 0) { - for(i=0; i < settings.autocomplete.length; i++){ - rule = settings.autocomplete[i]; - input.val(input.val().replace(rule.pattern, rule.replacement)); - clearBuffer(0, RegExp.lastMatch.length); - input.caret(checkVal(true)); - } - } - next += input.mask().length - startLength - 1; - if (settings.completed && next >= len) - settings.completed.call(input); - } - - } - return false; - } - }; - - function clearBuffer(start, end) { - for (var i = start; i < end && i < len; i++) { - if (tests[i]) - buffer[i] = settings.placeholder; - } - }; - - function writeBuffer() { return input.val(buffer.join('')).val(); }; - - function checkVal(allow) { - //try to place characters where they belong - var test = input.val(); - var lastMatch = -1; - for (var i = 0, pos = 0; i < len; i++) { - if (tests[i]) { - buffer[i] = settings.placeholder; - while (pos++ < test.length) { - var c = test.charAt(pos - 1); - if (tests[pos - 1] && tests[pos - 1].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 && lastMatch + 1 < partialPosition) { - input.val(""); - clearBuffer(0, len); - } else if (allow || lastMatch + 1 >= partialPosition) { - writeBuffer(); - if (!allow) 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() { - focusText = input.val(); - var pos = checkVal(); - writeBuffer(); - var moveCaret=function(){ - if (pos == mask.length) - input.caret(0, pos); - else - input.caret(pos); - }; - ($.browser.msie ? moveCaret:function(){setTimeout(moveCaret,0)})(); - }) - .bind("blur.mask", function() { - checkVal(); - if (input.val() != focusText) - input.change(); - }) - .bind("keydown.mask", keydownEvent) - .bind("keypress.mask", keypressEvent) - .bind(pasteEventName, function() { - setTimeout(function() { input.caret(checkVal(true)); }, 0); - }); - - checkVal(); //Perform initial check for existing values - }); - } - }); -})(jQuery); From 82e845ed567174e7aed27d7ee73390046570e862 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Thu, 11 Aug 2011 17:39:27 -0500 Subject: [PATCH 09/33] added predefined autocomplete definitions, began autocomplete specs --- spec/AutoComplete.Spec.js | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 spec/AutoComplete.Spec.js diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js new file mode 100644 index 0000000..ad64d1c --- /dev/null +++ b/spec/AutoComplete.Spec.js @@ -0,0 +1,89 @@ +feature("Autocompletion", function() { + story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings", function(){ + beforeEach(function(){ + $.mask.definitions['~'] = "[+-]"; + $.mask.definitions['m'] = "[0-9\/]"; + $.mask.definitions['d'] = "[0-9\/]"; + $.mask.definitions['y'] = "[0-9\/]"; + input.mask("mm/dd/yyyy",{ placeholder: " ", + autocomplete: $.mask.autocomplete_predefined['mmddyyyy'] + }); + }); + + scenario('User enters an invalid month',function(){ + given("an empty input", function(){ + }); + + when("entering an invalid month",function(){ + input.mashKeys("13"); + }); + + then("input should reset to blank",function(){ + expect(input).toHaveValue(' / / '); + }); + }); + + scenario('User enters a single digit followed by slash',function(){ + given("an empty input", function(){ + }); + + when("entering a single digit followed by slash",function(){ + input.mashKeys("4/"); + }); + + then("input value should have month zero padded",function(){ + expect(input).toHaveValue('04/ / '); + }); + }); + + scenario('User enters a valid two digit month',function(){ + given("an empty input", function(){ + }); + + when("entering '09'",function(){ + input.mashKeys("09"); + }); + + then("input value should advance to day fields",function(){ + expect(input).toHaveValue('09/ / '); + }); + }); + + scenario('User enters an invalid day of month',function(){ + given("an input with the month already entered", function(){ + input.mashKeys("09"); + }); + + when("entering '41'",function(){ + input.mashKeys("41"); + }); + + then("input value should reset to blank day of month",function(){ + expect(input).toHaveValue('09/ / '); + }); + + when("entering '35'",function(){ + input.mashKeys("35"); + }); + + then("input value should reset to blank day of month",function(){ + expect(input).toHaveValue('09/ / '); + }); + }); + + scenario('User enters single digit day of month, followed by slash',function(){ + given("an input with the month already entered", function(){ + input.mashKeys("09"); + }); + + when("entering '9/'",function(){ + input.mashKeys("9/"); + }); + + then("input value should zero pad day of month",function(){ + expect(input).toHaveValue('09/09/ '); + }); + }); + }); +}); + From 1b500739abcff2849db34287ef23a464f39125d2 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Thu, 11 Aug 2011 17:42:07 -0500 Subject: [PATCH 10/33] added predefined autocomplete definitions, began autocomplete specs --- demo/index.html | 2 +- spec/Setup.Spec.js | 2 +- spec/SpecRunner.html | 2 ++ src/jquery.maskedinput.js | 32 +++++++++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/demo/index.html b/demo/index.html index 64547a4..1ba8294 100644 --- a/demo/index.html +++ b/demo/index.html @@ -20,7 +20,7 @@ }, { pattern: /^1[3-9]/, - replacement: "" + replacement: " " }, { pattern: /^(\d\d.)([4-9])./, diff --git a/spec/Setup.Spec.js b/spec/Setup.Spec.js index ba765a6..aaaf2ef 100644 --- a/spec/Setup.Spec.js +++ b/spec/Setup.Spec.js @@ -10,7 +10,7 @@ feature("Masking an Input", function() { input.mashKeys("1"); }); - then("value should be correct",function(){ + then("value should be reflect the second mask",function(){ expect(input).toHaveValue('1_'); }); }); diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html index 87b3d91..2ded893 100644 --- a/spec/SpecRunner.html +++ b/spec/SpecRunner.html @@ -46,6 +46,8 @@ + + diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index a523c07..b28a114 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -15,7 +15,37 @@ 'a': "[A-Za-z]", '*': "[A-Za-z0-9]" }, - dataName:"rawMaskFn" + dataName:"rawMaskFn", + autocomplete_predefined: { + 'mmddyyyy': [ + { + pattern: /^(\d)\//, + replacement: "0$1" + }, + { + pattern: /^1[3-9]/, + replacement: " " + }, + { + pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, + replacement: "$1 " + }, + { + pattern: /^(\d\d.)(\d\/)./, + replacement: "$1" + "0" + "$2" + }, + { + pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, + { + pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } + ] + + + } }; $.fn.extend({ From 3dbbf78e3981e4a3f5a76737c36a8edd8c1b5357 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Thu, 11 Aug 2011 18:15:08 -0500 Subject: [PATCH 11/33] started on year specs for mmddyyyy --- spec/AutoComplete.Spec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js index ad64d1c..d3a6931 100644 --- a/spec/AutoComplete.Spec.js +++ b/spec/AutoComplete.Spec.js @@ -84,6 +84,20 @@ feature("Autocompletion", function() { expect(input).toHaveValue('09/09/ '); }); }); + + scenario('User enters a year less than 30, not 19 or 20',function(){ + given("an input with the month and day already entered", function(){ + input.mashKeys("0909"); + }); + + when("entering '18'",function(){ + input.mashKeys("18"); + }); + + then("input value should assume 2000s and prefix accordingly",function(){ + expect(input).toHaveValue('09/09/2018'); + }); + }); }); }); From 6cc8656c8915f9a1a564f277b9970b49db4c25f7 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Fri, 12 Aug 2011 06:51:16 -0500 Subject: [PATCH 12/33] translation of autocomplete spec to coffee-script --- spec/AutoComplete.Spec.coffee | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 spec/AutoComplete.Spec.coffee diff --git a/spec/AutoComplete.Spec.coffee b/spec/AutoComplete.Spec.coffee new file mode 100644 index 0000000..3a1e537 --- /dev/null +++ b/spec/AutoComplete.Spec.coffee @@ -0,0 +1,73 @@ +feature "Autocompletion", -> + story "User interacts with an input with 'mmddyyyy' predefined autocomplete settings", -> + beforeEach -> + $.mask.definitions["~"] = "[+-]" + $.mask.definitions["m"] = "[0-9/]" + $.mask.definitions["d"] = "[0-9/]" + $.mask.definitions["y"] = "[0-9/]" + input.mask "mm/dd/yyyy", + placeholder: " " + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] + + scenario "User enters an invalid month", -> + given "an empty input", -> + + when_ "entering an invalid month", -> + input.mashKeys "13" + + then "input should reset to blank", -> + expect(input).toHaveValue " / / " + + scenario "User enters a single digit followed by slash", -> + given "an empty input", -> + + when_ "entering a single digit followed by slash", -> + input.mashKeys "4/" + + then "input value should have month zero padded", -> + expect(input).toHaveValue "04/ / " + + scenario "User enters a valid two digit month", -> + given "an empty input", -> + + when_ "entering '09'", -> + input.mashKeys "09" + + then "input value should advance to day fields", -> + expect(input).toHaveValue "09/ / " + + scenario "User enters an invalid day of month", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '41'", -> + input.mashKeys "41" + + then "input value should reset to blank day of month", -> + expect(input).toHaveValue "09/ / " + + when_ "entering '35'", -> + input.mashKeys "35" + + then "input value should reset to blank day of month", -> + expect(input).toHaveValue "09/ / " + + scenario "User enters single digit day of month, followed by slash", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '9/'", -> + input.mashKeys "9/" + + then "input value should zero pad day of month", -> + expect(input).toHaveValue "09/09/ " + + scenario "User enters a year less than 30, not 19 or 20", -> + given "an input with the month and day already entered", -> + input.mashKeys "0909" + + when_ "entering '18'", -> + input.mashKeys "18" + + then "input value should assume 2000s and prefix accordingly", -> + expect(input).toHaveValue "09/09/2018" From 110151657e684549fd3b2cfd3f34a0502c15be72 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Fri, 12 Aug 2011 13:48:35 -0500 Subject: [PATCH 13/33] coffeescript'd the autocomplete specs --- .gitignore | 3 + spec/AutoComplete.Spec.coffee | 127 +++++++++++++-- spec/AutoComplete.Spec.js | 283 +++++++++++++++++++++------------- src/jquery.maskedinput.js | 25 ++- 4 files changed, 321 insertions(+), 117 deletions(-) diff --git a/.gitignore b/.gitignore index 44140a8..e3f7294 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ dist/* # vim undo files *~ +# vim swap files +*.swp +acs.tmp diff --git a/spec/AutoComplete.Spec.coffee b/spec/AutoComplete.Spec.coffee index 3a1e537..8631a20 100644 --- a/spec/AutoComplete.Spec.coffee +++ b/spec/AutoComplete.Spec.coffee @@ -1,10 +1,6 @@ feature "Autocompletion", -> story "User interacts with an input with 'mmddyyyy' predefined autocomplete settings", -> beforeEach -> - $.mask.definitions["~"] = "[+-]" - $.mask.definitions["m"] = "[0-9/]" - $.mask.definitions["d"] = "[0-9/]" - $.mask.definitions["y"] = "[0-9/]" input.mask "mm/dd/yyyy", placeholder: " " autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] @@ -15,7 +11,7 @@ feature "Autocompletion", -> when_ "entering an invalid month", -> input.mashKeys "13" - then "input should reset to blank", -> + then_ "input should reset to blank", -> expect(input).toHaveValue " / / " scenario "User enters a single digit followed by slash", -> @@ -24,7 +20,7 @@ feature "Autocompletion", -> when_ "entering a single digit followed by slash", -> input.mashKeys "4/" - then "input value should have month zero padded", -> + then_ "input value should have month zero padded", -> expect(input).toHaveValue "04/ / " scenario "User enters a valid two digit month", -> @@ -33,7 +29,7 @@ feature "Autocompletion", -> when_ "entering '09'", -> input.mashKeys "09" - then "input value should advance to day fields", -> + then_ "input value should advance to day fields", -> expect(input).toHaveValue "09/ / " scenario "User enters an invalid day of month", -> @@ -43,13 +39,13 @@ feature "Autocompletion", -> when_ "entering '41'", -> input.mashKeys "41" - then "input value should reset to blank day of month", -> + then_ "input value should reset to blank day of month", -> expect(input).toHaveValue "09/ / " - when_ "entering '35'", -> - input.mashKeys "35" + when_ "entering '36'", -> + input.mashKeys "36" - then "input value should reset to blank day of month", -> + then_ "input value should reset to blank day of month", -> expect(input).toHaveValue "09/ / " scenario "User enters single digit day of month, followed by slash", -> @@ -59,7 +55,7 @@ feature "Autocompletion", -> when_ "entering '9/'", -> input.mashKeys "9/" - then "input value should zero pad day of month", -> + then_ "input value should zero pad day of month", -> expect(input).toHaveValue "09/09/ " scenario "User enters a year less than 30, not 19 or 20", -> @@ -69,5 +65,110 @@ feature "Autocompletion", -> when_ "entering '18'", -> input.mashKeys "18" - then "input value should assume 2000s and prefix accordingly", -> + then_ "input value should assume 2000s and prefix accordingly", -> expect(input).toHaveValue "09/09/2018" + + scenario "User enters a year greater than 29", -> + given "an input with the month and day already entered", -> + input.mashKeys "0909" + + when_ "entering '64'", -> + input.mashKeys "64" + + then_ "input value should assume 1900s and prefix accordingly", -> + expect(input).toHaveValue "09/09/1964" + + scenario "user starts to enter a year beginning with 20", -> + given "an input with the month and day already entered", -> + input.mashKeys "0909" + + when_ "entering '20'", -> + input.mashKeys "20" + + then_ "input value should just reflect the '20' that was entered", -> + expect(input).toHaveValue "09/09/20 " + + scenario "user starts to enter a year beginning with 19", -> + given "an input with the month and day already entered", -> + input.mashKeys "0909" + + when_ "entering '19'", -> + input.mashKeys "19" + + then_ "input value should just reflect the '19' that was entered", -> + expect(input).toHaveValue "09/09/19 " + + story "User interacts with an input with 'mmyyyy' predefined autocomplete settings", -> + beforeEach -> + input.mask "mm/yyyy", + placeholder: " " + autocomplete: $.mask.autocomplete_predefined["mmyyyy"] + + scenario "User enters an invalid month", -> + given "an empty input", -> + + when_ "entering an invalid month", -> + input.mashKeys "13" + + then_ "input should reset to blank", -> + expect(input).toHaveValue " / " + + scenario "User enters a single digit followed by slash", -> + given "an empty input", -> + + when_ "entering a single digit followed by slash", -> + input.mashKeys "4/" + + then_ "input value should have month zero padded", -> + expect(input).toHaveValue "04/ " + + scenario "User enters a valid two digit month", -> + given "an empty input", -> + + when_ "entering '09'", -> + input.mashKeys "09" + + then_ "input value should advance to year field", -> + expect(input).toHaveValue "09/ " + + scenario "User enters a year less than 30, not 19 or 20", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '18'", -> + input.mashKeys "18" + + then_ "input value should assume 2000s and prefix accordingly", -> + expect(input).toHaveValue "09/2018" + + scenario "user starts to enter a year beginning with 20", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '20'", -> + input.mashKeys "20" + + then_ "input value should just reflect the '20' that was entered", -> + expect(input).toHaveValue "09/20 " + + scenario "user starts to enter a year beginning with 19", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '19'", -> + input.mashKeys "19" + + then_ "input value should just reflect the '19' that was entered", -> + expect(input).toHaveValue "09/19 " + + + scenario "User enters a year greater than 29", -> + given "an input with the month already entered", -> + input.mashKeys "09" + + when_ "entering '64'", -> + input.mashKeys "64" + + then_ "input value should assume 1900s and prefix accordingly", -> + expect(input).toHaveValue "09/1964" + diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js index d3a6931..a1899fd 100644 --- a/spec/AutoComplete.Spec.js +++ b/spec/AutoComplete.Spec.js @@ -1,103 +1,180 @@ -feature("Autocompletion", function() { - story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings", function(){ - beforeEach(function(){ - $.mask.definitions['~'] = "[+-]"; - $.mask.definitions['m'] = "[0-9\/]"; - $.mask.definitions['d'] = "[0-9\/]"; - $.mask.definitions['y'] = "[0-9\/]"; - input.mask("mm/dd/yyyy",{ placeholder: " ", - autocomplete: $.mask.autocomplete_predefined['mmddyyyy'] - }); - }); - - scenario('User enters an invalid month',function(){ - given("an empty input", function(){ - }); - - when("entering an invalid month",function(){ - input.mashKeys("13"); - }); - - then("input should reset to blank",function(){ - expect(input).toHaveValue(' / / '); - }); - }); - - scenario('User enters a single digit followed by slash',function(){ - given("an empty input", function(){ - }); - - when("entering a single digit followed by slash",function(){ - input.mashKeys("4/"); - }); - - then("input value should have month zero padded",function(){ - expect(input).toHaveValue('04/ / '); - }); - }); - - scenario('User enters a valid two digit month',function(){ - given("an empty input", function(){ - }); - - when("entering '09'",function(){ - input.mashKeys("09"); - }); - - then("input value should advance to day fields",function(){ - expect(input).toHaveValue('09/ / '); - }); - }); - - scenario('User enters an invalid day of month',function(){ - given("an input with the month already entered", function(){ - input.mashKeys("09"); - }); - - when("entering '41'",function(){ - input.mashKeys("41"); - }); - - then("input value should reset to blank day of month",function(){ - expect(input).toHaveValue('09/ / '); - }); - - when("entering '35'",function(){ - input.mashKeys("35"); - }); - - then("input value should reset to blank day of month",function(){ - expect(input).toHaveValue('09/ / '); - }); - }); - - scenario('User enters single digit day of month, followed by slash',function(){ - given("an input with the month already entered", function(){ - input.mashKeys("09"); - }); - - when("entering '9/'",function(){ - input.mashKeys("9/"); - }); - - then("input value should zero pad day of month",function(){ - expect(input).toHaveValue('09/09/ '); - }); - }); - - scenario('User enters a year less than 30, not 19 or 20',function(){ - given("an input with the month and day already entered", function(){ - input.mashKeys("0909"); - }); - - when("entering '18'",function(){ - input.mashKeys("18"); - }); - - then("input value should assume 2000s and prefix accordingly",function(){ - expect(input).toHaveValue('09/09/2018'); - }); - }); - }); -}); - +feature("Autocompletion", function() { + story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings", function() { + beforeEach(function() { + return input.mask("mm/dd/yyyy", { + placeholder: " ", + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] + }); + }); + scenario("User enters an invalid month", function() { + given("an empty input", function() {}); + when("entering an invalid month", function() { + return input.mashKeys("13"); + }); + return then("input should reset to blank", function() { + return expect(input).toHaveValue(" / / "); + }); + }); + scenario("User enters a single digit followed by slash", function() { + given("an empty input", function() {}); + when("entering a single digit followed by slash", function() { + return input.mashKeys("4/"); + }); + return then("input value should have month zero padded", function() { + return expect(input).toHaveValue("04/ / "); + }); + }); + scenario("User enters a valid two digit month", function() { + given("an empty input", function() {}); + when("entering '09'", function() { + return input.mashKeys("09"); + }); + return then("input value should advance to day fields", function() { + return expect(input).toHaveValue("09/ / "); + }); + }); + scenario("User enters an invalid day of month", function() { + given("an input with the month already entered", function() { + return input.mashKeys("09"); + }); + when("entering '41'", function() { + return input.mashKeys("41"); + }); + then("input value should reset to blank day of month", function() { + return expect(input).toHaveValue("09/ / "); + }); + when("entering '36'", function() { + return input.mashKeys("36"); + }); + return then("input value should reset to blank day of month", function() { + return expect(input).toHaveValue("09/ / "); + }); + }); + scenario("User enters single digit day of month, followed by slash", function() { + given("an input with the month already entered", function() { + return input.mashKeys("09"); + }); + when("entering '9/'", function() { + return input.mashKeys("9/"); + }); + return then("input value should zero pad day of month", function() { + return expect(input).toHaveValue("09/09/ "); + }); + }); + scenario("User enters a year less than 30, not 19 or 20", function() { + given("an input with the month and day already entered", function() { + return input.mashKeys("0909"); + }); + when("entering '18'", function() { + return input.mashKeys("18"); + }); + return then("input value should assume 2000s and prefix accordingly", function() { + return expect(input).toHaveValue("09/09/2018"); + }); + }); + scenario("User enters a year greater than 29", function() { + given("an input with the month and day already entered", function() { + return input.mashKeys("0909"); + }); + when("entering '64'", function() { + return input.mashKeys("64"); + }); + return then("input value should assume 1900s and prefix accordingly", function() { + return expect(input).toHaveValue("09/09/1964"); + }); + }); + scenario("user starts to enter a year beginning with 20", function() { + given("an input with the month and day already entered", function() {}); + input.mashKeys("0909"); + when("entering '20'", function() {}); + input.mashKeys("20"); + return then("input value should just reflect the '20' that was entered", function() { + return expect(input).toHaveValue("09/09/20 "); + }); + }); + return scenario("user starts to enter a year beginning with 19", function() { + given("an input with the month and day already entered", function() {}); + input.mashKeys("0909"); + when("entering '19'", function() {}); + input.mashKeys("19"); + return then("input value should just reflect the '19' that was entered", function() { + return expect(input).toHaveValue("09/09/19 "); + }); + }); + }); + return story("User interacts with an input with 'mmyyyy' predefined autocomplete settings", function() { + beforeEach(function() { + return input.mask("mm/yyyy", { + placeholder: " ", + autocomplete: $.mask.autocomplete_predefined["mmyyyy"] + }); + }); + scenario("User enters an invalid month", function() { + given("an empty input", function() {}); + when("entering an invalid month", function() { + return input.mashKeys("13"); + }); + return then("input should reset to blank", function() { + return expect(input).toHaveValue(" / "); + }); + }); + scenario("User enters a single digit followed by slash", function() { + given("an empty input", function() {}); + when("entering a single digit followed by slash", function() { + return input.mashKeys("4/"); + }); + return then("input value should have month zero padded", function() { + return expect(input).toHaveValue("04/ "); + }); + }); + scenario("User enters a valid two digit month", function() { + given("an empty input", function() {}); + when("entering '09'", function() { + return input.mashKeys("09"); + }); + return then("input value should advance to year field", function() { + return expect(input).toHaveValue("09/ "); + }); + }); + scenario("User enters a year less than 30, not 19 or 20", function() { + given("an input with the month already entered", function() { + return input.mashKeys("09"); + }); + when("entering '18'", function() { + return input.mashKeys("18"); + }); + return then("input value should assume 2000s and prefix accordingly", function() { + return expect(input).toHaveValue("09/2018"); + }); + }); + scenario("user starts to enter a year beginning with 20", function() { + given("an input with the month already entered", function() {}); + input.mashKeys("09"); + when("entering '20'", function() {}); + input.mashKeys("20"); + return then("input value should just reflect the '20' that was entered", function() { + return expect(input).toHaveValue("09/20 "); + }); + }); + scenario("user starts to enter a year beginning with 19", function() { + given("an input with the month already entered", function() {}); + input.mashKeys("09"); + when("entering '19'", function() {}); + input.mashKeys("19"); + return then("input value should just reflect the '19' that was entered", function() { + return expect(input).toHaveValue("09/19 "); + }); + }); + return scenario("User enters a year greater than 29", function() { + given("an input with the month already entered", function() { + return input.mashKeys("09"); + }); + when("entering '64'", function() { + return input.mashKeys("64"); + }); + return then("input value should assume 1900s and prefix accordingly", function() { + return expect(input).toHaveValue("09/1964"); + }); + }); + }); +}); \ No newline at end of file diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index b28a114..209e79c 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -13,7 +13,10 @@ definitions: { '9': "[0-9]", 'a': "[A-Za-z]", - '*': "[A-Za-z0-9]" + '*': "[A-Za-z0-9]", + 'm': "[0-9/]", + 'd': "[0-9/]", + 'y': "[0-9/]" }, dataName:"rawMaskFn", autocomplete_predefined: { @@ -42,6 +45,26 @@ pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } + ], + + + 'mmyyyy': [ + { + pattern: /^(\d)\//, + replacement: "0$1" + }, + { + pattern: /^1[3-9]/, + replacement: " " + }, + { + pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, + { + pattern: /^(\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } ] From 492b87cdf9e64437258acae6bec029b25eba7f82 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 13 Aug 2011 15:30:22 -0500 Subject: [PATCH 14/33] Rewrote coffescript spec to use plain Jasmine, to avoid keyword conficts. --- spec/AutoComplete.Spec.coffee | 228 ++++++++++------------------------ spec/AutoComplete.Spec.js | 194 +++++++---------------------- 2 files changed, 114 insertions(+), 308 deletions(-) diff --git a/spec/AutoComplete.Spec.coffee b/spec/AutoComplete.Spec.coffee index 8631a20..bf2c749 100644 --- a/spec/AutoComplete.Spec.coffee +++ b/spec/AutoComplete.Spec.coffee @@ -4,171 +4,75 @@ feature "Autocompletion", -> input.mask "mm/dd/yyyy", placeholder: " " autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] - - scenario "User enters an invalid month", -> - given "an empty input", -> - - when_ "entering an invalid month", -> - input.mashKeys "13" - - then_ "input should reset to blank", -> - expect(input).toHaveValue " / / " - - scenario "User enters a single digit followed by slash", -> - given "an empty input", -> - - when_ "entering a single digit followed by slash", -> - input.mashKeys "4/" - - then_ "input value should have month zero padded", -> - expect(input).toHaveValue "04/ / " - - scenario "User enters a valid two digit month", -> - given "an empty input", -> - - when_ "entering '09'", -> - input.mashKeys "09" - - then_ "input value should advance to day fields", -> - expect(input).toHaveValue "09/ / " - - scenario "User enters an invalid day of month", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '41'", -> - input.mashKeys "41" - - then_ "input value should reset to blank day of month", -> - expect(input).toHaveValue "09/ / " - - when_ "entering '36'", -> - input.mashKeys "36" - - then_ "input value should reset to blank day of month", -> - expect(input).toHaveValue "09/ / " - - scenario "User enters single digit day of month, followed by slash", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '9/'", -> - input.mashKeys "9/" - - then_ "input value should zero pad day of month", -> - expect(input).toHaveValue "09/09/ " - - scenario "User enters a year less than 30, not 19 or 20", -> - given "an input with the month and day already entered", -> - input.mashKeys "0909" - - when_ "entering '18'", -> - input.mashKeys "18" - - then_ "input value should assume 2000s and prefix accordingly", -> - expect(input).toHaveValue "09/09/2018" - - scenario "User enters a year greater than 29", -> - given "an input with the month and day already entered", -> - input.mashKeys "0909" - - when_ "entering '64'", -> - input.mashKeys "64" - - then_ "input value should assume 1900s and prefix accordingly", -> - expect(input).toHaveValue "09/09/1964" - - scenario "user starts to enter a year beginning with 20", -> - given "an input with the month and day already entered", -> - input.mashKeys "0909" - - when_ "entering '20'", -> - input.mashKeys "20" - - then_ "input value should just reflect the '20' that was entered", -> - expect(input).toHaveValue "09/09/20 " - - scenario "user starts to enter a year beginning with 19", -> - given "an input with the month and day already entered", -> - input.mashKeys "0909" - - when_ "entering '19'", -> - input.mashKeys "19" - - then_ "input value should just reflect the '19' that was entered", -> - expect(input).toHaveValue "09/09/19 " - + + it "Should reset the input to blank if the user inputs an invalid month", -> + input.mashKeys "13" + expect(input).toHaveValue " / / " + + it "Should zero pad a single digit month if followed by a slash", -> + input.mashKeys "4/" + expect(input).toHaveValue "04/ / " + + it "Should advance to the day fields if user enters a valid two digit month", -> + input.mashKeys "09" + expect(input).toHaveValue "09/ / " + + it "Should reset to blank day of month if any invalid day of month is entered", -> + input.mashKeys "0941" + expect(input).toHaveValue "09/ / " + + it "Should zero pad the day of month if a single digit day of month is followed by a slash", -> + input.mashKeys "099/" + expect(input).toHaveValue "09/09/ " + + it "Should consider a two digit year less than 30, but not 19 or 20, to be 21st century and prefix with '20'", -> + input.mashKeys "090918" + expect(input).toHaveValue "09/09/2018" + + it "Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", -> + input.mashKeys "090964" + expect(input).toHaveValue "09/09/1964" + + it "Should not prefix the year if the first two digits entered are '20'", -> + input.mashKeys "090920" + expect(input).toHaveValue "09/09/20 " + + it "Should not prefix the year if the first two digits entered are '19'", -> + input.mashKeys "090919" + expect(input).toHaveValue "09/09/19 " + story "User interacts with an input with 'mmyyyy' predefined autocomplete settings", -> beforeEach -> input.mask "mm/yyyy", placeholder: " " autocomplete: $.mask.autocomplete_predefined["mmyyyy"] - - scenario "User enters an invalid month", -> - given "an empty input", -> - - when_ "entering an invalid month", -> - input.mashKeys "13" - - then_ "input should reset to blank", -> - expect(input).toHaveValue " / " - - scenario "User enters a single digit followed by slash", -> - given "an empty input", -> - - when_ "entering a single digit followed by slash", -> - input.mashKeys "4/" - - then_ "input value should have month zero padded", -> - expect(input).toHaveValue "04/ " - - scenario "User enters a valid two digit month", -> - given "an empty input", -> - - when_ "entering '09'", -> - input.mashKeys "09" - - then_ "input value should advance to year field", -> - expect(input).toHaveValue "09/ " - - scenario "User enters a year less than 30, not 19 or 20", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '18'", -> - input.mashKeys "18" - - then_ "input value should assume 2000s and prefix accordingly", -> - expect(input).toHaveValue "09/2018" - - scenario "user starts to enter a year beginning with 20", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '20'", -> - input.mashKeys "20" - - then_ "input value should just reflect the '20' that was entered", -> - expect(input).toHaveValue "09/20 " - - scenario "user starts to enter a year beginning with 19", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '19'", -> - input.mashKeys "19" - - then_ "input value should just reflect the '19' that was entered", -> - expect(input).toHaveValue "09/19 " - - - scenario "User enters a year greater than 29", -> - given "an input with the month already entered", -> - input.mashKeys "09" - - when_ "entering '64'", -> - input.mashKeys "64" - - then_ "input value should assume 1900s and prefix accordingly", -> - expect(input).toHaveValue "09/1964" + + it "Should reset the input to blank if the user inputs an invalid month", -> + input.mashKeys "13" + expect(input).toHaveValue " / " + + it "Should zero pad a single digit month if followed by a slash", -> + input.mashKeys "4/" + expect(input).toHaveValue "04/ " + + it "Should advance to the year field if user enters a valid two digit month", -> + input.mashKeys "09" + expect(input).toHaveValue "09/ " + + it "Should consider a two digit digit less than 30, but not 19 or 20, to be 21st century and prefix with '20'", -> + input.mashKeys "0918" + expect(input).toHaveValue "09/2018" + + it "Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", -> + input.mashKeys "0964" + expect(input).toHaveValue "09/1964" + + it "Should not prefix the year if the first two digits entered are '20'", -> + input.mashKeys "0920" + expect(input).toHaveValue "09/20 " + + it "Should not prefix the year if the first two digits entered are '19'", -> + input.mashKeys "0919" + expect(input).toHaveValue "09/19 " + diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js index a1899fd..f406eef 100644 --- a/spec/AutoComplete.Spec.js +++ b/spec/AutoComplete.Spec.js @@ -6,100 +6,41 @@ feature("Autocompletion", function() { autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] }); }); - scenario("User enters an invalid month", function() { - given("an empty input", function() {}); - when("entering an invalid month", function() { - return input.mashKeys("13"); - }); - return then("input should reset to blank", function() { - return expect(input).toHaveValue(" / / "); - }); + it("Should reset the input to blank if the user inputs an invalid month", function() { + input.mashKeys("13"); + return expect(input).toHaveValue(" / / "); }); - scenario("User enters a single digit followed by slash", function() { - given("an empty input", function() {}); - when("entering a single digit followed by slash", function() { - return input.mashKeys("4/"); - }); - return then("input value should have month zero padded", function() { - return expect(input).toHaveValue("04/ / "); - }); + it("Should zero pad a single digit month if followed by a slash", function() { + input.mashKeys("4/"); + return expect(input).toHaveValue("04/ / "); }); - scenario("User enters a valid two digit month", function() { - given("an empty input", function() {}); - when("entering '09'", function() { - return input.mashKeys("09"); - }); - return then("input value should advance to day fields", function() { - return expect(input).toHaveValue("09/ / "); - }); + it("Should advance to the day fields if user enters a valid two digit month", function() { + input.mashKeys("09"); + return expect(input).toHaveValue("09/ / "); }); - scenario("User enters an invalid day of month", function() { - given("an input with the month already entered", function() { - return input.mashKeys("09"); - }); - when("entering '41'", function() { - return input.mashKeys("41"); - }); - then("input value should reset to blank day of month", function() { - return expect(input).toHaveValue("09/ / "); - }); - when("entering '36'", function() { - return input.mashKeys("36"); - }); - return then("input value should reset to blank day of month", function() { - return expect(input).toHaveValue("09/ / "); - }); + it("Should reset to blank day of month if any invalid day of month is entered", function() { + input.mashKeys("0941"); + return expect(input).toHaveValue("09/ / "); }); - scenario("User enters single digit day of month, followed by slash", function() { - given("an input with the month already entered", function() { - return input.mashKeys("09"); - }); - when("entering '9/'", function() { - return input.mashKeys("9/"); - }); - return then("input value should zero pad day of month", function() { - return expect(input).toHaveValue("09/09/ "); - }); + it("Should zero pad the day of month if a single digit day of month is followed by a slash", function() { + input.mashKeys("099/"); + return expect(input).toHaveValue("09/09/ "); }); - scenario("User enters a year less than 30, not 19 or 20", function() { - given("an input with the month and day already entered", function() { - return input.mashKeys("0909"); - }); - when("entering '18'", function() { - return input.mashKeys("18"); - }); - return then("input value should assume 2000s and prefix accordingly", function() { - return expect(input).toHaveValue("09/09/2018"); - }); + it("Should consider a two digit year less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { + input.mashKeys("090918"); + return expect(input).toHaveValue("09/09/2018"); }); - scenario("User enters a year greater than 29", function() { - given("an input with the month and day already entered", function() { - return input.mashKeys("0909"); - }); - when("entering '64'", function() { - return input.mashKeys("64"); - }); - return then("input value should assume 1900s and prefix accordingly", function() { - return expect(input).toHaveValue("09/09/1964"); - }); + it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { + input.mashKeys("090964"); + return expect(input).toHaveValue("09/09/1964"); }); - scenario("user starts to enter a year beginning with 20", function() { - given("an input with the month and day already entered", function() {}); - input.mashKeys("0909"); - when("entering '20'", function() {}); - input.mashKeys("20"); - return then("input value should just reflect the '20' that was entered", function() { - return expect(input).toHaveValue("09/09/20 "); - }); + it("Should not prefix the year if the first two digits entered are '20'", function() { + input.mashKeys("090920"); + return expect(input).toHaveValue("09/09/20 "); }); - return scenario("user starts to enter a year beginning with 19", function() { - given("an input with the month and day already entered", function() {}); - input.mashKeys("0909"); - when("entering '19'", function() {}); - input.mashKeys("19"); - return then("input value should just reflect the '19' that was entered", function() { - return expect(input).toHaveValue("09/09/19 "); - }); + return it("Should not prefix the year if the first two digits entered are '19'", function() { + input.mashKeys("090919"); + return expect(input).toHaveValue("09/09/19 "); }); }); return story("User interacts with an input with 'mmyyyy' predefined autocomplete settings", function() { @@ -109,72 +50,33 @@ feature("Autocompletion", function() { autocomplete: $.mask.autocomplete_predefined["mmyyyy"] }); }); - scenario("User enters an invalid month", function() { - given("an empty input", function() {}); - when("entering an invalid month", function() { - return input.mashKeys("13"); - }); - return then("input should reset to blank", function() { - return expect(input).toHaveValue(" / "); - }); + it("Should reset the input to blank if the user inputs an invalid month", function() { + input.mashKeys("13"); + return expect(input).toHaveValue(" / "); }); - scenario("User enters a single digit followed by slash", function() { - given("an empty input", function() {}); - when("entering a single digit followed by slash", function() { - return input.mashKeys("4/"); - }); - return then("input value should have month zero padded", function() { - return expect(input).toHaveValue("04/ "); - }); + it("Should zero pad a single digit month if followed by a slash", function() { + input.mashKeys("4/"); + return expect(input).toHaveValue("04/ "); }); - scenario("User enters a valid two digit month", function() { - given("an empty input", function() {}); - when("entering '09'", function() { - return input.mashKeys("09"); - }); - return then("input value should advance to year field", function() { - return expect(input).toHaveValue("09/ "); - }); + it("Should advance to the year field if user enters a valid two digit month", function() { + input.mashKeys("09"); + return expect(input).toHaveValue("09/ "); }); - scenario("User enters a year less than 30, not 19 or 20", function() { - given("an input with the month already entered", function() { - return input.mashKeys("09"); - }); - when("entering '18'", function() { - return input.mashKeys("18"); - }); - return then("input value should assume 2000s and prefix accordingly", function() { - return expect(input).toHaveValue("09/2018"); - }); + it("Should consider a two digit digit less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { + input.mashKeys("0918"); + return expect(input).toHaveValue("09/2018"); }); - scenario("user starts to enter a year beginning with 20", function() { - given("an input with the month already entered", function() {}); - input.mashKeys("09"); - when("entering '20'", function() {}); - input.mashKeys("20"); - return then("input value should just reflect the '20' that was entered", function() { - return expect(input).toHaveValue("09/20 "); - }); + it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { + input.mashKeys("0964"); + return expect(input).toHaveValue("09/1964"); }); - scenario("user starts to enter a year beginning with 19", function() { - given("an input with the month already entered", function() {}); - input.mashKeys("09"); - when("entering '19'", function() {}); - input.mashKeys("19"); - return then("input value should just reflect the '19' that was entered", function() { - return expect(input).toHaveValue("09/19 "); - }); + it("Should not prefix the year if the first two digits entered are '20'", function() { + input.mashKeys("0920"); + return expect(input).toHaveValue("09/20 "); }); - return scenario("User enters a year greater than 29", function() { - given("an input with the month already entered", function() { - return input.mashKeys("09"); - }); - when("entering '64'", function() { - return input.mashKeys("64"); - }); - return then("input value should assume 1900s and prefix accordingly", function() { - return expect(input).toHaveValue("09/1964"); - }); + return it("Should not prefix the year if the first two digits entered are '19'", function() { + input.mashKeys("0919"); + return expect(input).toHaveValue("09/19 "); }); }); }); \ No newline at end of file From 1a8adaff94721f88d0a55e26021727edc7cb9800 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 09:21:36 -0500 Subject: [PATCH 15/33] ported plugin to CoffeeScript; began work on progressive-reveal --- demo/index.html | 4 +- spec/Raw.Spec.js | 16 +- spec/SpecRunner.html | 2 +- spec/lib/jasmine-species/jasmine-grammar.js | 24 +- src/jquery.maskedinput.mod.coffee | 281 +++++++++++++++ src/jquery.maskedinput.mod.js | 362 ++++++++++++++++++++ 6 files changed, 677 insertions(+), 12 deletions(-) create mode 100644 src/jquery.maskedinput.mod.coffee create mode 100644 src/jquery.maskedinput.mod.js diff --git a/demo/index.html b/demo/index.html index d3f7688..6a79184 100644 --- a/demo/index.html +++ b/demo/index.html @@ -2,14 +2,14 @@ jQuery Mask Test - + - + diff --git a/spec/lib/jasmine-species/jasmine-grammar.js b/spec/lib/jasmine-species/jasmine-grammar.js index 9f4f734..2645f11 100644 --- a/spec/lib/jasmine-species/jasmine-grammar.js +++ b/spec/lib/jasmine-species/jasmine-grammar.js @@ -84,13 +84,35 @@ jasmine.grammar.GWT = { return this._addStepToCurrentSpec('Then ' + desc, func); }, + /** + * Defines a "whilst" step as a runs block that marks the interesting event in a GWT chain + */ + whilst: function(desc, func) { + return this._addStepToCurrentSpec('Whilst ' + desc, func); + }, + + /** + * Defines a "hence" step as a runs block that marks the conclusion of a Given, Whilst, Hence construct + */ + hence: function(desc, func) { + return this._addStepToCurrentSpec('Hence ' + desc, func); + }, + +/** * Defines an "and" step as a runs block that is a continuation from a "then" statement */ and: function(desc, func) { return this._addStepToCurrentSpec('And ' + desc, func); }, +/** + * Defines an "likewise" step as a runs block that is a continuation from a "then" statement + */ + likewise: function(desc, func) { + return this._addStepToCurrentSpec('Likewise ' + desc, func); + }, + /** * Defines a "but" step as a runs block that is a continuation from a "then" statement */ @@ -230,4 +252,4 @@ jasmine.grammar.Meta = { */ jasmine.grammar.getEnv = function() { return jasmine.grammar._currentEnv = jasmine.grammar._currentEnv || jasmine.getEnv(); -}; \ No newline at end of file +}; diff --git a/src/jquery.maskedinput.mod.coffee b/src/jquery.maskedinput.mod.coffee new file mode 100644 index 0000000..605b135 --- /dev/null +++ b/src/jquery.maskedinput.mod.coffee @@ -0,0 +1,281 @@ +# Masked Input plugin for jQuery - CoffeeScript version +# +# based upon Masked Input plugin for jQuery by Josh Bush +# (http://digitalbush.com/projects/masked-input-plugin/) + +$ = jQuery + +pasteEventName = (if $.browser.msie then 'paste' else 'input') + '.mask' +iPhone = window.orientation? + +$.mask = + # Predefined character definitions + definitions: + '9': "[0-9]" + 'a': "[A-Za-z]" + '*': "[A-Za-z0-9]" + 'm': "[0-9/]" + 'd': "[0-9/]" + 'y': "[0-9/]" + + dataName: "rawMaskFn" + autocomplete_predefined: + 'mmddyyyy': [ + { pattern: /^(\d)\//, replacement: "0$1" } + { pattern: /^1[3-9]/, replacement: " " } + { pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, replacement: "$1 " } + { pattern: /^(\d\d.)(\d\/)./, replacement: "$1" + "0" + "$2" } + { pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } + { pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } + ] + + + 'mmyyyy': [ + { pattern: /^(\d)\//, replacement: "0$1" } + { pattern: /^1[3-9]/, replacement: " " } + { pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } + { pattern: /^(\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } + ] + +$.fn.extend + caret: (begin, end) -> + if this.length == 0 then return + if typeof begin == 'number' + end = if typeof end == 'number' then end else begin + return this.each -> + 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 and 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: -> return this.trigger "unmask" + + mask: (mask, settings) -> + if not mask and this.length > 0 + input = $ this[0] + return (input.data $.mask.dataName)() + + settings = $.extend { + placeholder: "_" + completed: null + autocomplete: [] + progessive_reveal: false + }, settings + + defs = $.mask.definitions + tests = [] + partialPosition = mask.length + firstNonMaskPos = null + len = mask.length + + $.each (mask.split ""), (i, c) -> + if c == '?' + len-- + partialPosition = i + else if defs[c] + tests.push (RegExp defs[c]) + firstNonMaskPos = tests.length - 1 if firstNonMaskPos == null + else + tests.push null + + (this.trigger "unmask").each -> + input = $ this + buffer = $.map (mask.split ""), (c, i) -> + if c != '?' + return if defs[c] then settings.placeholder else c + focusText = input.val() + + seekNext = (pos) -> + while ++pos <= len and not tests[pos] + null + pos + + seekPrev = (pos) -> + ivala = input.val().split "" + while --pos >= 0 and not tests[pos] + ivala[pos] = settings.placeholder + ivala[pos] = settings.placeholder + input.val(ivala.join "") + pos + + shiftL = (begin, end) -> + return null if begin < 0 + + j = seekNext end + + for i in [begin...len] + if tests[i] + if j < len and 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 + + shiftR = (pos) -> + c = settings.placeholder + for i in [pos...len] + if tests[i] + j = seekNext i + t = buffer[i] + buffer[i] = c + if j < len and tests[j].test t + c = t + else + break + + keydownEvent = (e) -> + k = e.which + + # backspace, delete, and escape get special treatment + if k == 8 or k == 46 or (iPhone and k == 127) + {begin, end} = input.caret() + console.dir input.caret() + + if end == begin + begin = if k != 46 then seekPrev begin else end = seekNext begin - 1 + end = seekNext end if k == 46 + + clearBuffer begin, end + shiftL begin, end - 1 + + false + else if k == 27 + # escape + input.val focusText + input.caret 0, checkVal() + false + + keypressEvent = (e) -> + k = e.which + i = 0 + startLength = input.mask().length + + pos = input.caret() + + if e.ctrlKey or e.altKey or e.metaKey or k < 32 + true + else if k + if pos.end != pos.begin + 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 + input.caret next + + if settings.autocomplete.length > 0 + for i in [0...settings.autocomplete.length] + {pattern, replacement} = settings.autocomplete[i] + input.val input.val().replace pattern, replacement + clearBuffer 0, RegExp.lastMatch.length + input.caret checkVal true + + next += input.mask().length - startLength - 1 + if settings.completed and next >= len + settings.completed.call input + false + + clearBuffer = (start, end) -> + for i in [start...end] when i < len + buffer[i] = settings.placeholder if tests[i]? + + wbCount = 0 + + writeBuffer = -> + if settings.progressive_reveal + i = 0 + tmp_array = [] + ival = input.val() + + until not ival[i]? or ival[i] == buffer[i] == settings.placeholder + tmp_array.push ival[i++] + + while buffer[i] and buffer[i] != settings.placeholder + tmp_array.push buffer[i++] + + (input.val tmp_array.join "").val() + else + (input.val buffer.join "").val() + + checkVal = (allow) -> + test = input.val() + lastMatch = -1 + pos = 0 + + for i in [0...len] + if tests[i] + buffer[i] = settings.placeholder + while pos++ < test.length + c = test.charAt pos - 1 + if tests[pos - 1] and tests[pos - 1].test c + buffer[i] = c + lastMatch = i + break + if pos > test.length + break + else if buffer[i] == test.charAt pos and i != partialPosition + pos++ + lastMatch = i + + if not allow and lastMatch + 1 < partialPosition + input.val "" + clearBuffer 0, len + else if allow or lastMatch + 1 >= partialPosition + writeBuffer() + input.val input.val().substring 0, lastMatch + 1 if not allow + if partialPosition then i else firstNonMaskPos + + + input.data $.mask.dataName, -> + ($.map buffer, (c, i) -> + if tests[i] and c != settings.placeholder then c else null).join '' + + unless input.attr "readonly" + input.one "unmask", -> + input.unbind ".mask" + input.removeData $.mask.dataName + .bind "focus.mask", -> + focusText = input.val() + pos = checkVal() + writeBuffer() + moveCaret = -> + if pos == mask.length + input.caret 0, pos + else + input.caret pos + if $.browser.msie then moveCaret else do -> setTimeout moveCaret, 0 + .bind( "blur.mask", -> + checkVal() + if input.val() != focusText + input.change() + ).bind( "keydown.mask", keydownEvent) + .bind( "keypress.mask", keypressEvent ) + .bind pasteEventName, -> + setTimeout (() -> + input.caret checkVal true), 0 + + checkVal() # perform initial check for existing values diff --git a/src/jquery.maskedinput.mod.js b/src/jquery.maskedinput.mod.js new file mode 100644 index 0000000..8f67d52 --- /dev/null +++ b/src/jquery.maskedinput.mod.js @@ -0,0 +1,362 @@ +var $, iPhone, pasteEventName; +$ = jQuery; +pasteEventName = ($.browser.msie ? 'paste' : 'input') + '.mask'; +iPhone = window.orientation != null; +$.mask = { + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + '*': "[A-Za-z0-9]", + 'm': "[0-9/]", + 'd': "[0-9/]", + 'y': "[0-9/]" + }, + dataName: "rawMaskFn", + autocomplete_predefined: { + 'mmddyyyy': [ + { + pattern: /^(\d)\//, + replacement: "0$1" + }, { + pattern: /^1[3-9]/, + replacement: " " + }, { + pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, + replacement: "$1 " + }, { + pattern: /^(\d\d.)(\d\/)./, + replacement: "$1" + "0" + "$2" + }, { + pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } + ], + 'mmyyyy': [ + { + pattern: /^(\d)\//, + replacement: "0$1" + }, { + pattern: /^1[3-9]/, + replacement: " " + }, { + pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } + ] + } +}; +$.fn.extend({ + caret: function(begin, end) { + var range; + if (this.length === 0) { + return; + } + if (typeof begin === 'number') { + end = typeof end === 'number' ? end : begin; + return this.each(function() { + var range; + if (this.setSelectionRange) { + return this.setSelectionRange(begin, end); + } else if (this.createTextRange) { + range = this.createTextRange; + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + return 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 defs, firstNonMaskPos, input, len, partialPosition, tests; + if (!mask && this.length > 0) { + input = $(this[0]); + return (input.data($.mask.dataName))(); + } + settings = $.extend({ + placeholder: "_", + completed: null, + autocomplete: [], + progessive_reveal: false + }, settings); + defs = $.mask.definitions; + tests = []; + partialPosition = mask.length; + firstNonMaskPos = null; + len = mask.length; + $.each(mask.split(""), function(i, c) { + if (c === '?') { + len--; + return partialPosition = i; + } else if (defs[c]) { + tests.push(RegExp(defs[c])); + if (firstNonMaskPos === null) { + return firstNonMaskPos = tests.length - 1; + } + } else { + return tests.push(null); + } + }); + return (this.trigger("unmask")).each(function() { + var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; + input = $(this); + buffer = $.map(mask.split(""), function(c, i) { + if (c !== '?') { + if (defs[c]) { + return settings.placeholder; + } else { + return c; + } + } + }); + focusText = input.val(); + seekNext = function(pos) { + while (++pos <= len && !tests[pos]) { + null; + } + return pos; + }; + seekPrev = function(pos) { + var ivala; + ivala = input.val().split(""); + while (--pos >= 0 && !tests[pos]) { + ivala[pos] = settings.placeholder; + } + ivala[pos] = settings.placeholder; + input.val(ivala.join("")); + return pos; + }; + shiftL = function(begin, end) { + var i, j; + if (begin < 0) { + return null; + } + j = seekNext(end); + for (i = begin; begin <= len ? i < len : i > len; begin <= len ? i++ : 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(); + return input.caret(Math.max(firstNonMaskPos, begin)); + }; + shiftR = function(pos) { + var c, i, j, t, _results; + c = settings.placeholder; + _results = []; + for (i = pos; pos <= len ? i < len : i > len; pos <= len ? i++ : i--) { + if (tests[i]) { + j = seekNext(i); + t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) { + c = t; + } else { + break; + } + } + } + return _results; + }; + keydownEvent = function(e) { + var begin, end, k, _ref; + k = e.which; + if (k === 8 || k === 46 || (iPhone && k === 127)) { + _ref = input.caret(), begin = _ref.begin, end = _ref.end; + console.dir(input.caret()); + if (end === begin) { + begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); + if (k === 46) { + end = seekNext(end); + } + } + clearBuffer(begin, end); + shiftL(begin, end - 1); + return false; + } else if (k === 27) { + input.val(focusText); + input.caret(0, checkVal()); + return false; + } + }; + keypressEvent = function(e) { + var c, i, k, next, p, pattern, pos, replacement, startLength, _ref, _ref2; + k = e.which; + i = 0; + startLength = input.mask().length; + pos = input.caret(); + if (e.ctrlKey || e.altKey || e.metaKey || k < 32) { + return true; + } else if (k) { + if (pos.end !== pos.begin) { + 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); + input.caret(next); + if (settings.autocomplete.length > 0) { + for (i = 0, _ref = settings.autocomplete.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { + _ref2 = settings.autocomplete[i], pattern = _ref2.pattern, replacement = _ref2.replacement; + input.val(input.val().replace(pattern, replacement)); + clearBuffer(0, RegExp.lastMatch.length); + input.caret(checkVal(true)); + } + } + next += input.mask().length - startLength - 1; + if (settings.completed && next >= len) { + settings.completed.call(input); + } + } + } + return false; + } + }; + clearBuffer = function(start, end) { + var i, _results; + _results = []; + for (i = start; start <= end ? i < end : i > end; start <= end ? i++ : i--) { + if (i < len) { + _results.push(tests[i] != null ? buffer[i] = settings.placeholder : void 0); + } + } + return _results; + }; + wbCount = 0; + writeBuffer = function() { + var i, ival, tmp_array, _ref; + if (settings.progressive_reveal) { + i = 0; + tmp_array = []; + ival = input.val(); + while (!(!(ival[i] != null) || (ival[i] === (_ref = buffer[i]) && _ref === settings.placeholder))) { + tmp_array.push(ival[i++]); + } + while (buffer[i] && buffer[i] !== settings.placeholder) { + tmp_array.push(buffer[i++]); + } + return (input.val(tmp_array.join(""))).val(); + } else { + return (input.val(buffer.join(""))).val(); + } + }; + checkVal = function(allow) { + var c, i, lastMatch, pos, test; + test = input.val(); + lastMatch = -1; + pos = 0; + for (i = 0; 0 <= len ? i < len : i > len; 0 <= len ? i++ : i--) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + c = test.charAt(pos - 1); + if (tests[pos - 1] && tests[pos - 1].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 && lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else if (allow || lastMatch + 1 >= partialPosition) { + writeBuffer(); + if (!allow) { + input.val(input.val().substring(0, lastMatch + 1)); + } + } + if (partialPosition) { + return i; + } else { + return firstNonMaskPos; + } + }; + input.data($.mask.dataName, function() { + return ($.map(buffer, function(c, i) { + if (tests[i] && c !== settings.placeholder) { + return c; + } else { + return null; + } + })).join(''); + }); + if (!input.attr("readonly")) { + input.one("unmask", function() { + input.unbind(".mask"); + return input.removeData($.mask.dataName); + }).bind("focus.mask", function() { + var moveCaret, pos; + focusText = input.val(); + pos = checkVal(); + writeBuffer(); + moveCaret = function() { + if (pos === mask.length) { + return input.caret(0, pos); + } else { + return input.caret(pos); + } + }; + if ($.browser.msie) { + return moveCaret; + } else { + return (function() { + return setTimeout(moveCaret, 0); + })(); + } + }).bind("blur.mask", function() { + checkVal(); + if (input.val() !== focusText) { + return input.change(); + } + }).bind("keydown.mask", keydownEvent).bind("keypress.mask", keypressEvent).bind(pasteEventName, function() { + return setTimeout((function() { + return input.caret(checkVal(true)); + }), 0); + }); + } + return checkVal(); + }); + } +}); \ No newline at end of file From 22f64108240066007ad73b7ed2b3a7c8cf87e675 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 10:39:52 -0500 Subject: [PATCH 16/33] added leading slash supression to predefined masks and specs --- demo/index.html | 31 +------ spec/AutoComplete.Spec.coffee | 12 +++ spec/AutoComplete.Spec.js | 168 ++++++++++++++++++---------------- src/jquery.maskedinput.js | 16 +++- 4 files changed, 118 insertions(+), 109 deletions(-) diff --git a/demo/index.html b/demo/index.html index 1ba8294..3b349eb 100644 --- a/demo/index.html +++ b/demo/index.html @@ -10,36 +10,7 @@ $.mask.definitions['d'] = "[0-9\/]"; $.mask.definitions['y'] = "[0-9\/]"; $("#date").mask("mm/dd/yyyy",{ placeholder: " ", - autocomplete:[ - { - pattern: /^1\//, - replacement: "01" - },{ - pattern: /^([2-9])./, - replacement: "0$1" - }, - { - pattern: /^1[3-9]/, - replacement: " " - }, - { - pattern: /^(\d\d.)([4-9])./, - replacement: "$1" + "0" + "$2" - }, - { - pattern: /^(\d\d.)([1-3]\/)./, - replacement: "$1" + "0" + "$2" - }, - { - pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, - replacement: "$1" + "20" + "$2" - }, - { - pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, - replacement: "$1" + "19" + "$2" - } - ] - }); + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] }); $("#phone").mask("(999) 999-9999"); $("#phoneExt").mask("(999) 999-9999? x99999"); $("#iphone").mask("+33 999 999 999"); diff --git a/spec/AutoComplete.Spec.coffee b/spec/AutoComplete.Spec.coffee index bf2c749..151f323 100644 --- a/spec/AutoComplete.Spec.coffee +++ b/spec/AutoComplete.Spec.coffee @@ -9,6 +9,10 @@ feature "Autocompletion", -> input.mashKeys "13" expect(input).toHaveValue " / / " + it "Should reset the input to blank if the user inputs a slash as the first month character", -> + input.mashKeys "/" + expect(input).toHaveValue " / / " + it "Should zero pad a single digit month if followed by a slash", -> input.mashKeys "4/" expect(input).toHaveValue "04/ / " @@ -17,6 +21,10 @@ feature "Autocompletion", -> input.mashKeys "09" expect(input).toHaveValue "09/ / " + it "Should reset to blank day of month if a slash is entered as the first charactor of day of month", -> + input.mashKeys "09/" + expect(input).toHaveValue "09/ / " + it "Should reset to blank day of month if any invalid day of month is entered", -> input.mashKeys "0941" expect(input).toHaveValue "09/ / " @@ -51,6 +59,10 @@ feature "Autocompletion", -> input.mashKeys "13" expect(input).toHaveValue " / " + it "Should reset the input to blank if the user inputs a slash as the first character", -> + input.mashKeys "/" + expect(input).toHaveValue " / " + it "Should zero pad a single digit month if followed by a slash", -> input.mashKeys "4/" expect(input).toHaveValue "04/ " diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js index f406eef..4f9ade5 100644 --- a/spec/AutoComplete.Spec.js +++ b/spec/AutoComplete.Spec.js @@ -1,82 +1,96 @@ -feature("Autocompletion", function() { - story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings", function() { - beforeEach(function() { - return input.mask("mm/dd/yyyy", { - placeholder: " ", - autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] +(function() { + feature("Autocompletion", function() { + story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings", function() { + beforeEach(function() { + return input.mask("mm/dd/yyyy", { + placeholder: " ", + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] + }); }); - }); - it("Should reset the input to blank if the user inputs an invalid month", function() { - input.mashKeys("13"); - return expect(input).toHaveValue(" / / "); - }); - it("Should zero pad a single digit month if followed by a slash", function() { - input.mashKeys("4/"); - return expect(input).toHaveValue("04/ / "); - }); - it("Should advance to the day fields if user enters a valid two digit month", function() { - input.mashKeys("09"); - return expect(input).toHaveValue("09/ / "); - }); - it("Should reset to blank day of month if any invalid day of month is entered", function() { - input.mashKeys("0941"); - return expect(input).toHaveValue("09/ / "); - }); - it("Should zero pad the day of month if a single digit day of month is followed by a slash", function() { - input.mashKeys("099/"); - return expect(input).toHaveValue("09/09/ "); - }); - it("Should consider a two digit year less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { - input.mashKeys("090918"); - return expect(input).toHaveValue("09/09/2018"); - }); - it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { - input.mashKeys("090964"); - return expect(input).toHaveValue("09/09/1964"); - }); - it("Should not prefix the year if the first two digits entered are '20'", function() { - input.mashKeys("090920"); - return expect(input).toHaveValue("09/09/20 "); - }); - return it("Should not prefix the year if the first two digits entered are '19'", function() { - input.mashKeys("090919"); - return expect(input).toHaveValue("09/09/19 "); - }); - }); - return story("User interacts with an input with 'mmyyyy' predefined autocomplete settings", function() { - beforeEach(function() { - return input.mask("mm/yyyy", { - placeholder: " ", - autocomplete: $.mask.autocomplete_predefined["mmyyyy"] + it("Should reset the input to blank if the user inputs an invalid month", function() { + input.mashKeys("13"); + return expect(input).toHaveValue(" / / "); + }); + it("Should reset the input to blank if the user inputs a slash as the first month character", function() { + input.mashKeys("/"); + return expect(input).toHaveValue(" / / "); + }); + it("Should zero pad a single digit month if followed by a slash", function() { + input.mashKeys("4/"); + return expect(input).toHaveValue("04/ / "); + }); + it("Should advance to the day fields if user enters a valid two digit month", function() { + input.mashKeys("09"); + return expect(input).toHaveValue("09/ / "); + }); + it("Should reset to blank day of month if a slash is entered as the first charactor of day of month", function() { + input.mashKeys("09/"); + return expect(input).toHaveValue("09/ / "); + }); + it("Should reset to blank day of month if any invalid day of month is entered", function() { + input.mashKeys("0941"); + return expect(input).toHaveValue("09/ / "); + }); + it("Should zero pad the day of month if a single digit day of month is followed by a slash", function() { + input.mashKeys("099/"); + return expect(input).toHaveValue("09/09/ "); + }); + it("Should consider a two digit year less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { + input.mashKeys("090918"); + return expect(input).toHaveValue("09/09/2018"); + }); + it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { + input.mashKeys("090964"); + return expect(input).toHaveValue("09/09/1964"); + }); + it("Should not prefix the year if the first two digits entered are '20'", function() { + input.mashKeys("090920"); + return expect(input).toHaveValue("09/09/20 "); + }); + return it("Should not prefix the year if the first two digits entered are '19'", function() { + input.mashKeys("090919"); + return expect(input).toHaveValue("09/09/19 "); }); }); - it("Should reset the input to blank if the user inputs an invalid month", function() { - input.mashKeys("13"); - return expect(input).toHaveValue(" / "); - }); - it("Should zero pad a single digit month if followed by a slash", function() { - input.mashKeys("4/"); - return expect(input).toHaveValue("04/ "); - }); - it("Should advance to the year field if user enters a valid two digit month", function() { - input.mashKeys("09"); - return expect(input).toHaveValue("09/ "); - }); - it("Should consider a two digit digit less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { - input.mashKeys("0918"); - return expect(input).toHaveValue("09/2018"); - }); - it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { - input.mashKeys("0964"); - return expect(input).toHaveValue("09/1964"); - }); - it("Should not prefix the year if the first two digits entered are '20'", function() { - input.mashKeys("0920"); - return expect(input).toHaveValue("09/20 "); - }); - return it("Should not prefix the year if the first two digits entered are '19'", function() { - input.mashKeys("0919"); - return expect(input).toHaveValue("09/19 "); + return story("User interacts with an input with 'mmyyyy' predefined autocomplete settings", function() { + beforeEach(function() { + return input.mask("mm/yyyy", { + placeholder: " ", + autocomplete: $.mask.autocomplete_predefined["mmyyyy"] + }); + }); + it("Should reset the input to blank if the user inputs an invalid month", function() { + input.mashKeys("13"); + return expect(input).toHaveValue(" / "); + }); + it("Should reset the input to blank if the user inputs a slash as the first character", function() { + input.mashKeys("/"); + return expect(input).toHaveValue(" / "); + }); + it("Should zero pad a single digit month if followed by a slash", function() { + input.mashKeys("4/"); + return expect(input).toHaveValue("04/ "); + }); + it("Should advance to the year field if user enters a valid two digit month", function() { + input.mashKeys("09"); + return expect(input).toHaveValue("09/ "); + }); + it("Should consider a two digit digit less than 30, but not 19 or 20, to be 21st century and prefix with '20'", function() { + input.mashKeys("0918"); + return expect(input).toHaveValue("09/2018"); + }); + it("Should consider a two digit year greater than 29 to be 20th century and prefix with '19'", function() { + input.mashKeys("0964"); + return expect(input).toHaveValue("09/1964"); + }); + it("Should not prefix the year if the first two digits entered are '20'", function() { + input.mashKeys("0920"); + return expect(input).toHaveValue("09/20 "); + }); + return it("Should not prefix the year if the first two digits entered are '19'", function() { + input.mashKeys("0919"); + return expect(input).toHaveValue("09/19 "); + }); }); }); -}); \ No newline at end of file +}).call(this); diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index 209e79c..fe42da0 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -16,12 +16,16 @@ '*': "[A-Za-z0-9]", 'm': "[0-9/]", 'd': "[0-9/]", - 'y': "[0-9/]" + 'y': "[0-9]" }, dataName:"rawMaskFn", autocomplete_predefined: { 'mmddyyyy': [ - { + { + pattern: /^\//, + replacement: " " + }, + { pattern: /^(\d)\//, replacement: "0$1" }, @@ -33,6 +37,10 @@ pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, replacement: "$1 " }, + { + pattern: /^(\d\d.)\/ /, + replacement: "$1 " + }, { pattern: /^(\d\d.)(\d\/)./, replacement: "$1" + "0" + "$2" @@ -49,6 +57,10 @@ 'mmyyyy': [ + { + pattern: /^\//, + replacement: " " + }, { pattern: /^(\d)\//, replacement: "0$1" From d80a94af97910c5d11f2065af48c0d2fc6260c29 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 17:10:51 -0500 Subject: [PATCH 17/33] fixed a couple of autocomplete regexes --- src/jquery.maskedinput.mod.coffee | 6 +- src/jquery.maskedinput.mod.js | 682 +++++++++++++++--------------- 2 files changed, 345 insertions(+), 343 deletions(-) diff --git a/src/jquery.maskedinput.mod.coffee b/src/jquery.maskedinput.mod.coffee index d3864b7..d75abce 100644 --- a/src/jquery.maskedinput.mod.coffee +++ b/src/jquery.maskedinput.mod.coffee @@ -22,10 +22,10 @@ $.mask = autocomplete_predefined: 'mmddyyyy': [ { pattern: /^\//, replacement: " " } - { pattern: /^(\d)\//, replacement: "0$1" } - { pattern: /^(1[3-9])|[2-9]\d/, replacement: " " } - { pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, replacement: "$1 " } + { pattern: /^([1-9])\//, replacement: "0$1" } + { pattern: /^((1[3-9])|([2-9]\d)|(0\/))/, replacement: " " } { pattern: /^(\d\d.)\//, replacement: "$1 " } + { pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0\/))/, replacement: "$1 " } { pattern: /^(\d\d.)(\d\/)./, replacement: "$1" + "0" + "$2" } { pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } { pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } diff --git a/src/jquery.maskedinput.mod.js b/src/jquery.maskedinput.mod.js index ecf7205..932f565 100644 --- a/src/jquery.maskedinput.mod.js +++ b/src/jquery.maskedinput.mod.js @@ -1,371 +1,373 @@ -var $, iPhone, pasteEventName; -$ = jQuery; -pasteEventName = ($.browser.msie ? 'paste' : 'input') + '.mask'; -iPhone = window.orientation != null; -$.mask = { - definitions: { - '9': "[0-9]", - 'a': "[A-Za-z]", - '*': "[A-Za-z0-9]", - 'm': "[0-9/]", - 'd': "[0-9/]", - 'y': "[0-9]" - }, - dataName: "rawMaskFn", - autocomplete_predefined: { - 'mmddyyyy': [ - { - pattern: /^\//, - replacement: " " - }, { - pattern: /^(\d)\//, - replacement: "0$1" - }, { - pattern: /^(1[3-9])|[2-9]\d/, - replacement: " " - }, { - pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, - replacement: "$1 " - }, { - pattern: /^(\d\d.)\//, - replacement: "$1 " - }, { - pattern: /^(\d\d.)(\d\/)./, - replacement: "$1" + "0" + "$2" - }, { - pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, - replacement: "$1" + "20" + "$2" - }, { - pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, - replacement: "$1" + "19" + "$2" - } - ], - 'mmyyyy': [ - { - pattern: /^\//, - replacement: " " - }, { - pattern: /^(\d)\//, - replacement: "0$1" - }, { - pattern: /^1[3-9]/, - replacement: " " - }, { - pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, - replacement: "$1" + "20" + "$2" - }, { - pattern: /^(\d\d.)([3-8]\d)(?!\d)/, - replacement: "$1" + "19" + "$2" - } - ] - } -}; -$.fn.extend({ - caret: function(begin, end) { - var range; - if (this.length === 0) { - return; - } - if (typeof begin === 'number') { - end = typeof end === 'number' ? end : begin; - return this.each(function() { - var range; - if (this.setSelectionRange) { - return this.setSelectionRange(begin, end); - } else if (this.createTextRange) { - range = this.createTextRange; - range.collapse(true); - range.moveEnd('character', end); - range.moveStart('character', begin); - return range.select; +(function() { + var $, iPhone, pasteEventName; + $ = jQuery; + pasteEventName = ($.browser.msie ? 'paste' : 'input') + '.mask'; + iPhone = window.orientation != null; + $.mask = { + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + '*': "[A-Za-z0-9]", + 'm': "[0-9/]", + 'd': "[0-9/]", + 'y': "[0-9]" + }, + dataName: "rawMaskFn", + autocomplete_predefined: { + 'mmddyyyy': [ + { + pattern: /^\//, + replacement: " " + }, { + pattern: /^([1-9])\//, + replacement: "0$1" + }, { + pattern: /^((1[3-9])|([2-9]\d)|(0\/))/, + replacement: " " + }, { + pattern: /^(\d\d.)\//, + replacement: "$1 " + }, { + pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0\/))/, + replacement: "$1 " + }, { + pattern: /^(\d\d.)(\d\/)./, + replacement: "$1" + "0" + "$2" + }, { + pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" } - }); - } 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 defs, firstNonMaskPos, input, len, partialPosition, tests; - if (!mask && this.length > 0) { - input = $(this[0]); - return (input.data($.mask.dataName))(); - } - settings = $.extend({ - placeholder: "_", - completed: null, - autocomplete: [], - progessive_reveal: false - }, settings); - defs = $.mask.definitions; - tests = []; - partialPosition = mask.length; - firstNonMaskPos = null; - len = mask.length; - $.each(mask.split(""), function(i, c) { - if (c === '?') { - len--; - return partialPosition = i; - } else if (defs[c]) { - tests.push(RegExp(defs[c])); - if (firstNonMaskPos === null) { - return firstNonMaskPos = tests.length - 1; + ], + 'mmyyyy': [ + { + pattern: /^\//, + replacement: " " + }, { + pattern: /^(\d)\//, + replacement: "0$1" + }, { + pattern: /^1[3-9]/, + replacement: " " + }, { + pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" } + ] + } + }; + $.fn.extend({ + caret: function(begin, end) { + var range; + if (this.length === 0) { + return; + } + if (typeof begin === 'number') { + end = typeof end === 'number' ? end : begin; + return this.each(function() { + var range; + if (this.setSelectionRange) { + return this.setSelectionRange(begin, end); + } else if (this.createTextRange) { + range = this.createTextRange; + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + return range.select; + } + }); } else { - return tests.push(null); + 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 + }; } - }); - return (this.trigger("unmask")).each(function() { - var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; - input = $(this); - buffer = $.map(mask.split(""), function(c, i) { - if (c !== '?') { - if (defs[c]) { - return settings.placeholder; - } else { - return c; + }, + unmask: function() { + return this.trigger("unmask"); + }, + mask: function(mask, settings) { + var defs, firstNonMaskPos, input, len, partialPosition, tests; + if (!mask && this.length > 0) { + input = $(this[0]); + return (input.data($.mask.dataName))(); + } + settings = $.extend({ + placeholder: "_", + completed: null, + autocomplete: [], + progessive_reveal: false + }, settings); + defs = $.mask.definitions; + tests = []; + partialPosition = mask.length; + firstNonMaskPos = null; + len = mask.length; + $.each(mask.split(""), function(i, c) { + if (c === '?') { + len--; + return partialPosition = i; + } else if (defs[c]) { + tests.push(RegExp(defs[c])); + if (firstNonMaskPos === null) { + return firstNonMaskPos = tests.length - 1; } + } else { + return tests.push(null); } }); - focusText = input.val(); - seekNext = function(pos) { - while (++pos <= len && !tests[pos]) { - null; - } - return pos; - }; - seekPrev = function(pos) { - var ivala; - ivala = input.val().split(""); - while (--pos >= 0 && !tests[pos]) { - ivala[pos] = settings.placeholder; - } - ivala[pos] = settings.placeholder; - input.val(ivala.join("")); - return pos; - }; - shiftL = function(begin, end) { - var i, j; - if (begin < 0) { - return null; - } - j = seekNext(end); - for (i = begin; begin <= len ? i < len : i > len; begin <= len ? i++ : i--) { - if (tests[i]) { - if (j < len && tests[i].test(buffer[j])) { - buffer[i] = buffer[j]; - buffer[j] = settings.placeholder; + return (this.trigger("unmask")).each(function() { + var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; + input = $(this); + buffer = $.map(mask.split(""), function(c, i) { + if (c !== '?') { + if (defs[c]) { + return settings.placeholder; } else { - break; + return c; } - j = seekNext(j); } - } - writeBuffer(); - return input.caret(Math.max(firstNonMaskPos, begin)); - }; - shiftR = function(pos) { - var c, i, j, t, _results; - c = settings.placeholder; - _results = []; - for (i = pos; pos <= len ? i < len : i > len; pos <= len ? i++ : i--) { - if (tests[i]) { - j = seekNext(i); - t = buffer[i]; - buffer[i] = c; - if (j < len && tests[j].test(t)) { - c = t; - } else { - break; - } + }); + focusText = input.val(); + seekNext = function(pos) { + while (++pos <= len && !tests[pos]) { + null; } - } - return _results; - }; - keydownEvent = function(e) { - var begin, end, k, _ref; - k = e.which; - if (k === 8 || k === 46 || (iPhone && k === 127)) { - _ref = input.caret(), begin = _ref.begin, end = _ref.end; - console.dir(input.caret()); - if (end === begin) { - begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); - if (k === 46) { - end = seekNext(end); - } + return pos; + }; + seekPrev = function(pos) { + var ivala; + ivala = input.val().split(""); + while (--pos >= 0 && !tests[pos]) { + ivala[pos] = settings.placeholder; } - clearBuffer(begin, end); - shiftL(begin, end - 1); - return false; - } else if (k === 27) { - input.val(focusText); - input.caret(0, checkVal()); - return false; - } - }; - keypressEvent = function(e) { - var c, i, k, next, p, pattern, pos, replacement, startLength, _ref, _ref2; - k = e.which; - i = 0; - startLength = input.mask().length; - pos = input.caret(); - if (e.ctrlKey || e.altKey || e.metaKey || k < 32) { - return true; - } else if (k) { - if (pos.end !== pos.begin) { - clearBuffer(pos.begin, pos.end); - shiftL(pos.begin, pos.end - 1); + ivala[pos] = settings.placeholder; + input.val(ivala.join("")); + return pos; + }; + shiftL = function(begin, end) { + var i, j; + if (begin < 0) { + return null; } - 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); - input.caret(next); - if (settings.autocomplete.length > 0) { - for (i = 0, _ref = settings.autocomplete.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { - _ref2 = settings.autocomplete[i], pattern = _ref2.pattern, replacement = _ref2.replacement; - input.val(input.val().replace(pattern, replacement)); - clearBuffer(0, RegExp.lastMatch.length); - input.caret(checkVal(true)); - } + j = seekNext(end); + for (i = begin; begin <= len ? i < len : i > len; begin <= len ? i++ : i--) { + if (tests[i]) { + if (j < len && tests[i].test(buffer[j])) { + buffer[i] = buffer[j]; + buffer[j] = settings.placeholder; + } else { + break; } - next += input.mask().length - startLength - 1; - if (settings.completed && next >= len) { - settings.completed.call(input); + j = seekNext(j); + } + } + writeBuffer(); + return input.caret(Math.max(firstNonMaskPos, begin)); + }; + shiftR = function(pos) { + var c, i, j, t, _results; + c = settings.placeholder; + _results = []; + for (i = pos; pos <= len ? i < len : i > len; pos <= len ? i++ : i--) { + if (tests[i]) { + j = seekNext(i); + t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) { + c = t; + } else { + break; } } } - return false; - } - }; - clearBuffer = function(start, end) { - var i, _results; - _results = []; - for (i = start; start <= end ? i < end : i > end; start <= end ? i++ : i--) { - if (i < len) { - _results.push(tests[i] != null ? buffer[i] = settings.placeholder : void 0); + return _results; + }; + keydownEvent = function(e) { + var begin, end, k, _ref; + k = e.which; + if (k === 8 || k === 46 || (iPhone && k === 127)) { + _ref = input.caret(), begin = _ref.begin, end = _ref.end; + console.dir(input.caret()); + if (end === begin) { + begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); + if (k === 46) { + end = seekNext(end); + } + } + clearBuffer(begin, end); + shiftL(begin, end - 1); + return false; + } else if (k === 27) { + input.val(focusText); + input.caret(0, checkVal()); + return false; } - } - return _results; - }; - wbCount = 0; - writeBuffer = function() { - var i, ival, tmp_array, _ref; - if (settings.progressive_reveal) { + }; + keypressEvent = function(e) { + var c, i, k, next, p, pattern, pos, replacement, startLength, _ref, _ref2; + k = e.which; i = 0; - tmp_array = []; - ival = input.val(); - while (!(!(ival[i] != null) || (ival[i] === (_ref = buffer[i]) && _ref === settings.placeholder))) { - tmp_array.push(ival[i++]); + startLength = input.mask().length; + pos = input.caret(); + if (e.ctrlKey || e.altKey || e.metaKey || k < 32) { + return true; + } else if (k) { + if (pos.end !== pos.begin) { + 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); + input.caret(next); + if (settings.autocomplete.length > 0) { + for (i = 0, _ref = settings.autocomplete.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { + _ref2 = settings.autocomplete[i], pattern = _ref2.pattern, replacement = _ref2.replacement; + input.val(input.val().replace(pattern, replacement)); + clearBuffer(0, RegExp.lastMatch.length); + input.caret(checkVal(true)); + } + } + next += input.mask().length - startLength - 1; + if (settings.completed && next >= len) { + settings.completed.call(input); + } + } + } + return false; } - while (buffer[i] && buffer[i] !== settings.placeholder) { - tmp_array.push(buffer[i++]); + }; + clearBuffer = function(start, end) { + var i, _results; + _results = []; + for (i = start; start <= end ? i < end : i > end; start <= end ? i++ : i--) { + if (i < len) { + _results.push(tests[i] != null ? buffer[i] = settings.placeholder : void 0); + } } - return (input.val(tmp_array.join(""))).val(); - } else { - return (input.val(buffer.join(""))).val(); - } - }; - checkVal = function(allow) { - var c, i, lastMatch, pos, test; - test = input.val(); - lastMatch = -1; - pos = 0; - for (i = 0; 0 <= len ? i < len : i > len; 0 <= len ? i++ : i--) { - if (tests[i]) { - buffer[i] = settings.placeholder; - while (pos++ < test.length) { - c = test.charAt(pos - 1); - if (tests[pos - 1] && tests[pos - 1].test(c)) { - buffer[i] = c; - lastMatch = i; + return _results; + }; + wbCount = 0; + writeBuffer = function() { + var i, ival, tmp_array, _ref; + if (settings.progressive_reveal) { + i = 0; + tmp_array = []; + ival = input.val(); + while (!(!(ival[i] != null) || (ival[i] === (_ref = buffer[i]) && _ref === settings.placeholder))) { + tmp_array.push(ival[i++]); + } + while (buffer[i] && buffer[i] !== settings.placeholder) { + tmp_array.push(buffer[i++]); + } + return (input.val(tmp_array.join(""))).val(); + } else { + return (input.val(buffer.join(""))).val(); + } + }; + checkVal = function(allow) { + var c, i, lastMatch, pos, test; + test = input.val(); + lastMatch = -1; + pos = 0; + for (i = 0; 0 <= len ? i < len : i > len; 0 <= len ? i++ : i--) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + c = test.charAt(pos - 1); + if (tests[pos - 1] && tests[pos - 1].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 (pos > test.length) { - break; - } - } else if (buffer[i] === test.charAt(pos && i !== partialPosition)) { - pos++; - lastMatch = i; } - } - if (!allow && lastMatch + 1 < partialPosition) { - input.val(""); - clearBuffer(0, len); - } else if (allow || lastMatch + 1 >= partialPosition) { - writeBuffer(); - if (!allow) { - input.val(input.val().substring(0, lastMatch + 1)); + if (!allow && lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else if (allow || lastMatch + 1 >= partialPosition) { + writeBuffer(); + if (!allow) { + input.val(input.val().substring(0, lastMatch + 1)); + } } - } - if (partialPosition) { - return i; - } else { - return firstNonMaskPos; - } - }; - input.data($.mask.dataName, function() { - return ($.map(buffer, function(c, i) { - if (tests[i] && c !== settings.placeholder) { - return c; + if (partialPosition) { + return i; } else { - return null; + return firstNonMaskPos; } - })).join(''); - }); - if (!input.attr("readonly")) { - input.one("unmask", function() { - input.unbind(".mask"); - return input.removeData($.mask.dataName); - }).bind("focus.mask", function() { - var moveCaret, pos; - focusText = input.val(); - pos = checkVal(); - writeBuffer(); - moveCaret = function() { - if (pos === mask.length) { - return input.caret(0, pos); + }; + input.data($.mask.dataName, function() { + return ($.map(buffer, function(c, i) { + if (tests[i] && c !== settings.placeholder) { + return c; } else { - return input.caret(pos); + return null; } - }; - if ($.browser.msie) { - return moveCaret; - } else { - return (function() { - return setTimeout(moveCaret, 0); - })(); - } - }).bind("blur.mask", function() { - checkVal(); - if (input.val() !== focusText) { - return input.change(); - } - }).bind("keydown.mask", keydownEvent).bind("keypress.mask", keypressEvent).bind(pasteEventName, function() { - return setTimeout((function() { - return input.caret(checkVal(true)); - }), 0); + })).join(''); }); - } - return checkVal(); - }); - } -}); \ No newline at end of file + if (!input.attr("readonly")) { + input.one("unmask", function() { + input.unbind(".mask"); + return input.removeData($.mask.dataName); + }).bind("focus.mask", function() { + var moveCaret, pos; + focusText = input.val(); + pos = checkVal(); + writeBuffer(); + moveCaret = function() { + if (pos === mask.length) { + return input.caret(0, pos); + } else { + return input.caret(pos); + } + }; + if ($.browser.msie) { + return moveCaret; + } else { + return (function() { + return setTimeout(moveCaret, 0); + })(); + } + }).bind("blur.mask", function() { + checkVal(); + if (input.val() !== focusText) { + return input.change(); + } + }).bind("keydown.mask", keydownEvent).bind("keypress.mask", keypressEvent).bind(pasteEventName, function() { + return setTimeout((function() { + return input.caret(checkVal(true)); + }), 0); + }); + } + return checkVal(); + }); + } + }); +}).call(this); From 1a821295416f725408a883d362a818868867250e Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 19:25:35 -0500 Subject: [PATCH 18/33] corrected stylesheet link --- spec/SpecRunner.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html index 25ba0bb..48b8b2c 100644 --- a/spec/SpecRunner.html +++ b/spec/SpecRunner.html @@ -10,7 +10,7 @@ - + From ff40c38c57bf877e5f373dde05317a1cbe35ee95 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 19:30:01 -0500 Subject: [PATCH 19/33] Fixed some autocomplete regexes --- src/jquery.maskedinput.mod.coffee | 7 +++---- src/jquery.maskedinput.mod.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/jquery.maskedinput.mod.coffee b/src/jquery.maskedinput.mod.coffee index d75abce..dca05c9 100644 --- a/src/jquery.maskedinput.mod.coffee +++ b/src/jquery.maskedinput.mod.coffee @@ -23,9 +23,9 @@ $.mask = 'mmddyyyy': [ { pattern: /^\//, replacement: " " } { pattern: /^([1-9])\//, replacement: "0$1" } - { pattern: /^((1[3-9])|([2-9]\d)|(0\/))/, replacement: " " } + { pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " } { pattern: /^(\d\d.)\//, replacement: "$1 " } - { pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0\/))/, replacement: "$1 " } + { pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0[0\/]))/, replacement: "$1 " } { pattern: /^(\d\d.)(\d\/)./, replacement: "$1" + "0" + "$2" } { pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } { pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } @@ -35,7 +35,7 @@ $.mask = 'mmyyyy': [ { pattern: /^\//, replacement: " " } { pattern: /^(\d)\//, replacement: "0$1" } - { pattern: /^1[3-9]/, replacement: " " } + { pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " } { pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } { pattern: /^(\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } ] @@ -150,7 +150,6 @@ $.fn.extend # backspace, delete, and escape get special treatment if k == 8 or k == 46 or (iPhone and k == 127) {begin, end} = input.caret() - console.dir input.caret() if end == begin begin = if k != 46 then seekPrev begin else end = seekNext begin - 1 diff --git a/src/jquery.maskedinput.mod.js b/src/jquery.maskedinput.mod.js index 932f565..4289127 100644 --- a/src/jquery.maskedinput.mod.js +++ b/src/jquery.maskedinput.mod.js @@ -49,7 +49,7 @@ pattern: /^(\d)\//, replacement: "0$1" }, { - pattern: /^1[3-9]/, + pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " }, { pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, From 428cfa4ee7bd50d41c674958cd4fb8dd2e70a293 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Mon, 15 Aug 2011 19:31:01 -0500 Subject: [PATCH 20/33] Started progressive reveal mods, both code and tests --- spec/AutoComplete.Spec.coffee | 10 ++++++ spec/AutoComplete.Spec.js | 11 +++++- spec/ProgressiveReveal.Spec.coffee | 53 ++++++++++++++++++++++++++++ spec/ProgressiveReveal.Spec.js | 55 ++++++++++++++++++++++++++++++ spec/SpecRunner.html | 1 + src/jquery.maskedinput.mod.js | 5 ++- 6 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 spec/ProgressiveReveal.Spec.coffee create mode 100644 spec/ProgressiveReveal.Spec.js diff --git a/spec/AutoComplete.Spec.coffee b/spec/AutoComplete.Spec.coffee index 151f323..0bb249c 100644 --- a/spec/AutoComplete.Spec.coffee +++ b/spec/AutoComplete.Spec.coffee @@ -20,6 +20,7 @@ feature "Autocompletion", -> it "Should advance to the day fields if user enters a valid two digit month", -> input.mashKeys "09" expect(input).toHaveValue "09/ / " + expect(input.caret().begin).toEqual 3 it "Should reset to blank day of month if a slash is entered as the first charactor of day of month", -> input.mashKeys "09/" @@ -29,6 +30,10 @@ feature "Autocompletion", -> input.mashKeys "0941" expect(input).toHaveValue "09/ / " + it "Should reset to blank day of month if '00' is entered for the day of month", -> + input.mashKeys "0900" + expect(input).toHaveValue "09/ / " + it "Should zero pad the day of month if a single digit day of month is followed by a slash", -> input.mashKeys "099/" expect(input).toHaveValue "09/09/ " @@ -58,6 +63,11 @@ feature "Autocompletion", -> it "Should reset the input to blank if the user inputs an invalid month", -> input.mashKeys "13" expect(input).toHaveValue " / " + + it "Should reset to blank day of month if '00' is entered for the month", -> + input.mashKeys "00" + expect(input).toHaveValue " / " + it "Should reset the input to blank if the user inputs a slash as the first character", -> input.mashKeys "/" diff --git a/spec/AutoComplete.Spec.js b/spec/AutoComplete.Spec.js index 4f9ade5..845d854 100644 --- a/spec/AutoComplete.Spec.js +++ b/spec/AutoComplete.Spec.js @@ -21,7 +21,8 @@ }); it("Should advance to the day fields if user enters a valid two digit month", function() { input.mashKeys("09"); - return expect(input).toHaveValue("09/ / "); + expect(input).toHaveValue("09/ / "); + return expect(input.caret().begin).toEqual(3); }); it("Should reset to blank day of month if a slash is entered as the first charactor of day of month", function() { input.mashKeys("09/"); @@ -31,6 +32,10 @@ input.mashKeys("0941"); return expect(input).toHaveValue("09/ / "); }); + it("Should reset to blank day of month if '00' is entered for the day of month", function() { + input.mashKeys("0900"); + return expect(input).toHaveValue("09/ / "); + }); it("Should zero pad the day of month if a single digit day of month is followed by a slash", function() { input.mashKeys("099/"); return expect(input).toHaveValue("09/09/ "); @@ -63,6 +68,10 @@ input.mashKeys("13"); return expect(input).toHaveValue(" / "); }); + it("Should reset to blank day of month if '00' is entered for the month", function() { + input.mashKeys("00"); + return expect(input).toHaveValue(" / "); + }); it("Should reset the input to blank if the user inputs a slash as the first character", function() { input.mashKeys("/"); return expect(input).toHaveValue(" / "); diff --git a/spec/ProgressiveReveal.Spec.coffee b/spec/ProgressiveReveal.Spec.coffee new file mode 100644 index 0000000..14831ba --- /dev/null +++ b/spec/ProgressiveReveal.Spec.coffee @@ -0,0 +1,53 @@ +feature "Progressive Reveal", -> + + story "User interacts with an input with 'mmddyyyy' predefined autocomplete settings and 'progressive_reveal' set to true", -> + + beforeEach -> + input.mask "mm/dd/yyyy", + placeholder: " " + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] + progressive_reveal: true + + scenario "User hasn't entered anything yet", -> + + given "An empty input", -> + null + + whilst "User does nothing", -> + null + + hence "Input is blank", -> + (expect input).toHaveValue '' + + scenario "User enters two digit month", -> + + given "An empty input", -> + null + + whilst "User enters valid month", -> + input.mashKeys "11" + + hence "Input advances to the day of month field", -> + (expect input).toHaveValue "11/" + (expect input.caret().begin).toEqual 3 + + scenario "User enters two digit month, then backspace", -> + + given "An empty input", -> + null + + whilst "User enters valid month", -> + input.mashKeys "11" + + likewise "User hits backspace", -> + input.mashKeys (keys) -> + keys.type keys.backspace + + + hence "Input deletes slash and second digit of month", -> + (expect input).toHaveValue "1" + + likewise "The cursor is positioned after the first digit of month", -> + (expect input.caret().begin).toEqual 1 + + diff --git a/spec/ProgressiveReveal.Spec.js b/spec/ProgressiveReveal.Spec.js new file mode 100644 index 0000000..f947fd6 --- /dev/null +++ b/spec/ProgressiveReveal.Spec.js @@ -0,0 +1,55 @@ +(function() { + feature("Progressive Reveal", function() { + return story("User interacts with an input with 'mmddyyyy' predefined autocomplete settings and 'progressive_reveal' set to true", function() { + beforeEach(function() { + return input.mask("mm/dd/yyyy", { + placeholder: " ", + autocomplete: $.mask.autocomplete_predefined["mmddyyyy"], + progressive_reveal: true + }); + }); + scenario("User hasn't entered anything yet", function() { + given("An empty input", function() { + return null; + }); + whilst("User does nothing", function() { + return null; + }); + return hence("Input is blank", function() { + return (expect(input)).toHaveValue(''); + }); + }); + scenario("User enters two digit month", function() { + given("An empty input", function() { + return null; + }); + whilst("User enters valid month", function() { + return input.mashKeys("11"); + }); + return hence("Input advances to the day of month field", function() { + (expect(input)).toHaveValue("11/"); + return (expect(input.caret().begin)).toEqual(3); + }); + }); + return scenario("User enters two digit month, then backspace", function() { + given("An empty input", function() { + return null; + }); + whilst("User enters valid month", function() { + return input.mashKeys("11"); + }); + likewise("User hits backspace", function() { + return input.mashKeys(function(keys) { + return keys.type(keys.backspace); + }); + }); + hence("Input deletes slash and second digit of month", function() { + return (expect(input)).toHaveValue("1"); + }); + return likewise("The cursor is positioned after the first digit of month", function() { + return (expect(input.caret().begin)).toEqual(1); + }); + }); + }); + }); +}).call(this); diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html index 48b8b2c..00c02d1 100644 --- a/spec/SpecRunner.html +++ b/spec/SpecRunner.html @@ -47,6 +47,7 @@ + diff --git a/src/jquery.maskedinput.mod.js b/src/jquery.maskedinput.mod.js index 4289127..1bc094b 100644 --- a/src/jquery.maskedinput.mod.js +++ b/src/jquery.maskedinput.mod.js @@ -22,13 +22,13 @@ pattern: /^([1-9])\//, replacement: "0$1" }, { - pattern: /^((1[3-9])|([2-9]\d)|(0\/))/, + pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " }, { pattern: /^(\d\d.)\//, replacement: "$1 " }, { - pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0\/))/, + pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0[0\/]))/, replacement: "$1 " }, { pattern: /^(\d\d.)(\d\/)./, @@ -201,7 +201,6 @@ k = e.which; if (k === 8 || k === 46 || (iPhone && k === 127)) { _ref = input.caret(), begin = _ref.begin, end = _ref.end; - console.dir(input.caret()); if (end === begin) { begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); if (k === 46) { From 26673be34c325afaa9b6e23429dc9e23def3cd57 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Fri, 19 Aug 2011 14:39:31 -0500 Subject: [PATCH 21/33] ignore node files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e3f7294..d408882 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ dist/* # vim swap files *.swp acs.tmp +node_modules/ +npm-debug.log From cfcec578dc2b1d0ab737f0e9c7656bfd2a17a79d Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Fri, 19 Aug 2011 17:12:02 -0500 Subject: [PATCH 22/33] generated file no longer "mod" --- ...t.mod.coffee => jquery.maskedinput.coffee} | 3 +- src/jquery.maskedinput.js | 726 +++++++++--------- 2 files changed, 379 insertions(+), 350 deletions(-) rename src/{jquery.maskedinput.mod.coffee => jquery.maskedinput.coffee} (99%) diff --git a/src/jquery.maskedinput.mod.coffee b/src/jquery.maskedinput.coffee similarity index 99% rename from src/jquery.maskedinput.mod.coffee rename to src/jquery.maskedinput.coffee index dca05c9..df1d9e1 100644 --- a/src/jquery.maskedinput.mod.coffee +++ b/src/jquery.maskedinput.coffee @@ -1,7 +1,8 @@ -# Masked Input plugin for jQuery - CoffeeScript version +### Masked Input plugin for jQuery # # based upon Masked Input plugin for jQuery by Josh Bush # (http://digitalbush.com/projects/masked-input-plugin/) +### $ = jQuery diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index fe42da0..dbd6fbf 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -1,349 +1,377 @@ -/* - Masked Input plugin for jQuery - Copyright (c) 2007-@Year Josh Bush (digitalbush.com) - Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) - Version: @version -*/ -(function($) { - var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask"; - var iPhone = (window.orientation != undefined); - - $.mask = { - //Predefined character definitions - definitions: { - '9': "[0-9]", - 'a': "[A-Za-z]", - '*': "[A-Za-z0-9]", - 'm': "[0-9/]", - 'd': "[0-9/]", - 'y': "[0-9]" - }, - dataName:"rawMaskFn", - autocomplete_predefined: { - 'mmddyyyy': [ - { - pattern: /^\//, - replacement: " " - }, - { - pattern: /^(\d)\//, - replacement: "0$1" - }, - { - pattern: /^1[3-9]/, - replacement: " " - }, - { - pattern: /^(\d\d.)((3[2-9])|([4-9]\d))/, - replacement: "$1 " - }, - { - pattern: /^(\d\d.)\/ /, - replacement: "$1 " - }, - { - pattern: /^(\d\d.)(\d\/)./, - replacement: "$1" + "0" + "$2" - }, - { - pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, - replacement: "$1" + "20" + "$2" - }, - { - pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, - replacement: "$1" + "19" + "$2" - } - ], - - - 'mmyyyy': [ - { - pattern: /^\//, - replacement: " " - }, - { - pattern: /^(\d)\//, - replacement: "0$1" - }, - { - pattern: /^1[3-9]/, - replacement: " " - }, - { - pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, - replacement: "$1" + "20" + "$2" - }, - { - pattern: /^(\d\d.)([3-8]\d)(?!\d)/, - replacement: "$1" + "19" + "$2" - } - ] - - - } - }; - - $.fn.extend({ - //Helper Function for Caret positioning - caret: function(begin, end) { - if (this.length == 0) 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) { - var 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) { - var 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) { - if (!mask && this.length > 0) { - var input = $(this[0]); - return input.data($.mask.dataName)(); - } - settings = $.extend({ - placeholder: "_", - completed: null, - autocomplete: [ - /* an array of objects of the following format, which will be - * applied via a call to String.replace() after each update of the input - - { - pattern: /^([2-9])(.)/, - replacement: "0$1" - } - - */ - ] - - }, settings); - - var defs = $.mask.definitions; - var tests = []; - var partialPosition = mask.length; - var firstNonMaskPos = null; - var len = mask.length; - - $.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); - var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c }); - var 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) { - if(begin<0) - return; - for (var 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) { - for (var i = pos, c = settings.placeholder; i < len; i++) { - if (tests[i]) { - var j = seekNext(i); - var t = buffer[i]; - buffer[i] = c; - if (j < len && tests[j].test(t)) - c = t; - else - break; - } - } - }; - - function keydownEvent(e) { - var k=e.which; - - //backspace, delete, and escape get special treatment - if(k == 8 || k == 46 || (iPhone && k == 127)){ - var 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); - - return false; - } else if (k == 27) {//escape - input.val(focusText); - input.caret(0, checkVal()); - return false; - } - }; - - function keypressEvent(e) { - var k = e.which, - rule = null, - i = 0, - startLength = input.mask().length, - pos = input.caret(); - if (e.ctrlKey || e.altKey || e.metaKey || k<32) {//Ignore - return true; - } else if (k) { - if(pos.end-pos.begin!=0){ - clearBuffer(pos.begin, pos.end); - shiftL(pos.begin, pos.end-1); - } - - var p = seekNext(pos.begin - 1); - if (p < len) { - var c = String.fromCharCode(k); - if (tests[p].test(c)) { - shiftR(p); - buffer[p] = c; - writeBuffer(); - var next = seekNext(p); - input.caret(next); - - if (settings.autocomplete.length > 0) { - for(i=0; i < settings.autocomplete.length; i++){ - rule = settings.autocomplete[i]; - input.val(input.val().replace(rule.pattern, rule.replacement)); - clearBuffer(0, RegExp.lastMatch.length); - input.caret(checkVal(true)); - } - } - next += input.mask().length - startLength - 1; - if (settings.completed && next >= len) - settings.completed.call(input); - } - - } - return false; - } - }; - - function clearBuffer(start, end) { - for (var i = start; i < end && i < len; i++) { - if (tests[i]) - buffer[i] = settings.placeholder; - } - }; - - function writeBuffer() { return input.val(buffer.join('')).val(); }; - - function checkVal(allow) { - //try to place characters where they belong - var test = input.val(); - var lastMatch = -1; - for (var i = 0, pos = 0; i < len; i++) { - if (tests[i]) { - buffer[i] = settings.placeholder; - while (pos++ < test.length) { - var c = test.charAt(pos - 1); - if (tests[pos - 1] && tests[pos - 1].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 && lastMatch + 1 < partialPosition) { - input.val(""); - clearBuffer(0, len); - } else if (allow || lastMatch + 1 >= partialPosition) { - writeBuffer(); - if (!allow) 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() { - focusText = input.val(); - var pos = checkVal(); - writeBuffer(); - var moveCaret=function(){ - if (pos == mask.length) - input.caret(0, pos); - else - input.caret(pos); - }; - ($.browser.msie ? moveCaret:function(){setTimeout(moveCaret,0)})(); - }) - .bind("blur.mask", function() { - checkVal(); - if (input.val() != focusText) - input.change(); - }) - .bind("keydown.mask", keydownEvent) - .bind("keypress.mask", keypressEvent) - .bind(pasteEventName, function() { - setTimeout(function() { input.caret(checkVal(true)); }, 0); - }); - - checkVal(); //Perform initial check for existing values - }); - } - }); -})(jQuery); +(function() { + /* Masked Input plugin for jQuery + # + # based upon Masked Input plugin for jQuery by Josh Bush + # (http://digitalbush.com/projects/masked-input-plugin/) + */ + var $, iPhone, pasteEventName; + $ = jQuery; + pasteEventName = ($.browser.msie ? 'paste' : 'input') + '.mask'; + iPhone = window.orientation != null; + $.mask = { + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + '*': "[A-Za-z0-9]", + 'm': "[0-9/]", + 'd': "[0-9/]", + 'y': "[0-9]" + }, + dataName: "rawMaskFn", + autocomplete_predefined: { + 'mmddyyyy': [ + { + pattern: /^\//, + replacement: " " + }, { + pattern: /^([1-9])\//, + replacement: "0$1" + }, { + pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, + replacement: " " + }, { + pattern: /^(\d\d.)\//, + replacement: "$1 " + }, { + pattern: /^(\d\d.)((3[2-9])|([4-9]\d)|(0[0\/]))/, + replacement: "$1 " + }, { + pattern: /^(\d\d.)(\d\/)./, + replacement: "$1" + "0" + "$2" + }, { + pattern: /^(\d\d.\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } + ], + 'mmyyyy': [ + { + pattern: /^\//, + replacement: " " + }, { + pattern: /^(\d)\//, + replacement: "0$1" + }, { + pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, + replacement: " " + }, { + pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, + replacement: "$1" + "20" + "$2" + }, { + pattern: /^(\d\d.)([3-8]\d)(?!\d)/, + replacement: "$1" + "19" + "$2" + } + ] + } + }; + $.fn.extend({ + caret: function(begin, end) { + var range; + if (this.length === 0) { + return; + } + if (typeof begin === 'number') { + end = typeof end === 'number' ? end : begin; + return this.each(function() { + var range; + if (this.setSelectionRange) { + return this.setSelectionRange(begin, end); + } else if (this.createTextRange) { + range = this.createTextRange; + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + return 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 defs, firstNonMaskPos, input, len, partialPosition, tests; + if (!mask && this.length > 0) { + input = $(this[0]); + return (input.data($.mask.dataName))(); + } + settings = $.extend({ + placeholder: "_", + completed: null, + autocomplete: [], + progessive_reveal: false + }, settings); + defs = $.mask.definitions; + tests = []; + partialPosition = mask.length; + firstNonMaskPos = null; + len = mask.length; + $.each(mask.split(""), function(i, c) { + if (c === '?') { + len--; + return partialPosition = i; + } else if (defs[c]) { + tests.push(RegExp(defs[c])); + if (firstNonMaskPos === null) { + return firstNonMaskPos = tests.length - 1; + } + } else { + return tests.push(null); + } + }); + return (this.trigger("unmask")).each(function() { + var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; + input = $(this); + buffer = $.map(mask.split(""), function(c, i) { + if (c !== '?') { + if (defs[c]) { + return settings.placeholder; + } else { + return c; + } + } + }); + focusText = input.val(); + seekNext = function(pos) { + while (++pos <= len && !tests[pos]) { + null; + } + return pos; + }; + seekPrev = function(pos) { + var ivala; + ivala = input.val().split(""); + while (--pos >= 0 && !tests[pos]) { + ivala[pos] = settings.placeholder; + } + ivala[pos] = settings.placeholder; + input.val(ivala.join("")); + return pos; + }; + shiftL = function(begin, end) { + var i, j; + if (begin < 0) { + return null; + } + j = seekNext(end); + for (i = begin; begin <= len ? i < len : i > len; begin <= len ? i++ : 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(); + return input.caret(Math.max(firstNonMaskPos, begin)); + }; + shiftR = function(pos) { + var c, i, j, t, _results; + c = settings.placeholder; + _results = []; + for (i = pos; pos <= len ? i < len : i > len; pos <= len ? i++ : i--) { + if (tests[i]) { + j = seekNext(i); + t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) { + c = t; + } else { + break; + } + } + } + return _results; + }; + keydownEvent = function(e) { + var begin, end, k, _ref; + k = e.which; + if (k === 8 || k === 46 || (iPhone && k === 127)) { + _ref = input.caret(), begin = _ref.begin, end = _ref.end; + if (end === begin) { + begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); + if (k === 46) { + end = seekNext(end); + } + } + clearBuffer(begin, end); + shiftL(begin, end - 1); + return false; + } else if (k === 27) { + input.val(focusText); + input.caret(0, checkVal()); + return false; + } + }; + keypressEvent = function(e) { + var c, i, k, next, p, pattern, pos, replacement, startLength, _ref, _ref2; + k = e.which; + i = 0; + startLength = input.mask().length; + pos = input.caret(); + if (e.ctrlKey || e.altKey || e.metaKey || k < 32) { + return true; + } else if (k) { + if (pos.end !== pos.begin) { + 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); + input.caret(next); + if (settings.autocomplete.length > 0) { + for (i = 0, _ref = settings.autocomplete.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { + _ref2 = settings.autocomplete[i], pattern = _ref2.pattern, replacement = _ref2.replacement; + input.val(input.val().replace(pattern, replacement)); + clearBuffer(0, RegExp.lastMatch.length); + input.caret(checkVal(true)); + } + } + next += input.mask().length - startLength - 1; + if (settings.completed && next >= len) { + settings.completed.call(input); + } + } + } + return false; + } + }; + clearBuffer = function(start, end) { + var i, _results; + _results = []; + for (i = start; start <= end ? i < end : i > end; start <= end ? i++ : i--) { + if (i < len) { + _results.push(tests[i] != null ? buffer[i] = settings.placeholder : void 0); + } + } + return _results; + }; + wbCount = 0; + writeBuffer = function() { + var i, ival, tmp_array, _ref; + if (settings.progressive_reveal) { + i = 0; + tmp_array = []; + ival = input.val(); + while (!(!(ival[i] != null) || (ival[i] === (_ref = buffer[i]) && _ref === settings.placeholder))) { + tmp_array.push(ival[i++]); + } + while (buffer[i] && buffer[i] !== settings.placeholder) { + tmp_array.push(buffer[i++]); + } + return (input.val(tmp_array.join(""))).val(); + } else { + return (input.val(buffer.join(""))).val(); + } + }; + checkVal = function(allow) { + var c, i, lastMatch, pos, test; + test = input.val(); + lastMatch = -1; + pos = 0; + for (i = 0; 0 <= len ? i < len : i > len; 0 <= len ? i++ : i--) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + c = test.charAt(pos - 1); + if (tests[pos - 1] && tests[pos - 1].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 && lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else if (allow || lastMatch + 1 >= partialPosition) { + writeBuffer(); + if (!allow) { + input.val(input.val().substring(0, lastMatch + 1)); + } + } + if (partialPosition) { + return i; + } else { + return firstNonMaskPos; + } + }; + input.data($.mask.dataName, function() { + return ($.map(buffer, function(c, i) { + if (tests[i] && c !== settings.placeholder) { + return c; + } else { + return null; + } + })).join(''); + }); + if (!input.attr("readonly")) { + input.one("unmask", function() { + input.unbind(".mask"); + return input.removeData($.mask.dataName); + }).bind("focus.mask", function() { + var moveCaret, pos; + focusText = input.val(); + pos = checkVal(); + writeBuffer(); + moveCaret = function() { + if (pos === mask.length) { + return input.caret(0, pos); + } else { + return input.caret(pos); + } + }; + if ($.browser.msie) { + return moveCaret; + } else { + return (function() { + return setTimeout(moveCaret, 0); + })(); + } + }).bind("blur.mask", function() { + checkVal(); + if (input.val() !== focusText) { + return input.change(); + } + }).bind("keydown.mask", keydownEvent).bind("keypress.mask", keypressEvent).bind(pasteEventName, function() { + return setTimeout((function() { + return input.caret(checkVal(true)); + }), 0); + }); + } + return checkVal(); + }); + } + }); +}).call(this); From c0e430e44743557664ff1f3a104499887f4fd152 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 16:22:35 -0500 Subject: [PATCH 23/33] fixed bare call to CreateRange --- demo/index.html | 2 +- src/jquery.maskedinput.coffee | 6 +++--- src/jquery.maskedinput.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/demo/index.html b/demo/index.html index 7ff3fbd..55ed714 100644 --- a/demo/index.html +++ b/demo/index.html @@ -2,7 +2,7 @@ jQuery Mask Test - + - + - + diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index df1d9e1..47e3481 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -50,17 +50,17 @@ $.fn.extend if this.setSelectionRange this.setSelectionRange begin, end else if this.createTextRange - range = this.createTextRange + range = this.createTextRange() range.collapse true range.moveEnd 'character', end range.moveStart 'character', begin - range.select + range.select() else if this[0].setSelectionRange begin = this[0].selectionStart end = this[0].selectionEnd else if document.selection and document.selection.createRange - range = document.selection.createRange + range = document.selection.createRange() begin = 0 - range.duplicate().moveStart 'character', -100000 end = begin + range.text.length return { begin: begin, end: end} diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index dbd6fbf..b1264cb 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -79,11 +79,11 @@ if (this.setSelectionRange) { return this.setSelectionRange(begin, end); } else if (this.createTextRange) { - range = this.createTextRange; + range = this.createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', begin); - return range.select; + return range.select(); } }); } else { @@ -91,7 +91,7 @@ begin = this[0].selectionStart; end = this[0].selectionEnd; } else if (document.selection && document.selection.createRange) { - range = document.selection.createRange; + range = document.selection.createRange(); begin = 0 - range.duplicate().moveStart('character', -100000); end = begin + range.text.length; } From 5c86158e56caf404f23da69f532bcc73fa389d92 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 16:45:38 -0500 Subject: [PATCH 25/33] update for reserve word in spec --- spec/Delete.spec.js | 6 +++--- src/jquery.maskedinput.coffee | 4 ++-- src/jquery.maskedinput.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/Delete.spec.js b/spec/Delete.spec.js index 0ade2f8..e993572 100644 --- a/spec/Delete.spec.js +++ b/spec/Delete.spec.js @@ -12,15 +12,15 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ - expect(input).toHaveValue('1-3_'); + expect(input).tohavevalue('1-3_'); }); and("caret position should be correct",function(){ - expect(input.caret().begin).toEqual(2); + expect(input.caret().begin).toequal(2); }); }); diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 47e3481..d4906ef 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -57,8 +57,8 @@ $.fn.extend range.select() else if this[0].setSelectionRange - begin = this[0].selectionStart - end = this[0].selectionEnd + begin = this[0].selectionStart() + end = this[0].selectionEnd() else if document.selection and document.selection.createRange range = document.selection.createRange() begin = 0 - range.duplicate().moveStart 'character', -100000 diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index b1264cb..04eed1d 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -88,8 +88,8 @@ }); } else { if (this[0].setSelectionRange) { - begin = this[0].selectionStart; - end = this[0].selectionEnd; + 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); From 51cad1642a8fbb6b7bb8e5f521f987997c4bdbe6 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 16:45:38 -0500 Subject: [PATCH 26/33] update for reserve word in spec --- spec/Delete.spec.js | 16 ++++++++-------- src/jquery.maskedinput.coffee | 4 ++-- src/jquery.maskedinput.js | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/Delete.spec.js b/spec/Delete.spec.js index 0ade2f8..f92b384 100644 --- a/spec/Delete.spec.js +++ b/spec/Delete.spec.js @@ -12,15 +12,15 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ - expect(input).toHaveValue('1-3_'); + expect(input).tohavevalue('1-3_'); }); and("caret position should be correct",function(){ - expect(input.caret().begin).toEqual(2); + expect(input.caret().begin).toequal(2); }); }); @@ -36,7 +36,7 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ @@ -62,7 +62,7 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ @@ -86,7 +86,7 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ @@ -111,7 +111,7 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ @@ -135,7 +135,7 @@ feature("Delete Key", function() { }); when("hitting the delete key",function(){ - input.mashKeys(function(keys){keys.type(keys.delete)}); + input.mashKeys(function(keys){keys.type(keys[ 'delete' ]);}); }); then("value should be correct",function(){ diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 47e3481..d4906ef 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -57,8 +57,8 @@ $.fn.extend range.select() else if this[0].setSelectionRange - begin = this[0].selectionStart - end = this[0].selectionEnd + begin = this[0].selectionStart() + end = this[0].selectionEnd() else if document.selection and document.selection.createRange range = document.selection.createRange() begin = 0 - range.duplicate().moveStart 'character', -100000 diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index b1264cb..04eed1d 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -88,8 +88,8 @@ }); } else { if (this[0].setSelectionRange) { - begin = this[0].selectionStart; - end = this[0].selectionEnd; + 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); From 1523f1e24d02265f5307d0a26ee5c97e25b097c8 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 17:04:28 -0500 Subject: [PATCH 27/33] working on caret issues --- src/jquery.maskedinput.coffee | 4 ++-- src/jquery.maskedinput.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index d4906ef..47e3481 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -57,8 +57,8 @@ $.fn.extend range.select() else if this[0].setSelectionRange - begin = this[0].selectionStart() - end = this[0].selectionEnd() + begin = this[0].selectionStart + end = this[0].selectionEnd else if document.selection and document.selection.createRange range = document.selection.createRange() begin = 0 - range.duplicate().moveStart 'character', -100000 diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index 04eed1d..b1264cb 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -88,8 +88,8 @@ }); } else { if (this[0].setSelectionRange) { - begin = this[0].selectionStart(); - end = this[0].selectionEnd(); + 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); From 1c4345ba82e70625f30c5fc4c5f358771a78f8c2 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 17:14:49 -0500 Subject: [PATCH 28/33] corrected typo in progressive_reveal default setting --- src/jquery.maskedinput.coffee | 2 +- src/jquery.maskedinput.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 47e3481..0ae29a6 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -76,7 +76,7 @@ $.fn.extend placeholder: "_" completed: null autocomplete: [] - progessive_reveal: false + progressive_reveal: false }, settings defs = $.mask.definitions diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index b1264cb..ba0619a 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -114,7 +114,7 @@ placeholder: "_", completed: null, autocomplete: [], - progessive_reveal: false + progressive_reveal: false }, settings); defs = $.mask.definitions; tests = []; From 0af2bd4b064d7d618630c0d50f6d319f5d1b8642 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 17:19:52 -0500 Subject: [PATCH 29/33] fixed function calls in spec --- spec/Delete.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Delete.spec.js b/spec/Delete.spec.js index f92b384..8550d40 100644 --- a/spec/Delete.spec.js +++ b/spec/Delete.spec.js @@ -16,11 +16,11 @@ feature("Delete Key", function() { }); then("value should be correct",function(){ - expect(input).tohavevalue('1-3_'); + expect(input).toHaveValue('1-3_'); }); and("caret position should be correct",function(){ - expect(input.caret().begin).toequal(2); + expect(input.caret().begin).toEqual(2); }); }); From aa5cd286d31185eb21e2e347e2fa345ca2620434 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sat, 20 Aug 2011 21:20:54 -0500 Subject: [PATCH 30/33] fixed precedence problem in checkVal --- src/jquery.maskedinput.coffee | 2 +- src/jquery.maskedinput.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 0ae29a6..cefef8d 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -240,7 +240,7 @@ $.fn.extend break if pos > test.length break - else if buffer[i] == test.charAt pos and i != partialPosition + else if buffer[i] == ( test.charAt pos ) and i != partialPosition pos++ lastMatch = i diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index ba0619a..656c497 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -307,7 +307,7 @@ if (pos > test.length) { break; } - } else if (buffer[i] === test.charAt(pos && i !== partialPosition)) { + } else if (buffer[i] === (test.charAt(pos)) && i !== partialPosition) { pos++; lastMatch = i; } From 8b14f509bf21842f1deddbc7252f92862251f68b Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Sun, 21 Aug 2011 09:25:19 -0500 Subject: [PATCH 31/33] fixed logic around moveCaret() for IE --- src/jquery.maskedinput.coffee | 2 +- src/jquery.maskedinput.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index cefef8d..1ab9e04 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -270,7 +270,7 @@ $.fn.extend input.caret 0, pos else input.caret pos - if $.browser.msie then moveCaret else do -> setTimeout moveCaret, 0 + if $.browser.msie then moveCaret() else do -> setTimeout moveCaret, 0 .bind( "blur.mask", -> checkVal() if input.val() != focusText diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index 656c497..5296436 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -353,7 +353,7 @@ } }; if ($.browser.msie) { - return moveCaret; + return moveCaret(); } else { return (function() { return setTimeout(moveCaret, 0); From 041a94c3d027e45a4e20e6de8dc0f770c99d9f16 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Wed, 21 Sep 2011 08:57:52 -0500 Subject: [PATCH 32/33] add missing regex to mmyyyy --- src/jquery.maskedinput.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 1ab9e04..293657b 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -37,6 +37,7 @@ $.mask = { pattern: /^\//, replacement: " " } { pattern: /^(\d)\//, replacement: "0$1" } { pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " } + { pattern: /^(\d\d.)\//, replacement: "$1 " } { pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" } { pattern: /^(\d\d.)([3-8]\d)(?!\d)/, replacement: "$1" + "19" + "$2" } ] From 3acfd1e88d86ae8a1d9caf3380b8597c490bd856 Mon Sep 17 00:00:00 2001 From: Rod Knowlton Date: Wed, 21 Sep 2011 10:54:53 -0500 Subject: [PATCH 33/33] make Backspace and Delete clear input in certain conditions --- Cakefile | 6 +- demo/index.html | 5 +- spec/SpecRunner.html | 4 +- spec/lib/jasmine/json2.js | 478 ++++++++++++++++++++++++++++++++++ src/jquery.maskedinput.coffee | 24 +- src/jquery.maskedinput.js | 29 ++- 6 files changed, 531 insertions(+), 15 deletions(-) create mode 100644 spec/lib/jasmine/json2.js diff --git a/Cakefile b/Cakefile index d91b6b2..5965db2 100644 --- a/Cakefile +++ b/Cakefile @@ -18,8 +18,10 @@ minify = (js)-> process = uglify.uglify ast = process.ast_mangle(ast) ast = process.ast_squeeze(ast) - comment = uglify.parser.tokenizer(js)().comments_before[0].value; - '/*'+comment+'*/\n'+process.gen_code(ast) + #comment = uglify.parser.tokenizer(js)().comments_before[0].value; + #'/*'+comment+'*/\n'+process.gen_code(ast) + process.gen_code(ast) + replaceTokens = (js,tokens)-> js.replace( diff --git a/demo/index.html b/demo/index.html index 55ed714..89e9fb7 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,3 +1,4 @@ + jQuery Mask Test @@ -11,7 +12,9 @@ $.mask.definitions['y'] = "[0-9\/]"; $("#date").mask("mm/dd/yyyy",{ placeholder: " ", progressive_reveal: true, autocomplete: $.mask.autocomplete_predefined["mmddyyyy"] }); - $("#phone").mask("(999) 999-9999"); + $("#phone").mask("mm/yyyy",{ placeholder: " ", progressive_reveal: true, + autocomplete: $.mask.autocomplete_predefined["mmyyyy"] }); + $("#phoneExt").mask("(999) 999-9999? x99999"); $("#iphone").mask("+33 999 999 999"); $("#tin").mask("99-9999999"); diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html index fe20b29..461c78c 100644 --- a/spec/SpecRunner.html +++ b/spec/SpecRunner.html @@ -43,8 +43,8 @@ - - + + diff --git a/spec/lib/jasmine/json2.js b/spec/lib/jasmine/json2.js new file mode 100644 index 0000000..ac58079 --- /dev/null +++ b/spec/lib/jasmine/json2.js @@ -0,0 +1,478 @@ +/* + http://www.JSON.org/json2.js + 2009-08-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. +*/ + +/*jslint evil: true */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + +"use strict"; + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/. +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); diff --git a/src/jquery.maskedinput.coffee b/src/jquery.maskedinput.coffee index 293657b..c6f2e1f 100644 --- a/src/jquery.maskedinput.coffee +++ b/src/jquery.maskedinput.coffee @@ -148,20 +148,32 @@ $.fn.extend keydownEvent = (e) -> k = e.which + KEYDELETE = 46 + KEYBACKSPACE = 8 + KEYESCAPE = 27 + # backspace, delete, and escape get special treatment - if k == 8 or k == 46 or (iPhone and k == 127) + if k == KEYBACKSPACE or k == KEYDELETE or (iPhone and k == 127) {begin, end} = input.caret() if end == begin - begin = if k != 46 then seekPrev begin else end = seekNext begin - 1 - end = seekNext end if k == 46 + + if (k == KEYBACKSPACE or k == KEYDELETE) and begin < input.val().length + resetBuffer() + return false + + begin = if k != KEYDELETE then seekPrev begin else end = seekNext begin - 1 + end = seekNext end if k == KEYDELETE + else + resetBuffer() + return false clearBuffer begin, end shiftL begin, end - 1 false - else if k == 27 + else if k == KEYESCAPE # escape input.val focusText input.caret 0, checkVal() @@ -203,6 +215,10 @@ $.fn.extend settings.completed.call input false + resetBuffer = -> + input.val "" + input.caret 0, checkVal() + clearBuffer = (start, end) -> for i in [start...end] when i < len buffer[i] = settings.placeholder if tests[i]? diff --git a/src/jquery.maskedinput.js b/src/jquery.maskedinput.js index 5296436..12ee34f 100644 --- a/src/jquery.maskedinput.js +++ b/src/jquery.maskedinput.js @@ -56,6 +56,9 @@ }, { pattern: /^((1[3-9])|([2-9]\d)|(0[0\/]))/, replacement: " " + }, { + pattern: /^(\d\d.)\//, + replacement: "$1 " }, { pattern: /^(\d\d.)(1[0-8]|0\d|2[1-9])(?!\d)/, replacement: "$1" + "20" + "$2" @@ -135,7 +138,7 @@ } }); return (this.trigger("unmask")).each(function() { - var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; + var buffer, checkVal, clearBuffer, focusText, keydownEvent, keypressEvent, resetBuffer, seekNext, seekPrev, shiftL, shiftR, wbCount, writeBuffer; input = $(this); buffer = $.map(mask.split(""), function(c, i) { if (c !== '?') { @@ -202,20 +205,30 @@ return _results; }; keydownEvent = function(e) { - var begin, end, k, _ref; + var KEYBACKSPACE, KEYDELETE, KEYESCAPE, begin, end, k, _ref; k = e.which; - if (k === 8 || k === 46 || (iPhone && k === 127)) { + KEYDELETE = 46; + KEYBACKSPACE = 8; + KEYESCAPE = 27; + if (k === KEYBACKSPACE || k === KEYDELETE || (iPhone && k === 127)) { _ref = input.caret(), begin = _ref.begin, end = _ref.end; if (end === begin) { - begin = k !== 46 ? seekPrev(begin) : end = seekNext(begin - 1); - if (k === 46) { + if ((k === KEYBACKSPACE || k === KEYDELETE) && begin < input.val().length) { + resetBuffer(); + return false; + } + begin = k !== KEYDELETE ? seekPrev(begin) : end = seekNext(begin - 1); + if (k === KEYDELETE) { end = seekNext(end); } + } else { + resetBuffer(); + return false; } clearBuffer(begin, end); shiftL(begin, end - 1); return false; - } else if (k === 27) { + } else if (k === KEYESCAPE) { input.val(focusText); input.caret(0, checkVal()); return false; @@ -260,6 +273,10 @@ return false; } }; + resetBuffer = function() { + input.val(""); + return input.caret(0, checkVal()); + }; clearBuffer = function(start, end) { var i, _results; _results = [];