(function(factory, jQuery, Zepto){
'use strict';
if(typeof define==='function'&&define.amd){
define([ 'jquery' ], factory);
}else if(typeof exports==='object'&&typeof Meteor==='undefined'){
window.module.exports=factory(window.require('jquery'));
}else{
factory(jQuery||Zepto);
}}(
function($){
'use strict';
var JSON=window.JSON;
var globals;
var Mask=function(el, mask, options){
var jMask=this;
var regexMask;
var oldValue;
var p={
invalid: [],
getCaret: function(){
var sel,
pos=0,
ctrl,
dSel,
cSelStart;
try {
ctrl=el.get(0);
dSel=document.selection;
cSelStart=ctrl.selectionStart;
if(dSel&&navigator.appVersion.indexOf('MSIE 10')===-1){
sel=dSel.createRange();
sel.moveStart('character', -p.val().length);
pos=sel.text.length;
}else if(cSelStart||cSelStart==='0'){
pos=cSelStart;
}
return pos;
} catch(err){
window.console.log(err);
}},
setCaret: function(pos){
var range;
var ctrl;
try {
if(el.is(':focus')){
ctrl=el.get(0);
if(ctrl.setSelectionRange){
ctrl.setSelectionRange(pos, pos);
}else{
range=ctrl.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}}
} catch(err){
window.console.log(err);
}},
events: function(){
el.on('keydown.mask', function(e){
el.data('mask-keycode', e.keyCode||e.which);
el.data('mask-previus-value', el.val());
el.data('mask-previus-caret-pos', p.getCaret());
p.maskDigitPosMapOld=p.maskDigitPosMap;
})
.on($.jMaskGlobals.useInput ? 'input.mask':'keyup.mask', p.behaviour)
.on('paste.mask drop.mask', function(){
setTimeout(function(){
el.keydown().keyup();
}, 100);
})
.on('change.mask', function(){
el.data('changed', true);
})
.on('blur.mask', function(){
if(oldValue!==p.val()&&! el.data('changed')){
el.trigger('change');
}
el.data('changed', false);
})
.on('blur.mask', function(){
oldValue=p.val();
})
.on('focus.mask', function(e){
if(options.selectOnFocus===true){
$(e.target).select();
}})
.on('focusout.mask', function(){
if(options.clearIfNotMatch&&! regexMask.test(p.val())){
p.val('');
}});
},
getRegexMask: function(){
var maskChunks=[],
translation,
pattern,
optional,
recursive,
oRecursive,
r;
var i;
for(i=0; i < mask.length; i +=1){
translation=jMask.translation[ mask.charAt(i) ];
if(translation){
pattern=translation.pattern.toString().replace(/.{1}$|^.{1}/g, '');
optional=translation.optional;
recursive=translation.recursive;
if(recursive){
maskChunks.push(mask.charAt(i));
oRecursive={
digit: mask.charAt(i),
pattern: pattern
};}else{
maskChunks.push(! optional&&! recursive ? pattern:pattern + '?');
}}else{
maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
}}
r=maskChunks.join('');
if(oRecursive){
r=r.replace(new RegExp('(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)'), '($1)?').replace(new RegExp(oRecursive.digit, 'g'), oRecursive.pattern);
}
return new RegExp(r);
},
destroyEvents: function(){
el.off([ 'input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', '' ].join('.mask '));
},
val: function(v){
var isInput=el.is('input'),
method=isInput ? 'val':'text',
r;
if(arguments.length > 0){
if(el[ method ]()!==v){
el[ method ](v);
}
r=el;
}else{
r=el[ method ]();
}
return r;
},
calculateCaretPosition: function(oldVal){
var newVal=p.getMasked(),
caretPosNew=p.getCaret();
var caretPosOld,
newValL,
oldValL,
maskDigitsBeforeCaret=0,
maskDigitsAfterCaret=0,
maskDigitsBeforeCaretAll=0,
maskDigitsBeforeCaretAllOld=0,
i=0;
var caretPos;
if(oldVal!==newVal){
caretPosOld=el.data('mask-previus-caret-pos')||0;
newValL=newVal.length;
oldValL=oldVal.length;
for(i=caretPosNew; i < newValL; i +=1){
if(! p.maskDigitPosMap[ i ]){
break;
}
maskDigitsAfterCaret=maskDigitsAfterCaret + 1;
}
for(i=caretPosNew - 1; i >=0; i -=1){
if(! p.maskDigitPosMap[ i ]){
break;
}
maskDigitsBeforeCaret=maskDigitsBeforeCaret + 1;
}
for(i=caretPosNew - 1; i >=0; i -=1){
if(p.maskDigitPosMap[ i ]){
maskDigitsBeforeCaretAll=maskDigitsBeforeCaretAll + 1;
}}
for(i=caretPosOld - 1; i >=0; i -=1){
if(p.maskDigitPosMapOld[ i ]){
maskDigitsBeforeCaretAllOld=maskDigitsBeforeCaretAllOld + 1;
}}
if(caretPosNew > oldValL){
caretPosNew=newValL * 10;
}else if(caretPosOld >=caretPosNew&&caretPosOld!==oldValL){
if(! p.maskDigitPosMapOld[ caretPosNew ]){
caretPos=caretPosNew;
caretPosNew -=maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll;
caretPosNew -=maskDigitsBeforeCaret;
if(p.maskDigitPosMap[ caretPosNew ]){
caretPosNew=caretPos;
}}
}else if(caretPosNew > caretPosOld){
caretPosNew +=maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld;
caretPosNew +=maskDigitsAfterCaret;
}}
return caretPosNew;
},
behaviour: function(e){
var keyCode=el.data('mask-keycode');
var newVal;
var oldVal;
var caretPos;
e=e||window.event;
p.invalid=[];
if($.inArray(keyCode, jMask.byPassKeys)===-1){
newVal=p.getMasked();
caretPos=p.getCaret();
oldVal=el.data('mask-previus-value')||'';
setTimeout(function(){
p.setCaret(p.calculateCaretPosition(oldVal));
}, $.jMaskGlobals.keyStrokeCompensation);
p.val(newVal);
p.setCaret(caretPos);
return p.callbacks(e);
}},
getMasked: function(skipMaskChars, val){
var buf=[],
value=val===undefined ? p.val():val + '',
m=0,
maskLen=mask.length,
v=0,
valLen=value.length,
offset=1,
addMethod='push',
resetPos=-1,
maskDigitCount=0,
maskDigitPosArr=[],
lastMaskChar,
check;
var lastUntranslatedMaskChar;
var maskDigit;
var valDigit;
var translation;
var lastMaskCharDigit;
var newVal;
if(options.reverse){
addMethod='unshift';
offset=-1;
lastMaskChar=0;
m=maskLen - 1;
v=valLen - 1;
check=function(){
return m > -1&&v > -1;
};}else{
lastMaskChar=maskLen - 1;
check=function(){
return m < maskLen&&v < valLen;
};}
while(check()){
maskDigit=mask.charAt(m);
valDigit=value.charAt(v);
translation=jMask.translation[ maskDigit ];
if(translation){
if(valDigit.match(translation.pattern)){
buf[ addMethod ](valDigit);
if(translation.recursive){
if(resetPos===-1){
resetPos=m;
}else if(m===lastMaskChar&&m!==resetPos){
m=resetPos - offset;
}
if(lastMaskChar===resetPos){
m -=offset;
}}
m +=offset;
}else if(valDigit===lastUntranslatedMaskChar){
maskDigitCount=maskDigitCount - 1;
lastUntranslatedMaskChar=undefined;
}else if(translation.optional){
m +=offset;
v -=offset;
}else if(translation.fallback){
buf[ addMethod ](translation.fallback);
m +=offset;
v -=offset;
}else{
p.invalid.push({
p: v,
v: valDigit,
e: translation.pattern
});
}
v +=offset;
}else{
if(! skipMaskChars){
buf[ addMethod ](maskDigit);
}
if(valDigit===maskDigit){
maskDigitPosArr.push(v);
v +=offset;
}else{
lastUntranslatedMaskChar=maskDigit;
maskDigitPosArr.push(v + maskDigitCount);
maskDigitCount=maskDigitCount + 1;
}
m +=offset;
}}
lastMaskCharDigit=mask.charAt(lastMaskChar);
if(maskLen===valLen + 1&&! jMask.translation[ lastMaskCharDigit ]){
buf.push(lastMaskCharDigit);
}
newVal=buf.join('');
p.mapMaskdigitPositions(newVal, maskDigitPosArr, valLen);
return newVal;
},
mapMaskdigitPositions: function(newVal, maskDigitPosArr, valLen){
var maskDiff=options.reverse ? newVal.length - valLen:0;
var i;
p.maskDigitPosMap={};
for(i=0; i < maskDigitPosArr.length; i +=1){
p.maskDigitPosMap[ maskDigitPosArr[ i ] + maskDiff ]=1;
}},
callbacks: function(e){
var val=p.val(),
changed=val!==oldValue,
defaultArgs=[ val, e, el, options ],
callback=function(name, criteria, args){
if(typeof options[ name ]==='function'&&criteria){
options[ name ].apply(this, args);
}};
callback('onChange', changed===true, defaultArgs);
callback('onKeyPress', changed===true, defaultArgs);
callback('onComplete', val.length===mask.length, defaultArgs);
callback('onInvalid', p.invalid.length > 0, [ val, e, el, p.invalid, options ]);
}};
el=$(el);
oldValue=p.val();
mask=typeof mask==='function' ? mask(p.val(), undefined, el, options):mask;
jMask.mask=mask;
jMask.options=options;
jMask.remove=function(){
var caret=p.getCaret();
if(jMask.options.placeholder){
el.removeAttr('placeholder');
}
if(el.data('mask-maxlength')){
el.removeAttr('maxlength');
}
p.destroyEvents();
p.val(jMask.getCleanVal());
p.setCaret(caret);
return el;
};
jMask.getCleanVal=function(){
return p.getMasked(true);
};
jMask.getMaskedVal=function(val){
return p.getMasked(false, val);
};
jMask.init=function(onlyMask){
var i;
var translation;
var caret;
var maxlength;
onlyMask=onlyMask||false;
options=options||{};
jMask.clearIfNotMatch=$.jMaskGlobals.clearIfNotMatch;
jMask.byPassKeys=$.jMaskGlobals.byPassKeys;
jMask.translation=$.extend({}, $.jMaskGlobals.translation, options.translation);
jMask=$.extend(true, {}, jMask, options);
regexMask=p.getRegexMask();
if(onlyMask){
p.events();
p.val(p.getMasked());
}else{
if(options.placeholder){
el.attr('placeholder', options.placeholder);
}
if(el.data('mask')){
el.attr('autocomplete', 'off');
}
for(i=0, maxlength=true; i < mask.length; i +=1){
translation=jMask.translation[ mask.charAt(i) ];
if(translation&&translation.recursive){
maxlength=false;
break;
}}
if(maxlength){
el.attr('maxlength', mask.length).data('mask-maxlength', true);
}
p.destroyEvents();
p.events();
caret=p.getCaret();
p.val(p.getMasked());
p.setCaret(caret);
}};
jMask.init(! el.is('input'));
};
var notSameMaskObject=function(field, mask, options){
var maskObject=$(field).data('mask'),
stringify=JSON.stringify,
value=$(field).val()||$(field).text();
options=options||{};
try {
if(typeof mask==='function'){
mask=mask(value);
}
return typeof maskObject!=='object'||stringify(maskObject.options)!==stringify(options)||maskObject.mask!==mask;
} catch(err){
window.console.log(err);
}},
HTMLAttributes=function(){
var input=$(this),
options={},
prefix='data-mask-',
mask=input.attr('data-mask');
if(input.attr(prefix + 'reverse')){
options.reverse=true;
}
if(input.attr(prefix + 'clearifnotmatch')){
options.clearIfNotMatch=true;
}
if(input.attr(prefix + 'selectonfocus')==='true'){
options.selectOnFocus=true;
}
if(input.attr(prefix + 'placeholder')){
options.placeholder=input.attr(prefix + 'placeholder');
}
if(notSameMaskObject(input, mask, options)){
return input.data('mask', new Mask(this, mask, options));
}},
eventSupported=function(eventName){
var el=document.createElement('div'),
isSupported;
eventName='on' + eventName;
isSupported=eventName in el;
if(! isSupported){
el.setAttribute(eventName, 'return;');
isSupported=typeof el[ eventName ]==='function';
}
el=null;
return isSupported;
};
$.maskWatchers={};
$.fn.mask=function(mask, options){
var selector=this.selector,
maskGlobals=$.jMaskGlobals,
interval=maskGlobals.watchInterval,
watchInputs,
maskFunction;
options=options||{};
watchInputs=options.watchInputs||maskGlobals.watchInputs;
maskFunction=function(){
if(notSameMaskObject(this, mask, options)){
return $(this).data('mask', new Mask(this, mask, options));
}};
$(this).each(maskFunction);
if(selector&&selector!==''&&watchInputs){
clearInterval($.maskWatchers[ selector ]);
$.maskWatchers[ selector ]=setInterval(function(){
$(document).find(selector).each(maskFunction);
}, interval);
}
return this;
};
$.fn.masked=function(val){
return this.data('mask').getMaskedVal(val);
};
$.fn.unmask=function(){
clearInterval($.maskWatchers[ this.selector ]);
delete $.maskWatchers[ this.selector ];
return this.each(function(){
var dataMask=$(this).data('mask');
if(dataMask){
dataMask.remove().removeData('mask');
}});
};
$.fn.cleanVal=function(){
return this.data('mask').getCleanVal();
};
$.applyDataMask=function(selector){
var $selector;
selector=selector||$.jMaskGlobals.maskElements;
$selector=selector instanceof $ ? selector:$(selector);
$selector.filter($.jMaskGlobals.dataMaskAttr).each(HTMLAttributes);
};
globals={
maskElements: 'input,td,span,div',
dataMaskAttr: '*[data-mask]',
dataMask: true,
watchInterval: 300,
watchInputs: true,
keyStrokeCompensation: 10,
useInput: ! /Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent)&&eventSupported('input'),
watchDataMask: false,
byPassKeys: [ 9, 16, 17, 18, 36, 37, 38, 39, 40, 91 ],
translation: {
0: { pattern: /\d/ },
9: { pattern: /\d/, optional: true },
'#': { pattern: /\d/, recursive: true },
A: { pattern: /[a-zA-Z0-9]/ },
S: { pattern: /[a-zA-Z]/ }}
};
$.jMaskGlobals=$.jMaskGlobals||{};
globals=$.jMaskGlobals=$.extend(true, {}, globals, $.jMaskGlobals);
if(globals.dataMask){
$.applyDataMask();
}
setInterval(function(){
if($.jMaskGlobals.watchDataMask){
$.applyDataMask();
}}, globals.watchInterval);
},
window.jQuery,
window.Zepto
));