/*
* String.validBrackets(string)
* Checks string to be valid brackets. Valid brackets are:
*	quotes	- '' "" `' ``
*	single	- <> {} [] () %% || // \\
*	double	- miltiline comments
*		  /** / C/C++ like (without whitespace)
*		  <??> PHP like
*		  <%%> ASP like
*		  (**) Pascal like
*
* @param	string	Brackets (left and right)
* @return	boolean	Result of validity of brackets
* @access	static
*/
String.validBrackets = function(br)
{
	if ( ! br ) {
		return false;
	}
	var quot = "''\"\"`'``";
	var sgl = "<>{}[]()%%||//\\\\";
	var dbl = "/**/<??><%%>(**)";
	return (br.length == 2 && (quot + sgl).indexOf(br) != -1)
		|| (br.length == 4 && dbl.indexOf(br) != -1);
}

/*
* object.bracketize(string)
* Transform the string object by setting in frame of valid brackets
*
* @param	string	Brackets
* @return	string	Bracketized string
* @access	public
*/
String.prototype.bracketize = function(br)
{
	var string = this;
	if ( ! String.validBrackets(br) ) {
		return string;
	}
	var midPos = br.length / 2;
	return br.substr(0, midPos) + string.toString() + br.substr(midPos);
}

/*
* object.unbracketize(string)
* Transform the string object removing the leading and trailing brackets
* If the parameter is not defined the method will try to remove existing valid brackets
*
* @param	string	Brackets
* @return	string	Unbracketized string
* @access	public
*/
String.prototype.unbracketize = function(br)
{
	var string = this;
	if ( ! br ) {
		var len = string.length;
		for (var i = 2; i >= 1; i--) {
			br = string.substring(0, i) + string.substring(len - i);
			if ( String.validBrackets(br) ) {
				return string.substring(i, len - i);
			}
		}
	}
	if ( ! String.validBrackets(br) ) {
		return string;
	}
	var midPos = br.length / 2;
	var i = string.indexOf(br.substr(0, midPos));
	var j = string.lastIndexOf(br.substr(midPos));
	if (i == 0 && j == string.length - midPos) {
		string = string.substring(i + midPos, j);
	}
	return string;
}

/*
* object.radix(number, number, string)
* Transform the number object to string in accordance with a scale of notation
* If it is necessary the numeric string will aligned to right and filled by '0' character, by default
*
* @param	number	Radix of scale of notation (it have to be greater or equal 2 and below or equal 36)
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.radix = function(r, n, c)
{
	if ( ! c ) {
		c = '0';
	}
	return this.toString(r).padding(-Math.abs(n), c);
}

/*
* object.bin(number, string)
* Transform the number object to string of binary presentation
*
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.bin = function(n, c)
{
	return this.radix(0x02, (n) ? n : 16, c);
}

/*
* object.oct(number, string)
* Transform the number object to string of octal presentation
*
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.oct = function(n, c)
{
	return this.radix(0x08, (n) ? n : 6, c);
}

/*
* object.dec(number, string)
* Transform the number object to string of decimal presentation
*
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.dec = function(n, c)
{
	return this.radix(0x0A, (n) ? n : 2, c);
}

/*
* object.hexl(number, string)
* Transform the number object to string of hexadecimal presentation in lower-case of major characters (0-9 and a-f)
*
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.hexl = function(n, c)
{
	return this.radix(0x10, (n) ? n : 4, c);
}

/*
* object.hex(number, string)
* Transform the number object to string of hexadecimal presentation in upper-case of major characters (0-9 and A-F)
*
* @param	number	Width of numeric string
* @param	string	Padding chacracter (by default, '0')
* @return	string	Numeric string
* @access	public
*/
Number.prototype.hex = function(n, c)
{
	if ( ! c ) {
		c = '0';
	}
	return this.hexl(n, c).toUpperCase();
}

/*
* object.ltrim()
* Transform the string object removing leading whitespaces
*
* @return	string
* @access	public
*/
String.prototype.ltrim = function()
{
	return this.replace(/(^\s*)/, "");
}

/*
* object.rtrim()
* Transform the string object removing trailing whitespaces
*
* @return	string
* @access	public
*/
String.prototype.rtrim = function()
{
	return this.replace(/(\s*$)/g, "");
}

/*
* object.trim()
* Transform the string object removing leading and trailing whitespaces
*
* @return	string
* @access	public
*/
String.prototype.trim = function()
{
	return this.replace(/(^\s*)|(\s*$)/g, "");
}

/*
* object.dup()
* Transform the string object duplicating the string
*
* @return	string
* @access	public
*/
String.prototype.dup = function()
{
	var val = this.valueOf();
	return val + val;
}

/*
* object.repeat(number)
* Transform the string object multiplying the string
*
* @param	number	Amount of repeating
* @return	string
* @access	public
*/
String.prototype.repeat = function(n)
{
	if ( ! n || n <= 0 ) {
		return "";
	}
	if ( ! this.origin ) {
		this.origin = this.valueOf();
	}
	var val = this.repeat(parseInt(n / 2));
	return ((n % 2) ? this.origin : "") + val + val;
}

/*
* object.padding(number, string)
* Transform the string object to string of the actual width filling by the padding character (by default ' ')
* Negative value of width means right padding, and positive value means left one
*
* @param	number	Width of string
* @param	string	Padding chacracter (by default, ' ')
* @return	string
* @access	public
*/
String.prototype.padding = function(n, c)
{
	var val = this.valueOf();
	if ( ! n ) {
		return val;
	}
	if ( ! c ) {
		c = " ";
	}
	var pad = String(c).charAt(0).repeat(Math.abs(n) - this.length);
	return (n < 0) ? pad + val : val + pad;
//	return (n < 0) ? val + pad : pad + val;
}

/*
* object.alignRight(number, string)
* Wrapper for object.padding
* Synonym for object.padLeft
*
* @param	number	Width of string
* @param	string	Padding chacracter
* @return	string
* @access	public
*/
String.prototype.alignRight = 

/*
* object.padLeft(number, string)
* Wrapper for object.padding
* Transform the string object to string of the actual width adding the leading padding character (by default ' ')
*
* @param	number	Width of string
* @param	string	Padding chacracter
* @return	string
* @access	public
*/
String.prototype.padLeft = function(n, c)
{
	return this.padding(-Math.abs(n), c);
}

/*
* object.alignLeft(number, string)
* Wrapper for object.padding
* Synonym for object.padRight
*
* @param	number	Width of string
* @param	string	Padding chacracter
* @return	string
* @access	public
*/
String.prototype.alignLeft = 

/*
* object.padRight(number, string)
* Wrapper for object.padding
* Transform the string object to string of the actual width adding the trailing padding character (by default ' ')
*
* @param	number	Width of string
* @param	string	Padding chacracter
* @return	string
* @access	public
*/
String.prototype.padRight = function(n, c)
{
	return this.padding(Math.abs(n), c);
}

/**
* sprintf(format, argument_list)
*
* The string function like one in C/C++, PHP, Perl
* Each conversion specification is defined as below:
*
* %[index][alignment][padding][width][precision]type
*
* index		An optional index specifier that changes the order of the 
*		arguments in the list to be displayed.
* alignment	An optional alignment specifier that says if the result should be 
*		left-justified or right-justified. The default is 
*		right-justified; a "-" character here will make it left-justified.
* padding	An optional padding specifier that says what character will be 
*		used for padding the results to the right string size. This may 
*		be a space character or a "0" (zero character). The default is to 
*		pad with spaces. An alternate padding character can be specified 
*		by prefixing it with a single quote ('). See the examples below.
* width		An optional number, a width specifier that says how many 
*		characters (minimum) this conversion should result in.
* precision	An optional precision specifier that says how many decimal digits 
*		should be displayed for floating-point numbers. This option has 
*		no effect for other types than float.
* type		A type specifier that says what type the argument data should be 
*		treated as. Possible types:
*
* % - a literal percent character. No argument is required.  
* b - the argument is treated as an integer, and presented as a binary number.
* c - the argument is treated as an integer, and presented as the character 
*	with that ASCII value.
* d - the argument is treated as an integer, and presented as a decimal number.
* u - the same as "d".
* f - the argument is treated as a float, and presented as a floating-point.
* o - the argument is treated as an integer, and presented as an octal number.
* s - the argument is treated as and presented as a string.
* x - the argument is treated as an integer and presented as a hexadecimal 
*	 number (with lowercase letters).
* X - the argument is treated as an integer and presented as a hexadecimal 
*	 number (with uppercase letters).
*/
function sprintf()
{
	var args = arguments;
	var frmt = arguments[0].replace(/%%/g, "\0\0");
	var result = "", prev = 0, index = 0;
	var re = /%(\d+[\$#])?([+-])?('.|0| )?(\d*)(\.\d*)?([bcdfosuxX])/g;
	/*
	* The re.exec() method returns the array with the following properties
	* wich are used in this function
	*	x.index contains the substring position found at the origin string
	*	x[0] contains the found substring
	*	x[1] contains the index specifier (as \d+\$ or \d+#)
	*	x[2] contains the alignment specifier ("+" or "-" or empty)
	*	x[3] contains the padding specifier (space char, "0" or defined as '.)
	*	x[4] contains the width specifier (as \d*)
	*	x[5] contains the floating-point precision specifier (as \.\d*)
	*	x[6] contains the type specifier (as [bcdfosuxX])
	*/
	var x;
	while (x = re.exec(frmt)) {
		for (var i = 0; i < x.length; i++) if (x[i] == undefined) x[i] = "";
		index++;
		var ins = (x[1]) ? args[x[1].substring(0, x[1].length - 1)] : args[index];
		switch (x[6]) {
		case "b":
			ins = Number(ins).bin();
			break;
		case "c":
			ins = String.fromCharCode(ins);
			break;
		case "d":
		case "u":
			ins = Number(ins).dec();
			break;
		case "f":
			ins = Number(ins);
			if (x[5]) {
				ins = ins.toFixed(x[5].substr(1));
			} else if (x[4]) {
				ins = ins.toExponential(x[4]);
			} else {
				ins = ins.toExponential();
			}
			break;
		case "o":
			ins = Number(ins).oct();
			break;
		case "s":
			ins = String(ins);
			break;
		case "x":
			ins = Number(ins).hexl();
			break;
		case "X":
			ins = Number(ins).hex();
			break;
		}
		result += frmt.substring(prev, x.index);
		prev = x.index + x[0].length;
		result += ins.padding(x[2] + x[4], x[3].substr(x[3].length - 1));
	}
	if ( prev < frmt.length ) {
		result += frmt.substr(prev);
	}
	result = result.replace(/\0\0/g, "%");
	return result;
}

