/**
 * General-purpose JavaScript utilities for HourGlass.
 *
 * They can be used in the application and on the promo site.
 */



HG = window.HG  || {};

HG.util = {
	/**
	 * Determines if the specified element is contained in the
	 * specified array
	 * 
	 * @param element the element to look for
	 * @param array the array in which to look for the element
	 */
	inArray: function(element, array) {
		var i;
		var found = false;
		for (i in array) {
			if (array[i] == element) {
				found = true;
				break;
			}
		}
		return found;
	},
    
    /**
     * Pads a string with one zero if necessary. This function was 
     * extracted from legacy code that was repeated on many pages.
     *
     * @param string the string
     */
	padAZero: function (s) {
        s = s.toString();
		if (s.length == 1) {
			return '0' + s;
		} else {
            return s;
        }
    },
    
    /**
     * Determines whether the specified node is a decendant of
     * another node in the DOM tree
     *
     * @param node a DOM node
     * @param parent the node that might be the first argument's parent or
     *               ancestor
     */
    isDescendantOf: function (node, parent) {
		var current = node;
		while (current != null && current != parent) {
    		current = current.parentNode;    	
		}
    	return current != null;
    },
    	
        
    /**
     * Returns the absolute position of an element
     *
     * @param element a DOM element
     * @return An object representing the element's position (e.g. {left: 10, top:35})
     */
	getElementPosition : function (element) {
		var offsetTrail = element;
		var offsetLeft = 0;
		var offsetTop = 0;
		while (offsetTrail) {
			offsetLeft += offsetTrail.offsetLeft;
			offsetTop += offsetTrail.offsetTop;
			offsetTrail = offsetTrail.offsetParent;
		}
		if (navigator.userAgent.indexOf("Mac") != -1 && 
			typeof document.body.leftMargin != "undefined") {
			offsetLeft += document.body.leftMargin;
			offsetTop += document.body.topMargin;
		}
		return {left:offsetLeft, top:offsetTop};
	},
	
    /**
     * Determines whether a point is contained within
     * an element, with an optional "padding" value that can
     * be used as a "tolerance" specifier
     *
     * @param element a DOM element
     * @param x the X coordinate of the position to be tested
     * @param y the Y coordinate of the position to be tested
     * @param paddingX the tolerance on the X axis
     * @param paddingY the tolerance on the Y axis
     */
	inElement : function (element, x, y, paddingX, paddingY) {
    	var inElement;
    	
    	if (paddingX == undefined) {
    		paddingX = 0;
		}
    	
    	if (paddingY == undefined) {
    		paddingY = 0;
		}
    	
		var pos = this.getElementPosition(element)
    	
    	inElement = (
    	  (x >= pos.left - paddingX) &&
    	  (y >= pos.top - paddingY) &&
    	  (x <= pos.left + element.clientWidth + paddingX) &&
		  (y <= pos.top + element.clientHeight + paddingY)
		);
    	return inElement;
		  
    },
    
	addToList: function (list, option) {
			try {			
				list.add(option, null);
			} catch (e) {
				// For non-compliant browsers
				list.add(option);			
			}    
    },
    
    /**
	 * Returns true if the specified value is found in the
     * specified SELECT list, false otherwise
     * @param list a SELECT element
     * @param value the value to look for
     * @return a boolean item indicating if the value was found.
     */
	inList : function(list, value) {
		var i;
		var found = false;
		for (i=0;i<list.options.length;i++) {
			if (list.options[i].value == value) {
				found = true;
				break;
			}    		
		}
		return found;
		
	},
	
	/**
	 * Converts a form element to a GET URL for the
	 * purpose of dynamically re-directing a form submission
	 * (e.g. for AJAX or pop-ups)
	 */
	convertFormToUrl : function(form, targetUrl) {
		if (typeof(form) == 'string') {
			form = $(form);
		}
		
		
		var url;
		if (targetUrl == undefined) {
			url = form.action + "?";
		} else {
			url = targetUrl;
			if (targetUrl.search(/\?/) == -1) {
				url += '?';
			} else {
				url += '&';
			}
		}
		var i;
		var first = true;
		for (i=0;i<form.elements.length;i++) {
			var element = form.elements[i];
			
			if (element.type == 'checkbox' || element.type == 'radio') {
				if (!element.checked) {
					continue;
				}
			}
			if (element.value != undefined) {

				if (first) {
					first = false;
				} else {
					url += "&";
				}
			
					
				if (typeof(element.value) == 'object') { // an array
					var j;
					for (j in element.value) {
						url += element.name + "=" + encodeURI(element.value[j]);
					}
				} else {
					url += element.name + "=" + encodeURI(element.value);
				}
			}
		}
		return url;
	},
    
    toQueryString : function(object) {
        var first = true;
        var result = '';
        
        for (var key in object) {
            if (first) {
                first = false;
            } else {
                result += '&'
            }
            
            result += escape(key) + '=' + escape(object[key]);            
        }
        
        return result;
        
    },
	
    parseQueryString : function(queryString) {
        var pairs = queryString.split('&');
        var result = {};
        
        for (var i=0;i<pairs.length;i++) {
            var pair = pairs[i].split('=');            
            if (pair.length == 2) {
                result[unescape(pair[0])] = unescape(pair[1]);
            }
        }        
        
        return result;
    },
                                        
	/**
	 * Submits a form to a new pop-up window
	 *
	 * @param form a form element
	 * @param name the name of the window
	 * @param features the features (height, width, etc, see window.open())
	 * @param replace whether or not the window will be replaced.
	 * @param targetUrl the URL to which the form will be submitted (optional)
	 */
	submitFormToPopup : function(form, name, features, replace, targetUrl) {
		var url = this.convertFormToUrl(form, targetUrl);
		return window.open(url, name, features, replace);    	
	},
	
	/**
	 * Submits a form to an AJAX request processor
	 *
	 * NB: This function has not been finished yet
	 */
	submitFormToAJAX : function (form, targetUrl, method, callback) {
		var postData;
		var url;
		switch (method) {
			case 'get':
				url = this.convertFormToUrl(form, targetUrl);
				break;
			case 'post':
				url = targetUrl;
				// TODO: Implement convertFormToQueryString
				postData = this.convertFormToQueryString(form);
				break;
		}
		YAHOO.util.Connect.asyncRequest(method, url, callback, postData);
	},
	 
	
	isNumeric : function (sText) {
	   var ValidChars = "0123456789.";
	   var IsNumber=true;
	   var Char;
	
	 
	   for (i = 0; i < sText.length && IsNumber == true; i++) 
		  { 
		  Char = sText.charAt(i); 
		  if (ValidChars.indexOf(Char) == -1) 
			 {
			 IsNumber = false;
			 }
		  }
	   return IsNumber;
   
   },    
	/**
	 * Performs a deep copy of an object
	 * @param object an object
	 * @return a copy of the object
	 */
	copyObject: function(obj) {
		 // create a "new" object or array depending on the type of obj
		 var copy = (obj instanceof Array) ? [] : {};
		 
		 // loop over all of the value in the object or the array to copy them
		 for(var i in obj) {
			 // assign a temporarity value for the data inside the object
			 var item = obj[i];
			  // check to see if the data is complex or primitive
			 switch(item instanceof Array || item instanceof Object) {
				 case true:
					// if the data inside of the complex type is still complex, we need to
				   // break that down further, so call copyObject again on that complex
				   // item
				   copy[i] = this.copyObject(item);
					break;
				default:
				   // the data inside is primitive, so just copy it (this is a value copy)
				   copy[i] = item;
			 }
		 }
	   return copy;
	},
	
	
	/**
	 * Converts a JSON string or an AJAX response to a JavaScript object
	 *
	 * @param expression a JSON expression or an AJAX response
	 * @return an object
	 */
	processJSON : function(expression) {
		var script;
		
		if (typeof(expression) == 'object') {
			script = expression.responseText;
		} else {
			script = expression;
		}
		
		script = "(" + script + ")";
		return eval(script);
	},
	
	
	/**
	 * Displays a generic AJAX error
	 *
	 * @param o the response object (reserved for later use).
	 */
	ajaxError: function(o) {
		alert(HG.text.getText(HG_GETTEXT + '_ajax_error') + ' - ' + o.statusText);	
	},
	
	Set : function() {
		this.elements = [];
		this.elementMap = {};
		
	}	
}

HG.util.Set.prototype = {
	/**
	 * Determines whether this set contains an element.
	 *
	 * @param element the element to check for
	 * @return true if the set contains the element, false otherwise
	 */
	contains: function(element) {
		return this.elementMap[element] != null;
	},
	
	/**
	 * Returns all the elements in this set as an array
	 *
	 * @return an array of elements
	 */
	getElements: function() {
		return this.elements;
	},
	
	/**
	 * Returns the number of elements in this set
	 *
	 * @return the number of elements
	 */
	size : function() {
		return this.elements.length;
	},
	
	/**
	 * Adds an element to this set
	 *
	 * @param element the element to be added
	 */
	add : function(element) {
		if (!this.contains(element)) {
			this.elements.push(element);
			this.elementMap[element] = true;
		}
	},
	
	/**
	 * Removes an element from this set
	 *
	 * @param element the element to be removed
	 */
	remove: function(element) {
		this.elementMap[element] = null;

		var i;		
		for (i=0;i<this.elements.length;i++) {
			if (this.elements[i] == element) {
				this.elements.splice(i, 1);
				break;
			}
		}
	},
	
	/**
	 * Removes all elements from this set
	 */
	clear: function() {
		this.elements = [];
		this.elementMap = {};
	}
}



/**
 * Browser bookmark tools.
 *
 * Call HG.util.Bookmarks.addBookmark to quickly display a pre-filled
 * pop-up that allows the user to bookmark the current page or any other
 * page. The init() method needs to be called before any calls to 
 * addBookmark();
 */
HG.util.Bookmarks = {
	method : 'none',
	
	/**
	 * Determines the method used for bookmark generation
	 */
	init : function() {
		if (window.external != undefined && (HG.browser == undefined || HG.browser.is_ie)) {
			this.method = 'IE';
		} else if (window.sidebar != undefined) {
			this.method = 'Mozilla';
		}
	},
	
	
	/**
	 * Displays the bookmark creation pop-up using the method detected by
	 * init(). This method is a no-op in browsers other than IE and Mozilla/Gecko
	 * derivatives.
	 *
	 * @param title the name of the bookmark to be created
	 * @param url the URL to which the new bookmark will link
	 */
	addBookmark: function(title, url) {
		switch (this.method) {
			case 'IE':
				window.external.AddFavorite(location.href, title);
				break;
			case 'Mozilla':
				window.sidebar.addPanel(title, location.href, "");
				break;
			case 'none':
			default:
				// Do nothing
				break;
		}
	
	},
	
	/**
	 * Determines whether dynamic bookmarks are supported by the
	 * user's browser
	 *
	 * @return true if bookmarks are supported, false otherwise.
	 */
	isSupported : function() {
		return this.method != 'none';
	}
	
}

HG.util.Bookmarks.init();


HG.util.JSTest = function() {
    this.enabledId = 'main-javascript-enabled';
    this.disabledId = 'main-javascript-disabled';
}

HG.util.JSTest.prototype.run = function() {
    $(this.disabledId).style.display='none';
    document.body.jstest = this;
    window.onload = function() {
        $(document.body.jstest.enabledId).style.display = 'block';
    };
}

HG.util.Date = {
    monthLengths : {
        1: 31,
        2: null,
        3: 31,
        4: 30,
        5: 31,
        6: 30,
        7: 31,
        8: 31,
        9: 30,
        10: 31,
        11: 30,
        12: 31
    },
    isLeapYear : function(datea)
    {
        datea = parseInt(datea);

        if(datea%4 == 0)
        {
            if(datea%100 != 0)
            {
                return true;
            }
            else
            {
                if(datea%400 == 0)
                    return true;
                else
                    return false;
            }
        }
        return false;
    },
    
    getMonthLength : function(month, year, zeroBased) {
        if (zeroBased) {
            month++;
        }
        var monthLength;
        
        if (month == 2) {
            monthLength = (this.isLeapYear(year)) ? 29 : 28;
        } else {
            monthLength = this.monthLengths[month];
        }
        
        return monthLength;
    }
}

Date.prototype.getMonthLength = function() {
    return HG.util.Date.getMonthLength(this.getMonth(), this.getFullYear(), true);
}


HG.util.Cookie = {
    getValue : function(name, value) {
        var value = null;
        
        if (document.cookie.length > 0)
        {
            var start=document.cookie.indexOf(name + "=");
            var end;
            
            if (start!=-1) {
                start=start + name.length+1;
                end=document.cookie.indexOf(";",start);
                if (end==-1) {
                    end=document.cookie.length;
                }
                value = unescape(document.cookie.substring(start, end));
            }
        }
        
        return value;        
    },
    
    setValue : function(name, value, expireDays) {
        var cookieValue;
        
        cookieValue = name + "=" + escape(value); 
        if (expireDays) {
            var exdate = new Date();
            exdate.setDate(exdate.getDate()+expireDays);
            cookieValue += ";expires="+exdate.toGMTString();
        } 
        
        cookieValue += ';path=/';        
        
        document.cookie = cookieValue;
    }
}