/*
 * Core JavaScript Extension
 * 
 * Copyright (c) 2006-2010 i-deal Studio
 */

// return copy of string in UTF-8 uncoding
String.prototype.toUTF8 = function() {
	var n, s = '';
	for (var i = 0, iTextLen = this.length; i < iTextLen; i++) {
		n = this.charCodeAt(i);
		if (n < 128) s += String.fromCharCode(n);
		else if (n < 2048) s += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63);
		else if (n < 65536) s += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
		else s += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
	};
	return s;
};
// test string with RegExp
String.prototype.test = function(re) {
	return re.test(this);
};
// returns array of all match arrays. optional pockets should be Array or Object to receiv matches by subpatterns
String.prototype.MatchAll = function(re, pockets, limit) {
	if (!(re instanceof RegExp)) return null;
	var R = [], item, i, L;
	if (re.global) re.lastIndex = 0;
	else re = new RegExp(re.source, 'g' + (re.ignoreCase ? 'i' : '') + (re.multiline ? 'm' : ''));
	if (pockets && typeof pockets != 'object') pockets = void 0;
	while (null !== (item = re.exec(this))) {
		if (pockets) {
			for (i = 0, L = item.length; i < L; i++) {
				if (!pockets[i]) pockets[i] = [];
				pockets[i][R.length] = item[i];
			};
		};
		R[R.length] = item;
		if (limit > 0 && R.length >= limit) break;
	};
	return R.length ? R : null;
};
// return string without leading spaces
String.prototype.LTrim = function() {
	return this.replace( /^\s+/ , '' );
};
// return string without trailing spaces
String.prototype.RTrim = function() {
	return this.replace( /\s+$/ , '' );
};
// return string without leading and trailing spaces
String.prototype.Trim = function() {
	return this.replace( /^\s+|\s+$/g , '' );
};
// return strung with encoded HTML entities
String.prototype.HtmlEncode = function() {
	return this.replace( /&/g , '&amp;').replace( /</g , '&lt;').replace( />/g , '&gt;').replace( /"/g , '&quot;').replace( /'/g , '&#039;');
};
// return strung with decoded HTML entities
String.prototype.HtmlDecode = function() {
	return this.replace( /&lt;/g , '<').replace( /&gt;/g , '>').replace( /&quot;/g , '"').replace( /&#039;/g , "'").replace( /&amp;/g , '&');
};
// return string repeated specified number of times
String.prototype.Repeat = function(cnt) {
	var r = "";
	while (--cnt >= 0) r += this;
	return r;
};
// explode string into array by RegExp. FIX: but return empty array for empty string (instead of array with empty string)
String.prototype.Split = function(re) {	// FIX: "".split(re) returns [""] instead of []
	return this.length ? this.split(re) : [];
};
// return substring like PHP function substr()
String.prototype.SubStr = function(start, length) {
	var L = this.length;
	if (length === void 0) length = L;
	if (length == 0) return "";
	if (start === void 0) start = 0;
	var Start = (start >= 0) ? start : L + start;
	if (Start > L) Start = L;
	else if (Start < 0) Start = 0;
	var End = (length > 0) ? Start + length : L + length;
	return this.substring(Start, End);
};
// return string, where specified portion replaced by other substring; like PHP function substr_replace()
String.prototype.Splice = function(start, length, replacement) {
	var r = '', L = this.length;
	if (length === void 0) length = L;
	if (start === void 0) start = 0;
	var Start = (start >= 0) ? start : L + start;
	if (Start > L) Start = L;
	else if (Start < 0) Start = 0;
	if (Start > 0) r += this.substring(0, Start);
	if (replacement && replacement.length) r += replacement;
	var End = (length >= 0) ? Start + length : L + length;
	r += this.substring(End);
	return r;
};
// return copy of string with text replaced by RegExp with string or with a function applyed to an object
String.prototype.Replace = function(re, repl, obj) {
	if (typeof repl == 'function') {
		return this.replace(re, function(){
			return repl.apply(obj || this, arguments);
		});
	}
	else return this.replace(re, repl);
};
// escape string to url %HH
String.prototype.UrlEncode = function() {
	return this.Replace( /[^-_\.!~*'\(\)\da-zA-Z]/g , function(ch) {
		return '%' + ch.charCodeAt(0).Hex(2);
	});
};
// escape string to use in new RegExp()
String.prototype.RE = function() {
	return this.replace(/([\$\^\*\(\)\+\[\]\{\}\|\.\/\?\\])/g, '\\$1');
};
// replace substrings or chars like PHP strtr()
String.prototype.StrTr = function(from, to) {
	if (typeof from == 'object') {
		var a = [];
		for (var k in from) {
			a.Push(k.toString().RE());
		};
		if (a.length) {
			var re = new RegExp('(' + a.join('|') + ')', 'g');
			return this.replace(re, function(k) {
				return (k in from) && (from[k] != void 0) ? from[k] : '';
			});
		}
		else return '' + this;
	}
	else if (typeof from == 'string') {
		var L = from.length;
		if (L) {
			var R = {}, ch;
			if (typeof to != 'string') to = '';
			for (var i = 0; i < L; i++) {
				ch = from.charAt(i);
				if (!(ch in R)) R[ch] = to.charAt(i);
			};
			var re = new RegExp('([' + from.RE() + '])', 'g');
			return this.replace(re, function(k) {
				return (k in R) && (R[k] != void 0) ? R[k] : '';
			});
		}
		else return '' + this;
	}
	else {
		throw {message : 'Invalid argument'};
	};
};

// return string with hex value of specified length
Number.prototype.Hex = function(len) {
	var s = this.toString(16).toUpperCase(), L = s.length;
	if (len > 0) {
		if (len > L) s = '0'.Repeat(len - L) + s;
		else if (len < L) s = s.SubStr(-len);
	};
	return s;
};

// return slice of array like PHP function array_slice()
Array.prototype.Slice = function(start, length) {
	var L = this.length;
	if (length === void 0) length = L;
	if (length == 0) return [];
	if (start === void 0) start = 0;
	var Start = (start >= 0) ? start : L + start;
	if (Start > L) Start = L;
	else if (Start < 0) Start = 0;
	var End = (length > 0) ? Start + length : L + length;
	return this.slice(Start, End);
};
// add one or more elements to the end; update array and return new array length; like PHP function array_push()
Array.prototype.Push = Array.prototype.push = function(/* multiple values */){
	var L = this.length, n = arguments.length;
	this.length = L += n;
	while (n > 0) this[--L] = arguments[--n];
	return this.length;
};
// return last element of array and delete it off; update array; like PHP function array_pop()
Array.prototype.Pop = Array.prototype.pop = function() {
	var i = this.length;
	if (i > 0) {
		var r = this[--i];
		this.length = i;
		return r;
	};
};
// return last element of array
Array.prototype.Last = function() {
	var i = this.length;
	if (i > 0) return this[--i];
};
// search element by value with === operator; return index of element or -1
Array.prototype.Search = Array.prototype.find = function(value, start) {
	start = +start;
	var L = this.length, i = isNaN(start) || (start = Math.floor(start)) < 0 ? 0 : (start < L ? start : L - 1);
	for ( ; i < L; i++) if (this[i] === value) return i;
	return -1;
};
if (!Array.prototype.indexOf) Array.prototype.indexOf = Array.prototype.Search;
// check if offset exists in array; if second optional argument is true then check also if value is defined (not undefined)
Array.prototype.Isset = function(i, undef) {
	return i >= 0 && i < this.length && (!undef || this[i] !== void 0);
};
// delete one element from array by index; shift trailing; return reference to itself
Array.prototype.Unset = function(i) {
	var L = this.length;
	if (i >= 0 && i <= --L) {
		var n = i + 1;
		while (i < L) this[i++] = this[n++];
		this.length = L;
	};
	return this;
};
// update array: change its length and return reference to itself; on expanding new elements initializing with a value
Array.prototype.Resize = function(length, value) {
	var i = this.length;
	this.length = length;
	while (i < length) this[i++] = value;
	return this;
};
// extract element from the beginning of the array; remove this element (update array) and return extracted element; like PHP function array_shift()
Array.prototype.Shift = function() {
	var L = this.length;
	if (L) {
		var r = this[0], i = 0, n = 1;
		L--;
		while (i < L) this[i++] = this[n++];
		this.length = L;
		return r;
	};
};
// add elements to the beginning ot the array (update array) and return reference to itself; like PHP function array_unshift()
Array.prototype.Unshift = function(/* multiple values */) {
	var cnt = arguments.length;
	if (cnt) {
		var n = this.length, L;
		this.length = L = n + cnt;
		while (n > 0) this[--L] = this[--n];
		while (--cnt >= 0) this[cnt] = arguments[cnt];
	};
	return this;
};
// return copy of array where a portion replaced with other array
Array.prototype.Splice = function(start, length, replacement) {
	var r = [], L = this.length;
	if (length === void 0) length = L;
	if (start === void 0) start = 0;
	var Start = (start >= 0) ? start : L + start;
	if (Start > L) Start = L;
	else if (Start < 0) Start = 0;
	if (Start > 0) r = r.concat(this.slice(0, Start));
	if (replacement != void 0) {
		if (!(replacement instanceof Array)) replacement = [replacement];
		r = r.concat(replacement);
	};
	var End = (length >= 0) ? Start + length : L + length;
	r = r.concat(this.slice(End));
	return r;
};
// enumerate all elements with a function applyed to an object; return reference to itself
Array.prototype.Each = function(func, obj) {
	if (typeof func == 'function') {
		for (var i = 0, L = this.length; i < L; i++) {
			func.call(obj, this[i]);
		};
	};
	return this;
};
// for each element of the array apply a function to an object with
/*Array.prototype.Map = function(func, obj, args) {
	if (typeof func == 'function') {
		var r = [];
		if (!(args instanceof Array)) args = [];
		var ca = [null].concat(args);
		for (var i = 0, L = this.length; i < L; i++) {
			ca[0] = this[i];
			r.Push(func.apply(obj || this, ca));
		};
		return r;
	};
};*/
// insert elements into the array (update array) and return reference to itself
Array.prototype.Insert = function(start, values) {
	if (!(values instanceof Array)) values = [values];
	var cnt = values.length;
	if (cnt) {
		var n = this.length, L;
		this.length = L = n + cnt;
		if (start > n) start = n;
		else if (start < 0) start = 0;
		while (n > start) this[--L] = this[--n];
		while (--cnt >= 0) this[start + cnt] = values[cnt];
	};
	return this;
};

// makes current function inherited from specified function of object
Function.prototype.inheritsFrom = function(parent) {
	if (parent.constructor == Function) {
		//Normal Inheritance
		this.prototype = new parent;
		this.prototype.constructor = this;
		this.prototype.parent = parent.prototype;
	}
	else {
		//Pure Virtual Inheritance
		this.prototype = parent;
		this.prototype.constructor = this;
		this.prototype.parent = parent;
	};
	return this;
};

/* Inheritance example
function Foo(name) {
	this.name = name;
};
Foo.prototype.Lol = function() {
	alert(this.name);
};

Bar.inheritsFrom(Foo);
function Bar(name, index) {
	this.parent.constructor.call(this, name);
	this.index = index;
};
Bar.prototype.Echo = function() {
	alert('['+this.index+'] '+this.name);
};
*/


