1 /**
  2  * @author Gillis Haasnoot <gillis.haasnoot@gmail.com>
  3  * @package Banana.Controls
  4  * @summary TabSlider   
  5  */
  6 
  7 goog.provide('Banana.Controls.TabSlider');
  8 
  9 /** @namespace Banana.Controls.TabSlider */
 10 namespace('Banana.Controls').TabSlider = Banana.Controls.Panel.extend({
 11 /** @lends Banana.Controls.TabSlider.prototype */	
 12 	
 13 	/**
 14 	 * Creates a Tab Slider. A Tabslider is a horizontaly sliding component. 
 15 	 * Content can be added by using the addContent(content,tabname) method. 
 16 	 * Optionally setUseUrlHistory can be used to have tabslides available in browser history
 17 	 *  
 18 	 * Example:
 19 	 
 20 	var slider = new Banana.Controls.TabSlider();
 21 	slider.setStyle("height:300px; width:800px; border:1px solid black;border:1px solid #666666;");
 22 	slider.setUseUrlHistory(true);
 23 	slider.setSlideSpeed(1000);
 24 	slider.addContent(new Banana.Controls.DropDown(),"dropdown");
 25 	slider.addContent(new Banana.Controls.CheckBox(),"checkbox");
 26 
 27 	 * 
 28 	 * @constructs
 29 	 * @extends Banana.Controls.Panel
 30 	 */
 31 	init : function()
 32 	{
 33 		this._super();
 34 		this.addCssClass("BTabSlider")
 35 		
 36 		this.content = [];
 37 		
 38 		this.activatedTab = 0;
 39 		this.slideSpeed = 400;	
 40 		this.easing = 'swing';
 41 		this.useAutoHeight = false;
 42 		this.useUrlHistory = false;
 43 		
 44 		this.linkNextText = " ";
 45 		this.linkPreviousText = " ";
 46 	},
 47 	
 48 	onWindowResize : function()
 49 	{
 50 		this.repositionPanels();
 51 	},
 52 	
 53 	
 54 	/**
 55 	 * Adds content which is accessible through a tab link
 56 	 * Content can be a control or just plain text. A tabname is required and will appear as a link on top of the control
 57 	 * @param {mixed} content can be a control or plain text
 58 	 * @param {String} tabname
 59 	 * @return {this}
 60 	 */
 61 	addContent : function(content,tabname)
 62 	{
 63 		if (!tabname || typeof(tabname) != "string")
 64 		{
 65 			return log.error("cannot add content without tabname");
 66 		}
 67 		this.content.push({content:content,tabname:tabname});
 68 		
 69 		return this;
 70 	},
 71 	
 72 	/**
 73 	 * Set transition speed during slide
 74 	 * @param {int} speed in ms
 75 	 * @return {this}
 76 	 */
 77 	setSlideSpeed : function(speed)
 78 	{
 79 		this.slideSpeed = speed;
 80 		return this;
 81 	},
 82 	
 83 	/**
 84 	 * When true we set the height of the tabslider based on the current active tab.
 85 	 * Note that css properties margin and padding are ignored. This is due the fact that the tabslider uses absolute
 86 	 * positioned divs. height should be set manually or calculated with javascript.
 87 	 * 
 88 	 * @param {boolean}
 89 	 * @return {this}
 90 	 */
 91 	setUseAutoHeight : function(bool)
 92 	{
 93 		this.useAutoHeight = bool
 94 		return this;
 95 	},
 96 	
 97 	/**
 98 	 * Set easing type "swing" || "linear"
 99 	 * See JQuery animate() method for more information
100 	 * 
101 	 * @param {String} easing
102 	 * @return {this}
103 	 */
104 	setEasing : function(easing)
105 	{
106 		this.easing = easing;
107 		return this;
108 	},
109 	
110 	
111 	/**
112 	 * if true we save the active tab in the url. Using browser history will affect current active tab 
113 	 * @param {boolean}
114 	 * @return {this} 
115 	 */
116 	setUseUrlHistory : function(use)
117 	{
118 		this.useUrlHistory = use;
119 		return this;
120 	},
121 	
122 	/**
123 	 * sets the text of the next link. clicking on it will advance to the next link
124 	 * @param {String} text
125 	 * @return {this}
126 	 */
127 	setLinkNextText : function(text)
128 	{
129 		this.linkNextText = text;
130 		return this;
131 	},
132 	
133 	/**
134 	 * sets the text of the previous link. clicking on it will advance to the previous link
135 	 * @param {String} text
136 	 * @return {this}
137 	 */
138 	setLinkPreviousText : function()
139 	{
140 		this.linkPreviousText = text;
141 		return this;
142 	},
143 	
144 	/**
145 	 * @ignore
146 	 */
147 	createComponents : function()
148 	{
149 		this.buttonContainer = new Banana.Controls.Panel().addCssClass("BTabSliderButtonContainer");
150 
151 		var contentWrapper = new Banana.Controls.Panel().addCssClass("BTabSliderContentWrapper");
152 		
153 		this.contentContainer = new Banana.Controls.Panel().addCssClass("BTabSliderContentContainer");
154 		contentWrapper.addControl(this.contentContainer);
155 
156 		this.addControl(this.buttonContainer);
157 		this.addControl(contentWrapper);
158 		
159 		if (this.useUrlHistory)
160 		{
161 			this.urlKey = "tabSlidePos"+this.getClientId();
162 
163 			this.registerUrlHistory();
164 			
165 			var key = Banana.Util.UrlManager.getModule(this.urlKey);
166 			if (key)
167 			{
168 				this.activatedTab = key;
169 			}
170 		}
171 	},
172 	
173 	/**
174 	 * @ignore
175 	 */
176 	updateDisplay : function()
177 	{		
178 		//clear first. ie after refresh
179 		this.buttonContainer.clear();
180 		this.contentContainer.clear();
181 		
182 		var dim = this.getDimensions();
183 			
184 		//make sure the content container got the width of all contents together
185 		var containerWidth = dim.width * this.content.length;
186 		this.contentContainer.setCss({"width":containerWidth+"px"});
187 		
188 		//sizes for link place holders. we want them to equally spread out 
189 		var edgeContainerSize = 40;
190 		var centerContainersSize = (dim.width - (edgeContainerSize*2)) / this.content.length; 
191 		
192 		var widthToPerc = function(w)
193 		{
194 			return (w/dim.width) * 100 
195 		}
196 		
197 		
198 		var firstLinkWrapper = new Banana.Controls.Panel().setCss({'width':widthToPerc(edgeContainerSize)+'%'}).addCssClass("BTabSliderlinkWrapper");
199 		this.buttonContainer.addControl(firstLinkWrapper);
200 		
201 		var buttonPrev = new Banana.Controls.Link().addCssClass("BTabSliderLink BTabSliderLinkPrevious");
202 		buttonPrev.addControl(this.linkPreviousText);
203 		buttonPrev.setHref("#previous tab");
204 		firstLinkWrapper.addControl(buttonPrev);
205 		
206 		buttonPrev.bind('click',this.getProxy(function(e){
207 			
208 			this.activatedTab = this.ensureValidTab(--this.activatedTab);
209 			this.repositionPanels(this.slideSpeed);
210 			return false;
211 		}));
212 		
213 		var i,len;
214 		for (i=0, len=this.content.length; i< len; i++)
215 		{
216 			var linkWrapper = new Banana.Controls.Panel().setCss({'width':widthToPerc(centerContainersSize)+'%'}).addCssClass("BTabSliderlinkWrapper");
217 			this.buttonContainer.addControl(linkWrapper);
218 			
219 			var button = new Banana.Controls.Link().addCssClass("BTabSliderLink");
220 			button.addControl(this.content[i].tabname);
221 			button.setHref("#"+this.content[i].tabname);
222 			linkWrapper.addControl(button);
223 
224 			button.bind('click',this.getProxy(function(e){
225 				this.activatedTab = parseInt(e.data,10);
226 				this.repositionPanels(this.slideSpeed);
227 				return false;
228 			}),i.toString());
229 				
230 			//create a placeHolder where inside the content will be placed
231 			var panelHolder = new Banana.Controls.Panel().addCssClass("BTabSliderContentItemContainer");
232 			panelHolder.setCss({'width':dim.width+'px'});
233 			panelHolder.addControl(this.content[i].content);
234 			this.contentContainer.addControl(panelHolder);
235 		}
236 		
237 		var lastLinkWrapper = new Banana.Controls.Panel().setCss({'width':widthToPerc(edgeContainerSize)+'%'}).addCssClass("BTabSliderlinkWrapper");
238 		this.buttonContainer.addControl(lastLinkWrapper);
239 		
240 		var buttonNext = new Banana.Controls.Link().addCssClass("BTabSliderLink BTabSliderLinkNext");
241 		buttonNext.addControl(this.linkNextText);
242 		buttonNext.setHref("#previous tab");
243 		lastLinkWrapper.addControl(buttonNext);
244 		
245 		buttonNext.bind('click',this.getProxy(function(e){
246 			
247 			this.activatedTab = this.ensureValidTab(++this.activatedTab);
248 			this.repositionPanels(this.slideSpeed);
249 			return false;
250 		}));
251 		
252 		this.buttonContainer.invalidateDisplay();
253 		this.contentContainer.invalidateDisplay();
254 		
255 		this.repositionPanels(0);
256 	},
257 	
258 	/**
259 	 * @ignore
260 	 * Ensures valid tab range
261 	 */
262 	ensureValidTab : function(n)
263 	{
264 		if (n < 0)
265 		{
266 			n = this.content.length-1;
267 		}		
268 		else if (n > this.content.length-1)
269 		{
270 			n = 0;
271 		}
272 		
273 		return n;
274 	},
275 	
276 	/**
277 	 * Auto sets height. Because the tabslider uses absolute positioned divs, we have to set the height
278 	 * manually. this function calculates and sets the height based on the current active tab
279 	 */
280 	applyAutoHeight : function()
281 	{
282 		var height = this.content[this.activatedTab].content.getDimensions().height;
283 		
284 		this.setCss({'height':height+this.buttonContainer.getDimensions().height});		
285 	},
286 	
287 	/**
288 	 * @ignore
289 	 * Registers url change listener to make sure that when the user changes the url we reposition the panels
290 	 */
291 	registerUrlHistory : function()
292 	{
293 		Banana.Util.UrlManager.registerModule(this.urlKey);
294 		Banana.Util.UrlManager.listenModule(this.urlKey,this.getProxy(function(e,param){
295 
296 			this.activatedTab = param;
297 			this.repositionPanels(this.slideSpeed);
298 			
299 		}));
300 	},
301 	
302 	/**
303 	 * ensures the correct position of the current active tab
304 	 */
305 	repositionPanels : function(speed)
306 	{
307 		this.triggerEvent('onSlide');
308 		
309 		if (this.useAutoHeight)
310 		{
311 			this.applyAutoHeight();
312 		}
313 		
314 		var contentWidth = this.getDimensions().width;
315 		var left = this.activatedTab * contentWidth * -1;
316 		
317 		jQuery('#'+this.buttonContainer.getClientId()+' .BTabSliderLink').removeClass("BTabSliderLinkActive");
318 		jQuery('#'+this.buttonContainer.getClientId()+' .BTabSliderLink:eq('+(parseInt(this.activatedTab)+1)+')').addClass("BTabSliderLinkActive");
319 		
320 		jQuery("#"+this.contentContainer.getClientId()).animate({'left':left+"px"},{ duration: speed, easing: this.easing, complete:this.getProxy(function(){
321 			
322 			if (this.useUrlHistory)
323 			{
324 				//unregister module to prevent endless event loop
325 				Banana.Util.UrlManager.unlistenModule(this.urlKey);
326 				Banana.Util.UrlManager.setModule(this.urlKey,this.activatedTab.toString());
327 				this.registerUrlHistory();
328 			}
329 
330 			this.triggerEvent('onSlideFinished');
331 		})});
332 	}
333 });