/* ************************************************************************************* *\ * The MIT License * Copyright (c) 2007 Fabio Zendhi Nagao - http://zend.lojcomm.com.br * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * \* ************************************************************************************* */ var iMask = new Class({ defaultOptions: function() { return { targetClass: ".iMask", maskEmptyChr: '_', validNumbers: "1234567890", validAlphas: "abcdefghijklmnopqrstuvwxyz", validAlphaNums: "abcdefghijklmnopqrstuvwxyz1234567890", onFocus: function(obj) { obj.setStyles({"background-color":"#FFC", border:"1px solid #FC3"}); }, onBlur:function(obj) { obj.setStyles({"background-color":"#fff", border:"1px solid #ccc"}); }, onValid: function(event, obj) { obj.setStyles({"background-color":"#8f8", border:"1px solid #080"}); }, onInvalid:function(event, obj) { if(!event.shift) { obj.setStyles({"background-color":"#f88", border:"1px solid #800"}); } }, onKeyDown: Class.empty }; }, initialize: function(options) { this.setOptions(this.defaultOptions(), options); var fields = $$(this.options.targetClass); fields.each(function(obj, i) { obj.options = Json.evaluate(obj.alt); if(obj.options.type == "number") obj.setStyle("text-align", "right"); obj.addEvent("mousedown", function(event) { event = new Event(event); event.stop(); }); obj.addEvent("mouseup", function(event) { event = new Event(event); event.stop(); this._onMouseUp(event, obj); }.bind(this)); obj.addEvent("click", function(event) { event = new Event(event); event.stop(); }); obj.addEvent("keydown", function(event) { event = new Event(event); this._onKeyDown(event, obj); this.fireEvent("onKeyDown", obj, 20); }.bind(this)); obj.addEvent("keypress", function(event) { event = new Event(event); this._onKeyPress(event, obj); }.bind(this)); obj.addEvent("focus", function(event) { event = new Event(event); event.stop(); this._onFocus(event, obj); this.fireEvent("onFocus", obj, 20); }.bind(this)); obj.addEvent("blur", function(event) { event = new Event(event); event.stop(); this._onBlur(event, obj); this.fireEvent("onBlur", obj, 20); }.bind(this)); }.bind(this)); }, _onMouseUp: function(event, obj) { if(obj.options.type == "fixed") { var p = this._getSelectionStart(obj); this._setSelection(obj, p, (p + 1)); } else if(obj.options.type == "number") { this._setEnd(obj); } }, _onKeyDown: function(event, obj) { if(event.code == 13) { // enter obj.blur(); this._submitForm(obj); } else if(!(event.code == 9)) { event.stop(); if(obj.options.type == "fixed") { var p = this._getSelectionStart(obj); switch(event.code) { case 8: // Backspace this._selectPrevious(obj); break; case 36: // Home this._selectFirst(obj); break; case 35: // End this._selectLast(obj); break; case 37: // Left case 38: // Up this._selectPrevious(obj); break; case 39: // Right case 40: // Down this._selectNext(obj); break; case 46: // Delete this._updSelection(obj, p, this.options.maskEmptyChr); break; default: var chr = this._chrFromEvent(event); if(this._isViableInput(obj, p, chr)) { if(event.shift) {this._updSelection(obj, p, chr.toUpperCase());} else {this._updSelection(obj, p, chr);} this.fireEvent("onValid", [event, obj], 20); this._selectNext(obj); } else { this.fireEvent("onInvalid", [event, obj], 20); } break; } } else if(obj.options.type == "number") { switch(event.code) { case 8: // backspace case 46: // delete this._popNumber(obj); break; default: var chr = this._chrFromEvent(event); if(this.options.validNumbers.indexOf(chr) >= 0) { this._pushNumber(obj, chr); this.fireEvent("onValid", [event, obj], 20); } else { this.fireEvent("onInvalid", [event, obj], 20); } break; } } } }, _onKeyPress: function(event, obj) { if( !(event.code == 9) // tab && !(event.shift && event.code == 9) // shift + tab && !(event.code == 13) // enter && !(event.ctrl && event.code == 67) // ctrl + c && !(event.ctrl && event.code == 86) // ctrl + v && !(event.ctrl && event.code == 88) // ctrl + x ) { event.stop(); } }, _onFocus: function(event, obj) { if(obj.options.stripMask) obj.value = this._wearMask(obj, obj.value); if(obj.options.type == "fixed") {this._selectFirst.delay(20, this, obj);} else {this._setEnd.delay(20, this, obj);} }, _onBlur: function(event, obj) { if(obj.options.stripMask) obj.value = this._stripMask(obj); }, _selectAll: function(obj) { this._setSelection(obj, 0, obj.value.length); }, _selectFirst: function(obj) { for(var i = 0, len = obj.options.mask.length; i < len; i++) { if(this._isInputPosition(obj, i)) { this._setSelection(obj, i, (i + 1)); return; } } }, _selectLast: function(obj) { for(var i = (obj.options.mask.length - 1); i >= 0; i--) { if(this._isInputPosition(obj, i)) { this._setSelection(obj, i, (i + 1)); return; } } }, _selectPrevious: function(obj, p) { if(!$chk(p))p = this._getSelectionStart(obj); if(p <= 0) { this._selectFirst(obj); } else { if(this._isInputPosition(obj, (p - 1))) { this._setSelection(obj, (p - 1), p); } else { this._selectPrevious(obj, (p - 1)); } } }, _selectNext: function(obj, p) { if(!$chk(p))p = this._getSelectionEnd(obj); if(p >= obj.options.mask.length) { this._selectLast(obj); } else { if(this._isInputPosition(obj, p)) { this._setSelection(obj, p, (p + 1)); } else { this._selectNext(obj, (p + 1)); } } }, _setSelection: function(obj, a, b) { if(obj.setSelectionRange) { obj.focus(); obj.setSelectionRange(a, b); } else if(obj.createTextRange) { var r = obj.createTextRange(); r.collapse(); r.moveStart("character", a); r.moveEnd("character", (b - a)); r.select(); } }, _updSelection: function(obj, p, chr) { var value = obj.value; var output = ""; output += value.substring(0, p); output += chr; output += value.substr(p + 1); obj.value = output; this._setSelection(obj, p, (p + 1)); }, _setEnd: function(obj) { var len = obj.value.length; this._setSelection(obj, len, len); }, _getSelectionStart: function(obj) { var p = 0; if(obj.selectionStart) { if($type(obj.selectionStart) == "number") p = obj.selectionStart; } else if(document.selection) { var r = document.selection.createRange().duplicate(); r.moveEnd("character", obj.value.length); p = obj.value.lastIndexOf(r.text); if(r.text == "") p = obj.value.length; } return p; }, _getSelectionEnd: function(obj) { var p = 0; if(obj.selectionEnd) { if($type(obj.selectionEnd) == "number") {p = obj.selectionEnd;} } else if(document.selection) { var r = document.selection.createRange().duplicate(); r.moveStart("character", -obj.value.length); p = r.text.length; } return p; }, _isInputPosition: function(obj, p) { var mask = obj.options.mask.toLowerCase(); var chr = mask.charAt(p); if("9ax".indexOf(chr) >= 0) return true; return false; }, _isViableInput: function(obj, p, chr) { var mask = obj.options.mask.toLowerCase(); var chMask = mask.charAt(p); switch(chMask) { case '9': if(this.options.validNumbers.indexOf(chr) >= 0) return true; break; case 'a': if(this.options.validAlphas.indexOf(chr) >= 0) return true; break; case 'x': if(this.options.validAlphaNums.indexOf(chr) >= 0) return true; break; default: return false; break; } }, _wearMask: function(obj, str) { var mask = obj.options.mask.toLowerCase(); var output = ""; for(var i = 0, u = 0, len = mask.length; i < len; i++) { switch(mask.charAt(i)) { case '9': if(this.options.validNumbers.indexOf(str.charAt(u).toLowerCase()) >= 0) { if(str.charAt(u) == "") {output += this.options.maskEmptyChr;} else {output += str.charAt(u++);} } else { output += this.options.maskEmptyChr; } break; case 'a': if(this.options.validAlphas.indexOf(str.charAt(u).toLowerCase()) >= 0) { if(str.charAt(u) == "") {output += this.options.maskEmptyChr;} else {output += str.charAt(u++);} } else { output += this.options.maskEmptyChr; } break; case 'x': if(this.options.validAlphaNums.indexOf(str.charAt(u).toLowerCase()) >= 0) { if(str.charAt(u) == "") {output += this.options.maskEmptyChr;} else {output += str.charAt(u++);} } else { output += this.options.maskEmptyChr; } break; default: output += mask.charAt(i); break } } return output; }, _stripMask: function(obj) { var value = obj.value; if("" == value) return ""; var output = ""; if(obj.options.type == "fixed") { for(var i = 0, len = value.length; i < len; i++) { if((value.charAt(i) != this.options.maskEmptyChr) && (this._isInputPosition(obj, i))) {output += value.charAt(i);} } } else if(obj.options.type == "number") { for(var i = 0, len = value.length; i < len; i++) { if(this.options.validNumbers.indexOf(value.charAt(i)) >= 0) {output += value.charAt(i);} } } return output; }, _chrFromEvent: function(event) { var chr = ''; switch(event.code) { case 48: case 96: // 0 and numpad 0 chr = '0'; break; case 49: case 97: // 1 and numpad 1 chr = '1'; break; case 50: case 98: // 2 and numpad 2 chr = '2'; break; case 51: case 99: // 3 and numpad 3 chr = '3'; break; case 52: case 100: // 4 and numpad 4 chr = '4'; break; case 53: case 101: // 5 and numpad 5 chr = '5'; break; case 54: case 102: // 6 and numpad 6 chr = '6'; break; case 55: case 103: // 7 and numpad 7 chr = '7'; break; case 56: case 104: // 8 and numpad 8 chr = '8'; break; case 57: case 105: // 9 and numpad 9 chr = '9'; break; default: chr = event.key; // key pressed as a lowercase string break; } return chr; }, _pushNumber: function(obj, chr) { obj.value = obj.value + chr; this._formatNumber(obj); }, _popNumber: function(obj) { obj.value = obj.value.substring(0, (obj.value.length - 1)); this._formatNumber(obj); }, _formatNumber: function(obj) { // stripLeadingZeros var str2 = this._stripMask(obj); var str1 = ""; for(var i = 0, len = str2.length; i < len; i++) { if('0' != str2.charAt(i)) { str1 = str2.substr(i); break; } } // wearLeadingZeros str2 = str1; str1 = ""; for(var len = str2.length, i = obj.options.decDigits; len <= i; len++) { str1 += "0"; } str1 += str2; // decimalSymbol str2 = str1.substr(str1.length - obj.options.decDigits) str1 = str1.substring(0, (str1.length - obj.options.decDigits)) // groupSymbols var re = new RegExp("(\\d+)(\\d{"+ obj.options.groupDigits +"})"); while(re.test(str1)) { str1 = str1.replace(re, "$1"+ obj.options.groupSymbol +"$2"); } obj.value = str1 + obj.options.decSymbol + str2; }, _getObjForm: function(obj) { var parent = obj.getParent(); if(parent.getTag() == "form") { return parent; } else { return this._getObjForm(parent); } }, _submitForm: function(obj) { var form = this._getObjForm(obj); form.submit(); } }); iMask.implement(new Events); // Implements addEvent(type, fn), fireEvent(type, [args], delay) and removeEvent(type, fn) iMask.implement(new Options);// Implements setOptions(defaults, options)