//aryooeye.StringUtil.0.0.0;r.0.0.0;richarduie[at]yahoo[dot]com;(c)2008

/////////////////////////////////////////////////////////////////////////
//                                                                     //
//   String utilities using JavaScript object prototype                //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
//                                                                     //
//   RUI library member that enforces containment under "aryooeye"     //
//   namespace; offers pseudo-static reference options for public      //
//   members via name by dot(.)-references to members that can rely    //
//   on "instantiation" calls to default, no-argument constructor.     //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
//                                                                     //
//   Filename.......StringUtil.js                                      //
//   Author.........Richard Harrison (richarduie[at]yahoo[dot]com)     //
//   Created On.....2007-12-25                                         //
//   Last Revised...2008-01-31                                         //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

/**
 *  @Object: ScriptManager
 *
 *
 *  @Syntax: var strUtil = new aryooeeye.StringUtil()
 *           StringUtil.{method_name}() [static]
 */


/////////////////////////////////////////////////////////////////////
//   N A M E S P A C E   C O N T A I N E R   O V E R H E A D 
//
//   Enforce the condition that any RUI object can only be 
//   created under the namespace aryooeye in order to further 
//   ensure protection from potential name-collisions.
//
	// If namespace object does not yet exist, create it.
	if ('undefined' == typeof aryooeye) var aryooeye = new Function();
	// If object has already been attached, exit.
	if ('undefined' == typeof aryooeye.StringUtil) 
{	// open conditional object creation
	// Now add current object to aryooeye namespace.
	aryooeye.StringUtil = 


/**
 *  @Object: StringUtil
 *
 *
 *  @Syntax: var stringUtil = new StringUtil()
 *           StringUtil.{method_name}() [static]
 *
 *  @Goals:  make additional string functions available without 
 *           explicitly extending the String prototype or introducing 
 *           anonymous members to window context
 */

function (args) {
	//////////////////////////////////////////////////////////////////
	//  D E P E N D E N C Y   M A N A G E M E N T  ///////////////////
	//	 ...no current dependencies...

	//////////////////////////////////////////////////////////////////
	//  P R I V A T E   M E M B E R S  ///////////////////////////////

		// Declare private methods.

		// Declare private fields.
			// - publicly gettable and settable

			// - publicly gettable only

			// - purely private
			// define "whitespace" to be: blank, new-line, carriage-return, 
			// and tab characters
			var whitespace = ' \n\r\t';
			
	//////////////////////////////////////////////////////////////////
	//  P U B L I C   M E M B E R S  /////////////////////////////////
		// Declare public methods.
			// string reshaping functions - - - - - - - - - - - - - -

			this.wrap = function(str, wid) {
				// reshape {str} by adding new-line characters after every 
				// {wid}th character - wraps string to fit display area {wid} 
				// characters wide
				if (!wid < str.length) return str;
				return this.delimitBlocks(str, blockLen, '\n');
			}

			this.drop = function(str, len) {
				// remove {len} characters of {str}
				// if {len} is positive, remove characters from left-hand end
				// if {len} is negative, remove characters from right-hand end
				if (!len) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.drop()\n' +
						'object instance - ' + this.name + '\n' + 
						'numeric argument {len} required';
					throw new Error(msg);
				}
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.drop()\n' +
						'object instance - ' + this.name + '\n' + 
						'string argument {str} required';
					throw new Error(msg);
				}
				if (len > str.length) return '';
				if (0 > len) {
					// for positive {len}
					return str.substring(0, str.length + len);
				}
				else return str.substring(len);	// for negative {len}
			}

			this.take = function(str, len) {
				// return {len} characters of {str}
				// if {len} is positive, return characters from left-hand end
				// if {len} is negative, return characters from right-hand end
				if (!len) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.take()\n' +
						'object instance - ' + this.name + '\n' + 
						'numeric argument {len} required';
					throw new Error(msg);
				}
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.take()\n' +
						'object instance - ' + this.name + '\n' + 
						'string argument {str} required';
					throw new Error(msg);
				}
				if (0 < len) {
					// for positive {len}
					if (len > str.length) {
						// if {len} greater than length of {str}, add enough 
						// blanks to pad string on right to length {len}
						return this.padRight(str, len, ' ');
					}
					// otherwise, return first {len} characters
					else return str.substring(0, len);
				}
				if (0 > len) {
					// for negative {len}
					if (Math.abs(len) > str.length) {
						// if absolute value of {len} greater than length of 
						// {str}, add enough blanks to pad string on leftt to 
						// length {len}
						return this.padLeft(str, Math.abs(len), ' ');
					}
					// otherwise, return last {len} characters
					else return str.substring(str.length + len);
				}
				return '';
			}

			// string extension functions - - - - - - - - - - - - - -

			this.delimitListItems = function(strArray, delim) {
				if (!delim) delim = ',';
				var last = strArray.length - 1;
				var strList = '';
				for (var j = 0; j < last; j++) {
					strList += strArray[j] + ', ';
				}
				return strList + strArray[last];
			}
	
			this.delimitBlocks = function(str, blockLen, delim) {
				if (!delim) delim = ',';
				if (!blockLen) blockLen = 3;
				b = blockLen*-1;
				fstr = '';
				while (0 < str.length) {
					fstr = delim + this.take(str, b) + fstr;
					str = this.drop(str, b);
				}
				return this.dLN(fstr, ' ' + delim);
			}

			this.padLeft = function(str, len, padChar) {
				// lengthen {str} by adding {len} instances of {padChar} 
				// to the left-hand end of {str} - {padChar} is optional 
				// with default of {whitespace}
				if (!padChar) padChar = whitespace;	// default, if needed
				return this.createPad(len - str.length, padChar) + str;
			}

			this.padRight = function(str, len, padChar) {
				// lengthen {str} by adding {len} instances of {padChar} 
				// to the right-hand end of {str} - {padChar} is optional 
				// with default of {whitespace}
				if (!padChar) padChar = whitespace;	// default, if needed
				return str + this.createPad(len - str.length, padChar);
			}

			this.createPad = function(len, padChar) {
				// create a string of {len} instance of {padChar}
				// {padChar} is optional with default of {whitespace}
				if (!padChar) padChar = whitespace;	// default, if needed
				var pad = '';
				for (var i = 0; i < len; i++) {
					pad += padChar;
				}
				return pad;
			}


			// string cleanup functions - - - - - - - - - - - - - - -

			this.deleteLeadingNulls = function(str, nulls) {
				// delete leading instances of items in {nulls} from 
				// item {str} - return result; if {nulls) not given, 
				// default to {whitespace}
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.deleteLeadingNulls()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				var last = str.length;
				if (1 > last) return str;
				var i = 0;
				for (; i < last; i++) {
					if (!this.isIn(str.charAt(i), nulls)) break;
				}
				return str.substring(i);
			}

			this.dLN = function(str, nulls) {
				// convenience method to shorten call to deleteLeadingNulls()
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.dLN()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				return this.deleteLeadingNulls(str, nulls);
			}

			this.deleteTrailingNulls = function(str, nulls) {
				// delete trailing instances of items in {nulls} from 
				// item {str} - return result; if {nulls) not given, 
				// default to {whitespace}
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.deleteTrailingNulls()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				var last = str.length - 1;
				if (0 > last) return str;
				var i = last;
				for (; i > -1; i--) {
					if (!this.isIn(str.charAt(i), nulls)) break;
				}
				return str.substring(0, i+1);
			}

			this.dTN = function(str, nulls) {
				// convenience method to shorten call to deleteTrailingNulls()
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.dTN()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				return this.deleteTrailingNulls(str, nulls);
			}

			this.deleteAllNulls = function(str, nulls) {
				// delete all instances of items in {nulls} from 
				// item {str} - return result; if {nulls) not given, 
				// default to {whitespace}
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.deleteAllNulls()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				str = this.dLN(str, nulls);
				if (0 == str.length) return str;
				str = this.dTN(str, nulls);
				var last = str.length;
				if (1 > last) return str;
				var nstr = '';
				for (var i = 0; i < last; i++) {
					if (!this.isIn(str.charAt(i), nulls)) {
						nstr += str.charAt(i);
					}
				}
				return nstr;
			}

			this.dAN = function(str, nulls) {
				// convenience method to shorten call to deleteAllNulls()
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.dAN()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				return this.deleteAllNulls(str, nulls);
			}

			this.deleteExtraneousNulls = function(str, nulls) {
				// delete extraneous instances of items in {nulls} from 
				// item {str} - return result; if {nulls) not given, 
				// default to {whitespace}
				// "extraneous" is leading, trailing, and successive, 
				// multiple, embedded occurrences
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.deleteExtraneousNulls()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				str = this.dLN(str, nulls);
				if (0 == str.length) return str;
				str = this.dTN(str, nulls);
				var last = str.length - 1;
				if (0 > last) return str;
				var nstr = '';
				for (var i = 0; i < last; i++) {
					if (!this.isIn(str.charAt(i), nulls) || 
						 !this.isIn(str.charAt(i+1), nulls)   ) {
						nstr += str.charAt(i);
					}
				}
				return nstr + str.charAt(last);
			}

			this.dEN = function(str, nulls) {
				// convenience method to shorten call to deleteExtraneousNulls()
				if (!nulls) nulls = whitespace;	// default, if needed
				if (!str) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.dEN()\n' +
						'object instance - ' + this.name;
					throw new Error(msg);
				}
				return this.deleteExtraneousNulls(str, nulls);
			}

			this.trim = function(str) {
			// remove leading and trailing whitespace from string
				return this.dLN(this.dTN(str));
			}


			// string content analysis functions - - - - - - - - - - -

			this.isIn = function(c, cSet) {
				if (!cSet) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.isIn()\n' +
						'object instance - ' + this.name + '\n' +
						'argument {cSet} not given';
					throw new Error(msg);
				}
				if (!c) {
					var msg = 'MissingArgumentError: ' +
						this.className + '.isIn()\n' +
						'object instance - ' + this.name + '\n' +
						'argument {c} not given';
					throw new Error(msg);
				}
				var last = cSet.length;
				if (1 > last) {
					var msg = 'ArgumentLengthError: ' +
						this.className + '.isIn()\n' +
						'object instance - ' + this.name + '\n' +
						'argument {cSet} must contain characters';
					throw new Error(msg);
				}
				if (1 != c.length) {
					var msg = 'ArgumentLengthError: ' +
						this.className + '.isIn()\n' +
						'object instance - ' + this.name + '\n' +
						'argument {c} must contain exactly 1 character';
					throw new Error(msg);
				}
				return -1 != cSet.indexOf(c);
			}

			this.isLower = function(c) {
				return c == c.toLowerCase();
			}

			this.isUpper = function(c) {
				return c == c.toUpperCase();
			}


			// general functions - - - - - - - - - - - - - - - - - -

			this.getClassName = function() {
					return 'StringUtil';
			}


		// Declare public fields.
			// in case instance needs to know its name, set it directly,
			// after instantiation, i.e., {instance}.name = '{name}' -
			// this is purely an optional, convenience field, which
			// could be attached independent of this definition, but is
			// declared here with a default value for possible use in
			// the report() method
			// set default value - may be used by report()
			this.name = '_name_not_set_';

			// provide dot referencable field of standard property name - can
			// be overridden, but will be refreshed to actual name of "class,"
			// if getter is caller
			this.className = this.getClassName();

	//////////////////////////////////////////////////////////////////
	//  C O N S T R U C T O R  ///////////////////////////////////////
		// Construction steps - only run at instantiation.
		// ...no differential construction required at present...
}

}	// close conditional object creation

/////////////////////////////////////////////////////////////////////
//   S U P P O R T    P S E U D O - S T A T I C    C L A S S - 
//   S T Y L E    R E F E R E N C E S
	//	Since constructor supports default, no-argument instantiation, 
	// provide a "static" reference version to the public members.
	// Note that this removes rui namespace insulation against name-
	// collisions; developer is responsible for managing name-safety 
	// in implementation context. Next statement can be commented or 
	// de-commented without affecting base object in any way.
//	var StringUtil = new aryooeye.StringUtil();
