// TabbedPane class
	var TabbedPane = Class.create();
	TabbedPane.TOP = 'top';
	TabbedPane.BOTTOM = 'bottom';
	TabbedPane.DEFAULT_FONT_WIDTH = 10;
	Object.extend(TabbedPane.prototype, Object.prototype);
	Object.extend(TabbedPane.prototype, {
		// creates a new TabbedPane object.
		// @name the name of this tabbed pane (used in all it's sub components)
		// @containerId the id of where the TabbedPane will go.
		// @options the TabbedPane options.
		initialize: function(name, containerId, options) {
			this.name = name;
			this.containerId = containerId;
			this.tabs = new $H({});
			this.currentTabId = '';
			this.tabsVisible = true;
			this.options = this._createDefaultOptions();
			this._mergeOptions(options);
			if(this.options['createTabbedPaneOnInstantiation']) {
				this.createTabbedPane();
			}
		},
		// addDefaultLabelClickListener Attatches the label click listener to the
		// given tabLabel
		// @param tabLabel
		addDefaultLabelClickListener: function(tabLabel) {
			// default observer, simply shows the associated tab.
			var observer = function(event) {
				var eventElement = Event.element(event);
				var tab = eventElement.tab;
				var tabbedPane = tab.getTabbedPane();
				tabbedPane.showTab(tab.getId());
			};
			Event.observe(tabLabel, 'click', observer, false);
			var tabLabelDescendants = $(tabLabel).descendants();
			var descendantCount = tabLabelDescendants.length;
			var descendant = null;
			for(var descendantIndex = 0; descendantIndex < descendantCount; descendantIndex++) {
				// Note that all of Prototype’s DOM traversal methods ignore text 
				// nodes and return element nodes only.
				descendant = tabLabelDescendants[descendantIndex];
				Event.observe(tabLabel, 'click', observer, false);
			}
		},
		// "public" functions
		// addTab Adds a tab to this tabbed pane.
		// @param id The Unique identifier of this tab, should be html id compliant.
		// @param labelText The label text that will be used for the tab itself.
		// @param content The content of the tab.
		// @param ajaxOptions A map containing a url, parameters and any other
		//        Ajax related options to retrieve tab components from an Ajax call.
		addTab: function(id, labelText, content, ajaxOptions) {
			if(this._hasTab(id)) {
				this.updateTab(id, labelText, content, true);
			} else {
				var tab = this._createTab(id, labelText, content);
				this._addTab(tab);
				this.showTab(id);
			}
			if(typeof(ajaxOptions) != 'undefined') {
				// if ajaxOptions are included, then the tab will
				// either be created or updated with the given
				// labelText and content, then it will be updated
				// again by the content from the AJAX call.
				this.updateTabWithAjax(id, ajaxOptions);
			}
		},
		// createTabbedPane Creates the tabbed pane object.
		createTabbedPane: function() {
			this.mainContainer = this._createMainContainer();
			this.topLabelContainer = this._createLabelContainer(TabbedPane.TOP);
			this.bottomLabelContainer = this._createLabelContainer(TabbedPane.BOTTOM);
			this.contentContainer = this._createContentContainer();
			this.mainContainer.appendChild(this.topLabelContainer);
			this.mainContainer.appendChild(this.contentContainer);
			this.mainContainer.appendChild(this.bottomLabelContainer);
			var tabbedPaneContainer = $(this.containerId);
			if(tabbedPaneContainer && typeof(tabbedPaneContainer) != 'undefined') {
				if($(this.mainContainer.id) && typeof($(this.mainContainer.id)) != 'undefined') {
					// if the tabbedPaneContainer already has a tabbedPane in it
					// by this name, then we need to remove it first.
					tabbedPaneContainer.removeChild($(this.mainContainer.id));
				}
				tabbedPaneContainer.appendChild(this.mainContainer);
			}
		},
		// findAndAddTabsByClassName A convenience method to find all the elements
		// in the document with the given class name and add them to this 
		// tabbed pane.
		// @param className
		findAndAddTabsByClassName: function(className) {
			var elements = document.getElementsByClassName(className);
			var elementCount = 0;
			var elementName = '';
			var elementId = '';
			var element = null;
			var elementParent = null;
			if(elements && typeof(elements) != 'undefined' && elements.length > 0) {
				elementCount = elements.length;
				for(var index = 0; index < elementCount; index++) {
					element = elements[index];
					elementParent = element.parentNode;
					// remove it from the document.
					elementParent.removeChild(element);
					// make sure it's visible.
					Element.show(element);
					// get it's name and id.
					elementName = element.name;
					elementId = element.id;
					// add the element to this tabbed pane.
					this.addTab(elementId, elementName, element);
				}
			}
		},
		// getBottomLabelContainer gets the bottom label container for this
		// tabbed pane.
		getBottomLabelContainer: function() {
			return this.bottomLabelContainer;
		},
		// getContentContainer gets the content container for this tabbed pane.
		getContentContainer: function() {
			return this.contentContainer;
		},
		// getCurrentTabId Gets the current Tab's ID.
		getCurrentTabId: function() {
			return this.currentTabId;
		},
		// getMainContainer gets the main container for this tabbed pane
		getMainContainer: function() {
			return this.mainContainer;
		},
		// getTab Finds the tab object associated with the given id.
		// @param id
		getTab: function(id) {
			var tab = null;
			if(this._hasTab(id)) {
				tab = this.tabs[id];
			}
			return tab;
		},
		// getTabs Gets the tabIds for all the known tabs.
		getTabs: function() {
			var tabs = $H(this.tabs);
			return tabs.keys();
		},
		// getTabsVisible gets whether or not the tabs are visible or not.
		getTabsVisible: function() {
			return this.tabsVisible;
		},
		// getTopLabelContainer Finds the top label container for this tabbed pane.
		getTopLabelContainer: function() {
			return this.topLabelContainer;
		},
		// hasNextTab checks to see if this tabbed pane has a next tab.
		hasNextTab: function() {
			var currentTabIndex = this._getTabIndex(this.currentTabId);
			var tabIds = this.getTabs();
			var length = tabIds.length;
			var hasNextTab = currentTabIndex < (length - 1);
			return hasNextTab;
		},
		// hasPreviousTab checks to see if this tabbed pane has a previous tab.
		hasPreviousTab: function() {
			var currentTabIndex = this._getTabIndex(this.currentTabId);
			var hasPreviousTab = currentTabIndex > 0;
			return hasPreviousTab;
		},
		// hidePostTabContent Hides the post tab content.
		hidePostTabContent: function() {
			this._hidePostTabContent(TabbedPane.TOP);
			this._hidePostTabContent(TabbedPane.BOTTOM);
		},
		// hidePreTabContent Hides the pre tab content.
		hidePreTabContent: function() {
			this._hidePreTabContent(TabbedPane.TOP);
			this._hidePreTabContent(TabbedPane.BOTTOM);
		},
		// removeAllTabs Removes all the tabs associated with this TabbedPane.
		removeAllTabs: function() {
			var tabIds = this.getTabs();
			var tabId = '';
			var tabCount = tabIds.length;
			if(tabCount > 0) {
				for(var index = 0; index < tabCount; index++) {
					tabId = tabIds[index];
					this.removeTab(tabId);
				}
			}
			this.tabs = new $H({});
			this.currentTabId = '';
		},
		// removeTab Removes the tab with the given id.
		// @param id
		removeTab: function(id) {
			if(this.hasPreviousTab()) {
				// if there's a previous tab we'll show that one before removing
				// the requested tab.
				this.showPreviousTab();
			} else if (this.hasNextTab()) {
				// if there was no previous tab, but there was a next tab, show
				// that one before removing the requested tab.
				this.showNextTab();
			} else {
				// if there was no previous and no next tab, then there aren't
				// any other tabs. Correct the pre and post tab content for a 
				// blank tabbed pane.
				if(this.options['hidePostTabContentOnLastTab']) {
					this.hidePostTabContent();
				}
				if(this.options['hidePreTabContentOnFirstTab']) {
					this.hidePreTabContent();
				}
			}
			// actually remove the tab elements from the dom.
			this._removeTabContent(id);
			this._removeTabLabels(id);
			// remove the tab from the tabs hash.
			delete this.tabs[id];
		},
		// setTabsVisible Shows or hides all the tabs associated with this 
		// tabbed pane.
		// @param visible
		setTabsVisible: function(visible) {
			this.tabsVisible = visible;
			var tabIds = this.getTabs();
			var tabLength = tabIds.length;
			var tabId = '';
			var tab = null;
			var labelElements = null;
			for(var tabIndex = 0; tabIndex < tabLength; tabIndex++) {
				tabId = tabIds[tabIndex];
				tab = this.getTab();
				labelElements = tab.getLabelElements();
				if(visible) {
					Element.show(labelElements.top);
					Element.show(labelElements.bottom);
				} else {
					Element.hide(labelElements.top);
					Element.hide(labelElements.bottom);
				}
			}
		},
		// showFirstTab A convenience method to show the first tab in this
		// tabbed pane.
		showFirstTab: function() {
			var tabIds = this.getTabs();
			var firstTabId = null;
			if(tabIds && typeof(tabIds) != 'undefined' && tabIds.length > 0) {
				firstTabId = tabIds[0];
				this.showTab(firstTabId);
			}
		},
		// showLastTab A convenience method to show the last tab in this 
		// tabbed pane.
		showLastTab: function() {
			var tabIds = this.getTabs();
			var lastTabId = null;
			var tabCount = 0;
			if(tabIds && typeof(tabIds) != 'undefined' && tabIds.length > 0) {
				tabCount = tabIds.length;
				lastTabId = tabIds[tabCount - 1];
				this.showTab(lastTabId);
			}
		},
		// showTextTab Shows the next tab if there is one.
		// @return True if there was a next tab, false otherwise.
		showNextTab: function() {
			var currentTabIndex = this._getTabIndex(this.currentTabId);
			var tabIds = this.getTabs();
			// duplicated from hasNextTab to avoid looping twice. (getTabIndex)
			var hasNextTab = currentTabIndex < (tabIds.length - 1);
			if(hasNextTab) {
				var nextTabId = tabIds[currentTabIndex + 1];
				this.showTab(nextTabId);
			}
			return hasNextTab;
		},
		// showPostTabContent Shows the post tab content for the given position.
		// @param position
		showPostTabContent: function() {
			this._showPostTabContent(TabbedPane.TOP);
			this._showPostTabContent(TabbedPane.BOTTOM);
		},
		// showPreTabContent Shows the pre tab content for the given position.
		// @param position
		showPreTabContent: function() {
			this._showPreTabContent(TabbedPane.TOP);
			this._showPreTabContent(TabbedPane.BOTTOM);
		},
		// showPreviousTab Shows the previous tab if there is one.
		// @return True if there was a previous tab, false otherwise.
		showPreviousTab: function() {
			var currentTabIndex = this._getTabIndex(this.currentTabId);
			// duplicated from hasPreviousTab to avoid looping twice. (getTabIndex)
			var hasPreviousTab = currentTabIndex > 0;
			if(hasPreviousTab) {
				var tabIds = this.getTabs();
				var previousTabId = tabIds[currentTabIndex - 1];
				this.showTab(previousTabId);
			}
			return hasPreviousTab;
		},
		// showTab Shows the tab associated with the given id.
		// @param id
		showTab: function(id) {
			if(typeof(this.currentTabId) != 'undefined' && this.currentTabId != id) {
				this._hideTabContent(this.currentTabId);
				this._deSelectTabLabels(this.currentTabId);
			}
			this.currentTabId = id;
			this._showTabContent(this.currentTabId);
			this._selectTabLabels(this.currentTabId);
			if(this.options['hidePreTabContentOnFirstTab'] == true && this.hasPreviousTab() == false) {
				this.hidePreTabContent();
			} else {
				this.showPreTabContent();
			}
			if(this.options['hidePostTabContentOnLastTab'] == true && this.hasNextTab() == false) {
				this.hidePostTabContent();
			} else {
				this.showPostTabContent();
			}
			
			// make sure tabs are visible or hidden.
			var tab = this.getTab(id);
			var labelElements = tab.getLabelElements();
			if(this.tabsVisible) {
				Element.show(labelElements.top);
				Element.show(labelElements.bottom);
			} else {
				Element.hide(labelElements.top);
				Element.hide(labelElements.bottom);
			}
		},
		// updateTab updates the tab associated with the given id.
		// @param id
		// @param newLabelText
		// @param newContent
		// @param focus
		updateTab: function(id, newLabelText, newContent, focus) {
			if(this._hasTab(id)) {
				this.updateTabContent(id, newContent, false);
				this.updateTabLabel(id, newLabelText, focus);
			}
		},
		// updateTabContent Updates the content of the tab associated with the given id.
		// @param id
		// @param newContent
		// @param focus If True, then the tab with the given ID will get focus.
		updateTabContent: function(id, newContent, focus) {
			var tab = this.getTab(id);
			var typeOfContent = typeof(newContent);
			if(typeOfContent == 'string') {
				newContent = new String(newContent);
			}
			newContent = $(newContent);
			if(tab != null) {
				var contentElement = tab.getContentElement();
				if(typeOfContent == 'string') {
					// if new content is a string, we just update the contentElement.
					Element.update(contentElement, newContent);
					newContent.evalScripts();
				} else {
					// if it's not a string, then it must be an element.
					var contentChildNodes = contentElement.childNodes;
					var childCount = 0;
					if(contentChildNodes && typeof(contentChildNodes) != 'undefined') {
						childCount = contentChildNodes.length;
					}
					// let's remove all the content elements children first before
					// updating it with the new content.
					for(var child = 0; child < childCount; child++) {
						contentElement.removeChild(contentChildNodes[child]);
					}
					// contentElement should only contain 1 child now, and that's
					// the new content.
					contentElement.appendChild(newContent);
				}
			}
			if(focus) {
				this.showTab(tab.getId());
			}
		},
		// updateTabLabel Updates the labels of the tab associated with the given id.
		// @param id
		// @param newLabel
		// @param focus If True, then the tab with the given ID will get focus.
		updateTabLabel: function(id, newLabel, focus) {
			var tab = this.getTab(id);
			var typeOfLabel = typeof(newLabel);
			if(typeOfLabel == 'string') {
				newLabel = new String(newLabel);
			}
			var newTopLabel = newLabel.cloneNode(true);
			var newBottomLabel = newLabel.cloneNode(true);
			var tabLabelElements = null;
			if(tab != null) {
				tabLabelElements = tab.getLabelElements();
				if(typeOfLabel == 'string') {
					// if new content is a string, we just update the contentElement.
					Element.update(tabLabelElements.top, newTopLabel);
					Element.update(tabLabelElements.bottom, newBottomLabel);
					newLabel.evalScripts();
				} else {
					// if it's not a string, then it must be an element.
					var labelChildNodes = tabLabelElements.top.childNodes;
					var childCount = 0;
					if(labelChildNodes && typeof(labelChildNodes) != 'undefined') {
						childCount = labelChildNodes.length;
					}
					// let's remove all the content elements children first before
					// updating it with the new content.
					for(var child = 0; child < childCount; child++) {
						tabLabelElements.top.removeChild(labelChildNodes[child]);
					}
					// append the new label to the label elements
					try {
						tabLabelElements.top.appendChild(newTopLabel);
					} catch (ex) {
						alert(ex);
					}
					
					labelChildNodes = tabLabelElements.bottom.childNodes;
					childCount = 0;
					if(labelChildNodes && typeof(labelChildNodes) != 'undefined') {
						childCount = labelChildNodes.length;
					}
					// let's remove all the content elements children first before
					// updating it with the new content.
					for(var child = 0; child < childCount; child++) {
						tabLabelElements.bottom.removeChild(labelChildNodes[child]);
					}
					// append the new label to the label elements
					try {
						tabLabelElements.bottom.appendChild(newBottomLabel);
					} catch (ex) {
						alert(ex);
					}
				}
			}
			if(focus) {
				this.showTab(tab.getId());
			}
		},
		// updateTabWithAjax Updates a tab with contents retrieved using Ajax.
		// The response from the Ajax call needs to either been xml, or JSON. 
		// The TabbedPane will either expect a JSON Tab object, or an XML 
		// document with the following format.
		// Example XML:
		// <?xml version="1.0" encoding="UTF-8"?>
		// <tab>
		//    <id>the id passed as request parameter</id>
		//    <label>Label Text</label>
		//    <content>Tab Content</content>
		// </tab>
		// @param id
		// @param ajaxOptions
		updateTabWithAjax: function(id, ajaxOptions) {
			var url = ajaxOptions.url;
			var defaultOptions = $H({
				method: 'get'
			});
			ajaxOptions = defaultOptions.merge($H(ajaxOptions));
			// Must override the onSuccess, and onFailure so that the
			// TabbedPane can get the response.
			var overrideOptions = $H({
				onSuccess: this._updateTabWithAjaxSuccess.bind(this),
				onFailure: this._updateTabWithAjaxFailure.bind(this)
			});
			ajaxOptions = $H(ajaxOptions).merge(overrideOptions);
			new Ajax.Request(url, ajaxOptions);
		},
		// "private" functions
		// _addTab adds the given tab to this tabbed pane.
		// @param tab
		_addTab: function(tab) {
			var id = tab.getId();
			this.tabs[id] = tab;
			var tabLabelElements = tab.getLabelElements();
			
			// hide the new tabs content - it'll be shown right after
			// it's actually added so no biggie.
			Element.hide(tabLabelElements.top);
			Element.hide(tabLabelElements.bottom);
			Element.hide(tab.getContentElement());
			
			// update the top label container.
			var topPostTabContent = this._getPostTabContent('top');
			var topTabStopContent = this._getTabStopContent('top');
			this.topLabelContainer.removeChild(topPostTabContent);
			this.topLabelContainer.removeChild(topTabStopContent);
			this.topLabelContainer.appendChild(tabLabelElements.top);
			this.topLabelContainer.appendChild(topPostTabContent);
			this.topLabelContainer.appendChild(topTabStopContent);
			
			// update the bottom label container.
			var bottomPostTabContent = this._getPostTabContent('bottom');
			var bottomTabStopContent = this._getTabStopContent('bottom');
			this.bottomLabelContainer.removeChild(bottomPostTabContent);
			this.bottomLabelContainer.removeChild(bottomTabStopContent);
			this.bottomLabelContainer.appendChild(tabLabelElements.bottom);
			this.bottomLabelContainer.appendChild(bottomPostTabContent);
			this.bottomLabelContainer.appendChild(bottomTabStopContent);
			
			// update tab content container.
			this.contentContainer.appendChild(tab.getContentElement());
		},
		// _attachTab Attaches the given tab to the given element.
		// @param element
		// @param tab
		_attachTab: function(element, tab) {
			// add tab to element.
			element.tab = tab;
			// add tab to descendants
			var descendants = $(element).descendants();
			var descendantCount = descendants.length;
			var descendant = null;
			for(var descendantIndex = 0; descendantIndex < descendantCount; descendantIndex++) {
				// Note that all of Prototype’s DOM traversal methods ignore text 
				// nodes and return element nodes only.
				descendant = descendants[descendantIndex];
				descendant.tab = tab;
			}
		},
		// _calculateTabLabelWidth Calculates the variable width of a tab label
		// given the text.
		// @param tabLabel
		// @param labelText
		_calculateTabLabelWidth: function(tabLabel, labelText) {
			var fontSize = $(tabLabel).getStyle('fontSize');
			if(!fontSize || fontSize == null || fontSize == '') {
				fontSize = $(tabLabel).getStyle('font-size');
			}
			var length = fontSize.length;
			var position = length - 2;
			var px = '';
			if(position > 0) {
				px = fontSize.substring(position, length);
			}
			if(px == 'px') {
				fontSize = fontSize.substring(0, position);
				// trial and error showed that most fonts have a height to width
				// ration of about half.
				fontSize = fontSize / 2;
				// this corrects the difference and allows for a little padding
				// on the tab labels.
				fontSize = fontSize + 2;
			} else {
				fontSize = TabbedPane.DEFAULT_FONT_WIDTH;
			}
			var labelWidth = labelText.length * fontSize;
			labelWidth = labelWidth + 'px';
			return labelWidth;
		},
		// _createContentContainer Creates the content container for this tabbed pane.
		_createContentContainer: function() {
			var contentContainer = document.createElement("div");
			contentContainer.id = this.name + '_contentContainer';
			contentContainer.className = this.options['contentContainerClassName'];
			return contentContainer;
		},
		// _createDefaultOptions Creates the default options for this tabbed pane.
		_createDefaultOptions: function() {
			return {
				contentClassName: 'tabbedPaneContent',
				contentContainerClassName: 'tabbedPaneContentContainer',
				createTabbedPaneOnInstantiation: true,
				hidePostTabContentOnLastTab: true,
				hidePreTabContentOnFirstTab: true,
				labelClassName: 'tabbedPaneLabel',
				labelContainerClassName: 'tabbedPaneLabelContainer',
				mainContainerClassName: 'mainContainerClassName',
				postTabContentClassName: 'tabbedPanePostTabContent',
				preTabContentClassName: 'tabbedPanePreTabContent',
				selectedLabelClassName: 'tabbedPaneSelectedLabel',
				tabStopClassName: 'tabbedPaneTabStop',
				useVariableLabelWidth: false
			};
		},
		// _createLabelContainer Creates a label container with the given position.
		// @param position
		_createLabelContainer: function(position) {
			var labelContainer = document.createElement("div");
			labelContainer.id = this.name + '_labelContainer_' + position;
			labelContainer.className = this.options[position + 'LabelContainerClassName'];
			var preTabContent = this._createPreTabContent(position);
			var postTabContent = this._createPostTabContent(position);
			var tabStopContent = this._createTabStopContent(position);
			labelContainer.appendChild(preTabContent);
			labelContainer.appendChild(postTabContent);
			labelContainer.appendChild(tabStopContent);
			return labelContainer;
		},
		// _createMainContainer Creates the main container for this tabbed pane.
		_createMainContainer: function() {
			var mainContainer = document.createElement("div");
			mainContainer.id = this.name + '_mainContainer';
			mainContainer.className = this.options['mainContainerClassName'];
			return mainContainer;
		},
		// _createPostTabContent creates the post tab content element given the position.
		// @param position
		_createPostTabContent: function(position) {
			var postTabContent = document.createElement("div");
			postTabContent.id = this.name + '_postTabContent_' + position;
			postTabContent.className = this.options[position + 'PostTabContentClassName'];
			return postTabContent;
		},
		// _createPreTabContent Creates the pre tab content element given the position.
		// @param position
		_createPreTabContent: function(position) {
			var preTabContent = document.createElement("div");
			preTabContent.id = this.name + '_preTabContent_' + position;
			preTabContent.className = this.options[position + 'PreTabContentClassName'];
			return preTabContent;
		},
		// _createTab Creates a tab given the id, labelText and content.
		// @param id
		// @param labelText
		// @param content
		_createTab: function(id, labelText, content) {
			var tab = new Tab();
			var tabContent = this._createTabContent(id, content);
			var tabLabelTop = this._createTabLabel(id, labelText, 'top');
			var tabLabelBottom = this._createTabLabel(id, labelText, 'bottom');
			tab.setContent(content);
			tab.setContentElement(tabContent);
			tab.setId(id);
			tab.setLabelText(labelText);
			tab.setTabbedPane(this);
			tab.labelElements.top = tabLabelTop;
			tab.labelElements.bottom = tabLabelBottom;
			// Attatch the tab object to the tab components, so if there are any
			// event listeners on the tab components, the tab object can always
			// be retrieved.
			tabContent.tab = tab;
			// the labels need to have all children know about the tab.
			this._attachTab(tabLabelTop, tab);
			this._attachTab(tabLabelBottom, tab);
			return tab;
		},
		// _createTabContent Creates the tab content element for the given id and content.
		// @param id
		// @param content
		_createTabContent: function(id, content) {
			var typeOfContent = typeof(content);
			if(typeOfContent == 'string') {
				content = new String(content);
			}
			content = $(content);
			var tabContent = document.createElement("div");
			tabContent.id = this.name + '_' + id + '_content';
			if(typeOfContent == 'string') {
				Element.update(tabContent, content);
			} else {
				tabContent.appendChild(content);
			}
			tabContent.className = this.options['contentClassName'];
			return tabContent;
		},
		// _createTabLabel Creates a tab label for the given id, labelText and position
		// @param id
		// @param labelText
		// @param position
		_createTabLabel: function(id, labelText, position) {
			var tabLabel = document.createElement("div");
			tabLabel.id = this.name + '_' + id + '_label_' + position;
			tabLabel.innerHTML = labelText;
			tabLabel.className = this.options[position + 'LabelClassName'];
			if(this.options['useVariableLabelWidth']) {
				var tabLabelWidth = this._calculateTabLabelWidth(tabLabel, labelText);
				Element.setStyle(tabLabel, {
					width: tabLabelWidth
				});
			}
			this.addDefaultLabelClickListener(tabLabel);
			return tabLabel;
		},
		// _createTabStopContent Creates a tab stop for the given position.
		// @param position
		_createTabStopContent: function(position) {
			var tabStopContent = document.createElement("br");
			tabStopContent.id = this.name + '_tabStopContent_' + position;
			tabStopContent.className = this.options[position + 'TabStopClassName'];
			return tabStopContent;
		},
		// _deSelectTabLabel deselects the labels associated with the given tab id
		// @param id
		_deSelectTabLabels: function(id) {
			var tab = this.tabs[id];
			if(tab && typeof(tab) != 'undefined' && tab != null) {
				var tabLabelElements = tab.getLabelElements();
				tabLabelElements.top.className = this.options['topLabelClassName'];
				tabLabelElements.bottom.className = this.options['bottomLabelClassName'];
			}
		},
		// _getPostTabContent Finds the Post tab content for the given position.
		// @param position
		_getPostTabContent: function(position) {
			var postTabContent = $(this.name + '_postTabContent_' + position);
			return postTabContent;
		},
		// _getPreTabContent Finds the pre tab content for the given position.
		// @param position
		_getPreTabContent: function(position) {
			var preTabContent = $(this.name + '_preTabContent_' + position);
			return preTabContent;
		},
		// _getTabIndex Finds the index of the tab with the given id.
		// @param id
		_getTabIndex: function(id) {
			var tabIds = this.getTabs();
			var length = tabIds.length;
			var index = -1;
			var tabId = null;
			for(var tabIndex = 0; tabIndex < length && index < 0; tabIndex++) {
				tabId = tabIds[tabIndex];
				if(id == tabId) {
					index = tabIndex;
				}
			}
			return index;
		},
		// _getTabStopContent Finds the tap stop content for the given position.
		// @param position
		_getTabStopContent: function(position) {
			var tabStopContent = $(this.name + '_tabStopContent_' + position);
			return tabStopContent;
		},
		// _hasTab Checks to see if a tab with the given id already exists.
		// @param id
		_hasTab: function(id) {
			var hasTab = false;
			if(this.tabs[id] && typeof(this.tabs[id]) != 'undefined') {
				hasTab = true;
			}
			return hasTab;
		},
		// _hidePostTabContent Hides the post tab content for the given position.
		// @param position
		_hidePostTabContent: function(position) {
			var postTabContent = this._getPostTabContent(position);
			Element.hide(postTabContent);
		},
		// _hidePreTabContent Hides the pre tab content for the given position.
		// @param position
		_hidePreTabContent: function(position) {
			var preTabContent = this._getPreTabContent(position);
			Element.hide(preTabContent);
		},
		// _hideTabContent Hides the content associated with the given tab id.
		// @param id
		_hideTabContent: function(id) {
			var tab = this.tabs[id];
			if(tab && typeof(tab) != 'undefined' && tab != null) {
				Element.hide(tab.getContentElement());
			}
		},
		// _mergeOptions merges the given options with the default ones.
		// @param options The options given in the constructor
		_mergeOptions: function(options) {
			this.options = this._createDefaultOptions();
			Object.extend(this.options, options);
			var styleClassName = this.options['styleClassName'];
			// correct default/generic classNames to include the styleClassName option.
			if(styleClassName && typeof(styleClassName) != 'undefined') {
				var updatedOptions = {
					contentClassName: styleClassName + "_" + this.options['contentClassName'],
					contentContainerClassName: styleClassName + "_" + this.options['contentContainerClassName'],
					labelClassName: styleClassName + "_" + this.options['labelClassName'],
					labelContainerClassName: styleClassName + "_" + this.options['labelContainerClassName'],
					mainContainerClassName: styleClassName + "_" + this.options['mainContainerClassName'],
					postTabContentClassName: styleClassName + "_" + this.options['postTabContentClassName'],
					preTabContentClassName: styleClassName + "_" + this.options['preTabContentClassName'],
					selectedLabelClassName: styleClassName + "_" + this.options['selectedLabelClassName'],
					tabStopClassName: styleClassName + "_" + this.options['tabStopClassName']
				};
				Object.extend(this.options, updatedOptions);
			}
			// correct top/bottom label classNames.
			if(!this.options['topLabelClassName'] || typeof(this.options['topLabelClassName']) == 'undefined') {
				this.options['topLabelClassName'] = this.options['labelClassName'];
			}
			if(!this.options['bottomLabelClassName'] || typeof(this.options['bottomLabelClassName']) == 'undefined') {
				this.options['bottomLabelClassName'] = this.options['labelClassName'];
			}
			
			// correct top/bottom label container classNames.
			if(!this.options['topLabelContainerClassName'] || typeof(this.options['topLabelContainerClassName']) == 'undefined') {
				this.options['topLabelContainerClassName'] = this.options['labelContainerClassName'];
			}
			if(!this.options['bottomLabelContainerClassName'] || typeof(this.options['bottomLabelContainerClassName']) == 'undefined') {
				this.options['bottomLabelContainerClassName'] = this.options['labelContainerClassName'];
			}
			
			// correct top/bottom tabStop classNames.
			if(!this.options['topTabStopClassName'] || typeof(this.options['topTabStopClassName']) == 'undefined') {
				this.options['topTabStopClassName'] = this.options['tabStopClassName'];
			}
			if(!this.options['bottomTabStopClassName'] || typeof(this.options['bottomTabStopClassName']) == 'undefined') {
				this.options['bottomTabStopClassName'] = this.options['tabStopClassName'];
			}
			
			// correct top/bottom preTab content classNames.
			if(!this.options['topPreTabContentClassName'] || typeof(this.options['topPreTabContentClassName']) == 'undefined') {
				this.options['topPreTabContentClassName'] = this.options['preTabContentClassName'];
			}
			if(!this.options['bottomPreTabContentClassName'] || typeof(this.options['bottomPreTabContentClassName']) == 'undefined') {
				this.options['bottomPreTabContentClassName'] = this.options['preTabContentClassName'];
			}
			
			// correct top/bottom postTab content classNames.
			if(!this.options['topPostTabContentClassName'] || typeof(this.options['topPostTabContentClassName']) == 'undefined') {
				this.options['topPostTabContentClassName'] = this.options['postTabContentClassName'];
			}
			if(!this.options['bottomPostTabContentClassName'] || typeof(this.options['bottomPostTabContentClassName']) == 'undefined') {
				this.options['bottomPostTabContentClassName'] = this.options['postTabContentClassName'];
			}
			
			// correct selected label classNames.
			if(!this.options['selectedLabelClassName'] || typeof(this.options['selectedLabelClassName']) == 'undefined') {
				this.options['selectedLabelClassName'] = this.options['labelClassName'];
			}
			
			// correct top/bottom selected label classNames.
			if(!this.options['topSelectedLabelClassName'] || typeof(this.options['topSelectedLabelClassName']) == 'undefined') {
				this.options['topSelectedLabelClassName'] = this.options['selectedLabelClassName'];
			}
			if(!this.options['bottomSelectedLabelClassName'] || typeof(this.options['bottomSelectedLabelClassName']) == 'undefined') {
				this.options['bottomSelectedLabelClassName'] = this.options['selectedLabelClassName'];
			}
		},
		// _removeTabContent Removes the given tab's content from the content container.
		// @param id
		_removeTabContent: function(id) {
			var contentContainer = this.getContentContainer();
			var tab = this.getTab(id);
			if(tab != null) {
				var tabContent = tab.getContentElement();
				contentContainer.removeChild(tabContent);
			}
		},
		// _removeTabLabel Removes the given tab's labels from the top and bottom
		// containers
		// @param id
		_removeTabLabels: function(id) {
			var topLabelContainer = this.getTopLabelContainer();
			var bottomLabelContainer = this.getBottomLabelContainer();
			var tab = this.getTab(id);
			if(tab != null) {
				var labelElements = tab.getLabelElements();
				topLabelContainer.removeChild(labelElements.top);
				bottomLabelContainer.removeChild(labelElements.bottom);
			}
		},
		// _selectTabLabels Selects the tab labels for the tab associated with the given id.
		// @param id
		_selectTabLabels: function(id) {
			var tab = this.tabs[id];
			var tabLabelElements = tab.getLabelElements();
			tabLabelElements.top.className = this.options['topSelectedLabelClassName'];
			tabLabelElements.bottom.className = this.options['bottomSelectedLabelClassName'];
		},
		// _showPostTabContent Shows the post tab content for the given position.
		// @param position
		_showPostTabContent: function(position) {
			var postTabContent = this._getPostTabContent(position);
			Element.show(postTabContent);
		},
		// _showPreTabContent Shows the pre tab content for the given position.
		// @param position
		_showPreTabContent: function(position) {
			var preTabContent = this._getPreTabContent(position);
			Element.show(preTabContent);
		},
		// _showTabContent Shows the tab content for the tab associated with the given id.
		// @param id
		_showTabContent: function(id) {
			var tab = this.tabs[id];
			Element.show(tab.getContentElement());
		},
		// _updateTabWithAjaxSuccess The onSuccess callBack when updating a tab 
		// using Ajax.
		// @param transport
		// @param json
		_updateTabWithAjaxSuccess: function(transport, json) {
			// function body
			if(typeof(json) == 'undefined' || json == null) {
				var responseXml = transport.responseXML;
				var id = responseXml.getElementsByTagName("id")[0].firstChild.nodeValue;
				var labelText = responseXml.getElementsByTagName("label")[0].firstChild;
				labelText.parentNode.removeChild(labelText);
				var content = responseXml.getElementsByTagName("content")[0].firstChild;
				this.updateTab(id, labelText, content);
			}
		},
		// _updateTabWithAjaxFailure The onFailure callback when updating a tab 
		// using Ajax.
		// @param transport
		// @param json
		_updateTabWithAjaxFailure: function(transport, json) {
		}
	});
	
// Tab Class
	var Tab = Class.create();
	Object.extend(Tab.prototype, Object.prototype);
	Object.extend(Tab.prototype, {
		// initialize creates a new tab
		initialize: function() {
			this.labelElements = {
				top: null,
				bottom: null
			};
		},
		// getContent returns the original content of this tab - excluding the
		// container element.
		getContent: function() {
			return this.content;
		},
		// getContentElement returns the content of this tab inside it's container
		// element.
		getContentElement: function() {
			return this.contentElement;
		},
		// getId gets the id of this tab.
		getId: function() {
			return this.id;
		},
		// getLabelElements gets the label elements for this tab.
		getLabelElements: function() {
			return this.labelElements;
		},
		// getLabelText returns the original label text for this tab.
		getLabelText: function() {
			return this.labelText;
		},
		// getTabbedPane gets the tabbed pane this tab belongs to
		getTabbedPane: function() {
			return this.tabbedPane;
		},
		// setContent Sets the original content for this tab - excluding the
		// container element.
		// @param content
		setContent: function(content) {
			this.content = content;
		},
		// setContentElement Sets the content container element for this tab.
		// @param contentElement
		setContentElement: function(contentElement) {
			this.contentElement = contentElement;
		},
		// setId sets the id of this tab
		setId: function(id) {
			this.id = id;
		},
		// setLabelElements sets the labelElements for this tab.
		// @param labelElements Should be a map with two keys "top" and "bottom".
		setLabelElements: function(labelElements) {
			this.labelElements = labelElements;
		},
		// setLabelText sets the original label text for this tab.
		// @param labelText
		setLabelText: function(labelText) {
			this.labelText = labelText;
		},
		// setTabbedPane sets the tabbed pane this tab belongs to
		// @param tabbedPaneObj - obj appended here in case there's a global variable
		// named "tabbedPane".
		setTabbedPane: function(tabbedPaneObj) {
			this.tabbedPane = tabbedPaneObj;
		}
	});
/* Created using jEdit - Programmer's Text Editor (www.jedit.org).
	Open this file in jEdit to see what the next line does. */
/* ::tabSize=3:indentSize=3:noTabs=false:folding=indent:collapseFolds=3:: */