/**********************************************************************************
adds prototypes to the string object:

		.trim()
		.trimLeft()
		.trimRight()
		.encodeToHtml()
		.format()
		.isEmail()
		.reverse()

	Number extensions
		.toFixed()
		.toExponential()

--	ltrim,	 rtrim,	trim,	reverse,	toInt,	aryRep,	isEmail,	 suplant,	linkURL,	pCase,	pad
	String.prototype.ltrim  =function() {return this.replace(/^\s+/,'');}
	String.prototype.rtrim  =function() {return this.replace(/\s+$/,'');}
	String.prototype.trim   =function() {return this.replace(/^\s+/,'').replace(/\s+$/,'');}
	String.prototype.reverse=function() {var s='';var i=this.length;while(i-->0) s+=this.substring(i,i+1);return s;}
	String.prototype.toInt  =function() {var a=[];for(var i=0;i<this.length;i++) a[i]=this.charCodeAt(i);return a;}
	String.prototype.aryRep =function(a){var t=this;for(var i=0; i<a.length; i++){var n=new RegExp(a[i].r, "gi");t=t.replace(n, a[i].w);}return t;}
	String.prototype.isEmail=function() {return /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/gi.test(this)}
	String.prototype.suplant=function(o){var s=this;for(i in o)s=s.replace(new RegExp('{'+i+'}','gi'),o[i]);return s;}
	String.prototype.linkURL=function() {return this.replace(/(^|\s)(([fh]ttps?:\/\/|www\.)\S*)/gi, '$1'+new String('$2').link('$2'))}
	String.prototype.pCase  =function() {var o='';var l=true;for(var i=0;i<this.length;i++){var c=this.substr(i, 1);o+=l?c.toUpperCase():c;l=/(^|\W)$/gi.test(c);}return o;}
	String.prototype.pad    =function(v,l){return new Array(l-this.length+1).join(v)+this;}
var telnoRegxp = /^([0-9]{11})$/;
var emailRegxp = /^([\w]+)(.[\w]+)*@([\w]+)(.[\w]{2,3}){1,2}$/;
var urlRegxp = /^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.){1}([\w]+)(.[\w]+){1,2}$/;


emailRe = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/
phoneRe = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,3})|(\(?\d{2,3}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/
                  
Matches a limited version of the RFC 2822 email addr-spec form
^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$

Matches any legal domain name conforming to host name requirements.
^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?$

Same as above, except this matches only realistic TLDs, i.e., those with 2 to 6 letters.
^[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,6}$





**********************************************************************************/


// --------------------------------------------------------------------------------------------------------------------------------------------------
/**
    String methods
    
*/

if(String.prototype.trim == null){
    String.prototype.trim = function(){
		if (!this || this=='') return this;
		var str= this.toString();
		var i,j;
		for (i=0; i<str.length && str.charAt(i)==' '; i++);
		for (j=str.length-1; j>i && str.charAt(j)==' '; j--);
		return (i<=j && i<str.length ? str.substring(i,j+1) : '');
    }
}

if(String.prototype.trimLeft == null){
    String.prototype.trimLeft = function(){
		if (!this || this=='') return this;
		var str= this.toString();
		var i;
		for (i=0; i<str.length && str.charAt(i)==' '; i++);
		return (i<str.length ? str.substring(i,str.length) : '');
    }
}

if(String.prototype.trimRight == null){
    String.prototype.trimRight = function(){
		if (!this || this=='') return this;
		var str= this.toString();
		var j;
		for (j=str.length-1; j>=0 && str.charAt(j)==' '; j--);
		return (j >= 0 ? str.substring(0,j+1) : '');
    }
}

if(String.prototype.encodeToHtml == null){
	String.prototype.encodeToHtml = function (){
		if (!this || this=='') return this;
		var encodedHtml = this.toString();
		return encodedHtml.split("\x3C").join("&lt;").split("\x26").join("&amp;").split("\x3E").join("&gt;").split("\x22").join("&quot;");
   }

}

//	Matches a limited version of the RFC 2822 email addr-spec form
if(String.prototype.isEmail == null){
	String.prototype.isEmail = function() {
		var str= this.toString();
		return /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/.test(str);
		//return /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/gi.test(str);
	}
}

if(String.prototype.reverse == null){
	String.prototype.reverse = function() {
		var s='';
		var i=this.length;
		while(i-->0) s+=this.substring(i,i+1);
		return s;
	}
}


// --------------------------------------------------------------------------------------------------------------------------------------------------
/**
    Number functions.
    
    Contributed by Wolfgang Dumhs.
*/
if(Number.prototype.toFixed == null){
    Number.prototype.toFixed = function(d){
        var n = this;
        d = d || 0;
        var f = Math.pow(10, d);
        n = Math.round (f * n) / f;
        n = (n >= 0) ? n+Math.pow(10, -(d+1)) : n-Math.pow(10, -(d+1));
        n += '';
        return d == 0 ? n.substring(0, n.indexOf('.')) : n.substring(0, n.indexOf('.') + d + 1);
    }
}


// --------------------------------------------------------------------------------------------------------------------------------------------------

if(Number.prototype.toExponential == null){
    Number.prototype.toExponential = function(d){
        var n = this;
        var e = 0;
        if (n != 0){
            e = Math.floor(Math.log(Math.abs(n)) / Math.LN10);
        }
        n /= Math.pow(10, e);
        if (isFinite(d)){
            if (Math.abs(n) + 5*Math.pow(10, -(d+1)) >= 10.0){
                n /= 10;
                e += 1;
            }
            n = n.toFixed(d);
        }
        n += "e";
        if (e >= 0){
            n += "+";
        }
        n += e;
        return n;
    }
}


// --------------------------------------------------------------------------------------------------------------------------------------------------
//
var FormatSpecifier=function(s){
	var s = s.match(/%(\(\w+\)){0,1}([ 0-]){0,1}(\+){0,1}(\d+){0,1}(\.\d+){0,1}(.)/);
	if(s[1]){
		this.key=s[1].slice(1,-1);
	}else{
		this.key = null;
	}
	this.paddingFlag = s[2];
	if(this.paddingFlag==""){
		this.paddingFlag =" " 
	}
	this.signed=(s[3] == "+");
	this.minLength = parseInt(s[4]);
	if(isNaN(this.minLength)){
		this.minLength=0;
	}
	if(s[5]){
		this.percision = parseInt(s[5].slice(1,s[5].length));
	}else{
		this.percision=-1;
	}
	this.type = s[6];
}

// --------------------------------------------------------------------------------------------------------------------------------------------------

/*
	Formats a string replacing formatting specifiers with values provided as arguments
	which are formatted according to the specifier.
	This is an implementation of  python's % operator for strings and is similar to sprintf from C.
	Usage:
		resultString = formatString.format(value1, v2, ...);
	
	Each formatString can contain any number of formatting specifiers which are
	replaced with the formated values.
	
	specifier([...]-items are optional): 
		"%(key)[flag][sign][min][percision]typeOfValue"
		
		(key)  If specified the 1st argument is treated as an objec/associative array and the formating values 
				 are retrieved from that object using the key.
			
		flag:
			0      Use 0s for padding.
			-      Left justify result, padding it with spaces.
					Use spaces for padding.
		sign:
			+      Numeric values will contain a +|- infront of the number.
		min:
			l      The string will be padded with the padding character until it has a minimum length of l. 
		percision:
		   .x     Where x is the percision for floating point numbers and the lenght for 0 padding for integers.
		typeOfValue:
			d    Signed integer decimal.  	 
			i     Signed integer decimal. 	 
			b    Unsigned binary.                       //This does not exist in python!
			o    Unsigned octal. 	
			u    Unsigned decimal. 	 
			x    Unsigned hexidecimal (lowercase). 	
			X   Unsigned hexidecimal (uppercase). 	
			e   Floating point exponential format (lowercase). 	 
			E   Floating point exponential format (uppercase). 	 
			f    Floating point decimal format. 	 
			F   Floating point decimal format. 	 
			c   Single character (accepts byte or single character string). 	 
			s   String (converts any object using object.toString()). 	
	
	Examples:
		"%02d".format(8) == "08"
		"%05.2f".format(1.234) == "01.23"
		"123 in binary is: %08b".format(123) == "123 in binary is: 01111011"
		
	@param *  Each parameter is treated as a formating value. 
*/
if (!String.prototype.format)	 {
	String.prototype.format=function(){
		var sf = this.match(/(%(\(\w+\)){0,1}[ 0-]{0,1}(\+){0,1}(\d+){0,1}(\.\d+){0,1}[dibouxXeEfFgGcrs%])|([^%]+)/g);
		if(sf){
			if(sf.join("") != this){
				throw "Unsupported formating string.";
			}
		}else{
			throw "Unsupported formating string.";
		}
		var thisMod = this;
		var rslt ="";
		var s;
		var obj;
		var cnt=0;
		var frmt;
		var sign="";
		
		for(var i=0;i<sf.length;i++){
			s=sf[i];
			if(s == "%%"){
				s = "%";
			}else if(s.slice(0,1) == "%"){
				frmt = new FormatSpecifier(s);//get the formating object
				if(frmt.key){//an object was given as formating value
					if((typeof arguments[0]) == "object" && arguments.length == 1){
						obj = arguments[0][frmt.key];
					}else{
						throw "Object or associative array expected as formating value.";
					}
				}else{//get the current value
					if(cnt>=arguments.length){
						throw "Not enough arguments for format string";
					}else{
						obj=arguments[cnt];
						cnt++;
					}
				}
					
				if(frmt.type == "s"){//String
					s=obj.toString().pad(frmt.paddingFlag, frmt.minLength);
					
				}else if(frmt.type == "c"){//Character
					if(frmt.paddingFlag == "0"){
						frmt.paddingFlag=" ";//padding only spaces
					}
					if(typeof obj == "number"){//get the character code
						s = String.fromCharCode(obj).pad(frmt.paddingFlag , frmt.minLength) ;
					}else if(typeof obj == "string"){
						if(obj.length == 1){//make sure it's a single character
							s=obj.pad(frmt.paddingFlag, frmt.minLength);
						}else{
							throw "Character of length 1 required.";
						}
					}else{
						throw "Character or Byte required.";
					}
				}else if(typeof obj == "number"){
					//get sign of the number
					if(obj < 0){
						obj = -obj;
						sign = "-"; //negative signs are always needed
					}else if(frmt.signed){
						sign = "+"; // if sign is always wanted add it 
					}else{
						sign = "";
					}
					//do percision padding and number conversions
					switch(frmt.type){
						case "f": //floats
						case "F":
							if(frmt.percision > -1){
								s = obj.toFixed(frmt.percision).toString();
							}else{
								s = obj.toString();
							}
							break;
						case "E"://exponential
						case "e":
							if(frmt.percision > -1){
								s = obj.toExponential(frmt.percision);
							}else{
								s = obj.toExponential();
							}
							s = s.replace("e", frmt.type);
							break;
						case "b"://binary
							s = obj.toString(2);
							s = s.pad("0", frmt.percision);
							break;
						case "o"://octal
							s = obj.toString(8);
							s = s.pad("0", frmt.percision);
							break;
						case "x"://hexadecimal
							s = obj.toString(16).toLowerCase();
							s = s.pad("0", frmt.percision);
							break;
						case "X"://hexadecimal
							s = obj.toString(16).toUpperCase();
							s = s.pad("0", frmt.percision);
							break;
						default://integers
							s = parseInt(obj).toString();
							s = s.pad("0", frmt.percision);
							break;
					}
					if(frmt.paddingFlag == "0"){//do 0-padding
						//make sure that the length of the possible sign is not ignored
						s=s.pad("0", frmt.minLength - sign.length);
					}
					s=sign + s;//add sign
					s=s.pad(frmt.paddingFlag, frmt.minLength);//do padding and justifiing
				}else{
					throw "Number required.";
				}
			}
			rslt += s;
		}
		return rslt;
	}
}

// --------------------------------------------------------------------------------------------------------------------------------------------------

/**
	Padds a String with a character to have a minimum length.
	
	@param flag   "-":      to padd with " " and left justify the string.
						Other: the character to use for padding. 
	@param len    The minimum length of the resulting string.
*/
if (!String.prototype.pad)	 {
	String.prototype.pad = function(flag, len){
		flag = (!flag ? '':flag);
		var s = "";
		if(flag == "-"){
			var c = " ";
		}else{
			var c = flag;
		}
		for(var i=0;i<len-this.length;i++){
			s += c;
		}
		if(flag == "-"){
			s = this + s;
		}else{
			s += this;
		}
		return s;
	}
}

// --------------------------------------------------------------------------------------------------------------------------------------------------

/**
	Repeats a string.
	@param c  The count how often the string should be repeated.
*/
if (!String.prototype.mul)	{
	String.prototype.mul = function(c){
		var a = new Array(this.length * c);
		var s=""+ this;
		for(var i=0;i<c;i++){
			a[i] = s;
		}
		return a.join("");
	}
}


// --------------------------------------------------------------------------------------------------------------------------------------------------

