
var BSDClass = {
	create: function() {
    	return function() {
    		if(this.initialize) {
	      		this.initialize.apply(this, arguments);
	      	} else if(this.className) {
				BSDLogUtils.error("Couldn't find initialize function for class " + this.className);		      	
			} else {
				BSDLogUtils.error("Couldn't find initialize function for class " + arguments);		      	
	      	}
    	}
  	}
}
BSDStringUtils = {
	DEPENDENCIES: new Array(),
	VERSION: 1.1,
	
	toCamelCaseRegex: /-([a-z])/,
	
	toCamelCase: function(value) {
		var regex = BSDStringUtils.toCamelCaseRegex;
		for(; regex.test(value); value = value.replace(regex, RegExp.$1.toUpperCase()) );
		return value;
	},
	
	trimRegex: /^\s+|\s+$/g,
	
	trim: function(value) {
		var regex = BSDStringUtils.trimRegex;
		value = value.replace(regex, '');
		return value;
	},
	
	equalsTrimmed: function(value1, value2) {
		if(!value1 && !value2) {
			return true;
		}
		if(!value1) {
			return false;
		}
		if(!value2) {
			return false;
		}
		value1 = BSDStringUtils.trim(value1);
		value2 = BSDStringUtils.trim(value2);
		return value1 == value2;
	},

	startsWith: function(value, starting) {
		if(!value) {
			return false;
		}
		var regex = new RegExp("^" + starting, "g");
		if(regex.exec(value)) {
			return true;
		}
		return false;
	},
		
	endsWith: function(value, ending) {
		if(!value) {
			return false;
		}
		var regex = new RegExp(ending + "$", "g");
		if(regex.exec(value)) {
			return true;
		}
		return false;
	},
	
	stripWhitespace: function(value) {
		return value.replace(/\s/g, '');
	},
	
	truncate: function(value, length) {
		if(value.length > length) {
			value = value.substring(0, length);
		}
		return value;
	},
	
	brToLB: function(value) {
		if(!value) {
			return value;
		}
		value = value.replace("<br/>", "\n");
		value = value.replace("<br>", "\n");
		return value;
	},
	
	lbToBR: function(value) {
		if(!value) {
			return value;
		}
		value = value.replace("\r\n", "<br/>");
		value = value.replace("\r", "<br/>");
		value = value.replace("\n", "<br/>");
		return value;
	}
	
	
}

BSDTypeUtils = {
	DEPENDENCIES: new Array(),
	
	isArray: function(value) {
	    return BSDTypeUtils.isObject(value) && value.constructor == Array;		
	},
	
	isBoolean: function(value) {	
		return typeof value == 'boolean';
	},
		
	isEmpty: function(value) {
	    var i, v;
	    if (isObject(value)) {
	        for (i in value) {
	            v = value[i];
	            if (BSDTypeUtils.isUndefined(v) && BSDTypeUtils.isFunction(v)) {
	                return false;
	            }
	        }
	    }
	    return true;
	}, 
	
	isFunction: function(value) {
	    return typeof value == 'function';	
	},
	
	isNull: function(value) {
		return value == nulll;
	},
	
	isNumber: function(value) {
		return typeof value == 'number'; // && BSDTypeUtils.isFinite(value);
	},
	
	isObject: function(value) {
		return (value && typeof value == 'object');
	},

	isString: function(value) {
		return typeof value == 'string';
	},
	
	isUndefined: function(value) {
		return typeof value == 'undefined';
	}
	
}

var bsdObjectsByClassHash;

BSDDOMUtils = {
	DEPENDENCIES: new Array("BSDStringUtils", "BSDTypeUtils"),
	VERSION: 1.1,

	setElementValue: function(element, value) {
		if(element.innerHTML) {
			element.innerHTML = value;
		} else if(element.nodeType == 1) {
			var children = element.childNodes;
			for(i = 0; children && i < children.length; i++) {
				var currentChild = children[i];
				element.removeChild(currentChild);
			}
			var newTextNode = document.createTextNode(value);
			element.appendChild(newTextNode);
		} else {
			alert("Couldn't set value for node type " + element.nodeType);
		}
	},

	getAttributeValue: function(element, attributeName) {

		if(!element) {
			return;
		}
		if(element.getAttribute) {
			var currentAttribute = element.getAttribute(attributeName);
			if(currentAttribute) {
				return currentAttribute;
			}
		} else if(element.attributes) {
			var currentAttr = element.attributes[attributeName];
			if(currentAttr) {
				return currentAttr.value;
			}
		} else {

		}	



		
	},
	
	setAttributeValue: function(element, attributeName, attributeValue) {
		element.setAttribute(attributeName, attributeValue);
	},

	removeAttribute: function(element, attributeName) {
		element.removeAttribute(attributeName);
	},

	getObjectById: function(id, doc) {
		if(!doc) {
			doc = document;
		}
		
	    if(doc.getElementById) {
	        return doc.getElementById(id);
	    } else if(doc.all) {
	        return doc.all[id];
	    } else if(doc.layers) {
	        return doc.layers[id];
	    }	
	},
	
	getParentObjectByClass: function(element, className) {
		if(BSDDOMUtils.containsClass(element, className)) {
			return element;
		} else if(element.parentNode) {
			return this.getParentObjectByClass(element.parentNode, className);
		} else {

		}		
	},

	getParentObjectById: function(element, objectId) {
		if(element.id == objectId) {
			return element;
		} else if(element.parentNode) {
			return this.getParentObjectById(element.parentNode, objectId);
		} else {

		}		
	},

	getParentObjectByNodeName: function(element, nodeName, includeCurrent) {
		if(includeCurrent && element.nodeName && element.nodeName.toLowerCase() == nodeName.toLowerCase()) {

			return element;
		}
		if(element.parentNode == element || !element.parentNode || !element.parentNode.nodeName) {

			return null;
		}
		if(element.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {

			return element.parentNode;
		} else {

			return BSDDOMUtils.getParentObjectByNodeName(element.parentNode, nodeName);
		}
	},
	
	getObjectByNodeNameFromParent: function(parent, nodeName, includeCurrent) {
		if(includeCurrent && parent.nodeName && parent.nodeName.toLowerCase() == nodeName) {
			return parent;
		}
		for(var i = 0; i < parent.childNodes.length; i++) {
			var currentChild = parent.childNodes[i];
			if(currentChild.nodeName && currentChild.nodeName.toLowerCase() == nodeName) {
				return currentChild;
			}
		}
		for(var i = 0; i < parent.childNodes.length; i++) {
			var result = BSDDOMUtils.getObjectByNodeNameFromParent(parent.childNodes[i], nodeName, true);
			if(result) {
				return result;
			}
		}
		
	},

	getObjectByIdFromParent: function(parent, id, elementClassToIgnore) {
		if(!parent) {
			return;
		}
		var children = parent.childNodes;
		if(!children) {
			return null;
		}
		if(arguments.length > 3) {
			elementClassToIgnore = new Array();
			for(var i = 2; i < arguments.length; i++) {
				BSDArrayUtils.append(elementClassToIgnore, arguments[i]);
			}
		}
		
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(currentChild.id == id && (!elementClassToIgnore || !BSDDOMUtils.containsClass(currentChild, elementClassToIgnore))) {
				return currentChild;
			}
		}
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(!elementClassToIgnore || !BSDDOMUtils.containsClass(currentChild, elementClassToIgnore)) {
				var childValue = BSDDOMUtils.getObjectByIdFromParent(currentChild, id, elementClassToIgnore);
				if(childValue != null) {
					return childValue;
				}
			}
		}
	    return null;
	}, 

	getObjectByIdPrefixFromParent: function(parent, idPrefix, elementClassToIgnore) {
		var children = parent.childNodes;
		if(!children) {
			return null;
		}
		
		if(arguments.length > 3) {
			elementClassToIgnore = new Array();
			for(var i = 2; i < arguments.length; i++) {
				BSDArrayUtils.append(elementClassToIgnore, arguments[i]);
			}
		}
		
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(currentChild.id && currentChild.id.indexOf(idPrefix) == 0 && (!elementClassToIgnore || !BSDDOMUtils.containsClass(currentChild, elementClassToIgnore))) {
				return currentChild;
			}
		}
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(!elementClassToIgnore || !BSDDOMUtils.containsClass(currentChild, elementClassToIgnore)) {
				var childValue = BSDDOMUtils.getObjectByIdPrefixFromParent(currentChild, idPrefix, elementClassToIgnore);
				if(childValue != null) {
					return childValue;
				}
			}
		}
	    return null;
	}, 

	getObjectsByClass: function(className, parentElement, elementArray, elementClassToIgnore) {
		if(parentElement || elementArray || elementClassToIgnore) {
			return BSDDOMUtils.getObjectsByClassInternal(className, parentElement, elementArray, elementClassToIgnore);
		}
		if(document.getElementsByClassName) {
			return document.getElementsByClassName(className);
		}
		if(!bsdObjectsByClassHash) {
			BSDDOMUtils.buildObjectsByClassHash();
		} 
		
		var elementArray = bsdObjectsByClassHash[className];
		if(!elementArray) {

			elementArray = new Array();
		} 
		
		return elementArray;		
	},

	buildObjectsByClassHash: function() {
		bsdObjectsByClassHash = new Array();
		BSDDOMUtils.buildObjectsByClassHashByElement(document, true);
	},

	buildObjectsByClassHashByElement: function(parentElement) {
		if(!parentElement) {
			BSDLogUtils.error("Got null parentElement for buildObjectsByClassHashByElement");
			return;
		}
		if(parentElement.className) {
	        var split = parentElement.className.split(/\s+/);
	        for(var j = 0; j < split.length; j++) {
	        	var currentClassName = split[j];
	        	if(currentClassName.length < 1) {
	        		continue;
	        	}
				var classElements = bsdObjectsByClassHash[currentClassName];
				if(!classElements) {
					classElements = new Array();
					bsdObjectsByClassHash[currentClassName] = classElements;
				}
				classElements[classElements.length] = parentElement;				
			}			
		}
		
		
		var childNodes = parentElement.childNodes;
		for(var i = 0; childNodes && i < childNodes.length; i++) {
			var currentChild = childNodes[i];
			BSDDOMUtils.buildObjectsByClassHashByElement(currentChild);
		} 
		
	},
	
	getObjectsByClassInternal: function(className, parentElement, elementArray, elementClassToIgnore) {
		if(!elementArray) {
			elementArray = new Array();
		}
		if(!className) {
			return elementArray;
		}
		if(!parentElement) {
			parentElement = document;
		}
	    var children = parentElement.childNodes;
	    if(!children) {
	   		return elementArray;
	    }
	    for(var i = 0; i < children.length; i++) {
	        var currentChild = children[i];
		    if(currentChild.nodeType != 1) {
			    continue;
	        }	
	        
	        var split = currentChild.className.split(/\s+/);
	        for(var j = 0; j < split.length; j++) {
	        	var currentClassName = split[j];
	        	if(!currentClassName || currentClassName.length < 1) {
	        		continue;
	        	}

			    if(currentClassName == className) {
			        var index = elementArray.length;
			        elementArray[index] = currentChild;
		        } else if(elementClassToIgnore && currentClassName == elementClassToIgnore) {

		        	continue;
		        }
		    }	        
	        
		    BSDDOMUtils.getObjectsByClass(className, currentChild, 
							elementArray, elementClassToIgnore);
	    }
	    return elementArray;
	},
	
	getObjectsById: function(id, parentElement, elementArray) {
		if(!elementArray) {
			elementArray = new Array();
		}
		if(!id) {
			return elementArray;
		}
		if(!parentElement) {
			parentElement = document;
		}
	    var children = parentElement.childNodes;
	    if(!children) {
	   		return elementArray;
	    }
	    for(var i = 0; i < children.length; i++) {
	        var currentChild = children[i];
		    if(currentChild.nodeType != 1) {
			    continue;
	        }	
		    if(currentChild.id == id) {
		        var index = elementArray.length;
		        elementArray[index] = currentChild;
	        }
		    BSDDOMUtils.getObjectsById(id, currentChild, 
							elementArray);
	    }
	    return elementArray;
	},

	getObjectsByNodeName: function(parentElement, nodeName, elementArray) {
		if(!elementArray) {
			elementArray = new Array();
		}
		if(!nodeName) {
			return elementArray;
		}
		if(!parentElement) {
			BSDLogUtils.error("ERROR: Got null parentElement for getObjectsByNodeName()");
			return;
		}
	    var children = parentElement.childNodes;
	    if(!children) {
	   		return null;
	    }
	    for(var i = 0; i < children.length; i++) {
	        var currentChild = children[i];
		    if(currentChild.nodeType != 1) {
			    continue;
	        }	
		    if(currentChild.nodeName == nodeName) {
		        var index = elementArray.length;
		        elementArray[index] = currentChild;
	        }
		    BSDDOMUtils.getObjectsByNodeName(currentChild, nodeName, 
							elementArray);
	    }
	    return elementArray;
	},

	getRootElement: function() {
		if(document.documentElement) {
			return document.documentElement;
		}
		return null;
	},
	
	getNextElementSibling: function(element) {
		var sibling = element.nextSibling;
		while(sibling && sibling.nodeType != 1) {
			sibling = sibling.nextSibling;
		}
		return sibling;
	},

	getPreviousElementSibling: function(element) {
		var sibling = element.previousSibling;
		while(sibling && sibling.nodeType != 1) {
			sibling = sibling.previousSibling;
		}
		return sibling;
	},
	
	getElementStyle: function(element, styleName) {
		if(!element.style) {

			return;
		}
		var ieStyleName = BSDStringUtils.toCamelCase(styleName);
		var styleValue = element.style[ieStyleName];
	    if(!styleValue) {
			if(document.defaultView && document.defaultView.getComputedStyle) {
	        	var cssStyleValue = document.defaultView.getComputedStyle(element, "");
	        	if(!cssStyleValue) {
	        		return null;
	        	}
	        	styleValue = cssStyleValue.getPropertyValue(styleName);

	      	} else if(element.currentStyle) {
	        	styleValue = element.currentStyle[ieStyleName];
	      	}
	  	}

		if(styleValue == 'auto') {
			return null;
		}
	  	return styleValue;
	},
	
 
	elementContainsStyle: function(element, stylePropertyName, stylePropertyValue) {
	    stylePropertyValue = stylePropertyValue.toLowerCase();
	    if(element.style && element.style[stylePropertyName] &&
						element.style[stylePropertyName].toLowerCase() == stylePropertyValue) {
			return true;
	    }
	    return false;
	},

	setElementStyle: function(element, stylePropertyName, stylePropertyValue) {
		BSDDOMUtils.changeElementStyle(element, stylePropertyName, stylePropertyValue);
	},
		
	changeElementStyle: function(element, stylePropertyName, stylePropertyValue) {
	    var elementStyle = element.style;
	    if(elementStyle) {
	    	try {
			    elementStyle[stylePropertyName] = stylePropertyValue;
			} catch (err) {  


			}
		}
		if(stylePropertyName == 'background-color') {
			element.style.backgroundColor = stylePropertyValue;
		} else if(stylePropertyName == 'font-family') {
			element.style.fontFamily = stylePropertyValue;
		} else if(stylePropertyName == 'text-align') {
			element.style.textAlign = stylePropertyValue;
		}
	},
	
	cloneElementStyle: function(source, target, stylePropertyName) {
		if(stylePropertyName) {
			var value = BSDDOMUtils.getElementStyle(source, stylePropertyName);
			if(value) {

				BSDDOMUtils.changeElementStyle(target, stylePropertyName, value);
			}
			return value;
		}
	},
	
	cloneAllElementStyles: function(source, target) {
		for(var styleName in source.style) {
			if(styleName) {
				BSDDOMUtils.cloneElementStyle(source, target, styleName);
			}
		}
	
	},
	
	getElementMargin: function(source, tryChildren) {
		var margin = BSDDOMUtils.getElementStyle(source, 'margin');
		var iTop = 0;
		var iRight = 0;
		var iLeft = 0;
		var iBottom = 0;
		if(margin) {
			var split = margin.split(/\s*px\s*/i);
			if(split.length < 1 && margin.length > 0) {
				iTop = parseInt(margin);
			}
			if(split.length > 0) {
				iTop = parseInt(split[0]);
			}
			if(split.length > 1) {
				iRight = parseInt(split[1]);
			}
			if(split.length > 2) {
				iBottom = parseInt(split[2]);
			}
			if(split.length > 3) {
				iLeft = parseInt(split[3]);
			}			
		}
		
		var marginTop = BSDDOMUtils.getElementStyle(source, 'margin-top');
		if(marginTop && marginTop.length > 2) {
			iTop = marginTop.replace(/\s*px\s*/i, '');
		}
		var marginRight = BSDDOMUtils.getElementStyle(source, 'margin-right');
		if(marginRight && marginRight.length > 2) {
			iRight = marginRight.replace(/\s*px\s*/i, '');
		}
		var marginBottom = BSDDOMUtils.getElementStyle(source, 'margin-bottom');
		if(marginBottom && marginBottom.length > 2) {
			iBottom = marginBottom.replace(/\s*px\s*/i, '');
		}
		var marginLeft = BSDDOMUtils.getElementStyle(source, 'margin-left');
		if(marginLeft && marginLeft.length > 2) {
			iLeft = marginLeft.replace(/\s*px\s*/i, '');
		}

		if(tryChildren && iTop == 0 && iRight == 0 && iBottom == 0 && iLeft == 0) {

			var marginChild = null;
			for(var i = 0; i < source.childNodes.length; i++) {
				var currentChild = source.childNodes[i];
				if(currentChild.nodeType == 1 && !BSDVisibilityUtils.isObjectHidden(currentChild)) {
					if(!marginChild) {
						marginChild = currentChild;
					} else {
						marginChild = null;
						break;
					}
				}
				if(marginChild) { 
					return BSDDOMUtils.getElementMargin(marginChild, false);
				}
			}		
		}
		
		var margin = new Object();
		
		margin.margin = iTop + 'px ' + iRight + 'px ' + iBottom + 'px ' + iLeft + 'px';
		margin.top = iTop;
		margin.right = iRight;
		margin.bottom = iBottom;
		margin.left = iLeft;


		return margin;
	},

	
	getElementWidth: function(element) {
		var iWidth = element.offsetWidth;
		if(iWidth && iWidth > 0) {
			return iWidth;
		}
		var width = BSDDOMUtils.getElementStyle(element, 'width');
		if(width && width.length > 0) {
			width = width.replace(/\s*px\s*/i, '');
			return parseInt(width);
		}
		
		width = BSDDOMUtils.getAttributeValue(element, 'width');		
		if(width && width.length > 0) {
			return parseInt(width);
		}
		return 0;
	},
	
	getElementHeight: function(element) {
		var iHeight = element.offsetHeight;
		if(iHeight && iHeight > 0) {
			return iHeight;
		}
		var height = BSDDOMUtils.getElementStyle(element, 'height');
		if(height && height.length > 0) {
			height = height.replace(/\s*px\s*/i, '');
			return parseInt(height);
		}
		height = BSDDOMUtils.getAttributeValue(element, 'height');
		if(height && height.length > 0) {
			return parseInt(height);
		}
		return 0;
	},
	
	cloneElement: function(sourceElement, doShallowClone) {
		var deep = true;
		if(doShallowClone) {
			deep = false;
		}
		return sourceElement.cloneNode(deep);
	},
    

	cloneElementDimensions: function(source, target, deltaWidth, deltaHeight) {
	    var newWidth = source.offsetWidth;
	    var newHeight = source.offsetHeight;
	    if(deltaWidth) {
			newWidth += deltaWidth;
	    }
	    if(deltaHeight) {
			newHeight += deltaHeight;
	    }
	    BSDDOMUtils.changeElementStyle(target, 'width', newWidth);
	    BSDDOMUtils.changeElementStyle(target, 'height', newHeight);
	},

	cloneDimensions: function(sourceDimensions, target) { 
	    BSDDOMUtils.changeElementStyle(target, 'width', sourceDimensions.width);
	    BSDDOMUtils.changeElementStyle(target, 'height', sourceDimensions.height);	
	},
	
	cloneElementMargins: function(source, target, tryChildren) {
		var margin = BSDDOMUtils.getElementMargin(source, tryChildren);
		BSDDOMUtils.changeElementStyle(target, 'margin', margin.margin);

		return margin.margin != '0px 0px 0px 0px';		
	},

	createElement: function(nodeName, parent, id, className) {
		var element = document.createElement(nodeName);	
		if(parent) {
			parent.appendChild(element);
		}
		if(id) {
			element.id = id;
		}
		if(className) {
			element.className = className;
		}
		return element;
	},
	
	removeElement: function(element) {
		var parent = element.parentNode;
		if(!parent) {
			return;
		}
		if(element.nodeName == 'TR') {
			while(parent && parent.nodeName != 'TABLE') {
				parent = parent.parentNode;
			}
			if(parent) {
				parent.deleteRow(element.rowIndex);
			} else {
				BSDLogUtils.error("ERROR: Couldn't find table parent for row to remove");
			}
		} else {
			parent.removeChild(element);
		}
	},
	
	getPreviousSiblingElement: function(element) {
		var sibling = element.previousSibling;
		while(sibling && sibling.nodeType != 1) {
			sibling = sibling.previousSibling;
		}
		return sibling;
	},
	
	getNextSiblingElement: function(element) {
		var sibling = element.nextSibling;
		while(sibling && sibling.nodeType != 1) {
			sibling = sibling.nextSibling;
		}
		return sibling;
	},
	
	setCursor: function(element, cursorName) {
		BSDDOMUtils.changeElementStyle(element, 'cursor', cursorName);
	},

	setMoveCursor: function(element) {
		BSDDOMUtils.setCursor(element, 'move');
	},
	
	setDefaultCursor: function(element) {
		BSDDOMUtils.setCursor(element, 'default');
	},
	
	setClass: function(element, className) {
		element.className = className;
	},
	
	addClass: function(element, className, prepend) {
		if(element.className) {
			if(prepend) {
				element.className = className + " " + element.className;
			} else {
				element.className += " " + className;
			}
		} else {
			element.className = className;
		}
	},
	
	removeClass: function(element, className) {
		if(!element.className || element.className.length < 1) {
			return;
		}
		var newClassName = "";
		var split = element.className.split(/\s+/);
	    for(var i = 0; i < split.length; i++) {
	        var currentClassName = split[i];
	        if(!currentClassName || currentClassName.length < 1) {
	        	continue;
	        }

			if(currentClassName != className) {
				newClassName += currentClassName;
				if(i < split.length -1) {
					newClassName += " ";
				}
			}
		}
		element.className = newClassName;
	},
	
	containsClass: function(element, className) {
		if(!element.className || !className) {
			return false;
		}
		var multipleClasses = BSDTypeUtils.isArray(className);
        var split = element.className.split(/\s+/);
        for(var j = 0; j < split.length; j++) {
        	var currentClassName = split[j];
        	if(!currentClassName || currentClassName.length < 1) {
        		continue;
        	}

        	if(multipleClasses && BSDArrayUtils.contains(className, currentClassName)) {
        		return true;
		    } else if(currentClassName == className) {
		    	return true;
	        } 
	    }		
	    return false;
	},
	
	addChild: function(element, child) {
		element.appendChild(child);
	},
	
	moveElement: function(element, newParent) {
		BSDDOMUtils.removeElement(element);
		BSDDOMUtils.addChild(newParent, element);
	},
	
	replaceElement: function(oldElement, newElement) {
		oldElement.parentNode.replaceChild(newElement, oldElement);
	},
	
	replaceElementByIdAndHtml: function(oldElementId, newElementHtml) {
		var oldElement = BSDDOMUtils.getObjectById(oldElementId);
		if(!oldElement) {
			BSDLogUtils.warning("Couldn't find element to replace with id: " + oldElementId);
			return;
		}
		newElementHtml = newElementHtml.replace(/scripx/g, 'script');
		alert(newElementHtml);
		oldElement.innerHTML = newElementHtml;
		if(oldElement.childNodes.length == 1) {
			BSDDOMUtils.replaceElement(oldElement, oldElement.childNodes[0]);
		}
		
	},
	
	replaceElementByParentId: function(parentElementId) {
		var parentElement = BSDDOMUtils.getObjectById(parentElementId);
		if(!parentElement) {
			BSDLogUtils.warning("Couldn't find parent element to replace with id: " + parentElementId);
			return;
		}

		var elementsToMove = new Array();
		for(var i = 0; i < parentElement.childNodes.length; i++) {

			var currentElement = parentElement.childNodes[i];

			if(currentElement.nodeType != 1) {
				continue;
			}
			var targetId = BSDDOMUtils.getAttributeValue(currentElement, 'rid');
			if(!targetId) {
				BSDLogUtils.warning("Couldn't find target id for element to replace: " + currentElement.id);
				continue;
			}
			var target = BSDDOMUtils.getObjectById(targetId);
			if(!target) {
				BSDLogUtils.warning("Couldn't find target element to replace: " + targetId);
				continue;
			}
			
			var currentHolder = new Object();
			currentHolder.source = currentElement;
			currentHolder.target = target;
			elementsToMove[elementsToMove.length] = currentHolder;
		}
		for(var i = 0; i < elementsToMove.length; i++) {
			var currentHolder = elementsToMove[i];
			BSDDOMUtils.replaceElement(currentHolder.target, currentHolder.source);

		}
	

	},
	
	addText: function(element, text) {
		var textNode = document.createTextNode(text);
		element.appendChild(textNode);
	},
	
	setText: function(element, text) {
		if(!element || !element.childNodes) {
			BSDLogUtils.error("Cannot set text on null element");
			return;
		}
		if(element.nodeName && element.nodeName.toLowerCase() == 'input') {
			element.value = text;
		} else if(element.nodeName && element.nodeName.toLowerCase() == 'select') {
			if(!text || text.length < 1) {
				if(element.options && element.options.length > 0) {
					element.options[0].selected = true;
					return;
				}
			}
			for(var i = 0; i < element.childNodes.length; i++) {
				var currentChild = element.childNodes[i];
				var value = currentChild.value;
				if(value && value == text) {
					currentChild.selected = true;
					break;
				} else if(!value && BSDDOMUtils.getText(element) == text) {
					currentChild.selected = true;
					break;
				}
			}
		} else {			
			for(var i = 0; i < element.childNodes.length; i++) {
				if(element.childNodes[i].nodeType == 3) {
					element.removeChild(element.childNodes[i]);
					i--;
				} 
			}
			BSDDOMUtils.addText(element, text);
		}
		
	},
	
	setTextById: function(elementId, text, parentElement) {
		var element;
		if(parentElement) {
			element = BSDDOMUtils.getObjectByIdFromParent(parentElement, elementId);
		} else {
			element = BSDDOMUtils.getObjectById(elementId);
		}
		if(!element) {
			return;
		}
		BSDDOMUtils.setText(element, text);
	},
	
	getText: function(element) {
		var text = "";
		if(!element) {
			return text;
		}
		if(element.nodeName && element.nodeName.toLowerCase() == 'input') {
			return element.value;
		}
		
		if(!element.childNodes) {
			return text;
		}
		for(var i = 0; i < element.childNodes.length; i++) {
			if(element.childNodes[i].nodeType == 3) {
				text += element.childNodes[i].nodeValue;
			}		
		}
		return text;
	},
	
	getTextById: function(elementId) {
		var element = BSDDOMUtils.getObjectById(elementId);
		if(!element) {
			return;
		}
		return BSDDOMUtils.getText(element);	
	},
	
	appendElementToRoot: function(element) {
		if(document.body) {
			document.body.appendChild(element);
		} else {
			for(var i = 0; i < document.childNodes.length; i++) {
				document.childNodes[i].appendChild(element);
			}
		}
	},
	
	clear: function(element) {
		if(element.nodeName.toLowerCase() == 'table' && element.tBodies && element.tBodies.length > 0) {
			for(var i = 0; i < element.tBodies.length; i++) {

				BSDDOMUtils.clear(element.tBodies[i]);
			}
		} else {
		    while(element.childNodes.length > 0) {

				element.removeChild(element.childNodes[0]);
			}
		}	
	},
	
	getContainsChildElements: function(element, exceptionClass) {
		if(!element || !element.childNodes) {
			return false;
		}
		for(var i = 0; i < element.childNodes.length; i++) {
			var currentChild = element.childNodes[i];
			if(exceptionClass && BSDDOMUtils.containsClass(currentChild, exceptionClass)) {
				continue;
			}
			if(currentChild.nodeType == 1) {
				return true;
			}
		}
		return false;
	},
	
	insertAfter: function(existingElement, newElement) {
		var parentNode = existingElement.parentNode;
		if(!parentNode) {
			return false;
		}
		if(existingElement.nextSibling) {
			parentNode.insertBefore(newElement, existingElement.nextSibling);
		} else {
			parentNode.appendChild(newElement);
		}
		
		return true;
	},
	
	insertBefore: function(existingElement, newElement) {
		var parentNode = existingElement.parentNode;
		if(!parentNode) {
			return false;
		}

		if(existingElement.nodeName.toUpperCase() == 'TR' && !newElement.nodeName.toUpperCase() == 'TR') {
			var row = document.createElement('tr');
			if(!newElement.nodeName.toUpperCase() == 'TD') {
				var column = document.createElement('td');
				column.appendChild(newElement);
				row.appendChild(column);
			} else {
				row.appendChild(newElement);
			}
			newElement = row;
		} 

		parentNode.insertBefore(newElement, existingElement);
		
		return true;
	},
	
	insertChild: function(parentElement, newElement, index) {
		if(!parentElement) {
			BSDLogUtils.error("Got null parentElement for insertChild");
			return;
		}
		var childNodes = parentElement.childNodes;
		if(index && childNodes.length > index) {
			BSDDOMUtils.insertBefore(childNodes[index], newElement);
		} else {
			BSDDOMUtils.addChild(parentElement, newElement);
		}
	},
	
	getElementParentIndex: function(element) {
		var parentNode = element.parentNode;
		for(var i = 0; i < parentNode.childNodes.length; i++){
			if(parentNode.childNodes[i] == element) {
				return i;
			}
		}
	},
	
	appendAsRow: function(table, rowContents) {
		var row = document.createElement('tr');
		var column = document.createElement('td');
		
		column.innerHTML = rowContents;
		table.tBodies[0].appendChild(row);
		row.appendChild(column);
	},
	
	setInnerHTML: function(element, content) {
  		if(element.nodeName.toUpperCase() == 'TABLE') {
  			BSDDOMUtils.clear(element);
  			if(BSDStringUtils.startsWith(content, '<tr') && element.tBodies && element.tBodies.length > 0) {
  				element.tBodies[0].innerHTML = content;
  			} else {
				BSDDOMUtils.appendAsRow(element, content);
			}
  		} else {
	  		element.innerHTML = content;
	  	}
	
	},
	
	getFrameDocument: function(frame) {
		if(frame.contentDocument) {
			return frame.contentDocument;
		} else {
			return frame.document; //ie
		}
	}
	

}
BSDArrayUtils = {
	DEPENDENCIES: new Array("BSDTypeUtils"),
	
	insert: function(array, value, index) {
		if(array.splice && BSDTypeUtils.isArray(value)) {
			for(var i = 0; i < value.length; i++) {
				array.splice(index + i, 0, value[i]);
			}		
		} else if(array.splice) {
			array.splice(index, 0, value);
		} else if(BSDTypeUtils.isArray(value)) {
			for(var i = array.length - 1 + value.length; i > index; i--) {
				array[i] = array[i-1];			
			}
			for(var i = 0; i < value.length; i++) {
				array[index + i] = value[i];
			}		
		} else {
			for(var i = array.length; i > index; i--) {
				array[i] = array[i-1];			
			}
			array[index] = value;
		}
	},
	
	append: function(array, value) {
		if(array.push && !BSDTypeUtils.isArray(value)) {
			array.push(value);
		} else if(BSDTypeUtils.isArray(value)) {
			var j = 0;
			var newLength = array.length + value.length;
			for(var i = array.length; i < newLength; i++) {
				array[i] = value[j];
				j++
			}
		} else {	
			array[array.length] = value;
		}
	},
	
	deleteElement: function(array, index, count) {
		if(!count) {
			count = 1;
		}
		if(array.splice) {
			array.splice(index, count);
			return array;
		} else {
			var newArray = new Array();
			for(var i = 0; i < array.length; i++) {
				if(i < index && i >= index + count) {
					BSDArrayUtils.append(newArray, array[i]);
				}
			}
			return newArray;
		}
	}, 
	
	replace: function(array, index, value) {
		array[index] = value;
	},
	
	copy: function(sourceArray, targetArray) {
		var j = targetArray.length;
		for(var i = 0; i < sourceArray.length; i++) {
			targetArray[j + i] = sourceArray[i];
		}
	},
	
	toCommaDelimitedString: function(sourceArray) {
		var value = "";
		for(var i = 0; i < sourceArray.length; i++) {
			value += sourceArray[i];
			if(i < sourceArray.length - 1) {
				value += ",";
			}
		}	
		return value;
	},
	
	insertUnique: function(array, value, index) {
		for(var i = 0; i < array.length; i++) {
			if(array[i] == value) {
				BSDArrayUtils.deleteElement(array, i);
				break;
			}
		}
		BSDArrayUtils.insert(array, value, index);
	},
	
	contains: function(array, value) {
		for(var i = 0; i < array.length; i++) {
			if(array[i] == value) {
				return true;
			}
		}
		return false;
	}
}


BSDInteractiveCalendar = BSDClass.create();
BSDInteractiveCalendar.DEPENDENCIES = new Array("BSDClass", "BSDDOMUtils", "BSDArrayUtils");
BSDInteractiveCalendar.prototype = {
	
	className: "BSDInteractiveCalendar",
	initialize: function(elementId) {
	    this.elementId = elementId;
	    
	    this.initializeCalendar();
   	},

	initializeCalendar: function() {
		this.element = BSDDOMUtils.getObjectById(this.elementId);
		if(!this.element) {
			BSDLogUtils.error("Couldn't find calendar element with id " + elementId);
			return;
		}

		this.curDateElement = BSDDOMUtils.getObjectByIdFromParent(this.element, 'CALENDAR_CUR_DATE');
		if(!this.curDateElement) {
			BSDLogUtils.error("Couldn't load current date element for calendar: " + this.elementId);
			return;
		}
		
		this.previousLink = BSDDOMUtils.getObjectByIdFromParent(this.element, 'CALENDAR_LINK_PREVIOUS');
		this.nextLink = BSDDOMUtils.getObjectByIdFromParent(this.element, 'CALENDAR_LINK_NEXT');
		this.monthLabel = BSDDOMUtils.getObjectByIdFromParent(this.element, 'CALENDAR_LABEL');

		if(!this.previousLink || !this.nextLink) {
			BSDLogUtils.error("Couldn't find calendar previous or next links with id " + elementId);
			return;
		}		

		var controller = this;
		function previousClickHandler(e) {
			BSDEventUtils.stopPropagation(e);
			controller.doSwitch(-1);
			return false;			
		}  		
		function nextClickHandler(e) {

			BSDEventUtils.stopPropagation(e);
			controller.doSwitch(1);
			return false;			
		}  		
		
		
		BSDEventUtils.registerEvent(this.previousLink, "click", previousClickHandler);
		BSDEventUtils.registerEvent(this.nextLink, "click", nextClickHandler);
		BSDLogUtils.debug("Initialized calendar: " + this.curDateElement + " " + this.elementId);
	},

	doSwitch: function(increment) {
		var date = this.getCurDate();
		if(!date) {
			return;
		}
		var newDate = new Date();
		newDate.setTime(date.getTime());
		if(increment > 0 && newDate.getMonth() < 11) {
			newDate.setMonth(date.getMonth() + increment);
		} else if(increment > 0) {
			newDate.setMonth(0);
		} else if(increment < 0 && newDate.getMonth() > 0) {
			newDate.setMonth(date.getMonth() + increment);
		} else if(increment < 0) {
			newDate.setMonth(11);
		}
		newDate.setDate(1);
		
		var beginDay = newDate.getDay();
				
		alert("Got date: " + date);
		var weekIndex = 0;
		var oldWeeks = BSDDOMUtils.getObjectsByClass('calendarweek', this.element);
		if(oldWeeks.length < 1) {
			BSDLogUtils.error("Couldn't find calendarweek rows in calendar " + this.elementId);
			return;
		}

		var weekIndex = oldWeeks.length/2; //want to get the middle week
		if(weekIndex >= oldWeeks.length) {
			weekIndex = oldWeeks.length - 1;
		}
		var oldWeek = oldWeeks[weekIndex];
		var dayElements = BSDDOMUtils.getObjectsByClass('calendarday', oldWeek);
		var modelDay = null;
		if(dayElements.length > 0) {
			modelDay = dayElements[0];
		}		
		
		if(!modelDay) {
			BSDLogUtils.error("Couldn't find calendarday element in calendar " + this.elementId);
			return;		
		}

		var currentDate = getWeekBeginDate(newDate, beginDay);
		var lastWeek = null;
		for(var i = 0; i < oldWeeks.length; i++) {
			var currentWeek = oldWeeks[i];
			var newWeek = BSDDOMUtils.clone(currentWeek);
			currentDate = buildWeek(currentDate, modelDay, weekElement);
			BSDDOMUtils.replaceElement(currentWeek, newWeek);
		}
		
		while(currentDate.getMonth() == newDate.getMonth()) {
			var newWeek = BSDDOMUtils.clone(oldWeek);
			currentDate = buildWeek(currentDate, modelDay, weekElement);
			BSDDOMUtils.addChild(oldWeek.parentNode, newWeek);
		}
						
			
	},
	
	buildWeek: function(currentDate, modelDay, weekElement) {
		for(var i = 0; i < 7; i++) {
			var newDay = BSDDOMUtils.cloneElement(modelDay);
			var label = BSDDOMUtils.getObjectByIdFromParent(newDay, 'calendardaylabel');
			if(!label) {
				label = newDay;
			}
			var dayOfMonth = currentDate.getDate();
			BSDDOMUtils.setText(label, dayOfMonth);
			
			BSDDOMUtils.addChild(weekElement, newDay);
			
			var curTime = currentDate.getTime();
			curTime += 1000*60*60*24;
			currentDate = new Date();
			currentDate.setTime(curTime);
		}
		return currentDate;
	},
	
	getWeekBeginDate: function(date, monthBeginDay) {
		var beginDate = date;
		while(monthBeginDay > 0) {
			var previousTime = date.getTime() - 1000*60*60*24;
			var previousDate = new Date();
			previousDate.setTime(previousTime);
			monthBeginDay = previousDate.getDay();
			beginDate = previousDate;
		}
		return beginDate;
	},
	
	getCurDate: function() {
		var strDate = BSDDOMUtils.getText(this.curDateElement);
		BSDLogUtils.debug("Got date for calendar: " + strDate);
		var regex = new RegExp("([0-9]+)\-([0-9]+)\-([0-9]{4})");
		var result = regex.exec(strDate);
		if(!result || result.length < 4) {
			BSDLogUtils.error("Got invalid date for calendar " + this.elementId + ": " + strDate);
			return;
		}
		var month = result[1];
		var day = result[2];
		var year = result[3];
		var date = new Date();
		date.setMonth(month - 1);
		date.setDate(day);
		date.setFullYear(year);
		return date;
	},
   	
    toString: function() {
		var str = "[" + this.elementId + "]";
		return str;
    },
    
    clone: function() {
    	var clone = new BSDInteractiveCalendar(this.ccid, this.ccdid, this.isChild, this.templateElementId, this.relationshipTargetTypeId, this.relationshipTargetId);
    	return clone;
    }
    
}
BSDVisibilityUtils = {
	DEPENDENCIES: new Array("BSDDOMUtils"),
	VERSION: 1.0,
		
	switchById: function(current,next) {
	    var currentObj = BSDDOMUtils.getObjectById(current);
	    var nextObj = BSDDOMUtils.getObjectById(next);
	    if(!currentObj || ! nextObj) {
	    		return;
	    }
	    var nextObjDisplay = nextObj.style.display;
	    var nextObjVisibility = nextObj.style.visibility;
	    nextObj.style.display = currentObj.style.display;
	    nextObj.style.visibility = currentObj.style.visibility;
	    currentObj.style.display = nextObjDisplay;
	    currentObj.style.visibility = nextObjVisibility;
	},
	
	showByClass: function(className) {
		var objects = BSDDOMUtils.getObjectsByClass(className);
		for(var i = 0; i < objects.length; i++) {
			BSDVisibilityUtils.showObject(objects[i]);
		}
	},
	
	showByClassAndParentId: function(className, parentId) {
		var parent = BSDDOMUtils.getObjectById(parentId);	
		BSDVisibilityUtils.showByClassAndParent(className, parent);
	},
	
	showByClassAndParent: function(className, parent) {
		var objects = BSDDOMUtils.getObjectsByClass(className, parent);
		for(var i = 0; i < objects.length; i++) {
			BSDVisibilityUtils.showObject(objects[i]);
		}
	},
	
	hideByClass: function(className) {
		var objects = BSDDOMUtils.getObjectsByClass(className);
		for(var i = 0; i < objects.length; i++) {
			BSDVisibilityUtils.hideObject(objects[i]);
		}
	},
	
	hideByClassAndParentId: function(className, parentId) {
		var parent = BSDDOMUtils.getObjectById(parentId);
		BSDVisibilityUtils.hideByClassAndParent(className, parent);
	},
	
	hideByClassAndParent: function(className, parent) {
		var objects = BSDDOMUtils.getObjectsByClass(className, parent);
		for(var i = 0; i < objects.length; i++) {
			BSDVisibilityUtils.hideObject(objects[i]);
		}
	},
	
	showById: function(objectName) {
	    var object = BSDDOMUtils.getObjectById(objectName);
	    BSDVisibilityUtils.showObject(object);
	    return object;
	},
	
	showObject: function(object) {
		if(!object) {
			return;
		}
		object.style.display = "";
		object.style.visibility = "visible";
	},
	
	hideById: function(objectName) {
	    var object = BSDDOMUtils.getObjectById(objectName);
		BSDVisibilityUtils.hideObject(object);
		return object;
	},
	
	hideObject: function(object) {
		if(!object) {
			return;
		}
	    object.style.display = "none";
	    if((object.nodeName == 'TR' || object.nodeName == 'TD') && object.visibility) {
	    	object.style.visibility = "collapse";
	    } else if(object.visibility) {
		    object.style.visibility = "hidden";
	    }
	},
	
	showByObject: function(currentObj, nextObj) {
	    BSDVisibilityUtils.showObject(nextObj);
	    BSDVisibilityUtils.hideObject(currentObj);
	},
	
	isObjectHidden: function(object) {
	    if(object.style && object.style.display && object.style.display.toLowerCase() == 'none') {
			return true;
	    }
	    return false;
	},
	
	toggleObject: function(object) {
		if(BSDVisibilityUtils.isObjectHidden(object)) {
			BSDVisibilityUtils.showObject(object);
		} else {
			BSDVisibilityUtils.hideObject(object);		
		}
	},
	
	switchByNameAndJustify: function(switchObjectName, justifyObjectName) {
	    var switchObj = BSDDOMUtils.getObjectById(switchObjectName);
	    var justifyObj = BSDDOMUtils.getObjectById(justifyObjectName);
	
	    var existingHeight = 0;
	    if(!BSDVisibilityUtils.isObjectHidden(justifyObj)) {
			existingHeight = parseInt(justifyObj.style.height);
	    }
	    if(BSDVisibilityUtils.isObjectHidden(switchObj)) {
			showObject(switchObj);
			if(existingHeight > 0) { 
		    		switchObj.style.height = (existingHeight/2) + "%";
		    		justifyObj.style.height = (existingHeight/2) + "%";
			} 
	    } else {
	 		BSDVisibilityUtils.hideObject(switchObj);
			if(existingHeight > 0) { 
		    		switchObj.style.height = '0%';
		    		justifyObj.style.height = (existingHeight*2) + "%";
			} 
	    }
	},
	
	showIfSelected: function(object, searchValue, objectIdToShow) {

		if(object.value && object.value == searchValue) {
	       	BSDVisibilityUtils.showById(objectIdToShow);
	  	} else {
	       	BSDVisibilityUtils.hideById(objectIdToShow);
	   	}
	},

	showIfSelectedById: function(objectId, searchValue, objectIdToShow) {
		var object = BSDDOMUtils.getObjectById(objectId);
		BSDVisibilityUtils.showIfSelected(object, searchValue, objectIdToShow);
	}

}


BSDLogUtils = {
	DEPENDENCIES: new Array("BSDDOMUtils", "BSDClass"),
	VERSION: 1.1,

	isLogWindowEnabled: false,
	
	debugEnabled: true,
	warningEnabled: true,
	errorEnabled: true,
	
	logStatements: new Array(),

	showLogWindow: function() {
		var logElement = BSDLogUtils.logElement;
		if(!logElement) {
			logElement = BSDDOMUtils.getObjectById("BSDLogWindow");
		}

		if(!logElement) {
			logElement = BSDDOMUtils.createElement("div");
			logElement.id = "BSDLogWindow";
			BSDDOMUtils.changeElementStyle(logElement, 'position', 'absolute');			
			BSDDOMUtils.changeElementStyle(logElement, 'text-align', 'left');	
			BSDLogUtils.logElement = logElement;
			document.body.appendChild(logElement);

			BSDLogUtils.showLogStatements();
		}
		BSDDOMUtils.changeElementStyle(logElement, "top", 0); // + currentScrollPosition.y);
		BSDDOMUtils.changeElementStyle(logElement, "left", 450); // + currentScrollPosition.x);
				
	},

	showLogStatements: function() {
		var logElement = BSDLogUtils.logElement;
		for(var i = 0; i < BSDLogUtils.logStatements.length; i++) {
			var currentStatement = BSDLogUtils.logStatements[i];
			if(currentStatement.isError && !BSDLogUtils.errorEnabled) {
				continue;
			} else if(currentStatement.isWarning && !BSDLogUtils.warningEnabled) {
				continue;
			} else if(currentStatement.isDebug && !BSDLogUtils.debugEnabled) {
				continue;
			}
			
			BSDLogUtils.displayLogStatement(currentStatement);
		}
	},
	
	displayLogStatement: function(statement) {
		var logElement = BSDLogUtils.logElement;

		var statementElement = BSDDOMUtils.createElement("div", logElement, null, "BSDLogStatement");			
		statementElement.statementId = statement.id;

		var statementDateElement = BSDDOMUtils.createElement("span", statementElement, null, "BSDLogStatementDate");
		statementDateElement.innerHTML = statement.date.getHours() + ":" + statement.date.getMinutes() + ":" + statement.date.getSeconds();

		var statementTypeElement = BSDDOMUtils.createElement("span", statementElement, null, "BSDLogStatementType");
		statementTypeElement.innerHTML = statement.type;
		
		var statementMsgElement = BSDDOMUtils.createElement("span", statementElement, null, "BSDLogStatementMessage");
		statementMsgElement.innerHTML = statement.message;

	},		
	
	error: function(message) {
		var newStatement = new BSDLogStatement(BSDLogUtils.logStatements.length, "ERROR", message);
		BSDArrayUtils.append(BSDLogUtils.logStatements, newStatement);
		if(BSDLogUtils.errorEnabled) {
			BSDLogUtils.displayLogStatement(newStatement);		
		}
	},
	
	warning: function(message) {
		var newStatement = new BSDLogStatement(BSDLogUtils.logStatements.length, "WARNING", message);
		BSDArrayUtils.append(BSDLogUtils.logStatements, newStatement);
		if(BSDLogUtils.warningEnabled) {
			BSDLogUtils.displayLogStatement(newStatement);		
		}
	},
	
	debug: function(message) {
		var newStatement = new BSDLogStatement(BSDLogUtils.logStatements.length, "DEBUG", message);
		BSDArrayUtils.append(BSDLogUtils.logStatements, newStatement);
		if(BSDLogUtils.debugEnabled) {
			BSDLogUtils.displayLogStatement(newStatement);		
		}
	},
	
	registerEvent: function(element, type, func) {
	    if(element.addEventListener) {
			element.addEventListener(type, func, true);
	    } else if(element.attachEvent) {
			element.attachEvent('on' + type, func);
	    } else {
	    	alert("ERROR: Couldn't register event: " + type + " " + func);
	    }

	},
	
	recordImageTime: function(src) {
		var image = new Image();
		image.src = src;
		var breakBlock = BSDDOMUtils.getObjectById('kcmBreakBlock');
		if(!breakBlock) {
			breakBlock = document.body;
		}
		BSDDOMUtils.insertChild(breakBlock, image, 0);

	}
	
}

if(BSDLogUtils.isLogWindowEnabled) {
	BSDLogUtils.registerEvent(window, "load", BSDLogUtils.showLogWindow);
}


BSDLogStatement = BSDClass.create();
BSDLogStatement.prototype = {

	className: "BSDLogStatement",
	initialize: function(id, type, message) {
		this.id = id;
	    this.type = type;
		this.message = message;
		this.date = new Date();
	},
	
	isError: function() {
		if(this.type == 'ERROR') {
			return true;
		}
		return false;
	},

	isWarning: function() {
		if(this.type == 'WARNING') {
			return true;
		}
		return false;
	},

	isDebug: function() {
		if(this.type == 'DEBUG') {
			return true;
		}
		return false;
	}

}


BSDEventUtils = {
	DEPENDENCIES: new Array("BSDLogUtils"),

	registerEvent: function(element, type, func) {
	    if(element.addEventListener) {
			element.addEventListener(type, func, true);			
	    } else if(element.attachEvent) {
			element.attachEvent('on' + type, func);
	    } else {
	    	BSDLogUtils.error("ERROR: Couldn't register event: " + type + " " + func);
	    	return false;
	    }
		return true;
	},
	
	stopPropagation: function(event) {	    
	    if(event.stopPropagation) {
	    	event.stopPropagation(); 
		} else {
	    	event.cancelBubble = true; 
	  	}

	  	if(event.preventDefault) {
	      	event.preventDefault(); 
	  	} else {
	      	event.returnValue = false; 
		}
	},
	
	removeEvent: function(element, type, func) {
		if(element.removeEventListener) {
			element.removeEventListener(type, func, true);
		} else if(element.detachEvent) { //was: && element['on' + type]) {
			element.detachEvent('on' + type, func);
		} else {
			BSDLogUtils.error("Couldn't removeEvent: " + element.detachEvent + " " + element[type]);
			return false;
		}	
		return true;
	},
	
	
	fixEventTarget: function(event) {
	    if(!event) {
			event = window.event;
	    }
	
	    if(event.target) {
			if(event.target.nodeType == 3) {
			    event.target = event.target.parentNode;
			}
		} else if(event.srcElement) {
			event.target = event.srcElement
	    }
	    return event.target;
	},

	getKeyPressed: function(event) {
	    var nbr;
	    if(window.event) {
			nbr = event.which;
	    } else { 
			nbr = event.keyCode;
	    }
	    var keyChar = String.fromCharCode(nbr);
		return keyChar;	
	},

	handleKeyPress: function(event, targetKeyCode) {
	    var nbr;
	    if(window.event) {
			nbr = event.which;
	    } else { 
			nbr = event.keyCode;
	    }

		
		for(var i = 1; i < arguments.length; i++) {
			var currentCode = arguments[i];

			if(nbr == currentCode) {
				return true;
			}
		}
		
		return false;
	},
	
	getIsLeftClick: function(event) {
		var nbr;
		if(event.which) {
			nbr = event.which;			
		} else {
			nbr = event.button;					
		}

		if(nbr == 1) {
			return true;
		}
		return false;
	},
	
	getIsRightClick: function(event) {
		var nbr;
		if(event.which) {
			nbr = event.which;			
		} else {
			nbr = event.button;					
		}
		if(nbr == 3) {
			return true;
		}
		return false;
	
	}

}


BSDColorUtils = {
	DEPENDENCIES: new Array(),
	
	rgbColorToHex: function(rgb) {  
		if(!rgb) {
			return;
		}
		var color = '#';  
  		if(rgb.slice(0,4) == 'rgb(') {  
    			var cols = rgb.slice(4, rgb.length-1).split(',');  
    			var i=0; do { color += this.toColorPart(parseInt(cols[i])) } while (++i<3);  
  		} else {  
    			if(rgb.slice(0,1) == '#') {  
	      			if(rgb.length==4) { 
	      				for(var i=1;i<4;i++) {
	      					color += (rgb.charAt(i) + rgb.charAt(i)).toLowerCase();  
	      				}
	      			}
	      			if(rgb.length==7) {
	      				color = rgb.toLowerCase();  
	      			}
    			}  
  		}  
  		if(color.length == 7) {
  			return color;
  		}
  		return rgb;
	},
	
	toColorPart: function(intValue) {
	    var digits = intValue.toString(16);
	    if (intValue < 16) return '0' + digits;
	    return digits;
  	},
  	
  	rgbRegex: /\s*rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,?\s*(\d+)?\s*\)\s*/,
  	
  	incrementRGBColor: function(rgbColor, incrementValue) {
  		if(!incrementValue) {
  			incrementValue = 50;
  		}

  		var rIncrementValue = incrementValue;
  		var gIncrementValue = incrementValue;
  		var bIncrementValue = incrementValue;
  		if(incrementValue.length && incrementValue.length > 2) {
  			rIncrementValue = incrementValue[0];
  			gIncrementValue = incrementValue[1];
  			bIncrementValue = incrementValue[2];
  		}
  		
  		var regexResult = rgbColor.match(BSDColorUtils.rgbRegex);
  		if(regexResult != null && regexResult.length > 0) {
  			var r = parseInt(regexResult[1]);
  			var g = parseInt(regexResult[2]);
  			var b = parseInt(regexResult[3]);
  			if(regexResult.length > 4) {
  				var a = parseInt(regexResult[4]);
  				if(a == 0 || (r > 245 && g > 245 && b > 245)) {
  					rIncrementValue = -1 * Math.abs(rIncrementValue);
  					gIncrementValue = -1 * Math.abs(gIncrementValue);
  					bIncrementValue = -1 * Math.abs(bIncrementValue);
  					r = 255;
  					g = 255;
  					b = 255;
  				}
  			}
  			r += rIncrementValue;
  			g += gIncrementValue;
  			b += bIncrementValue;

  			rgbColor = "rgb(" + r + "," + g + "," + b + ")";
  		} else if(rgbColor.toLowerCase() == "transparent") {
  			var r = 255 - Math.abs(rIncrementValue);
  			var g = 255 - Math.abs(gIncrementValue);
  			var b = 255 - Math.abs(bIncrementValue);
  			rgbColor = "rgb(" + r + ", " + g + ", " + b + ")";
  		} else {
  			BSDLogUtils.debug("No rgb match: [" + rgbColor + "]");
  		}
  		return rgbColor;
  	}

}	
BSDHighlightUtils = {
	DEPENDENCIES: new Array("BSDColorUtils", "BSDDOMUtils", "BSDLogUtils"),

	highlightElement: function(element, newColor, incrementValue) {
		if(element.oldBGColor) {
			return;
		}
		var currentBGColor = BSDDOMUtils.getElementStyle(element, 'background-color');

		if(!element.oldBGColor) {
			element.oldBGColor = BSDColorUtils.rgbColorToHex(currentBGColor);
		}
		if(!newColor) {
			newColor = BSDColorUtils.incrementRGBColor(currentBGColor, incrementValue);
			newColor = BSDColorUtils.rgbColorToHex(newColor);
		} 
		element.style.backgroundColor = newColor;

	},
	
	unHighlightElement: function(element) {
		var oldBGColor = element.oldBGColor;
		if(!oldBGColor) {
			oldBGColor = null;
		}	

		element.style.backgroundColor = oldBGColor;
		element.oldBGColor = null;
	},
	
	setMoveCursor: function(element) {
		if(element.oldCursor) {
			return;
		}
		var oldCursor = BSDDOMUtils.getElementStyle(element, 'cursor');
		if(!oldCursor) {
			oldCursor = "default";
		}
		element.style.cursor = "move";
		element.oldCursor = oldCursor;

	},
	
	unSetMoveCursor: function(element) {
		var oldCursor = element.oldCursor;
		element.style.cursor = oldCursor;
		element.oldCursor = null;

	},
	
	setBorderOnElement: function(element, width, newColor, incrementValue) {
		if(element.oldBorder) {
			return;
		}
		
		if(!width) {
			width = 1;
		}
		if(!newColor) {
			var currentBGColor = BSDDOMUtils.getElementStyle(element, 'background-color');
			if(!currentBGColor) {
				hexColor = "#555";
			} else {
				newColor = BSDColorUtils.incrementRGBColor(currentBGColor, incrementValue);
				newColor = BSDColorUtils.rgbColorToHex(newColor);
			}
		}
		var oldBorder = BSDDOMUtils.getElementStyle(element, "border");
		if(oldBorder) {
			element.oldBorder = oldBorder;
		}
		var newBorder = " " + width + "px solid " + newColor;

		BSDDOMUtils.changeElementStyle(element, "border", newBorder);
		BSDLogUtils.debug("Set element border: " + newBorder);

		
	},
	
	unSetBorderOnElement: function(element) {
		var oldBorder = element.oldBorder;
		if(!oldBorder) {
			oldBorder = "none";
		}
		BSDDOMUtils.changeElementStyle(element, "border", oldBorder);
		element.oldBorder = null;
	},
	
	highlightElementByOverlay: function(element) {
		if(element.bsdHighlightOverlay) {
			return;
		}
		var highlightElement = BSDDOMUtils.createElement("div", document.body);
		highlightElement.className = 'BSDHighlightedBox';
		var positionX = BSDLocationUtils.getObjectLocationX(element);
		var positionY = BSDLocationUtils.getObjectLocationY(element);
		var adjustX = 5;
		if(positionX < adjustX) {
			adjustX = positionX;
		}
		var adjustY = 5;
		if(positionY < adjustY) {
			adjustY = positionY;
		}
		var width = 5 + adjustX;
		var height = 5 + adjustY;
		BSDLocationUtils.makeElementAbsolutelyPositioned(highlightElement);
		BSDLocationUtils.cloneElementLocation(element, highlightElement, -adjustX, -adjustY, width, height);
		element.bsdHighlightOverlay = highlightElement;
	},
	
	unHighlightElementByOverlay: function(element) {
		if(!element.bsdHighlightOverlay) {
			return;
		}
		BSDDOMUtils.removeElement(element.bsdHighlightOverlay);
		element.bsdHighlightOverlay = null;
	}
	
	
	
}
var bsdValidatedFormList = {};
var bsdValidatedFormIndex = 0;
BSDValidatedForm = BSDClass.create();
BSDValidatedForm.DEPENDENCIES = new Array("BSDClass", "BSDDOMUtils", "BSDVisibilityUtils", "BSDEventUtils", "BSDLogUtils", "BSDHighlightUtils", "BSDArrayUtils");
BSDValidatedForm.VERSION = 1.1;

BSDValidatedForm.prototype = {

	className: "BSDValidatedForm",
	initialize: function(formName, submitButtonId, validationFields, validationOptions) {

		this.formName = formName;
		this.form = document.forms[formName];
		
		this.options = validationOptions;
		if(!this.options) {
			this.options = this.getDefaultOptions();
		}
		if(!this.options.messageHash) {
			this.options.messageHash = new BSDFormMessageHash();
		}
		
		if(validationFields && validationFields.length > 0) {
			this.fields = validationFields;
		} else {
			this.fields = new Array();
		}

		if(!this.form) {
			BSDLogUtils.error("ERROR: Couldn't find form for validation: " + formName);
			for(var i = 0; i < document.forms.length; i++) {
				BSDLogUtils.debug("Form: " + document.forms[i].name);
			}
			return;
		}

		var initSuccess = this.initializeForm();
		if(!initSuccess) {
			this.form = this.retryInitializeForm();
		}
		if(!this.form) {
			return;
		}
		this.initializePageErrorMessage();
		this.initializeFields();
		
		bsdValidatedFormList[this.formName] = this;
		this.bsdIndex = bsdValidatedFormIndex++;
		BSDLogUtils.debug("Initialized validation form " + formName + " " + this.bsdIndex + " " + this.form.bsdIndex + " " + (bsdValidatedFormList[this.formName] == this) );
		this.form.bsdIndex = this.bsdIndex;
  	},
  	

	doFormSubmit: function(e) {

		var form = this.form;
		if(form.skipValidation || BSDDOMUtils.getAttributeValue(form, 'skip-validation') == 'true') {
			return true;
		}
		if(typeof tinyMCE != 'undefined') {
			tinyMCE.triggerSave();
		}
		
		var result = this.doValidation();

		if(result.isValid) {

			form.hasError = false;
			return true;
		}
		
		if(this.pageErrorMessageTextElement && result.pageMessage) {
			var pageMessage = this.options.messageHash.getMessage(result.pageMessage);
			this.pageErrorMessageTextElement.innerHTML = pageMessage;
			BSDVisibilityUtils.showObject(this.pageErrorMessageContainer);
			for(var i = 0; i < this.pageErrorMessageContainer.parentNode.childNodes.length; i++) {
				var currentChild = this.pageErrorMessageContainer.parentNode.childNodes[i];
				if(currentChild != this.pageErrorMessageElemen
							&& currentChild.id 
							&& currentChild.id.indexOf(this.pageErrorMessageContainer.id) > 0) {
					this.pageErrorMessageContainer.parentNode.removeChild(currentChild);
				}
			}
		}

		if(e) {
			BSDEventUtils.stopPropagation(e);
		}
		form.hasError = true;
		BSDNavigationUtils.navigateTo("#" + form.name);
		return false;
		
	},
  	

	doValidation: function() {

		var listFieldsInPageMessage = this.options.listFieldsInPageMessage;
		var fieldPageMessageEnabled = this.options.fieldPageMessageEnabled;

		var pageMessage = this.options.defaultPageMessage;
		var pageMessagePrefix = this.options.pageMessagePrefix;
		if(!pageMessagePrefix) {
			pageMessagePrefix = "";
		}
		var pageMessageSuffix = this.options.pageMessageSuffix;
		if(!pageMessageSuffix) {
			pageMessageSuffix = "";
		}

		var isValid = true;
		for(var i = 0; i < this.fields.length; i++) {
			var currentField = this.fields[i];

			var result = currentField.validate();
			if(result && !result.isValid) {
				isValid = false;


				if(listFieldsInPageMessage && currentField.getLabel()) {
					pageMessage += pageMessagePrefix + currentField.getLabel + pageMessageSuffix;
				} else if((fieldPageMessageEnabled || currentField.forcePageMessage) && result.pageMessage) {
					var resultMessage = this.options.messageHash.getMessage(result.pageMessage);
					pageMessage += pageMessagePrefix + resultMessage + pageMessageSuffix;				
				} 
			}

		}

		
		var result = new BSDValidatedFormResult(isValid, null, pageMessage);				

		return result;
	},
	
	getDefaultOptions: function() {
		var options = new Object();
		options.defaultPageMessage = "There are some problems with your submission.  Please fix them and try again:";
		options.pageMessagePrefix = "<br>";
		options.validateFieldsOnChange = true;
		options.fieldHighlightStyle = "fieldError";
		options.labelHighlightStyle = "fieldLabelError";
		
		return options;
	},
  	
  	addValidationField: function(validationField) {

  		this.initializeField(validationField);
  		BSDArrayUtils.append(this.fields, validationField);


  	},
  	
  	addField: function(fieldElementId, validator, messageElementId, labelElementId) {
  		var validationField = new BSDValidatedFormField(this.options, fieldElementId, validator, messageElementId, labelElementId);
  		this.addValidationField(validationField);
  	},
  	
  	removeField: function(fieldElementId) {
  		for(var i = 0; i < this.fields.length; i++) {
  			if(this.fields[i].fieldElement && this.fields[i].fieldElement.id == fieldElementId) {
  				BSDArrayUtils.deleteElement(this.fields, i);
  			}
  		}

  	},
  	
  	initializePageErrorMessage: function() {
  		var errorMessageContainerId = "ERROR_MESSAGE_ROW";
  		if(this.options.errorMessageContainerId) {
  			errorMessageContainerId = this.options.errorMessageContainerId;
  		}
  		var containerElement = BSDDOMUtils.getObjectByIdFromParent(this.form, errorMessageContainerId);
  		if(!containerElement) {
  			containerElement = BSDDOMUtils.getObjectById(errorMessageContainerId);
  		}
  		if(!containerElement) {
  			BSDLogUtils.error("ERROR: Couldn't find error message container for form validator [" + errorMessageContainerId + "]");  		
  			return;  		
  		}
  		this.pageErrorMessageContainer = containerElement;

  		var errorMessageTextId = "ERROR_MESSAGE_TEXT";
  		if(this.options.errorMessageTextId) {
  			errorMessageTextId = this.options.errorMessageTextElementId;
  		}
  		var textElement = BSDDOMUtils.getObjectById(errorMessageTextId);
  		if(!textElement) {
  			BSDLogUtils.error("ERROR: Couldn't find error message text element for form validator");  		
  			return;  		
  		}
  		this.pageErrorMessageTextElement = textElement;

  	},
  	
  	initializeForm: function() {
  		var formValidator = this;
  		if(this.form.isValidationHandlerInitialized) {
  			return true;
  		}
		function formOnSubmitHandler(e) {

			return formValidator.doFormSubmit(e);			
		}   


		var success = BSDEventUtils.registerEvent(this.form, "submit", formOnSubmitHandler);
		if(success) {
			this.form.isValidationHandlerInitialized = true;
		}
		return success;
  	},
  	
  	retryInitializeForm: function() {

		this.form = BSDDOMUtils.getObjectById(this.formName);
		if(!this.form || !this.initializeForm()) {
			BSDLogUtils.error("Couldn't initialize form " + this.formName + " by id");
			return;
		}

		if(this.form.bsdIndex && this.form.bsdIndex <= bsdValidatedFormIndex) {

			var formList = BSDDOMUtils.getObjectsById(this.formName, document);

			for(var i = 0; i < formList.length; i++) {
				if(!formList[i].bsdIndex) {
					this.form = formList[i];
					BSDLogUtils.debug("Got form from list: " + this.form.name);
					break;
				}
			}
		} 
		return this.form; 	
  	},
  	
  	initializeFields: function() {  	
  		for(var i = 0; i < this.fields.length; i++) {
  			var currentField = this.fields[i];
  			this.initializeField(currentField);
  		}
  	},
  	
  	initializeField: function(field) {
		field.initializeElements(this.form, this.options);  		
  	}, 
  	
  	replaceValidator: function(fieldId, newValidator) {
  		for(var i = 0; i < this.fields.length; i++) {
  			var currentField = this.fields[i];

  			if(currentField.fieldElementId == fieldId) {  				
  				for(var j = 0; j < currentField.validators.length; j++) {
  					var currentValidator = currentField.validators[j];
  					if(currentValidator.className != newValidator.className) {
  						continue;
  					}
  					if(newValidator.fieldMessage) {
  						newValidator.fieldMessage = currentValidator.fieldMessage;
  					}
  					if(newValidator.pageMessage) {
  						newValidator.pageMessage = currentValidator.pageMessage;
  					}
  					BSDArrayUtils.replace(currentField.validators, j, newValidator);
  					return currentField;
  				}
  			}
  		}
		return;  	
  	} 	
}

BSDValidatedFormUtils = {
	reinitializeForms: function() {

		for(var formName in bsdValidatedFormList) {
			var currentForm = bsdValidatedFormList[formName];

			currentForm.initializeFields();
		}
	},
	
	replaceValidator: function(fieldId, newValidator) {

		for(var formName in bsdValidatedFormList) {
			var currentForm = bsdValidatedFormList[formName];

			var validatedField = currentForm.replaceValidator(fieldId, newValidator);
			if(validatedField) {
				return validatedField;
			}
		}

	}
}

BSDValidatedFormField = BSDClass.create();
BSDValidatedFormField.prototype = {

	initialize: function(validationOptions, fieldElementId, messageElementId, labelElementId, validator) {

		this.options = validationOptions;
		this.fieldElementId = fieldElementId;
		this.messageElementId = messageElementId;
		this.labelElementId = labelElementId;

		this.validators = new Array();
		if(validator) {
			this.addValidator(validator);
		}
	},
	
	initializeElements: function(parentFormElement, options) {
		if(parentFormElement) {
			this.parentFormElement = parentFormElement;
		}

  		if(!this.options) {
  			this.options = options;
  		}  	
  		if(this.fieldElementId) {
  			var element = BSDDOMUtils.getObjectByIdFromParent(this.parentFormElement, this.fieldElementId);
  			if(!element) {
  				BSDLogUtils.error("ERROR: Couldn't find field element with id " + this.fieldElementId);
  				this.fieldElement = null;
  			} else {
  				element.bsdIndex = this.bsdIndex;
  				this.fieldElement = element;

  			}
  		}
  		if(this.labelElementId) {
  			var element = BSDDOMUtils.getObjectByIdFromParent(this.parentFormElement, this.labelElementId);
  			if(!element) {
  				BSDLogUtils.error("ERROR: Couldn't find field label with id " + this.labelElementId);
  			} else {
  				this.labelElement = element;
  			}  		
  		}
  		if(this.messageElementId) {
  			var element = BSDDOMUtils.getObjectByIdFromParent(this.parentFormElement, this.messageElementId);
  			if(!element) {
  				BSDLogUtils.error("ERROR: Couldn't find field message with id " + this.messageElementId);
  			} else {
  				this.messageElement = element;
  			}    			
  		}
  		
  		if(this.options && this.options.validateFieldsOnChange && this.fieldElement) {
			this.initializeOnChange(this);  			
  		}
  		
  		this.bsdIndex = this.bsdIndex;
	
	},
	
  	initializeOnChange: function(field) {
  		
		function fieldOnChangeHandler(e) {
			BSDLogUtils.debug("Got field onchange event");
			var validationResult = field.validate();
		}   
		
		function fieldOnKeydownHandler(e) {
			field.clearError();			
		}	
		
		BSDEventUtils.registerEvent(field.fieldElement, "change", fieldOnChangeHandler);		
		BSDEventUtils.registerEvent(field.fieldElement, "keydown", fieldOnKeydownHandler);		
  	},	
	
	addValidator: function(newValidator) {	
		BSDArrayUtils.append(this.validators, newValidator);
	},
	
	validate: function() {
		var result;
		for(var i = 0; i < this.validators.length; i++) {
			result = this.validateByValidator(this.validators[i]);
			if(!result.isValid) {
				return result;
			}
		}
		return result;
	},
	
	validateByValidator: function(validator) {
		BSDLogUtils.debug("validateByValidator: BEGIN " + this.fieldElementId + " " + this.fieldElement);
		if(!this.fieldElement && this.fieldElementId) {
			this.initializeElements();
		}
		if(!this.fieldElement || this.fieldElement.skipValidation) {
			var result = new Object();
			result.isValid = true;
			return result;
		}
		var value = BSDFormUtils.getFieldValue(this.fieldElement);

		var result = validator.validate(value);

		if(result.newValue && result.newValue.length > 0) {

			this.fieldElement.value = result.newValue;
		}
		/* This doesn't work because the response doesn't come back until the current thread is finished.   Need to rework messaging to happen asyncronously
		var beginTime = new Date();
		var currentTime = new Date();
		
  		BSDLogUtils.debug("Checking is pending: " + result.isPending + " " + (currentTime.getTime() - beginTime.getTime()));
  		while(result.isPending && currentTime.getTime() - beginTime.getTime() < 2000) { //2 second timeout
  			BSDLogUtils.debug("Is pending, looping: " + (currentTime.getTime() - beginTime.getTime()));
			var x = "y" + "z" + "w";
			var y = x + "A";		
			currentTime = new Date();
					
  		}
  		*/


		if(this.fieldElement && !result.isValid && this.options.fieldHighlightStyle) {
			BSDDOMUtils.addClass(this.fieldElement, this.options.fieldHighlightStyle);
		}

		if(this.messageElement && !result.isValid && result.fieldMessage) {
			var resultMessage = this.options.messageHash.getMessage(result.fieldMessage);
		  	this.messageElement.innerHTML = resultMessage;

			for(var i = 0; i < this.messageElement.childNodes.length; i++) {
				var currentChild = this.messageElement.childNodes[i];
				if(currentChild.nodeType == 1 && !BSDVisibilityUtils.isObjectHidden(currentChild)) {
					BSDVisibilityUtils.hideObject(currentChild);
					currentChild.hiddenByValidator = true;
				}				
			}
		}
		
		if(this.labelElement && !result.isValid && this.options.labelHighlightStyle) {
			BSDDOMUtils.addClass(this.labelElement, this.options.labelHighlightStyle);
		}
		
		if(this.isError && result.isValid) {			
			this.clearError();		
		}
		this.isError = !result.isValid;


		return result;		
	},

	
	clearError: function() {

		if(this.fieldElement && this.options.fieldHighlightStyle) {
			BSDDOMUtils.removeClass(this.fieldElement, this.options.fieldHighlightStyle);
		}
		
		if(this.messageElement) {
			this.messageElement.innerHTML = "";
			for(var i = 0; i < this.messageElement.childNodes.length; i++) {
				var currentChild = this.messageElement.childNodes[i];
				if(currentChild.nodeType == 1 && currentChild.hiddenByValidator) {
					BSDVisibilityUtils.showObject(currentChild);
					currentChild.hiddenByValidator = false;
				}				
			}
		}
		
		if(this.labelElement && this.options.labelHighlightStyle) {
			BSDDOMUtils.removeClass(this.labelElement, this.options.labelHighlightStyle);
		}
	}
	
	
}


BSDValidatedFormResult = BSDClass.create();
BSDValidatedFormResult.prototype = {

	initialize: function(isValid, fieldMessage, pageMessage) {
		this.isValid = isValid;
		this.isPending = false;
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
	},
	
	
	handleValidationResponse: function(data) {
		var successful = BSDAjaxUtils.doNavigationReply(data, true);

		if(!successful) {
			BSDLogUtils.error("Ajax validation request failed: " + data);
	    } else if(data.errorMessage) {
	    	this.isValid = false;
		} else {
			this.isValid = true;
		}	
		this.isPending = false;
	}	
	
}


BSDRegexFormFieldValidator = BSDClass.create();
BSDRegexFormFieldValidator.prototype = {
	
	initialize: function(isRequired, fieldMessage, pageMessage, regex) {
		this.regex = new RegExp(regex);
		this.fieldMessage = fieldMessage;
		this.isRequired = isRequired;
		this.pageMessage = pageMessage;
	},
	
	validate: function(value) {
		var result;

		if(this.isRequired && (!value || value.length < 1)) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);				
		} else if(value && value.length > 0 && this.regex && this.regex.exec && this.regex.exec(value) == null) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);							
		} else {
			result = new BSDValidatedFormResult(true);									
		}
		return result;
	}

}


BSDRequiredFormFieldValidator = BSDClass.create();
BSDRequiredFormFieldValidator.prototype = {
	
	initialize: function(isRequired, fieldMessage, pageMessage) {
		this.isRequired = isRequired;
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
	},
	
	validate: function(value) {
		var result;

		
		if(!value && this.isRequired) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
		} else if(this.minLength && value && value.length < this.minLength) {
			result = new BSDValidatedFormResult(false, this.minLengthErrorMessage, this.pageMessage);										
		} else {
			result = new BSDValidatedFormResult(true);									
		}
		return result;
	}

}


BSDCardCodeFormFieldValidator = BSDClass.create();
BSDCardCodeFormFieldValidator.prototype = {
	
	initialize: function(isRequired, fieldMessage, pageMessage) {
		this.isRequired = isRequired;
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
		this.regex = new RegExp(/[\d]{3,4}/);
	},
	
	validate: function(value) {
		var result;
		if(!value && this.isRequired) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
		} else if(this.minLength && value && value.length < this.minLength) {
			result = new BSDValidatedFormResult(false, this.minLengthErrorMessage, this.pageMessage);										
		} else if(value && value.indexOf('*') > -1) {
			result = new BSDValidatedFormResult(true);									
		} else if(value) {
			var typeId = BSDCardNumberFormFieldValidator.getCardTypeId();
			if(!typeId) {
				result = new BSDValidatedFormResult(false);
			} else if(typeId == 15 && value.length != 4) {
				result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
			} else if(value.length != 3) {
				result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
			} else if(this.regex && this.regex.exec && this.regex.exec(value) == null) {
				result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
			} else {
				result = new BSDValidatedFormResult(true);												
			}
		}
		return result;
	}

}



BSDCardNumberFormFieldValidator = BSDClass.create();
BSDCardNumberFormFieldValidator.prototype = {
	
	initialize: function(isRequired, fieldMessage, pageMessage) {
		this.isRequired = isRequired;
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
		
	},
	
	validate: function(value) {
		var result;
		if(!value && this.isRequired) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
		} else if(this.minLength && value && value.length < this.minLength) {
			result = new BSDValidatedFormResult(false, this.minLengthErrorMessage, this.pageMessage);										
		} else if(value && value.indexOf('*') > -1) {
			result = new BSDValidatedFormResult(true);									
		} else if(value) {
			value = value.replace(/[\D]+/g, "");
			var typeId = BSDCardNumberFormFieldValidator.getCardTypeId();
			if(!typeId) {
				result = new BSDValidatedFormResult(false);
			} else if(!validateCardNumber(value, typeId)) { //this function is inserted into the page html by java
				result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);		
			} else {
				result = new BSDValidatedFormResult(true);												
			}
		}
		return result;
	}

}

BSDCardNumberFormFieldValidator.getCardTypeId = function() {
	var typeIdElement = BSDDOMUtils.getObjectById("PAYMENT_METHOD_TYPE_ID");
	if(typeIdElement && typeIdElement.value && typeIdElement.value.length > 0) {
		return new typeIdElement.value;
	}
	
	var buttons = BSDDOMUtils.getObjectsByClass('PAYMENT_METHOD_TYPE_RADIO_BUTTON');
	for(var i = 0; i < buttons.length; i++) {
		var currentButton = buttons[i];
		if(currentButton.checked && currentButton.value && currentButton.value.length > 0) {
			return currentButton.value;
		} 
	}

	BSDLogUtils.error("Couldn't find PAYMENT_METHOD_TYPE_ID field value for form validation");
	return null;
		
}

BSDCardNumberFormFieldValidator.validateCardNumberLength = function(cardNumber) {
	for(var i = 1; i < arguments.length; i++) {
		if(cardNumber.length == arguments[i]) {
			return true;
		}
	}
	return false;
}

BSDCardNumberFormFieldValidator.validateCardNumberPrefix = function(cardNumber) {
	for(var i = 1; i < arguments.length; i++) {
		var value = arguments[i] + '';
		if(cardNumber.indexOf(value) == 0) {
			return true;
		}
	}

	return false;
}

BSDCardNumberFormFieldValidator.validateMod10 = function(cardNumber) {
	var total = 0;
	var j = 1;	
	for(var i = cardNumber.length - 1; i >= 0; i--) {
		var digit = cardNumber.charAt(i);
		if(j % 2 == 0) {
			var multDigit = parseInt(digit) * 2;
			if(multDigit > 9) {
				var strMultDigit = multDigit + '';
				var digit1 = parseInt(strMultDigit.charAt(0));
				var digit2 = parseInt(strMultDigit.charAt(1));
				total += digit1 + digit2;
			} else {
				total += multDigit;
			}
		} else {
			total += parseInt(digit);
		}
		j++;
	}
	return total % 10 == 0;
}

BSDExerciseValidator = BSDClass.create();
BSDExerciseValidator.prototype = {
	
	initialize: function(isRequired, fieldMessage, pageMessage) {
		this.isRequired = isRequired;
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
		
	},
	
	validate: function(value) {
		var result = new BSDValidatedFormResult(true, this.fieldMessage, this.pageMessage);
		if(!value || value.length < 1) {
			return result;
		} 
		
		var parts = value.split(/\|/);
		if(parts.length < 4) {
			return result;
		}
		
		var algorithm = parts[1];
		var expressionResult = '';
		if(algorithm == 'eval') {
			expressionResult = eval(parts[3]);
		}
		value = parts[0] + "|" + parts[1] + "|" + parts[2] + "|" + parts[3] + "|" + expressionResult;
		result.newValue = value;
		return result;
	}

}
		


BSDSupportedFileTypeValidator = BSDClass.create();
BSDSupportedFileTypeValidator.prototype = {
	
	className: "BSDSupportedFileTypeValidator",
	initialize: function(fieldMessage, pageMessage, validImageFileExtensions, validVideoFileExtensions, validAudioFileExtensions, validDocumentFileExtensions, validFlashFileExtensions) {
		this.fieldMessage = fieldMessage;
		this.pageMessage = pageMessage;
		this.validFileExtensions = new Array();
		BSDArrayUtils.append(this.validFileExtensions, validImageFileExtensions);
		BSDArrayUtils.append(this.validFileExtensions, validVideoFileExtensions);

		BSDArrayUtils.append(this.validFileExtensions, validDocumentFileExtensions);
		BSDArrayUtils.append(this.validFileExtensions, validFlashFileExtensions);
		
		if(!this.fieldMessage) {
			this.fieldMessage = "This type of file isn't supported";
		}
		if(!this.pageMessage) {
			this.pageMessage = "One of your files has an unsupported format.  Valid file types include: <ul>";
			if(validImageFileExtensions && validImageFileExtensions.length > 0) {
				this.pageMessage += "<li>Images:  " + this.getFileExtensionMessage(validImageFileExtensions) + "</li>";
			}
			if(validVideoFileExtensions && validVideoFileExtensions.length > 0) {
				this.pageMessage += "<li>Video:  " + this.getFileExtensionMessage(validVideoFileExtensions) + "</li>";
			}
			if(validAudioFileExtensions && validAudioFileExtensions.length > 0) {
				this.pageMessage += "<li>Audio:  " + this.getFileExtensionMessage(validAudioFileExtensions) + "</li>";
			}
			if(validDocumentFileExtensions && validDocumentFileExtensions.length > 0) {
				this.pageMessage += "<li>Documents:  " + this.getFileExtensionMessage(validDocumentFileExtensions) + "</li>";
			}
			if(validFlashFileExtensions && validFlashFileExtensions.length > 0) {
				this.pageMessage += "<li>Flash:  " + this.getFileExtensionMessage(validFlashFileExtensions) + "</li>";
			}
			this.pageMessage += "</ul>";
		}
	},
	
	validate: function(value) {
		var result;
		if(!value || value.length < 1) {
			return new BSDValidatedFormResult(true);		
		} 
		
		var fileParts = value.split(".");
		var extension = fileParts[fileParts.length - 1];
		BSDLogUtils.debug("Got file type result: " + extension + " [" + this.fieldMessage + "][" + this.pageMessage + "] " + this.validFileExtensions);
		if(!extension || extension.length < 1) {
			result = new BSDValidatedFormResult(false, 'This file is missing its file extension (e.g. .jpg)', this.pageMessage);		
		} else if(!BSDArrayUtils.contains(this.validFileExtensions, extension.toLowerCase())) {
			result = new BSDValidatedFormResult(false, this.fieldMessage, this.pageMessage);				
		} else {
			return new BSDValidatedFormResult(true);		
		}
		return result;
	},
	
	getFileExtensionMessage: function(extensions) {
		var message = "";
		for(var i = 0; i < extensions.length; i++) {
			message += extensions[i];
			if(i < extensions.length - 1) {
				message += ", ";
			}
		}
		return message;
	}
}

BSDFormMessageHash = BSDClass.create();
BSDFormMessageHash.prototype = {
	
	initialize: function(hash) {
		this.hash = hash;
	},
	
	getMessage: function(messageId) {
		if(!messageId) {
			return messageId;
		}
		var newMessage = this.hash[messageId];
		if(newMessage) {
			return newMessage;
		}
		return messageId;
	}

}


BSDNavigationUtils = {
	DEPENDENCIES: new Array("BSDEventUtils"),
	
	registerClickNavigation: function(element, href) {  

		this.navigateToHref = function(event) {
			window.location = href;
		}
		BSDEventUtils.registerEvent(element, "click", navigateToHref);
	},
	
	refreshPage: function() {

		if(window.location && window.location.reload) {
			window.location.reload(true);
		} else if(window.location && window.location.replace) {
			window.location.replace(document.URL);
		} else {
			window.location.href = documentURL;
		}
	},
	
	refreshWithArgs: function(queryArgs) {
		var url = document.URL;
		var args = BSDNavigationUtils.getDocumentQueryArgs();
		if(url.indexOf("?") > 0) {
			url = url.substring(0, url.indexOf("?"));
		}

		for(var name in queryArgs) {
			args[name] = queryArgs[name];
		}

		url += "?";
		var i = 0;
		for(var name in args) {
			if(i != 0) {
				url += "&";
			}
			url += name;
			url += "="
			url += args[name];
			i++;
		}

		BSDNavigationUtils.navigateTo(url);
	},
	
	navigateTo: function(href) {
		window.location.href = href;
	},
	
	getHost: function() {
		return window.location.protocol + "//" + window.location.host;
	},
	
	getDocumentURI: function(includeQueryString) {
		var url = document.URL;
		return BSDNavigationUtils.getURI(url, includeQueryString);
	},
	
	getURI: function(url, includeQueryString) {
		var beginIndex = url.indexOf("://");
		if(beginIndex < 0) {
			beginIndex = 0;
		} else {
			beginIndex += 4;
		}
		var slashIndex = url.indexOf("/", beginIndex);
		if(slashIndex < 0) {
			return;
		}
		
		var qIndex = -1;
		if(!includeQueryString) {
			qIndex = url.indexOf("?", beginIndex);
		}
		if(qIndex > 0) {	
			return url.substring(slashIndex, qIndex);
		} else {
			return url.substring(slashIndex);
		}
		
	},

	populateQueryArgs: function(arguments, dashToUnderscore) {
		var queryIndex = document.URL.indexOf("?");
		var hashIndex = document.URL.indexOf("#");
		if(queryIndex < 0) {
			return arguments;
		}
		
		var query;
		if(hashIndex > 0) {
			query = document.URL.substring(queryIndex + 1, hashIndex);
		} else {
			query = document.URL.substring(queryIndex + 1);
		}
		
		var args = query.split("&");
		for(var i = 0; i < args.length; i++) {
			var parts = args[i].split('=');
			if(parts.length > 1) {
				var name = parts[0];
				if(dashToUnderscore) {		
					var nameRegex = /-/g;
					name = name.replace(nameRegex, "_");
				}
				arguments[name] = parts[1];
			}
		}
		return arguments;
	},
		
	getDocumentQueryArgs: function() {
		var arguments = {};
		BSDNavigationUtils.populateQueryArgs(arguments);
		return arguments;
	}
}	
BSDFormUtils = {
	DEPENDENCIES: new Array("BSDDOMUtils", "BSDNavigationUtils"),
	VERSION: 1.0,
	
	backgroundFormIndex: 0,
	
	submitOnEnter: function(formField, e) {
		var keycode;
		if(window.event) {
			keycode = window.event.keyCode;
		} else if(e) {
			keycode = e.which;
		} else {
			return true;
		}
		if(keycode == 13) {
	   		formField.form.submit();
	 		return false;
	   	} else {
		   return true;
		}
	},
	
	cloneFormElement: function(formElementId, formElementParentId, maxCount, dontReIdElements) {
		var parentElement;
		var inputElement;
		if(formElementParentId) {
			parentElement = BSDDOMUtils.getObjectById(formElementParentId);
		}
		if(parentElement) {
			inputElement = BSDDOMUtils.getObjectByIdFromParent(parentElement, formElementId);
		} else {
			inputElement = BSDDOMUtils.getObjectById(formElementId);
			parentElement = inputElement;
		}
	
	    var newParentElement = null;
		var newElement = null;
        if(inputElement) {
			var count = 1;
			if(inputElement.cloneCount) {
				count = parseInt(inputElement.cloneCount);
			}
			if(maxCount && count >= maxCount) {
				return;
			}
			
        	if(!inputElement.name) {
        		alert("ERROR: Couldn't get name of input element");
        		return;
        	}
            newParentElement = parentElement.cloneNode(true);
            if(parentElement.nextSibling) {
             	parentElement.parentNode.insertBefore(newParentElement, parentElement.nextSibling);
            } else {
             	parentElement.parentNode.appendChild(newParentElement);           
            }
            parentElement.parentNode.appendChild(newParentElement);
			
			count++;
			inputElement.cloneCount = count;

			if(newParentElement.id != formElementId) {
				newElement = BSDDOMUtils.getObjectByIdFromParent(newParentElement, formElementId);
			} else {
				newElement = newParentElement;
			}


			if(!dontReIdElements) {
				newElement.name = newElement.name + count;
				newElement.id = newElement.id + count;
			}
			if(!dontReIdElements && newParentElement != newElement) {
				newParentElement.id = newParentElement.id + count;
				var childFields = newParentElement.childNodes;
				for(var i = 0; i < childFields.length; i++) {
					var currentChild = childFields[i];
					if(currentChild.id == formElementId) {
						continue;
					}
					currentChild.id = currentChild.id + count;
				}
			}

        } else {
             alert("ERROR: Couldn't find element with id " + formElementId);
        }
        return newParentElement;	
	},
	
	debugForms: function() {
	    var message = "";
		var forms = document.forms;
		for(var i = 0; i < forms.length; i++) {
			message += debugFormElements(forms[i]);
		}
		alert(message);
	},
	
	debugFormElements: function(elements) {
		var message = "";
		for(var i = 0; i < elements.length; i++) {
			var currentElement = elements[i];
			message += currentElement.name + ": [" + currentElement.value + "]\n";
		}
		return message;
	},
	
	getIsOptionSelectedById: function(elementId, targetValue) {
		var select = BSDDOMUtils.getObjectById(elementId);
		if(!select || !select.options) {
			BSDLogUtils.debug("Couldn't find select with id: " + elementId);
			return false;
		}
		
		var value = select.options[select.selectedIndex].value;
		if(!value) {
			return false;
		}
		return targetValue == value;
	},
	
	getFormParamsByName: function(formName) {
		var form = document.forms[formName];
		if(!form) {
			BSDLogUtils.error("Couldn't find form with name: " + formName);
			return;
		}
	},
	
	getFormParams: function(form) {
		var data = {};

		for(var i = 0; i < form.elements.length; i++) {
			var currentElement = form.elements[i];

			if(currentElement.value) {
				data[currentElement.name] = currentElement.value;
			}
		}
		return data;
	},
	
	selectAllByClass: function(className) {
		var elements = BSDDOMUtils.getObjectsByClass(className);
		for(var i = 0; i < elements.length; i++) {
			elements[i].checked = true;
		}
		return true;
	},
	
	getFieldValueById: function(fieldElementId) {
		var element = BSDDOMUtils.getObjectById(fieldElementId);
		if(!element) {
			BSDLogUtils.error("Couldn't find element with id: " + fieldElementId);
			return;
		}
		return BSDFormUtils.getFieldValue(element);
	},
	
	getFieldValue: function(fieldElement) {
		var value = fieldElement.value;
		if(fieldElement && fieldElement.nodeName.toLowerCase() == 'input' && BSDDOMUtils.getAttributeValue(fieldElement, "type").toLowerCase() == 'radio') {
			var radioElement = fieldElement.form.elements[fieldElement.name];
			for(var i = 0; i < radioElement.length; i++) {
				var currentRadio = radioElement[i];
				if(currentRadio.checked) {
					value = currentRadio.value;
					break;
				}
			}
			
			BSDLogUtils.debug("Got value for radio field " + fieldElement.id + " [" + value + "]");
		} else if(fieldElement && fieldElement.nodeName.toLowerCase() == 'select' && (!value || value.length < 1) ) {

			var selectedIndex = fieldElement.selectedIndex;
			var strSelected = " " + selectedIndex;

			if(strSelected.length > 1 && fieldElement.options && fieldElement.options.length > selectedIndex) {
				var option = fieldElement.options[selectedIndex];
				value = option.value;
				if(!value || value.length < 1) {
					value = BSDDOMUtils.getText(option);
				}

				BSDLogUtils.debug("Got option value: [" + value + "]");

			}
		}
		return value;
	
	},
	
	getSelectLabel: function(fieldElement) {

		var label;
		var selectedIndex = fieldElement.selectedIndex;
		var strSelected = " " + selectedIndex;

		if(strSelected.length > 1 && fieldElement.options && fieldElement.options.length > selectedIndex) {
			var option = fieldElement.options[selectedIndex];
			label = option.text;
			if(!label || label.length < 1) {
				label = BSDDOMUtils.getText(option);
			}



		}
		return label;
	},
	
	setFieldValue: function(fieldElement, value) {
		if(fieldElement.nodeName.toLowerCase() == 'select') {
			for(var i = 0; i < fieldElement.options.length; i++) {
				if(fieldElement.options[i].value == value) {
					fieldElement.selectedIndex = i;
					return;
				}
			}
		} else {
			fieldElement.value = value;
		}
	},
	
	toggleNestedSelect: function(parentSelect, childSelectIdPrefix, defaultChildId) {
		var selectedValue = BSDFormUtils.getFieldValue(parentSelect);
		var defaultChild = BSDDOMUtils.getObjectById(defaultChildId);
		if(!defaultChild) {
			BSDLogUtils.error("Couldn't find default nested select element with id: " + defaultChildId);
		}
		if(!selectedValue || selectedValue.length < 1) {
			if(defaultChild) {
				BSDDOMUtils.setAttributeValue(defaultChild, 'disabled', 'disabled');
			}
			return true;
		}
		
		var children = BSDDOMUtils.getObjectsByClass(parentSelect.id + "_CHILD");
		selectedValue = BSDStringUtils.stripWhitespace(selectedValue);
		var selectedChild;
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];

			if(BSDStringUtils.endsWith(currentChild.id, "_" + selectedValue)) {
				selectedChild = currentChild;
			} else if(currentChild != defaultChild) {
				BSDVisibilityUtils.hideObject(currentChild);
				currentChild.disabled = true;

				
			}
				if(currentChild.otherField) {

					BSDVisibilityUtils.hideObject(currentChild.otherField);
				}
		}

		if(selectedChild) {
			
			BSDVisibilityUtils.showObject(selectedChild);
			BSDDOMUtils.setAttributeValue(selectedChild, 'disabled', false);
			
			selectedChild.disabled = false;
			if(defaultChild && defaultChild != selectedChild) {
				BSDVisibilityUtils.hideObject(defaultChild);
			}
			if(selectedChild.otherField) {
				BSDVisibilityUtils.showObject(selectedChild.otherField);
			}
			
		} else {
			if(defaultChild) {
				BSDVisibilityUtils.showObject(defaultChild);
				defaultChild.disabled = true;

				if(defaultChild.otherField) {
					BSDVisibilityUtils.hideObject(currentChild.otherField);
				}
				
			}
		}
		
		return true;
	},
	
	toggleSelectOther: function(parentSelect, otherFieldId, otherValue) {

		var otherField = BSDDOMUtils.getObjectById(otherFieldId);
		if(!otherField) {
			BSDLogUtils.error("Couldn't find default other field with id: " + otherFieldId);
			return true;
		}
		
		var selectedValue = BSDFormUtils.getFieldValue(parentSelect);
		if(selectedValue && selectedValue == otherValue) {
			BSDVisibilityUtils.showObject(otherField);
			parentSelect.otherField = otherField;
		} else {
			BSDVisibilityUtils.hideObject(otherField);
			parentSelect.otherField = null;
		}
		
		return true;
		
	},
	
	mirrorFieldValue: function(field, targetElementId, parentElementId) {
		
		var target;
		if(parentElementId) {
			var parentElement = BSDDOMUtils.getObjectById(parentElementId);
			if(parentElement) {
				target = BSDDOMUtils.getObjectByIdFromParent(parentElement, targetElementId);
			} else {
				BSDLogUtils.error("ERROR: Couldn't get parent for mirrorFieldValue: " + parentElementId);
			}
		} else {
			target = BSDDOMUtils.getObjectById(targetElementId);
		}
		if(!target) {
			BSDLogUtils.error("ERROR: Couldn't get target for mirrorFieldValue: " + targetElementId);
			return;
		}
		
		BSDDOMUtils.setText(target, field.value);

	},
	
	refreshOnSelect: function(field) {
		var value = BSDFormUtils.getFieldValue(field);
		var args = {};
		args[field.name] = value;
		BSDNavigationUtils.refreshWithArgs(args);
	}
	
	
}
BSDPoint = BSDClass.create();
BSDPoint.DEPENDENCIES = new Array("BSDClass");
BSDPoint.prototype = {

	className: "BSDPoint",
	initialize: function(x, y) {
	    this.x = parseInt(x);
	    this.y = parseInt(y);
   	},
   	
    toString: function() {
		var str = "[" + this.x + "," + this.y + "]";
		return str;
    }
}

BSDPoint.calculateDistance = function(point1, point2) {
	var distance1 = Math.pow((point1.x - point2.x), 2);
	var distance2 = Math.pow((point1.y - point2.y), 2);
	var distance = distance1 + distance2;
	if(isNaN(distance)) {
		alert("NAN: " + point1 + point2 + " " + distance1 + " " + distance2 + " " + distance);
	}
	distance = Math.sqrt(distance);
	return distance;
}
BSDElementPosition = BSDClass.create();
BSDElementPosition.DEPENDENCIES = new Array("BSDClass", "BSDLocationUtils", "BSDArrayUtils");
BSDElementPosition.prototype = {

	className: "BSDElementPosition",
	initialize: function(element, xPosition, yPosition) {
	    if(xPosition) {
			this.x = xPosition;
	    } else {
	        this.x = BSDLocationUtils.getObjectLocationX(element);
	    }
	    if(yPosition) {
	        this.y = yPosition;
	    } else {
	        this.y = BSDLocationUtils.getObjectLocationY(element);
	    }
	    this.minX = this.x;
	    this.minY = this.y;
	    if(element) {
		    this.width = BSDDOMUtils.getElementWidth(element);
		    this.height = BSDDOMUtils.getElementHeight(element);
		}
	    this.maxX = this.minX + this.width;
	    this.maxY = this.minY + this.height;
	    this.element = element;
	},
	
	clone: function() {
		var newPosition = new BSDElementPosition(this.element);
		newPosition.x = this.x;
		newPosition.y = this.y;
		newPosition.minX = this.minX;
		newPosition.minY = this.minY;
		newPosition.maxX = this.maxX;
		newPosition.maxY = this.maxY;
		newPosition.width = this.width;
		newPosition.height = this.height;
		return newPosition;
	},

	addExclusionPosition: function(newExclusionPosition) {
		if(!this.exclusionPositions) {
			this.exclusionPositions = new Array();			
		}
		BSDArrayUtils.append(this.exclusionPositions, newExclusionPosition);
	},

    contains: function(x, y) {
		if(x < this.minX) {
	        return false;
	    } else if(y < this.minY) {
		    return false;
	    } else if(x > this.maxX) { 
		    return false;
	    } else if(y > this.maxY) {
		    return false;
	    }
	    for(var i = 0; this.exclusionPositions && i < this.exclusionPositions.length; i++) {
	    	var currentExclusionPosition = this.exclusionPositions[i];
	    	if(currentExclusionPosition.contains(x, y)) {
	    		return false;
	    	}
	    }
		return true;
    },

	containsPosition: function(position) {
		return this.contains(position.x, position.y);
	},
	
	getCenter: function() {
		return new BSDPoint(this.minX + this.width/2, this.minY + this.height/2);		
	},

    toString: function() {
		var str = "[" + this.x + "," + this.y + " " + this.maxX + ",";
		str += this.maxY + "]";
		return str;
    },
    
    setWidthFromParent: function() {
    	var parent = this.element.parentNode;
    	while(parent && parent.offsetWidth == 0) {
    		parent = parent.parentNode;
    	}
    	var width = parent.offsetWidth;
    	this.setWidth(width);
    },
    
    setWidth: function(newWidth) {
    	this.width = newWidth;
    	this.maxX = this.minX + this.width;
    },
    
    setHeight: function(newWidth) {
    	this.height = newHeight;
    	this.maxY = this.minY + this.height;
    },
    

    
    checkDimensions: function(element) {

		var children = element.childNodes;
		if(!children) {
			return null;
		}
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(this.width < currentChild.offsetWidth) {
				this.width = currentChild.offsetWidth;
			}
			if(this.height < currentChild.offsetHeight) {
				this.height = currentChild.offsetHeight;
			}
			checkDimensions(currentChild);
		}
    
    },
    
    getDistance: function(x, y) {
    	var xSqrd = Math.pow(x - this.x, 2);
    	var ySqrd = Math.pow(y - this.y, 2);
    	var distance = Math.sqrt(xSqrd + ySqrd);
    	return distance;
    }
    
}
BSDTimeoutUtils = {
	DEPENDENCIES: new Array("BSDArrayUtils"),

	setTimeout: function(functionName, timeInMillis) {
		var argsString = BSDTimeoutUtils.getArgumentsString(2, arguments);
		window.setTimeout(functionName + argsString, timeInMillis);
	},

	setManagedTimeout: function(timeInMillis) {
		var argsString = BSDTimeoutUtils.getArgumentsString(1, arguments);
		window.setTimeout("BSDTimeoutUtils.handleTimeout" + argsString, timeInMillis);
	},
		
	timeoutManagers: new Object(),
	
	addTimeoutManager: function(newManager) {
		BSDTimeoutUtils.timeoutManagers[newManager.key] = newManager;
	},
	
	handleTimeout: function(managerKey, timeoutRequestId) {

		var manager = BSDTimeoutUtils.timeoutManagers[managerKey];
		if(!manager) {
			alert("Couldn't find timeout manager with key: " + managerKey);
			return;
		}
		manager.handleTimeout(timeoutRequestId);
	},
	
	getArgumentsString: function(beginningIndex, argsArray) {
		var argsString = "(";
		for(var i = beginningIndex; i < argsArray.length; i++) {
			if(i > 1) {
				argsString += ", ";
			}
			var isString = typeof(argsArray[i]) == 'string';
			if(isString) {
				argsString += "'";
			}
			argsString += argsArray[i];
			if(isString) {
				argsString += "'";
			}
		}	
		argsString += ")";
		return argsString;
	}
	
	
}
BSDDebugUtils = {
	DEPENDENCIES: new Array(),
	
	debugDOM: function(element) {  
		if(!element) {
			return;
		}
		var message = BSDDebugUtils.getElementMessage(element);		
		alert(message);		
	},
	
	getElementMessage: function(element, indentLevel) {
		if(!indentLevel) {
			indentLevel = 0;
		}
		var message = "\n";
		message += BSDDebugUtils.getIndentSpaces(indentLevel);
		message += "[";
		message += element.nodeName;
		message += "][";
		message += element.id
		message += "][";
		message += element.className
		message += "]";
		
		for(var i = 0; element && element.childNodes && element.childNodes.length > i; i++) {
			message += BSDDebugUtils.getElementMessage(element.childNodes[i], indentLevel + 1);
		}
		return message;
	},
	
	getIndentSpaces: function(indentLevel) {
		var indent = "";
		for(var i = 0; i < indentLevel; i++) {
			indent += "   ";
		}
		return indent;
	},
	
	dumpObject: function(objectToDump) {
		if(!objectToDump) {
			return "";
		} 
		var content;
		for(propertyName in objectToDump) {
			var propertyValue = objectToDump[propertyName];
			if(!content) {
				content = "";
			} else {
				content += "\n";
			}
			content += "[" + propertyName + "=" + propertyValue + "]";	
		}
		if(!content) {
			return "";
		}
		return content;

	}

}	
BSDScrollUtils = {
	DEPENDENCIES: new Array("BSDPoint", "BSDTimeoutUtils", "BSDLogUtils", "BSDDebugUtils"),
	WINDOW_IS_SCROLLING: false,

	scrollTo: function(x, y) {
		window.scrollTo(x, y);
	},
	
	scrollBy: function(xIncrement, yIncrement) {

		window.scrollBy(xIncrement, yIncrement);
	},

	scrollBySlowly: function(x, y, xIncrement, yIncrement, timeoutMillis) {
		if(BSDScrollUtils.WINDOW_IS_SCROLLING) {
			return;
		}
		BSDScrollUtils.WINDOW_IS_SCROLLING = true;
		BSDScrollUtils.scrollBySlowlyInternal(x, y, xIncrement, yIncrement, timeoutMillis, null, null);
	},
	
	scrollBySlowlyInternal: function(x, y, xIncrement, yIncrement, timeoutMillis, xCyclesRemaining, yCyclesRemaining) {
		if(xCyclesRemaining == null && xIncrement != 0) {
			xCyclesRemaining = x/xIncrement; 
		}
		if(yCyclesRemaining == null && yIncrement != 0) {
			yCyclesRemaining = y/yIncrement; 
		}

		var currentXIncrement = 0;
		var currentYIncrement = 0;
		if(xCyclesRemaining > 0) {
			currentXIncrement = xIncrement;
			x -= xIncrement;			
		}

		if(yCyclesRemaining > 0) {
			currentYIncrement = yIncrement;
			y -= yIncrement;			
		}

		xCyclesRemaining -= 1;
		yCyclesRemaining -= 1;

		if(xCyclesRemaining > 0 || yCyclesRemaining > 0) {
			BSDScrollUtils.scrollBy(currentXIncrement, currentYIncrement);
	        BSDTimeoutUtils.setTimeout("BSDScrollUtils.scrollBySlowlyInternal", timeoutMillis);

	    } else {
	    	BSDScrollUtils.WINDOW_IS_SCROLLING = false;	    	
	    }
	},
	
	getCurrentScrollPosition: function() {
		var x,y;
		if (self.pageYOffset) { // all except Explorer
			x = self.pageXOffset;
			y = self.pageYOffset;
		} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
			x = document.documentElement.scrollLeft;
			y = document.documentElement.scrollTop;
		} else if (document.body) { // all other Explorers
			x = document.body.scrollLeft;
			y = document.body.scrollTop;
		}
		return new BSDPoint(x, y);
	},
	
	getCurrentPageDimensions: function() {
		var x,y;
		if (self.innerHeight) { // all except Explorer		
			x = self.innerWidth;
			y = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode		
			x = document.documentElement.clientWidth;
			y = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			x = document.body.clientWidth;
			y = document.body.clientHeight;
		}	
		return new BSDPoint(x, y);
	},
	
	getDistanceFromTopLeftOfWindow: function(point) {
		var currentScrollPosition = BSDScrollUtils.getCurrentScrollPosition();
		var xDiff = point.x - currentScrollPosition.x;
		var yDiff = point.y - currentScrollPosition.y;
		return new BSDPoint(xDiff, yDiff);
	},

	getDistanceFromBottomRightOfWindow: function(point) {
		var currentScrollPosition = BSDScrollUtils.getCurrentScrollPosition();
		var currentPageDimensions = BSDScrollUtils.getCurrentPageDimensions();
		var xDiff = currentScrollPosition.x + currentPageDimensions.x - point.x;
		var yDiff = currentScrollPosition.y + currentPageDimensions.y - point.y;
		return new BSDPoint(xDiff, yDiff);
	},
	
	
	debugScroll: function(scrollElement) {
		if(!this.bsdScrollCount) {
			this.bsdScrollCount = 0;
		}

		this.bsdScrollCount++;
		
		BSDLogUtils.debug("Top: " + scrollElement.scrollTop + " Height: " + scrollElement.scrollHeight);









	},
	
	debugScrollTable: function(scrollTable) {
		var children = scrollTable.childNodes;
		var beginVisible = 0;
		var endVisible = 0;
		var message = "";
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			if(currentChild.nodeName == 'TBODY') {
				i = 0;
				children = currentChild.childNodes;
			} else if(currentChild.nodeName == 'TR') {
				message += "\n" + i + ": " + BSDDOMUtils.getElementStyle(currentChild, 'visibility') + " " + BSDDOMUtils.getElementStyle(currentChild, 'display');
			} else if(currentChild.nodeType == 1) {
				BSDLogUtils.error("unknown table child: " + currentChild.nodeName);
			}
		}
		BSDLogUtils.debug(message);
	}
	
			
}
BSDLocationUtils = {
	DEPENDENCIES: new Array("BSDPoint", "BSDElementPosition", "BSDScrollUtils"),

	getObjectLocationX: function(obj) {
		try {
			var curleft = 0;
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					curleft += obj.offsetLeft;
					obj = obj.offsetParent;
				}
			} else if (obj.x) {
				curleft = obj.x;
			}
			return curleft;
		} catch (err) {  

		}
	},
	
	getObjectLocationY: function(obj) {
		try {
			var curtop = 0;
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					curtop += obj.offsetTop;
					obj = obj.offsetParent;
				}
			} else if (obj.y) {
				curtop = obj.y;
			}
			return curtop;
		} catch (err) {  

		}
	},
	
	getObjectLocation: function(obj) {
		return new BSDPoint(BSDLocationUtils.getObjectLocationX(obj), BSDLocationUtils.getObjectLocationY(obj));
	},
	
	getEventPosition: function(e) {
		var posx = 0;
		var posy = 0;
		if(!e) {
		 	e = window.event;
		}
		if(e.pageX || e.pageY) {
			posx = e.pageX;
			posy = e.pageY;
		} else if(e.clientX || e.clientY) {
			posx = e.clientX + document.body.scrollLeft;
			posy = e.clientY + document.body.scrollTop;
		} else if(e.eventX || e.eventY) { //a way for us to manually set location of an event
			posx = e.eventX;
			posy = e.eventY;
		}


		var position = new BSDPoint(posx, posy);
		return position;
	},
	
	getIsAbolutelyPositioned: function(element) {
		var position = BSDDOMUtils.getElementStyle(element, 'position');
		if(position == 'absolute') {
			return true;
		}
		return false;
	},
	
	cloneElementLocation: function(source, target, adjustX, adjustY, adjustWidth, adjustHeight) {
		if(!adjustX) {
			adjustX = 0;
		}
		if(!adjustY) {
			adjustY = 0;
		}
		if(!adjustWidth) {
			adjustWidth = 0;
		}	
		if(!adjustHeight) {
			adjustHeight = 0;
		}
		var location = BSDLocationUtils.getObjectLocation(source);
	    var offsetWidth = source.offsetWidth + adjustWidth;
	    var offsetHeight = source.offsetHeight + adjustHeight;	
	    var locationX = location.x + adjustX;
	    var locationY = location.y + adjustY;
		BSDDOMUtils.changeElementStyle(target, 'left', locationX + "px");
	    BSDDOMUtils.changeElementStyle(target, 'top', locationY + "px");
	    BSDDOMUtils.changeElementStyle(target, 'width', offsetWidth + "px");
	    BSDDOMUtils.changeElementStyle(target, 'height', offsetHeight + "px");

	    var zIndex = BSDDOMUtils.getElementStyle(source, "z-index");
	    if(zIndex) {
		    BSDDOMUtils.changeElementStyle(target, 'z-index', zIndex);
		}
	},
	
	setElementLocation: function(element, point) {
		BSDDOMUtils.changeElementStyle(element, 'left', point.x + "px");
	    BSDDOMUtils.changeElementStyle(element, 'top', point.y + "px");	
	},
	
	adjustElementLocation: function(element, point) {
		var location = BSDLocationUtils.getObjectLocation(element);
		if(point.x) {
			BSDDOMUtils.changeElementStyle(element, "left", (location.x + point.x) + "px");
		}
		if(point.y) {
			BSDDOMUtils.changeElementStyle(element, "top", (location.y + point.y) + "px");
		}
	},
	
	setElementOrientation: function(element, orientation) {
		var location = new Object();
	    var width = element.offsetWidth;
	    var height = element.offsetHeight;
		if(orientation == 'top-left') {
			return; //default orientation is top-left
		} else if(orientation == 'top') {
			location.x = -width/2;
		} else if(orientation == 'top-right') {
			location.x = -width;
		} else if(orientation == 'right') {
			location.x = -width;
			location.y = -height/2;
		} else if(orientation == 'bottom-right') {
			location.x = -width;
			location.y = -height;
		} else if(orientation == 'bottom') {
			location.x = -width/2;
			location.y = -height;
		} else if(orientation == 'bottom-left') {
			location.y = -height;
		} else if(orientation == 'left') {
			location.y = -height/2;
		} else if(orientation == 'center') {
			location.x = -width/2;
			location.y = -height/2;
		} else {
			BSDLogUtils.warning("Unknown value for orientation: " + orientation);
			return;
		}
		BSDLogUtils.debug("Setting orientation: [" + location.x + "][" + location.y + "][" + orientation + "][" + width + "][" + height + "]");
		BSDLocationUtils.adjustElementLocation(element, location);
	}, 
	
	makeElementAbsolutelyPositioned: function(element, parent, retainDimensions, position) {
		var oldStyle = BSDLocationUtils.createOldStyle(element);
		element.bsdOldStyle = oldStyle;
		
	    var posX = BSDLocationUtils.getObjectLocationX(element);
	    var posY = BSDLocationUtils.getObjectLocationY(element);
	    if(position && position.x && position.y) {
	    	posX = position.x;
	    	posY = position.y;
	    }
	    var offsetWidth = element.offsetWidth;
	    var offsetHeight = element.offsetHeight;

	    if(parent) {
			posX = BSDLocationUtils.getObjectLocationX(parent);
	    	posY = BSDLocationUtils.getObjectLocationY(parent);
	  		offsetWidth = parent.offsetWidth;
	    	offsetHeight = parent.offsetHeight;
	    }
	    BSDDOMUtils.changeElementStyle(element, 'position', 'absolute');
	    BSDDOMUtils.changeElementStyle(element, 'left', posX + "px");
	    BSDDOMUtils.changeElementStyle(element, 'top', posY + "px");
	    if(!retainDimensions) {
		    BSDDOMUtils.changeElementStyle(element, 'width', offsetWidth);
		    BSDDOMUtils.changeElementStyle(element, 'height', offsetHeight);
		}
	    BSDDOMUtils.changeElementStyle(element, 'z-index', '100000');

	},
	
	makeElementNormallyPositioned: function(element, parent) {
		var position = BSDDOMUtils.getElementStyle(element, 'position');
		if(!position || position.toLowerCase() != 'absolute') {
			return;
		}

		var oldStyle = element.bsdOldStyle;
		if(!oldStyle) {
			oldStyle = BSDLocationUtils.createOldStyle(element);
		}
	    BSDDOMUtils.changeElementStyle(element, 'position', oldStyle.position);
	    BSDDOMUtils.changeElementStyle(element, 'left', oldStyle.left);
	    BSDDOMUtils.changeElementStyle(element, 'top', oldStyle.top);
	    BSDDOMUtils.changeElementStyle(element, 'width', oldStyle.width);
	    BSDDOMUtils.changeElementStyle(element, 'height', oldStyle.height);
	    BSDDOMUtils.changeElementStyle(element, 'z-index', oldStyle.zIndex);
	    element.bsdOldStyle = null;
	},
	
	createOldStyle: function(element) {
		var oldStyle = new Object();
		
		var position;
		var left;
		var top;
		var width;
		var height;
		var zIndex;
		if(element) {
			position = BSDDOMUtils.getElementStyle(element, 'position');
			left = BSDDOMUtils.getElementStyle(element, 'left');
			top = BSDDOMUtils.getElementStyle(element, 'top');
			width = BSDDOMUtils.getElementStyle(element, 'width');
			height = BSDDOMUtils.getElementStyle(element, 'height');
			zIndex = BSDDOMUtils.getElementStyle(element, 'z-index');
		}
		if(!position) {
			position = '';
		}
		if(!left) {
			left = '';
		}
		if(!top) {
			top = '';
		}
		if(!width) {
			width = '';
		}
		if(!height) {
			height = '';
		}
		if(!zIndex) {
			zIndex = '';
		}
	    oldStyle.position = position;
	    oldStyle.left = left;
	    oldStyle.top = top
	    oldStyle.width = width
	    oldStyle.height = height
	    oldStyle.zIndex = zIndex;
		return oldStyle;
	},
	
	getObjectFromParentAndLocation: function(parentElement, x, y) {
		if(!parentElement) {
			return;
		}
		var children = parentElement.childNodes;
		for(var i = 0; i < children.length; i++) {
			var currentChild = children[i];
			var position = new BSDElementPosition(currentChild);
			if(position.contains(x, y)) {
				var childContains = BSDLocationUtils.getObjectFromParentAndLocation(currentChild, x, y);
				if(childContains) {
					return childContains;
				} else {
					return currentChild;
				}
			}
		}
		return null;
	},
	
	getObjectFromParentAndClassAndLocation: function(parentElement, className, x, y, elementToIgnore) {
	    var elements = BSDDOMUtils.getObjectsByClass(className, parentElement);
	    for(var i = 0; elements && i < elements.length; i++) {
	    		var currentElement = elements[i];
			var position = new BSDElementPosition(currentElement);
			if(position.contains(x, y) && currentElement != elementToIgnore) {
			    return currentElement;
		    }
	    }	        
	},
	
	getObjectFromParentAndNodeNameAndLocation: function(parentElement, nodeName, x, y, elementToIgnore) {
	    var elements = BSDDOMUtils.getObjectsByNodeName(parentElement, nodeName);
	    for(var i = 0; elements && i < elements.length; i++) {
	    		var currentElement = elements[i];
			var position = new BSDElementPosition(currentElement);
			if(position.contains(x, y) && currentElement != elementToIgnore) {
			    return currentElement;
		    }
	    }	        
	},
	
	centerElementWithinWindow: function(element) {
		var elementPosition = new BSDElementPosition(element);
		var scrollPosition = BSDScrollUtils.getCurrentScrollPosition();
		var pageDimensions = BSDScrollUtils.getCurrentPageDimensions();
		
		var position = new Object();
		position.x = pageDimensions.x/2 - elementPosition.width/2 + scrollPosition.x;
		position.y = pageDimensions.y/2 - elementPosition.height/2 + scrollPosition.y;
		
		BSDLocationUtils.setElementLocation(element, position);
	
	},
	
	positionElementWithinWindow: function(element, keepDimensionFixed, bufferSizeX, bufferSizeY) {
		var elementPosition = new BSDElementPosition(element);
		var scrollPosition = BSDScrollUtils.getCurrentScrollPosition();
		var pageDimensions = BSDScrollUtils.getCurrentPageDimensions();
		if(!bufferSizeX) {
			bufferSizeX = 20; //provide an extra margin of 20px
		}
		if(!bufferSizeY) {
			bufferSizeY = 20;
		}
		var newX;
		var newY;
		var newWidth;
		var newHeight;



		if(elementPosition.minX < scrollPosition.x) {
			newX = scrollPosition.x;
		}
		if(elementPosition.minY < scrollPosition.y) {
			newY = scrollPosition.y;
		}
		var maxXDelta = elementPosition.maxX - (scrollPosition.x + pageDimensions.x) + bufferSizeX; 
		if(maxXDelta > 0) {
			if(!newX) {
				newX = elementPosition.minX - maxXDelta - 1;
			} else if(newX) {
				newWidth = elementPosition.width - (maxXDelta + newX);
			}
		} 
		
		var maxYDelta = elementPosition.maxY - (scrollPosition.y + pageDimensions.y) + bufferSizeY; 

		if(maxYDelta > 0) {
			if(!newY) {
				newY = elementPosition.minY - maxYDelta - 1;
			} else if(newY) {
				newHeight = elementPosition.height - (maxYDelta + newY);
			}
		}
		
		if(newX < scrollPosition.x) {
			newWidth = elementPosition.width - (scrollPosition.x - newX) - bufferSizeX; 
			newX = scrollPosition.x + 1;
		}
		if(newY < scrollPosition.y) {
			newHeight = elementPosition.height - (scrollPosition.y - newY) - bufferSizeY;
			newY = scrollPosition.y + 1;
		}
		
		
		if(newX || newY) {

			var newTopLeft = new BSDPoint();
			if(newX) {
				newTopLeft.x = newX
			} else {
				newTopLeft.x = elementPosition.minX;
			}
			if(newY) {
				newTopLeft.y = newY;				
			} else {
				newTopLeft.y = elementPosition.minY;
			}

			BSDLocationUtils.setElementLocation(element, newTopLeft);
		}
		
		if(keepDimensionFixed) {
			return;
		}

		if(newWidth) {
		    BSDDOMUtils.changeElementStyle(element, 'width', newWidth);
		}
		if(newHeight) {
			BSDDOMUtils.changeElementStyle(element, 'height', newHeight);
		}
		
	}

}
// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (KCMAjaxGui == null) var KCMAjaxGui = {};
KCMAjaxGui._path = '/ajax';
KCMAjaxGui.doNavigation = function(p0, p1, callback) {
  dwr.engine._execute(KCMAjaxGui._path, 'KCMAjaxGui', 'doNavigation', p0, p1, callback);
}
KCMAjaxGui.doRendering = function(p0, p1, callback) {
  dwr.engine._execute(KCMAjaxGui._path, 'KCMAjaxGui', 'doRendering', p0, p1, callback);
}
KCMAjaxGui.getRenderedContent = function(p0, p1, callback) {
  dwr.engine._execute(KCMAjaxGui._path, 'KCMAjaxGui', 'getRenderedContent', p0, p1, callback);
}
/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Declare an object to which we can add real functions.
 */
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

/**
 * Set an alternative error handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setErrorHandler = function(handler) {
  dwr.engine._errorHandler = handler;
};

/**
 * Set an alternative warning handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setWarningHandler = function(handler) {
  dwr.engine._warningHandler = handler;
};

/**
 * Setter for the text/html handler - what happens if a DWR request gets an HTML
 * reply rather than the expected Javascript. Often due to login timeout
 */
dwr.engine.setTextHtmlHandler = function(handler) {
  dwr.engine._textHtmlHandler = handler;
}

/**
 * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setTimeout = function(timeout) {
  dwr.engine._timeout = timeout;
};

/**
 * The Pre-Hook is called before any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPreHook = function(handler) {
  dwr.engine._preHook = handler;
};

/**
 * The Post-Hook is called after any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPostHook = function(handler) {
  dwr.engine._postHook = handler;
};

/**
 * Custom headers for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setHeaders = function(headers) {
  dwr.engine._headers = headers;
};

/**
 * Custom parameters for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setParameters = function(parameters) {
  dwr.engine._parameters = parameters;
};

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.XMLHttpRequest = 1;

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.IFrame = 2;

/** XHR remoting type constant. See dwr.engine.setRpcType() */
dwr.engine.ScriptTag = 3;

/**
 * Set the preferred remoting type.
 * @param newType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setRpcType = function(newType) {
  if (newType != dwr.engine.XMLHttpRequest && newType != dwr.engine.IFrame && newType != dwr.engine.ScriptTag) {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidRpcType", message:"RpcType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag" });
    return;
  }
  dwr.engine._rpcType = newType;
};

/**
 * Which HTTP method do we use to send results? Must be one of "GET" or "POST".
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setHttpMethod = function(httpMethod) {
  if (httpMethod != "GET" && httpMethod != "POST") {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidHttpMethod", message:"Remoting method must be one of GET or POST" });
    return;
  }
  dwr.engine._httpMethod = httpMethod;
};

/**
 * Ensure that remote calls happen in the order in which they were sent? (Default: false)
 * @see getahead.org/dwr/browser/engine/ordering
 */
dwr.engine.setOrdered = function(ordered) {
  dwr.engine._ordered = ordered;
};

/**
 * Do we ask the XHR object to be asynchronous? (Default: true)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setAsync = function(async) {
  dwr.engine._async = async;
};

/**
 * Does DWR poll the server for updates? (Default: false)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
  if (activeReverseAjax) {
    // Bail if we are already started
    if (dwr.engine._activeReverseAjax) return;
    dwr.engine._activeReverseAjax = true;
    dwr.engine._poll();
  }
  else {
    // Can we cancel an existing request?
    if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) dwr.engine._pollReq.abort();
    dwr.engine._activeReverseAjax = false;
  }
  // TODO: in iframe mode, if we start, stop, start then the second start may
  // well kick off a second iframe while the first is still about to return
  // we should cope with this but we don't
};

/**
 * Set the preferred polling type.
 * @param newPollType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setPollType = function(newPollType) {
  if (newPollType != dwr.engine.XMLHttpRequest && newPollType != dwr.engine.IFrame) {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidPollType", message:"PollType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame"  });
    return;
  }
  dwr.engine._pollType = newPollType;
};

/**
 * The default message handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultErrorHandler = function(message, ex) {
  dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);

  if (message == null || message == "") alert("A server error has occured. More information may be available in the console.");
  // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
  else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
  else alert(message);
};

/**
 * The default warning handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultWarningHandler = function(message, ex) {
  dwr.engine._debug(message);
};

/**
 * For reduced latency you can group several remote calls together using a batch.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.beginBatch = function() {
  if (dwr.engine._batch) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
    return;
  }
  dwr.engine._batch = dwr.engine._createBatch();
};

/**
 * Finished grouping a set of remote calls together. Go and execute them all.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.endBatch = function(options) {
  var batch = dwr.engine._batch;
  if (batch == null) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
    return;
  }
  dwr.engine._batch = null;
  if (batch.map.callCount == 0) return;

  // The hooks need to be merged carefully to preserve ordering
  if (options) dwr.engine._mergeBatch(batch, options);

  // In ordered mode, we don't send unless the list of sent items is empty
  if (dwr.engine._ordered && dwr.engine._batchesLength != 0) {
    dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
  }
  else {
    dwr.engine._sendData(batch);
  }
};

/** @deprecated */
dwr.engine.setPollMethod = function(type) { dwr.engine.setPollType(type); };
dwr.engine.setMethod = function(type) { dwr.engine.setRpcType(type); };
dwr.engine.setVerb = function(verb) { dwr.engine.setHttpMethod(verb); };

//==============================================================================
// Only private stuff below here
//==============================================================================

/** The original page id sent from the server */
dwr.engine._origScriptSessionId = "1642613D154AFE15F0A440A8DC63EBC2";

/** The session cookie name */
dwr.engine._sessionCookieName = "JSESSIONID"; // JSESSIONID

/** Is GET enabled for the benefit of Safari? */
dwr.engine._allowGetForSafariButMakeForgeryEasier = "false";

/** The script prefix to strip in the case of scriptTagProtection. */
dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";

/** The default path to the DWR servlet */
dwr.engine._defaultPath = "/ajax";

/** The read page id that we calculate */
dwr.engine._scriptSessionId = null;

/** The function that we use to fetch/calculate a session id */
dwr.engine._getScriptSessionId = function() {
  if (dwr.engine._scriptSessionId == null) {
    dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000);
  }
  return dwr.engine._scriptSessionId;
};

/** A function to call if something fails. */
dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;

/** For debugging when something unexplained happens. */
dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;

/** A function to be called before requests are marshalled. Can be null. */
dwr.engine._preHook = null;

/** A function to be called after replies are received. Can be null. */
dwr.engine._postHook = null;

/** An map of the batches that we have sent and are awaiting a reply on. */
dwr.engine._batches = {};

/** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
dwr.engine._batchesLength = 0;

/** In ordered mode, the array of batches waiting to be sent */
dwr.engine._batchQueue = [];

/** What is the default rpc type */
dwr.engine._rpcType = dwr.engine.XMLHttpRequest;

/** What is the default remoting method (ie GET or POST) */
dwr.engine._httpMethod = "POST";

/** Do we attempt to ensure that calls happen in the order in which they were sent? */
dwr.engine._ordered = false;

/** Do we make the calls async? */
dwr.engine._async = true;

/** The current batch (if we are in batch mode) */
dwr.engine._batch = null;

/** The global timeout */
dwr.engine._timeout = 0;

/** ActiveX objects to use when we want to convert an xml string into a DOM object. */
dwr.engine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];

/** The ActiveX objects to use when we want to do an XMLHttpRequest call. */
dwr.engine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];

/** Are we doing comet or polling? */
dwr.engine._activeReverseAjax = false;

/** What is the default polling type */
dwr.engine._pollType = dwr.engine.XMLHttpRequest;
//dwr.engine._pollType = dwr.engine.IFrame;

/** The iframe that we are using to poll */
dwr.engine._outstandingIFrames = [];

/** The xhr object that we are using to poll */
dwr.engine._pollReq = null;

/** How many milliseconds between internal comet polls */
dwr.engine._pollCometInterval = 200;

/** How many times have we re-tried to poll? */
dwr.engine._pollRetries = 0;
dwr.engine._maxPollRetries = 0;

/** Do we do a document.reload if we get a text/html reply? */
dwr.engine._textHtmlHandler = null;

/** If you wish to send custom headers with every request */
dwr.engine._headers = null;

/** If you wish to send extra custom request parameters with each request */
dwr.engine._parameters = null;

/** Undocumented interceptors - do not use */
dwr.engine._postSeperator = "\n";
dwr.engine._defaultInterceptor = function(data) {return data;}
dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;

/** Batch ids allow us to know which batch the server is answering */
dwr.engine._nextBatchId = 0;

/** A list of the properties that need merging from calls to a batch */
dwr.engine._propnames = [ "rpcType", "httpMethod", "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];

/** Do we stream, or can be hacked to do so? */
dwr.engine._partialResponseNo = 0;
dwr.engine._partialResponseYes = 1;
dwr.engine._partialResponseFlush = 2;

/**
 * @private Send a request. Called by the Javascript interface stub
 * @param path part of URL after the host and before the exec bit without leading or trailing /s
 * @param scriptName The class to execute
 * @param methodName The method on said class to execute
 * @param func The callback function to which any returned data should be passed
 *       if this is null, any returned data will be ignored
 * @param vararg_params The parameters to pass to the above class
 */
dwr.engine._execute = function(path, scriptName, methodName, vararg_params) {
  var singleShot = false;
  if (dwr.engine._batch == null) {
    dwr.engine.beginBatch();
    singleShot = true;
  }
  var batch = dwr.engine._batch;
  // To make them easy to manipulate we copy the arguments into an args array
  var args = [];
  for (var i = 0; i < arguments.length - 3; i++) {
    args[i] = arguments[i + 3];
  }
  // All the paths MUST be to the same servlet
  if (batch.path == null) {
    batch.path = path;
  }
  else {
    if (batch.path != path) {
      dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
      return;
    }
  }
  // From the other params, work out which is the function (or object with
  // call meta-data) and which is the call parameters
  var callData;
  var lastArg = args[args.length - 1];
  if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() };
  else callData = args.pop();

  // Merge from the callData into the batch
  dwr.engine._mergeBatch(batch, callData);
  batch.handlers[batch.map.callCount] = {
    exceptionHandler:callData.exceptionHandler,
    callback:callData.callback
  };

  // Copy to the map the things that need serializing
  var prefix = "c" + batch.map.callCount + "-";
  batch.map[prefix + "scriptName"] = scriptName;
  batch.map[prefix + "methodName"] = methodName;
  batch.map[prefix + "id"] = batch.map.callCount;
  for (i = 0; i < args.length; i++) {
    dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i);
  }

  // Now we have finished remembering the call, we incr the call count
  batch.map.callCount++;
  if (singleShot) dwr.engine.endBatch();
};

/** @private Poll the server to see if there is any data waiting */
dwr.engine._poll = function(overridePath) {
  if (!dwr.engine._activeReverseAjax) return;

  var batch = dwr.engine._createBatch();
  batch.map.id = 0; // TODO: Do we need this??
  batch.map.callCount = 1;
  batch.isPoll = true;
  if (navigator.userAgent.indexOf("Gecko/") != -1) {
    batch.rpcType = dwr.engine._pollType;
    batch.map.partialResponse = dwr.engine._partialResponseYes;
  }
  else if (document.all) {
    batch.rpcType = dwr.engine.IFrame;
    batch.map.partialResponse = dwr.engine._partialResponseFlush;
  }
  else {
    batch.rpcType = dwr.engine._pollType;
    batch.map.partialResponse = dwr.engine._partialResponseNo;
  }
  batch.httpMethod = "POST";
  batch.async = true;
  batch.timeout = 0;
  batch.path = (overridePath) ? overridePath : dwr.engine._defaultPath;
  batch.preHooks = [];
  batch.postHooks = [];
  batch.errorHandler = dwr.engine._pollErrorHandler;
  batch.warningHandler = dwr.engine._pollErrorHandler;
  batch.handlers[0] = {
    callback:function(pause) {
      dwr.engine._pollRetries = 0;
      setTimeout("dwr.engine._poll()", pause);
    }
  };

  // Send the data
  dwr.engine._sendData(batch);
  if (batch.rpcType == dwr.engine.XMLHttpRequest) {
  // if (batch.map.partialResponse != dwr.engine._partialResponseNo) {
    dwr.engine._checkCometPoll();
  }
};

/** Try to recover from polling errors */
dwr.engine._pollErrorHandler = function(msg, ex) {
  // if anything goes wrong then just silently try again (up to 3x) after 10s
  dwr.engine._pollRetries++;
  dwr.engine._debug("Reverse Ajax poll failed (pollRetries=" + dwr.engine._pollRetries + "): " + ex.name + " : " + ex.message);
  if (dwr.engine._pollRetries < dwr.engine._maxPollRetries) {
    setTimeout("dwr.engine._poll()", 10000);
  }
  else {
    dwr.engine._debug("Giving up.");
  }
};

/** @private Generate a new standard batch */
dwr.engine._createBatch = function() {
  var batch = {
    map:{
      callCount:0,
      page:window.location.pathname + window.location.search,
      httpSessionId:dwr.engine._getJSessionId(),
      scriptSessionId:dwr.engine._getScriptSessionId()
    },
    charsProcessed:0, paramCount:0,
    headers:[], parameters:[],
    isPoll:false, headers:{}, handlers:{}, preHooks:[], postHooks:[],
    rpcType:dwr.engine._rpcType,
    httpMethod:dwr.engine._httpMethod,
    async:dwr.engine._async,
    timeout:dwr.engine._timeout,
    errorHandler:dwr.engine._errorHandler,
    warningHandler:dwr.engine._warningHandler,
    textHtmlHandler:dwr.engine._textHtmlHandler
  };
  if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook);
  if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook);
  var propname, data;
  if (dwr.engine._headers) {
    for (propname in dwr.engine._headers) {
      data = dwr.engine._headers[propname];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (dwr.engine._parameters) {
    for (propname in dwr.engine._parameters) {
      data = dwr.engine._parameters[propname];
      if (typeof data != "function") batch.parameters[propname] = data;
    }
  }
  return batch;
}

/** @private Take further options and merge them into */
dwr.engine._mergeBatch = function(batch, overrides) {
  var propname, data;
  for (var i = 0; i < dwr.engine._propnames.length; i++) {
    propname = dwr.engine._propnames[i];
    if (overrides[propname] != null) batch[propname] = overrides[propname];
  }
  if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
  if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
  if (overrides.headers) {
    for (propname in overrides.headers) {
      data = overrides.headers[propname];
     