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 });