1 /**
   2  * @namespace JekFk Framework
   3  * @author Vincenzo De Vivo http://www.vincenzodevivo.com
   4  */
   5 var Jek = {
   6 
   7 	/**
   8 	 * JekFk framework info
   9 	 * 
  10 	 * @namespace
  11 	 */
  12 	info : {
  13 		/**
  14 		 * The current name of framework
  15 		 * 
  16 		 * @type String
  17 		 */
  18 		name : 'JekFk',
  19 		/**
  20 		 * The current version of framework
  21 		 * 
  22 		 * @type String
  23 		 */
  24 		version : '0.6'
  25 	},
  26 	/**
  27 	 * Framework statistics
  28 	 * 
  29 	 * @type JekStats
  30 	 */
  31 	fkStats : new JekStats(),
  32 	/**
  33 	 * Generates a unique ID
  34 	 * 
  35 	 * @param {String}idType[optional]
  36 	 * 
  37 	 * <b><i>default</i></b> value: "jek"
  38 	 * @returns {String} A unique ID
  39 	 */
  40 	id : function(idType) {
  41 		idType = this.defParam(idType, 'jek');
  42 		return idType + this.fkStats.increase(idType);
  43 	},
  44 	/**
  45 	 * If param is undefined then returns default parameter else returns param
  46 	 * 
  47 	 * @param {Object}param
  48 	 * @param {Object}defaultParam
  49 	 * @returns {Object}
  50 	 */
  51 	defParam : function(param, defaultParam) {
  52 		if (this.isUndef(param)) {
  53 			return defaultParam;
  54 		} else {
  55 			return param;
  56 		}
  57 	},
  58 	/**
  59 	 * Returns true if <b>o</b> is of type Boolean, false otherwise.
  60 	 * 
  61 	 * @param {any}o
  62 	 * @returns {Boolean}
  63 	 */
  64 	isBoolean : function(o) {
  65 		return typeof o === 'boolean';
  66 	},
  67 	/**
  68 	 * Returns true if <b>o</b> is null, false otherwise.
  69 	 * 
  70 	 * @param {any}o
  71 	 * @returns {Boolean}
  72 	 */
  73 	isNull : function(o) {
  74 		return o === null;
  75 	},
  76 	/**
  77 	 * Returns true if <b>o</b> is of type String, false otherwise.
  78 	 * 
  79 	 * @param {any}o
  80 	 * @returns {Boolean}
  81 	 */
  82 	isString : function(o) {
  83 		return this.toObjName(o) == '[object String]';
  84 	},
  85 	/**
  86 	 * Returns true if <b>o</b> is of type Number, false otherwise.
  87 	 * 
  88 	 * @param {any}o
  89 	 * @returns {Boolean}
  90 	 */
  91 	isNumber : function(o) {
  92 		return this.toObjName(o) == '[object Number]';
  93 	},
  94 	/**
  95 	 * Returns true if <b>o</b> is of type Array, false otherwise.
  96 	 * 
  97 	 * @param {any}o
  98 	 * @returns {Boolean}
  99 	 */
 100 	isArray : function(o) {
 101 		return this.toObjName(o) == '[object Array]';
 102 	},
 103 	/**
 104 	 * Returns true if <b>o</b> is of type Function, false otherwise.
 105 	 * 
 106 	 * @param {any}o
 107 	 * @returns {Boolean}
 108 	 */
 109 	isFn : function(o) {
 110 		return typeof o === 'function';
 111 	},
 112 	/**
 113 	 * Returns true if <b>o</b> is of type undefined, false otherwise.
 114 	 * 
 115 	 * @param {any}o
 116 	 * @returns {Boolean}
 117 	 */
 118 	isUndef : function(o) {
 119 		return typeof o === 'undefined';
 120 	},
 121 	/**
 122 	 * Returns true if <b>o</b> is of type Object, false otherwise.
 123 	 * 
 124 	 * @param {any}o
 125 	 * @returns {Boolean}
 126 	 */
 127 	isObject : function(o) {
 128 		return !!o && this.toObjName(o) === '[object Object]';
 129 	},
 130 	/**
 131 	 * Return true if email is correct, false � otherwise
 132 	 * 
 133 	 * @param {String}email
 134 	 * @returns {Boolean}
 135 	 */
 136 	isEmail : function(email) {
 137 		var eReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
 138 		return eReg.test(email);
 139 	},
 140 	/**
 141 	 * Returns object toString: '[object Object]' | '[object String]' | '[object
 142 	 * Array]' | '[object Number]' | ...
 143 	 * 
 144 	 * @param {any}o
 145 	 * @returns {String}
 146 	 */
 147 	toObjName : function(o) {
 148 		return Object.prototype.toString.call(o);
 149 	},
 150 	/**
 151 	 * Round a number
 152 	 * 
 153 	 * @param {Number}value
 154 	 * @param {Number}decimalPositions
 155 	 * @returns {Number}
 156 	 */
 157 	roundTo : function(value, decimalPositions) {
 158 		var i = value * Math.pow(10, decimalPositions);
 159 		i = Math.round(i);
 160 		return i / Math.pow(10, decimalPositions);
 161 	},
 162 	/**
 163 	 * Generates a Globally Unique IDentifier (GUID)
 164 	 * 
 165 	 * @returns {String}
 166 	 */
 167 	guid : function() {
 168 		function r() {
 169 			var now = new Date();
 170 			var seed = now.getSeconds();
 171 			return ((1 + Math.random(seed)) * parseInt('10000', 16)).toString(
 172 					16).substring(1, 5);
 173 		}
 174 		return (r() + r() + '-' + r() + '-' + r() + '-' + r() + '-' + r() + r() + r())
 175 				.toUpperCase();
 176 	},
 177 	/**
 178 	 * Escapes a string according to the JSON specification.
 179 	 * 
 180 	 * @param {String}str
 181 	 * @returns {String}
 182 	 */
 183 	escapeString : function(str) {
 184 		var s = "";
 185 		var ch;
 186 		var len = str.length;
 187 		for ( var i = 0; i < len; i++) {
 188 			ch = str.charAt(i);
 189 			switch (ch) {
 190 			case '"':
 191 				s += "\\\"";
 192 				break;
 193 			case '\\':
 194 				s += "\\\\";
 195 				break;
 196 			case '\b':
 197 				s += "\\b";
 198 				break;
 199 			case '\f':
 200 				s += "\\f";
 201 				break;
 202 			case '\n':
 203 				s += "\\n";
 204 				break;
 205 			case '\r':
 206 				s += "\\r";
 207 				break;
 208 			case '\t':
 209 				s += "\\t";
 210 				break;
 211 			default:
 212 				if (ch < ' ') {
 213 					var hexCode = ch.charCodeAt(0).toString(16);
 214 					var zeroPad = hexCode.length == 2 ? "00" : "000";
 215 					s += "\\u" + zeroPad + hexCode;
 216 				} else {
 217 					s += ch;
 218 				}
 219 			}
 220 		}
 221 		return s;
 222 	},
 223 	/**
 224 	 * Decodes a JSON string to an Object.
 225 	 * 
 226 	 * @param {String}json
 227 	 * @returns {Object}
 228 	 */
 229 	fromJSON : function(json) {
 230 		return (new Function("return " + json))();
 231 	},
 232 	/**
 233 	 * Returns a string containing the JSON representation of 'obj' value.
 234 	 * 
 235 	 * @param {Object}obj
 236 	 * @returns {String}
 237 	 */
 238 	toJSON : function(obj) {
 239 		var json = new String();
 240 		var value;
 241 		var separator = ",";
 242 		var n = 0;
 243 		var obj_isFlat = false;
 244 		var obj_isArr = false;
 245 		var obj_isObj = false;
 246 
 247 		if (this.isUndef(obj) || this.isBoolean(obj) || this.isNull(obj)
 248 				|| this.isNumber(obj)) {
 249 			obj_isFlat = true;
 250 			json += String(obj);
 251 		} else if (this.isString(obj)) {
 252 			obj_isFlat = true;
 253 			obj = this.escapeString(obj);
 254 			json += '"' + obj + '"';
 255 		} else if (obj_isArr = this.isArray(obj)) {
 256 			json = "[";
 257 		} else if (obj_isObj = this.isObject(obj)) {
 258 			json = "{";
 259 		}
 260 		if (!obj_isFlat) {
 261 			for ( var key in obj) {
 262 				value = obj[key];
 263 				key = '"' + key + '"';
 264 				if (n > 0 && !this.isFn(value)) {
 265 					json += separator;
 266 				}
 267 				if (this.isArray(value)) {
 268 					if (!obj_isArr) {
 269 						json += key + ":";
 270 					}
 271 					json += this.toJSON(value);
 272 					n++;
 273 				} else if (obj_isArr) {
 274 					json += this.toJSON(value);
 275 					n++;
 276 				} else if (!this.isFn(value)) {
 277 					json += key + ":" + this.toJSON(value);
 278 					n++;
 279 				}
 280 
 281 			}
 282 			if (obj_isArr) {
 283 				json += "]";
 284 			} else if (obj_isObj) {
 285 				json += "}";
 286 			}
 287 		}
 288 		return json;
 289 	},
 290 	/**
 291 	 * Trim leading whitespace characters from the String.
 292 	 * 
 293 	 * @param {String}str
 294 	 * @returns {String}
 295 	 */
 296 	trimLeft : function(str) {
 297 		return str.replace(/^\s+/, '');
 298 	},
 299 	/**
 300 	 * Trim trailing whitespace characters from the String.
 301 	 * 
 302 	 * @param {String}str
 303 	 * @returns {String}
 304 	 */
 305 	trimRight : function(str) {
 306 		return str.replace(/\s+$/, '');
 307 	},
 308 	/**
 309 	 * Trim marginal whitespace characters from the String.
 310 	 * 
 311 	 * @param {String}str
 312 	 * @returns {String}
 313 	 */
 314 	trim : function(str) {
 315 		return str.replace(/^\s+|\s+$/g, '');
 316 	},
 317 	/**
 318 	 * Read in a sequence of words from string input and capitalize each one
 319 	 * (make first letter uppercase; make rest lowercase).
 320 	 * 
 321 	 * @param {String}str
 322 	 * @returns {String}
 323 	 */
 324 	capitalize : function(str) {
 325 		function upp_0() {
 326 			return arguments[0].toUpperCase();
 327 		}
 328 		return str.toLowerCase().replace(/\b[a-z]/g, upp_0);
 329 	},
 330 	/**
 331 	 * Repeats the specified string 'n' times to form a new String.
 332 	 * 
 333 	 * @param {String}str
 334 	 *            String to repeat
 335 	 * @param {Number}n
 336 	 *            How many times to repeat the string
 337 	 * @returns {String}
 338 	 */
 339 	repeat : function(str, n) {
 340 		var sb = new JekString();
 341 		for ( var j = 0; j < n; j++) {
 342 			sb.append(str);
 343 		}
 344 		return sb.toString();
 345 	},
 346 
 347 	/**
 348 	 * Split string into word array
 349 	 * 
 350 	 * @param {String}string
 351 	 * @returns {Array}
 352 	 */
 353 	words : function(string) {
 354 		string = string.replace(/^\W+|\W+$/g, '');
 355 		var arr = string.split(/\W+/);
 356 		return arr;
 357 	},
 358 	/**
 359 	 * Switch a string between two alternating values
 360 	 * 
 361 	 * @param {String}str
 362 	 * @param {String}val1
 363 	 * @param {String}val2
 364 	 * @returns {String}
 365 	 */
 366 	toggle : function(str, val1, val2) {
 367 		return str == val1 ? val2 : val1;
 368 	},
 369 	/**
 370 	 * Encodes URL
 371 	 * 
 372 	 * @param {String}str
 373 	 * @returns {String}
 374 	 */
 375 	urlEncode : function(str) {
 376 		return escape(Jek.conversion.utf8Encode(str));
 377 	},
 378 	/**
 379 	 * Decodes URL
 380 	 * 
 381 	 * @param {String}str
 382 	 * @returns {String}
 383 	 */
 384 	urlDecode : function(str) {
 385 		return Jek.conversion.utf8Decode(unescape(str));
 386 	},
 387 
 388 	 /**
 389 	 * padLeft
 390 	 * 
 391 	 * @param str
 392 	 * @param char
 393 	 * @param num
 394 	 */
 395 	padLeft : function(str, ch, num) {
 396 		var re = new RegExp(".{" + num + "}$");
 397 		if (!ch) ch = " ";
 398         var pad = Jek.repeat(ch, num);
 399         return re.exec(pad + str)[0];
 400 	},
 401 	/**
 402 	 * padRight
 403 	 * 
 404 	 * @param str
 405 	 * @param char
 406 	 * @param num
 407 	 */
 408 	padRight : function (str, ch, num){
 409         var re = new RegExp("^.{" + num + "}");
 410         if (!ch) ch = " ";
 411         var pad = Jek.repeat(ch, num);
 412         return re.exec(str + pad)[0];
 413     },
 414     
 415     /**
 416 	 * padRight
 417 	 * 
 418 	 * @param str
 419 	 * @param char
 420 	 * @param num
 421 	 */
 422     removeVowels : function (str){
 423 		var vowels = /[aeiou]/gi;
 424 		return str.replace(vowels,'');
 425     },
 426 
 427 	/**
 428 	 * Create new package
 429 	 * 
 430 	 * @param {String}pk
 431 	 * @param {any}newObj[optional]
 432 	 * @returns {Object}
 433 	 */
 434 	newPackage : function(pk, newObj) {
 435 		var obj = window;
 436 		var elArr = pk.split('.');
 437 		var el;
 438 		var len = elArr.length;
 439 		for ( var i = 0; i < len; i++) {
 440 			el = elArr[i];
 441 			if (typeof newObj != 'undefined' && i + 1 == len) {
 442 				obj[el] = newObj;
 443 			} else if (!obj[el]) {
 444 				obj[el] = {};
 445 			}
 446 			obj = obj[el];
 447 		}
 448 		return obj;
 449 	},
 450 	/**
 451 	 * Try all functions in input
 452 	 * 
 453 	 * @param {Function}args...
 454 	 * @returns {any}
 455 	 */
 456 	tryFns : function() {
 457 		var ret;
 458 		for ( var i = 0, len = arguments.length; i < len; i++) {
 459 			var fn = arguments[i];
 460 			try {
 461 				ret = fn();
 462 				return ret;
 463 			} catch (e) {
 464 			}
 465 		}
 466 		return ret;
 467 	},
 468 	/**
 469 	 * @namespace
 470 	 */
 471 	array : {
 472 		/**
 473 		 * Returns the minimum element of the given array, according to the
 474 		 * natural ordering of its elements.
 475 		 * 
 476 		 * @param {Array}array
 477 		 * @returns {Number,String}
 478 		 */
 479 		min : function(array) {
 480 			var mi = array[0];
 481 			for ( var i = 1; i < array.length; i++) {
 482 				var n = array[i];
 483 				if (n < mi) {
 484 					mi = n;
 485 				}
 486 			}
 487 			return mi;
 488 		},
 489 		/**
 490 		 * Returns the maximum element of the given array, according to the
 491 		 * natural ordering of its elements.
 492 		 * 
 493 		 * @param {Array}array
 494 		 * @returns {Number,String}
 495 		 */
 496 		max : function(array) {
 497 			var mx = array[0];
 498 			for ( var i = 1; i < array.length; i++) {
 499 				var n = array[i];
 500 				if (n > mx) {
 501 					mx = n;
 502 				}
 503 			}
 504 			return mx;
 505 		},
 506 		/**
 507 		 * Returns the position of the first occurrence of the argument within
 508 		 * the array(==). If the argument doesn't exist in the array, returns
 509 		 * -1.
 510 		 * 
 511 		 * @param {Array}array
 512 		 * @param {any}value
 513 		 * @returns {Number}
 514 		 */
 515 		indexOf : function(array, value) {
 516 			for ( var i = 0; i < array.length; i++) {
 517 				if (value == array[i]) {
 518 					return i;
 519 				}
 520 			}
 521 			return -1;
 522 		},
 523 		/**
 524 		 * <b>IndexOf Exactly</b><br>
 525 		 * Returns the position of the first occurrence of the argument within
 526 		 * the array(===). If the argument doesn't exist in the array, returns
 527 		 * -1.
 528 		 * 
 529 		 * @param {Array}array
 530 		 * @param {any}value
 531 		 * @returns {Number}
 532 		 */
 533 		indexOfEx : function(array, value) {
 534 			for ( var i = 0; i < array.length; i++) {
 535 				if (value === array[i]) {
 536 					return i;
 537 				}
 538 			}
 539 			return -1;
 540 		},
 541 		/**
 542 		 * Returns true if this array contains the specified element(==).
 543 		 * 
 544 		 * @param {Array}array
 545 		 * @param {any}value
 546 		 * @returns {Boolean}
 547 		 */
 548 		contains : function(array, value) {
 549 			return this.indexOf(array, value) != -1;
 550 		},
 551 		/**
 552 		 * <b>Contains Exactly</b><br>
 553 		 * Returns true if this array contains the specified element(===).
 554 		 * 
 555 		 * @param {Array}array
 556 		 * @param {any}value
 557 		 * @returns {Boolean}
 558 		 */
 559 		containsEx : function(array, value) {
 560 			return this.indexOfEx(array, value) != -1;
 561 		},
 562 		/**
 563 		 * Removes the element at the specified position in the array
 564 		 * 
 565 		 * @param {Array}array
 566 		 * @param {Number}index
 567 		 * @returns {Object}
 568 		 */
 569 		remove : function(array, index) {
 570 			var value = array[index];
 571 			array.splice(index, 1);
 572 			return value;
 573 		},
 574 		/**
 575 		 * Return a new array with no duplicates
 576 		 * 
 577 		 * @param {Array}array
 578 		 * @returns {Array}
 579 		 */
 580 		unique : function(array) {
 581 			var arr = [];
 582 			var l = array.length;
 583 			for ( var i = 0; i < l; i++) {
 584 				for ( var j = i + 1; j < l; j++) {
 585 					if (array[i] === array[j])
 586 						j = ++i;
 587 				}
 588 				arr.push(array[i]);
 589 			}
 590 			return arr;
 591 		}
 592 	},
 593 	/**
 594 	 * @namespace
 595 	 */
 596 	conversion : {
 597 		/**
 598 		 * argsToArray
 599 		 * 
 600 		 * @param {Object}args
 601 		 * @returns {Array}
 602 		 */
 603 		argsToArray : function(args) {
 604 			var arr = new Array();
 605 			for ( var i = 0; i < args.length; i++) {
 606 				arr.push(args[i]);
 607 			}
 608 			return arr;
 609 		},
 610 		/**
 611 		 * decToHex
 612 		 * 
 613 		 * @param {Number}number
 614 		 * @returns {String}
 615 		 */
 616 		decToHex : function(number) {
 617 			if (number < 0) {
 618 				number = 0xFFFFFFFF + number + 1;
 619 			}
 620 			return number.toString(16).toUpperCase();
 621 		}
 622 	},
 623 	/**
 624 	 * @namespace
 625 	 */
 626 	algorithm : {},
 627 	/**
 628 	 * @namespace
 629 	 */
 630 	stats : {
 631 		/**
 632 		 * Returns the arithmetic mean of the entries in the input array
 633 		 * 
 634 		 * @param {Array}array
 635 		 * @returns {Number}
 636 		 */
 637 		mean : function(array) {
 638 			if (array.length > 0) {
 639 				return this.sum(array) / array.length;
 640 			}
 641 			return 0;
 642 		},
 643 		/**
 644 		 * Returns the sum of the entries in the input array
 645 		 * 
 646 		 * @param {Array}array
 647 		 * @returns {Number}
 648 		 */
 649 		sum : function(array) {
 650 			var sum = 0;
 651 			for ( var i = 0; i < array.length; i++) {
 652 				sum += array[i];
 653 			}
 654 			return sum;
 655 		},
 656 		/**
 657 		 * Returns the minimum element of the given array, according to the
 658 		 * natural ordering of its elements.
 659 		 * 
 660 		 * @param {Array}array
 661 		 * @returns {Number}
 662 		 */
 663 		min : function(array) {
 664 			return Jek.array.min(array);
 665 		},
 666 		/**
 667 		 * Returns the maximum element of the given array, according to the
 668 		 * natural ordering of its elements.
 669 		 * 
 670 		 * @param {Array}array
 671 		 * @returns {Number}
 672 		 */
 673 		max : function(array) {
 674 			return Jek.array.max(array);
 675 		},
 676 		/**
 677 		 * Convert values in an array to percentage
 678 		 * 
 679 		 * @param {Array}array
 680 		 * @param {String}suffix[optional]
 681 		 * @param {Number}decimalPositions[optional]
 682 		 *            <b><i>default</i></b> value = 0
 683 		 * @returns {Array}
 684 		 */
 685 		percentage : function(array, suffix, decimalPositions) {
 686 			var hasSuffix = Jek.isString(suffix) && suffix.length > 0;
 687 			if (!Jek.isNumber(decimalPositions)) {
 688 				decimalPositions = 0;
 689 			}
 690 			var sum = this.sum(array);
 691 			var ratio = 100 / sum;
 692 			var resArray = new Array();
 693 			var resItem;
 694 			for ( var i = 0; i < array.length; i++) {
 695 				resItem = Jek.roundTo(array[i] * ratio, decimalPositions);
 696 
 697 				if (hasSuffix) {
 698 					resItem += suffix;
 699 				}
 700 				resArray.push(resItem);
 701 			}
 702 			return resArray;
 703 		}
 704 	}
 705 };
 706 /**
 707  * Encodes the string data to UTF-8
 708  * 
 709  * @param {String}string
 710  * @returns {String}
 711  */
 712 Jek.conversion.utf8Encode = function(string) {
 713 	string = string.replace(/\r\n/g, "\n");
 714 	var utftext = "";
 715 	for ( var n = 0; n < string.length; n++) {
 716 		var c = string.charCodeAt(n);
 717 		if (c < 128) {
 718 			utftext += String.fromCharCode(c);
 719 		} else if ((c > 127) && (c < 2048)) {
 720 			utftext += String.fromCharCode((c >> 6) | 192);
 721 			utftext += String.fromCharCode((c & 63) | 128);
 722 		} else {
 723 			utftext += String.fromCharCode((c >> 12) | 224);
 724 			utftext += String.fromCharCode(((c >> 6) & 63) | 128);
 725 			utftext += String.fromCharCode((c & 63) | 128);
 726 		}
 727 	}
 728 	return utftext;
 729 };
 730 /**
 731  * Decodes data, assumed to be UTF-8 encoded, to ISO-8859-1
 732  * 
 733  * @param {String}utftext
 734  * @returns {String}
 735  */
 736 Jek.conversion.utf8Decode = function(utftext) {
 737 	var string = "";
 738 	var i = 0;
 739 	var c = c1 = c2 = 0;
 740 
 741 	while (i < utftext.length) {
 742 		c = utftext.charCodeAt(i);
 743 		if (c < 128) {
 744 			string += String.fromCharCode(c);
 745 			i++;
 746 		} else if ((c > 191) && (c < 224)) {
 747 			c2 = utftext.charCodeAt(i + 1);
 748 			string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
 749 			i += 2;
 750 		} else {
 751 			c2 = utftext.charCodeAt(i + 1);
 752 			c3 = utftext.charCodeAt(i + 2);
 753 			string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6)
 754 					| (c3 & 63));
 755 			i += 3;
 756 		}
 757 	}
 758 	return string;
 759 };
 760 Jek.conversion.base64KeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 761 /**
 762  * Jek.conversion.base64Encode  
 763  * 
 764  * @param {String}string
 765  * @returns {String}
 766  */
 767 Jek.conversion.base64Encode = function(str) {
 768 	var res = '';
 769 	var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
 770 	var i = 0;
 771 	var keyStr = Jek.conversion.base64KeyStr;
 772 
 773 	str = Jek.conversion.utf8Encode(str);
 774 
 775 	while (i < str.length) {
 776 
 777 		chr1 = str.charCodeAt(i++);
 778 		chr2 = str.charCodeAt(i++);
 779 		chr3 = str.charCodeAt(i++);
 780 
 781 		enc1 = chr1 >> 2;
 782 		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
 783 		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
 784 		enc4 = chr3 & 63;
 785 
 786 		if (isNaN(chr2)) {
 787 			enc3 = enc4 = 64;
 788 		} else if (isNaN(chr3)) {
 789 			enc4 = 64;
 790 		}
 791 
 792 		res = res +
 793 		keyStr.charAt(enc1) + keyStr.charAt(enc2) +
 794 		keyStr.charAt(enc3) + keyStr.charAt(enc4);
 795 
 796 	}
 797 	return res;	
 798 };
 799 /**
 800  * Jek.conversion.base64Decode  
 801  * 
 802  * @param {String}string
 803  * @returns {String}
 804  */
 805 Jek.conversion.base64Decode = function(str) {
 806 	var res = '';
 807 	var chr1, chr2, chr3;
 808 	var enc1, enc2, enc3, enc4;
 809 	var i = 0;
 810 	var keyStr = Jek.conversion.base64KeyStr;
 811 
 812 	str = str.replace(/[^A-Za-z0-9\+\/\=]/g, '');
 813 
 814 	while (i < str.length) {
 815 
 816 		enc1 = keyStr.indexOf(str.charAt(i++));
 817 		enc2 = keyStr.indexOf(str.charAt(i++));
 818 		enc3 = keyStr.indexOf(str.charAt(i++));
 819 		enc4 = keyStr.indexOf(str.charAt(i++));
 820 
 821 		chr1 = (enc1 << 2) | (enc2 >> 4);
 822 		chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
 823 		chr3 = ((enc3 & 3) << 6) | enc4;
 824 
 825 		res = res + String.fromCharCode(chr1);
 826 
 827 		if (enc3 != 64) {
 828 			res = res + String.fromCharCode(chr2);
 829 		}
 830 		if (enc4 != 64) {
 831 			res = res + String.fromCharCode(chr3);
 832 		}
 833 	}
 834 	return Jek.conversion.utf8Decode(res);
 835 };
 836 /**
 837  * Jek.algorithm.crc32
 838  * 
 839  * @param {String}string
 840  * @returns {String}
 841  */
 842 Jek.algorithm.crc32 = function(string) {
 843 	string = Jek.conversion.utf8Encode(string);
 844 	var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D";
 845 	var crc = 0;
 846 	var x = 0;
 847 	var y = 0;
 848 	crc = crc ^ (-1);
 849 	for ( var i = 0, iTop = string.length; i < iTop; i++) {
 850 		y = (crc ^ string.charCodeAt(i)) & 0xFF;
 851 		x = "0x" + table.substr(y * 9, 8);
 852 		crc = (crc >>> 8) ^ x;
 853 	}
 854 	return Jek.conversion.decToHex(crc ^ (-1));
 855 };
 856 /**
 857  * The Levenshtein distance is a metric for measuring the amount of difference
 858  * between two strings, is the minimum number of edits needed to transform one
 859  * string into the other, with the allowable edit operations being insertion,
 860  * deletion, or substitution of a single character.
 861  * 
 862  * @param {String}string1
 863  * @param {String}string2
 864  * @returns {Number}
 865  */
 866 Jek.algorithm.levenshteinDistance = function(string1, string2) {
 867 	var d = new Array(), n, m, i, j, s_i, t_j, cost;
 868 	// Step I
 869 	n = string1.length;
 870 	m = string2.length;
 871 	if (n == 0) {
 872 		return m;
 873 	}
 874 	if (m == 0) {
 875 		return n;
 876 	}
 877 	for (w = 0; w <= n + 1; w++) {
 878 		var arr = new Array();
 879 		for (ww = 0; ww <= m + 1; ww++) {
 880 			arr.push({});
 881 		}
 882 		d.push(arr);
 883 	}
 884 	// Step II
 885 	for (i = 0; i <= n; i++) {
 886 		d[i][0] = i;
 887 	}
 888 	for (j = 0; j <= m; j++) {
 889 		d[0][j] = j;
 890 	}
 891 	// Step III
 892 	for (i = 1; i <= n; i++) {
 893 		s_i = string1.charAt(i - 1);
 894 		// Step IV
 895 		for (j = 1; j <= m; j++) {
 896 			t_j = string2.charAt(j - 1);
 897 			// Step V
 898 			if (s_i == t_j) {
 899 				cost = 0;
 900 			} else {
 901 				cost = 1;
 902 			}
 903 			// Step VI
 904 			d[i][j] = Jek.array.min([ d[i - 1][j] + 1, d[i][j - 1] + 1,
 905 					d[i - 1][j - 1] + cost ]);
 906 		}
 907 	}
 908 	// Step VII
 909 	return d[n][m];
 910 };
 911 /**
 912  * @param {String}str1
 913  * @param {String}str2
 914  * @param {Number}decimalPositions[optional]
 915  *            <b><i>default</i></b> value = 0
 916  * @returns {Number}
 917  */
 918 Jek.algorithm.stringDistancePerc = function(str1, str2, decimalPositions) {
 919 	if (!Jek.isNumber(decimalPositions)) {
 920 		decimalPositions = 0;
 921 	}
 922 	var l1 = str1.length;
 923 	var l2 = str2.length;
 924 	var ratio = l1 >= l2 ? 100 / l1 : 100 / l2;
 925 
 926 	return Jek.roundTo(Jek.algorithm.levenshteinDistance(str1, str2) * ratio,
 927 			decimalPositions);
 928 };
 929 /**
 930  * Check if a string contains a sequence of characters or numbers
 931  * 
 932  * @example <b>Example:</b> 12345, abcdef, 876543210, 123bcde, 12344321, etc.
 933  * @param {String}str
 934  * @returns {Number}
 935  */
 936 Jek.algorithm.charSequenceCheck = function(str) {
 937 	var res = new Array();
 938 	var i;
 939 	var l = str.length;
 940 	for (i = 0; i < l - 1; i++) {
 941 		res.push(str.charCodeAt(i + 1) - str.charCodeAt(i));
 942 	}
 943 	res = Jek.array.unique(res);
 944 	if (res.length == 0) {
 945 		return 0;
 946 	} else if (res.length == 1) {
 947 		if (res[0] == -1 || res[0] == 0 || res[0] == 1) {
 948 			return 1;
 949 		} else {
 950 			return 0;
 951 		}
 952 	} else if (res.length == l-1) {
 953 		return 0;
 954 	} else {
 955 		return res.length;
 956 	}
 957 };
 958 /**
 959  * Generate a password from string (like Name + Surname) based on a passed seed
 960  * 
 961  * @param {String}str
 962  * @param {String}seed
 963  * @param {Number}pswLen
 964  * @returns {String}
 965  */
 966 Jek.algorithm.generatePassword = function(str, seed, pswLen) {
 967 	var inp = Jek.algorithm.crc32(str + seed);
 968 	var noVowels = Jek.removeVowels(str).toLowerCase();
 969 	var prefix = '';
 970 	var sufix = '';
 971 	
 972 	if (noVowels.length>0){
 973 		sufix = noVowels.charAt(0);
 974 		sufix+=sufix.toUpperCase();
 975 		
 976 		prefix = noVowels.charAt(0);
 977 
 978 		var words = Jek.words(noVowels);
 979 		if (words.length>1){
 980 			prefix += words[1].charAt(0);
 981 		}else if(noVowels.length>1) {
 982 			prefix += noVowels.charAt(1);
 983 		}
 984 	}
 985 	
 986 	var res = '';
 987 	for (n=0; n < pswLen; n++){
 988 		var out = 0;
 989 		for (i in  inp){
 990 		   out ^= inp.charCodeAt(i)*n; 
 991 		}
 992 		res +=out;
 993 	}
 994 	var num = '';
 995 	var prefixSufixLen=prefix.length+sufix.length;
 996 	
 997 	if (pswLen-prefixSufixLen>0){
 998 		num = Jek.padLeft(res,'0', pswLen-prefixSufixLen);
 999 	}
1000 	return prefix + num + sufix; 
1001 };
1002 /**
1003  * Calculate the dv1 hash of a string.
1004  * 
1005  * @param {String}str
1006  * @param {Number}len
1007  * @returns {String}
1008  */
1009 Jek.algorithm.dv1= function(str, len) {
1010 	var seed = 'dv1';
1011 	var out=len+str.length+1;
1012 	var crc = Jek.algorithm.crc32(seed+str+out);
1013 	var res = crc;
1014 	while (res.length < len) {
1015 		out /= 1.00003; 
1016 		res += Jek.algorithm.crc32(''+(crc.charCodeAt(crc.length-1)+out));
1017 	}
1018 	return res.substring(0, len);
1019 };
1020 /**
1021  * Encrypt string
1022  * 
1023  * @param {String}str
1024  * @param {String}psw
1025  * @returns {String}
1026  */
1027 Jek.algorithm.jsEncrypt = function(str, psw) {
1028 	var bigPsw= Jek.algorithm.dv1(psw, str.length);
1029 	var res = '';
1030 	for (i in  str){
1031 		res+= String.fromCharCode(str.charCodeAt(i) ^ bigPsw.charCodeAt(i)); 
1032 	}
1033 	return Jek.conversion.base64Encode(res);	
1034 };
1035 /**
1036  * Decrypt string
1037  * 
1038  * @param {String}str
1039  * @param {String}psw
1040  * @returns {String}
1041  */
1042 Jek.algorithm.jsDecrypt = function(str, psw) {
1043 	str = Jek.conversion.base64Decode(str);
1044 	var bigPsw= Jek.algorithm.dv1(psw, str.length);
1045 	var res = '';
1046 	for (i in  str){
1047 		res+= String.fromCharCode(str.charCodeAt(i) ^ bigPsw.charCodeAt(i)); 
1048 	}
1049 	return res;	
1050 };
1051 /**
1052  * Faster javascript StringBuilder implementation
1053  * 
1054  * @class
1055  * @param {String}string[optional]
1056  *            <b><i>default</i></b> value = ""
1057  * @returns {JekString}
1058  */
1059 function JekString(string) {
1060 	var _length = 0;
1061 
1062 	/**
1063 	 * All strings
1064 	 */
1065 	var _strings = new Array(Jek.defParam(string, ''));
1066 
1067 	/**
1068 	 * Returns the length (character count).
1069 	 * 
1070 	 * @returns {Number}
1071 	 */
1072 	this.length = function() {
1073 		return _length;
1074 	};
1075 	/**
1076 	 * Appends the specified string to this character sequence.
1077 	 * 
1078 	 * @param {String}string
1079 	 * @returns {JekString}
1080 	 */
1081 	this.append = function(string) {
1082 		if (string) {
1083 			_strings.push(string);
1084 			_length += string.length;
1085 		}
1086 		return this;
1087 	};
1088 	/**
1089 	 * Allows you to define a tokenized string and pass an arbitrary number of
1090 	 * arguments to replace the tokens, and append to this character sequence.
1091 	 * 
1092 	 * @param {String}string
1093 	 * @param {String}args...
1094 	 * @returns {JekString}
1095 	 * @example JekString.appendFormat('Lorem {0} dolor {1} amet','ipsum','sit') ->
1096 	 *          <i>"Lorem ipsum dolor sit amet"</i>
1097 	 */
1098 	this.appendFormat = function() {
1099 		if (arguments.length > 0) {
1100 			var str = arguments[0];
1101 			for ( var i = 1; i < arguments.length; ++i) {
1102 				str = str.replace("{" + (i - 1) + "}", arguments[i]);
1103 			}
1104 			_strings.push(str);
1105 			_length += str.length;
1106 		}
1107 		return this;
1108 	};
1109 	/**
1110 	 * Removes all character sequence.
1111 	 * 
1112 	 * @returns {JekString}
1113 	 */
1114 	this.clear = function() {
1115 		_strings = new Array('');
1116 		_length = 0;
1117 		return this;
1118 	};
1119 	/**
1120 	 * Returns a string representing the data in this sequence.
1121 	 * 
1122 	 * @returns {String}
1123 	 */
1124 	this.toString = function() {
1125 		return _strings.join('');
1126 	};
1127 };
1128 /**
1129  * JekArray
1130  * 
1131  * @class
1132  * @param {Array}array[optional]
1133  *            <b><i>default</i></b> value = new Array()
1134  * @returns {JekArray}
1135  */
1136 function JekArray(arrey) {
1137 	/**
1138 	 * All items
1139 	 * 
1140 	 * @type Array
1141 	 */
1142 	this.items = arrey || new Array();
1143 	/**
1144 	 * Returns the number of elements in this array
1145 	 * 
1146 	 * @returns {Number}
1147 	 */
1148 	this.size = function() {
1149 		return this.items.length;
1150 	};
1151 	/**
1152 	 * Returns true if this array contains the specified element(==).
1153 	 * 
1154 	 * @param {any}value
1155 	 * @returns {Boolean}
1156 	 */
1157 	this.contains = function(value) {
1158 		return Jek.array.contains(this.items, value);
1159 	};
1160 	/**
1161 	 * <b>Contains Exactly</b><br>
1162 	 * Returns true if this array contains the specified element(===).
1163 	 * 
1164 	 * @param {any}value
1165 	 * @returns {Boolean}
1166 	 */
1167 	this.containsEx = function(value) {
1168 		return Jek.array.containsEx(this.items, value);
1169 	};
1170 	/**
1171 	 * Returns the position of the first occurrence of the argument within the
1172 	 * array(==). If the argument doesn't exist in the array, returns -1.
1173 	 * 
1174 	 * @param {any}value
1175 	 * @returns {Number}
1176 	 */
1177 	this.indexOf = function(value) {
1178 		return Jek.array.indexOf(this.items, value);
1179 	};
1180 	/**
1181 	 * <b>IndexOf Exactly</b><br>
1182 	 * Returns the position of the first occurrence of the argument within the
1183 	 * array(===). If the argument doesn't exist in the array, returns -1.
1184 	 * 
1185 	 * @param {any}value
1186 	 * @returns {Number}
1187 	 */
1188 	this.indexOfEx = function(value) {
1189 		return Jek.array.indexOfEx(this.items, value);
1190 	};
1191 	/**
1192 	 * Appends the specified element to the end of this array.
1193 	 * 
1194 	 * @param {any}value
1195 	 * @returns {Array}
1196 	 */
1197 	this.add = function(value) {
1198 		this.items.push(value);
1199 		return this.items;
1200 	};
1201 	/**
1202 	 * Replaces the element at the specified position in this array with the
1203 	 * specified element.
1204 	 * 
1205 	 * @param {Number}index
1206 	 * @param {any}value
1207 	 * @returns {Boolean}
1208 	 */
1209 	this.set = function(index, value) {
1210 		if (index >= 0 && index < this.items.length) {
1211 			this.items[index] = value;
1212 			return true;
1213 		} else {
1214 			return false;
1215 		}
1216 	};
1217 	/**
1218 	 * Removes the element at the specified position in the array
1219 	 * 
1220 	 * @param {Number}index
1221 	 * @returns {any}
1222 	 */
1223 	this.remove = function(index) {
1224 		return Jek.array.remove(this.items, index);
1225 	};
1226 	/**
1227 	 * Returns the element at the specified position in this array.
1228 	 * 
1229 	 * @param {Number}index
1230 	 * @returns {any}
1231 	 */
1232 	this.get = function(index) {
1233 		return this.items[index];
1234 	};
1235 	/**
1236 	 * Returns the last element.
1237 	 * 
1238 	 * @returns {any}
1239 	 */
1240 	this.getLast = function() {
1241 		if (this.items.length > 0) {
1242 			return this.items[this.items.length - 1];
1243 		}
1244 		return undefined;
1245 	};
1246 	/**
1247 	 * Returns the minimum element of this array, according to the natural
1248 	 * ordering of its elements.
1249 	 * 
1250 	 * @returns {Number,String}
1251 	 */
1252 	this.min = function() {
1253 		return Jek.array.min(this.items);
1254 	};
1255 	/**
1256 	 * Returns the maximum element of this array, according to the natural
1257 	 * ordering of its elements.
1258 	 * 
1259 	 * @returns {Number,String}
1260 	 */
1261 	this.max = function() {
1262 		return Jek.array.max(this.items);
1263 	};
1264 	/**
1265 	 * 
1266 	 * @returns {Array}
1267 	 */
1268 	this.unique = function() {
1269 		return Jek.array.unique(this.items);
1270 	};
1271 	/**
1272 	 * Iterates on array calling the supplied function.
1273 	 * 
1274 	 * @param {Function}fn
1275 	 * @param {Object}scope[optional]
1276 	 *            <b><i>default</i></b> value = entry
1277 	 * @returns {any}
1278 	 * @example JekArray.each(function(key, value) {<br>
1279 	 *          ...<br>
1280 	 *          });
1281 	 * @example JekArray.each(function(key, value) {<br>
1282 	 *          ...<br> }, scope);
1283 	 */
1284 	this.each = function(fn, scope) {
1285 		for ( var i = 0; i < this.items.length; i++) {
1286 			if (fn.call(scope || this.items[i], this.items[i], i, this.items) === false) {
1287 				return i;
1288 			}
1289 		}
1290 	};
1291 };
1292 /**
1293  * JekKeyValue
1294  * 
1295  * @class
1296  * @param {any}key
1297  * @param {any}value
1298  * @returns {JekKeyValue}
1299  */
1300 function JekKeyValue(key, value) {
1301 	/**
1302 	 * key
1303 	 * 
1304 	 * @type any
1305 	 */
1306 	this.key = key;
1307 	/**
1308 	 * value
1309 	 * 
1310 	 * @type any
1311 	 */
1312 	this.value = value;
1313 };
1314 /**
1315  * JekMap
1316  * 
1317  * @class
1318  * @returns {JekMap}
1319  */
1320 function JekMap() {
1321 	/**
1322 	 * @type Array
1323 	 */
1324 	this.items = new Array();
1325 	/**
1326 	 * size
1327 	 * 
1328 	 * @returns {Number} size
1329 	 */
1330 	this.size = function() {
1331 		var sz = 0;
1332 		for ( var key in this.items) {
1333 			sz++;
1334 		}
1335 		return sz;
1336 	};
1337 	/**
1338 	 * set
1339 	 * 
1340 	 * @param {any}key
1341 	 * @param {any}value
1342 	 * @returns {any} value
1343 	 */
1344 	this.set = function(key, value) {
1345 		this.items[key] = value;
1346 		return value;
1347 	};
1348 	/**
1349 	 * get
1350 	 * 
1351 	 * @param {any}key
1352 	 * @returns {any} value
1353 	 */
1354 	this.get = function(key) {
1355 		return this.items[key];
1356 	};
1357 	/**
1358 	 * remove
1359 	 * 
1360 	 * @param {any}key
1361 	 * @returns {any} value
1362 	 */
1363 	this.remove = function(key) {
1364 		var value = this.items[key];
1365 		delete this.items[key];
1366 		return value;
1367 	};
1368 	/**
1369 	 * clear
1370 	 * 
1371 	 * @returns {Boolean}
1372 	 */
1373 	this.clear = function() {
1374 		for ( var key in this.items) {
1375 			this.remove(key);
1376 		}
1377 		return true;
1378 	};
1379 	/**
1380 	 * containsKey
1381 	 * 
1382 	 * @param {any}key
1383 	 * @returns {Boolean}
1384 	 */
1385 	this.containsKey = function(key) {
1386 		return (typeof this.items[key] != 'undefined');
1387 	};
1388 	/**
1389 	 * containsValue
1390 	 * 
1391 	 * @param {any}value
1392 	 * @returns {Boolean}
1393 	 */
1394 	this.containsValue = function(value) {
1395 		for ( var key in this.items) {
1396 			if (this.items[key] === value) {
1397 				return true;
1398 			}
1399 		}
1400 		return false;
1401 	};
1402 	/**
1403 	 * entrySet
1404 	 * 
1405 	 * @returns {Array}
1406 	 */
1407 	this.entrySet = function() {
1408 		var res = new Array();
1409 		for ( var key in this.items) {
1410 			res.push(this.items[key]);
1411 		}
1412 		return res;
1413 	};
1414 	/**
1415 	 * keySet
1416 	 * 
1417 	 * @returns {Array}
1418 	 */
1419 	this.keySet = function() {
1420 		var res = new Array();
1421 		for ( var key in this.items) {
1422 			res.push(key);
1423 		}
1424 		return res;
1425 	};
1426 	/**
1427 	 * values
1428 	 * 
1429 	 * @returns {[JekKeyValue]} Array of JekKeyValue
1430 	 */
1431 	this.values = function() {
1432 		var res = new Array();
1433 		for ( var key in this.items) {
1434 			res.push(new JekKeyValue(key, this.items[key]));
1435 		}
1436 		return res;
1437 	};
1438 	/**
1439 	 * Iterates on elements calling the supplied function.
1440 	 * 
1441 	 * @param {Function}fn
1442 	 * @param {Object}scope[optional]
1443 	 *            <b><i>default</i></b> value = {JekKeyValue}entry
1444 	 * @returns {any}
1445 	 * @example JekMap.each(function(key, value) {<br>
1446 	 *          ...<br>
1447 	 *          });
1448 	 * @example JekMap.each(function(key, value) {<br>
1449 	 *          ...<br>}, scope);
1450 	 */
1451 	this.each = function(fn, scope) {
1452 		var entry;
1453 		for ( var key in this.items) {
1454 			entry = new JekKeyValue(key, this.items[key]);
1455 			if (fn.call(scope || entry, entry.key, entry.value) === false) {
1456 				return entry;
1457 			}
1458 		}
1459 	};
1460 };
1461 /**
1462  * JekStats
1463  * 
1464  * @class
1465  */
1466 function JekStats() {
1467 	/**
1468 	 * @type JekMap
1469 	 */
1470 	this.stats = new JekMap();
1471 	/**
1472 	 * @param {String}statId
1473 	 * @param {Number}value[optional]
1474 	 *            <b><i>default</i></b> value = 1
1475 	 * @returns {Number}value
1476 	 */
1477 	this.increase = function(statId, value) {
1478 		value = Jek.defParam(value, 1);
1479 		var oldValue;
1480 		if (this.stats.containsKey(statId)) {
1481 			oldValue = this.stats.get(statId);
1482 			return this.stats.set(statId, oldValue + value);
1483 		} else {
1484 			return this.stats.set(statId, value);
1485 		}
1486 	};
1487 	/**
1488 	 * @param {String}statId
1489 	 * @param {Number}value[optional]
1490 	 *            <b><i>default</i></b> value = 1
1491 	 * @returns {Number}value
1492 	 */
1493 	this.decrease = function(statId, value) {
1494 		value = Jek.defParam(value, 1);
1495 		var oldValue;
1496 		if (this.stats.containsKey(statId)) {
1497 			oldValue = this.stats.get(statId);
1498 			return this.stats.set(statId, oldValue - value);
1499 		} else {
1500 			return this.stats.set(statId, 0 - value);
1501 		}
1502 	};
1503 	/**
1504 	 * @param {String}statId
1505 	 * @returns {Number}value
1506 	 */
1507 	this.get = function(statId) {
1508 		return this.stats.get(statId);
1509 	};
1510 }
1511 /**
1512  * JekLinkEntry
1513  * 
1514  * @class
1515  * @param {any}value
1516  * @param {JekLinkEntry,null}prev
1517  * @param {JekLinkEntry,null}next
1518  * @returns {JekLinkEntry}
1519  * @see JekLinkedList
1520  */
1521 function JekLinkEntry(value, prev, next) {
1522 	this.value = value;
1523 	this.prev = prev;
1524 	this.next = next;
1525 };
1526 
1527 /**
1528  * JekLinkedList
1529  * 
1530  * @class
1531  * @returns {JekLinkedList}
1532  */
1533 function JekLinkedList() {
1534 	var _first = null;
1535 	var _last = null;
1536 	/**
1537 	 * add
1538 	 * 
1539 	 * @param {any}value
1540 	 */
1541 	this.add = function(value) {
1542 		var entry = null;
1543 		if (!_first) {
1544 			_first = new JekLinkEntry(value, null, null);
1545 			_last = _first;
1546 		} else {
1547 			entry = new JekLinkEntry(value, _last, null);
1548 			_last.next = entry;
1549 			_last = entry;
1550 		}
1551 	};
1552 	/**
1553 	 * addFirst
1554 	 * 
1555 	 * @param {any}value
1556 	 * @returns {Boolean}
1557 	 */
1558 	this.addFirst = function(value) {
1559 		return this.insert(0, value);
1560 	};
1561 	/**
1562 	 * addAll
1563 	 * 
1564 	 * @param {Array}values
1565 	 * @returns {Boolean}
1566 	 */
1567 	this.addAll = function(values) {
1568 		var res = false;
1569 		for ( var index in values) {
1570 			this.add(values[index]);
1571 			res = true;
1572 		}
1573 		return res;
1574 	};
1575 	/**
1576 	 * first
1577 	 * 
1578 	 * @returns {any}
1579 	 */
1580 	this.first = function() {
1581 		if (_first) {
1582 			return _first.value;
1583 		}
1584 	};
1585 	/**
1586 	 * last
1587 	 * 
1588 	 * @returns {any}
1589 	 */
1590 	this.last = function() {
1591 		if (_last) {
1592 			return _last.value;
1593 		}
1594 	};
1595 	/**
1596 	 * get
1597 	 * 
1598 	 * @param {Number}index
1599 	 * @returns {any}
1600 	 */
1601 	this.get = function(index) {
1602 		var entry = this._getEntry(index);
1603 		if (entry) {
1604 			return entry.value;
1605 		} else {
1606 			return undefined;
1607 		}
1608 	};
1609 	/**
1610 	 * _getEntry
1611 	 * 
1612 	 * @param {Number}index
1613 	 * @returns {JekLinkEntry}
1614 	 */
1615 	this._getEntry = function(index) {
1616 		var i = 0;
1617 		if (_first) {
1618 			var entry = _first;
1619 			while (entry) {
1620 				if (i == index) {
1621 					return entry;
1622 				}
1623 				entry = entry.next;
1624 				i++;
1625 			}
1626 		}
1627 		return undefined;
1628 	};
1629 	/**
1630 	 * set
1631 	 * 
1632 	 * @param {Number}index
1633 	 * @param {any}value
1634 	 * @returns {Boolean}
1635 	 */
1636 	this.set = function(index, value) {
1637 		var entry = this._getEntry(index);
1638 		if (entry) {
1639 			entry.value = value;
1640 			return true;
1641 		} else {
1642 			return false;
1643 		}
1644 	};
1645 	/**
1646 	 * insert
1647 	 * 
1648 	 * @param {Number}index
1649 	 * @param {any}value
1650 	 * @returns {Boolean}
1651 	 */
1652 	this.insert = function(index, value) {
1653 		var entry = this._getEntry(index);
1654 		var newEntry;
1655 		if (entry) {
1656 			if (entry == _first) {
1657 				newEntry = new JekLinkEntry(value, null, entry);
1658 				entry.prev = newEntry;
1659 				_first = newEntry;
1660 			}
1661 			// else if (entry == _last) {
1662 			// newEntry = new JekLinkEntry(value, entry, null);
1663 			// entry.next = newEntry;
1664 			// _last = newEntry;
1665 			// }
1666 			else {
1667 				newEntry = new JekLinkEntry(value, entry.prev, entry);
1668 				entry.prev.next = newEntry;
1669 				entry.prev = newEntry;
1670 			}
1671 		} else {
1672 			this.add(value);
1673 		}
1674 		return true;
1675 	};
1676 	/**
1677 	 * size
1678 	 * 
1679 	 * @returns {Number}
1680 	 */
1681 	this.size = function() {
1682 		var sz = 0;
1683 		if (_first) {
1684 			var entry = _first;
1685 			while (entry) {
1686 				entry = entry.next;
1687 				sz++;
1688 			}
1689 		}
1690 		return sz;
1691 	};
1692 	/**
1693 	 * toArray
1694 	 * 
1695 	 * @returns {Array}
1696 	 */
1697 	this.toArray = function() {
1698 		var arr = new Array();
1699 		if (_first) {
1700 			var entry = _first;
1701 			while (entry) {
1702 				arr.push(entry.value);
1703 				entry = entry.next;
1704 			}
1705 		}
1706 		return arr;
1707 	};
1708 	/**
1709 	 * toJekLinkEntryArray
1710 	 * 
1711 	 * @returns {[JekLinkEntry]} Array of JekLinkEntry
1712 	 */
1713 	this.toJekLinkEntryArray = function() {
1714 		var arr = new Array();
1715 		if (_first) {
1716 			var entry = _first;
1717 			while (entry) {
1718 				arr.push(entry);
1719 				entry = entry.next;
1720 			}
1721 		}
1722 		return arr;
1723 	};
1724 	/**
1725 	 * contains
1726 	 * 
1727 	 * @param {any}value
1728 	 * @returns {Boolean}
1729 	 */
1730 	this.contains = function(value) {
1731 		return (this.indexOf(value) != -1);
1732 	};
1733 	/**
1734 	 * indexOf
1735 	 * 
1736 	 * @param {any}value
1737 	 * @returns {Number}
1738 	 */
1739 	this.indexOf = function(value) {
1740 		var res = 0;
1741 		if (_first) {
1742 			var entry = _first;
1743 			while (entry) {
1744 				if (entry.value === value) {
1745 					return res;
1746 				}
1747 				entry = entry.next;
1748 				res++;
1749 			}
1750 		}
1751 		return -1;
1752 	};
1753 	/**
1754 	 * remove
1755 	 * 
1756 	 * @param {any}value
1757 	 * @returns {Boolean}
1758 	 */
1759 	this.remove = function(value) {
1760 		if (_first) {
1761 			var entry = _first;
1762 			while (entry) {
1763 				if (entry.value === value) {
1764 					if (entry == _first) {
1765 						_first = entry.next;
1766 						if (_first) {
1767 							_first.prev = null;
1768 						} else {
1769 							_last = null;
1770 						}
1771 
1772 					} else if (entry == _last) {
1773 						_last = entry.prev;
1774 						_last.next = null;
1775 					} else {
1776 						entry.prev.next = entry.next;
1777 						entry.next.prev = entry.prev;
1778 					}
1779 					delete entry;
1780 					return true;
1781 				}
1782 				entry = entry.next;
1783 			}
1784 		}
1785 		return false;
1786 	};
1787 	/**
1788 	 * removeLast
1789 	 * 
1790 	 * @returns {Boolean}
1791 	 */
1792 	this.removeLast = function() {
1793 		if (_last) {
1794 			_last = _last.prev;
1795 			_last.next = null;
1796 			return true;
1797 		}
1798 		return false;
1799 	};
1800 	/**
1801 	 * removeFirst
1802 	 * 
1803 	 * @returns {Boolean}
1804 	 */
1805 	this.removeFirst = function() {
1806 		if (_first) {
1807 			_first = _first.next;
1808 			if (_first) {
1809 				_first.prev = null;
1810 			} else {
1811 				_last = null;
1812 			}
1813 			return true;
1814 		}
1815 		return false;
1816 	};
1817 	/**
1818 	 * clear
1819 	 */
1820 	this.clear = function() {
1821 		_first = null;
1822 		_last = null;
1823 	};
1824 	/**
1825 	 * each
1826 	 * 
1827 	 * @param {Function}fn
1828 	 * @param {Object}scope[optional]
1829 	 *            <b><i>default</i></b> value = {JekLinkEntry}entry
1830 	 * @returns {any}
1831 	 * @example JekLinkedList.each(function(key, value) {<br>
1832 	 *          ...<br>
1833 	 *          });
1834 	 * @example JekLinkedList.each(function(key, value) {<br>
1835 	 *          ...<br>}, scope);
1836 	 */
1837 	this.each = function(fn, scope) {
1838 		var i = 0;
1839 		if (_first) {
1840 			var entry = _first;
1841 			while (entry) {
1842 				if (fn.call(scope || entry, entry.value, i) === false) {
1843 					return entry;
1844 				}
1845 				;
1846 				entry = entry.next;
1847 				i++;
1848 			}
1849 		}
1850 	};
1851 };