/*
 * Copyright (c) 2007, Harley Davidson Inc. All rights reserved.
 */
/** 
 * This function is just a shortcut replacement for document.getElementById(el) ... 
 * which is a very common function but is lengthy textwise and prone to simple errors.
 * 
 * uses yahoo's YAHOO.util.Dom.get()
 * @method get
 * @param {String | HTMLElement |Array} el  Accepts a string to use as an ID for getting 
 *                                          a DOM reference, an actual DOM reference, or an 
 *                                          Array of IDs and/or HTMLElements.
 * @return {HTMLElement | Array}            A DOM reference to an HTML element or an array 
 *                                          of HTMLElements.
 */
function $(el) {return YAHOO.util.Dom.get(el);}

/** 
 * This function is a shortcut to log messages using YAHOO's logger (if it exists).
 * @method log
 * @param {String} message  the message that we are logging
 */
function log(message) {if(showDebug){YAHOO.log(message,'HD :');}}


/**
 * HD.util is the namespace used for all common Harley Davidson utility functions.
 * @class HD.util
 * @type Object
 * @static
 */
HD.util = {
	/**
	 * Format a template string with a list of replacement strings
	 * 
	 * @param {String} str           The string to run replacements on
	 * @param {Object} replacements  An object literal of hashes to be replaced along with their values
	 * @return {String}              The resulting string
	 */
	template : function(str, replacements) {
		for (var hash in replacements) {
			str = str.replace(new RegExp(hash,'g'), (replacements[hash] != null ? replacements[hash] : '').toString());
		}
		return str;
	},
	
	/**
	 * Check whether the object passes in is null, undefined, or blank, in which case returns true.
	 */
	hasValue : function(obj) {
		return obj != null && typeof(obj) != "undefined" && obj != "";
	},
	
	/**
	 * This is a recursive utility function that clones an object to the
	 * depth level specified.
	 * 
	 * @method cloneObject
	 * @param  {object}   oldObj  the object that we are cloning
	 * @param  {boolean}  deep    indicates if this is a deep clone or not defaults to false
	 * @return {object}           the cloned element
	 */
	cloneObject : function (oldObj,deep) {
		if (oldObj == null) {
			return null;
		} else if (oldObj instanceof String) {
			// special case String since copy just loops over index elements
			// NOTE:: pretty sure this isn't necessary as a string is copied 
			// every time you declare a new var equal to it ... will test.
			return new String(oldObj);
		} else if (typeof(oldObj.clone) == 'function') {
			// allow objects to define a clone function
			return oldObj.clone();
		} else {
			// create the new object
			var newObj = new oldObj.constructor();

			// copy the elements
			return this.copyObject(oldObj, newObj, deep);
		}
	},

	/**
	 * This is a recursive utility function that copies an object to the
	 * depth level specified.
	 * 
	 * @method copyObject
	 * @param  {object}   oldObj  the object being copied
	 * @param  {object}   newObj  the object that the values are copied to
	 * @param  {boolean}  deep    indicates if this is a deep clone or not defaults to false
	 * @return {object}           the copied object
	 */
	copyObject : function (oldObj, newObj, deep) {	
		// loop through the properties (p) and add it to the new element
		var v;
		
		for(var p in oldObj) {
			v = oldObj[p];
			newObj[p] = ((deep) && (typeof(v) == 'object')) ? this.cloneObject(v, true) : v;
		}
		
		return newObj;
	},

	/**
	 * This function will return the index of an array element equal to the given value.
	 * 
	 * @method indexOf
	 * @param {Array} array   the array to search
	 * @param {Object} value  the value to find
	 * @return {int} the index of the matching element, or -1 if an element could not be found 
	 */
	indexOf : function(array, value) {
		for (var i = 0; i < array.length; i++) {
			if (array[i] == value) {
				return i;
			}
		}
		return -1;
	},
	
    /**
     * This function is responsible for getting the scrolling size of 
     * parent elements of the element provided.
     * 
     * @method getRelativeScroll
     * @param {Object}  el  The element we want the coordinates of
     */
	getRelativeScroll : function (el) {
        // get the offest position of the element relative to its parent
        var x = ( isNaN(el.scrollLeft) ) ? 0 : el.scrollLeft ;
        var y = ( isNaN(el.scrollTop) ) ? 0 : el.scrollTop ;

		// while the element has a parent node, get its scroll position
		while (el.parentNode) {
            x += ( isNaN(el.parentNode.scrollLeft) ) ? 0 : el.parentNode.scrollLeft;
            y += ( isNaN(el.parentNode.scrollTop) ) ? 0 : el.parentNode.scrollTop;
			// set the el to its parentNode to see if there
			// are any more parents to account for
			el = el.parentNode;
        }

		return [x,y];
	},
	
	/**
	 * This function return the bounding box of an element relative to the page.
	 *
	 * @method getBoundingBox
	 * @param  {object}   el   The element we want the bounding box for
	 * @return {array}         The array of points 
	 */
	getBoundingBox : function (el) {
		// get the start coordinates of the element
		var XY = YAHOO.util.Dom.getXY(el);
		if (!XY) {return false;}
	
		// set the bounding box
		var X1 = XY[0];
		var X2 = XY[0] + el.offsetWidth;
		var Y1 = XY[1];
		var Y2 = XY[1] + el.offsetHeight;
	
		return [X1,X2,Y1,Y2];
	},

	/**
	 * This function will tell you if the element in question will overlap
	 * the edges of the screen when displayed at the coordinates provided.
	 *
	 * @method getPageOverlap
	 * @param  {object}  el     The element we want the bounding box
	 * @param  {int}     top    The top / Y coordinate we will show the element
	 * @param  {int}     left   The left / X position we will show the element
	 * @return {object}         Static object containing booleans indicating what sides 
	 *                          the element overlaps the page edge
	 */
	getPageOverlap : function (el,top,left) {
		// if position is not provided use the position of the element provided
		if (top == null || left == null) {
			var pos = YAHOO.util.Dom.getXY(el);
			left = pos[0];
			top = pos[1];
		}
	
		// check if the element overlaps the edges
		var overlap = {
			overTop : (top < this.getPageScrollTop()) ? true : false,
			overBottom :((top + el.offsetHeight) > (this.getPageHeight() + this.getPageScrollTop())) ? true : false,
			overLeft : (left < this.getPageScrollLeft()) ? true : false,
			overRight :((left + el.offsetWidth) > (this.getPageWidth() + this.getPageScrollLeft())) ? true : false
		};
		return overlap;
	},
	
	/**
	 * Function will retrieve the text of an html element.
	 *
	 * @method getInnerText
	 * @param  {object} el  The element we want the bounding box
	 * @return {String}     The Text String
	 */
	getInnerText : function (el) {
		// Inner text property returns the element's content without html tags.  If 
		// the browser has this property, return it.
		if (el.innerText) {
			return el.innerText || "";
		} else {
			// else get the node's value to prevent the 
			// text from containing html text.
			return this.getHTMLFreeText(el);
		}
	},
	
	/**
	 * Function will retrieve the number of an html element.
	 *
	 * @method getInnerNumber
	 * @param  {object} el  The element we want the bounding box
	 * @return {float}     The Text String
	 */
	getInnerNumber : function (el) {
		// create a variable which is going to hold the cells text value
		var num = "";
	
		// Inner text property returns the element's content
		// without html tags.  If the browser has this property,
		// return it.
		if (el.innerText) {
			num =  el.innerText;
		} else {
			// else use the node's value to get the inner text
			num = this.getHTMLFreeText(el);
		}
	
		// check if cellData contains "(" and ")" as first and last characters. If so
		// that means that it's a number and it's negative
		num = num.replace(/\(/g, "-");
		num = num.replace(/\)/g, "");
	
		// remove all non numeric chars
		num = num.replace(/[^0-9.\-]/g,'');
	
		// obtain a float from the element data's text
		num = parseFloat(num);
	
		// set to zero if is not a number.
		if (isNaN(num)) {num = 0;}
	
		return num;
	},
		
	/**
	 * Function will retrieve the text of an html element for those browsers 
	 * that do not have innerText attribute
	 * on nodes.
	 *
	 * @method getHTMLFreeText
	 * @param  {object} el  The element we want the bounding box
	 * @return {string}     The Text String
	 */
	getHTMLFreeText : function (el) {
		//init vars
		var text = "";
		var elc = el.childNodes;
		var l = elc.length;
	
		// loop through each html element until you get its 
		// text value.
		for (var i = 0; i < l ; i++) {
			switch (elc[i].nodeType) {
			  case 1: 
				// Type is an element
				text = text + this.getHTMLFreeText(elc[i]);
				break;
			  case 3:
				// Type is text
				text = text + elc[i].nodeValue;
				break;
			  default:
				break;
			}
		}
		return text;
	},
	
	/**
	 * Function to trim empty characters from the right side of a string 
	 *
	 * @method rtrim
	 * @param  {string} str  The string we want to trim
	 * @return {string}      The trimmed string
	 */
	rtrim : function(str) {
    	return str.toString().replace(/\s+$/,"");
	},

	/**
	 * Function to trim empty characters from the right side of all strings contained
	 * in the given object.
	 * 
	 * @method rtrimObject
	 * @param  {Object} obj  The string we want to trim
	 * @return {string}      The trimmed string
	 */
	rtrimObject : function(obj) {
		var value;
		
		for (var prop in obj) {
			value = obj[prop];
			
			if ((typeof value == "string") || (value instanceof String)) {
				obj[prop] = HD.util.rtrim(value);
			}			
		}	
	},	
	
	/**
	 * Function will check if the provided xy point is outside of
	 * the bounds of the provided element.
	 *
     * @method pointOutsideBounds
	 * @param {Array}   pXY  [x,y] position of point
	 * @param {Object}  el   element that we are checking the region for
	 * @param {int}     aoT  allowable distance in from the top
	 * @param {int}     aoB  allowable distance in from the bottom
	 * @param {int}     aoL  allowable distance in from the left
	 * @param {int}     aoR  allowable distance in from the right
	 */
	pointOutsideBounds : function (pXY,el,aoT,aoB,aoL,aoR) {
		aoT = aoT || 0;
		aoB = aoB || 0;
		aoL = aoL || 0;
		aoR = aoR || 0;
		var elB = this.getBoundingBox(el);
		var x1 = elB[0] + aoT ;
		var x2 = elB[1] - aoB ;
		var y1 = elB[2] + aoL ;
		var y2 = elB[3] - aoR ;
		
		// log("x1: " + x1 + " x2: " + x2 +" y1: " + y1 +" y2: " + y2 + " pXY:" + pXY[0] + " pXY:" + pXY[1])
	
		// if the place the user clicked is inside the menu leave it showing,
		// else hide it.
		if (  x1 < pXY[0] && pXY[0] < x2 &&  y1 < pXY[1] && pXY[1] < y2 ) {
			return false;
		} else {
			return true;
		}	 
	},
	
	/**
	 * Function will determine if the two dom elements passed in overlap
	 * 
     * @method elementsOverlap
	 * @param  {Dom Object} el1
	 * @param  {Dom Object} el2
	 * @return {boolean}
	 */
	elementsOverlap : function(el1,el2) {
		if (!el1 || !el2) {return;}

        // get the bounding boxes 
        var el1Pos = HD.util.getBoundingBox(el1);
        var el2Pos = HD.util.getBoundingBox(el2);

		// get the positions ... this is just to make the overlap check easier to read
        var el1X1 = el1Pos[0];
        var el1X2 = el1Pos[1];
        var el1Y1 = el1Pos[2];
        var el1Y2 = el1Pos[3];
        var el2X1 = el2Pos[0];
        var el2X2 = el2Pos[1];
        var el2Y1 = el2Pos[2];
        var el2Y2 = el2Pos[3];

        if (!(el1X1 > el2X2 || el1X2 < el2X1 || el1Y1 > el2Y2 || el1Y2 < el2Y1)) {
			// yup overlap
			return true;
		}

		// no ovelap
		return false;
	},
	
	
	/**
	 * Function will check to see if an element view wraps another element view completely
	 * 
     * @method elementViewContainsElement
	 * @param {Object} el      the element we want to see is within the other
	 * @param {Object} wrapper the potential wrapper for the other element
	 */
	elementViewContainsElement : function(el,wrapper) {
		if (!el || !potentialWrapper) {return;}
		
        // get the bounding boxes 
        var ePos = HD.util.getBoundingBox(el);
        var wPos = HD.util.getBoundingBox(wrapper);

		// check the container
		if (wPos[0] <= ePos[0] && wPos[1] >= ePos[1] && wPos[2] <= ePos[2] && wPos[3] <= ePos[3]) {
			return true;
		}

		return false;
	},
	
	
	/**
	 * Function will check if the provided shape is inside of
	 * the bounds of the map view.
	 *
     * @method shapeInsideView
	 * @param {Object}  el  shape object (i.e. pushpin) that we want to test
	 * @return {boolean} 
	 */
	shapeInsideView : function (el) {
		// Grab the pixel offsets of the point.
		var pixelOffset = HD.rp.hdMap.map.LatLongToPixel(new VELatLong(el.Latitude, el.Longitude));
		// Adjust offsets so that they are in relation to the viewport rather than the map itself.
		pixelOffset = [pixelOffset.x + HD.rp.hdMap.map.GetLeft(), pixelOffset.y + HD.rp.hdMap.map.GetTop()];
		// Check if the point is within the bounds of the map.
		return !HD.util.pointOutsideBounds(pixelOffset, HD.rp.hdMap.map.mapelement);
	},
	
    /**
     * Holds var for current browser type.
     * 
	 * @return {boolean} 
     */
    isIE : (navigator.userAgent.indexOf("MSIE") >= 0) ? true : false,
	
    /**
     * Holds var for current browser type.
     * 
	 * @return {boolean} 
     */
    isIE7 : ((navigator.userAgent.indexOf("MSIE") >= 0) ? true : false) && !!window.XMLHttpRequest,
	
	


    /**
     * Retrieves the current height/width of the page.
     * 
     * @method getPageDimensions
     * @return {Array} returns page x & y in pixels
     */
    getPageDimensions : function () {
        var pageArr = [0,0];
        pageArr[0] = (document.documentElement.offsetWidth || document.body.offsetWidth) ;
        pageArr[1] = (self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight);
        return pageArr;
    },

	/**
     * Retrieves the current width of the page.
     * 
     * @method getPageWidth
     * @return {int} page width in pixels
     */
    getPageWidth : function () {
        return (document.documentElement.offsetWidth || document.body.offsetWidth );
    },

    /**
     * Retrieves the current height of the page.
     * 
     * @method getPageHeight
     * @return {int} page height in pixels
     */
    getPageHeight : function () {
        return ( self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight);
    },
    
    /**
     * Retrieves the current vertical/horizontal scroll position of the page.
     * 
     * @method getPageScroll
     * @return {int} page width in pixels
     */
    getPageScroll : function () {
        var scrTop = (document.documentElement.scrollTop || document.body.scrollTop);
        var scrLeft = (document.documentElement.scrollLeft || document.body.scrollLeft);
        return [scrLeft,scrTop];  
    },

    /**
     * Retrieves the current vertical scroll position of the page.
     * 
     * @method getPageScrollTop
     * @return {int} the vertical scroll amount of the current div
     */
    getPageScrollTop : function () {
        var scrTop = (document.documentElement.scrollTop || document.body.scrollTop);
        return scrTop;  
    },

    /**
     * Retrieves the current horiszontal scroll position of the page.
     * 
     * @method getPageScrollLeft
     * @return {int} the horizontal scroll amount of the current div
     */
    getPageScrollLeft : function () {
        var scrLeft = (document.documentElement.scrollLeft || document.body.scrollLeft);
        return scrLeft;
  
    },	
	
	/**
	 * Determines the distance between the given points.
	 * 
     * @method getDistance
	 * @param  {Object} point1  point 1
	 * @param  {Object} point2  point 2
	 * @return {float}          the distance (in radians)
	 */
	getDistance : function(point1, point2) {
        var lat1 = HD.util.Math.toRadians(parseFloat(point1.latitude));
        var lon1 = HD.util.Math.toRadians(parseFloat(point1.longitude));
        var lat2 = HD.util.Math.toRadians(parseFloat(point2.latitude));
        var lon2 = HD.util.Math.toRadians(parseFloat(point2.longitude));

        return Math.acos((Math.sin(lat1) * Math.sin(lat2)) + (Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)));
	},
	
	/**
	 * Returns the position of the mouse based on a passed in Event.
	 * 
     * @method getMouseXY
	 * @param  {Event}  e  the mouse event that we use to determine the position
	 * @return {Array}     the X and Y position 
 	 */	
	getMouseXY : function(e) {
		var x = (e.pageX) ? e.pageX : e.clientX + document.body.scrollLeft;
		var y = (e.pageY) ? e.pageY : e.clientY + document.body.scrollTop;
		return [x,y];
	},
	
	/**
	 * This method checks the current url to see if it contains a match to the 
	 * passed in name. 
	 * 
	 * @method getRequestParam
	 * @param  {String} name  the url that we are parsing
	 * @return {String}       the match       
	 */
	getRequestParam : function (name) {
		name = name.replace(/\[/g,"\[").replace(/\]/g,"\]");
		var regexS = "[\\?&]"+name+"=([^&#]*)";
		var regex = new RegExp(regexS);
		var results = regex.exec(window.location.href);
		var res = results == null ? null : results[1];
		return !!res && res.match(/^(true|1)$/) ? true :
			(!!res && res.match(/^(false|0)$/) ? false : res);
	},

	/**
	 * Returns a string that has special characters set to their html safe code values.
	 * Used when appending strings in javascript as html.
	 * 
	 * @method convertToJSSafeHTMLString
	 * @param  {String} s  the string that we will process to be javascript / html safe 
	 * @return {String}    the processed string
	 */
	convertToJSSafeHTMLString : function (s) {
		// array of regual expressions that we will use to make sure a string is
		// safe to be displayed in html.
		var regexArray = new Array();
		// change existing ampersands first
		regexArray[regexArray.length] = {from: /&/g ,to: '&amp;'};
		// change quotation marks that will mess up a html string
		regexArray[regexArray.length] = {from: /'/g ,to: '&#39;'};
		regexArray[regexArray.length] = {from: /"/g ,to: '&quot;'};

		// loop through the array and set the content of the string
		for( var i = 0; i < regexArray.length; i++ ) {
			s = s.replace(regexArray[i].from,regexArray[i].to);
		}

		return s;
	},
	
	/**
	 * Checks if the passed in element is an html element
	 * 
	 * @method isHTMLElement
	 * @param  {Object}       ele
	 * @param  {String|Array} tagname  (optional) the valid tagname/s for the element to be
	 * @return {boolean}               true if an html element matching the tagname/s, false otherwise
	 */
	isHTMLElement : function(el, tagname) {
		// if the element is not an 
		if (el == null || typeof el != "object" || el.nodeName == null) {
			return false;
		}

		// if no tagname we are not comparing to anything, so return true
		if (!tagname) {return true;}

		// if the tagname is a string compare it to the elements nodename
		if (typeof tagname == "string" && tagname.toLowerCase() ==  el.nodeName.toLowerCase()) {
			return true;
		}
			
		// if we have an array of elements we want to compare to do a recursive call with the array contents
		if (this.isArray(tagname)) {
			for (var i = 0; i < tagname.length; i++) {
				// recursive call with the element to determine the contents of the array
				if (this.isHTMLElement(el,tagname[i])) {
					return true;
				}
			}
		}

		return false;
	},
	
	/**
	 * Checks if the passed in element is an array 
	 * 
	 * @method isArray
	 * @param {Object} el
	 */
	isArray : function(el) {
		return (el!=null && typeof(el)=="object" && typeof(el.length)=="number" && (el.length==0 || typeof((el[0])) != "undefined"));	
	},
	
	/**
	 * Based on the state string passed in determines the matching state code.
	 * 
	 * @method convertToStateCode
	 * @param  {String} stateString   the long state string name that we are matching to
	 * @return {String} stateCode     the corresponding state code
	 */
	convertToStateCode : function(stateName) {
		if (!stateName) {
			return null;
		}
		
		stateName = stateName.toLowerCase();

		// Compare this state to all the states in the list.
		var states = HD.content.Text.states;
		
		for (var i = 0; i < states.length; i++) {
			if (stateName == states[i].name.toLowerCase()) {
				return states[i].code;
			}
		}

		return null;
	},
	
	/**
	 * Binary search algorithm for sorted arrays, allows for custom comparisons and insert indexes
	 * @param  {Array} array         An array of objects to search through
	 * @param  {Object} value        An object to search for or compare against
	 * @param  {Function} compareFn  (OPTIONAL) A custom function to compare two objects;
	 *                               must return -1 (less than), 0 (equal to), or 1 (greater than)
	 * @param  {Boolean} insert      (OPTIONAL) Whether to return the first entry greater 
	 *                               or equal to the value (for inserts)
	 * @return {Number} index        The index in the array of the matching/comparing value or null
	 */
	binarySearch : function(array, value, compareFn, insert) {
		// initialize
		var low = 0, high = array.length - 1;
		compareFn = compareFn || function(a,b) {
			return a == b ? 0 : a < b ? -1 : 1;
		};
		// search for matching/compared value
		while (low < high) {
			var mid = Math.floor((low + high) / 2);
			if (/* array[mid] < value */ compareFn(array[mid], value) == -1) {
				low = mid + 1;
			} else {
				high = mid;
			}
		}
		// return result (matching index or insert index if applicable)
		if (low < array.length && (/* array[low] == value */ compareFn(array[low], value) == 0 || 
			(!!insert && /* array[low] > value */ compareFn(array[low], value) == 1))) {
			return low;
		} else {
			return null;
		} 
	},
	
	/**
	 * This function will return a string showing the objects contents.
	 * 
	 * @method debugObject
	 * @param  {Object} object  the object that we want to see the contents of
	 * @return {String}         the String value representing the object
	 */
	debugObject : function(object) {
		var temp = "";
		for(a in object) {
			try {
				temp+=a+' ['+object[a]+']\n';
			} catch(e) {
				continue;
			}
		}	
		return temp;
	},
	
	/**
	 * Convenience method to allow passing of either element or element ID.
	 * 
	 * @method setClassName
	 * @param  {Object/String} element  Element or its ID.
	 * @param  {String} className  		Class name.
	 */
	setClassName : function(element, className) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.className = className;
	},
	
	/**
	 * Convenience method to allow passing of either element or element ID.
	 * 
	 * @method hideShowSection
	 * @param  {Boolean} isChecked  			Determines whether to show or hide section.
	 * @param  {Object/String} section  		Section or its ID.
	 * @param  {Object/String} mirrorElement	Element 'mirroring' the one which triggered this method, keeps top and bottom controls synced.
	 */
	hideShowSection : function(isChecked, section, mirrorElement) {
		var mirrorEl = this.getElement(mirrorElement);
		section = this.getElement(section);
		
		section.style.display = isChecked ? "block" : "none";
		if(mirrorEl != null) {
			mirrorEl.checked = isChecked;
		}
	},
	
	/**
	 * Convenience method to allow passing of either element or element ID.
	 * 
	 * @method getElement
	 * @param  {Object/String} element  Element or its ID.
	 */
	getElement : function(element) {
		if(element == null) {return null;}
		return typeof element == "string" ? $(element) : element;
	},
	
	/**
	 * Set's element's <code>disabled</code> property to <code>true</code>.
	 * 
	 * @method disableElement
	 * @param  {Object/String} element  Element or its ID.
	 */
	disableElement : function(element) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.disabled = true;
	},
	
	/**
	 * Set's element's <code>checked</code> property to <code>false</code>, and <code>disabled</code> property to <code>true</code>.
	 * 
	 * @method uncheckElement
	 * @param  {Object/String} element  Element or its ID.
	 */
	uncheckAndDisable : function(elementId) {
		this.uncheckElement(elementId);
		this.disableElement(elementId);
	},
	
	/**
	 * Set's element's <code>checked</code> property to <code>true</code>.
	 * 
	 * @method checkElement
	 * @param  {Object/String} element  Element or its ID.
	 */
	checkElement : function(element) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.checked = true;
	},
	
	/**
	 * Set's element's <code>checked</code> property to <code>false</code>.
	 * 
	 * @method uncheckElement
	 * @param  {Object/String} element  Element or its ID.
	 */
	uncheckElement : function(element) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.checked = false;
	},
	
	/**
	 * Set's element style's <code>display</code> property to "block".
	 * 
	 * @method setVisible
	 * @param  {Object/String} element  Element or its ID.
	 */
	setVisible : function(element) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.style.display = "block";
	},
	
	/**
	 * Set's element style's <code>display</code> property to "none".
	 * 
	 * @method setVisible
	 * @param  {Object/String} element  Element or its ID.
	 */
	setInvisible : function(element) {
		var el = this.getElement(element);
		if(el == null) {return;}
		el.style.display = "none";
	},
	
	/**
	 * Sets element's <code>innerHTML</code> to content.
	 * 
	 * @method setSectionContent
	 * @param  {String} elementId	Element's ID.
	 * @param  {String} content  	New content.
	 */
	setSectionContent : function(elementId, content) {	
		var el = $(elementId);
		var parentEl = el.parentNode;
		
		var newEl = document.createElement('div');
		newEl.innerHTML = content;
		
		parentEl.insertBefore(newEl, el);
		parentEl.removeChild(el);
		
		return newEl;
	},
	
	createXMLDoc : function(xmlText) {
		var doc;
		if (window.ActiveXObject) {
			// code for IE
			doc = new ActiveXObject("Microsoft.XMLDOM");
			doc.async = "false";
			if(xmlText != null) {
				doc.loadXML(xmlText);
			}
		} else {
			// code for Mozilla, Firefox, Opera, etc.
			if(xmlText != null) {
				doc = new DOMParser().parseFromString(xmlText,"text/xml");
			} else {
				doc = document.implementation.createDocument("", "", null);
			}
		}
		return doc;
	}
};	

/**
 * This IdGenerator class is a static class that will return a unique id 
 * based on a simple counter.  This could be adjusted at a later date to
 * a more advanced method of generating unique strings.
 *
 * @class IdGenerator
 * @static
 * @namespace HD.util
 */
HD.util.IdGenerator = {
	counter : 1,
	generateId : function() { return this.counter++; }
};


/**
 * This Math class is a static class that provides mathematical functions not
 * available on the JavaScript Math class. 
 *
 * @class Math
 * @static
 * @namespace HD.util
 */
HD.util.Math = {
	
	/**
	 * Converts an angle measured in radians to an approximately equivalent angle measured in degrees.
	 * 
	 * @method toDegrees
	 * @param  {float}  angrad  the angle in radians
	 * @return {float}          the angle in degrees
	 */
	toDegrees : function (angrad) {
		return (180/Math.PI)*angrad;	
	},
	
	/**
	 * Converts an angle measured in degrees to an approximately equivalent angle measured in radians.
	 * 
	 * @method toRadians
	 * @param  {float}  angdeg  the angle in degrees
	 * @return {float}          the angle in radians
	 */
	toRadians : function (angdeg) {
		return angdeg*Math.PI/180;
	},
	
	/**
	 * Converts a length measured in miles to the equivalent in kilometers
	 * 
	 * @method toKilometers
	 * @param  {float} miles  length in miles
	 * @return {float}        the length in kilometers
	 */
	toKilometers : function(miles) {
		return miles * 1.609344;
	},


	/**
	 * This function converts a lat/long item's degrees to radians.
	 *
	 * The function is very flexible on formats, allowing signed decimal degrees (numeric 
	 * or text), or deg-min-sec suffixed by compass direction (NSEW). A variety of 
	 * separators are accepted (eg 3? 37' 09"W) or fixed-width format without separators 
	 * (eg 0033709W). Seconds and minutes may be omitted. Minimal validation is done.
	 * 
	 * @method latLngToRadians
	 * @param  {Number|String} brng  the angle as represented in lat/lng
	 * @return {Number}              the lat/lng in radians 
	 */
	latLngToRadians : function(brng) {
		// signed decimal degrees without NSEW
		if (!isNaN(brng)) {
			return this.toRadians(brng);
		}
		
		// strip trailing whitespace
		brng = brng.replace(/[\s]*$/,'');
	
		// compass dir'n
		var dir = brng.slice(-1).toUpperCase();

		var deg;

		// check for correct compass direction
		if (!dir.match(/[NSEW]/)) {
			return NaN;
		}

		// and lose it off the end
		brng = brng.slice(0,-1);

		// check for separators indicating d/m/s
		var dms = brng.split(/[\s:,??'\'"\"]/);
		// convert to decimal degrees...
		switch (dms.length) {
			// interpret 3-part result as d/m/s
			case 3:
				deg = dms[0]/1 + dms[1]/60 + dms[2]/3600;
				break;
			// interpret 2-part result as d/m
			case 2:
				deg = dms[0]/1 + dms[1]/60;
				break;
			// non-separated format dddmmss
			case 1:
				// - normalise N/S to 3-digit degrees
				if (/[NS]/.test(dir)) {brng = '0' + brng;}
				deg = brng.slice(0,3)/1 + brng.slice(3,5)/60 + brng.slice(5)/3600; break;
			default: return NaN;
		}

		// take west and south as -ve
		if (/[WS]/.test(dir)) {deg = -deg;}

		// then convert to radians
		return this.toRadians(deg);
	},
	

	/**
	 * This object will determine the distance between two latitude and longitude points by  
	 * processing the haversine formula.
	 * 
	 * The haversine formula is an equation important in navigation, giving great-circle distances 
	 * between two points on a sphere from their longitudes and latitudes.
	 * 
	 * @method getDistanceBetweenLatLng
	 * @param  {Object|VELatLng} p1    object representing the lat/lng point (ex. {Latitude:45,Longitude:45} or VELatLng).   
	 * @param  {Object|VELatLng} p2    object representing the lat/lng point (ex. {Latitude:46,Longitude:46} or VELatLng).  
	 * @param  {String}          uom   (optional) units of measure
	 * @return {Number}                the distance between the two points in kilometers or miles
	 */
	getDistanceBetweenLatLng : function(p1,p2,uom) {
		uom = uom || "km";
		// convert the lat lngs to radians for processing
		p1 = {lat:this.latLngToRadians(p1.Latitude || p1.latitude || 0),lng:this.latLngToRadians(p1.Longitude || p1.longitude || 0)};
		p2 = {lat:this.latLngToRadians(p2.Latitude || p2.latitude || 0),lng:this.latLngToRadians(p2.Longitude || p2.longitude || 0)};
		
		// earth's mean radius in km or miles
		R = (uom == "km") ? 6371 : 3959;
		
		var dLat  = p2.lat - p1.lat;
		var dLong = p2.lng - p1.lng;
		
		// run the haversine equation
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(p1.lat) * Math.cos(p2.lat) * 
				Math.sin(dLong/2) * Math.sin(dLong/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		
		return d;
	}
};


// registers the user object with HD
HD.register("util", "1.0");
