/*!
* Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
*
* This Software is licenced under the LGPL Licence (GNU Lesser General
* Public License).
* In addition to the LGPL Licence the Software is subject to the
* following conditions:
*
* i every modification must be public and comunicated to the Author
* ii every "jsbean" added to this library must be self consistent
* except for the dependence from jsbeans-x.x.x.js
* iii 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.
*/
/**
* A collection of utilities for inputs autocompletion
* @namespace jsbeans
* @class SmartInsert
* @static
* @author Francesco Mele
* @credits Antonio Guerra for original inspiration
*/
jsbeans.SmartInsert = {
/**
* Prevents the 'enter', useful in you don't want an accindental enter over an {@param} <code class="param">input</code>
* @method noReturn
* @param event {event}
* @param input {DOM} an input of type text or password
* */
noReturn: function(event, obj) {
var kc = this._getKeyCode(event);
var status = true;
if (kc == 13) {
status = false;
}
this._returnValue(event, status);
},
/**
* Avoids entering characters other than integers
* @method int
* @param event {event}
* @param input {DOM} an input of type text
* */
"int": function(event,obj) {
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9]/;
if (chrCode.search(re) == -1) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* Avoids entering characters other than floats, optionally indicating precision.
* @method float
* @param event {event}
* @param input {DOM} an input of type text
* @param [options] {JSON}
* <pre>
* precision: string // precision, optional with default "99999999999999999999.9999999999"
* </pre>
* */
"float": function(event,obj) {
var kc = this._getKeyCode(event);
var status = true;
var args = arguments[2] || {};
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9]/;
if (obj.value.length == 0 && chrCode.search(re) == -1) {
status = false;
}
else if (obj.value.length > 0) {
var sp = obj.value.split(".");
var spLen = sp.length;
if (spLen >= 2 && chrCode == ".") {
status = false;
}
else if (chrCode != "." && chrCode.search(re) == -1) {
status = false;
}
else {
var precision = args.precision || "99999999999999999999.9999999999";
var fullLen = parseInt(precision.split(".")[0]);
var decimalLen = parseInt(precision.split(".")[1]);
if (chrCode == "." && decimalLen <= 0) {
status = false;
}
if (spLen == 2 && sp[1].length == decimalLen) {
status = false;
}
if (sp[0].length == fullLen - decimalLen) {
if (spLen == 1 && chrCode != "." && decimalLen > 0) {
status = false;
}
}
}
}
}
this._returnValue(event, status);
},
/**
* Autocompletion for date.
* @method date
* @param event {event}
* @param input {DOM} an input of type text
* @param [pattern] {String} one among "dd/MM/yyyy", "dd-MM-yyyy", "MM/dd/yyyy" and "MM-dd-yyyy". Default "MM/dd/yyyy".
* */
date: function(event, obj) {
var dateFormat = null;
if (arguments.length == 3) {
var supportedFormats = ["dd/MM/yyyy", "dd-MM-yyyy", "MM/dd/yyyy", "MM-dd-yyyy"];
var isSupported = false;
for (var i = 0; i < supportedFormats.length; i++) {
if (arguments[2] == supportedFormats[i]) {
dateFormat = arguments[2];
isSupported = true;
break;
}
}
if (!isSupported) {
return;
}
}
if (dateFormat == null) {
var getDefaultFormat = function() {
var res = "dd/MM/yyyy";
var lang = null;
if (typeof navigator.userLanguage != "undefined") {
lang = navigator.userLanguage.toUpperCase();
}
else if (typeof navigator.language != "undefined") {
lang = navigator.language.toUpperCase();
}
if (lang != null && lang.indexOf("IT") != -1) {
res = "dd/MM/yyyy";
}
else {
res = "MM/dd/yyyy";
}
return res;
};
dateFormat = getDefaultFormat();
}
var separator = dateFormat.indexOf("/") != -1 ? "/" : "-";
var format = function(obj, chrCode) {
var append = false;
var status = true;
var dPart = obj.value.split(separator);
if (dateFormat.indexOf("d") == 0) {
switch (dPart.length) {
case 1 :
var day = dPart[0];
if (day.length == 1) {
if (chrCode == separator) {
obj.value = '0' + obj.value;
}
else if (day.charAt(0) > '3') {
obj.value = '0' + obj.value;
append = true;
}
else if (day.charAt(0) == '3' && chrCode > '1') {
obj.value = '0' + obj.value;
append = true;
}
}
else if (day.length == 2) {
if (chrCode != separator){
append = true;
}
}
break;
case 2 :
var day = dPart[0];
var month = dPart[1];
if (day.length == 1) {
obj.value = '0' + obj.value;
}
if (chrCode == separator) {
if (month.length == 1) {
obj.value = obj.value.substring(0, obj.value.indexOf(separator) + 1) + '0' + obj.value.substring(obj.value.indexOf(separator) + 1, obj.value.length);
}
}
else if (month.length == 2) {
if (chrCode != separator) {
append = true;
}
}
else if (month.length == 1) {
if (month.charAt(0) > '1' || (month.charAt(0) == '1' && chrCode > '2')) {
obj.value = obj.value.substring(0, obj.value.lastIndexOf(separator) + 1) + '0' + obj.value.substring(obj.value.lastIndexOf(separator) + 1, obj.value.length);
append = true;
}
}
break;
}
}
else {
switch (dPart.length) {
case 1 :
var month = dPart[0];
if (month.length == 1) {
if (chrCode == separator) {
obj.value = '0' + obj.value;
}
else if (month.charAt(0) > '1') {
obj.value = '0' + obj.value;
append = true;
}
else if (month.charAt(0) == '1' && chrCode>'2') {
obj.value = '0' + obj.value;
append = true;
}
}
else if (month.length == 2) {
if (chrCode != separator) {
append = true;
}
}
break;
case 2 :
var month = dPart[0];
var day = dPart[1];
if (month.length == 1) {
obj.value = '0' + obj.value;
}
if (chrCode == separator) {
if (day.length == 1) {
obj.value = obj.value.substring(0, obj.value.indexOf(separator) + 1) + '0' + obj.value.substring(obj.value.indexOf(separator) + 1, obj.value.length);
}
}
else if (day.length == 2) {
if (chrCode != separator) {
append = true;
}
}
else if (day.length == 1) {
if (day.charAt(0) > '3' || (day.charAt(0) == '3' && chrCode > '1')) {
obj.value = obj.value.substring(0, obj.value.lastIndexOf(separator) + 1) + '0' + obj.value.substring(obj.value.lastIndexOf(separator) + 1, obj.value.length);
append = true;
}
}
break;
}
}
if (append) {
obj.value = obj.value + separator;
}
return status;
};
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9\/]/;
status = format(obj, chrCode);
if (chrCode.search(re) == -1 || obj.value.length >= 10) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* Autocompletion for IP addresses.
* @method ip
* @param event {event}
* @param input {DOM} an input of type text
* */
ip: function(event, obj) {
var separator = ".";
var format = function(obj, chrCode) {
var append = false;
var status = true;
var part = obj.value.split(separator);
if ((obj.value == null || obj.value == "") && chrCode == "0") {
obj.value = chrCode + separator;
return false;
}
if (part.length > 4) {
status = false;
}
switch (part.length) {
case 1:
var _1 = part[0];
var n1 = parseInt(_1.charAt(0));
var n2 = parseInt(_1.charAt(1));
var nCode = parseInt(chrCode);
if (_1.length == 1) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 == 0) {
obj.value = obj.value + separator;
}
}
else if (_1.length == 2) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 >= 3) {
obj.value = obj.value + separator;
}
else if (n1 == 2) {
if (n2 == 5 && nCode > 5) {
obj.value = obj.value + separator;
}
else if (n2 > 5) {
obj.value = obj.value + separator;
}
}
}
else if (_1.length == 3) {
obj.value = obj.value + separator;
}
break;
case 2:
var _1 = part[1];
var n1 = parseInt(_1.charAt(0));
var n2 = parseInt(_1.charAt(1));
var nCode = parseInt(chrCode);
if (_1.length == 1) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 == 0) {
obj.value = obj.value + separator;
}
}
else if (_1.length == 2) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 >= 3) {
obj.value = obj.value + separator;
}
else if (n1 == 2) {
if (n2 == 5 && nCode > 5) {
obj.value = obj.value + separator;
}
else if (n2 > 5) {
obj.value = obj.value + separator;
}
if (nCode == 0) {
status = false;
}
}
}
else if (_1.length == 3) {
if (chrCode != separator){
obj.value = obj.value + separator;
}
}
break;
case 3:
var _1 = part[2];
var n1 = parseInt(_1.charAt(0));
var n2 = parseInt(_1.charAt(1));
var nCode = parseInt(chrCode);
if (_1.length == 1) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 == 0) {
obj.value = obj.value + separator;
}
}
else if (_1.length == 2) {
if (chrCode == separator) {
obj.value = obj.value + separator;
}
else if (n1 >= 3) {
obj.value = obj.value + separator;
}
else if (n1 == 2) {
if (n2 == 5 && nCode > 5) {
obj.value = obj.value + separator;
}
else if (n2 > 5) {
obj.value = obj.value + separator;
}
if (nCode == 0) {
status = false;
}
}
}
else if (_1.length == 3) {
if (chrCode != separator){
obj.value = obj.value + separator;
}
}
break;
case 4:
var _1 = part[3];
var n1 = parseInt(_1.charAt(0));
if (n1 == 0) {
status = false;
break;
}
var n2 = parseInt(_1.charAt(1));
var nCode = parseInt(chrCode);
if (nCode == 0 && _1.length == 0) {
obj.value = obj.value + "0";
status = false;
break;
}
if (_1.length == 1) {
if (chrCode == separator) {
status = false;
}
else if (n1 == 0) {
status = false;
}
}
else if (_1.length == 2) {
if (chrCode == separator) {
status = false;
}
else if (n1 >= 3) {
status = false;
}
else if (n1 == 2) {
if (n2 == 5 && nCode > 5) {
status = false;
}
else if (n2 > 5) {
status = false;
}
if (nCode == 0) {
}
}
}
else if (_1.length == 3) {
if (chrCode != separator){
status = false;
}
}
break;
}
return status;
};
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9\/]/;
status = format(obj, chrCode);
if (chrCode.search(re) == -1 || obj.value.length >= 15) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* Avoids entering character single or double quotes (" or ').
* @method noQuote
* @param event {event}
* @param input {DOM} an input of type text
* */
noQuote: function(event, obj) {
var kc = this._getKeyCode(event);
var status = true;
// 34 = double quote
// 39 = single quote
if (kc == 34 || kc == 39) {
status = false;
}
this._returnValue(event, status);
},
/**
* Allows entering only characters matching given {@param} <code class="param">re</code>.
* @method mask
* @param event {event}
* @param input {DOM} an input of type text
* @param re {RegEx}
* */
mask: function(event,obj,re) {
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
if (chrCode.search(re) == -1) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* Allows entering only characters DON'T matching given {@param} <code class="param">re</code>.
* @method unmask
* @param event {event}
* @param input {DOM} an input of type text
* @param re {RegEx}
* */
unmask: function(event,obj,re) {
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
if (chrCode.search(re) != -1) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* Autocompletion for currency, optionally indicating precision.<br/>
* It uses dots for thousands and commas for decimals.
* @method currency
* @param event {event}
* @param input {DOM} an input of type text
* @param [options] {JSON}
* <pre>
* precision: string // precision, optional with default "9999999999999999999999999.2"
* </pre>
* */
currency: function(event,obj) {
var kc = this._getKeyCode(event);
var status = true;
var args = arguments[2] || {};
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9]/;
if (obj.value.length == 0 && chrCode.search(re) == -1) {
status = false;
}
else if (obj.value.length > 0) {
var sp = obj.value.split(",");
var spLen = sp.length;
if (spLen >= 2 && chrCode == ",") {
status = false;
}
else if (chrCode != "." && chrCode != "," && chrCode.search(re) == -1) {
status = false;
}
else {
if (chrCode == ".") {
this._returnValue(event, false);
return;
}
var formatLeft = function(v) {
v = v.split(".").join("").split("").reverse();
var res = "";
for (var i = 1; i < v.length; i++) {
if ((i) % 3 == 0) {
res += ".";
}
res += v[i-1];
}
return res.split("").reverse().join("");
};
var left = sp[0];
var right = sp[1] || null;
var precision = args.precision || "9999999999999999999999999.2";
var fullLen = parseInt(precision.split(".")[0]);
var centLen = parseInt(precision.split(".")[1]);
if (chrCode == "," && centLen <= 0) {
this._returnValue(event, false);
return;
}
if (right != null && typeof(right) != "undefined") {
if (chrCode.search(re) == -1 || right.length >= centLen) {
this._returnValue(event, false);
return;
}
}
else if (chrCode == ",") {
}
else if (obj.value.indexOf(",") != (obj.value.length - 1)) {
if (left.split(".").join("").length >= (fullLen - centLen)) {
this._returnValue(event, false);
return;
}
else {
obj.value = formatLeft(chrCode + obj.value);
}
}
}
}
}
this._returnValue(event, status);
},
/**
* Autocompletion for time.
* @method time
* @param event {event}
* @param input {DOM} an input of type text
* @param [pattern] {String} one among "hh:mm", "hh.mm", "hh:mm:ss" and "hh.mm.ss". Default "hh:mm".
* */
time: function(event, obj) {
var timeFormat = null;
if (arguments.length == 3) {
var supportedFormats = ["hh:mm", "hh.mm", "hh:mm:ss", "hh.mm.ss"];
var isSupported = false;
for (var i = 0; i < supportedFormats.length; i++) {
if (arguments[2].toLowerCase() == supportedFormats[i].toLowerCase()) {
timeFormat = arguments[2];
isSupported = true;
break;
}
}
if (!isSupported) {
return;
}
}
if (timeFormat == null) {
timeFormat = "hh:mm";
}
var separator = timeFormat.indexOf(":") != -1 ? ":" : ".";
var format = function(obj, chrCode) {
if (obj.value.length == timeFormat.length) {
return false;
}
var status = true;
var tPart = obj.value.split(separator);
switch (tPart.length) {
case 1 :
var hour = tPart[0];
if (hour.length == 1) {
if (chrCode == separator) {
obj.value = '0' + obj.value;
}
else if (hour.charAt(0) > '2' || (hour.charAt(0) == '2' && chrCode > '4')) {
obj.value = '0' + obj.value + separator;
if (chrCode > '5') {
obj.value = obj.value + '0';
}
}
}
else if (hour.length == 2) {
if (chrCode != separator) {
obj.value = obj.value + separator;
}
}
break;
case 2 :
var hour = tPart[0];
var minutes = tPart[1];
if (hour.length == 1) {
obj.value = '0' + obj.value;
}
if (chrCode == separator /*|| minutes.length == 2*/) {
status = false;
}
else if (minutes.length == 1) {
if (minutes.charAt(0) > '5') {
obj.value = obj.value.substring(0, obj.value.lastIndexOf(separator) + 1) + '0' + obj.value.substring(obj.value.lastIndexOf(separator) + 1, obj.value.length);
if (timeFormat.indexOf("ss") != -1) {
obj.value = obj.value + separator;
}
}
}
else if (minutes.length == 2) {
if (chrCode != separator) {
obj.value = obj.value + separator;
}
}
break;
case 3 :
var minutes = tPart[1];
var seconds = tPart[2];
if (minutes.length == 1) {
obj.value = '0' + obj.value;
}
if (chrCode == separator /*|| minutes.length == 2*/) {
status = false;
}
else if (seconds.length == 1) {
if (seconds.charAt(0) > '5') {
obj.value = obj.value.substring(0, obj.value.lastIndexOf(separator) + 1) + '0' + obj.value.substring(obj.value.lastIndexOf(separator) + 1, obj.value.length);
status = false;
}
}
break;
}
return status;
};
var kc = this._getKeyCode(event);
var status = true;
if (!this._isSpecialKey(event)) {
var chrCode = String.fromCharCode(kc);
var re = /[0-9]/;
status = format(obj, chrCode);
if (chrCode.search(re) == -1 || obj.value.length >= timeFormat.length) {
status = false;
}
}
this._returnValue(event, status);
},
/**
* @method _returnValue
* @param event {event}
* @param object {DOM}
* @private
* */
_returnValue: function(event, status) {
if (!status && document.addEventListener) {
event.preventDefault();
}
event.returnValue = status;
event.cancelBubble = !status;
},
/**
* @method _getKeyCode
* @param event {event}
* @param object {DOM}
* @private
* */
_getKeyCode: function(event) {
var kc = event.keyCode;
if (kc == 0) {
kc = event.charCode;
}
return kc;
},
/**
* @method _isSpecialKey
* @param event {event}
* @param object {DOM}
* @private
* */
_isSpecialKey: function(event) {
var kc = event.keyCode;
// 8 = backspace
// 13 = enter
if (kc == 13 || kc == 8 || (kc != 0 && (typeof(event.charCode) != "undefined") && !event.charCode)) {
return true;
}
return false;
}
};