
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 {
			alert("ERROR: Couldn't find parent with class: " + className);
		}		
	},

	getParentObjectById: function(element, objectId) {
		if(element.id == objectId) {
			return element;
		} else if(element.parentNode) {
			return this.getParentObjectById(element.parentNode, objectId);
		} else {
			alert("ERROR: Couldn't find parent with id: " + id);
		}		
	},

	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(!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);
	}

}

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;
	}
	
	
}

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;
	}

}

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 = "F1897C2DEFEE3FC55227E1876AEDF70C";

/** 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];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (overrides.parameters) {
    for (propname in overrides.parameters) {
      data = overrides.parameters[propname];
      if (typeof data != "function") batch.map["p-" + propname] = "" + data;
    }
  }
};

/** @private What is our session id? */
dwr.engine._getJSessionId =  function() {
  var cookies = document.cookie.split(';');
  for (var i = 0; i < cookies.length; i++) {
    var cookie = cookies[i];
    while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
    if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
      return cookie.substring(11, cookie.length);
    }
  }
  return "";
}

/** @private Check for reverse Ajax activity */
dwr.engine._checkCometPoll = function() {
  for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
    var text = "";
    var iframe = dwr.engine._outstandingIFrames[i];
    try {
      text = dwr.engine._getTextFromCometIFrame(iframe);
    }
    catch (ex) {
      dwr.engine._handleWarning(iframe.batch, ex);
    }
    if (text != "") dwr.engine._processCometResponse(text, iframe.batch);
  }
  if (dwr.engine._pollReq) {
    var req = dwr.engine._pollReq;
    var text = req.responseText;
    dwr.engine._processCometResponse(text, req.batch);
  }

  // If the poll resources are still there, come back again
  if (dwr.engine._outstandingIFrames.length > 0 || dwr.engine._pollReq) {
    setTimeout("dwr.engine._checkCometPoll()", dwr.engine._pollCometInterval);
  }
};

/** @private Extract the whole (executed an all) text from the current iframe */
dwr.engine._getTextFromCometIFrame = function(frameEle) {
  var body = frameEle.contentWindow.document.body;
  if (body == null) return "";
  var text = body.innerHTML;
  // We need to prevent IE from stripping line feeds
  if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
    text = text.substring(5, text.length - 7);
  }
  return text;
};

/** @private Some more text might have come in, test and execute the new stuff */
dwr.engine._processCometResponse = function(response, batch) {
  if (batch.charsProcessed == response.length) return;
  if (response.length == 0) {
    batch.charsProcessed = 0;
    return;
  }

  var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
  if (firstStartTag == -1) {
    // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
    batch.charsProcessed = response.length;
    return;
  }
  // if (firstStartTag > 0) {
  //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
  // }

  var lastEndTag = response.lastIndexOf("//#DWR-END#");
  if (lastEndTag == -1) {
    // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
    return;
  }

  // Skip the end tag too for next time, remembering CR and LF
  if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
    batch.charsProcessed = lastEndTag + 13;
  }
  else {
    batch.charsProcessed = lastEndTag + 11;
  }

  var exec = response.substring(firstStartTag + 13, lastEndTag);

  dwr.engine._receivedBatch = batch;
  dwr.engine._eval(exec);
  dwr.engine._receivedBatch = null;
};

/** @private Actually send the block of data in the batch object. */
dwr.engine._sendData = function(batch) {
  batch.map.batchId = dwr.engine._nextBatchId++;
  dwr.engine._batches[batch.map.batchId] = batch;
  dwr.engine._batchesLength++;
  batch.completed = false;

  for (var i = 0; i < batch.preHooks.length; i++) {
    batch.preHooks[i]();
  }
  batch.preHooks = null;
  // Set a timeout
  if (batch.timeout && batch.timeout != 0) {
    batch.interval = setInterval(function() { dwr.engine._abortRequest(batch); }, batch.timeout);
  }
  // Get setup for XMLHttpRequest if possible
  if (batch.rpcType == dwr.engine.XMLHttpRequest) {
    if (window.XMLHttpRequest) {
      batch.req = new XMLHttpRequest();
    }
    // IE5 for the mac claims to support window.ActiveXObject, but throws an error when it's used
    else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
      batch.req = dwr.engine._newActiveXObject(dwr.engine._XMLHTTP);
    }
  }

  var prop, request;
  if (batch.req) {
    // Proceed using XMLHttpRequest
    if (batch.async) {
      batch.req.onreadystatechange = function() { dwr.engine._stateChange(batch); };
    }
    // If we're polling, record this for monitoring
    if (batch.isPoll) {
      dwr.engine._pollReq = batch.req;
      // In IE XHR is an ActiveX control so you can't augment it like this
      // however batch.isPoll uses IFrame on IE so were safe here
      batch.req.batch = batch;
    }
    // Workaround for Safari 1.x POST bug
    var indexSafari = navigator.userAgent.indexOf("Safari/");
    if (indexSafari >= 0) {
      var version = navigator.userAgent.substring(indexSafari + 7);
      if (parseInt(version, 10) < 400) {
        if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") batch.httpMethod = "GET";
        else dwr.engine._handleWarning(batch, { name:"dwr.engine.oldSafari", message:"Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier." });
      }
    }
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    try {
      batch.req.open(batch.httpMethod, request.url, batch.async);
      try {
        for (prop in batch.headers) {
          var value = batch.headers[prop];
          if (typeof value == "string") batch.req.setRequestHeader(prop, value);
        }
        if (!batch.headers["Content-Type"]) batch.req.setRequestHeader("Content-Type", "text/plain");
      }
      catch (ex) {
        dwr.engine._handleWarning(batch, ex);
      }
      batch.req.send(request.body);
      if (!batch.async) dwr.engine._stateChange(batch);
    }
    catch (ex) {
      dwr.engine._handleError(batch, ex);
    }
  }
  else if (batch.rpcType != dwr.engine.ScriptTag) {
    // Proceed using iframe
    var idname = batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map["c0-id"];
    batch.div = document.createElement("div");
    batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "'></iframe>";
    document.body.appendChild(batch.div);
    batch.iframe = document.getElementById(idname);
    batch.iframe.batch = batch;
    batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
    if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe);
    request = dwr.engine._constructRequest(batch);
    if (batch.httpMethod == "GET") {
      batch.iframe.setAttribute("src", request.url);
      // document.body.appendChild(batch.iframe);
    }
    else {
      batch.form = document.createElement("form");
      batch.form.setAttribute("id", "dwr-form");
      batch.form.setAttribute("action", request.url);
      batch.form.setAttribute("target", idname);
      batch.form.target = idname;
      batch.form.setAttribute("method", batch.httpMethod);
      for (prop in batch.map) {
        var value = batch.map[prop];
        if (typeof value != "function") {
          var formInput = document.createElement("input");
          formInput.setAttribute("type", "hidden");
          formInput.setAttribute("name", prop);
          formInput.setAttribute("value", value);
          batch.form.appendChild(formInput);
        }
      }
      document.body.appendChild(batch.form);
      batch.form.submit();
    }
  }
  else {
    batch.httpMethod = "GET"; // There's no such thing as ScriptTag using POST
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    batch.script = document.createElement("script");
    batch.script.id = "dwr-st-" + batch.map["c0-id"];
    batch.script.src = request.url;
    document.body.appendChild(batch.script);
  }
};

dwr.engine._ModePlainCall = "/call/plaincall/";
dwr.engine._ModeHtmlCall = "/call/htmlcall/";
dwr.engine._ModePlainPoll = "/call/plainpoll/";
dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";

/** @private Work out what the URL should look like */
dwr.engine._constructRequest = function(batch) {
  // A quick string to help people that use web log analysers
  var request = { url:batch.path + batch.mode, body:null };
  if (batch.isPoll == true) {
    request.url += "ReverseAjax.dwr";
  }
  else if (batch.map.callCount == 1) {
    request.url += batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr";
  }
  else {
    request.url += "Multiple." + batch.map.callCount + ".dwr";
  }
  // Play nice with url re-writing
  var sessionMatch = location.href.match(/jsessionid=([^?]+)/);
  if (sessionMatch != null) {
    request.url += ";jsessionid=" + sessionMatch[1];
  }

  var prop;
  if (batch.httpMethod == "GET") {
    // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
    // to a string in the loop below so we do it manually here.
    batch.map.callCount = "" + batch.map.callCount;
    request.url += "?";
    for (prop in batch.map) {
      if (typeof batch.map[prop] != "function") {
        request.url += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&";
      }
    }
    request.url = request.url.substring(0, request.url.length - 1);
  }
  else {
    // PERFORMANCE: for iframe mode this is thrown away.
    request.body = "";
    for (prop in batch.map) {
      if (typeof batch.map[prop] != "function") {
        request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
      }
    }
    request.body = dwr.engine._contentRewriteHandler(request.body);
  }
  request.url = dwr.engine._urlRewriteHandler(request.url);
  return request;
};

/** @private Called by XMLHttpRequest to indicate that something has happened */
dwr.engine._stateChange = function(batch) {
  var toEval;

  if (batch.completed) {
    dwr.engine._debug("Error: _stateChange() with batch.completed");
    return;
  }

  var req = batch.req;
  try {
    if (req.readyState != 4) return;
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
    // It's broken - clear up and forget this call
    dwr.engine._clearUp(batch);
    return;
  }

  try {
    var reply = req.responseText;
    reply = dwr.engine._replyRewriteHandler(reply);
    var status = req.status; // causes Mozilla to except on page moves

    if (reply == null || reply == "") {
      dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
    }
    else if (status != 200) {
      dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
    }
    else {
      var contentType = req.getResponseHeader("Content-Type");
      if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
        if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
          batch.textHtmlHandler();
        }
        else {
          dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
        }
      }
      else {
        // Comet replies might have already partially executed
        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
          dwr.engine._processCometResponse(reply, batch);
        }
        else {
          if (reply.search("//#DWR") == -1) {
            dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
          }
          else {
            toEval = reply;
          }
        }
      }
    }
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
  }

  dwr.engine._callPostHooks(batch);

  // Outside of the try/catch so errors propogate normally:
  dwr.engine._receivedBatch = batch;
  if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
  dwr.engine._eval(toEval);
  dwr.engine._receivedBatch = null;

  dwr.engine._clearUp(batch);
};

/** @private Called by the server: Execute a callback */
dwr.engine._remoteHandleCallback = function(batchId, callId, reply) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) {
    dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
    return;
  }
  // Error handlers inside here indicate an error that is nothing to do
  // with DWR so we handle them differently.
  try {
    var handlers = batch.handlers[callId];
    if (!handlers) {
      dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
    }
    else if (typeof handlers.callback == "function") handlers.callback(reply);
  }
  catch (ex) {
    dwr.engine._handleError(batch, ex);
  }
};

/** @private Called by the server: Handle an exception for a call */
dwr.engine._remoteHandleException = function(batchId, callId, ex) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) { dwr.engine._debug("Warning: null batch in remoteHandleException", true); return; }
  var handlers = batch.handlers[callId];
  if (handlers == null) { dwr.engine._debug("Warning: null handlers in remoteHandleException", true); return; }
  if (ex.message == undefined) ex.message = "";
  if (typeof handlers.exceptionHandler == "function") handlers.exceptionHandler(ex.message, ex);
  else if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
};

/** @private Called by the server: The whole batch is broken */
dwr.engine._remoteHandleBatchException = function(ex, batchId) {
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: Reverse ajax should not be used */
dwr.engine._remotePollCometDisabled = function(ex, batchId) {
  dwr.engine.setActiveReverseAjax(false);
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: An IFrame reply is about to start */
dwr.engine._remoteBeginIFrameResponse = function(iframe, batchId) {
  if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
  dwr.engine._callPostHooks(dwr.engine._receivedBatch);
};

/** @private Called by the server: An IFrame reply is just completing */
dwr.engine._remoteEndIFrameResponse = function(batchId) {
  dwr.engine._clearUp(dwr.engine._receivedBatch);
  dwr.engine._receivedBatch = null;
};

/** @private This is a hack to make the context be this window */
dwr.engine._eval = function(script) {
  if (script == null) return null;
  if (script == "") { dwr.engine._debug("Warning: blank script", true); return null; }
  // dwr.engine._debug("Exec: [" + script + "]", true);
  return eval(script);
};

/** @private Called as a result of a request timeout */
dwr.engine._abortRequest = function(batch) {
  if (batch && !batch.completed) {
    clearInterval(batch.interval);
    dwr.engine._clearUp(batch);
    if (batch.req) batch.req.abort();
    dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
  }
};

/** @private call all the post hooks for a batch */
dwr.engine._callPostHooks = function(batch) {
  if (batch.postHooks) {
    for (var i = 0; i < batch.postHooks.length; i++) {
      batch.postHooks[i]();
    }
    batch.postHooks = null;
  }
}

/** @private A call has finished by whatever means and we need to shut it all down. */
dwr.engine._clearUp = function(batch) {
  if (!batch) { dwr.engine._debug("Warning: null batch in dwr.engine._clearUp()", true); return; }
  if (batch.completed == "true") { dwr.engine._debug("Warning: Double complete", true); return; }

  // IFrame tidyup
  if (batch.div) batch.div.parentNode.removeChild(batch.div);
  if (batch.iframe) {
    // If this is a poll frame then stop comet polling
    for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
      if (dwr.engine._outstandingIFrames[i] == batch.iframe) {
        dwr.engine._outstandingIFrames.splice(i, 1);
      }
    }
    batch.iframe.parentNode.removeChild(batch.iframe);
  }
  if (batch.form) batch.form.parentNode.removeChild(batch.form);

  // XHR tidyup: avoid IE handles increase
  if (batch.req) {
    // If this is a poll frame then stop comet polling
    if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null;
    delete batch.req;
  }

  if (batch.map && batch.map.batchId) {
    delete dwr.engine._batches[batch.map.batchId];
    dwr.engine._batchesLength--;
  }

  batch.completed = true;

  // If there is anything on the queue waiting to go out, then send it.
  // We don't need to check for ordered mode, here because when ordered mode
  // gets turned off, we still process *waiting* batches in an ordered way.
  if (dwr.engine._batchQueue.length != 0) {
    var sendbatch = dwr.engine._batchQueue.shift();
    dwr.engine._sendData(sendbatch);
  }
};

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleError = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
  else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
  dwr.engine._clearUp(batch);
};

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleWarning = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
  else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
  dwr.engine._clearUp(batch);
};

/**
 * @private Marshall a data item
 * @param batch A map of variables to how they have been marshalled
 * @param referto An array of already marshalled variables to prevent recurrsion
 * @param data The data to be marshalled
 * @param name The name of the data being marshalled
 */
dwr.engine._serializeAll = function(batch, referto, data, name) {
  if (data == null) {
    batch.map[name] = "null:null";
    return;
  }

  switch (typeof data) {
  case "boolean":
    batch.map[name] = "boolean:" + data;
    break;
  case "number":
    batch.map[name] = "number:" + data;
    break;
  case "string":
    batch.map[name] = "string:" + encodeURIComponent(data);
    break;
  case "object":
    if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data);
    else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data;
    else if (data instanceof Number) batch.map[name] = "Number:" + data;
    else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime();
    else if (data && data.join) batch.map[name] = dwr.engine._serializeArray(batch, referto, data, name);
    else batch.map[name] = dwr.engine._serializeObject(batch, referto, data, name);
    break;
  case "function":
    // We just ignore functions.
    break;
  default:
    dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
    batch.map[name] = "default:" + data;
    break;
  }
};

/** @private Have we already converted this object? */
dwr.engine._lookup = function(referto, data, name) {
  var lookup;
  // Can't use a map: getahead.org/ajax/javascript-gotchas
  for (var i = 0; i < referto.length; i++) {
    if (referto[i].data == data) {
      lookup = referto[i];
      break;
    }
  }
  if (lookup) return "reference:" + lookup.name;
  referto.push({ data:data, name:name });
  return null;
};

/** @private Marshall an object */
dwr.engine._serializeObject = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  // This check for an HTML is not complete, but is there a better way?
  // Maybe we should add: data.hasChildNodes typeof "function" == true
  if (data.nodeName && data.nodeType) {
    return dwr.engine._serializeXml(batch, referto, data, name);
  }

  // treat objects as an associative arrays
  var reply = "Object_" + dwr.engine._getObjectClassName(data) + ":{";
  var element;
  for (element in data) {
    if (typeof data[element] != "function") {
      batch.paramCount++;
      var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
      dwr.engine._serializeAll(batch, referto, data[element], childName);

      reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
    }
  }

  if (reply.substring(reply.length - 2) == ", ") {
    reply = reply.substring(0, reply.length - 2);
  }
  reply += "}";

  return reply;
};

/** @private Returns the classname of supplied argument obj */
dwr.engine._errorClasses = { "Error":Error, "EvalError":EvalError, "RangeError":RangeError, "ReferenceError":ReferenceError, "SyntaxError":SyntaxError, "TypeError":TypeError, "URIError":URIError };
dwr.engine._getObjectClassName = function(obj) {
  // Try to find the classname by stringifying the object's constructor
  // and extract <class> from "function <class>".
  if (obj && obj.constructor && obj.constructor.toString)
  {
    var str = obj.constructor.toString();
    var regexpmatch = str.match(/function\s+(\w+)/);
    if (regexpmatch && regexpmatch.length == 2) {
      return regexpmatch[1];
    }
  }

  // Now manually test against the core Error classes, as these in some 
  // browsers successfully match to the wrong class in the 
  // Object.toString() test we will do later
  if (obj && obj.constructor) {
	for (var errorname in dwr.engine._errorClasses) {
      if (obj.constructor == dwr.engine._errorClasses[errorname]) return errorname;
    }
  }

  // Try to find the classname by calling Object.toString() on the object
  // and extracting <class> from "[object <class>]"
  if (obj) {
    var str = Object.prototype.toString.call(obj);
    var regexpmatch = str.match(/\[object\s+(\w+)/);
    if (regexpmatch && regexpmatch.length==2) {
      return regexpmatch[1];
    }
  }

  // Supplied argument was probably not an object, but what is better?
  return "Object";
};

/** @private Marshall an object */
dwr.engine._serializeXml = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  var output;
  if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
  else if (data.toXml) output = data.toXml;
  else output = data.innerHTML;

  return "XML:" + encodeURIComponent(output);
};

/** @private Marshall an array */
dwr.engine._serializeArray = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  var reply = "Array:[";
  for (var i = 0; i < data.length; i++) {
    if (i != 0) reply += ",";
    batch.paramCount++;
    var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
    dwr.engine._serializeAll(batch, referto, data[i], childName);
    reply += "reference:";
    reply += childName;
  }
  reply += "]";

  return reply;
};

/** @private Convert an XML string into a DOM object. */
dwr.engine._unserializeDocument = function(xml) {
  var dom;
  if (window.DOMParser) {
    var parser = new DOMParser();
    dom = parser.parseFromString(xml, "text/xml");
    if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
      var message = dom.documentElement.firstChild.data;
      message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
      throw message;
    }
    return dom;
  }
  else if (window.ActiveXObject) {
    dom = dwr.engine._newActiveXObject(dwr.engine._DOMDocument);
    dom.loadXML(xml); // What happens on parse fail with IE?
    return dom;
  }
  else {
    var div = document.createElement("div");
    div.innerHTML = xml;
    return div;
  }
};

/** @param axarray An array of strings to attempt to create ActiveX objects from */
dwr.engine._newActiveXObject = function(axarray) {
  var returnValue;  
  for (var i = 0; i < axarray.length; i++) {
    try {
      returnValue = new ActiveXObject(axarray[i]);
      break;
    }
    catch (ex) { /* ignore */ }
  }
  return returnValue;
};

/** @private Used internally when some message needs to get to the programmer */
dwr.engine._debug = function(message, stacktrace) {
  var written = false;
  try {
    if (window.console) {
      if (stacktrace && window.console.trace) window.console.trace();
      window.console.log(message);
      written = true;
    }
    else if (window.opera && window.opera.postError) {
      window.opera.postError(message);
      written = true;
    }
  }
  catch (ex) { /* ignore */ }

  if (!written) {
    var debug = document.getElementById("dwr-debug");
    if (debug) {
      var contents = message + "<br/>" + debug.innerHTML;
      if (contents.length > 2048) contents = contents.substring(0, 2048);
      debug.innerHTML = contents;
    }
  }
};

/*
 * 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.util == null) dwr.util = {};
if (DWRUtil == null) var DWRUtil = dwr.util;

/** @private The flag we use to decide if we should escape html */
dwr.util._escapeHtml = true;

/**
 * Set the global escapeHtml flag
 */
dwr.util.setEscapeHtml = function(escapeHtml) {
  dwr.util._escapeHtml = escapeHtml;
}

/** @private Work out from an options list and global settings if we should be esccaping */
dwr.util._shouldEscapeHtml = function(options) {
  if (options && options.escapeHtml != null) {
    return options.escapeHtml;
  }
  return dwr.util._escapeHtml;
}

/**
 * Return a string with &, <, >, ' and " replaced with their entities
 * @see TODO
 */
dwr.util.escapeHtml = function(original) {
  var div = document.createElement('div');
  var text = document.createTextNode(original);
  div.appendChild(text);
  return div.innerHTML;
}

/**
 * Replace common XML entities with characters (see dwr.util.escapeHtml())
 * @see TODO
 */
dwr.util.unescapeHtml = function(original) {
  var div = document.createElement('div');
  div.innerHTML = original.replace(/<\/?[^>]+>/gi, '');
  return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
}

/**
 * Replace characters dangerous for XSS reasons with visually similar characters
 * @see TODO
 */
dwr.util.replaceXmlCharacters = function(original) {
  original = original.replace("&", "+");
  original = original.replace("<", "\u2039");
  original = original.replace(">", "\u203A");
  original = original.replace("\'", "\u2018");
  original = original.replace("\"", "\u201C");
  return original;
}

/**
 * Return true iff the input string contains any XSS dangerous characters
 * @see TODO
 */
dwr.util.containsXssRiskyCharacters = function(original) {
  return (original.indexOf('&') != -1
    || original.indexOf('<') != -1
    || original.indexOf('>') != -1
    || original.indexOf('\'') != -1
    || original.indexOf('\"') != -1);
}

/**
 * Enables you to react to return being pressed in an input
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.onReturn = function(event, action) {
  if (!event) event = window.event;
  if (event && event.keyCode && event.keyCode == 13) action();
};

/**
 * Select a specific range in a text box. Useful for 'google suggest' type functions.
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.selectRange = function(ele, start, end) {
  ele = dwr.util._getElementById(ele, "selectRange()");
  if (ele == null) return;
  if (ele.setSelectionRange) {
    ele.setSelectionRange(start, end);
  }
  else if (ele.createTextRange) {
    var range = ele.createTextRange();
    range.moveStart("character", start);
    range.moveEnd("character", end - ele.value.length);
    range.select();
  }
  ele.focus();
};

/**
 * Find the element in the current HTML document with the given id or ids
 * @see http://getahead.org/dwr/browser/util/$
 */
if (document.getElementById) {
  dwr.util.byId = function() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
      var element = arguments[i];
      if (typeof element == 'string') {
        element = document.getElementById(element);
      }
      if (arguments.length == 1) {
        return element;
      }
      elements.push(element);
    }
    return elements;
  };
}
else if (document.all) {
  dwr.util.byId = function() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
      var element = arguments[i];
      if (typeof element == 'string') {
        element = document.all[element];
      }
      if (arguments.length == 1) {
        return element;
      }
      elements.push(element);
    }
    return elements;
  };
}

/**
 * Alias $ to dwr.util.byId
 * @see http://getahead.org/dwr/browser/util/$
 */
var $;
if (!$) {
  $ = dwr.util.byId;
}

/**
 * This function pretty-prints simple data or whole object graphs, f ex as an aid in debugging.
 * @see http://getahead.org/dwr/browser/util/todescriptivestring
 */
dwr.util.toDescriptiveString = function(data, showLevels, options) {
  if (showLevels === undefined) showLevels = 1;
  var opt = {};
  if (dwr.util._isObject(options)) opt = options;
  var defaultoptions = {
    escapeHtml:false,
    baseIndent: "",
    childIndent: "\u00A0\u00A0",
    lineTerminator: "\n",
    oneLineMaxItems: 5,
    shortStringMaxLength: 13,
    propertyNameMaxLength: 30 
  };
  for (var p in defaultoptions) if (!(p in opt)) opt[p] = defaultoptions[p];
  if (typeof options == "number") {
    var baseDepth = options;
    opt.baseIndent = dwr.util._indent2(baseDepth, opt);
  }

  var skipDomProperties = {
    document:true, ownerDocument:true,
    all:true,
    parentElement:true, parentNode:true, offsetParent:true,
    children:true, firstChild:true, lastChild:true,
    previousSibling:true, nextSibling:true,
    innerHTML:true, outerHTML:true,
    innerText:true, outerText:true, textContent:true,
    attributes:true,
    style:true, currentStyle:true, runtimeStyle:true,
    parentTextEdit:true
  };
  
  function recursive(data, showLevels, indentDepth, options) {
    var reply = "";
    try {
      // string
      if (typeof data == "string") {
        var str = data;
        if (showLevels == 0 && str.length > options.shortStringMaxLength)
          str = str.substring(0, options.shortStringMaxLength-3) + "...";
        if (options.escapeHtml) {
          // Do the escape separately for every line as escapeHtml() on some 
          // browsers (IE) will strip line breaks and we want to preserve them
          var lines = str.split("\n");
          for (var i = 0; i < lines.length; i++) lines[i] = dwr.util.escapeHtml(lines[i]);
          str = lines.join("\n");
        }
        if (showLevels == 0) { // Short format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return "\\n";
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        else { // Long format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return options.lineTerminator + indent(indentDepth+1, options);
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        reply = '"' + str + '"';
      }
      
      // function
      else if (typeof data == "function") {
        reply = "function";
      }
    
      // Array
      else if (dwr.util._isArray(data)) {
        if (showLevels == 0) { // Short format (don't show items)
          if (data.length > 0)
            reply = "[...]";
          else
            reply = "[]";
        }
        else { // Long format (show items)
          var strarr = [];
          strarr.push("[");
          var count = 0;
          for (var i = 0; i < data.length; i++) {
            if (! (i in data)) continue;
            var itemvalue = data[i];
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            if (i != count) {
              strarr.push(i);
              strarr.push(":");
            }
            strarr.push(recursive(itemvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("]");
          reply = strarr.join("");
        }
      }
      
      // Objects except Date
      else if (dwr.util._isObject(data) && !dwr.util._isDate(data)) {
        if (showLevels == 0) { // Short format (don't show properties)
          reply = dwr.util._detailedTypeOf(data);
        }
        else { // Long format (show properties)
          var strarr = [];
          if (dwr.util._detailedTypeOf(data) != "Object") {
            strarr.push(dwr.util._detailedTypeOf(data));
            if (typeof data.valueOf() != "object") {
              strarr.push(":");
              strarr.push(recursive(data.valueOf(), 1, indentDepth, options));
            }
            strarr.push(" ");
          }
          strarr.push("{");
          var isDomObject = dwr.util._isHTMLElement(data); 
          var count = 0;
          for (var prop in data) {
            var propvalue = data[prop];
            if (isDomObject) {
              if (!propvalue) continue;
              if (typeof propvalue == "function") continue;
              if (skipDomProperties[prop]) continue;
              if (prop.toUpperCase() == prop) continue;
            }
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            strarr.push(prop.length > options.propertyNameMaxLength ? prop.substring(0, options.propertyNameMaxLength-3) + "..." : prop);
            strarr.push(":");
            strarr.push(recursive(propvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1 && count > 0) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("}");
          reply = strarr.join("");
        }
      }
  
      // undefined, null, number, boolean, Date
      else {
        reply = "" + data;
      }
  
      return reply;
    }
    catch(err) {
      return (err.message ? err.message : ""+err);
    }
  }

  function indent(count, options) {
    var strarr = [];
    strarr.push(options.baseIndent);
    for (var i=0; i<count; i++) {
      strarr.push(options.childIndent);
    }
    return strarr.join("");
  };
  
  return recursive(data, showLevels, 0, opt);
}

/**
 * Setup a GMail style loading message.
 * @see http://getahead.org/dwr/browser/util/useloadingmessage
 */
dwr.util.useLoadingMessage = function(message) {
  var loadingMessage;
  if (message) loadingMessage = message;
  else loadingMessage = "Loading";
  dwr.engine.setPreHook(function() {
    var disabledZone = dwr.util.byId('disabledZone');
    if (!disabledZone) {
      disabledZone = document.createElement('div');
      disabledZone.setAttribute('id', 'disabledZone');
      disabledZone.style.position = "absolute";
      disabledZone.style.zIndex = "1000";
      disabledZone.style.left = "0px";
      disabledZone.style.top = "0px";
      disabledZone.style.width = "100%";
      disabledZone.style.height = "100%";
      document.body.appendChild(disabledZone);
      var messageZone = document.createElement('div');
      messageZone.setAttribute('id', 'messageZone');
      messageZone.style.position = "absolute";
      messageZone.style.top = "0px";
      messageZone.style.right = "0px";
      messageZone.style.background = "red";
      messageZone.style.color = "white";
      messageZone.style.fontFamily = "Arial,Helvetica,sans-serif";
      messageZone.style.padding = "4px";
      disabledZone.appendChild(messageZone);
      var text = document.createTextNode(loadingMessage);
      messageZone.appendChild(text);
      dwr.util._disabledZoneUseCount = 1;
    }
    else {
      dwr.util.byId('messageZone').innerHTML = loadingMessage;
      disabledZone.style.visibility = 'visible';
      dwr.util._disabledZoneUseCount++;
    }
  });
  dwr.engine.setPostHook(function() {
    dwr.util._disabledZoneUseCount--;
    if (dwr.util._disabledZoneUseCount == 0) {
      dwr.util.byId('disabledZone').style.visibility = 'hidden';
    }
  });
};

/**
 * Set a global highlight handler
 */
dwr.util.setHighlightHandler = function(handler) {
  dwr.util._highlightHandler = handler;
};

/**
 * An example highlight handler
 */
dwr.util.yellowFadeHighlightHandler = function(ele) {
  dwr.util._yellowFadeProcess(ele, 0);
};
dwr.util._yellowFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._yellowFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._yellowFadeSteps.length) {
    ele.style.backgroundColor = "#ffff" + dwr.util._yellowFadeSteps[colorIndex];
    setTimeout("dwr.util._yellowFadeProcess('" + ele.id + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * An example highlight handler
 */
dwr.util.borderFadeHighlightHandler = function(ele) {
  ele.style.borderWidth = "2px";
  ele.style.borderStyle = "solid";
  dwr.util._borderFadeProcess(ele, 0);
};
dwr.util._borderFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._borderFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._borderFadeSteps.length) {
    ele.style.borderColor = "#ff" + dwr.util._borderFadeSteps[colorIndex] + dwr.util._borderFadeSteps[colorIndex];
    setTimeout("dwr.util._borderFadeProcess('" + ele.id + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * A focus highlight handler
 */
dwr.util.focusHighlightHandler = function(ele) {
  try {
    ele.focus();
  }
  catch (ex) { /* ignore */ }
};

/** @private the current global highlight style */
dwr.util._highlightHandler = null;

/**
 * Highlight that an element has changed
 */
dwr.util.highlight = function(ele, options) {
  if (options && options.highlightHandler) {
    options.highlightHandler(dwr.util.byId(ele));
  }
  else if (dwr.util._highlightHandler != null) {
    dwr.util._highlightHandler(dwr.util.byId(ele));
  }
};

/**
 * Set the value an HTML element to the specified value.
 * @see http://getahead.org/dwr/browser/util/setvalue
 */
dwr.util.setValue = function(ele, val, options) {
  if (val == null) val = "";
  if (options == null) options = {};
  if (dwr.util._shouldEscapeHtml(options) && typeof(val) == "string") {
    val = dwr.util.escapeHtml(val);
  }

  var orig = ele;
  if (typeof ele == "string") {
    ele = dwr.util.byId(ele);
    // We can work with names and need to sometimes for radio buttons, and IE has
    // an annoying bug where getElementById() returns an element based on name if
    // it doesn't find it by id. Here we don't want to do that, so:
    if (ele && ele.id != orig) ele = null;
  }
  var nodes = null;
  if (ele == null) {
    // Now it is time to look by name
    nodes = document.getElementsByName(orig);
    if (nodes.length >= 1) ele = nodes.item(0);
  }

  if (ele == null) {
    dwr.util._debug("setValue() can't find an element with id/name: " + orig + ".");
    return;
  }

  // All paths now lead to some update so we highlight a change
  dwr.util.highlight(ele, options);

  if (dwr.util._isHTMLElement(ele, "select")) {
    if (ele.type == "select-multiple" && dwr.util._isArray(val)) dwr.util._selectListItems(ele, val);
    else dwr.util._selectListItem(ele, val);
    return;
  }

  if (dwr.util._isHTMLElement(ele, "input")) {
    if (ele.type == "radio" || ele.type == "checkbox") {
      if (nodes && nodes.length >= 1) {
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type != ele.type) continue;
          if (dwr.util._isArray(val)) {
            node.checked = false;
            for (var j = 0; j < val.length; j++)
              if (val[i] == node.value) node.checked = true;
          }
          else {
            node.checked = (node.value == val);
          }
        }
      }
      else ele.checked = (val == true);
    }
    else ele.value = val;

    return;
  }

  if (dwr.util._isHTMLElement(ele, "textarea")) {
    ele.value = val;
    return;
  }

  // If the value to be set is a DOM object then we try importing the node
  // rather than serializing it out
  if (val.nodeType) {
    if (val.nodeType == 9 /*Node.DOCUMENT_NODE*/) val = val.documentElement;
    val = dwr.util._importNode(ele.ownerDocument, val, true);
    ele.appendChild(val);
    return;
  }

  // Fall back to innerHTML
  ele.innerHTML = val;
};

/**
 * @private Find multiple items in a select list and select them. Used by setValue()
 * @param ele The select list item
 * @param val The array of values to select
 */
dwr.util._selectListItems = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found  = false;
  var i;
  var j;
  for (i = 0; i < ele.options.length; i++) {
    ele.options[i].selected = false;
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].value == val[j]) {
        ele.options[i].selected = true;
      }
    }
  }
  // If that fails then try searching through the visible text
  if (found) return;

  for (i = 0; i < ele.options.length; i++) {
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].text == val[j]) {
        ele.options[i].selected = true;
      }
    }
  }
};

/**
 * @private Find an item in a select list and select it. Used by setValue()
 * @param ele The select list item
 * @param val The value to select
 */
dwr.util._selectListItem = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found = false;
  var i;
  for (i = 0; i < ele.options.length; i++) {
    if (ele.options[i].value == val) {
      ele.options[i].selected = true;
      found = true;
    }
    else {
      ele.options[i].selected = false;
    }
  }

  // If that fails then try searching through 