
/**
 * Private Singleton fpr managing e.g. open events of  
 * @class Publicis.Components.SelectBoxManager
 * @namespace Publicis.Components
 * @singleton
 */
Publicis.Components.SelectBoxManager = {
	boxes: [],
	register: function (cmp){
		Publicis.Components.SelectBoxManager.boxes.push(cmp);
		cmp.addListener("open", Publicis.Components.SelectBoxManager.closeOthers);
	},
	closeOthers: function (cmp){
		for (var i = 0; i < Publicis.Components.SelectBoxManager.boxes.length; i++){
			if (cmp != Publicis.Components.SelectBoxManager.boxes[i]){
				Publicis.Components.SelectBoxManager.boxes[i].closeList();
			}
		}
	}
};

/**
 * @namespace Publicis.Components
 * @class Publicis.Components.SelectBox
 * @extends Publicis.Observable
 * Makes a html like selectbox with more events ,controlls and configuration options
 * <br><br>
 * Dependency on Prototype.js Version 1.6.x
 * <br><br>
 * TODO: Add CSS Styles to /resources/Publicis.css<br><br>
 * Example: 
 * <br>
 * <xmp style='background-color:#dddddd; border: 1px solid #aaaaaa;'>
 Publicis.onDomReady(function (){
		
		combo = new Publicis.Components.SelectBox({
			renderTo: "container",
			data: [
				{
					value: "1",
					text : "AAA"
				},
				{
					value: "2",
					text : "BBB"
				},
				{
					value: "3",
					text : "CCC"
				}
			],
			valueProp: "value",
			textProp: "text",
			emptyText: "Bitte Selektieren",
			name: "feld1",
			id: "region-general",
			dhtmlScroll: false
		})
	
You need a DIV Tag with ID = 'container' in your HTML Code
</xmp>
 * @constructor
 * Creates a new Select Box
 * @param {Object} cfg Configuration options
 * @author Alexis Dorn
 */
			
Publicis.Components.SelectBox = function (cfg){
	this.cfg = Object.clone(cfg);
	/**
	 * @cfg {String|DOMElement} renderTo Container Element of list
	 */
	this.container = $(this.cfg.renderTo);
	/**
	 * @cfg {Object[]} data Data Container. Array of data objects (@see config.valueProp and config.textProp)
	 */
	/**
	 * @cfg {String} valueProp Key of data objects, which will contain the value (@see config.data)
	 */  
	 /**
	 * @cfg {String} textProp Key of data objects, which will contain the text (@see config.data)
	 */
	 /**
	 * @cfg {String} bodyCls CSS Class of the List Container. Default to pubList
	 */  
	 if (typeof this.cfg.bodyCls == "undefined"){
		this.cfg.bodyCls = "gui-select";
	 }
	 /**
	 * @cfg {String} disabled true to render disabled
	 */  
	 if (typeof this.cfg.disabled != "boolean"){
		this.cfg.disabled = false;
	 }
	 /**
	 * @cfg {String} emptyText A text which will be shown, when no selection is done
	 */  
	 if (typeof this.cfg.emptyText == "undefined"){
		this.cfg.emptyText = "";
	 }
	 /**
	 * @cfg {Integer} selectedIndex Index of the selected Data. Not yet implemented
	 */ 
	this.selectedIndex = false;
	if (!isNaN(parseInt(this.cfg.selectedIndex, 10))){
		this.selectedIndex = this.cfg.selectedIndex;
	}
	/**
	* @cfg {Integer} name Name of the form element
	*/ 
	if (typeof this.cfg.name == "undefined"){
		this.cfg.name = "";
	}
	
	/**
	* @cfg {String} id Unique ID for the component
	*/ 
	if (typeof this.cfg.id == "undefined"){
		this.cfg.id = Publicis.uniqueId();
	}
	/**
	* @cfg {Boolean} dhtmlScroll If the items of the box will change dynamically, make sure not to enable dhtmlScroll
	* as there is a bug in the slider
	*/  
	if (typeof this.cfg.dhtmlScroll != "boolean"){
		this.cfg.dhtmlScroll = false;
	}
	
	
	Publicis.Components.SelectBox.superclass.constructor.call(this, cfg);
	
	
	this.registerEvent([
		/**
		 * @event render
		 * Fires when component is rendered
		 * @param {Publicis.Components.SelectBox} this
		 * @param {Prototype.Element} this.el
		 */
		"render",
		/**
		 * @event itemclick
		 * Fires when a item has been clicked
		 * @param {Publicis.Components.SelectBox} this
		 * @param {Prototype.Element} el Element of the list item
		 * @param {Mixed} value Value of the list item
		 * @param {String} text Text of the list item
		 * @param {Integer} index Index of data
		 */
		"itemclick",
		/**
		 * @event update
		 * Fires when component is updated with data
		 * @param {Publicis.Components.SelectBox} this
		 * @param {Prototype.Element} this.el
		 * @param {Array} this.data
		 */
		"update",
		/**
		 * @event beforeupdate
		 * Fires when before component is updated with data. Return false to stop updating
		 * @param {Publicis.Components.SelectBox} this
		 * @param {Prototype.Element} this.el
		 * @param {Array} this.data
		 */
		"beforeupdate",
		/**
		 * @event select
		 * Fires when a item has been selected
		 * @param {Publicis.Components.SelectBox} this
		 * @param {Object} selection Selection Object (text, value, index)
		 * @param {Object} oldSelection Former selection Object (text, value, index)
		 */
		"select",
		/**
		 * @event beforeopen
		 * Fires when before the list will be opened. Return false to stop open
		 * @param {Publicis.Components.SelectBox} this
		 */
		"beforeopen",
		/**
		 * @event open
		 * Fires when the list is opened.
		 * @param {Publicis.Components.SelectBox} this
		 */
		"open",
		/**
		 * @event beforeclose
		 * Fires when before the list will be closed. Return false to stop closing
		 * @param {Publicis.Components.SelectBox} this
		 */
		"beforeclose",
		/**
		 * @event close
		 * Fires when the list is closed.
		 * @param {Publicis.Components.SelectBox} this
		 */
		"close",
		/**
		 * @event beforeload
		 * Fires when before the list will be marked as loading. Return false to stop loading
		 * @param {Publicis.Components.SelectBox} this
		 */
		"beforeload",
		/**
		 * @event load
		 * Fires when the list is dismarked as load (@see this.clearLoading).
		 * @param {Publicis.Components.SelectBox} this
		 */
		"load"
		
	]);
	/**
	 * @private
	 */
	this.selection = false;
	/**
	 * @private
	 */
	this.rendered = false;
	/**
	 * @private
	 */
	this._isLoading = false;
	
	this.render();

	//Register at manager
	Publicis.Components.SelectBoxManager.register(this);
};

Publicis.extend(Publicis.Components.SelectBox, Publicis.Observable, {
	/**
	 * Return the List Template
	 * @private
	 */
	getTemplate: function (){
		return new Template([
				"<div id='#{id}' class='#{bodyCls}'>",
					"<p class='title' id='#{id}-titleelement'><a href='javascript:void(null)' id='#{id}-selectfield' pubActive='0' title='#{tooltipText}'>#{selectedText}</a></p>",
					"<input id='#{id}-inputfield' type='hidden' name='#{name}' value='#{selectedValue}'/>",
					"<div id='#{id}-listcontainer' class='content' style='#{contentStyle}'></div>",
					//"<div id='#{id}-loadingcontainer' class='loader' style='position:relative;top:0px;left:0px;border:1px solid black;'><img src='" + RESOURCES_PATH + "../pub/img/ajax-loading.gif' alt=''/></div>",
				"</div>"
			].join("")
		);	
	},
	/**
	 * Builds the list
	 * @private
	 */
	render: function (){
		var template = this.getTemplate();
		var tmplateData = {
			id: this.cfg.id,
			bodyCls: this.cfg.bodyCls, 
			selectedText: this.cfg.emptyText,
			selectedValue: "",
			tooltipText: this.cfg.emptyText,
			name: this.cfg.name,
			//contentStyle: "height:100px;overflow:auto;background-color:red;width: 500px;"
			contentStyle: ""
		};
		//this.container.style.width = "400px";
		this.container.innerHTML = template.evaluate(tmplateData);
		
		
		this.titleElement = $(document.getElementById(this.cfg.id + "-titleelement"));
		this.selectfield = $(document.getElementById(this.cfg.id + "-selectfield"));
		this.inputfield = $(document.getElementById(this.cfg.id + "-inputfield"));
		this.listcontainer = $(document.getElementById(this.cfg.id + "-listcontainer"));
		this.el = this.elBody = $(document.getElementById(this.cfg.id));
		
		if (this.cfg.disabled){
			this.selectfield.setOpacity(0.5);
		}
		
		//Obserer to close the liston document click
		var body = $(document.getElementsByTagName("body")[0]);
		body.observe("click", this.closeList.bindAsEventListener(this, true));
		//Observer for click event on selector
		this.selectfield.observe("click", this.onSelectFieldClick.bindAsEventListener(this));
		
		this.list = new Publicis.Components.List({
			renderTo: this.listcontainer,
			data: this.cfg.data,
			valueProp: this.cfg.valueProp,
			textProp: this.cfg.textProp,
			id: this.cfg.id + "-listobject",
			dhtmlScroll: this.cfg.dhtmlScroll,
			listeners: {
				itemclick: {
					fn: this.onItemClick,
					scope: this
				},
				update:{
					fn: this.onListUpdate,
					scope: this
				},
				beforeupdate: {
					fn: this.onBeforeListUpdate,
					scope: this
				}
			}
		});
		
		this.rendered = true;
		this.fireEvent("render", this, this.el);
	},
	/**
	 * Observer for click event on the a tag
	 * @private
	 * @param {Object} e
	 */
	onSelectFieldClick: function (e){
		if (this.cfg.disabled){
			return;
		}
		var open = this.selectfield.readAttribute("pubActive");
		if (open == 0){
			this.openList();
		}else{
			this.closeList();
		}
		Event.stop(e);
	},
	/**
	 * Open the list
	 * @private
	 */
	openList: function (){
		if (this.fireEvent("beforeopen", this) === false){
			return;
		}
		
		this.selectfield.setAttribute("pubActive", "1");
		this.el.addClassName("active-gui-select");
		this.listcontainer.addClassName("active-content");
		if (false && this.list.hasDhtmlScrollbar()) {
			this.list.scrollbar.setValue(0);
		}
		
		//checking if list is out of viewport
		var viewport = document.viewport.getDimensions();
		var elPosInViewport = this.listcontainer.viewportOffset();
		var elHeight = this.listcontainer.getHeight();
		var elButtomLine = elPosInViewport.top + elHeight;
		if (elButtomLine > viewport.height){
			var newHeight = viewport.height - elPosInViewport.top;
			if (newHeight > 10){
				this.listcontainer.style.height = newHeight + "px";
				this.listcontainer.style.overflowY = "scroll"; 
			}   
		}  
		
		this.fireEvent("open", this);
	},
	/**
	 * Close the list
	 * @private
	 */
	closeList: function (e, a){
		if(typeof a != "undefined" && a === true){
			var open = this.selectfield.readAttribute("pubActive");
			if (open == 0) {
				return;
			}
		}
		if (this.fireEvent("beforeclose", this) === false){
			return;
		}
		this.selectfield.setAttribute("pubActive", "0");
		this.el.removeClassName("active-gui-select");
		this.listcontainer.removeClassName("active-content");
		this.fireEvent("close", this);
	},
	/**
	 * Updates selected data and call this.updateView. If value doesn´t exits, list and selection will be cleared
	 * @private
	 */
	updateSelection: function(value){
		var data = this.list.getDataById(value);
		
		if (data === false){
			this.selection = false;
			this.clearView();
			return false;
		}
		oldSelection = this.getSelection();
		if (oldSelection !== false){
			oldSelection = Object.clone(oldSelection);
		}
				
		this.selection = {
			index: data.index,
			value: data.value,
			text: data.text
		};
		
		this.updateView();
		
		this.fireEvent("select", this, this.selection, oldSelection);
	},
	/**
	 * Updates the view with the selected data
	 * @private
	 */
	updateView: function (){
		this.selectfield.innerHTML = this.selection.text;
		this.inputfield.setAttribute("value", this.selection.value);
	},
	/**
	 * Clears view 
	 * @private
	 */
	clearView: function (emptyText){
		this.selectfield.innerHTML = (typeof emptyText != "undefined") ? emptyText : this.cfg.emptyText;
		this.inputfield.setAttribute("value", "");
	},
	/**
	 * Observer for list event
	 * @private
	 * @param {Object} list
	 * @param {Object} el
	 * @param {Object} data
	 */
	onListUpdate: function(list, el, data){
		var sel = this.getSelection();
		if (sel !== false){
			this.updateSelection(sel.value, sel.text, sel.index);
		}
		this.fireEvent("update", this, el, data);
	},
	/**
	 * Observer for list event
	 * @private
	 * @param {Object} list
	 * @param {Object} el
	 * @param {Object} data
	 */
	onBeforeListUpdate: function(list, el, data){
		if (this.hasSelection()) {
			this.clearView("");
		}
		return this.fireEvent("beforeupdate", this, el, data);
	},
	/**
	 * Observer for list event
	 * @private
	 * @param {Object} list
	 * @param {Object} el
	 * @param {Object} value
	 * @param {Object} text
	 */
	onItemClick: function (list, el, value, text, index){
		this.fireEvent("itemclick", this, el, value, text, index);
		this.closeList();
		this.updateSelection(value, text, index);
	},
	/**
	 * Returns the input element of combobox. Don´t change properties (e.g. value, name, id) directly!!!
	 */
	getInputElement: function (){
		return this.inputfield;
	},
	/**
	 * Updates the component with data, must be in defined structure (@see config.valueProp and config.textProp) 
	 * @param {Object} data
	 */
	update : function (data){
		if (this.rendered) {
			this.list.update(data);
		}
	},
	/**
	 * Enables component for selection
	 */
	enable: function (){
		this.cfg.disabled = false;
		this.selectfield.setOpacity(1);
	},
	/**
	 * Disables component for selection
	 * @param {Boolean} clear Runs clearSelection. Default to false
	 */
	disable: function (clear, opacity){
		clear = (typeof clear == "boolean") ? clear : false;
		opacity = (typeof opacity == "number") ? opacity : 0.5;
		this.cfg.disabled = true;
		this.titleElement.removeClassName("loading");
		this.selectfield.setOpacity(opacity);
		if (clear){
			this.clearSelection();
		}
	},
	/**
	 * Returns if the selectbox is disabled
	 * @return boolean
	 */
	isDisabled: function (){
		return this.cfg.disabled;
	},
	/**
	 * Clears the Selectbox and delets all list items
	 */
	clearAll: function (){
		this.update([]);
	},
	/**
	 * Clears the selection of selectbox
	 */
	clearSelection: function (){
		this.updateSelection(false);
	},
	/**
	 * Gets a selection object (text, value, index) of hte actual selection
	 * Will be false, if no selection is available
	 * @return Object|boolean false if ther is no selection
	 */
	getSelection: function (){
		return this.selection;
	},
	/**
	 * rturn true, if there is a selection
	 */
	hasSelection: function (){
		return (this.selection === false) ? false : true;
	},
	/**
	 * Sets selection by data index
	 * Will only works, if there is the given index in data array
	 * @param {Object} index
	 * @return Boolean
	 */
	setSelection: function (index){
		var data = this.list.getData(index);
		if (data !== false){
			this.updateSelection(data.value, data.text, data.index);
			return true;
		}
		return false;
	},
	/**
	 * Sets selection by data value (id)
	 * Will only works, if there is the given id in data array
	 * @param {Object} index
	 * @return Boolean
	 */
	setSelectionById: function (id){
		var data = this.list.getDataById(id);
		if (data !== false){
			this.updateSelection(data.value, data.text, data.index);
			return true;
		}
		return false;
	},
	/**
	 * Marks Selectbox as loading and disables it.
	 * Please make sure, that css class loading exitst 
	 */
	setLoading: function(){
		if (this.fireEvent("beforeload", this) === false){
			return;
		}
		this._isLoading = true;
		this.disable(false, 0.15);
		this.titleElement.addClassName("loading");
	},
	/**
	 * Clears loading and enables box
	 */
	clearLoading: function(){
		this._isLoading = false;
		this.enable();
		this.titleElement.removeClassName("loading");
		this.fireEvent("load", this);
	},
	/**
	 * Returns, if the box is marked as loading
	 * @return boolean
	 */
	isLoading: function(){
		return this._isLoading;
	}
	 
});
