changed git call from https to git readonly
[atutor.git] / mods / photo_album / fluid / component-templates / js / jquery.ui-1.0 / ui.tabs.js
1 /*
2  * Tabs 3 - New Wave Tabs
3  *
4  * Copyright (c) 2007 Klaus Hartl (stilbuero.de)
5  * Dual licensed under the MIT (MIT-LICENSE.txt)
6  * and GPL (GPL-LICENSE.txt) licenses.
7  */
8
9 (function($) {
10
11     // if the UI scope is not availalable, add it
12     $.ui = $.ui || {};
13
14     // tabs initialization
15     $.fn.tabs = function(initial, options) {
16         if (initial && initial.constructor == Object) { // shift arguments
17             options = initial;
18             initial = null;
19         }
20         options = options || {};
21
22         initial = initial && initial.constructor == Number && --initial || 0;
23
24         return this.each(function() {
25             new $.ui.tabs(this, $.extend(options, { initial: initial }));
26         });
27     };
28
29     // other chainable tabs methods
30     $.each(['Add', 'Remove', 'Enable', 'Disable', 'Click', 'Load'], function(i, method) {
31         $.fn['tabs' + method] = function() {
32             var args = arguments;
33             return this.each(function() {
34                 var instance = $.ui.tabs.getInstance(this);
35                 instance[method.toLowerCase()].apply(instance, args);
36             });
37         };
38     });
39     $.fn.tabsSelected = function() {
40         var selected = -1;
41         if (this[0]) {
42             var instance = $.ui.tabs.getInstance(this[0]),
43                 $lis = $('li', this);
44             selected = $lis.index( $lis.filter('.' + instance.options.selectedClass)[0] );
45         }
46         return selected >= 0 ? ++selected : -1;
47     };
48
49     // tabs class
50     $.ui.tabs = function(el, options) {
51
52         this.source = el;
53
54         this.options = $.extend({
55
56             // basic setup
57             initial: 0,
58             event: 'click',
59             disabled: [],
60             // TODO bookmarkable: $.ajaxHistory ? true : false,
61             unselected: false,
62             unselect: options.unselected ? true : false,
63
64             // Ajax
65             spinner: 'Loading…',
66             cache: false,
67             idPrefix: 'tab-',
68
69             // animations
70             /*fxFade: null,
71             fxSlide: null,
72             fxShow: null,
73             fxHide: null,*/
74             fxSpeed: 'normal',
75             /*fxShowSpeed: null,
76             fxHideSpeed: null,*/
77
78             // callbacks
79             add: function() {},
80             remove: function() {},
81             enable: function() {},
82             disable: function() {},
83             click: function() {},
84             hide: function() {},
85             show: function() {},
86             load: function() {},
87
88             // CSS classes
89             navClass: 'ui-tabs-nav',
90             selectedClass: 'ui-tabs-selected',
91             disabledClass: 'ui-tabs-disabled',
92             containerClass: 'ui-tabs-container',
93             hideClass: 'ui-tabs-hide',
94             loadingClass: 'ui-tabs-loading'
95
96         }, options);
97
98         this.tabify(true);
99
100         // save instance for later
101         var uuid = 'tabs' + $.ui.tabs.prototype.count++;
102         $.ui.tabs.instances[uuid] = this;
103         $.data(el, 'tabsUUID', uuid);
104
105     };
106
107     // static
108     $.ui.tabs.instances = {};
109     $.ui.tabs.getInstance = function(el) {
110         return $.ui.tabs.instances[$.data(el, 'tabsUUID')];
111     };
112
113     // instance methods
114     $.extend($.ui.tabs.prototype, {
115         count: 0,
116         tabify: function(init) {
117
118             this.$tabs = $('a:first-child', this.source);
119             this.$containers = $([]);
120
121             var self = this, o = this.options;
122             
123             this.$tabs.each(function(i, a) {
124                 // inline tab
125                 if (a.hash && a.hash.replace('#', '')) { // safari 2 reports '#' for an empty hash
126                     self.$containers = self.$containers.add(a.hash);
127                 }
128                 // remote tab
129                 else {
130                     $.data(a, 'href', a.href);
131                     var id = a.title && a.title.replace(/\s/g, '_') || o.idPrefix + (self.count + 1) + '-' + (i + 1);
132                     a.href = '#' + id;
133                     self.$containers = self.$containers.add(
134                         $('#' + id)[0] || $('<div id="' + id + '" class="' + o.containerClass + '"></div>')
135                             .insertAfter( self.$containers[i - 1] || self.source )
136                     );
137                 }
138             });
139
140             if (init) {
141
142                 // Try to retrieve initial tab from fragment identifier in url if present,
143                 // otherwise try to find selected class attribute on <li>.
144                 this.$tabs.each(function(i, a) {
145                     if (location.hash) {
146                         if (a.hash == location.hash) {
147                             o.initial = i;
148                             // prevent page scroll to fragment
149                             //if (($.browser.msie || $.browser.opera) && !o.remote) {
150                             if ($.browser.msie || $.browser.opera) {
151                                 var $toShow = $(location.hash), toShowId = $toShow.attr('id');
152                                 $toShow.attr('id', '');
153                                 setTimeout(function() {
154                                     $toShow.attr('id', toShowId); // restore id
155                                 }, 500);
156                             }
157                             scrollTo(0, 0);
158                             return false; // break
159                         }
160                     } else if ( $(a).parents('li:eq(0)').is('li.' + o.selectedClass) ) {
161                         o.initial = i;
162                         return false; // break
163                     }
164                 });
165
166                 // attach necessary classes for styling if not present
167                 $(this.source).is('.' + o.navClass) || $(this.source).addClass(o.navClass);
168                 this.$containers.each(function() {
169                     var $this = $(this);
170                     $this.is('.' + o.containerClass) || $this.addClass(o.containerClass);
171                 });
172
173                 // highlight tab
174                 var $lis = $('li', this.source);
175                 this.$containers.addClass(o.hideClass);
176                 $lis.removeClass(o.selectedClass);
177                 if (!o.unselected) {
178                     this.$containers.slice(o.initial, o.initial + 1).show();
179                     $lis.slice(o.initial, o.initial + 1).addClass(o.selectedClass);
180                 }
181
182                 // load if remote tab
183                 if ($.data(this.$tabs[o.initial], 'href')) {
184                     this.load(o.initial + 1, $.data(this.$tabs[o.initial], 'href'));
185                     if (o.cache) {
186                         $.removeData(this.$tabs[o.initial], 'href'); // if loaded once do not load them again
187                     }
188                 }
189
190                 // disabled tabs
191                 for (var i = 0, position; position = o.disabled[i]; i++) {
192                     this.disable(position);
193                 }
194
195             }
196
197             // setup animations
198             var showAnim = {}, showSpeed = o.fxShowSpeed || o.fxSpeed,
199                 hideAnim = {}, hideSpeed = o.fxHideSpeed || o.fxSpeed;
200             if (o.fxSlide || o.fxFade) {
201                 if (o.fxSlide) {
202                     showAnim['height'] = 'show';
203                     hideAnim['height'] = 'hide';
204                 }
205                 if (o.fxFade) {
206                     showAnim['opacity'] = 'show';
207                     hideAnim['opacity'] = 'hide';
208                 }
209             } else {
210                 if (o.fxShow) {
211                     showAnim = o.fxShow;
212                 } else { // use some kind of animation to prevent browser scrolling to the tab
213                     showAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
214                     showSpeed = 1; // as little as 1 is sufficient
215                 }
216                 if (o.fxHide) {
217                     hideAnim = o.fxHide;
218                 } else { // use some kind of animation to prevent browser scrolling to the tab
219                     hideAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
220                     hideSpeed = 1; // as little as 1 is sufficient
221                 }
222             }
223
224             // reset some styles to maintain print style sheets etc.
225             var resetCSS = { display: '', overflow: '', height: '' };
226             if (!$.browser.msie) { // not in IE to prevent ClearType font issue
227                 resetCSS['opacity'] = '';
228             }
229
230             // Hide a tab, animation prevents browser scrolling to fragment,
231             // $show is optional.
232             function hideTab(clicked, $hide, $show) {
233                 $hide.animate(hideAnim, hideSpeed, function() { //
234                     $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
235                     if ($.browser.msie) {
236                         $hide[0].style.filter = '';
237                     }
238                     o.hide(clicked, $hide[0], $show && $show[0] || null);
239                     if ($show) {
240                         showTab(clicked, $show, $hide);
241                     }
242                 });
243             }
244
245             // Show a tab, animation prevents browser scrolling to fragment,
246             // $hide is optional
247             function showTab(clicked, $show, $hide) {
248                 if (!(o.fxSlide || o.fxFade || o.fxShow)) {
249                     $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab containers
250                 }
251                 $show.animate(showAnim, showSpeed, function() {
252                     $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
253                     if ($.browser.msie) {
254                         $show[0].style.filter = '';
255                     }
256                     o.show(clicked, $show[0], $hide && $hide[0] || null);
257                 });
258             }
259
260             // switch a tab
261             function switchTab(clicked, $hide, $show) {
262                 /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
263                     $.ajaxHistory.update(clicked.hash);
264                 }*/
265                 $(clicked).parents('li:eq(0)').addClass(o.selectedClass)
266                     .siblings().removeClass(o.selectedClass);
267                 hideTab(clicked, $hide, $show);
268             }
269
270             // tab click handler
271             function tabClick(e) {
272
273                 //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
274                 var $li = $(this).parents('li:eq(0)'),
275                     $hide = self.$containers.filter(':visible'),
276                     $show = $(this.hash);
277
278                 // If tab is already selected and not unselectable or tab disabled or click callback returns false stop here.
279                 // Check if click handler returns false last so that it is not executed for a disabled tab!
280                 if (($li.is('.' + o.selectedClass) && !o.unselect) || $li.is('.' + o.disabledClass)
281                     || o.click(this, $show[0], $hide[0]) === false) {
282                     this.blur();
283                     return false;
284                 }
285                     
286                 // if tab may be closed
287                 if (o.unselect) {
288                     if ($li.is('.' + o.selectedClass)) {
289                         $li.removeClass(o.selectedClass);
290                         self.$containers.stop();
291                         hideTab(this, $hide);
292                         this.blur();
293                         return false;
294                     } else if (!$hide.length) {
295                         $li.addClass(o.selectedClass);
296                         self.$containers.stop();
297                         showTab(this, $show);
298                         this.blur();
299                         return false;
300                     }
301                 }
302
303                 // stop possibly running animations
304                 self.$containers.stop();
305
306                 // show new tab
307                 if ($show.length) {
308
309                     // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
310                     /*if ($.browser.msie && o.bookmarkable) {
311                         var showId = this.hash.replace('#', '');
312                         $show.attr('id', '');
313                         setTimeout(function() {
314                             $show.attr('id', showId); // restore id
315                         }, 0);
316                     }*/
317
318                     if ($.data(this, 'href')) { // remote tab
319                         var a = this;
320                         self.load(self.$tabs.index(this) + 1, $.data(this, 'href'), function() {
321                             switchTab(a, $hide, $show);
322                         });
323                         if (o.cache) {
324                             $.removeData(this, 'href'); // if loaded once do not load them again
325                         }
326                     } else {
327                         switchTab(this, $hide, $show);
328                     }
329
330                     // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
331                     /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
332                     var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
333                     setTimeout(function() {
334                         scrollTo(scrollX, scrollY);
335                     }, 0);*/
336
337                 } else {
338                     throw 'jQuery UI Tabs: Mismatching fragment identifier.';
339                 }
340
341                 this.blur(); // prevent IE from keeping other link focussed when using the back button
342
343                 //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
344                 return false;
345
346             }
347
348             // attach click event, avoid duplicates from former tabifying
349             this.$tabs.unbind(o.event, tabClick).bind(o.event, tabClick);
350
351         },
352         add: function(url, text, position) {
353             if (url && text) {
354                 var o = this.options;
355                 position = position || this.$tabs.length; // append by default
356                 if (position >= this.$tabs.length) {
357                     var method = 'insertAfter';
358                     position = this.$tabs.length;
359                 } else {
360                     var method = 'insertBefore';
361                 }
362                 if (url.indexOf('#') == 0) { // ajax container is created by tabify automatically
363                     var $container = $(url);
364                     // try to find an existing element before creating a new one
365                     ($container.length && $container || $('<div id="' + url.replace('#', '') + '" class="' + o.containerClass + ' ' + o.hideClass + '"></div>'))
366                         [method](this.$containers[position - 1]);
367                 }
368                 $('<li><a href="' + url + '"><span>' + text + '</span></a></li>')
369                     [method](this.$tabs.slice(position - 1, position).parents('li:eq(0)'));
370                 this.tabify();
371                 o.add(this.$tabs[position - 1], this.$containers[position - 1]); // callback
372             } else {
373                 throw 'jQuery UI Tabs: Not enough arguments to add tab.';
374             }
375         },
376         remove: function(position) {
377             if (position && position.constructor == Number) {
378                 var $removedTab = this.$tabs.slice(position - 1, position).parents('li:eq(0)').remove();
379                 var $removedContainer = this.$containers.slice(position - 1, position).remove();
380                 this.tabify();
381                 this.options.remove($removedTab[0], $removedContainer[0]); // callback
382             }
383         },
384         enable: function(position) {
385             var $li = this.$tabs.slice(position - 1, position).parents('li:eq(0)'), o = this.options;
386             $li.removeClass(o.disabledClass);
387             if ($.browser.safari) { // fix disappearing tab after enabling in Safari... TODO check Safari 3
388                 $li.animate({ opacity: 1 }, 1, function() {
389                     $li.css({ opacity: '' });
390                 });
391             }
392             o.enable(this.$tabs[position - 1], this.$containers[position - 1]); // callback
393         },
394         disable: function(position) {
395             var $li = this.$tabs.slice(position - 1, position).parents('li:eq(0)'), o = this.options;
396             if ($.browser.safari) { // fix opacity of tab after disabling in Safari... TODO check Safari 3
397                 $li.animate({ opacity: 0 }, 1, function() {
398                    $li.css({ opacity: '' });
399                 });
400             }
401             $li.addClass(this.options.disabledClass);
402             o.disable(this.$tabs[position - 1], this.$containers[position - 1]); // callback
403         },
404         click: function(position) {
405             this.$tabs.slice(position - 1, position).trigger('click');
406         },
407         load: function(position, url, callback) {
408             var self = this,
409                 o = this.options,
410                 $a = this.$tabs.slice(position - 1, position).addClass(o.loadingClass),
411                 $span = $('span', $a),
412                 text = $span.html();
413
414             // shift arguments
415             if (url && url.constructor == Function) {
416                 callback = url;
417             }
418
419             // set new URL
420             if (url) {
421                 $.data($a[0], 'href', url);
422             }
423
424             // load
425             if (o.spinner) {
426                 $span.html('<em>' + o.spinner + '</em>');
427             }
428             setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
429                 $($a[0].hash).load(url, function() {
430                     if (o.spinner) {
431                         $span.html(text);
432                     }
433                     $a.removeClass(o.loadingClass);
434                     // This callback is required because the switch has to take place after loading
435                     // has completed.
436                     if (callback && callback.constructor == Function) {
437                         callback();
438                     }
439                     o.load(self.$tabs[position - 1], self.$containers[position - 1]); // callback
440                 });
441             }, 0);
442         }
443     });
444
445 })(jQuery);