/*
 * Copyright (c) 1997-2009, Ron Lussier. All rights reserved.
 *
 * This software is an unpublished work subject to a confidentiality agreement
 * and protected by copyright and trade secret law. Unauthorized copying,
 * redistribution or other use of this work is prohibited. All copies must
 * retain this copyright notice. Any use or exploitation of this work without
 * authorization could subject the perpetrator to criminal and civil liability.
 *
 * The above copyright notice does not indicate actual or intended publication
 * of this source code.
 */

/*---------------------------------------------------------------------------------------------------------
 * This file contains the core code used by Earthenberm applications.
 *
 * THE CODE IN THIS FILE SHOULD NOT HAVE ANY DEPENDENCIES OUTSIDE OF THIS FILE!!!
 *---------------------------------------------------------------------------------------------------------*/

if (typeof EB == 'undefined') {
	/**
	 * The EARTHENBERM global namespace object.
	 */
	var EB = {};
	}

/*
 * This adds a trim() function to the String class, all
 * strings automatically get it.
 *
 * trim() removes all leading and trailing and whitespace for the string,
 * returning a new string that is the cleaned string.
 */

String.prototype.trim = function() {
	a = this.replace(/^\s+/, '');
	return a.replace(/\s+$/, '');
}

String.prototype.startsWith = function(s) {
	return this.indexOf( s ) == 0;
}
	
String.prototype.endsWith = function(s) {
    var		offset = this.length - s.length;

    return (offset >= 0) && (this.lastIndexOf(s) == offset);
}


String.prototype.countWords = function() {
	var r = 0;
	a=this.replace(/\s/g,' ');
	a=a.split(' ');
	for (z=0; z<a.length; z++) {if (a[z].length > 0) r++;}
	return r;
	}

/**
 * Capitalizes the first letter of each word.
 */
String.prototype.capitalize = function(){ //v1.0
    return this.replace(/\w+/g, function(a){
        return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
    });
};

/**
 *  Remove all occurrences of a token in the string
 */
String.prototype.removeToken = function( token ) {
	var		i = this.indexOf( token );
	var		result;

	if (i == -1) return this;
  
	result = this.substring( 0, i ) + this.substring( i + token.length ).removeToken( token );

	return result;
	}

/**
 * Checks to see if the array contains the specified object.
 */
Array.prototype.contains = function(obj) {
	var i = this.length;

	while (i--) {
		if (this[i] === obj) {
			return true;
		}
	}

	return false;
}

/*---------------------------------------------------------------------------------------------------------
 * Javascript template functionality - Added to 'String'
 *---------------------------------------------------------------------------------------------------------*/

/**
 * Adds a printf() statement to the string class, all strings automatically get it.
 *
 * printf() assumes 'this' is a template, and accepts a variable number of parameters
 * to be placed into the template.  It searches the template for {0} and puts in argument0, searches
 * for {1} and puts in argument1, etc.
 *
 * REMEMBER: the template begins numbering {0} at 0, not 1!
 *
 * argument0-n: the parameters to be replaced in the string  
 */
String.prototype.printf = function() {   
	var		num = arguments.length;   
	var		oStr = this;     

	for (var loop = 0; loop < num; loop += 1) {
		var		pattern = '\\{' + (loop) + '\\}';
		var		re = new RegExp(pattern, 'g');

		oStr = oStr.replace(re, arguments[loop]);   
		}

	return oStr; 
}


/*---------------------------------------------------------------------------------------------------------
 * Logging
 *---------------------------------------------------------------------------------------------------------*/

/**
 * Writes a message to the javascript log, using different techniques depending on browser.
 */
EB.log = function (msg) {
	msg = 'EARTHENBERM: ' + msg;
	if (console && console.log) {
		console.log(msg);
	}
	else if (opera && opera.postError) {
		/*
		 * Opera!
		 */
		opera.postError(msg);
	}
}


/*---------------------------------------------------------------------------------------------------------
 * Browser Detection
 *---------------------------------------------------------------------------------------------------------*/

/**
 * Returns the Microsoft Internet Explorer (major) version number, or 0 for other browsers.
 *
 * The function works by finding the "MSIE " string and extracting the version number following the space,
 * up to the decimal point for the minor version which is ignored.
 */
EB.getIEVersion = function() {
	var		userAgent = window.navigator.userAgent;
	var		msie = userAgent.indexOf ( 'MSIE ' );

	if (msie > 0) {						// is Microsoft Internet Explorer; return version number

		return parseInt ( userAgent.substring( msie+5, userAgent.indexOf ( '.', msie ) ) );
	}
	else {
		return 0;						// It's another browser
	}
}

/**
 * @return		true if the current browser is a flavor of IE, and false otherwise.
 */
EB.isIE = function() {
	return (EB.getIEVersion() != 0);
}

/*
 * Finds an Ajax template on the page, unescapes it, and returns it
 */
EB.getTemplate = function(sElementId) {
	return unescape(document.getElementById(sElementId).innerHTML);
	}

/*
 * Returns array with page width, height and window width, height.
 */
EB.getPageSize = function() {
	var		xScroll, yScroll;

	if (window.innerHeight && window.scrollMaxY) {
		xScroll = window.innerWidth + window.scrollMaxX;
		yScroll = window.innerHeight + window.scrollMaxY;
		}
	else if (document.body.scrollHeight > document.body.offsetHeight) {
		// all but Explorer Mac

		xScroll = document.body.scrollWidth;
		yScroll = document.body.scrollHeight;
		}
	else {
		// Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari

		xScroll = document.body.offsetWidth;
		yScroll = document.body.offsetHeight;
		}
	
	var windowWidth, windowHeight;
	
	if (self.innerHeight) {
		// all except Explorer
		
		if (document.documentElement.clientWidth) {
			windowWidth = document.documentElement.clientWidth; 
			}
		else {
			windowWidth = self.innerWidth;
			}

		windowHeight = self.innerHeight;
		}
	else if (document.documentElement && document.documentElement.clientHeight) {
		// Explorer 6 Strict Mode
		
		windowWidth = document.documentElement.clientWidth;
		windowHeight = document.documentElement.clientHeight;
		}
	else if (document.body) {
		// other Explorers
		
		windowWidth = document.body.clientWidth;
		windowHeight = document.body.clientHeight;
		}
	
	// for small pages with total height less then height of the viewport
	
	if (yScroll < windowHeight) {
		pageHeight = windowHeight;
		}
	else { 
		pageHeight = yScroll;
		}

	// for small pages with total width less then width of the viewport
	
	if (xScroll < windowWidth) {
		pageWidth = xScroll;		
		}
	else {
		pageWidth = windowWidth;
		}

	arrayPageSize = new Array( pageWidth, pageHeight, windowWidth, windowHeight )

	return arrayPageSize;
}

/**
 * A list of currencies that are formatted with a number of decimal places that is not 2.
 */
EB.currencyDecimals =
			{
				BIF: 0,
				BYR: 0,
				CLF: 0,
				CLP: 0,
				DJF: 0,
				GNF: 0,
				JPY: 0,
				KMF: 0,
				KRW: 0,
				MGA: 0,
				PYG: 0,
				RWF: 0,
				VUV: 0,
				BHD: 3,
				IQD: 3,
				JOD: 3,
				KWD: 3,
				LYD: 3,
				OMR: 3,
				TND: 3
			};

/**
 * Format a number as a string.
 *
 * @param value				The value to be formatted.
 *							This should be a string, float or int value.
 * @param currency			A three-letter ISO 4217 currency code (i.e., 'USD')
 *							This value is optional.  If it is not specified, then no currency
 *							will be displayed.
 * @param decimalPlaces		The number of decimal places to use in the formatted value.
 *							(This parameter is optional, and defaults to something reasonable,
 *							based on the currency code.)
 * @param grouping			If true, groups the digits (using a comma separator).
 *							For example, the number 299792458 is formatted as 299,792,458.
 *							This parameter is optional and defaults to true.
 */
EB.formatNumber = function( value, currency, decimalPlaces, grouping ) {
	if ((typeof currency == 'undefined') || (currency == null)) {
		currency = '';
	}

	if (typeof decimalPlaces != 'number') {
		if (currency.length > 0) {
			decimalPlaces = EB.currencyDecimals[currency];
		}
		else {
			decimalPlaces = 0;
		}
		
		if (typeof decimalPlaces != 'number') {
			decimalPlaces = 2;
		}
	}

	if (decimalPlaces < 0) {
		decimalPlaces = 0;
	}
	else if (decimalPlaces > 4) {
		decimalPlaces = 4;
	}

	if (typeof grouping != 'boolean') {
		grouping = true;
	}

	/*
	 * Create a multiplier for constructing the decimal places.  This value ends up being '1'
	 * for zero decimal places, '10' for one decimal place, and '100' for two decimal places.
	 */
	var		decimalMultiplier = Math.pow( 10, decimalPlaces );

	value = value.toString().replace( /\$|\,/g, '' );

	if (isNaN( value )) {
		value = '0';
	}

	sign = (value == (value = Math.abs( value )));

	value = Math.floor( value * decimalMultiplier + 0.50000000001 );

	var		cents = value % decimalMultiplier;

	value = Math.floor( value / decimalMultiplier ).toString();

	/**
	 * Add leading zeros to decimal if necessary.
	 */
	cents = cents.toString();
	while (cents.length < decimalPlaces) {
		cents = '0' + cents;
	}

	if (grouping) {
		/**
		 * Add commas.
		 */
		for (var i = 0; i < Math.floor((value.length - (1+i)) / 3); i=i+1) {
			value = value.substring( 0, value.length - (4*i+3) ) + ',' +
						value.substring( value.length - (4*i+3) );
		}
	}

	/**
	 * Add sign for negative numbers.
	 */
	var		result = ((sign) ? '' : '-') + value;

	if (decimalPlaces > 0) {
		result += '.' + cents;
	}

	/**
	 * Add currency.
	 */
	if (currency.length > 0) {
		result = result + ' ' + currency;

		var		prefix = '';

		switch (currency) {
			case 'USD':
				prefix = '$';
				break;
			case 'EUR':
				prefix = '&euro; ';
				break;
			case 'GBP':
				prefix = '&pound; ';
				break;
			case 'JPY':
				prefix = '&yen; ';
				break;
		}

		return prefix + result;
	}
		
	return result;
}

EB.hideTags = function( tag, className, hide ) {
	if (typeof tag != 'string') {
		return false;
	}

	if (typeof className != 'string') {
		className = null;
	}

	if (typeof hide != 'boolean') {
		hide = false;
	}
	
	var		selects = EB.dom.getElementsByClass( className, null, 'select' );
	var		visibility;
	
	if (hide) {
		visibility = 'hidden';
	}
	else {
		visibility = 'visible';
	}
	
	for (var loop = 0; loop < selects.length; loop++) {
		selects[loop].style.visibility = visibility;
	}
}

EB.openWindow = function(url, windowRel) {
	if (typeof windowRel !== 'string') {
		windowRel = '_blank';
	}
	else if (windowRel.length <= 0) {
		windowRel = '_blank';
	}

	var		newWindow = window.open(url, windowRel);
	newWindow.focus();

	return false;
}

EB.openCenteredPercentWindow = function(url, name, percent) {
	if (typeof percent == 'number') {
		percent = percent / 100;

		var		width = screen.width * percent;
		var		height = screen.height * percent;
		
		return EB.openCenteredWindow(url, name, width, height);
	}
}

EB.openCenteredWindow = function(url, name, width, height)
{
	/*
	 * Fudge factors for window decoration space.
	 * In my tests these work well on all platforms & browsers.
	 */
	width += 32;
	height += 96;
	wleft = (screen.width - width) / 2;
	wtop = (screen.height - height) / 2;

	/*
	 * IE5 and other old browsers might allow a window that is partially offscreen or
	 * wider than the screen. Fix that.
	 * (Newer browsers fix this for us, but let's be thorough.)
	 */
	if (wleft < 0) {
		width = screen.width;
		wleft = 0;
	}

	if (wtop < 0) {
		height = screen.height;
		wtop = 0;
	}

	var win = window.open(url, name,
							'width=' + width + ', height=' + height + ', ' + 'left=' + wleft + ', top=' + wtop +
							'location=yes, menubar=yes, status=yes, toolbar=yes, scrollbars=yes, resizable=yes' );
	/*
	 * Just in case width and height are ignored
	 */
	win.resizeTo( width, height );

	/*
	 * Just in case left and top are ignored
	 */
	win.moveTo(wleft, wtop);

	win.focus();

	return false;
}
