/* * DEK JavaScript Library * Copyright(c) 2008-2010, DEK International * * Version 1.0.12 *//* ############################## User Interface ############################## *//** * A utitlities class giving access to all the generic user interface functions * @class Core.Ui * @singleton */Core.Ui = {	/**	 * Caculates how far down and how far left the page has current been scrolled.	 * @method scrollOffset	 * @static	 * @return {Core.Point} The top left of the current view port.	 */	scrollOffset : function() {		if (typeof(window.pageXOffset)=="number") {			return new Core.Point(window.pageXOffset, window.pageYOffset);		} else if (document.documentElement) {			return new Core.Point(					document.body.scrollLeft + document.documentElement.scrollLeft, 					document.body.scrollTop + document.documentElement.scrollTop);		} else if (typeof(document.body.scrollLeft)=="number") {			return new Core.Point(document.body.scrollLeft, document.body.scrollTop);		} else {			return new Core.Point(0, 0)		}	},	/**	 * Caculates the size of the visible area in the browser window.	 * @method clientSize	 * @static	 * @return {Core.Point} The size of the current view port.	 */	clientSize : function() {		if (typeof(window.innerWidth)=="number") {			return new Core.Point(window.innerWidth, window.innerHeight);		} else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight)) {			return new Core.Point(document.documentElement.clientWidth, document.documentElement.clientHeight);		} else if(typeof(document.body.clientWidth)=="number") {			return new Core.Point(document.body.clientWidth, document.body.clientHeight);		} else {			return new Core.Point(0, 0);		}	},		/**	 * Adds a button to a action bar.  Adds the listener for when the button is clicked and sets up listeners to highlight the button on mouse over.	 * @method addButton	 * @static	 * @param {String}/HTMLElement/{Core.Element} actionBarGroup The id, DOM node or Core.Element for the action bar group this button should be added to.	 * @param {String} label The label for the button	 * @param Boolean first (Optional) If this is the first button in the group, specifiy this as true.  Defaults to false.	 * @param {Function} listener (Optional) The function to be called when the button is pressed.	 * @return {Core.Element} The element for the button created.	 */	addButton: function(actionBarElement, label, first, listener) {		actionBarElement = Core.get(actionBarElement);		if(actionBarElement!=null) {			var button = actionBarElement.createChild({tag:"div", html:label});			if(first) {				button.addClass("first");			}						if(listener) {				button.addListener("click", listener);			}			button.addListener("mousedown", button.addClass.bind(button, ["pressed"], true));			button.addListener("mouseup", button.removeClass.bind(button, ["pressed"], true));			return button;		} else {			return null;		}	},	/**	 * Adds the listener for when the button is clicked and sets up listeners to highlight the button on mouse over.	 * @method initButton	 * @static	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for this button.	 * @param {Function} listener (Optional) The function to be called when the button is pressed.	 */	initButton: function(element, listener) {		element = Core.get(element);		if(element!=null) {			if(listener) {				element.addListener("click", listener);			}			element.addListener("mousedown", element.addClass.bind(element, ["pressed"], true));			element.addListener("mouseup", element.removeClass.bind(element, ["pressed"], true));		}	},	/**	 * Creates a new action bar combo button.  This is the equivalent of calling <code>new Core.Ui.ActionBarComboButton(element, popup, listener, text, width)</code>.	 * @method initComboButton	 * @static	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for this button.	 * @param {Core.Ui.ComboListPopup} popup The combo list to show when this button selected.	 * @param {Function} listener (Optional) The function to be called when an item is selected. 	 * @param {String} text (Optional) The label for this button.  By default the text is left as specified in the HTML. 	 * @param {Number} width (Optional) The width of the button.  By defualt the button is sized to it's contents.	 * @return {Core.Ui.ActionBarComboButton} The new comboButton	 */	initComboButton: function(element, popup, listener, text, width) {		return new Core.Ui.ActionBarComboButton(element, popup, listener, text, width);	},		/*	 * Checks to see if div popups are enabled.	 * @method popupsEnabled	 * @static	 * @return Boolean True when div popups are enabled.	 */	popupsEnabled: function() {		return typeof(this.popupStack)!="undefined";	},	/*	 * Enables div popups are enabled.  This is called by <code>createPopupElement</code> and on 	 * the first call adds all elements nessasery to support div popups.	 * @method enabledPopups	 * @static	 */	enablePopups: function() {		if(!this.popupStack) {			var body = new Core.Element(document.body);			body.addListener("click", this.hideNonPersistantPopups.bind(this));									this.overlayElement = body.createChild({tag:"div", klass:"overlay"});			this.overlayElement.setUseVisibility(true);			this.popupsElement = body.createChild({tag:"div", klass:"popups"});			this.popupStack = new Array();		}	},	/*	 * Creates a <code>div</code> HTML element with a CSS class of "popup" to be used as the container for a popup.	 * @method createPopupElement	 * @static	 */	createPopupElement: function() {		this.enablePopups();		return this.popupsElement.createChild({tag:"div", klass:"popup"});	},	/*	 * Popups call this when they wish to become visible.  This controls the popup stack macking sure popups 	 * appear in the correct z order when displayed.	 * @method showPopup	 * @static	 * @param {Core.Ui.Popup} popup The popup requesting to be displayed.	 * @param {Boolean} focus When true popup will be brought in to focus when it is shown.	 */	showPopup: function(popup, focus) {		if(this.isPopupVisible(popup)) {			this.hidePopup(popup);		}				this.popupStack.push(popup);						// z index		if(popup.isModal()) {			this.overlayElement.setZIndex(this.popupStack.length*10);			this.overlayElement.show("block");		}		popup.getElement().setZIndex((this.popupStack.length*10)+1);						// event		popup.showEvent.fire({popup: popup});				// show		popup.getElement().show("block");		if(focus) {			popup.getElement().getDom().focus();		}	},	/*	 * Popups call this when they wish to become hidden.	 * This controls the popup stack macking sure popups appear in the correct z order when displayed.	 * @method hidePopup	 * @static	 * @param {Core.Ui.Popup} popup The popup requesting to be hidden.	 */	hidePopup : function(popup) {		if(this.popupStack.length>0) {			// find popup			var index=	0;			var found = false;			for(; index<this.popupStack.length && !found; index++) {				if(popup===this.popupStack[index]) {					found = true;				}			}						// remove popup			if(found) {				index--;				this.popupStack.splice(index, 1);				popup.getElement().hide();								// adjust z-indexs				for(; index<this.popupStack.length; index++) {					if(this.popupStack[index] && this.popupStack[index].setZIndex) {						this.popupStack[index].setZIndex(((index+1)*10)+1);					}				}											// move modal				var modal=false;				for(var index=this.popupStack.length-1; index>=0 && !modal; index--) {					if(this.popupStack[index].isModal()) {						modal=true;						this.overlayElement.setZIndex((index+1)*10);					} 				}				if(!modal) {					this.overlayElement.hide();				}								// event				popup.hideEvent.fire({popup: popup});			}		}	},	/*	 * Checks to see if a popup is visible by checking the popup stack for that popup	 * @method isPopupVisible	 * @static	 * @param {Core.Ui.Popup} popup The popup.	 * @return Booelan True when the popup is visible, false when not.	 */	isPopupVisible : function(popup) {		if(this.popupStack.length>0) {			var found = false;			for(var index=	0; index<this.popupStack.length && !found; index++) {				if(popup===this.popupStack[index]) {					found = true;				}			}			return found;		} else {			return false;		}	},	/*	 * Hides all the non persistant popups currently displayed from the top of the stack down.  	 * If an event is passed and the target of that event is a popup in the stack, the code will not	 * hide that popup or any below it.	 * @method hideNonPersistantPopups	 * @static	 * @param Object event An event object that triggered this hide action.	 */	hideNonPersistantPopups: function(event) {		if(this.popupsEnabled) {			var remove = true;			while(this.popupStack.length>0 && remove) {				if(this.popupStack[this.popupStack.length-1].isPersistent()) {					remove = false;				} else {					var popupDom = this.popupStack[this.popupStack.length-1].getElement().getDom();					var target = event.target;					while(remove && target) {						if(target==popupDom) {							remove = false;						}						target = target.parentNode;					}					if(remove) {						this.hidePopup(this.popupStack[this.popupStack.length-1]);					}				}			}		}	},	/**	 * Displays a simple dialog box with a single message.	 * @method msgbox	 * @static	 * @param {String} title The title for this dialog.	 * @param {String} message The message for this dialog.	 * @param {Array} actions (Optional) An array of strings, each represnting a button to be displayed.  Defaults to a single "OK" button.	 * @param {Function} listener (Optional) A function that should be added as a temporary listener to the close event for this dialog.	 * @see Core.Ui.Dialog	 */	msgbox: function(title, message, actions, listener) {		if(!this.msgboxDialog) {			this.msgboxDialog=new Core.Ui.Dialog(true, true, "msgbox");		}				title = title ? title : "Message";		message = message ? message : "<p> </p>";		if(message.trim().substr(0,1)!="<") {			message = "<p>"+message+"</p>";		}		actions = actions ? actions : ["OK"];									this.msgboxDialog.setTitle(title);		this.msgboxDialog.setContent("<div class=\"text\">"+message+"</div>");		this.msgboxDialog.setActions(actions);			if(listener) {				this.msgboxDialog.closeEvent.addTempListener(listener);		}		this.msgboxDialog.show();	},	/**	 * Displays status message, normally this is shown in a red box at the top of the screen.	 * @method showStatusMessage	 * @static	 * @param {String} message The message to display.	 * @param {Boolean} modal (Optional) When true is the staus message will block input to the rest of the screen.  Defaults to false.	 * @param Number delay (Optional) The number of milliseconds delay before this status message should be displayed. Defaults to 0.	 */	showStatusMessage: function(message, modal, delay) {		delay = isNaN(delay) ? 0 : parseInt(delay);		if(delay>0) {			if(this.statusBoxTimer) {				window.clearTimeout(this.statusBoxTimer);			}			this.statusBoxTimer = window.setTimeout(Core.Ui.showStatusMessage.bind(Core.Ui, [message, modal], true), delay);		} else {			if(this.statusBoxTimer) {				window.clearTimeout(this.statusBoxTimer);				this.statusBoxTimer=null;			}						if(!this.statusBox) {				this.statusBox = new Core.Ui.StatusBox();				this.statusBoxMessages = [];			}					message = typeof(message)=="string" ? message : "Loading";			modal = typeof(modal)=="boolean" ? modal : false;					if(this.statusBoxMessages.length==0 || this.statusBoxMessages[this.statusBoxMessages.length-1]!=message) {				this.statusBoxMessages.push(message);			}			this.statusBox.show(message, modal);		}	},	/**	 * Hides the status message.	 * @method hideStatusMessage	 * @static	 * @param {String} message (Optional) The message to hide, when not specified the last message is hidden.	 */	hideStatusMessage: function(message) {		if(this.statusBoxTimer) {			window.clearTimeout(this.statusBoxTimer);			this.statusBoxTimer=null;		}		if(this.statusBox) {			var found = false;			if(typeof(message)=="string") {				for(var index=0; index<this.statusBoxMessages.length && !found; index++) {					if(this.statusBoxMessages[index]==message) {						this.statusBoxMessages.splice(index, 1);						found = true;					}				}			} 			if(typeof(message)!="string" || !found) {				this.statusBoxMessages.pop();			} 						if(this.statusBoxMessages.length>0) {				this.statusBox.show(this.statusBoxMessages[this.statusBoxMessages.length-1], this.statusBox.isModal());			} else {				this.statusBox.hide();			}		}	},	/**	 * Displays a simple box with a message in it at the current mouse position.  	 * The popup becomes hidden when the user clicks anywhere else on the screen.	 * @method mousePopup	 * @static	 * @param Object event The mouse event that triggered this popup.	 * @param {String} content The content of the popup.	 * @param {Core.Point} point (Optional) Where to place the popup, when not specified the mouse location is used. (Normaly used becuase IE deletes your original event);	 */	mousePopup: function(event, content, point, width) {		if(!this.mousePopupPopup) {			this.mousePopupPopup = new Core.Ui.Popup(false, false, "mousePopup");		}		if(typeof(cssClass)=="string") {			this.mousePopupPopup.addClass("cssClass");		}		point = point ? point : new Core.Point(event.clientX, event.clientY);				this.mousePopupPopup.setContent(content);		this.mousePopupPopup.setOffset(Core.Ui.scrollOffset().add(point.constrainTo(new Core.Point(0, 0), Core.Ui.clientSize().minus(this.mousePopupPopup.getElement().getSize()))));		this.mousePopupPopup.show(event);	},	/**	 * Displays a dialog box using the body of a normal html page as content.	 * @method subpage	 * @static	 * @param {String} title The title for this dialog.	 * @param {String} url The url of the page to display.	 * @param {Function} listener (Optional) A function that should be added as a temporary listener to the close event for this dialog.	 */	subpage: function(title, url, listener) {		if(!this.subpageDialog) {			this.subpageDialog=new Core.Ui.Dialog(true, true, "");			this.subpageDialog.setActions(["OK"]);		}				this.subpageDialog.setTitle(title);		this.subpageDialog.setContent("Loading...");				if(listener) {			this.subpageDialog.closeEvent.addTempListener(listener);		}		this.subpageDialog.show();		Core.addRequest("GET", url, "", Core.Ui.doSubpage.bind(Core.Ui));	},	doSubpage: function(event) {		var text = event.request.responseText.replace(/\n/g, "");		var match = /<body[^>]*>(.*)<\/body>/.exec(text);		if(match && match[1]) {			this.subpageDialog.setContent(match[1]);			}	},		/*	 * Creates and returns a text represntaion of the UI object.	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui]";	}};/* ############################## Expanding field ############################## *//** * The ExapndingTextArea class adds functionality to a text area so it automaticly expands to fit * the content of the field.  You can either create an ExpandingTextArea directly or add the css * class "expand" to the element and use {Core.Form#createSpecialFields} * @class Core.Ui.ExpandingTextArea */Core.Ui.ExpandingTextArea = Core.create({	/**	 * Creates a new ExapndingTextArea	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} input The id, dom node or Core.Element for the textarea.	 */	init : function(input) {		var element = Core.get(input);		this.input = element.getDom();		this.inputLength = 0;		this.inputWidth = 0;				this.min = parseInt(element.readStyle("height"), 10);		if(isNaN(this.min) || this.min<=0) {			this.min = this.input.offsetHeight;		}		if(this.min<13) {  // need to find a way to make this linked to font size.			this.min=13;		}				this.resize();		element.addListener("keyup", this.resize.bind(this));		element.addListener("focus", this.resize.bind(this));	},	resize: function(event) {		var length = this.input.value.length;		var width = this.input.offsetWidth;				if(this.inputLength!=length || this.inputWidth!=width) {			if(!Core.isIE && !Core.isOpera) {				this.input.style.height = "0px";			}			this.input.style.height = Math.max(this.input.scrollHeight, this.min) + "px";			this.inputLength = length;			this.inputWidth = width;		}	}});/* ############################## Popup ############################## *//** * The base class for all popups.  Provides the basic functionlity to create, show and hide  * popups.  Popups are basicly absolutely positioned div elements that have varible a * z index and are shown and hidden as required. * @class Core.Ui.Popup */Core.Ui.Popup = Core.create({	/**	 * Creates a new popup. Popups are initially craeted hidden.	 * @method init	 * @constructor	 * @param Boolean modal When true this popup will block access to the main page and other popups until it is closed.	 * @param Booelan persistent When true this popup will continue to display even if the user clicks out side the popup.  Modal popups are always persistent.	 * @param {String} className (Optional) Any additonal css class names to be added to this popup.	 * @param {String} content (Optional) The content of this popup.	 */	init : function(modal, persistent, className, content) {		this.modal = modal;		this.persistent = modal ? true : persistent;		this.focusOnShow = true;		this.element = Core.Ui.createPopupElement();		this.element.setUseVisibility(true);		if(className && className!="") {			this.element.addClass(className);		}		if(content) {			this.setContent(content);		}				/**		 * Fires when an popup is shown		 * @event showEvent		 * @param {Core.Ui.Popup} popup The popup that has just been shown.		 */		this.showEvent = new Core.Event("Core.Ui.Popup:show");		/**		 * Fires when an popup is hidden		 * @event hideEvent		 * @param {Core.Ui.Popup} popup The popup that has just been hidden.		 */		this.hideEvent = new Core.Event("Core.Ui.Popup:hide");	},	/**	 * @method isModal	 * @return Boolean True if this popup is modal.	 */	isModal: function() {		return this.modal;	},	/**	 * @method isPersistent	 * @return Boolean True if this popup is persistent.	 */	isPersistent: function() {		return this.persistent;	},	/**	 * @method isFocusOnShow	 * @return Boolean True if this popup is set to gain focus when it is shown.	 */	isFocusOnShow: function() {		return this.focusOnShow;	},	/**	 * @method isFocusOnShow	 * @param Boolean focus If this popup should gain focus when it is shown.	 */	setFocusOnShow: function(focus) {		this.focusOnShow=focus;	},	/**	 * Set the position of this popup.	 * @method setOffset	 * @param {Core.Point} point The top left point for this popup.	 */	setOffset: function(point) {		this.element.setOffset(point);	},	/**	 * Returns the popups top level div element.	 * @method getElement	 * @return {Core.Element} The popups div element.	 */	getElement: function() {		return this.element;	},	/**	 * Used to check if this popup is currently visble	 * @method isVisible	 * @return Booelan True when the popup is currenlty visible, false when not.	 */	isVisible: function() {		return Core.Ui.isPopupVisible(this);	},	/**	 * This returns the element where content should be placed, useful for popups that implement a frame of some sort.	 * @method getContentElement	 * @return {Core.Element} The popups content element.	 */	getContentElement: function() {		return this.element;	},	/**	 * Sets the content	 * @method setContent	 * @param {String} html The content for this popup.	 */	setContent: function(html) {		this.getContentElement().setHtml(html);	},	/**	 * Sets the size of this  popup	 * @method setSize	 * @param {Core.Point} size The new size for this popup.	 */	setSize: function(point) {		this.getElement().setSize(point);	},		/**	 * Makes the popup visible.	 * @method show	 * @param Object event (Optional) If the show opertaion is triggered by an event, this should be the event information object of that event.	 */	show: function(event) {		if(event) {			Core.Ui.hideNonPersistantPopups(event);			Core.EventManager.stopPropagation(event);		}					Core.Ui.showPopup(this, this.focusOnShow);	},	/**	 * Hides the popup	 * @method hide	 * @param Object event (Optional) If the hide opertaion is triggered by an event, this should be the event information object of that event.	 */	hide: function(event) {		if(event) {			Core.Ui.hideNonPersistantPopups(event);			Core.EventManager.stopPropagation(event);		}		Core.Ui.hidePopup(this);	},		/*	 * Creates and returns a text represntaion of this Popup	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.Popup]";	}});/* ############################## Dialog ############################## *//** * The dialog class provides basic dialog functionality.  A dialog has three sections, at  * the top there is a title, in the middle is the content and at the bottom are buttons or  * actions.  The dialog is closed when any of the buttons at the bottom are pressed and  * which button used is included in the closeEvent. * @class Core.Ui.Dialog * @extends Core.Ui.Popup */Core.Ui.Dialog = Core.create(Core.Ui.Popup, {	/**	 * Creates a new dialog. The Dialog is initially craeted hidden.	 * @method init	 * @constructor	 * @param Boolean modal When true this dialog will block access to the main page and other popups until it is closed.	 * @param Booelan persistent When true this dialog will continue to display even if the user clicks out side the dialog.  Modal dialogs are always persistent.	 * @param {String} className (Optional) Any additonal css class names to be added to this dialog.	 * @param {String} title (Optional) The title of this dialog.	 * @param {String} content (Optional) The content of this dialog.	 * @param {Array} actions (Optional) An array of strings, each represnting a button to be displayed.	 */	init : function(modal, persistent, className, title, content, actions) {		Core.Ui.Popup.prototype.init.call(this, modal, persistent, "dialog");		if(className && className!="") {			this.element.addClass(className);		}		var formElement = this.element.createChild({tag:"div", klass:"form"});		this.headElement = formElement.createChild({tag:"div", klass:"head"});		if(title) {			this.setTitle(title);		}		this.bodyElement = formElement.createChild({tag:"div", klass:"body"});		if(content) {			this.setContent(content);		}		this.footElement = formElement.createChild({tag:"div", klass:"foot"});		if(actions) {			this.setActions(actions);		}				new Core.Ui.DragControl(this.element, this.headElement);				/**		 * Fires when the dialog is closed		 * @event closeEvent		 * @param {Core.Ui.Dialog} dialog The dialog that has just been closed.		 * @param {String} result The button that was used to close the dialog.		 */		this.closeEvent = new Core.Event("Core.Ui.Dialog:close");	},	/**	 * The dialog is centred horizontaly and placed a fith of the way down the screen.  It is then made visible.	 * @method show	 * @param Object event (Optional) If the show opertaion is triggered by an event, this should be the event information object of that event.	 */	show: function(event) {		var hoz = Core.Ui.scrollOffset().add(Core.Ui.clientSize().minus(this.element.getSize()).multiply(0.5));		var ver = Core.Ui.scrollOffset().add(Core.Ui.clientSize().multiply(0.2));				this.setOffset(new Core.Point(hoz.x, ver.y));		Core.Ui.Popup.prototype.show.call(this, event);	},	/**	 * Set the title for this dialog box.	 * @method setTitle	 * @param {String} text The new title.	 */	setTitle: function(text) {		this.headElement.setHtml("<h3>"+text+"</h3>");	},	/**	 * This returns the element where content should be placed.	 * @method getContentElement	 * @return {Core.Element} The dialogs content element.	 */	getContentElement: function() {		return this.bodyElement;	},	/**	 * Sets the actions (buttons) for this dialog.	 * @method setActions	 * @param {Array} actions An array of strings, each represnting a button to be displayed.  	 */	setActions: function(actions) {		this.footElement.clear();		var actionBar = this.footElement.createChild({tag:"div",  klass:"actionBar"});				if(actions.length>0) {			if(typeof(actions[0])=="string") {				var actionGroup = actionBar.createChild({tag:"div",  klass:"actionGroup actionGroupRight"});				for(var action=0; action<actions.length; action++) {					Core.Ui.addButton(actionGroup, actions[action], action==0, this.actionListener.bind(this, [actions[action]]));				}			} else {							for(var group=0; group<actions.length; group++) {					var actionGroup = actionBar.createChild({tag:"div",  klass:"actionGroup actionGroupRight"});					for(var action=0; action<actions[group].length; action++) {						Core.Ui.addButton(actionGroup, actions[group][action], action==0, this.actionListener.bind(this, [actions[group][action]]));					}							}			}		}				if(Core.isIE) {			this.footElement.createChild({tag:"div", klass:"clear"});		}	},	/* This method listens for button presses and closes the dialog when pressed. */	actionListener: function(event, button) {		this.hide();		this.closeEvent.fire({dialog: this, result: button});	},	/**	 * When a dialog contains a form, the submit action on that form can be set to close the dialog via method.	 * @method setFormAction	 * @param {Corm.Form}/{String}/HTMLElement/{Core.Element} form Either a Core.Form, and id, HTML element or Core.Element for the form element.	 * @param {String} action The action (button) this form should simulate pressing when submitted.	 */	setFormAction: function(form, action) {		if(form instanceof Core.Form) {			form = form.getFormElement();		} else {			form = Core.get(form);		}			form.addListener("submit", this.formActionListener.bind(this, [action]));	},	/* listener for form actions */	formActionListener: function(event, button) {		Core.EventManager.catchEvent(event);		this.actionListener(event, button);	},		/*	 * Creates and returns a text represntaion of this Popup	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.Dialog]";	}});/* ############################## Status box ############################## *//** * Represents a status box popup.  Normaly this would not be used directly but via {Core.Ui#showStatusMessage}. * @class Core.Ui.StatusBox * @extends Core.Ui.Popup */Core.Ui.StatusBox = Core.create(Core.Ui.Popup, {	/**	 * Creates a status popup.	 * @method init	 * @constructor	 */	init : function() {		Core.Ui.Popup.prototype.init.call(this, false, true, "statusBox");		this.setFocusOnShow(false);	},	/**	 * Makes the status box visible, the status box is horizontaly centered at the top of the screen.	 * @method show	 * @param {String} message The status message to disaply.	 * @param Boolean modal When true the status message is modal.	 * @param {Object} event (Optional) If the show method is triggered from an event, this should be the event object.	 */	show: function(message, modal, event) {		this.setContent(message);		this.modal = modal;						var hoz = Core.Ui.scrollOffset().add(Core.Ui.clientSize().minus(this.element.getSize()).multiply(0.5));		var ver = Core.Ui.scrollOffset();				this.setOffset(new Core.Point(hoz.x, ver.y));		Core.Ui.Popup.prototype.show.call(this, event);	},	/*	 * Creates and returns a text represntaion of this Popup	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.StatusBox]";	}});/* ############################## Action Bar Combo Button ############################## *//** * Represents an action bar combo button. * @classs Core.Ui.ActionBarComboButton * @see Core.Ui.ComboListPopup */Core.Ui.ActionBarComboButton = Core.create({	/**	 * Creates a new action bar combo button.  This is the equivalent of calling <code>new Core.Ui.ActionBarComboButton(element, popup, listener, text, width)</code>.	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for this button.	 * @param {Core.Ui.ComboListPopup} popup The combo list to show when this button selected.	 * @param {Function} listener (Optional) The function to be called when an item is selected. 	 * @param {String} text (Optional) The label for this button.  By default the text is left as specified in the HTML. 	 * @param {Number} width (Optional) The width of the button.  By defualt the button is sized to it's contents.	 */	init : function(buttonElement, popup, listener, text, width) {			this.element = Core.get(buttonElement);		if(this.element!=null) {			// find action bar element			this.actionBarElement=this.element.getParent();			while(!this.actionBarElement.hasClass("actionBar") && this.actionBarElement!=null) {				this.actionBarElement=this.actionBarElement.getParent();			}						// set text and add combo arrow			if(text) {				this.updateText(text);			} else {				this.updateText(this.element.getHtml());			}			this.element.addClass("combo");						// set width			if(width) {				this.element.getDom().style.width = width+"px";			}						// setup popup events			if(popup) {				this.open = false;				this.popup = popup;				this.element.addListener("click", this.click.bind(this));						popup.hideEvent.addListener(this.hide.bind(this));				if(listener) {					popup.selectionEvent.addListener(listener);				}			}		}	},	/**	 * Updates the text of the combo button.	 * @method updateText	 * @param {String} text The new text for the button.	 */	updateText: function(text) {		if(text) {			this.element.setHtml(text);			this.element.createChild({tag:"div", klass:"inline-block comboImg"});		}	},	/* private: listenes for button click and shows the dialog when that happens. */	click: function(event) {		if(this.open) {			this.hide();		} else {			this.show(event);		}			},	/* private: makes the combo popup visible */	show: function(event) {		this.element.addClass("pressed");				var actionBarPos = this.actionBarElement.getTotalOffset();		var actionBarSize = this.actionBarElement.getSize();		var topLeft = actionBarPos.add(new Core.Point(0, actionBarSize.y), true);		var bottomRight = topLeft.add(new Core.Point(actionBarSize.x-this.popup.getElement().getSize().x, 0), true);					this.popup.setOffset(this.element.getTotalOffset().constrainTo(topLeft, bottomRight));		this.popup.show(event);		this.open = true;	},	/* private: hides the combo popup */	hide: function(event) {		this.element.removeClass("pressed");				this.popup.hide();		this.open = false;	}});/** * Creates a combo field, similar to a selection element but with out using the OS to render the field.  This is useful in IE. * @class Core.Ui.ComboField */Core.Ui.ComboField = Core.create({	/**	 * Creates a new new view dialog field	 * @method init	 * @constructor	 * @param {Core.Form} form The form the field is part of.	 * @param {String}/HTMLInput/{Core.Element} field The name, HTML input or Core.Element for the input to replace.	 * @param {Object} options The options to display as an object with values and aliases.	 */	 	init: function(form, field, options) {		this.form = form;		this.field = field;		this.options = options;						// replace input		var element = this.form.getElement(this.field);		this.entryElement = element.createSibling({tag: "div", klass: element.hasClass("double") ? "comboFieldDouble" : element.hasClass("full") ? "comboFieldFull" : "comboField"});		this.entryElement.addListener("click", this.entryListener.bind(this));		this.entryLink = this.entryElement.createChild({tag:"a", href:"javascript:"});		this.updateValue();		this.entryLink.addListener("click", this.entryListener.bind(this));		var newInput = element.getDom().cloneNode(false);		newInput.type="hidden";		element.replace(newInput);		element.getDom().onSetValue = this.updateValue.bind(this);				// create popup		this.popup = new Core.Ui.ComboListPopup(options, "comboFieldListPopup");				// listen to dialog		this.popup.selectionEvent.addListener(this.popupListener.bind(this));						// events		/**		 * Fires when the field is changed via the dialog.		 * @event changeEvent		 * @param {Core.Element} field The field updated.		 * @param {String} value The new value.		 */		this.changeEvent = new Core.Event("Core.Ui.ComboField:change");	},		/* listens to the field, when clicked it opens the dialog */	entryListener: function(event) {		this.popup.setSelected(this.form.getValue(this.field));				var fieldPos = this.entryElement.getTotalOffset();		var fieldSize = this.entryElement.getSize();		var popupSize = this.popup.getElement().getSize();		var popupDom = this.popup.getElement().getDom()				this.popup.setOffset(fieldPos.add(new Core.Point(1, fieldSize.y+1)));		if(popupSize.x<fieldSize.x) {			popupDom.style.width = fieldSize.x;		}				if(popupSize.y>150) {			popupDom.style.height = 150;			popupDom.style.overflowY= "auto";		}				this.popup.show(event);	},	/* dialogListener updates the field when the dialog closes */	popupListener: function(event) {		if(event.value!=null) {			this.form.setValue(this.field, event.value);			this.entryLink.setHtml(event.display);			this.changeEvent.fire({field: this.field, value: event.value});		}		this.entryLink.getDom().focus();	},	/* private updates the display from the field */	updateValue: function() {		var value= this.form.getValue(this.field);		if(this.options[value]) {			value = this.options[value];		}		this.entryLink.setHtml(value);	},	toString : function() {		return "[Core.Ui.ComboField]"	}});/* ############################## Combo List Popup ############################## *//** * A combo list popup used with Core.Ui.ActionBarComboButton * @class Core.Ui.ComboListPopup * @extends Core.Ui.Popup * @see Core.Ui.ActionBarComboButton */Core.Ui.ComboListPopup = Core.create(Core.Ui.Popup, {	/**	 * Creates a new ComboListPopup with the defined options.  The list of options is defined as an	 * object where each property is an option.  The property name is the value for the option and the	 * property value is what to display in the list.	 * @method init	 * @constructor	 * @param {Object} options The list of options for this combo defined as an object.	 * @param {String} cssClass (Optional) The css class the list should use, deafults to "comboListPopup".	 */	init : function(options, cssClass) {		cssClass = cssClass ? cssClass : "comboListPopup";		Core.Ui.Popup.prototype.init.call(this, false, false, cssClass);				this.setOptions(options);				/**		 * Fires when an option is selected		 * @event selectionEvent		 * @param {String} selection The value of the option selected, this is the same as the <code>value</code>.		 * @param {String} value The value of the option selected, this is the same as the <code>selection</code>		 * @param {String} display The display value of the selected option.		 */		this.selectionEvent = new Core.Event("Core.Ui.ComboListPopup:selection");	},	/**	 * Reset the options in the list	 * @method setOptions	 * @param {Object} options The list of options for this combo defined as an object.	 */	setOptions: function(options) {		this.rows = {}		var contentElement = this.getContentElement();		contentElement.clear();		var listElement = contentElement.createChild({tag: "ul"});		for(var option in options) {			var optionElement = listElement.createChild({tag: "li", html: options[option]});			optionElement.addListener("click", this.select.bind(this, [option, options[option]]));			optionElement.addListener("mouseover", optionElement.addClass.bind(optionElement, ["highlight"], true));			optionElement.addListener("mouseout", optionElement.removeClass.bind(optionElement, ["highlight"], true));			this.rows[option] = optionElement;		}	},	/**	 * Reset the options in the list	 * @method setSelected	 * @param option The option currently selected.	 */	setSelected: function(selected) {		for(var option in this.rows) {			if(selected == option) {				this.rows[option].addClass("selected");			} else {				this.rows[option].removeClass("selected");			}		}	},	/* private: fires the selectionEvent */	select: function(event, option, display) {		this.hide(event);		this.selectionEvent.fire({selection:option, value:option, display:display});	}});/* ############################## DatePicker ############################## *//** * A date picker field converts a normal html input elment into a field with a date picker popup. * You can either create a DatePickerField directly or add the css class "datePicker" to the  * element and use {Core.Form#createSpecialFields}. * @class Core.Ui.DatePickerField * @see Core.Ui.DatePicker */Core.Ui.DatePickerField = Core.create({	/**	 * Creates a new datePicker field.	 * @method init	 * @constructor	 * @param {Core.Form} form The form the field belongs to.	 * @param {String}/HTMLInput/{Core.Element} field The name, input element or {Core.Element} for the field to turn in to a date picker field.	 * @param {String} fieldFormat (Optional) A format string to define how the date should be stored in a field.  Defaults to "dd mmmm yyyy".	 * @param {String} displayFormat (Optional) A format string to define how the date should be displayed.  Defaults to "dd-mmm-yyyy".      * @see Date	 */	init: function(form, field, fieldFormat, displayFormat) {		this.form = form;		this.field = this.form.getElement(field);		this.fieldFormat = fieldFormat ? fieldFormat : "dd mmmm yyyy";		this.displayFormat = displayFormat ? displayFormat : "dd-mmm-yyyy";		this.disabled = false;				// replace input		var date = this.parseDate();		this.entryElement = this.field.createSibling({tag: "div", klass: "datePicker"});		this.entryElement.addListener("click", this.entryListener.bind(this));		this.entryInput = this.entryElement.createChild({tag: "input", type: "text", klass: "datePickerEntry", value: (date!=null? date.format(this.displayFormat) : "")});		this.entryInput.addListener("change", this.entryInputListener.bind(this));		this.entryInput.addListener("keypress", this.entryInputKeyPressListener.bind(this));		var newInput = this.field.getDom().cloneNode(false);		newInput.type="hidden";		this.field.replace(newInput);		this.field.removeClass("datePicker");		this.field.getDom().onSetValue = this.updateDate.bind(this);				// create popup		this.popup = new Core.Ui.Popup(false, false, "datePicker");		this.datePicker = new Core.Ui.DatePicker(this.popup.getContentElement());		this.datePicker.dateSelectedEvent.addListener(this.datePickerListener.bind(this));				// events		/**		 * Fires when the date changed.		 * @event changeEvent		 * @param HTMLInput field The input element for this date picker field.		 * @param {Date} value The date selected.		 * @param {Date} date The date selected.		 */		this.changeEvent = new Core.Event("Core.Ui.DatePickerField:change");			},	/* private: trys to read the date from the field */	parseDate: function() {		var date = this.form.getValue(this.field).parseDate();		if(date==null) {			date = this.form.getValue(this.field).parseDate(this.fieldFormat);		}		if(date==null) {			date = this.form.getValue(this.field).parseDate(this.displayFormat);		}		return date;	},		/* private: listens to the visual element so when the user clicks on it, the popup is shown. */	entryListener: function(event) {		if(event.target===this.entryElement.getDom()) {			this.datePicker.setDate(this.parseDate());					var location = this.entryElement.getTotalOffset();			location.y += this.entryElement.getSize().y;			this.popup.setOffset(location);			this.popup.show(event);		}	},	/* private: listens to input. */	entryInputKeyPressListener: function(event) {		if(event.keyCode==13) {			Core.EventManager.catchEvent(event);			this.entryInputListener(event);		}	},	entryInputListener: function(event) {				var value = this.entryInput.getDom().value;		var date = value.parseDate();		if(date==null) {			date = value.parseDate(this.displayFormat);		}		if(date==null) {			date = value.parseDate(this.feildFormat);		}				if(date==null) {			this.form.setValue(this.field, value, false);			this.changeEvent.fire({field: this.field, value: value, date: value});		} else {			this.form.setValue(this.field, date.format(this.fieldFormat), false);			this.entryInput.getDom().value = date.format(this.displayFormat);			this.changeEvent.fire({field: this.field, value: date, date: date});		}	},	/* private: listener for the date picker popup, updating the field and visual element when a date is selected */	datePickerListener: function(event) {		if(event.date!=null) {			var date = event.date;						this.form.setValue(this.field, date.format(this.fieldFormat), false);			//this.entryElement.setHtml(date.format(this.displayFormat));			this.entryInput.getDom().value = date.format(this.displayFormat);			this.changeEvent.fire({field: this.field, value: date, date: date});			this.popup.hide();		}	},	/**	 * Sets the date for this field.	 * @method setDate	 * @param {Date} date (Optional) The new date.	 */	setDate: function(date) {		if(date==null) {			date = new Date();		}		this.form.setValue(this.field, date.format(this.fieldFormat), false);		//this.entryElement.setHtml(date.format(this.displayFormat));		this.entryInput.getDom().value = date.format(this.displayFormat);	},		/**	 * Updates the date displayed using the value in the field	 * @method updateDate	 */	updateDate: function() {		var date = this.parseDate();		if(date==null) {			//this.entryElement.setHtml("");			this.entryInput.getDom().value = "";		} else {			//this.entryElement.setHtml(date.format(this.displayFormat));			this.entryInput.getDom().value = date.format(this.displayFormat);		}	},	/**	 * Enables the date picker field	 * @method enable	 */	enable: function() {		this.entryInput.getDom().disabled=false;		this.entryElement.removeClass("disabledDatePicker");		this.disabled=false;	},	/**	 * Disables the date picker field	 * @method disable	 */	disable: function() {		this.entryInput.getDom().disabled=true;		this.entryElement.addClass("disabledDatePicker");		this.disabled=true;	},	/*	 * Creates and returns a text represntaion of this Popup	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.DatePickerField]";	}});/** * A date picker is graphical date picker.   * @class Core.Ui.DatePicker * @see Core.Ui.DatePickerField */Core.Ui.DatePicker = Core.create({	/**	 * Creates a new date picker	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The element the data picker should be added to.	 */	init: function(element) {		this.element = Core.get(element);				var table = this.element.createChild({tag: "table"});		var header = table.createChild({tag: "thead"}); 		var body = table.createChild({tag: "tbody"});						var navRow = header.createChild({tag: "tr", klass: "nav"});		var prev = navRow.createChild({tag: "td", klass: "navButton", html: "&lt;"});		prev.addListener("click", this.changeMonth.bind(this, [-1], true));		var navText = navRow.createChild({tag: "td", klass: "navText", colSpan: "5"});		this.navTextMonth = navText.createChild({tag: "span"});		navText.createChild({html: " "});		this.navTextYear = navText.createChild({tag: "span"});		var next = navRow.createChild({tag: "td", klass: "navButton", html: "&gt;"});		next.addListener("click", this.changeMonth.bind(this, [1], true));				this.monthsPopup = new Core.Ui.ComboListPopup({0:"January", 1:"February", 2:"March", 3:"April", 4:"May", 5:"June", 6:"July", 7:"August", 8:"September", 9:"October", 10:"November", 11:"December"}, "datePickerListPopup");		this.monthsPopup.selectionEvent.addListener(this.selectMonth.bind(this));		this.navTextMonth.addListener("click", this.showMonths.bind(this));				this.yearsPopup = new Core.Ui.ComboListPopup({}, "datePickerListPopup");		this.yearsPopup.selectionEvent.addListener(this.selectYear.bind(this));		this.navTextYear.addListener("click", this.showYears.bind(this));								var daysOfTheWeek = new Array("M", "T", "W", "T", "F", "S", "S");		var row = header.createChild({tag: "tr", klass: "daysOfTheWeek"});		for(var day=0; day<7; day++) {			row.createChild({tag: "td", html: daysOfTheWeek[day]});			}				this.days = new Array();		for(var week=0; week<6; week++) {			var row = body.createChild({tag: "tr", klass: "days"});			for(var day=0; day<7; day++) {				this.days[(week*7)+day] = row.createChild({tag: "td", html: "0"});				}			}		body.addListener("mouseover", Core.Element.highlight.bind(["highlight", true]));		body.addListener("mouseout", Core.Element.highlight.bind(["highlight", false]));		body.addListener("click", this.selectDate.bind(this));				this.todaysDate = new Date();		this.currentDate = new Date();		this.selectedDate = null;		this.updateDisplay();				// events		/**		 * Fires when a date is selected		 * @event dateSelectedEvent		 * @param {Core.Ui.DatePicker} datePicker A reference to this date picker.		 * @param {Date} date The date selected.		 */		this.dateSelectedEvent = new Core.Event("Core.Ui.DatePicker:selected");	},	/* private: re-draws the display */	updateDisplay: function() {		//this.navText.setHtml(this.currentDate.format("mmmm yyyy"));		this.navTextMonth.setHtml(this.currentDate.format("mmmm"));		this.navTextYear.setHtml(this.currentDate.format("yyyy"));				var date = new Date();		date.setFullYear(this.currentDate.getFullYear(), this.currentDate.getMonth(), 1);				date.setHours(12, 0, 0);		date.adjust(0,0,-(((date.getDay()+1)%7)+5),0,0,0);				for(var index=0; index<42; index++) {			var highlight = this.days[index].hasClass("highlight");							if(date.getMonth()<this.currentDate.getMonth()) {				this.days[index].setClass("prevMonth");			} else if(date.getMonth()>this.currentDate.getMonth()) {				this.days[index].setClass("nextMonth");			} else {				this.days[index].setClass("currentMonth");				if(date.getFullYear()==this.todaysDate.getFullYear() && date.getMonth()==this.todaysDate.getMonth() && date.getDate()==this.todaysDate.getDate()) {					this.days[index].addClass("today");				}				if(this.selectedDate!=null && date.getFullYear()==this.selectedDate.getFullYear() && date.getMonth()==this.selectedDate.getMonth() && date.getDate()==this.selectedDate.getDate()) {					this.days[index].addClass("selected");				}			}			if(date.getDay()==0 || date.getDay()==6) {				this.days[index].addClass("weekend");			}			if(highlight) {				this.days[index].addClass("highlight");			}			this.days[index].setHtml(date.getDate());			date.adjust(0,0,1,0,0,0);		}	},	/* private: triggered when the month changes */	changeMonth: function(months) {		this.currentDate.adjust(0,months,0,0,0,0);		this.updateDisplay();	},	/* private: triggered when the use clicks on the month */	showMonths: function(event) {		var offset = this.navTextMonth.getTotalOffset();		offset.y -= this.currentDate.getMonth()*15+1;		offset.constrainTo(Core.Ui.scrollOffset(), Core.Ui.scrollOffset().add(Core.Ui.clientSize()));				this.monthsPopup.setOffset(offset);				this.monthsPopup.show(event);	},	/* private: triggered when the use selects a month */	selectMonth: function(event) {		this.currentDate.setMonth(event.value, 1);		this.updateDisplay();	},	/* private: triggered when the use clicks on the year */	showYears: function(event) {		var years = {};		for(var year = this.currentDate.getFullYear()-5; year<=this.currentDate.getFullYear()+5; year ++) {			years[year]=year;		}		this.yearsPopup.setOptions(years);				var offset = this.navTextYear.getTotalOffset();		offset.y -= 76;		offset.constrainTo(Core.Ui.scrollOffset(), Core.Ui.scrollOffset().add(Core.Ui.clientSize()));				this.yearsPopup.setOffset(offset);				this.yearsPopup.show(event);	},	/* private: triggered when the use selects a year */	selectYear: function(event) {		this.currentDate.setFullYear(event.value, this.currentDate.getMonth(), 1);		this.updateDisplay();	},	/* private: fires the date selected event */	selectDate: function(event) {		if(event && event.target) {			var element = Core.get(event.target);			var newDate = new Date();			var date = new Date();			var month = 0;			if(element.hasClass("currentMonth")) {				var month = this.currentDate.getMonth();				element.addClass("selected");			} else if(element.hasClass("prevMonth")) {				var month = (12+this.currentDate.getMonth()-1)%12;			} else {				var month = (this.currentDate.getMonth()+1)%12;			}			date.setFullYear(this.currentDate.getFullYear(), month, parseInt(element.getHtml()));			date.setHours(0, 0, 0);			this.setDate(date);						this.dateSelectedEvent.fire({datePicker: this, date: date});		}	},	/**	 * Set the current selected date for this date picker.	 * @method setDate	 * @param {Date} newDate The new date.	 */	setDate: function(newDate) {		if(newDate==null) {			this.currentDate = new Date();			this.selectedDate = null;		} else {			this.currentDate = newDate.clone();			this.selectedDate = newDate.clone();				}		this.updateDisplay();	},		/*	 * Creates and returns a text represntaion of this Popup	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.DatePicker]";	}});/* ############################## Tabs ############################## *//** * <p>The tabs object controls a set of tabs.</p> * <p>The HTML for a set of tabs should be a follows:</p><pre><code>&lt;div class="tabs" id="tabs"&gt;  &lt;ul class="tabs"&gt;    &lt;li&gt;Tab One&lt;/li&gt;    &lt;li&gt;Tab Two&lt;/li&gt;    &lt;li&gt;Tab Three&lt;/li&gt;  &lt;/ul&gt;  &lt;div class="tabsContent"&gt;    &lt;div class="tabContent"&gt;      &lt;!-- Tab One Content --&gt;    &lt;/div&gt;    &lt;div class="tabContent"&gt;      &lt;!-- Tab Two Content --&gt;    &lt;/div&gt;    &lt;div class="tabContent"&gt;      &lt;!-- Tab Three Content --&gt;    &lt;/div&gt;  &lt;/div&gt;&lt;/div&gt;</code></pre> * @class Core.Ui.Tabs * @see Core.Ui.Tabs.Tab */Core.Ui.Tabs = Core.create({	/**	 * Creates a new tab control	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The element tabs and their contents are in.	 * @param {String} cookieName (Optional) The state of the table is saved in a cookie, this defines the name of the cookie.  Defaults to "", which means no cookie will be created.	 * @param {String} cookiePath (Optional) The state of the table is saved in a cookie, this defines the path of the cookie.  Defaults to "/".	 */	init: function(element, cookieName, cookiePath) {		this.element = Core.get(element);		this.tabs = new Array();		this.activeTab = "";		this.cookieName = typeof(cookieName)=="string" ? cookieName : "";		this.cookiePath = typeof(cookiePath)=="string" ? cookiePath: "/";				/**		 * Fires when the selected tab is changed		 * @event tabSwitchEvent		 * @param {Core.Ui.Tabs.Tab} tab The tab now shown.		 * @param {String} title The title of the tab now shown.		 */		this.tabSwitchEvent = new Core.Event("Core.Ui.Tabs:tabSwitch");				if(this.element) {			var tabList = this.element.findFirstChild("ul", "tabs");			var contentList = this.element.findFirstChild("div", "tabsContent");						if(tabList!=null && contentList!=null) {				var tab = tabList.findFirstChild("li");				var content = contentList.findFirstChild("div");				while(tab!=null && content!=null) {					this.tabs.push(new Core.Ui.Tabs.Tab(this, tab, content));														var tab = tabList.findNextChild(tab, "li");					var content = contentList.findNextChild(content, "div");				}				if(this.tabs.length>0) {					this.tabs[0].getTabElement().addClass("first");					this.tabs[this.tabs.length-1].getTabElement().addClass("last");					this.tabs[0].display(true);					this.activeTab = this.tabs[0].getTitle();				}					} 						if(this.element.hasClass("verticalTabs")) {				var height = tabList.getSize().y;				for(var index=0; index<this.tabs.length; index++) {					this.tabs[index].getContentElement().getDom().style.minHeight=height+"px";				}			}						// cookie			if(this.cookieName!="") {				var oldTab = Core.readCookie("tabStatus_"+this.cookieName);				if(oldTab) {					this.switchTo(oldTab);				}			}		}	},	/* private: switch to a  tab */	showTab: function(tab) {		var before = true;		for(var index=0; index<this.tabs.length; index++) {			if(tab == this.tabs[index]) {				this.tabs[index].display(true);				before = false;			} else {				this.tabs[index].display(false, before);			}		}		this.tabSwitchEvent.fire({tab: tab, title: tab.getTitle()});		this.activeTab = tab.getTitle();		if(this.cookieName!="") {			Core.createCookie("tabStatus_"+this.cookieName, this.activeTab, null, this.cookieUrl);		}	},	/**	 * @method getActiveTab	 * @return {String} The name of the active tab.	 */	getActiveTab: function() {		return this.activeTab;	},	/**	 * Changes the active tab to the one requested	 * @method switchTo	 * @param {String} tabName The name or title of the tab to select.	 */	switchTo: function(tabName) {		var found = false;		for(var index=0; index<this.tabs.length && !found; index++) {			if(this.tabs[index].getTitle()==tabName) {				this.showTab(this.tabs[index]);				found=true;			}		}	},	/*	 * Creates and returns a text represntaion of this Tab control	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.Tabs]";	}});/** * Represents a Tab in Tab control.  This would be created by a Core.Ui.Tabs object. * @class Core.Ui.Tabs.Tab * @see Core.Ui.Tabs */Core.Ui.Tabs.Tab = Core.create({	/**	 * Creates a new tab for a tab control.	 * @method init	 * @constructor	 * @param {Core.Ui.Tabs} control The tab control for this tab.	 * @param {Core.Element} tab The element representing the tab part of the tab.	 * @param {Core.Element} content The element representing the conent for a tab.	 */	init: function(control, tab, content) {		this.control = control;		this.tab = tab;		this.content = content;		this.title = this.tab.getHtml();		this.tab.addClass("after");		this.tab.addListener("click", this.showTab.bind(this));		this.tab.addListener("mouseover", Core.Element.highlight.bind(["highlight", true]));		this.tab.addListener("mouseout", Core.Element.highlight.bind(["highlight", false]));	},	/**	 * @method getTitle	 * @return {String} The title of this tab.	 */	getTitle: function() {		return this.title;	},	/**	 * @method getTabElement	 * @return {Core.Element} The tab element for a tab.	 */	getTabElement: function() {		return this.tab;	},	/**	 * @method getContentElement	 * @return {Core.Element} The content element for a tab.	 */	getContentElement: function() {		return this.content;	},	/* private: used by the tab control to display or hide a tab */	display: function(display, before) {		if(display) {			this.tab.addClass("active");			this.content.addClass("active");						this.tab.removeClass("before");			this.tab.removeClass("after");		} else {			this.tab.removeClass("active");			this.content.removeClass("active");						if(typeof(before)=="boolean") {				if(before) {					this.tab.changeClass("after", "before");				} else {					this.tab.changeClass("before", "after");				}							}		}				if(Core.isIE) {			this.tab.removeClass("before-first");			this.tab.removeClass("after-last");			this.tab.removeClass("active-first");			this.tab.removeClass("active-last");						if(display) {				if(this.tab.hasClass("first")) {					this.tab.addClass("active-first");				}				if(this.tab.hasClass("last")) {					this.tab.addClass("active-last");				}			} else if(typeof(before)=="boolean") {				if(before && this.tab.hasClass("first")) {					this.tab.addClass("before-first");				}				if(!before && this.tab.hasClass("last")) {					this.tab.addClass("after-last");				}							}		}	},	/* private: switches to this tab*/	showTab: function(event) {		this.control.showTab(this);	},	/*	 * Creates and returns a text represntaion of this Tab	 * @method toString	 * @return {String}	 */	toString : function() {		return "[Core.Ui.Tab "+this.getTitle()+"]";	}});/* ############################## Table ############################## *//** * <p>The Core.Ui.Table takes a Core.Data object and represents the data in a table.  In * cases where the data is in a tree, sub rows are accessed via twisties.  You can  * confugure how the data is displayed by passing an options object to the construtor.   * The options object can have the following parameters:</p> * <table cellspacing="0" cellpadding="0" > * <tr><th style="width: 120px;">Option Name</th><th style="width: 120px;">Type</th><th>Description</th></tr> * <tr><td>design</td><td>Core.Data</td><td>A data object defines the columns for this table.  The table expects each row to have cells named <code>name</code>, <code>title</code> and <code>width</code>, for example <pre><code>var columns = new Core.Data.Array([  {name: "name", title: "Name", width: "300px"},  {name: "size", title: "Size (KB)", width: "125px"},  {name: "modified", title: "Modified", width: "125px"}]);</code></pre></td></tr> * <tr><td>data</td><td>Core.Data</td><td>A data object with the data for this table.</td></tr> * <tr><td>filterColumn</td><td>String</td><td>(Optional) The name of the column to which to apply a filter to.  By defualt no filter is used.</td></tr> * <tr><td>filterValue</td><td>String</td><td>(Optional) The value to search for in the filter.</td></tr> * <tr><td>filterPolarity</td><td>Boolean</td><td>(Optional) When true only entires are displayed that match the filter value, when false values that do not match the value are shown.  Defaults to true</td></tr> * <tr><td>nav</td><td>Core.Ui.Table.Navigator</td><td>(Optional) A page navigator for the table.</td></tr> * <tr><td>clickAction</td><td>Number/Function</td><td>(Optional) Defines what action to take when a row is clicked on, see the action constants for standard actions or pass a function for a custom action.  Defaults to Core.Ui.Table.OPEN</td></tr> * <tr><td>doubleClickAction</td><td>Number/Function</td><td>(Optional) Defines what action to take when a row is double clicked on, see the action constants for standard actions or pass a function for a custom action.  Defaults to Core.Ui.Table.OPEN</td></tr> * <tr><td>selectMany</td><td>Boolean</td><td>(Optional) Specifies if multiple rows can be selected.  Defaults to false.</td></tr> * <tr><td>selectCategories</td><td>Boolean</td><td>(Optional) Specifies if rows with children rows can be selected.  Defaults to false.</td></tr> * <tr><td>checkBoxes</td><td>Boolean</td><td>(Optional) Specifies if check boxes would be used to select rows.  Defaults to false.</td></tr> * <tr><td>boundColumn</td><td>String</td><td>(Optional) The name of the column used as a value for this row</td></tr> * <tr><td>element</td><td>Core.Element</td><td>The element where the table should be added</td></tr> * <tr><td>title</td><td>String</td><td>(Optional) The title for this table</td></tr> * <tr><td>tableClass</td><td>String</td><td>(Optional) A CSS class to be applied to the table</td></tr> * <tr><td>collapsible</td><td>Boolean</td><td>(Optional) Specifies if categories should be collapsible.  Defaults to true.</td></tr> * <tr><td>header</td><td>Boolean</td><td>(Optional) Specifies if this table should display a header.  Defaults to true.</td></tr> * <tr><td>expandColumn</td><td>String</td><td>(Optional) The name of the column which should expand to fill avalible space, defaults to "" which will expand the last column.</td></tr> * <tr><td>footer</td><td>Boolean</td><td>(Optional) Specifies if this table should display a footer.  Defaults to true.</td></tr> * <tr><td>height</td><td>Number/Function</td><td>(Optional) sets the height of the table.  For positive numbers the table is set to that height, for negative numbers the table is set that distance from the bottom of the screen, for 0 the table height is left to change natrually.  For functions the function is called to define the height of the table.  Defaults to 300</td></tr> * <tr><td>fitContent</td><td>Boolean</td><td>(Optional) When true the table will re-size to fit the content after the first load, the table height will then be fixed at this height.  Defaults to false.</td></tr> * <tr><td>hideWhenEmpty</td><td>Boolean</td><td>(Optional) When true the table is hidden if there is no data, defaults to false.</td></tr> * <tr><td>linkColumn</td><td>String</td><td>(Optional) The name of the column which should be shown as a link.</td></tr> * <tr><td>target</td><td>String</td><td>(Optional) For links in this table this defines what the target attributes should be set to.  Defaults to "_top"</td></tr> * <tr><td>cookieName</td><td>String</td><td>(Optional) The state of the table is saved in a cookie, this defines the name of the cookie.  Defaults to "", which means no cookie will be created.</td></tr> * <tr><td>cookiePath</td><td>String</td><td>(Optional) The state of the table is saved in a cookie, this defines the path of the cookie.  Defaults to "/".</td></tr> * </table> * @class Core.Ui.Table */Core.Ui.Table = Core.create({	/**	 * Create a new table.	 * @mehod inti	 * @constructor	 * @param Object options The options for this table, see the options table above to a list of possible options.	 */	init: function(options) {		Core.copyNew(options, Core.Options);				// data		this.design = options.readOption("design", null);		this.data = options.readOption("data", null);		this.rows = new Array();		this.hash = new Array();		this.rowsFlat = new Array();		this.filterColumn = options.readOption("filterColumn", "");		this.filterValue = options.readOption("filterValue", "");		this.filterPolarity = options.readBooleanOption("filterPolarity", true);				// navigation		this.nav = options.readOption("nav", null);		if(this.nav!=null) {			this.nav.registerTable(this);		}				// selection		this.selectedRows = new Array();		this.lastSelectedRow = null;		this.selectedValues = new Array();		this.selectMany = options.readBooleanOption("selectMany", false);		this.selectCategories = options.readBooleanOption("selectCategories", false);		this.checkBoxes = options.readBooleanOption("checkBoxes", false);		this.boundColumn = options.readOption("boundColumn", "");				// html element		this.element = Core.get(options.readOption("element", null));		this.title = options.readOption("title", "");		this.tableClass = options.readOption("tableClass", "");		this.collapsible = options.readBooleanOption("collapsible", true);		this.header = options.readBooleanOption("header", true);		this.expandColumn = options.readOption("expandColumn", "");		this.footer = options.readBooleanOption("footer", true);		this.fitContent = options.readBooleanOption("fitContent", false);		if(this.fitContent) {			this.height = null;		} else {			this.height = options.readIntOption("height", 300);			if(typeof(this.height)!="number" || this.height<=0) {				Core.EventManager.addListener(window, "resize", this.resize.bind(this));			}		}		this.hideWhenEmpty = options.readBooleanOption("hideWhenEmpty", false);				// actions		this.clickAction = options.readOption("clickAction", Core.Ui.Table.OPEN);		this.doubleClickAction = options.readOption("doubleClickAction", Core.Ui.Table.OPEN);		this.linkTarget = options.readOption("target", "_top");		this.linkColumn = options.readOption("linkColumn", "");				// cookie		this.cookieName = options.readOption("cookieName", "");		this.cookiePath = options.readOption("cookiePath", "/");		this.state=[];		if(this.cookieName!="") {			var stateString = Core.readCookie("tableStatus_"+this.cookieName);			if(stateString!=null) {				this.state = stateString.split(",");			}						this.scrollState = Core.readCookie("tableScrollStatus_"+this.cookieName);			this.ignoreScrollEvents=false;		}				if(this.data!=null && this.design!=null && this.element!=null) {			this.design.addListeners(this.createTable.bind(this), this.createTable.bind(this));			//this.data.addListeners(this.createRows.bind(this, [true]), this.createRows.bind(this, [false]));		}	},	/**	 * @method getFilterColumn	 * @return {String} The name of the column being used to filter entries.	 */	getFilterColumn: function() {		return this.filterColumn;	},	/**	 * @method getFilterValue	 * @return {String} The value being looked for in a filter.	 */	getFilterValue: function() {		return this.filterValue;	},	/**	 * @method getFilterPolarity	 * @return {Boolean} The polarity of the filter.  When true only entires are displayed that match the filter value, when false values that do not match the value are shown.	 */	getFilterPolarity: function() {		return this.filterPolarity;	},	/**	 * Sets the a filter for this table, only documents that match the filter value will be displayed in the view.	 * @method setFilter	 * @param {String} column The name of the column to be used to filter entries.	 * @param {String} value The value to look for in the filter	 * @param {Boolean} (Optional) When true only entires are displayed that match the filter value, when false values that do not match the value are shown.  Defaults to true</td></tr>	 */	setFilter: function(column, value, polarity) {		this.filterColumn = column;		this.filterValue = value;		this.filterPolarity = typeof(polarity)=="boolean" ? polarity : true;		var hasRow = false;		for(var index=0; index<this.rowsFlat.length; index++) {			if(!this.rowsFlat[index].filter(this.filterColumn, this.filterValue, this.filterPolarity)) {				hasRow = true;			}		}				if(hasRow) {			this.element.show();			this.noneRowElement.hide();		} else {			if(this.hideWhenEmpty) {				this.element.hide();			} else {				this.noneRowElement.show();			}		}	},	/**	 * Counts the visible rows	 * @method countVisibleRows	 * @return {Number} The number of visible rows.	 */	countVisibleRows: function() {		var count = 0;		for(var index=0; index<this.rowsFlat.length; index++) {			if(this.rowsFlat[index].isVisible()) {				count++;			}		}		return count;	},	/*	 * @method getSelectCategories	 * @return {Boolean} Retruns true when documents with children should be selected.	 */	getSelectCategories: function() {		return this.selectCategories;	},	/*	 * @method isCollapsible	 * @return {Boolean} Retruns true when rows with children can be collapsed.	 */	isCollapsible: function() {		return this.collapsible;	},	/*	 * @method useCheckBoxes	 * @return {Boolean} Retruns true when check boxes should be used to select rows.	 */	useCheckBoxes : function() {		return this.checkBoxes;	},	/*	 * @method getBoundColumn	 * @return {String} Retruns the column name being used as a value of each row.	 */	getBoundColumn: function() {		return this.boundColum;	},	/*	 * @method getClickAction	 * @return {Number}/{Function} Returns what is being used as a click action, see the action constants for standard actions.	 */	getClickAction : function() {		return this.clickAction;	},	/*	 * @method getDoubleClickAction	 * @return {Number}/{Function} Returns what is being used as a double click action, see the action constants for standard actions.	 */	getDoubleClickAction : function() {		return this.doubleClickAction;	},	/*	 * @method getLinkTarget	 * @return {String} Returns what the target attributes in links should be set to.	 */	getLinkTarget: function() {		return this.linkTarget;	},	/*	 * @method getLinkColumn	 * @return {String} Returns the name of the column which should be rendered as a link.	 */	getLinkColumn: function() {		return this.linkColumn;	},			/* private: listens to the design data object and creates the table element, columns, header and footer. */	createTable: function(event) {		this.element.clear();				this.columns = event.data.getRows();				// title		if(this.title && this.title!="") {			this.element.createChild({tag: "h4", html: this.title});		}				// - create table		this.tableDivElement = this.element.createChild({tag: "div", klass: "table"});		if(this.height!=null && this.height!=0) {			this.tableDivElement.addClass("fixedHeight");		}		this.tableElement = this.tableDivElement.createChild({tag: "table", cellPadding: "0", cellSpacing: "0"});		if(this.tableClass!="") {			this.tableElement.addClass(this.tableClass);		}			if(!this.tableElement.hasClass("nowrap") || !(Core.isIE6 || Core.isIE7)) { //IE bug, it can't do fixed width columns with a nowrap			var colGroup = this.tableElement.createChild({tag: "colgroup"});			if(this.useCheckBoxes()) {				colGroup.createChild({tag: "col", width: "16px"}, this.tableHeadElement);				}			for (var index=0; index<this.columns.length; index++) {				if((this.expandColumn=="" && index==this.columns.length-1) || this.expandColumn==this.columns[index].getValue("name")) {					colGroup.createChild({tag: "col", width: "auto"}, this.tableHeadElement);					} else {					colGroup.createChild({tag: "col", width: this.columns[index].getValue("width")}, this.tableHeadElement);					}			}		} else {			if(this.useCheckBoxes()) {				var colGroup = this.tableElement.createChild({tag: "colgroup"});				colGroup.createChild({tag: "col", width: "16px"}, this.tableHeadElement);				}		}		if(this.header) {			this.tableHeadElement = this.tableElement.createChild({tag: "thead"}); 		}		this.tableBodyElement = this.tableElement.createChild({tag: "tbody"});		this.tableElement.preventTextSelection();				// add header		if(this.header) {			var rowElement = this.tableHeadElement.createChild({tag: "tr"});			if(this.useCheckBoxes()) {				rowElement.createChild({tag: "td", html: "&nbsp;", klass: "check"});				}			for (var index=0; index<this.columns.length; index++) {				var cell = rowElement.createChild({tag: "td", html: this.columns[index].getValue("title")});				if(this.columns[index].hasCell("icon") && this.columns[index].getValue("icon")=="true") {					cell.addClass("icon");				}				if(this.columns[index].hasCell("align")) {					cell.addClass(this.columns[index].getValue("align"));				}			}		}				// none row		this.noneRowElement = this.tableBodyElement.createChild({tag: "tr", klass: "none"});		this.noneRowElement.createChild({tag: "td", colSpan: this.useCheckBoxes() ? this.columns.length+1 : this.columns.length, html:"None"});		this.noneRowElement.hide();				// add footer		if(this.footer) {			this.footerElement = this.element.createChild({tag: "div", klass: "footer"});			if(this.nav!=null) {				this.nav.createFooterNavigation(this.footerElement);			}		}				// size		this.resize();				// scroll		if(this.height!=null && this.height!=0) {			this.tableDivElement.addListener("scroll", this.saveScroll.bind(this));			this.tableBodyElement.addListener("scroll", this.saveScroll.bind(this));		}				if(this.hideWhenEmpty) {			this.element.hide();		}				// listen for data now		this.data.addListeners(this.createRows.bind(this, [true]), this.createRows.bind(this, [false]));	},	/**	 * This causes the tables data source to reload.	 * @method reload	 */	reload: function(event) {		this.data.load();	},	/* private: listens to the tables data source adding rows to the table when required. */	createRows: function(event, reset) {		var data;		if(reset) {			this.tableBodyElement.getDom().scrollTop=0;			this.tableBodyElement.clear();						this.noneRowElement = this.tableBodyElement.createChild({tag: "tr", klass: "none"});			this.noneRowElement.createChild({tag: "td", colSpan: this.useCheckBoxes() ? this.columns.length+1 : this.columns.length, html:"None"});			this.noneRowElement.hide();						this.rows = new Array();			this.hash = new Array();			data = event.data.getRows();		} else {			data = event.newRows;		}				this.addRows(data);				/* for(var index=0; index<data.length; index++) {			var row = this.addRow(data[index].getFullPosition(), this.tableBodyElement);			row.initialise(data[index], this.columns);		} */				this.rowsFlat = this.createRowsFlat();				if(this.countVisibleRows()>0) {			this.element.show();			this.noneRowElement.hide();		} else {			if(this.hideWhenEmpty) {				this.element.hide();			} else {				this.noneRowElement.show();			}		}				if(this.fitContent) {			var size = this.tableDivElement.getSize();			this.tableDivElement.getDom().style.height = size.y+"px";			this.fitContent=false;		}		if(this.scrollState!=null) {			if (!document.all || window.opera) {				if(this.tableBodyElement.getDom().scrollTop!=this.scrollState) {					this.ignoreScrollEvents=true;					this.tableBodyElement.getDom().scrollTop=this.scrollState;				}			} else {				if(this.tableDivElement.getDom().scrollTop!=this.scrollState) {					this.ignoreScrollEvents=true;					this.tableDivElement.getDom().scrollTop=this.scrollState;				}			}		}	},	/* private: called from createRows to loop through all the new rows and add them to the table */	addRows: function(data) {		for(var index=0; index<data.length; index++) {			var row = this.addRow(data[index].getFullPosition(), this.tableBodyElement);			row.initialise(data[index], this.columns);			this.reselect(row);						this.addRows(data[index].getChildRows());		}	},	/* private: called from addRows to add an individule row */	addRow: function(position) {		// calculate index for this row		var index;		var firstPositionPart=""+position.shift();				if(typeof(this.hash[firstPositionPart])!="undefined") {			index = this.hash[firstPositionPart];		} else {			index = this.rows.length;			this.hash[firstPositionPart] = index;		}			// add row		if(this.getRow(index)==null) {			var rowElement = this.tableBodyElement.createChild({tag: "tr"}, this.getNextElement(index));			this.rows[index] = new Core.Ui.Table.Row(this, rowElement);		}		// add children				if(position.length>0) {						return this.rows[index].addRow(position, this.tableBodyElement);		} else {			return this.rows[index];		}	},	/* private: tries to return the row at the specified index */	getRow: function(index) {		if(this.rows[index]) {			return this.rows[index];		} else {			return null;		}	},	/* private: used by add row when adding rows to the end of the table*/	getNextElement: function(index) {		while(++index<this.rows.length) {			if(this.rows[index]) {				return this.rows[index].getRowElement();			}		}		return null;	},	/* private: creates a flat list of rows, useful when selecting multiple rows.*/	createRowsFlat: function() {		var rv=new Array();		for(var index=0; index<this.rows.length; index++) {			if(this.rows[index]) {				rv = rv.concat(this.rows[index].createRowsFlat());			}		}		return rv;	},		/* private: called to size thie table, this is added to the window as a resize listener for tables that should extend to the bottom of the window. */	resize: function() {		if(this.height!=null && this.height!=0) {				var height=300;			if(typeof(this.height)=="number") {				height = this.height;				if(height<0) {					var clientSize = Core.Ui.clientSize();					var position = this.tableDivElement.getTotalOffset();					var footerSize = this.footer ? this.footerElement.getSize() : new Core.Point(0,0);					height = clientSize.y-position.y-footerSize.y+height;				}			} else if(typeof(this.height)=="function") {				height = this.height();			}					this.tableDivElement.getDom().style.height = height+"px";			if (!document.all || window.opera) {				if(this.header) {					this.tableBodyElement.getDom().style.height = height-this.tableHeadElement.getDom().offsetHeight+"px";				} else {					this.tableBodyElement.getDom().style.height = height+"px";				}			}		}	},		/* private: called by a row element when it is selected, this works out which other rows to select/unselect. */	selectRow : function(event, row) {		if(this.selectMany) {			var select = true;			if(!event.ctrlKey && !this.useCheckBoxes()) {				while(this.selectedRows.length>0) {					this.selectedRows.shift().setSelected(false);				}				this.selectedValues = new Array()	;			} else {				if(event.shiftKey && this.lastSelectedRow!=null) {					select = this.lastSelectedRow.isSelected();				} else {					select = !row.isSelected();				}			}					if(event.shiftKey && this.lastSelectedRow!=null) {				var start = this.rowsFlat.indexOf(this.lastSelectedRow);				var end = this.rowsFlat.indexOf(row);				for(var index=Math.min(start, end); index<=Math.max(start, end); index++) {					if(select) {						if(this.rowsFlat[index].canSelect()) {							this.rowsFlat[index].setSelected(true);							this.selectedRows.pushUnique(this.rowsFlat[index]);							this.selectedValues.pushUnique(this.getBoundValue(this.rowsFlat[index]));						}					} else {						this.rowsFlat[index].setSelected(false);						this.selectedRows.deleteElement(this.rows[index]);						this.selectedValues.deleteElement(this.getBoundValue(this.rowsFlat[index]));					}				}			} else {				this.lastSelectedRow = row;				if(select) {					if(row.canSelect()) {						row.setSelected(true);						this.selectedRows.push(row);						this.selectedValues.push(this.getBoundValue(row));					}				} else {					row.setSelected(false);					this.selectedRows.deleteElement(row);					this.selectedValues.deleteElement(this.getBoundValue(row));				}			}		} else {			if(event.ctrlKey && row.isSelected()) {				row.setSelected(false);				this.selectedValues = new Array()	;			} else {				if(this.lastSelectedRow!=null) {					this.lastSelectedRow.setSelected(false);				}				this.lastSelectedRow = row;				this.selectedRows[0] = row;				this.selectedValues[0] = this.getBoundValue(row);				row.setSelected(true);			}		}	},	/* private: get ths bound value for a row */	getBoundValue: function(row) {		return row.getValue(this.boundColumn);	},	/* private: called from add row, if the row was previously selected then it is reselected */	reselect: function(row) {		if(this.selectedValues.indexOf(this.getBoundValue(row))>=0) {			if(this.selectMany) {				this.selectedRows.push(row);			} else {				this.lastSelectedRow = row;				this.selectedRows[0] = row;			}			row.setSelected(true);		}	},	/**	 * This returns all the values, as defined by the bound column of all the selected rows.	 * @method getSelectedValues	 * @return {Array} The selected values.	 */	getSelectedValues: function() {		return this.selectedValues;	},	/**	 * This sets which values should be selected as defined by the bound column.	 * @method setSelectedValues	 * @param {Array} selectedValues The selected values.	 */	setSelectedValues: function(selectedValues) {		this.selectedValues = selectedValues;		this.selectedValues.trim();	},	/**	 * This clears the selected values so nothing is selected.	 * @method clearSelectedValues	 */	clearSelectedValues: function() {		this.selectedValues = new Array();	},	/* private saves the state of the table to a cookie */	saveState: function() {				if(this.cookieName!="") {			var stateString = "";			for(var index=0; index<this.rows.length; index++) {				stateString += this.rows[index].getState();			}			if(stateString!="") {				this.state = stateString.split(",");				Core.createCookie("tableStatus_"+this.cookieName, stateString.substring(1), null, this.cookiePath);			} else {				this.state = [];				Core.createCookie("tableStatus_"+this.cookieName, "-", null, this.cookiePath);			}		}	},	checkRowState: function(fullPosition) {		return this.state.indexOf(fullPosition)>=0;	},	saveScroll: function(event) {		if(!this.ignoreScrollEvents) {			if (!document.all || window.opera) {				Core.createCookie("tableScrollStatus_"+this.cookieName, this.tableBodyElement.getDom().scrollTop, null, this.cookiePath);			} else {				Core.createCookie("tableScrollStatus_"+this.cookieName, this.tableDivElement.getDom().scrollTop, null, this.cookiePath);			}			this.scrollState = null;		} else {			this.ignoreScrollEvents=false;		}	},	/**	 * This clears all the cookies storing state information for this table	 * @method clearState	 */	clearState: function() {				if(this.cookieName!="") {			this.state = [];			this.scrollState = 0;						Core.eraseCookie("tableStatus_"+this.cookieName, this.cookiePath);			Core.eraseCookie("tableScrollStatus_"+this.cookieName, this.cookiePath);		}	},	/**	 * Expands all rows in this table.  This may cause a lot of data to be loaded, which will hinder performace, use with care.	 * @method expandAll	 */	expandAll: function() {		for(var index=0; index<this.rows.length; index++) {			this.rows[index].expandAll();		}	},	/**	 * Expands all rows in this table	 * @method colapseAll	 */	collapseAll: function() {		for(var index=0; index<this.rows.length; index++) {			this.rows[index].collapseAll();		}	}});Core.copy(Core.Ui.Table, {	/**	 * @property NONE	 * @static Actions	 * Perform no action when a row is clicked on.	 */	NONE : 0,		/**	 * @property SELECT	 * @static Actions	 * Selects the row when a row is clicked on.	 */	SELECT : 1,		/**	 * @property OPEN	 * @static Actions	 * If in the data source rows include a cell called <code>url</code>.  This click action will open that url when the row is clicked on, 	 */	OPEN: 2});/* A private class used to control rows for tables */Core.Ui.Table.Row = Core.create({	init: function(table, rowElement) {		this.table = table;		this.rowElement = rowElement;		this.data;		this.rows = new Array();				this.hash = new Array();	},	initialise: function(data, columns) {		this.data = data;				// html element		if(this.table.useCheckBoxes()) {			var cell = this.rowElement.createChild({tag: "td", klass:"check"});			this.checkBox = cell.createChild({tag: "input", type: "checkbox"});		}		if(!this.hasChildren()) {			// normal row			for(var index=0; index<columns.length; index++) {				var colName = columns[index].getValue("name");				if(this.data.hasValue(colName)) {					var cellElement = this.rowElement.createChild({tag: "td"});					if(columns[index].hasCell("icon") && columns[index].getValue("icon")=="true") {						cellElement.addClass("icon");					}					if(columns[index].hasCell("align")) {						cellElement.addClass(columns[index].getValue("align"));					}					var cell = this.data.getCell(colName);										if(cell.getIndent()>0) {						cellElement.getDom().style.paddingLeft = (cell.getIndent()*16)+5;					}										if(colName==this.table.getLinkColumn()) {						cell.render(cellElement, this.data.getValue("url"));					} else {						cell.render(cellElement);					}										if(cell.isDynamic()) {						cell.updateEvent.addListener(cell.render.bind(cell, [cellElement], true));							//cell.updateEvent.addListener(this.renderCell.bind(this, [cellElement, cell], true));						}				} else {					this.rowElement.createChild({tag: "td", html: "&nbsp;"});				}							}			this.loaded = true;			this.expandable = false;		} else {			// categorised row			//var done = false;			var catCellElement = null;			var catCell = null;			var lastCellElement = null;			var colSpan = 1;						for(var index=0; index<columns.length; index++) {				var colName = columns[index].getValue("name");				if(this.data.hasValue(colName)) {					if(lastCellElement!=null) {						lastCellElement.getDom().colSpan = colSpan;						colSpan=1;					}										lastCellElement = this.rowElement.createChild({tag: "td", html: this.data.getValue(colName)});										if(catCellElement==null || (columns[index].hasValue("categorise") && columns[index].getValue("categorise")=="true")) {						catCellElement = lastCellElement;						catCell = this.data.getCell(colName);					}				} else {					if(lastCellElement!=null) {						colSpan++;					} else {						this.rowElement.createChild({tag: "td", html: "&nbsp;"});					}				}			}			if(lastCellElement!=null) {				lastCellElement.getDom().colSpan = colSpan;			}			if(catCellElement!=null) {				catCellElement.addClass("cat");				if(this.table.isCollapsible()) {					catCellElement.clear();					this.toggleImage = catCellElement.createChild({tag: "div", klass: "inline-block toggle"});					if(catCell.getIndent()>0) {						this.toggleImage.getDom().style.marginLeft = catCell.getIndent()*16;					}					catCellElement.createChild({tag:"span", html: catCell.getValue()});				}			}						this.rowElement.addClass("cat");						// normal flags			this.loaded = false;			this.expandable = true;			this.expanded = false;			this.start=1;			this.step=1;			this.children = new Array();			this.childInsert = this;		}				// row actions		var click = this.table.getClickAction();		var doubleClick = this.table.getDoubleClickAction();				if(!this.hasChildren() || this.table.getSelectCategories()) {			if(click==Core.Ui.Table.SELECT) {				this.rowElement.addListener("click", this.select.bind(this));				} else if(click==Core.Ui.Table.OPEN && this.data.hasValue("url")) {				this.rowElement.addListener("click", this.openLink.bind(this));				} else if(typeof(click)=="function") {				this.rowElement.addListener("click", click.bind([{row: this.data}], true));			} else if(this.hasChildren()) {				this.rowElement.addListener("click", this.toggle.bind(this));			}					if(doubleClick==Core.Ui.Table.OPEN && this.data.hasValue("url")) {				this.rowElement.addListener("dblclick", this.openLink.bind(this));				} else if(typeof(doubleClick)=="function") {				this.rowElement.addListener("dblclick", doubleClick.bind([{row: this.data}], true));			} else if(this.hasChildren()) {				this.rowElement.addListener("dblclick", this.toggle.bind(this));			}		}				if(this.hasChildren() && this.table.isCollapsible()) {			if(this.table.getSelectCategories()) {				this.toggleImage.addListener("click", this.toggle.bind(this));				this.toggleImage.addListener("dblclick", this.toggle.bind(this));				} else {				this.rowElement.addListener("click", this.toggle.bind(this));				this.rowElement.addListener("dblclick", this.toggle.bind(this));			}		}				if(this.table.useCheckBoxes()) {			this.checkBox.addListener("click", this.select.bind(this));			this.checkBox.addListener("dbllick", this.select.bind(this));		}				this.filter(this.table.getFilterColumn(), this.table.getFilterValue(), this.table.getFilterPolarity());				if(!this.table.isCollapsible() || this.table.checkRowState(this.data.getFullPosition().join("."))) {			this.setExpanded(true);		}	},	renderCell: function(cellElement, cell) {		cellElement.setHtml(cell.getValue());	},	getPosition: function() {		return this.data.getPosition();	},	hasChildren: function() {		return this.data.hasChildren();	},	getRowElement: function() {		return this.rowElement;	},	getLastRowElement: function() {		if(this.hasChildren) {			return this.getPrevElement(this.rows.length);		} else {			return this.getRowElement();		}			},	addRow: function(position, tableBodyElement) {		// calculate index for this row		var index;		var firstPositionPart=""+position.shift();				if(typeof(this.hash[firstPositionPart])!="undefined") {			index = this.hash[firstPositionPart];		} else {			index = this.rows.length;			this.hash[firstPositionPart] = index;		}			// add row		if(this.getRow(index)==null) {			var rowElement = tableBodyElement.createChild({tag: "tr"}, this.getPrevElement(index), true);			this.rows[index] = new Core.Ui.Table.Row(this.table, rowElement);						if(this.expanded) {				this.rows[index].show();			} else {				this.rows[index].setExpanded(false);				this.rows[index].hide();			}		}						// add children		if(position.length>0) {						return this.rows[index].addRow(position, tableBodyElement);		} else {			return this.rows[index];		}	},	getRow: function(index) {		if(this.rows[index]) {			return this.rows[index];		} else {			return null;		}	},	getPrevElement: function(index) {		while(--index>=0) {			if(this.rows[index]) {				return this.rows[index].getLastRowElement();			}		}		return this.getRowElement();	},	createRowsFlat: function() {		var rv=new Array();		rv.push(this);		for(var index=0; index<this.rows.length; index++) {			if(this.rows[index]) {				rv = rv.concat(this.rows[index].createRowsFlat());			}		}		return rv;	},		hasValue: function(cellName) {		return this.data.hasValue(cellName);	},	getValue: function(cellName) {		return this.data.getValue(cellName);	},		filter: function(column, value, polarity) {		this.rowElement.removeClass("filtered");		if(column!="" && value!="") {			if(this.data.hasValue(column)) {				if((polarity && this.data.getValue(column)!=value) || (!polarity && this.data.getValue(column)==value)) {					this.rowElement.addClass("filtered");					return true;				}			}		}		return false;	},	select: function(event) {		Core.EventManager.stopPropagation(event);		this.table.selectRow(event, this);	},	isSelected: function() {		return this.selected;	},	canSelect: function() {		if(!this.hasChildren() || this.table.getSelectCategories()) {			return !this.rowElement.hasClass("hidden") && !this.rowElement.hasClass("filtered");		} else {			return false;		}	},	setSelected : function(selected) {		if(typeof(selected)!="boolean") {			selected = !this.selected;		}				if(selected) {			this.rowElement.addClass("select");			if(this.checkBox) {				this.checkBox.getDom().checked = true;			}		} else {			this.rowElement.removeClass("select");			if(this.checkBox) {				this.checkBox.getDom().checked = false;			}		}		this.selected = selected;	},	openLink: function(event) {		Core.EventManager.stopPropagation(event);		url = this.data.getValue("url");		if(url!="") {			window.open(url, this.table.getLinkTarget());		} else if(this.expandable) {			this.toggle(event);		}	},			toggle: function(event) {		Core.EventManager.stopPropagation(event);		this.setExpanded();		this.table.saveState();	},	setExpanded: function(expanded) {		if(this.expandable) {			if(typeof(expanded)!="boolean") {				expanded = !this.expanded;			}					if(expanded) {				if(this.toggleImage) {					this.toggleImage.getDom().style.backgroundPosition = "-294px -108px";				}				for(var index=0; index<this.rows.length; index++) {					if(this.rows[index]) {						this.rows[index].show();					}				}				this.data.loadChildRows();			} else {				if(this.toggleImage) {					this.toggleImage.getDom().style.backgroundPosition = "-294px -96px";				}				for(var index=0; index<this.rows.length; index++) {					if(this.rows[index]) {						this.rows[index].setExpanded(false);						this.rows[index].hide();					}				}			}			this.expanded = expanded;					}	},	expandAll: function() {		this.setExpanded(true);		for(var index=0; index<this.rows.length; index++) {			if(this.rows[index]) {				this.rows[index].expandAll();			}		}	},	collapseAll: function() {		this.setExpanded(false);	},	getState: function() {		if(this.expanded) {			var state = ","+this.data.getFullPosition().join(".");			for(var index=0; index<this.rows.length; index++) {				state += this.rows[index].getState();			}			return state				} else {			return "";		}	},		show: function() {		this.rowElement.removeClass("hidden");	},	hide: function() {		this.rowElement.addClass("hidden");	},	isVisible: function() {		return !this.rowElement.hasClass("hidden") && !this.rowElement.hasClass("filtered");	}});/** * <p>For data sources that are broken in to "pages" or can be searched, navigation may need to * be added to the table.  The table navigator is an abstract super class that defines the methods * for adding that navigation to the table.  For data sources that require navigation a sub class * should be created implementing these methods for adding the navigation to the table.</p> * <p>As tables simply display all the data in the data object, the navigator should also be  * responsible for controlling the data source so the correct data is loaded for the "page" * or search.</p> * @class Core.Ui.Table.Navigator */Core.Ui.Table.Navigator = Core.create({	/**	 * This is called from the table to regiter it with the navigation.  This is abstract and so	 * does nothing, subclasses should override this method with appropriate code.	 * @method registerTable	 * @param {Core.Ui.Table} tabel This is the table.	 */	registerTable: function(table) {		// do nothing - abstract	},	/**	 * This is called from the table to draw the navigation in the footer of the table.  This is abstract and so	 * does nothing, subclasses should override this method with appropriate code.	 * @method craeteFooterNavigation	 * @param {Core.Element} footerElement This is the footer element for the table.	 */	createFooterNavigation: function(footerElement) {		// do nothing - abstract	}});/* ############################## Tree ############################## *//** * <p>The Core.Ui.Tree takes a Core.Data object and represents the data in a tree.  You  * can confugure how the data is displayed by passing an options object to the construtor.   * The options object can have the following parameters:</p> * <table cellspacing="0" cellpadding="0" > * <tr><th style="width: 120px;">Option Name</th><th style="width: 120px;">Type</th><th>Description</th></tr> * <tr><td>data</td><td>Core.Data</td><td>A data object with the data for this tree.  The tree expects each row to have cells named <code>title</code>, <code>type</code> and <code>url</code>.  For a list of valid types check the type constants.</td></tr> * <tr><td>element</td><td>Core.Element</td><td>The element where the tree should be added.</td></tr> * <tr><td>hasRoot</td><td>Boolean</td><td>(Optional) Specifies if the data contains only one top level entry which should be treated as the root of the tree.  Defaults to false.</td></tr> * <tr><td>clickAction</td><td>Number/Function</td><td>(Optional) Defines what action to take when the tree node is clicked on, see the action constants for standard actions or pass a function for a custom action.  Defaults to Core.Ui.Table.OPEN</td></tr> * <tr><td>doubleClickAction</td><td>Number/Function</td><td>(Optional) Defines what action to take when the tree node is double clicked on, see the action constants for standard actions or pass a function for a custom action.  Defaults to Core.Ui.Table.OPEN</td></tr> * <tr><td>target</td><td>String</td><td>(Optional) For links in this table this defines what the target attributes should be set to.  Defaults to "_top"</td></tr> * <tr><td>highlightCurrent</td><td>Boolean</td><td>(Optional) If the link points to the current url, this option is highlighted.  Defaults to true.</td></tr> * <tr><td>cookieName</td><td>String</td><td>(Optional) The state of the tree is saved in a cookie, this defines the name of the cookie.  Defaults to "", which means no cookie will be created.</td></tr> * <tr><td>cookiePath</td><td>String</td><td>(Optional) The state of the tree is saved in a cookie, this defines the path of the cookie.  Defaults to "/".</td></tr>  * </table> * @class Core.Ui.Tree */Core.Ui.Tree = Core.create({	/**	 * Creates a new tree	 * @method init	 * @constructor	 * @param Object options The options for this tree as defined above.	 */	init: function(options) {		Core.copyNew(options, Core.Options);				this.data = options.readOption("data", null);		this.element = Core.get(options.readOption("element", null));		this.element.addClass("outline");		this.hasRoot = options.readBooleanOption("hasRoot", false);						this.cookieName = options.readOption("cookieName", "");		this.cookiePath = options.readOption("cookiePath", "/");				this.clickAction = options.readOption("clickAction", Core.Ui.Tree.OPEN);		this.doubleClickAction = options.readOption("doubleClickAction", Core.Ui.Tree.OPEN);		this.linkTarget = options.readOption("target", "_top");		this.highlightCurrent = options.readBooleanOption("highlightCurrent", true);				this.entries = new Array();				if(this.data!=null && this.element!=null) {			this.element.preventTextSelection();			this.element.addListener("mouseover", Core.Element.highlight.bind(["highlight", true, "div"]));			this.element.addListener("mouseout", Core.Element.highlight.bind(["highlight", false, "div"]));					this.data.addListeners(this.setEntries.bind(this), this.setEntries.bind(this));		}	},	/*	 * @method getClickAction	 * @return {Number}/{Function} Returns what is being used as a click action, see the action constants for standard actions.	 */	getClickAction : function() {		return this.clickAction;	},	/*	 * @method getDoubleClickAction	 * @return {Number}/{Function} Returns what is being used as a double click action, see the action constants for standard actions.	 */	getDoubleClickAction : function() {		return this.doubleClickAction;	},	/*	 * @method getLinkTarget	 * @return {String} Returns what the target attribute should be set to in links.	 */	getLinkTarget: function() {		return this.linkTarget;	},	/*	 * @method getHighlightCurrent	 * @return Boolean Returns true if "current" link should be highlighted	 */	getHighlightCurrent: function() {		return this.highlightCurrent;	},	/* private: listens to data source and adds rows to tree */	setEntries: function(event) {		this.entries = new Array();				var rows = event.data.getRows();		for(var index=0; index<rows.length; index++) {			this.entries.push(new Core.Ui.Tree.Entry(this, this.element, true, this.hasRoot, rows[index]));		}				this.draw();	},	/* private: adds the html to document */	draw	: function() {		this.element.clear();				for(var index=0; index<this.entries.length; index++) {			this.entries[index].draw(index==0, index==this.entries.length-1);		}				this.loadState();				for(var index=0; index<this.entries.length; index++) {			this.entries[index].expandCurrent();		}	},		/* private saves the state of the tree to a cookie */	saveState: function() {		if(this.cookieName!="") {			var stateString = "";			for(var index=0; index<this.entries.length; index++) {				stateString += this.entries[index].getState(index);			}			if(stateString!="") {				Core.createCookie("treeStatus_"+this.cookieName, stateString.substring(1), null, this.cookiePath);			} else {				Core.createCookie("treeStatus_"+this.cookieName, "-", null, this.cookiePath);			}		}	},	/* private loads the state of the tree from a cookie */	loadState: function() {		var stateString = null;		if(this.cookieName!="") {			var stateString = Core.readCookie("treeStatus_"+this.cookieName);		}		if(stateString!=null) {			if(stateString!="-") {				var stateArray = stateString.split(",");				for(var stateIndex=0; stateIndex<stateArray.length; stateIndex++) {					var indices = stateArray[stateIndex].split(".");					if(indices.length>0) {						var index = parseInt(indices.shift());						if(!isNaN(index) && index<this.entries.length) {							this.entries[index].setState(indices);						}					}				}			}		} else {			for(var index=0; index<this.entries.length; index++) {				this.entries[index].setDefaultState();			}		} 	},		toString: function() {		return "[Core.Ui.Tree]";	}});Core.copy(Core.Ui.Tree, {	/**	 * @property BLANK	 * @static Types	 * Represents a folder node in the tree, it has a value of -2.	 */	BLANK: -2,	/**	 * @property FOLDER	 * @static Types	 * Represents a folder node in the tree, it has a value of -1.	 */	FOLDER: -1,	/**	 * @property LINK	 * @static Types	 * Represents a link node in the tree, it has a value of 0.	 */	LINK: 0,	/**	 * @property VIEW	 * @static Types	 * Represents a view node in the tree, it has a value of 2.	 */	VIEW: 2,	/**	 * @property FORM	 * @static Types	 * Represents a form node in the tree, it has a value of 3.	 */	FORM: 3,	/**	 * @property PAGE	 * @static Types	 * Represents a page node in the tree, it has a value of 7.	 */	PAGE: 7,		/**	 * @property NONE	 * @static Actions	 * Perform no action when a row is clicked on.	 */	NONE : 0,		/**	 * @property OPEN	 * @static Actions	 * If in the data source rows include a cell called <code>url</code>.  This click action will open that url when the row is clicked on, 	 */	OPEN: 2});/* private tree entry class */Core.Ui.Tree.Entry = Core.create({	init: function(tree, element, root, displayAsRoot, row) {		this.tree = tree;		this.element = element;		this.parentElement = element;		this.root = root;		this.displayAsRoot = displayAsRoot;		this.title = row.getValue("title");		this.type = row.getValue("type");		this.url = row.getValue("url");		this.modifer = 0;		this.current = this.url!="" && location.href.endsWith(this.url);							this.entries = new Array();						var rows = row.getChildRows();		for(var index=0; index<rows.length; index++) {			this.entries.push(new Core.Ui.Tree.Entry(this.tree, this.element, false, false, rows[index]));		}				this.expandable = this.entries.length>0;		this.expanded = false;		this.expandedDefault = this.expandable ? row.getValue("expanded")=="true" : false;				this.type = this.type=="" || (this.expandable && this.type==0) ? -1 : this.type;	},	draw	: function(first, last, padding) {		var html="";		if(padding) {			html += padding;		} else {			padding = "";		}				this.modifer = last ? 20 : first ? -20 : 0;		if(!this.expandable) {						html +="<div class=\"inline-block img\" style=\"background-position: 0px -"+(60+this.modifer)+"px;\"></div>";			if(this.type>Core.Ui.Tree.BLANK) {				html +="<div class=\"inline-block img\" style=\"background-position: 0px -"+(180+(this.type*20))+"px;\"></div>";			}			html +="<span>"+this.title+"</span>";			this.element = this.parentElement.createChild({tag:"div", html: html});					} else {			this.element = this.parentElement.createChild({tag:"div", html: html});			this.toggleImage = this.element.createChild({tag:"div", klass: "inline-block img"});			this.toggleImage.getDom().style.backgroundPosition = "0px -"+(120+this.modifer)+"px";			if(this.type>Core.Ui.Tree.BLANK) {				this.folderImage = this.element.createChild({tag:"div", klass: "inline-block img"});					this.folderImage.getDom().style.backgroundPosition = "0px -"+(180+(this.type*20))+"px";					}			this.element.createChild({tag:"span", html:this.title});						if(last) {				padding += "<div class=\"inline-block img\"></div>";			} else {				padding += "<div class=\"inline-block img\" style=\"background-position: 0px -20px;\"></div>";			}			for(var index=0; index<this.entries.length; index++) {				this.entries[index].draw(false, index==this.entries.length-1, padding);					}		}				// actions		var click = this.tree.getClickAction();		var doubleClick = this.tree.getDoubleClickAction();				if(click==Core.Ui.Tree.OPEN && this.url!="") {			this.element.addListener("click", this.openLink.bind(this));			} else if(typeof(click)=="function") {			this.element.addListener("click", click.bind([{url:this.url}], true));		} else if(this.expandable) {			this.element.addListener("click", this.toggle.bind(this));		}				if(doubleClick==Core.Ui.Tree.OPEN && this.url!="") {			this.element.addListener("dblclick", this.openLink.bind(this));			} else if(typeof(doubleClick)=="function") {			this.element.addListener("dblclick", doubleClick.bind([{url:this.url}], true));		} else if(this.expandable) {			this.element.addListener("dblclick", this.toggle.bind(this));		}				if(this.expandable) {			this.toggleImage.addListener("click", this.toggle.bind(this));			this.toggleImage.addListener("dblclick", this.toggle.bind(this));		}				// check for current		if(this.current && this.tree.getHighlightCurrent()) {			this.element.addClass("current");		}				// hide by defualt		if(!this.root) {			this.hide();		}	},		openLink: function(event) {		if(this.url!="") {			window.open(this.url, this.tree.getLinkTarget());		} else if(this.expandable) {			this.toggle(event);		}	},			toggle: function(event) {		Core.EventManager.stopPropagation(event);		this.setExpanded();		this.tree.saveState();	},	setExpanded: function(expanded) {		if(this.expandable) {			if(typeof(expanded)!="boolean") {				expanded = !this.expanded;			}					if(expanded) {				this.toggleImage.getDom().style.backgroundPosition = "-20px -"+(120+this.modifer)+"px";				if(this.type>Core.Ui.Tree.BLANK) {					this.folderImage.getDom().style.backgroundPosition = "-20px -"+(180+(this.type*20))+"px";				}				for(var index=0; index<this.entries.length; index++) {					this.entries[index].show();				}			} else {				this.toggleImage.getDom().style.backgroundPosition = "0px -"+(120+this.modifer)+"px";				if(this.type>Core.Ui.Tree.BLANK) {					this.folderImage.getDom().style.backgroundPosition = "0px -"+(180+(this.type*20))+"px";				}				for(var index=0; index<this.entries.length; index++) {					this.entries[index].setExpanded(false);					this.entries[index].hide();				}			}			this.expanded = expanded;		}	},	show: function() {		this.element.show();	},	hide: function() {		this.element.hide();	},		getState: function(prefix) {		if(this.expanded) {			var state = "";			for(var index=0; index<this.entries.length; index++) {				state += this.entries[index].getState(prefix+"."+index);			}			if(state!="") {				return state			} else {				return ","+prefix;			}				} else {			return "";		}	},	setState: function(indices) {		this.setExpanded(true);				if(indices.length>0) {			var index = parseInt(indices.shift());			if(!isNaN(index) && index<this.entries.length) {				this.entries[index].setState(indices);			}		}	},	setDefaultState: function() {		if(this.expandedDefault) {			this.setExpanded(true);			for(var index=0; index<this.entries.length; index++) {				this.entries[index].setDefaultState();			}		}	},	expandCurrent: function() {		if(this.current) {			return true;		} else {			for(var index=0; index<this.entries.length; index++) {				if(this.entries[index].expandCurrent()) {					this.setExpanded(true);					return true;				}			}			return false;		}	},		toString: function() {		return "[Core.Ui.Tree.Entry: "+this.title+"]";	}});/* ############################## Navigation ############################## *//** * <p>The Core.Ui.Navigation uses the data in a Core.Data object to create a simple colapsable navigation system normally for use on the left hand side. * You can confugure how the data is displayed by passing an options object to the construtor.   * The options object can have the following parameters:</p> * <table cellspacing="0" cellpadding="0" > * <tr><th style="width: 120px;">Option Name</th><th style="width: 120px;">Type</th><th>Description</th></tr> * <tr><td>data</td><td>Core.Data</td><td>A data object with the data for this navigation.  The tree expects each row to have cells named <code>title</code> and <code>url</code>. First level entries are treated as categories, all others are treated as links</td></tr> * <tr><td>element</td><td>Core.Element</td><td>The element where the navigation should be added.</td></tr> * <tr><td>target</td><td>String</td><td>(Optional) Defines what the target attribute for links should be set to.  Defaults to "_top"</td></tr> * <tr><td>cookieName</td><td>String</td><td>(Optional) The state of the navigation object is stored in a cookie, this defines the name of the cookie.  Defaults to "", which means no cookie will be created.</td></tr> * <tr><td>cookiePath</td><td>String</td><td>(Optional) The state of the navigation object is saved in a cookie, this defines the path of the cookie.  Defaults to "/".</td></tr> * <tr><td>lastUrl</td><td>String</td><td>(Optional) The url of the last selected entry, this overides the state saved by a cookie.</td></tr> * <tr><td>active</td><td>String</td><td>(Optional) The title of the active entry. The title should be the section title followed by the link title sperated by a /.  For example "Activities/By Company".  This overrides any state saved by a cookie or passed by the lastUrl parameter.</td></tr>  * </table> * @class Core.Ui.Navigation */Core.Ui.Navigation = Core.create({	/**	 * Creates a new navigation object	 * @method init	 * @constructor	 * @param Object options The options for this navigation object as defined above.	 */	init: function(options) {		Core.copyNew(options, Core.Options);				this.data = options.readOption("data", null);		this.element = Core.get(options.readOption("element", null));		this.element.addClass("navigation");								this.cookieName = options.readOption("cookieName", "");		this.cookiePath = options.readOption("cookiePath", "/");		this.lastUrl = options.readOption("lastUrl", "");		if (this.lastUrl=="") {			this.lastUrl=unescape(Core.readCookie("navStatus_"+this.cookieName));		}		this.active = options.readOption("active", "");				this.linkTarget = options.readOption("target", "_top");				if(this.data!=null && this.element!=null) {			/*this.element.preventTextSelection();			this.element.addListener("mouseover", Core.Element.highlight.bind(["highlight", true, "div"]));			this.element.addListener("mouseout", Core.Element.highlight.bind(["highlight", false, "div"]));*/					this.data.addListeners(this.setEntries.bind(this), this.setEntries.bind(this));		}	},	/*	 * @method getLinkTarget	 * @return {String} Returns what the target attribute should be set to in links.	 */	getLinkTarget: function() {		return this.linkTarget;	},	/* private: listens to data source and creates navigation */	setEntries: function(event) {		var foundActive = false;				this.element.clear();		this.entries = new Array();		this.subSections = new Array();		var active = typeof(this.active)=="string" ? this.active.split("/") : ["",""];				var rows = event.data.getRows();		for(var index=0; index<rows.length; index++) {			var html = "<a href=\""+rows[index].getValue("url")+"\" target=\""+this.getLinkTarget()+"\">"+rows[index].getValue("title")+"</a>";			var entry = this.element.createChild({tag: "div", klass: (index==0?"section firstSection":"section"), html: rows[index].getValue("title")});						var subEntries = this.setSubEntries(rows[index].getChildRows(), active[0]==rows[index].getValue("title") ? active[1] : "");			if(subEntries!=null) {				entry.addListener("click", this.switchSection.bind(this, [subEntries]));				this.subSections.push(subEntries);			}			if (subEntries.hasClass("activeEntries")) {				foundActive=true;			}		}				if(!foundActive && this.subSections.length>0) {			this.subSections[0].addClass("activeEntries");		}	},	/* private: setup sections in navigation */	setSubEntries: function(entries, active) {		if(entries) {			var nav = this.element.createChild({tag: "div", klass: "entries"});			for(var index=0; index<entries.length; index++) {				var url = entries[index].getValue("url");				var html = "<a href=\""+url+"\" target=\""+this.getLinkTarget()+"\">"+entries[index].getValue("title")+"</a>";				var entry = nav.createChild({tag: "div", klass: "entry", html: html});				entry.addListener("click", this.saveState.bind(this, [url], true));				if(active!="") {					if(active==entries[index].getValue("title")) {						nav.addClass("activeEntries");						entry.addClass("currentEntry");					}				} else {					if(typeof(this.lastUrl)=="string" && this.lastUrl===unescape(entries[index].getValue("url")).replace(/!/g, "?")) {						nav.addClass("activeEntries");						entry.addClass("currentEntry");					}				}				}			return nav;		} else {			return null;		}	},	/* private listener to switch the active section */	switchSection: function(event, currentSection) {		for(var index=0; index<this.subSections.length; index++) {			this.subSections[index].removeClass("activeEntries");			}		currentSection.addClass("activeEntries");		},	/* private saves the state of the tree to a cookie */	saveState: function(url) {		if(this.cookieName!="" && url!="") {			Core.createCookie("navStatus_"+this.cookieName, url.replace(/!/g, "?"), null, this.cookiePath);		}	},	toString: function() {		return "[Core.Ui.Navigation]";	}});/* ############################## Split Pane ############################## *//** * Create a ui element where the page is divided at the desired ratio.  The user can however * move the divider to change the size of the two parts. * @class Core.Ui.SplitPane */Core.Ui.SplitPane = Core.create({	/**	 * Creates a new split pane.  The html for a split pane relies on the left and right content 	 * being in sperate div tags inside a container.  For example:<pre><code>&lt;div class="splitPane" id="splitPane"&gt;  &lt;div class="left"&gt;Left content&lt;/div&gt;  &lt;div class="right"&gt;Right content&lt;/div&gt;&lt;/div&gt;</code></pre>	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The id, html node or Core.Element for the split pane container.	 * @param {Number} ratio (Optional) A number between 0 and 1 represnting the size of the left hand element, defults to 0.25 (a quater).  This will be overriden if a cookie is set.	 * @param {Number} height (Optional) The height of the split pane.  For positive numbers the split pane is set to that height, for negative numbers the split pane is set that distance from the bottom of the screen. For 0 the height is not adjusted.  For functions the function is called to define the height of the split pane. Defaults to -10.	 * @param {String} cookieName (Optional) The name of the cookie to save the user selected width.  Defaults to "", which means no cookie will be created and the ratio is used. 	 * @param {String} cookiePath (Optional) Defines the path of the cookie.  Defaults to "/"	 */	 /**	 * Creates a new split pane where the height for the left and right components are controled       * speratly and the element sized verticaly are not the left and right divs.  The html for a split       * pane relies on the left a right content being in sperate div tags inside a container.  For example:<pre><code>&lt;div class="splitPane" id="splitPane"&gt;  &lt;div class="left"&gt;Left content&lt;/div&gt;  &lt;div class="right"&gt;Right content&lt;/div&gt;&lt;/div&gt;</code></pre>	 * @method init2	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The id, html node or Core.Element for the split pane container.	 * @param {Number} ratio A number between 0 and 1 represnting the size of the left hand element.  This will be overriden if a cookie is set.	 * @param {String}/HTMLElement/{Core.Element} leftComponent The id, html node or Core.Element for the component in the left hand side that should be sized vertically.  Specify "" to use the default div element.	 * @param {Number} leftHeight The height of the left component.  For positive numbers the left component is set to that height, for negative numbers the left component is set that distance from the bottom of the screen. For functions the function is called to define the height of the left component.	 * @param {String}/HTMLElement/{Core.Element} rightComponent The id, html node or Core.Element for the component in the right hand side that should be sized vertically.  Specify "" to use the default div element.	 * @param {Number} rightHeight The height of the right component.  For positive numbers the right component is set to that height, for negative numbers the right component is set that distance from the bottom of the screen. For functions the function is called to define the height of the right component. 	 * @param {String} cookieName (Optional) The name of the cookie to save the user selected width.  Defaults to "", which means no cookie will be created and the ratio is used. 	 * @param {String} cookiePath (Optional) Defines the path of the cookie.  Defaults to "/"	 */	init: function(element, ratio, leftComponent, leftHeight, rightComponent, rightHeight, cookieName, cookiePath) {		this.element = Core.get(element);		this.leftElement = this.element.findFirstChild("div");		this.rightElement = this.element.findNextChild(this.leftElement, "div");		this.sliderElement = this.element.createChild({tag: "div", klass: "slider"});		this.ratio = ratio ? ratio : 0.25;		this.leftWidth = null;				if(typeof(rightHeight)=="undefined") {			var height = typeof(leftComponent)!="undefined" ? leftComponent : -10;			this.leftComponent = this.leftElement;			this.leftHeight = height;			this.rightComponent = this.rightElement;			this.rightHeight = height;			this.cookieName = leftHeight ? leftHeight : "";			this.cookiePath = rightComponent ? rightComponent : "/";		} else {			this.leftComponent = leftComponent=="" ? this.leftElement : Core.get(leftComponent);			this.leftHeight = leftHeight;			this.rightComponent = rightComponent=="" ? this.rightElement : Core.get(rightComponent);			this.rightHeight = rightHeight;			this.cookieName = cookieName ? cookieName : "";			this.cookiePath = cookiePath ? cookiePath : "/";		}		//alert(this.element+" "+this.ratio+" "+this.leftComponent+" "+this.leftHeight+" "+this.rightComponent+" "+this.rightHeight);				this.leftComponent.addClass("scroll");		this.rightComponent.addClass("scroll");				if(this.cookieName!="") {			var value = Core.readCookie("splitPaneStatus_"+this.cookieName);			if(!isNaN(parseInt(value))) {				this.leftWidth=parseInt(value);			}		}						this.resize();		Core.EventManager.addListener(window, "resize", this.resize.bind(this));				var drag = new Core.Ui.DragControl(this.sliderElement, this.sliderElement, Core.Ui.DragControl.HORIZONTAL, this.element);		drag.moveEvent.addListener(this.moveSlider.bind(this));	},	/* private called when the window resizes */	resize: function() {		// --- Width ---		var paneSize = this.element.getSize();		var paneWidth = paneSize.x-10;		if(this.leftWidth==null) {			this.leftWidth = paneWidth * this.ratio;			}		var leftWidth = Math.min(Math.max(0, this.leftWidth), paneWidth-7);		var rightWidth = paneWidth - leftWidth - 7;				this.sliderElement.getDom().style.left = (leftWidth+5)+"px";				// adjust for padding + borders		if (!document.all || window.opera) {			leftWidth -= parseInt(this.leftElement.readStyle("padding-left")) + parseInt(this.leftElement.readStyle("padding-right"));			leftWidth -= parseInt(this.leftElement.readStyle("border-left-width")) + parseInt(this.leftElement.readStyle("border-right-width"));			rightWidth -= parseInt(this.rightElement.readStyle("padding-left")) + parseInt(this.rightElement.readStyle("padding-right"));			rightWidth -= parseInt(this.rightElement.readStyle("border-left-width")) + parseInt(this.rightElement.readStyle("border-right-width"));		}				this.leftElement.getDom().style.width =   leftWidth+"px";		this.rightElement.getDom().style.width = rightWidth+"px";						// --- Height ---		if(this.leftHeight==0) {			this.element.getDom().style.height = (Math.max(this.leftComponent.getSize().y, this.rightComponent.getSize().y)+5)+"px";		} else {			// left height			var leftHeight=-10;			if(typeof(this.leftHeight)=="number") {				leftHeight = this.leftHeight;				if(leftHeight<0) {					var clientSize = Core.Ui.clientSize();					var position = this.leftComponent.getTotalOffset();					leftHeight = clientSize.y-position.y+leftHeight;				}			} else if(typeof(this.leftHeight)=="function") {				leftHeight = this.leftHeight();			}					// right height			var rightHeight=-10;			if(typeof(this.rightHeight)=="number") {				rightHeight = this.rightHeight;				if(rightHeight<0) {					var clientSize = Core.Ui.clientSize();					var position = this.rightComponent.getTotalOffset();					rightHeight = clientSize.y-position.y+rightHeight;				}			} else if(typeof(this.rightHeight)=="function") {				rightHeight = this.rightHeight();			}						// adjust for padding			if (!Core.isIE) {				leftHeight -= parseInt(this.leftComponent.readStyle("padding-top")) + parseInt(this.leftComponent.readStyle("padding-bottom"));				rightHeight -= parseInt(this.rightComponent.readStyle("padding-top")) + parseInt(this.rightComponent.readStyle("padding-bottom"));			}			// apply heigts					this.leftComponent.getDom().style.height = (leftHeight-5)+"px";			this.rightComponent.getDom().style.height = (rightHeight-5)+"px";						var elementHeight = Math.max(this.leftElement.getSize().y, this.rightElement.getSize().y);						this.element.getDom().style.height = (elementHeight)+"px";			this.sliderElement.getDom().style.height =  (elementHeight-5)+"px";		}	},	moveSlider: function(event) {		this.leftWidth = event.offset.x-5;		if(this.cookieName!="") {			Core.createCookie("splitPaneStatus_"+this.cookieName, this.leftWidth, 0, this.cookiePath);		}		this.resize();	}});/* ############################## Dialog Field ############################## *//** * A dialog field converts a normal html input elment into a field with a selection dialog popup,  * @class Core.Ui.DialogField * @see Core.Ui.SelectionDialog */Core.Ui.DialogField = Core.create({	/**	 * Creates a new new view dialog field	 * @method init	 * @constructor	 * @param {Core.Form} form The form the field is part of.	 * @param {String}/HTMLInput/{Core.Element} field The name, HTML input or Core.Element for the input to replace.	 * @param {Core.Ui.SelectionDialog} dialog The selection dialog that should be shown.	 */	 	init: function(form, field, dialog) {		this.form = form;		this.field = field;		this.dialog = dialog						// replace input		var element = this.form.getElement(this.field);		this.entryElement = element.createSibling({tag: "div", klass: element.hasClass("double") ? "comboFieldDouble" : element.hasClass("full") ? "comboFieldFull" : "comboField"});		this.entryElement.addListener("click", this.entryListener.bind(this));		this.entryLink = this.entryElement.createChild({tag:"a", href:"#", html: this.form.getValue(this.field)});		this.entryLink.addListener("click", this.entryListener.bind(this));		var newInput = element.getDom().cloneNode(false);		newInput.type="hidden";		element.replace(newInput);		element.getDom().onSetValue = this.updateValue.bind(this);				// listen to dialog		this.dialog.closeEvent.addListener(this.dialogListener.bind(this));						// events		/**		 * Fires when the field is changed via the dialog.		 * @event changeEvent		 * @param {Core.Element} field The field updated.		 * @param {String} value The new value.		 */		this.changeEvent = new Core.Event("Core.Ui.TableDialogField:change");	},	/* listens to the field, when clicked it opens the dialog */	entryListener: function(event) {		this.dialog.setSelectedValues(this.form.getValue(this.field).split(", "));		this.dialog.show(event);	},	/* dialogListener updates the field when the dialog closes */	dialogListener: function(event) {		if(event.selected!=null) {			var value = "";			if(event.selected.length>0) {				value = event.selected.join(", ");			}			this.form.setValue(this.field, value);			this.entryLink.setHtml(value);			this.changeEvent.fire({field: this.field, value: value});		}		this.entryLink.getDom().focus();	},	/* private updates the display from the field */	updateValue: function() {		this.entryLink.setHtml(this.form.getValue(this.field));	},	toString : function() {		return "[Core.Ui.DialogField]"	}});/* ############################## Selection Dialog ############################## *//** * This is an abstract dialog class that wraps a dialog used for selecting values, it is used by Core.Ui.DialogField * @class Core.Ui.SelectionDialog * @see Core.Ui.DialogField */Core.Ui.SelectionDialog = Core.create({	 /**	 * This creates the view dialog.	 * @method init	 * @constructor	 * @param {String} dialogTitle The title for the dialog.	 * @param {String} cssClass (Optional) The CSS class for this dialog.	 */	init: function(dialogTitle, cssClass) {		// create dialog		this.dialog=new Core.Ui.Dialog(true, true, cssClass?cssClass:"", dialogTitle, "", ["OK","Cancel"]);		this.dialog.closeEvent.addListener(this.closeListener.bind(this));				// events		/**		 * Fires when the dialog is closed.		 * @event closeEvent		 * @param {Core.Domino.ViewDialog} dialog A refrence to this dialog		 * @param {String} result The button used to close the close the dialog		 * @param {Array} selected An array of strings with all the values selected in the view.		 */		this.closeEvent = new Core.Event("Core.Ui.SelectionDialog:close");	},	/**	 * Set the values that should be selected, this is abstract implmentation and it is intended that sub classes override this.	 * @method setSelectedValues	 * @param {Array} selected An array of strings with the values to select.	 */	setSelectedValues: function(selected) {		// abstract	},	/**	 * Gets the values that are selected, this is abstract implmentation and it is intended that sub classes override this.	 * @method getSelectedValues	 * @return {Array} An array of strings with the values selected.	 */	getSelectedValues: function() {		// abstract	},	/**	 * Makes the dialog visable	 * @method show	 * @param {Object} event If the show opereation is triggered by an event, this should be the event information object 	 */	show: function(event) {		this.dialog.show(event);	},	/**	 * Waits for the dialog to close and fires a modified close event with the selected values	 * @method closeListener      */	closeListener: function(event) {		var result = event.result;		var selected = null;		if(result=="OK") {			selected = this.getSelectedValues();		}				this.closeEvent.fire({dialog: this, result: result, selected: selected});		this.setSelectedValues([]);	},	toString : function() {		return "[Core.Ui.SelectionDialog]"	}});/* ############################## Table Dialog ############################## *//** * A table dialog is a dialog with a table used for making a selection. * @class Core.Ui.TableDialog * @extends Core.Ui.SelectionDialog */Core.Ui.TableDialog = Core.create(Core.Ui.SelectionDialog, {	/**	 * This creates the view dialog.	 * @method init	 * @constructor	 * @param {Core.Data} design The design source for the table.	 * @param {Core.Data} data The data source for the table.	 * @param {String} boundColumn The column to use as a value for the selected row.	 * @param Boolean selectMany When true the user will be able to select multiple rows.	 * @param {String} dialogTitle The title for the dialog.	 */	 /**	 * This creates the view dialog.	 * @method init2	 * @constructor	 * @param {Object} options The options object for the table.  The following options will be set automaticly: element, clickAction and doubleClickAction.	 * @param {String} dialogTitle The title for the dialog.	 */	init: function(design, data, column, selectMany, dialogTitle) {		if(arguments.length>2) {			// init1			Core.Ui.SelectionDialog.prototype.init.call(this, dialogTitle);										// create table			var tableElement = this.dialog.getContentElement().createChild({tag:"div", klass:"view"});			this.table = new Core.Ui.Table({				data: data,				design: design,				element: tableElement,				header: false,				selectMany: selectMany,				checkBoxes: selectMany,				boundColumn: column ? column : "",				clickAction: Core.Ui.Table.SELECT,     	          doubleClickAction: this.viewDoubleClickListener.bind(this)			});		} else {			// init 2			dialogTitle = data;			var options = design;			Core.Ui.SelectionDialog.prototype.init.call(this, dialogTitle);								// create table			options.element = this.dialog.getContentElement().createChild({tag:"div", klass:"view"});			options.clickAction = Core.Ui.Table.SELECT;			options.doubleClickAction = this.viewDoubleClickListener.bind(this);			this.table = new Core.Ui.Table(options);		}	},	/**	 * Set the values that should be selected in the table.	 * @method setSelectedValues	 * @param {Array} selected An array of strings with the values to select.	 */	setSelectedValues: function(selected) {		this.table.setSelectedValues(selected);	},	/**	 * Gets the values that are selected in the table.	 * @method getSelectedValues	 * @return {Array} An array of strings with the values selected.	 */	getSelectedValues: function() {		return this.table.getSelectedValues();	},	/* private: listens for double clicks on rows and closes the dialog */	viewDoubleClickListener : function(event) {		this.dialog.hide();		event.result = "OK";		this.closeListener(event);	},	toString : function() {		return "[Core.Ui.TableDialog]"	}});/* ############################## Drag Control ############################## *//** * A drag control is used to make a element moveable by the user. * @class Core.Ui.DragControl */Core.Ui.DragControl = Core.create({	/**	 * This creates the drag control.	 * @method init	 * @constructor	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for the element to be moved.	 * @param {String}/HTMLElement/{Core.Element} handle (Optional) The id, DOM node or Core.Element for the element where the user selects to move <code>element</code>.  Defaults to <code>element</code>.	 * @param Direction direction (Optional) Specifies which direction the object can be dragged in. See the Direction constants for possible values.	 * @param {String}/HTMLElement/{Core.Element} container (Optional) The id, DOM node or Core.Element for an element the the <code>element</code> must stay inside of.  If unspecified the visible screen is used.	 */	init: function(element, handle, direction, container) {		this.element = Core.get(element);		this.handle = handle ? Core.get(handle) : this.element;		this.direction = typeof(direction)=="number" ? direction : Core.Ui.DragControl.BOTH;		this.container = container ? Core.get(container) : null;				this.documents = [document];		this.topLeft = null;		this.bottomRight = null;		this.grabOffset = null;				var position = this.element.readStyle("position");		if (position == "static") {			this.element.getDom().style["position"] = "relative";		}				// bind methods		this.dragStart = this.dragStart.bind(this);		this.dragMove = this.dragMove.bind(this);		this.dragEnd = this.dragEnd.bind(this);				// setup handle		this.handle.addListener("mousedown", this.dragStart);		this.handle.preventTextSelection();			// events		/**		 * Fires when the element is dragged.		 * @event closeEvent		 * @param {Core.Domino.ViewDialog} dialog A refrence to this dialog		 * @param {String} result The button used to close the close the dialog		 * @param {Array} selected An array of strings with all the values selected in the view.		 */		this.startEvent = new Core.Event("Core.Ui.DragControl:start");		this.moveEvent = new Core.Event("Core.Ui.DragControl:move");		this.endEvent = new Core.Event("Core.Ui.DragControl:end");	},	/**	 * If the drag may cross another frame or iframe, then you can add the document for this 	 * frame to the drag control via this method.  This will ensure the mouse movements are 	 * still passed to the drag control	 * @method addDocument	 * @param HTMLDocument doc The document for the frame you wish to add.	 */	addDocument: function(doc) {		this.documents.push(doc);	},	updateMouseEvent: function(event) {		Core.copy(event, {			getPosition : function() {				return new Core.Point(this.clientX, this.clientY);			},			getOffset : function() {				if(this.pageX || this.pageY) {					return new Core.Point(this.pageX, this.pageY)				} else if (this.clientX || this.clientY) {					return this.getPosition().add(Core.Ui.scrollOffset())				}			}		});	},	dragStart : function(event) {		if(event) {			this.updateMouseEvent(event);					if(this.container==null) {				this.topLeft = Core.Ui.scrollOffset();				this.bottomRight = Core.Ui.scrollOffset().add(Core.Ui.clientSize()).minus(this.element.getSize());			} else {				this.topLeft = this.container.getTotalOffset();				this.bottomRight = this.container.getTotalOffset().add(this.container.getSize()).minus(this.element.getSize());			}			this.grabOffset = this.element.getTotalOffset().minus(event.getOffset());					for(var doc=0; doc<this.documents.length; doc++) {				Core.EventManager.addListener(this.documents[doc], "mousemove", this.dragMove);				Core.EventManager.addListener(this.documents[doc], "mouseup", this.dragEnd);							this.documents[doc].onmousemove = Core.returnFalse; // prevent text selection in ie				this.documents[doc].onmousedown = Core.returnFalse; // prevent text selection in ns			}					this.startEvent.fire({				element: this.element,				mouseEvent: event,				offset: this.element.getOffset()			});		}	},	dragMove : function(event) {		if(event) {			this.updateMouseEvent(event);					var change = event.getOffset().add(this.grabOffset).constrainTo(this.topLeft, this.bottomRight).minus(this.element.getTotalOffset());			if(this.direction==Core.Ui.DragControl.HORIZONTAL) {				change.y = 0;			} else if(this.direction==Core.Ui.DragControl.VERTICAL) {				change.x = 0;			}							var newPosition = this.element.getOffset().add(change);			if(this.direction==Core.Ui.DragControl.HORIZONTAL) {				this.element.setXOffset(newPosition);			} else if(this.direction==Core.Ui.DragControl.VERTICAL) {				this.element.setYOffset(newPosition);			} else {				this.element.setOffset(newPosition);			}							this.moveEvent.fire({				element: this.element,				mouseEvent: event,				offset: newPosition			});		}	},	dragEnd : function(event) {		if(event) {			this.updateMouseEvent(event);									for(var doc=0; doc<this.documents.length; doc++) {				Core.EventManager.removeListener(this.documents[doc], "mousemove", this.dragMove);				Core.EventManager.removeListener(this.documents[doc], "mouseup", this.dragEnd);				this.documents[doc].onmousemove = null;				this.documents[doc].onmousedown = null;			}				this.endEvent.fire({				element: this.element,				mouseEvent: event,				offset: this.element.getOffset()			});		}	}});Core.copy(Core.Ui.DragControl, {	/**	 * @property BOTH	 * @static Direction	 * The element can be dragged in both the x and y directions.	 */	BOTH : 0,		/**	 * @property HORIZONTAL	 * @static Direction	 * The element can only be dragged in the x direction.	 */	HORIZONTAL : 1,		/**	 * @property VERTICAL	 * @static Direction	 * The element can only be dragged in the y direction.	 */	VERTICAL : 2});/* ############################## Text edior ############################## *//** * <p>The Core.Ui.TextEditor is an implmentation of a cross browser rich text editor.  It currently  * supports IE and Firefox.  The editor replaces a text area and presents the user with up to  * three ways to enter data, in plain text, in rich text and in HTML.  The rich text part can be  * configure by passing the following options object to the constructor and the HTML editor can  * be disabled.</p> * <table cellspacing="0" cellpadding="0" > * <tr><th style="width: 120px;">Option Name</th><th style="width: 120px;">Type</th><th>Description</th></tr> * <tr><td>cssFile</td><td>String</td><td>(Optional) The url for a CSS file to be applied to the rich text area.</td></tr> * <tr><td>ieCssFile</td><td>String</td><td>(Optional) The url for a CSS file to be applied to the rich text area in addition to the <code>cssFile</code> if the browser is IE.</td></tr> * <tr><td>styles</td><td>Boolean</td><td>(Optional) When true the styles drop down will be shown.  Defaults to true.</td></tr> * <tr><td>links</td><td>Boolean</td><td>(Optional) When true the insert link button will be shown.  Defaults to true.</td></tr> * <tr><td>images</td><td>Boolean</td><td>(Optional) When true the insret image button will be shown.  Defaults to true.</td></tr> * <tr><td>tables</td><td>Boolean</td><td>(Optional) When true the insert table button will be shown.  Defaults to true.</td></tr> * </table> * @class Core.Ui.TextEditor */Core.Ui.TextEditor = Core.create({	/**	 * Creates a new TextEditor	 * @method init	 * @constructor	 * @param {Core.Form} form The form containging the data and type fields.	 * @param {String}dataField The name of the field should store the text to be edited.	 * @param {String}typeField The name of the field that will identify the type of text in the field, valid options are "plain", "rich" and "html"	 * @param {String} title (Optional) A title to display at the top of the screen, an empty string mean no title, defaults to an empty string.	 * @param Object richTextOptions (Optional) An options object including the following options, 	 * @param boolean html (Optional) When true, html is a valid input option, defaults to false.	 * @param boolean plain (Optional) When true, plain is a valid input option, defaults to true.	 */	init: function(form, dataField, typeField, title, richTextOptions, htmlInput) {		this.form = form;		this.dataField = dataField;		this.typeField = typeField;		title = title ? title : "";		htmlInput = typeof(htmlInput)=="boolean" ? htmlInput : false;		plainInput = typeof(plainInput)=="boolean" ? plainInput : true;				// replace input		var element = this.form.getElement(this.dataField);		this.editorElement = element.createSibling({tag: "div", klass: "textEditor"});		var box = this.editorElement.createChild({tag: "div", klass: "box"});		if(title!="") {			box.createChild({tag: "h4", html:title});		}		var textareaNode = element.getDom().cloneNode(true);		textareaNode.value = element.getDom().value;		box.getDom().appendChild(textareaNode);		this.textarea = Core.get(textareaNode);		element.remove();				// create rich text field		this.richTextField = this.createRichTextField(this.form, this.dataField, box, richTextOptions);				// create tabs		var typeTabs = this.editorElement.createChild({tag: "ul", klass: "tabs"});				this.plainTab = typeTabs.createChild({tag: "li", html: "Plain Text"});		if(plainInput) {			this.plainTab.addListener("click", this.switchToPlain.bind(this));		} else {			this.plainTab.hide();		}				this.richTab = typeTabs.createChild({tag: "li", html: "Rich Text"});		this.richTab.addListener("click", this.switchToRich.bind(this));				this.htmlTab = typeTabs.createChild({tag: "li", html: "HTML"});		if(htmlInput) {			this.htmlTab.addListener("click", this.switchToHtml.bind(this));				} else {					this.htmlTab.hide();		}				// load correct tab to start		this.textarea.removeClass("hidden");		if(this.form.getValue(this.typeField)=="plain") this.switchToPlain(false);		if(this.form.getValue(this.typeField)=="rich") this.switchToRich(false);		if(this.form.getValue(this.typeField)=="html") this.switchToHtml(false);				// create slider		this.slider = this.editorElement.createChild({tag: "div", klass: "slider"});		var drag = new Core.Ui.DragControl(this.slider, this.slider, Core.Ui.DragControl.VERTICAL);		drag.addDocument(this.richTextField.getDocument());		drag.moveEvent.addListener(this.moveSlider.bind(this));				var height = Core.readCookie("textEditorHeight");		if(!isNaN(parseInt(height))) {			this.height = parseInt(height);			this.resize();		} else {			this.height = 300;			this.resize();		}	},	/**	 * Creates the rich text field for use in this editor, by default this is a Core.Ui.RichTextField, over ride this if you wish to implement a custom rich text editor.	 * @method createRichTextFeild	 * @param {Core.Form} form The form containging the data and type fields.	 * @param {String} dataField The name of the field should store the text to be edited.	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for where to place the rich text field and its action bar.	 * @param Object options (Optional) An options object including the above options, 	 * @return {Core.Ui.RichTextField} Arich text field.	 */	createRichTextField: function(form, dataField, element, options) {		return new Core.Ui.RichTextField(form, dataField, element, options);	},	// private resize	moveSlider: function(event) {		this.height = Math.max(event.offset.y-26, 150);		Core.createCookie("textEditorHeight", this.height, 0, this.cookiePath);		this.resize();	},	resize: function() {		this.slider.getDom().style.top = (this.height+26)+"px";		this.textarea.getDom().style.height = this.height+"px";		this.richTextField.resize(this.height);		//this.rteFrame.getDom().style.height = (this.height-27)+"px";	},	// private: swtich to plain tab	switchToPlain: function(tabSwitch) {		if(tabSwitch) {			Core.Ui.msgbox("Lose Formating", "By switiching to plain text you will lose all formating.  Do you want to continue?", ["Yes", "No"], this.doSwitchToPlain.bind(this));		} else {			this.doSwitchToPlain();		}	},	doSwitchToPlain: function(event) {		if(!event || event.result=="Yes") {			if(event && event.result=="Yes") {				this.removeFormatting();			}			this.form.setValue(this.typeField, "plain");					this.textarea.show();			this.richTextField.hide();					this.plainTab.addClass("active");			this.richTab.removeClass("active");			this.htmlTab.removeClass("active");			}	},	// private: swtich to rich tab	switchToRich: function(tabSwitch) {		if(tabSwitch) {			if(this.form.getValue(this.typeField)=="plain") {				this.addFormatting();			}			this.richTextField.update();		}		this.form.setValue(this.typeField, "rich");				this.textarea.hide();		this.richTextField.show();				this.plainTab.removeClass("active");		this.richTab.addClass("active");		this.htmlTab.removeClass("active");	},	// private: swtich to html tab	switchToHtml: function(tabSwitch) {		if(tabSwitch) {			if(this.form.getValue(this.typeField)=="plain") {				this.addFormatting();			}		}		this.form.setValue(this.typeField, "html");				this.textarea.show();		this.richTextField.hide();				this.plainTab.removeClass("active");		this.richTab.removeClass("active");		this.htmlTab.addClass("active");	},	// private: used to remove formating from text	removeFormatting: function() {		var text = this.form.getValue(this.dataField);		// add carrage returns		text = text.replace(/(<(\/(p|h1|h2|h3|h4|h5|h6|li)|br\/?)>)(?!(\n|\r|$))/gi, function (matched, group){			return group+"\n";		});		// strip html		text = text.replace(/<\/?[^>]+(>|$)/g, "");		// convert &lt; &gt and &nbsp; to < > and a space		text = text.replace(/&(lt|gt|nbsp);/gi, function (matched, group) {			switch(group.toLowerCase()) {				case "lt": return "<";				case "gt": return ">";				case "nbsp": return " ";				default: return matched;			}		});		// convert &; and &gt; to < and >		text = text.replace(/&(lt|gt);/gi, function (matched, group){			return (group.toLowerCase() == "lt")? "<" : ">";		});		this.form.setValue(this.dataField, text);	},	// private: used to add formating to text	addFormatting: function() {		var text = this.form.getValue(this.dataField);		// convert < and > to &lt; and &gt;		text = text.replace(/<|>/g, function (matched){			return (matched == "<")? "&lt;" : "&gt;";		});		// replace carrage returns		text = text.replace(/\r/g, "");		//text = text.replace(/\n/g, "<br/>\n");		text = text.replace(/(^|\n)(.*)(?=(\n|$))/g, function (matched, group1, group2) {			return group2=="" ? "" : "<p>"+group2+"</p>\n";		});		this.form.setValue(this.dataField, text);	}});/** * <p>The Core.Ui.RichTextField is an implmentation of a cross browser rich text field.  It currently  * supports IE and Firefox.</p> * <table cellspacing="0" cellpadding="0" > * <tr><th style="width: 120px;">Option Name</th><th style="width: 120px;">Type</th><th>Description</th></tr> * <tr><td>cssFile</td><td>String</td><td>(Optional) The url for a CSS file to be applied to the rich text area.</td></tr> * <tr><td>ieCssFile</td><td>String</td><td>(Optional) The url for a CSS file to be applied to the rich text area in addition to the <code>cssFile</code> if the browser is IE.</td></tr> * <tr><td>styles</td><td>Boolean</td><td>(Optional) When true the styles drop down will be shown.  Defaults to true.</td></tr> * <tr><td>links</td><td>Boolean</td><td>(Optional) When true the insert link button will be shown.  Defaults to true.</td></tr> * <tr><td>images</td><td>Boolean</td><td>(Optional) When true the insret image button will be shown.  Defaults to true.</td></tr> * <tr><td>tables</td><td>Boolean</td><td>(Optional) When true the insert table button will be shown.  Defaults to true.</td></tr> * </table> * @class Core.Ui.RichTextField */Core.Ui.RichTextField = Core.create({	/**	 * Creates a new TextEditor	 * @method init	 * @constructor	 * @param {Core.Form} form The form containging the data and type fields.	 * @param {String} dataField The name of the field should store the text to be edited.	 * @param {String}/HTMLElement/{Core.Element} element The id, DOM node or Core.Element for where to place the rich text field and its action bar.	 * @param Object options (Optional) An options object including the above options, 	 */	init: function(form, dataField, element, options) {		this.form = form;		this.dataField = dataField;		this.options = Core.copyNew(options, Core.Options);						// create box		element = Core.get(element);		this.container = element.createChild({tag: "div", klass:"richTextField"});				// create rte icons		this.actions = [			/*[				{title:"Cut", icon:0, command:"cut"},				{title:"Copy", icon:1, command:"copy"},				{title:"Paste", icon:2, command:"paste"}			],			[				{title:"Undo", icon:3, command:"undo"},				{title:"Redo", icon:4, command:"redo"}			],*/		];		if(this.options.readBooleanOption("styles", true)) {			this.actions.push(				[					{title:"Normal", combo:true, command:"formatBlock", options: {"<p>":"Normal", "<h1>":"Document Title", "<h2>":"Section Title", "<h5>":"Heading", "<h6>":"Sub Heading", "<pre>":"Monospace"}}				]			);		}		this.actions.push(			[				{title:"Bold", icon:5, command:"bold"},				{title:"Italic", icon:6, command:"italic"},				{title:"Underline", icon:7, command:"underline"}			]		);		this.actions.push(			[				{title:"Align Left", icon:8, command: "justifyLeft"},				{title:"Centre", icon:9, command: "justifyCenter"},				{title:"Align Right", icon:10, command: "justifyRight"},				{title:"Justify", icon:11, command: "justifyFull"}			]		);		this.actions.push(			[				{title:"Numbering", icon:12, command: "insertOrderedList"},				{title:"Bullets", icon:13, command: "insertUnorderedList"},				{title:"Decrease Indent", icon:14, command: "outdent"},				{title:"Increase Indent", icon:15, command: "indent"}			]		);		this.actions.push(			[				{title:"Font Colour", icon:16, command: "foreColor", func: this.selectForeColour.bind(this)},				{title:"Background Colour", icon:17, command: "backColor", func: this.selectBackColour.bind(this)}			]		);		var inserts = [];		if(this.options.readBooleanOption("links", true)) {			inserts.push({title:"Insert Link", icon:18, command: "createLink", func: this.editLink.bind(this)});		}		if(this.options.readBooleanOption("images", true)) {			inserts.push({title:"Insert Picture", icon:19, command: "insertImage", func: this.insertImage.bind(this)});		}		if(this.options.readBooleanOption("tables", true)) {			inserts.push({title:"Insert Table", icon:20, command: "insertImage", func: this.insertTable.bind(this)});		}		if(inserts.length>0) {			this.actions.push(inserts);			if(this.options.readBooleanOption("tables", true)) {				this.actions.push([					{title:"Insert Row Before", icon:21, command: "updateTable", func: this.insertRow.bind(this, [true])},					{title:"Insert Row After", icon:22, command: "updateTable", func: this.insertRow.bind(this, [false])},					{title:"Remove Row", icon:23, command: "updateTable", func: this.removeRow.bind(this)}				]);				this.actions.push([					{title:"Insert Column Before", icon:24, command: "updateTable", func: this.insertColumn.bind(this,[true])},					{title:"Insert Column After", icon:25, command: "updateTable", func: this.insertColumn.bind(this, [false])},					{title:"Remove Column", icon:26, command: "updateTable", func: this.removeColumn.bind(this)}				]);			}		}							this.actionBar = this.container.createChild({tag: "div", klass: "actionBar"});		this.createActionBar();						// create rte		this.range = null;		this.ignoreHide  = 0;		this.rteDiv = this.container.createChild({tag: "div", klass:"textarea"});		this.rteFrame = this.rteDiv.createChild({tag: "iframe", id: this.dataField+"RTE", frameBorder: "0"});				if (document.all) {			//IE			this.rteWindow = frames[this.dataField+"RTE"];			this.rte = this.rteWindow.document;			this.rte.designMode = "On";						this.update();		} else {			// W3C			this.rteFrame.getDom().contentDocument.designMode = "on";			this.rteWindow = this.rteFrame.getDom().contentWindow;			this.rte = this.rteWindow.document;			this.update();		}				this.resize(150);				// events		/**		 * Fires when the field has been changed.		 * @event changeEvent		 * @param {Core.Ui.RichTextField} richTextField A refrence to this rich text field		 * @param {String} value The text currenlty in the field		 */		this.changeEvent = new Core.Event("Core.Ui.RichTextField:change");	},	/**	 * Returns the document for the iframe being used for this rich text field	 * @method getDocument	 * @return {Object} The document object	 */	getDocument: function() {		return this.rte;	},	/**	 * Resizes the field	 * @method resize	 * @param {Number} height The new height the Rich Text Field should be	 */	resize: function(height) {		this.rteFrame.getDom().style.height = (height-27)+"px";	},	/**	 * Makes the rich text field visible.	 * @method show	 */	show: function() {		this.container.show();	},	/**	 * Makes the rich text field invisible.	 * @method hide	 */	hide: function() {		this.container.hide();	},	// private: called when the rte looses focus and writes back to textfield and fires a change event	updateFromRTE: function() {		this.form.setValue(this.dataField, this.rte.body.innerHTML);		this.changeEvent.fire({			richTextField: this,			value: this.rte.body.innerHTML		});	},	/**	 * Updates the content of the field.	 * @method update	 * @param {String} content (Optional) The content of the field, if not specified the content is taken from the data field specified in the contructor.	 */	update: function(content) {		var frameHtml = "<html id=\""+this.dataField+"RTE\">\n";		if(this.options.hasOption("cssFile")) {			frameHtml += "<head>";			if(Core.isIE) {				frameHtml += "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\" />";			}			frameHtml += "<link media=\"all\" type=\"text/css\" href=\"" + this.options.readOption("cssFile", "") + "\" rel=\"stylesheet\">";			if(Core.isIE && this.options.hasOption("ieCssFile")) {				frameHtml += "<link media=\"all\" type=\"text/css\" href=\"" + this.options.readOption("ieCssFile", "") + "\" rel=\"stylesheet\">";			}			frameHtml += "</head>\n";		} else {			frameHtml += "<head><style>body{background: #FFFFFF; margin: 0px; padding: 0px;}</style></head>\n";		}		frameHtml += "<body>";		if(content) {			frameHtml += content;		} else {			frameHtml += this.form.getValue(this.dataField);		}		frameHtml += "</body>\n";		frameHtml += "</html>";				this.rte.open();		this.rte.write(frameHtml);		this.rte.close();				if (document.all) {			//IE			this.rteWindow.attachEvent("onfocus", this.hideNonPersistantPopups.bind(this));			this.rteWindow.attachEvent("onblur", this.updateFromRTE.bind(this));			this.rte.attachEvent("onkeyup", this.refreshActionBar.bind(this));			this.rte.attachEvent("onmouseup", this.refreshActionBar.bind(this));		} else {			// W3C			try {				Core.EventManager.addListener(this.rte, "focus", Core.Ui.hideNonPersistantPopups.bind(Core.Ui));				Core.EventManager.addListener(this.rte, "blur", this.updateFromRTE.bind(this));				Core.EventManager.addListener(this.rte, "keyup", this.forceParagraph.bind(this));				Core.EventManager.addListener(this.rte, "keyup", this.refreshActionBar.bind(this));				Core.EventManager.addListener(this.rte, "mouseup", this.refreshActionBar.bind(this));				if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {					Core.EventManager.addListener(this.rte, "keypress", this.keyboardListener.bind(this));				}			} catch (e) {				alert("Error updating rte. "+e);			}		}				this.refreshActionBar();	},	createActionBar: function() {		for(var actionGroup=0; actionGroup<this.actions.length; actionGroup++) {			var actionGroupElement = this.actionBar.createChild({tag:"div", klass:"actionGroup"});			for(var action=0; action<this.actions[actionGroup].length; action++) {				if(this.actions[actionGroup][action].combo) {					var actionElement = actionGroupElement.createChild({tag:"div", klass: (action==0 ? "combo first" : "combo")});					var comboPopup = new Core.Ui.ComboListPopup(this.actions[actionGroup][action].options);					comboPopup.showEvent.addListener(this.saveSelection.bind(this));					this.actions[actionGroup][action].combo = new Core.Ui.RichTextField.ActionBarComboButton(actionElement, comboPopup, this.rteComboCommand.bind(this, [this.actions[actionGroup][action].command]), this.actions[actionGroup][action].title);										this.actions[actionGroup][action].element = actionElement				} else {					var html = "<a href=\"javascript:;\" title=\""+this.actions[actionGroup][action].title+"\" style=\"background-position: 0px -"+(this.actions[actionGroup][action].icon*20)+"px;\"></a>";					var actionElement = actionGroupElement.createChild({tag:"div", klass: (action==0 ? "first" : ""), html:html});									actionElement.addListener("mousedown", actionElement.addClass.bind(actionElement, ["pressed"], true));					actionElement.addListener("mouseup", actionElement.removeClass.bind(actionElement, ["pressed"], true));					actionElement.addListener("mouseout", actionElement.removeClass.bind(actionElement, ["pressed"], true));																	if(this.actions[actionGroup][action].func) {						actionElement.addListener("click", this.actions[actionGroup][action].func);											} else if(this.actions[actionGroup][action].command) {						var command = this.actions[actionGroup][action].command;						var options = "";						if(this.actions[actionGroup][action].options) {							options = this.actions[actionGroup][action].options;						}											actionElement.addListener("click", this.rteCommand.bind(this, [command, options], true));					} else {						actionElement.addClass("disabled");					}									this.actions[actionGroup][action].element = actionElement;				}			}		}	},	refreshActionBar: function() {		for(var actionGroup=0; actionGroup<this.actions.length; actionGroup++) {			for(var action=0; action<this.actions[actionGroup].length; action++) {											if(this.actions[actionGroup][action].command && this.actions[actionGroup][action].element) { 					var command = 	this.actions[actionGroup][action].command;					var element = this.actions[actionGroup][action].element;																		if(this.actions[actionGroup][action].combo) {						// update combos						try {							var value = this.rte.queryCommandValue(command);							if(command=="formatBlock") {								value = this.getFormatBlockValue(value);							}							var display = this.actions[actionGroup][action].options[value];							if(!display) {								display=this.actions[actionGroup][action].title;							}							this.actions[actionGroup][action].combo.updateText(display);						} catch (e) {							//alert(e);						}					} else {						// update buttons						if(command=="updateTable") {							if(this.findAncestorNode("TD")) {								element.removeClass("disabled");							} else {								element.addClass("disabled");							}													} else {							try {								if(this.rte.queryCommandState(command)) {									element.addClass("active");								} else {																element.removeClass("active");								}							} catch (e) {}								try {								if(this.rte.queryCommandEnabled(command)) {									element.removeClass("disabled");								} else {																element.addClass("disabled");								}							} catch (e) {}						}					}				} 			}		}	},	getFormatBlockValue: function(value) {		switch(value.toLowerCase()) {			case "h1":			case "heading 1":				return "<h1>";				break;			case "h2":			case "heading 2":				return "<h2>";				break;			case "h3":			case "heading 3":				return "<h3>";				break;			case "h4":			case "heading 4":				return "<h4>";				break;			case "h5":			case "heading 5":				return "<h5>";				break;			case "h6":			case "heading 6":				return "<h6>";				break;			case "pre":			case "formatted":				return "<pre>";				break;			default :				return "<p>";		}	},	rteCommand: function(command, option) {		try {			this.rteWindow.focus();			if(this.rte.queryCommandEnabled(command)) {				this.rte.execCommand(command, false, option);				this.refreshActionBar();				this.rteWindow.focus();			}		} catch (e) {			//alert(e);		}	},	rteComboCommand: function(event, command) {		try {			this.rteWindow.focus();						// reselect range			this.restoreSelection();						if(this.rte.queryCommandEnabled(command)) {				this.rte.execCommand(command, false, event.value);				this.refreshActionBar();				this.rteWindow.focus();			}		} catch (e) {			//alert(e);		}	},	selectForeColour: function(event) {		if(!this.foreColourPicker) {			this.foreColourPicker = new Core.Ui.RichTextField.ColourPickerPopup("Automatic");			this.foreColourPicker.selectionEvent.addListener(this.setColour.bind(this, ["foreColor"]));		}				var element = Core.get(event.target);		this.foreColourPicker.setOffset(element.getTotalOffset().add(new Core.Point(0, element.getSize().y+1)));		this.foreColourPicker.show(event);	},	selectBackColour: function(event) {		if(!this.backColourPicker) {			this.backColourPicker = new Core.Ui.RichTextField.ColourPickerPopup("None");			this.backColourPicker.selectionEvent.addListener(this.setColour.bind(this, ["backColor"]));		}				var element = Core.get(event.target);		this.backColourPicker.setOffset(element.getTotalOffset().add(new Core.Point(0, element.getSize().y+1)));		this.backColourPicker.show(event);	},	setColour: function(event, command) {		var colour = document.all ? event.colour : event.colour=="" ? "inherit" : event.colour;		this.rteCommand(command, colour);	},	editLink: function(event) {		if(!this.linkDialog) {			content = "";			content +="<div class=\"fields\">";			content +="<table cellpadding=\"0\" cellspacing=\"0\">";			content +="<tr class=\"first\"><th>Link text</th><td><input type=\"text\" name=\"text\" class=\"full\"/></td></tr>";			content +="<tr><th>Protocol</th><td><select name=\"protocol\" class=\"full\"/><option value=\"http://\">HTTP</option><option value=\"https://\">HTTPS</option><option value=\"ftp:\">FTP</option><option value=\"mailto:\">Mail To</option></select></td></tr>";			content +="<tr><th>URL</th><td><input type=\"text\" name=\"url\" class=\"full\"/></td></tr>";			content +="</table>";			content +="</div>";						this.linkDialog = new Core.Ui.Dialog(true, true, "", "Edit Link", "", ["Save", "Cancel"]);			this.linkDialog.closeEvent.addListener(this.linkDialogListener.bind(this));			var contentElement = this.linkDialog.getContentElement();			var formElement = contentElement.createChild({tag: "form", html:content});			this.linkForm = new Core.Form(formElement.getDom());		}				var linkNode = this.findAncestorNode("A");		if(linkNode!=null) {			this.selectNode(linkNode);		} 		this.saveSelection();		this.linkForm.setValue("text", this.selectedText);		this.linkForm.setValue("protocol", "http://");		if(linkNode!=null) {			var href = linkNode.getAttribute("href");			if(href.startsWith("http://")) {				this.linkForm.setValue("url", href.substr(7));			} else if(href.startsWith("https://")) {				this.linkForm.setValue("protocol", "https://");				this.linkForm.setValue("url", href.substr(8));			} else if(href.startsWith("ftp://")) {				this.linkForm.setValue("protocol", "ftp://");				this.linkForm.setValue("url", href.substr(6));			} else if(href.startsWith("mailto:")) {				this.linkForm.setValue("protocol", "mailto:");				this.linkForm.setValue("url", href.substr(7));			} else {				this.linkForm.setValue("url", href);			}		} else {			this.linkForm.setValue("url", "");		}		this.linkDialog.show();	},	linkDialogListener : function(event) {		if(event.result=="Save") {			try {				this.rteWindow.focus();							// reselect range				this.restoreSelection();				this.rte.execCommand("unlink", false, null);								// replace text				if(this.selectedText!=this.linkForm.getValue("text")) {					this.replaceSelection(this.linkForm.getValue("text"));				}								// create link				var url = this.linkForm.getValue("protocol")+this.linkForm.getValue("url");				if(url!="") {					this.rte.execCommand("createLink", false, url);				}				this.rteWindow.focus();			} catch (e) {				//alert(e);			}		}	},	insertImage: function(event) {		if(!this.imageDialog) {			content = "";			content +="<div class=\"fields\">";			content +="<table cellpadding=\"0\" cellspacing=\"0\">";			content +="<tr class=\"first\"><th>URL</th><td><input type=\"text\" name=\"url\" class=\"full\"/></td></tr>";			content +="</table>";			content +="</div>";						this.imageDialog = new Core.Ui.Dialog(true, true, "", "Insert Image", "", ["Insert", "Cancel"]);			this.imageDialog.closeEvent.addListener(this.imageDialogListener.bind(this));			var contentElement = this.imageDialog.getContentElement();			var formElement = contentElement.createChild({tag: "form", html:content});			this.imageForm = new Core.Form(formElement.getDom());		}				this.saveSelection();		this.imageForm.setValue("url", "");		this.imageDialog.show();	},	imageDialogListener : function(event) {		if(event.result=="Insert") {			try {				this.rteWindow.focus();							// reselect range				this.restoreSelection();								// create image				var url = this.imageForm.getValue("url");				if(url!="") {					this.rte.execCommand("insertImage", false, url);				}				this.rteWindow.focus();			} catch (e) {				//alert(e);			}		}	},	insertTable: function(event) {		if(!this.tableDialog) {			content = "";			content +="<div class=\"fields\">";			content +="<table cellpadding=\"0\" cellspacing=\"0\">";			content +="<tr class=\"first\"><th>Columns</th><td><input type=\"text\" name=\"columns\" class=\"full\" value=\"2\"/></td></tr>";			content +="<tr><th>Rows</th><td><input type=\"text\" name=\"rows\" class=\"full\" value=\"2\"/></td></tr>";			content +="</table>";			content +="</div>";						this.tableDialog = new Core.Ui.Dialog(true, true, "msgbox", "Insert Table", "", ["Insert", "Cancel"]);			this.tableDialog.closeEvent.addListener(this.tableDialogListener.bind(this));			var contentElement = this.tableDialog.getContentElement();			var formElement = contentElement.createChild({tag: "form", html:content});			this.tableForm = new Core.Form(formElement.getDom());		}				this.saveSelection();		this.tableDialog.show();			},	tableDialogListener : function(event) {		if(event.result=="Insert") {			try {				this.rteWindow.focus();				var rows = this.tableForm.getValue("rows");				var cols = this.tableForm.getValue("columns");								if (!(rows == 0 || rows == "" || cols == 0 || cols == "")){					var htmlText = "<table class=\"rteTable\">";					for(i=1; i<=rows; i++){						htmlText += "<tr>";						for (j=1; j<=cols; j++) {							htmlText += "<td></td>";																			}						htmlText += "</tr>";					}											htmlText += "</table>";					this.restoreSelection();										if (document.all) { // IE						this.rte.selection.createRange().pasteHTML(htmlText);					} else { // Others						this.rte.execCommand('insertHTML', false, htmlText);					}				}			} catch (e) {				//alert(e);			}		}			},	insertRow: function(event, before) {		var trNode = this.findAncestorNode("TR");				if (trNode) {			var currentRow = Core.get(trNode);			var newRow = currentRow.createSibling({tag:"tr"}, !before);						var cell = currentRow.findFirstChild("td");			while(cell!=null) {				newRow.createChild({tag:"td"});				cell = currentRow.findNextChild(cell, "td");			}		}	},	removeRow: function(event) {		var trNode = this.findAncestorNode("TR");		if(trNode) {			Core.get(trNode).remove();			this.refreshActionBar();		}	},	insertColumn: function(event, before) {		var tdNode = this.findAncestorNode("TD");				if (tdNode) {			var row = Core.get(tdNode.parentNode);			var tbody = row.getParent();			var position = 0;						// find position of td in tr			var cell = row.findFirstChild("td");			while(cell!=null && cell.getDom()!=tdNode) {				position++;				cell = row.findNextChild(cell, "td");			}						// insert tds			row = tbody.findFirstChild("tr");			while(row!=null) {				var index = 0;				cell = row.findFirstChild("td");				while(cell!=null && index<position) {					index++;					cell = row.findNextChild(cell, "td");				}				row.createChild({tag:"td"}, cell, !before);				row = tbody.findNextChild(row, "tr");			}		}	},	removeColumn: function(event) {		var tdNode = this.findAncestorNode("TD");				if (tdNode) {			var row = Core.get(tdNode.parentNode);			var tbody = row.getParent();			var position = 0;						// find position of td in tr			var cell = row.findFirstChild("td");			while(cell!=null && cell.getDom()!=tdNode) {				position++;				cell = row.findNextChild(cell, "td");			}						// remove tds			row = tbody.findFirstChild("tr");			while(row!=null) {				var index = 0;				cell = row.findFirstChild("td");				while(cell!=null && index<position) {					index++;					cell = row.findNextChild(cell, "td");				}				cell.remove();				row = tbody.findNextChild(row, "tr");			}			this.refreshActionBar();		}	},	findAncestorNode: function(nodeName) {		nodeName = nodeName.toUpperCase();		if (this.rteWindow.getSelection) { // moz			var selection = this.rteWindow.getSelection();			if(selection!=null) {				var container = selection.getRangeAt(selection.rangeCount - 1).startContainer;				while(container && container!=this.rte) {					if(container.nodeName == nodeName) {						return container;					}					container = container.parentNode;				}			}			return null;					} else if (this.rte.selection) { // ie			this.ignoreHide++;			this.rteWindow.focus();			var range = this.rte.selection.createRange();			var container;						if (this.rte.selection.type=="Control") {				for(var index=0 ; index<range.length; index++) {					if(range(index).parentNode) {						container = range(index).parentNode ;						break ;					}				}			} else {				container = range.parentElement() ;			}						while(container && container!=this.rte) {				if(container.nodeName == nodeName) {					return container;				}				container = container.parentNode;			}			return null;		}	},	selectNode: function(node) {		if (this.rteWindow.getSelection) { // moz			var range = this.rte.createRange();			range.selectNode(node);						var selection = this.rteWindow.getSelection();			selection.removeAllRanges();			selection.addRange(range);					} else if (this.rte.selection) { // ie			this.ignoreHide++;			this.rteWindow.focus();			this.rte.selection.empty();						var range;			try {				// Try to select the node as a control.				range = this.rte.body.createControlRange();				range.addElement(node);			} catch (e) {				range = this.rte.body.createTextRange();				range.moveToElementText(node);			}			range.select();		}	},	saveSelection: function() {		if (this.rteWindow.getSelection) { // moz			var selection = this.rteWindow.getSelection();			this.range = selection.getRangeAt(selection.rangeCount - 1).cloneRange();			this.selectedText = selection;		} else if (this.rte.selection) { // ie			this.ignoreHide++;			this.rteWindow.focus();			this.range = this.rte.selection.createRange();			this.selectedText = this.range.text;			}	},	restoreSelection: function() {		if(this.range!=null) {			if (this.rteWindow.getSelection) { // moz				var selection = this.rteWindow.getSelection();				selection.removeAllRanges();				selection.addRange(this.range);						} else if (this.rte.selection) { // ie				this.range.select();			}		}	},	replaceSelection: function(newText) {		if (this.rteWindow.getSelection) { // moz			var node = document.createTextNode(newText);			var selection = this.rteWindow.getSelection();			var range = selection.getRangeAt(selection.rangeCount - 1);						range.deleteContents();			range.insertNode(node);			range.selectNode(node);					} else if (this.rte.selection) { // ie			this.ignoreHide++;			this.rteWindow.focus();			var range = this.rte.selection.createRange();			range.text = newText;			range.select();			range.moveStart("character", 0-newText.length);			range.select();		}	},	hideNonPersistantPopups: function(event) {		if(this.ignoreHide<=0) {			Core.Ui.hideNonPersistantPopups(event);		} else {			this.ignoreHide--;		}	},	forceParagraph: function(event) {		if(this.rte.queryCommandValue("formatBlock")=="") {			this.rteCommand("formatBlock", "<p>");		}	},	keyboardListener: function(event) {		if(event.ctrlKey) {			var catchEvent = true;			switch(String.fromCharCode(event.charCode).toLowerCase()) {				case "b":					this.rteCommand("bold", "");					break;				case "i":					this.rteCommand("italic", "");					break;				case "u":					this.rteCommand("underline", "");					break;				default :					catchEvent = false;			}						if(catchEvent) {				Core.EventManager.catchEvent(event);			}		} /*else if(event.keyCode==13) {			alert("yes");			this.rteCommand("insertParagraph ", "");		}*/	}});Core.Ui.RichTextField.ColourPickerPopup = Core.create(Core.Ui.Popup, {	/*	 * Creates a new ColourPickerPopup	 * @method init	 * @constructor	 * @param {String} inheritText (Optional) The text to display for the inherit option, defaults to "" which then isn't displayed.	 */	init : function(inheritText) {		Core.Ui.Popup.prototype.init.call(this, false, false, "colourPickerPopup");				inheritText = inheritText ? inheritText : "";				var colours = [			[				{name: "Black", colour: "000000"},				{name: "Brown", colour: "993300"},				{name: "Olive Green", colour: "333300"},				{name: "Dark Green", colour: "003300"},				{name: "Dark Teal", colour: "003366"},				{name: "Dark Blue", colour: "000080"},				{name: "Indigo", colour: "333399"},				{name: "Very Dark Gray", colour: "333333"}			],			[				{name: "Dark Red", colour: "800000"},				{name: "Orange", colour: "FF6600"},				{name: "Dark Yellow", colour: "808000"},				{name: "Green", colour: "008000"},				{name: "Teal", colour: "008080"},				{name: "Blue", colour: "0000FF"},				{name: "Blue-Gray", colour: "666699"},				{name: "Dark Gray", colour: "808080"}			],			[				{name: "Red", colour: "FF0000"},				{name: "Light Orange", colour: "FF9900"},				{name: "Lime", colour: "99CC00"},				{name: "Sea Green", colour: "339966"},				{name: "Aqua", colour: "33CCCC"},				{name: "Light Blue", colour: "3366FF"},				{name: "Violet", colour: "800080"},				{name: "Gray", colour: "999999"}			],			[				{name: "Pink", colour: "FF00FF"},				{name: "Gold", colour: "FFCC00"},				{name: "Yellow", colour: "FFFF00"},				{name: "Bright Green", colour: "00FF00"},				{name: "Turquoise", colour: "00FFFF"},				{name: "Sky Blue", colour: "00CCFF"},				{name: "Plum", colour: "993366"},				{name: "Light Gray", colour: "C0C0C0"}			],			[				{name: "Rose", colour: "FF99CC"},				{name: "Tan", colour: "FFCC99"},				{name: "Light Yellow", colour: "FFFF99"},				{name: "Light Green", colour: "CCFFCC"},				{name: "Light Turquoise", colour: "CCFFFF"},				{name: "Pale Blue", colour: "99CCFF"},				{name: "Lavender", colour: "CC99FF"},				{name: "White", colour: "FFFFFF"}			]		];				// create content		var contentElement = this.getContentElement();		var table = contentElement.createChild({tag: "table", cellPadding: "0", cellSpacing: "0"});		var header = table.createChild({tag: "thead"}); 		var body = table.createChild({tag: "tbody"});				if(inheritText!="") {			var tr = header.createChild({tag: "tr"});			var td = tr.createChild({tag: "td", colSpan: colours[0].length, html: "<a href=\"javascript:;\">"+inheritText+"</a>"});			td.addListener("click", this.select.bind(this, [inheritText, ""]));		}				for(var row=0; row<colours.length; row++) {			var tr = body.createChild({tag: "tr"});			for(var col=0; col<colours[row].length; col++) {				var td = tr.createChild({tag: "td", html: "<a href=\"javascript:;\" title=\""+colours[row][col].name+"\" style=\"background: #"+colours[row][col].colour+";\"></a>"});				td.addListener("click", this.select.bind(this, [colours[row][col].name, colours[row][col].colour]));			}		}						/*		 * Fires when an option is selected		 * @event selectionEvent		 * @param {String} name The name of the selected colour.		 * @param {String} colour The RGB 6 charcter hex code.  Does not include the #.		 */		this.selectionEvent = new Core.Event("Core.Ui.TextEditor.ColourPickerPopup:selection");	},	/* private: fires the selectionEvent */	select: function(event, name, colour) {		this.hide(event);		this.selectionEvent.fire({name: name, colour: colour});	}});/* Private, special action bar combo button for RTEs that uses a link so the text selection is preserved in ie 8 */Core.Ui.RichTextField.ActionBarComboButton = Core.create(Core.Ui.ActionBarComboButton, {	updateText: function(text) {		if(text) {			this.element.clear();			this.element.createChild({tag:"a", href:"javascript:;", html: text});			this.element.createChild({tag:"div", klass:"inline-block comboImg"});					}	}});
