1 /**
  2  * @author Gillis Haasnoot <gillis.haasnoot@gmail.com>
  3  * @package Banana.Controls
  4  * @summary Dropdown control  
  5  */
  6 
  7 goog.provide('Banana.Controls.DataControls.ListControls.DropDown');
  8 
  9 goog.require('Banana.Controls.DataControls.ListControls.ListControl');
 10 /**
 11  * @author Gillis Haasnoot <gillis.haasnoot@gmail.com>
 12  * @link www.vivesta.com
 13  * @copyright Copyright © 2010
 14  * @license  xxx
 15  * @version 0.0.1
 16  * @package Banana.Controls
 17  *
 18  * Dropdown component
 19  *
 20  * use setDataSource() to create a a list
 21  * use setData() to select indices
 22  *
 23  */
 24 
 25 /** @namespace Banana.Controls.DropDown */
 26 namespace('Banana.Controls').DropDown = Banana.Controls.ListControl.extend(
 27 /** @lends Banana.Controls.DropDown.prototype */
 28 {	
 29 	/**
 30 	 * Creates a Dropdown
 31 	 * 
 32 	 * Example:
 33 	 
 34 	 var dropdown = new Banana.Controls.DropDown();
 35 	 
 36 	 this.addControl(dropdown);
 37 	 
 38 	 dropdown.setDataSource([1,2,3,4,5,6,7,8,9]);
 39 	 dropdown.setData(4);
 40 	 
 41 	 ///another way top populate datasource is with complex objects.
 42 	 //by default complex objects should have a dataKeyField and dataValueField.
 43 	 //where dataKeyField = key and dataValueField = value;
 44 	 //To change this use setDataKeyField and setDataValueField.
 45 	 
 46 	 dropdown.setDataSource([{key:1,value:'one'},{key:2,value:'two'}]);
 47 	 dropdown.setData(2);
 48 	 
 49 	 * @constructs
 50 	 * @extends Banana.Controls.ListControl
 51 	 */
 52 	init : function()
 53 	{
 54 		this._super();
 55 
 56 		this.addCssClass('BDropDown');
 57 		
 58 		this.optGroupField = "group";
 59 
 60 		this.bind('change',this.getProxy(function(e)
 61 		{
 62 			this.setData(Banana.Util.DomHelper.getData(this) || []);
 63 			this.isChanged = true;
 64 			this.triggerEvent('selectionChanged');
 65 		}));
 66 		
 67 		// Prevent propagation of event, because parent controls, e.g.
 68 		// a datagrid row, can capture this event in Chrome and prevent
 69 		// it from functioning.
 70 		this.bind('mousedown', function(e) {
 71 			e.stopPropagation();
 72 		});
 73 	},
 74 	
 75 	/**
 76 	 * @override
 77 	 * @ignore
 78 	 */
 79 	updateDisplay: function()
 80 	{
 81 		this._super();
 82 		
 83 		// This is a Chrome Fix
 84 		// Chrome can set the DropDown selected index to -1 showing an empty
 85 		// option in the browser. When found reset the index to 0. only when prompt text is set
 86 		if (!this.getPromptText()) return;
 87 		
 88 		if (this.isRendered) {
 89 			var obj = jQuery('#' + this.getClientId())[0];
 90 			if (obj && obj.selectedIndex == -1)
 91 				obj.selectedIndex = 0;
 92 		}
 93 	},
 94 
 95 	/**
 96 	 * @return {mixed}
 97 	 */
 98 	getData : function()
 99 	{
100 		return (this._super() == '__prompt__' ? undefined : this._super())
101 	},
102 	
103 	/**
104 	 * Set the data of the Control
105 	 * 
106 	 * @param {mixed} data Data to set
107 	 * @return {this}
108 	 */
109 	setData: function(data)
110 	{
111 		// When data is not available and prompttext is set select
112 		// the prompttext in the dropdown
113 		if ((data === null || data === undefined) && this.getPromptText()) {
114 			data = '__prompt__';
115 		}
116 		return this._super(data);
117 	},
118 	
119 	/**
120 	 * Sets the data by index.
121 	 * If your datasource is ["foo","apple","me"] you can use setDataByIndex(1) to
122 	 * set apple as the data.
123 	 * 
124 	 * @param {int} index
125 	 * @return {this}
126 	 */
127 	setDataByIndex : function(index)
128 	{
129 		var i = 0;
130 		for(var prop in this.datasource)
131 		{
132 			if (typeof(this.datasource[prop]) == 'function') continue;
133 			
134 			if (i==index)
135 			{
136 				if (this.datasource[prop][this.dataKeyField])
137 				{
138 					this.setData(this.datasource[prop][this.dataKeyField]);
139 				}
140 				else
141 				{
142 					this.setData(prop);
143 				}
144 				break;
145 			}
146 			i++;
147 		}
148 		
149 		return this;
150 	}
151 });
152 
153 /**
154  * @return {String}
155  */
156 Banana.Controls.DropDown.prototype.getTagName = function()
157 {
158 	return 'select';
159 };
160 
161 /**
162  * we override the get html function to manualy render the <option> tags
163  * instead of this method we can create an <option> class, but it will  cause some
164  * overhead in very large lists. So for optimal result we handle this way
165  * 
166  * @ignore
167  */
168 Banana.Controls.DropDown.prototype.getHtml = function()
169 {
170 	var html = [];
171 
172 	html.push('<'+this.getTagName()+' ');
173 	html.push(this.getHtmlAttributes());
174 	html.push('>')
175 
176 	var datasource = this.datasource;
177 
178 	if (this.getPromptText())
179 	{
180 		html.push(this.getOption('__prompt__',this.getPromptText()));
181 	}
182 
183 	var prevOptGroup = null;
184 	var add = false;
185 
186 	for(var prop in datasource)
187 	{
188 		//stupid prototype adds functions to array prototype
189 		if (typeof(datasource[prop]) == 'function') continue;
190 		
191 		if (typeof(datasource[prop]) == 'object')
192 		{
193 			if (!datasource[prop]) {continue;}
194 
195 			var optGroup = datasource[prop]['group'];
196 			var key = datasource[prop][this.dataKeyField];
197 			var value = datasource[prop][this.dataValueField];
198 			
199 			for (var x = 0; x < datasource[prop][this.dataDepthField]; x++) {
200 				value = "    " + value;
201 			}
202 			
203 			if (optGroup != prevOptGroup)
204 			{
205 				html.push('<optgroup label="'+optGroup+'">');
206 				add = true;
207 			}
208 
209 			html.push(this.getOption(key,value));
210 
211 			if (!add && optGroup != prevOptGroup)
212 			{
213 				html.push('</optgroup>');
214 				add = false;
215 			}
216 
217 			prevOptGroup = optGroup;
218 		}
219 		else
220 		{				
221 			var value = prop;
222 
223 			if (datasource instanceof Array)
224 			{
225 				value = datasource[prop];
226 			}
227 
228 			html.push(this.getOption(value,datasource[prop]));
229 		}
230 		
231 	}
232 
233 	html.push('</'+this.getTagName()+'>');
234 	return html.join('');
235 };
236 
237 /**
238  * Sets the prompt text. This text is visible as first option in the dropdown.
239  * Note that a prompt text doesnt have a value. calling getData() while prompttext is selected
240  * results in undefined
241  * 
242  * @param {String} text 
243  * @return {this}
244  */
245 Banana.Controls.DropDown.prototype.setPromptText = function(text)
246 {
247 	this.promptText = text;
248 	return this;
249 };
250 
251 /**
252  * @return {String}
253  */
254 Banana.Controls.DropDown.prototype.getPromptText = function()
255 {
256 	return this.promptText;
257 };
258 
259 /**
260  * Sets the optgroup
261  * @param {String} value
262  * @return {this}
263  */
264 Banana.Controls.DropDown.prototype.setOptGroupField = function(value)
265 {
266 	this.optGroupField = value;
267 	return this;
268 };
269 
270 /**
271  * @return {String}
272  */
273 Banana.Controls.DropDown.prototype.getOptGroupField = function()
274 {
275 	return this.optGroupField;
276 };
277 
278 /**
279  * When called we use opt grouping. By default the optgroup field is "group"
280  * @return {this}
281  */
282 Banana.Controls.DropDown.useOptGrouping = function()
283 {
284 	this.optGrouping = true;
285 	return this;
286 },
287 
288 /**
289  * used to render a option line
290  * 
291  * @param {String} value
292  * @param {String} key
293  * @ignore
294  * @return {String}
295  */
296 Banana.Controls.DropDown.prototype.getOption = function(value,key)
297 {
298 	var selected = '';
299 
300 	if (this.data == value || (value == '__prompt__' && this.data == undefined))
301 	{
302 		selected = 'selected=selected';
303 	}
304 	return '<option '+selected+' class="BDropDownOption" value="'+value+'">'+key+'</option>';
305 };
306 
307 /**
308  * used to render a option line with optgrouping enabled
309  * @param {String} value
310  * @param {String} key
311  * @ignore
312  * @return {this}
313  */
314 Banana.Controls.DropDown.prototype.getOptGroup = function(value,key)
315 {
316 	var selected = '';
317 
318 	if (this.getData() == value)
319 	{
320 		selected = 'selected=selected';
321 	}
322 	return '<option '+selected+' class="BDropDownOption" value="'+value+'">'+key+'</option>';
323 };