PageSourceSearch

https://www.snafu.de/wp-content/uploads/ac_assets/uc_elegant_side_tabs/jquery.responsiveTabs.js?ver=1.5.142

js snafu.de collected 2026-09-24 07:27:13 UTC 26,451 bytes, 698 lines download raw bytes

1;(function ( $, window, undefined ) {
2
3    /** Default settings */
4    var defaults = {
5        active: null,
6        event: 'click',
7        disabled: [],
8        collapsible: 'accordion',
9        startCollapsed: false,
10        rotate: false,
11        setHash: false,
12        animation: 'default',
13        animationQueue: false,
14        duration: 500,
15        fluidHeight: true,
16        scrollToAccordion: false,
17        scrollToAccordionOnLoad: true,
18        scrollToAccordionOffset: 0,
19        accordionTabElement: '<div></div>',
20        click: function(){},
21        activate: function(){},
22        deactivate: function(){},
23        load: function(){},
24        activateState: function(){},
25        classes: {
26            stateDefault: 'r-tabs-state-default',
27            stateActive: 'r-tabs-state-active',
28            stateDisabled: 'r-tabs-state-disabled',
29            stateExcluded: 'r-tabs-state-excluded',
30            container: 'r-tabs',
31            ul: 'r-tabs-nav',
32            tab: 'r-tabs-tab',
33            anchor: 'r-tabs-anchor',
34            panel: 'r-tabs-panel',
35            accordionTitle: 'r-tabs-accordion-title'
36        }
37    };
38
39    /**
40     * Responsive Tabs
41     * @constructor
42     * @param {object} element - The HTML element the validator should be bound to
43     * @param {object} options - An option map
44     */
45    function ResponsiveTabs(element, options) {
46        this.element = element; // Selected DOM element
47        this.$element = $(element); // Selected jQuery element
48
49        this.tabs = []; // Create tabs array
50        this.state = ''; // Define the plugin state (tabs/accordion)
51        this.rotateInterval = 0; // Define rotate interval
52        this.$queue = $({});
53
54        // Extend the defaults with the passed options
55        this.options = $.extend( {}, defaults, options);
56
57        this.init();
58    }
59
60
61    /**
62     * This function initializes the tab plugin
63     */
64    ResponsiveTabs.prototype.init = function () {
65        var _this = this;
66
67        // Load all the elements
68        this.tabs = this._loadElements();
69        this._loadClasses();
70        this._loadEvents();
71
72        // Window resize bind to check state
73        $(window).on('resize', function(e) {
74            _this._setState(e);
75            if(_this.options.fluidHeight !== true) {
76                _this._equaliseHeights();
77            }
78        });
79
80        // Hashchange event
81        $(window).on('hashchange', function(e) {
82            var tabRef = _this._getTabRefBySelector(window.location.hash);
83            var oTab = _this._getTab(tabRef);
84
85            // Check if a tab is found that matches the hash
86            if(tabRef >= 0 && !oTab._ignoreHashChange && !oTab.disabled) {
87                // If so, open the tab and auto close the current one
88                _this._openTab(e, _this._getTab(tabRef), true);
89            }
90        });
91
92        // Start rotate event if rotate option is defined
93        if(this.options.rotate !== false) {
94            this.startRotation();
95        }
96
97        // Set fluid height
98        if(this.options.fluidHeight !== true) {
99            _this._equaliseHeights();
100        }
101
102        // --------------------
103        // Define plugin events
104        //
105
106        // Activate: this event is called when a tab is selected
107        this.$element.bind('tabs-click', function(e, oTab) {
108            _this.options.click.call(this, e, oTab);
109        });
110
111        // Activate: this event is called when a tab is selected
112        this.$element.bind('tabs-activate', function(e, oTab) {
113            _this.options.activate.call(this, e, oTab);
114        });
115        // Deactivate: this event is called when a tab is closed
116        this.$element.bind('tabs-deactivate', function(e, oTab) {
117            _this.options.deactivate.call(this, e, oTab);
118        });
119        // Activate State: this event is called when the plugin switches states
120        this.$element.bind('tabs-activate-state', function(e, state) {
121            _this.options.activateState.call(this, e, state);
122        });
123
124        // Load: this event is called when the plugin has been loaded
125        this.$element.bind('tabs-load', function(e) {
126            var startTab;
127
128            _this._setState(e); // Set state
129
130            // Check if the panel should be collaped on load
131            if(_this.options.startCollapsed !== true && !(_this.options.startCollapsed === 'accordion' && _this.state === 'accordion')) {
132
133                startTab = _this._getStartTab();
134
135                // Open the initial tab
136                _this._openTab(e, startTab); // Open first tab
137
138                // Call the callback function
139                _this.options.load.call(this, e, startTab); // Call the load callback
140            }
141        });
142        // Trigger loaded event
143        this.$element.trigger('tabs-load');
144    };
145
146    //
147    // PRIVATE FUNCTIONS
148    //
149
150    /**
151     * This function loads the tab elements and stores them in an array
152     * @returns {Array} Array of tab elements
153     */
154    ResponsiveTabs.prototype._loadElements = function() {
155        var _this = this;
156        var $ul = this.$element.children('ul:first');
157        var tabs = [];
158        var id = 0;
159
160        // Add the classes to the basic html elements
161        this.$element.addClass(_this.options.classes.container); // Tab container
162        $ul.addClass(_this.options.classes.ul); // List container
163
164        // Get tab buttons and store their data in an array
165        $('li', $ul).each(function() {
166            var $tab = $(this);
167            var isExcluded = $tab.hasClass(_this.options.classes.stateExcluded);
168            var $anchor, $panel, $accordionTab, $accordionAnchor, panelSelector;
169
170            // Check if the tab should be excluded
171            if(!isExcluded) {
172
173                $anchor = $('a', $tab);
174                panelSelector = $anchor.attr('href');
175                $panel = $(panelSelector);
176                $accordionTab = $(_this.options.accordionTabElement).insertBefore($panel);
177                $accordionAnchor = $('<a></a>').attr('href', panelSelector).html($anchor.html()).appendTo($accordionTab);
178
179                var oTab = {
180                    _ignoreHashChange: false,
181                    id: id,
182                    disabled: ($.inArray(id, _this.options.disabled) !== -1),
183                    tab: $(this),
184                    anchor: $('a', $tab),
185                    panel: $panel,
186                    selector: panelSelector,
187                    accordionTab: $accordionTab,
188                    accordionAnchor: $accordionAnchor,
189                    active: false
190                };
191
192                // 1up the ID
193                id++;
194                // Add to tab array
195                tabs.push(oTab);
196            }
197        });
198        return tabs;
199    };
200
201
202    /**
203     * This function adds classes to the tab elements based on the options
204     */
205    ResponsiveTabs.prototype._loadClasses = function() {
206        for (var i=0; i<this.tabs.length; i++) {
207            this.tabs[i].tab.addClass(this.options.classes.stateDefault).addClass(this.options.classes.tab);
208            this.tabs[i].anchor.addClass(this.options.classes.anchor);
209            this.tabs[i].panel.addClass(this.options.classes.stateDefault).addClass(this.options.classes.panel);
210            this.tabs[i].accordionTab.addClass(this.options.classes.accordionTitle);
211            this.tabs[i].accordionAnchor.addClass(this.options.classes.anchor);
212            if(this.tabs[i].disabled) {
213                this.tabs[i].tab.removeClass(this.options.classes.stateDefault).addClass(this.options.classes.stateDisabled);
214                this.tabs[i].accordionTab.removeClass(this.options.classes.stateDefault).addClass(this.options.classes.stateDisabled);
215           }
216        }
217    };
218
219    /**
220     * This function adds events to the tab elements
221     */
222    ResponsiveTabs.prototype._loadEvents = function() {
223        var _this = this;
224
225        // Define activate event on a tab element
226        var fActivate = function(e) {
227            var current = _this._getCurrentTab(); // Fetch current tab
228            var activatedTab = e.data.tab;
229
230            e.preventDefault();
231
232            // Trigger click event for whenever a tab is clicked/touched even if the tab is disabled
233            activatedTab.tab.trigger('tabs-click', activatedTab);
234
235            // Make sure this tab isn't disabled
236            if(!activatedTab.disabled) {
237
238                // Check if hash has to be set in the URL location
239                if(_this.options.setHash) {
240                    // Set the hash using the history api if available to tackle Chromes repaint bug on hash change
241                    if(history.pushState) {
242                        history.pushState(null, null, window.location.origin + window.location.pathname + window.location.search + activatedTab.selector);
243                    } else {
244                        // Otherwise fallback to the hash update for sites that don't support the history api
245                        window.location.hash = activatedTab.selector;
246                    }
247                }
248
249                e.data.tab._ignoreHashChange = true;
250
251                // Check if the activated tab isnt the current one or if its collapsible. If not, do nothing
252                if(current !== activatedTab || _this._isCollapisble()) {
253                    // The activated tab is either another tab of the current one. If it's the current tab it is collapsible
254                    // Either way, the current tab can be closed
255                    _this._closeTab(e, current);
256
257                    // Check if the activated tab isnt the current one or if it isnt collapsible
258                    if(current !== activatedTab || !_this._isCollapisble()) {
259                        _this._openTab(e, activatedTab, false, true);
260                    }
261                }
262            }
263        };
264
265        // Loop tabs
266        for (var i=0; i<this.tabs.length; i++) {
267            // Add activate function to the tab and accordion selection element
268            this.tabs[i].anchor.on(_this.options.event, {tab: _this.tabs[i]}, fActivate);
269            this.tabs[i].accordionAnchor.on(_this.options.event, {tab: _this.tabs[i]}, fActivate);
270        }
271    };
272
273    /**
274     * This function gets the tab that should be opened at start
275     * @returns {Object} Tab object
276     */
277    ResponsiveTabs.prototype._getStartTab = function() {
278        var tabRef = this._getTabRefBySelector(window.location.hash);
279        var startTab;
280
281        // Check if the page has a hash set that is linked to a tab
282        if(tabRef >= 0 && !this._getTab(tabRef).disabled) {
283            // If so, set the current tab to the linked tab
284            startTab = this._getTab(tabRef);
285        } else if(this.options.active > 0 && !this._getTab(this.options.active).disabled) {
286            startTab = this._getTab(this.options.active);
287        } else {
288            // If not, just get the first one
289            startTab = this._getTab(0);
290        }
291
292        return startTab;
293    };
294
295    /**
296     * This function sets the current state of the plugin
297     * @param {Event} e - The event that triggers the state change
298     */
299    ResponsiveTabs.prototype._setState = function(e) {
300        var $ul = $('ul:first', this.$element);
301        var oldState = this.state;
302        var startCollapsedIsState = (typeof this.options.startCollapsed === 'string');
303        var startTab;
304
305        // The state is based on the visibility of the tabs list
306        if($ul.is(':visible')){
307            // Tab list is visible, so the state is 'tabs'
308            this.state = 'tabs';
309        } else {
310            // Tab list is invisible, so the state is 'accordion'
311            this.state = 'accordion';
312        }
313
314        // If the new state is different from the old state
315        if(this.state !== oldState) {
316            // If so, the state activate trigger must be called
317            this.$element.trigger('tabs-activate-state', {oldState: oldState, newState: this.state});
318
319            // Check if the state switch should open a tab
320            if(oldState && startCollapsedIsState && this.options.startCollapsed !== this.state && this._getCurrentTab() === undefined) {
321                // Get initial tab
322                startTab = this._getStartTab(e);
323                // Open the initial tab
324                this._openTab(e, startTab); // Open first tab
325            }
326        }
327    };
328
329    /**
330     * This function opens a tab
331     * @param {Event} e - The event that triggers the tab opening
332     * @param {Object} oTab - The tab object that should be opened
333     * @param {Boolean} closeCurrent - Defines if the current tab should be closed
334     * @param {Boolean} stopRotation - Defines if the tab rotation loop should be stopped
335     */
336    ResponsiveTabs.prototype._openTab = function(e, oTab, closeCurrent, stopRotation) {
337        var _this = this;
338        var scrollOffset;
339
340        // Check if the current tab has to be closed
341        if(closeCurrent) {
342            this._closeTab(e, this._getCurrentTab());
343        }
344
345        // Check if the rotation has to be stopped when activated
346        if(stopRotation && this.rotateInterval > 0) {
347            this.stopRotation();
348        }
349
350        // Set this tab to active
351        oTab.active = true;
352        // Set active classes to the tab button and accordion tab button
353        oTab.tab.removeClass(_this.options.classes.stateDefault).addClass(_this.options.classes.stateActive);
354        oTab.accordionTab.removeClass(_this.options.classes.stateDefault).addClass(_this.options.classes.stateActive);
355
356        // Run panel transiton
357        _this._doTransition(oTab.panel, _this.options.animation, 'open', function() {
358            var scrollOnLoad = (e.type !== 'tabs-load' || _this.options.scrollToAccordionOnLoad);
359
360            // When finished, set active class to the panel
361            oTab.panel.removeClass(_this.options.classes.stateDefault).addClass(_this.options.classes.stateActive);
362
363            // And if enabled and state is accordion, scroll to the accordion tab
364            if(_this.getState() === 'accordion' && _this.options.scrollToAccordion && (!_this._isInView(oTab.accordionTab) || _this.options.animation !== 'default') && scrollOnLoad) {
365
366                // Add offset element's height to scroll position
367                scrollOffset = oTab.accordionTab.offset().top - _this.options.scrollToAccordionOffset;
368
369                // Check if the animation option is enabled, and if the duration isn't 0
370                if(_this.options.animation !== 'default' && _this.options.duration > 0) {
371                    // If so, set scrollTop with animate and use the 'animation' duration
372                    $('html, body').animate({
373                        scrollTop: scrollOffset
374                    }, _this.options.duration);
375                } else {
376                    //  If not, just set scrollTop
377                    $('html, body').scrollTop(scrollOffset);
378                }
379            }
380        });
381
382        this.$element.trigger('tabs-activate', oTab);
383    };
384
385    /**
386     * This function closes a tab
387     * @param {Event} e - The event that is triggered when a tab is closed
388     * @param {Object} oTab - The tab object that should be closed
389     */
390    ResponsiveTabs.prototype._closeTab = function(e, oTab) {
391        var _this = this;
392        var doQueueOnState = typeof _this.options.animationQueue === 'string';
393        var doQueue;
394
395        if(oTab !== undefined) {
396            if(doQueueOnState && _this.getState() === _this.options.animationQueue) {
397                doQueue = true;
398            } else if(doQueueOnState) {
399                doQueue = false;
400            } else {
401                doQueue = _this.options.animationQueue;
402            }
403
404            // Deactivate tab
405            oTab.active = false;
406            // Set default class to the tab button
407            oTab.tab.removeClass(_this.options.classes.stateActive).addClass(_this.options.classes.stateDefault);
408
409            // Run panel transition
410            _this._doTransition(oTab.panel, _this.options.animation, 'close', function() {
411                // Set default class to the accordion tab button and tab panel
412                oTab.accordionTab.removeClass(_this.options.classes.stateActive).addClass(_this.options.classes.stateDefault);
413                oTab.panel.removeClass(_this.options.classes.stateActive).addClass(_this.options.classes.stateDefault);
414            }, !doQueue);
415
416            this.$element.trigger('tabs-deactivate', oTab);
417        }
418    };
419
420    /**
421     * This function runs an effect on a panel
422     * @param {Element} panel - The HTML element of the tab panel
423     * @param {String} method - The transition method reference
424     * @param {String} state - The state (open/closed) that the panel should transition to
425     * @param {Function} callback - The callback function that is called after the transition
426     * @param {Boolean} dequeue - Defines if the event queue should be dequeued after the transition
427     */
428    ResponsiveTabs.prototype._doTransition = function(panel, method, state, callback, dequeue) {
429        var effect;
430        var _this = this;
431
432        // Get effect based on method
433        switch(method) {
434            case 'slide':
435                effect = (state === 'open') ? 'slideDown' : 'slideUp';
436                break;
437            case 'fade':
438                effect = (state === 'open') ? 'fadeIn' : 'fadeOut';
439                break;
440            default:
441                effect = (state === 'open') ? 'show' : 'hide';
442                // When default is used, set the duration to 0
443                _this.options.duration = 0;
444                break;
445        }
446
447        // Add the transition to a custom queue
448        this.$queue.queue('responsive-tabs',function(next){
449            // Run the transition on the panel
450            panel[effect]({
451                duration: _this.options.duration,
452                complete: function() {
453                    // Call the callback function
454                    callback.call(panel, method, state);
455                    // Run the next function in the queue
456                    next();
457                }
458            });
459        });
460
461        // When the panel is openend, dequeue everything so the animation starts
462        if(state === 'open' || dequeue) {
463            this.$queue.dequeue('responsive-tabs');
464        }
465
466    };
467
468    /**
469     * This function returns the collapsibility of the tab in this state
470     * @returns {Boolean} The collapsibility of the tab
471     */
472    ResponsiveTabs.prototype._isCollapisble = function() {
473        return (typeof this.options.collapsible === 'boolean' && this.options.collapsible) || (typeof this.options.collapsible === 'string' && this.options.collapsible === this.getState());
474    };
475
476    /**
477     * This function returns a tab by numeric reference
478     * @param {Integer} numRef - Numeric tab reference
479     * @returns {Object} Tab object
480     */
481    ResponsiveTabs.prototype._getTab = function(numRef) {
482        return this.tabs[numRef];
483    };
484
485    /**
486     * This function returns the numeric tab reference based on a hash selector
487     * @param {String} selector - Hash selector
488     * @returns {Integer} Numeric tab reference
489     */
490    ResponsiveTabs.prototype._getTabRefBySelector = function(selector) {
491        // Loop all tabs
492        for (var i=0; i<this.tabs.length; i++) {
493            // Check if the hash selector is equal to the tab selector
494            if(this.tabs[i].selector === selector) {
495                return i;
496            }
497        }
498        // If none is found return a negative index
499        return -1;
500    };
501
502    /**
503     * This function returns the current tab element
504     * @returns {Object} Current tab element
505     */
506    ResponsiveTabs.prototype._getCurrentTab = function() {
507        return this._getTab(this._getCurrentTabRef());
508    };
509
510    /**
511     * This function returns the next tab's numeric reference
512     * @param {Integer} currentTabRef - Current numeric tab reference
513     * @returns {Integer} Numeric tab reference
514     */
515    ResponsiveTabs.prototype._getNextTabRef = function(currentTabRef) {
516        var tabRef = (currentTabRef || this._getCurrentTabRef());
517        var nextTabRef = (tabRef === this.tabs.length - 1) ? 0 : tabRef + 1;
518        return (this._getTab(nextTabRef).disabled) ? this._getNextTabRef(nextTabRef) : nextTabRef;
519    };
520
521    /**
522     * This function returns the previous tab's numeric reference
523     * @returns {Integer} Numeric tab reference
524     */
525    ResponsiveTabs.prototype._getPreviousTabRef = function() {
526        return (this._getCurrentTabRef() === 0) ? this.tabs.length - 1 : this._getCurrentTabRef() - 1;
527    };
528
529    /**
530     * This function returns the current tab's numeric reference
531     * @returns {Integer} Numeric tab reference
532     */
533    ResponsiveTabs.prototype._getCurrentTabRef = function() {
534        // Loop all tabs
535        for (var i=0; i<this.tabs.length; i++) {
536            // If this tab is active, return it
537            if(this.tabs[i].active) {
538                return i;
539            }
540        }
541        // No tabs have been found, return negative index
542        return -1;
543    };
544
545    /**
546     * This function gets the tallest tab and applied the height to all tabs
547     */
548    ResponsiveTabs.prototype._equaliseHeights = function() {
549        var maxHeight = 0;
550
551        $.each($.map(this.tabs, function(tab) {
552            maxHeight = Math.max(maxHeight, tab.panel.css('minHeight', '').height());
553            return tab.panel;
554        }), function() {
555            this.css('minHeight', maxHeight);
556        });
557    };
558
559    //
560    // HELPER FUNCTIONS
561    //
562
563    ResponsiveTabs.prototype._isInView = function($element) {
564        var docViewTop = $(window).scrollTop(),
565            docViewBottom = docViewTop + $(window).height(),
566            elemTop = $element.offset().top,
567            elemBottom = elemTop + $element.height();
568        return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
569    };
570
571    //
572    // PUBLIC FUNCTIONS
573    //
574
575    /**
576     * This function activates a tab
577     * @param {Integer} tabRef - Numeric tab reference
578     * @param {Boolean} stopRotation - Defines if the tab rotation should stop after activation
579     */
580    ResponsiveTabs.prototype.activate = function(tabRef, stopRotation) {
581        var e = jQuery.Event('tabs-activate');
582        var oTab = this._getTab(tabRef);
583        if(!oTab.disabled) {
584            this._openTab(e, oTab, true, stopRotation || true);
585        }
586    };
587
588    /**
589     * This function deactivates a tab
590     * @param {Integer} tabRef - Numeric tab reference
591     */
592    ResponsiveTabs.prototype.deactivate = function(tabRef) {
593        var e = jQuery.Event('tabs-dectivate');
594        var oTab = this._getTab(tabRef);
595        if(!oTab.disabled) {
596            this._closeTab(e, oTab);
597        }
598    };
599
600    /**
601     * This function enables a tab
602     * @param {Integer} tabRef - Numeric tab reference
603     */
604    ResponsiveTabs.prototype.enable = function(tabRef) {
605        var oTab = this._getTab(tabRef);
606        if(oTab){
607            oTab.disabled = false;
608            oTab.tab.addClass(this.options.classes.stateDefault).removeClass(this.options.classes.stateDisabled);
609            oTab.accordionTab.addClass(this.options.classes.stateDefault).removeClass(this.options.classes.stateDisabled);
610        }
611    };
612
613    /**
614     * This function disable a tab
615     * @param {Integer} tabRef - Numeric tab reference
616     */
617    ResponsiveTabs.prototype.disable = function(tabRef) {
618        var oTab = this._getTab(tabRef);
619        if(oTab){
620            oTab.disabled = true;
621            oTab.tab.removeClass(this.options.classes.stateDefault).addClass(this.options.classes.stateDisabled);
622            oTab.accordionTab.removeClass(this.options.classes.stateDefault).addClass(this.options.classes.stateDisabled);
623        }
624    };
625
626    /**
627     * This function gets the current state of the plugin
628     * @returns {String} State of the plugin
629     */
630    ResponsiveTabs.prototype.getState = function() {
631        return this.state;
632    };
633
634    /**
635     * This function starts the rotation of the tabs
636     * @param {Integer} speed - The speed of the rotation
637     */
638    ResponsiveTabs.prototype.startRotation = function(speed) {
639        var _this = this;
640        // Make sure not all tabs are disabled
641        if(this.tabs.length > this.options.disabled.length) {
642            this.rotateInterval = setInterval(function(){
643                var e = jQuery.Event('rotate');
644                _this._openTab(e, _this._getTab(_this._getNextTabRef()), true);
645            }, speed || (($.isNumeric(_this.options.rotate)) ? _this.options.rotate : 4000) );
646        } else {
647            throw new Error("Rotation is not possible if all tabs are disabled");
648        }
649    };
650
651    /**
652     * This function stops the rotation of the tabs
653     */
654    ResponsiveTabs.prototype.stopRotation = function() {
655        window.clearInterval(this.rotateInterval);
656        this.rotateInterval = 0;
657    };
658
659    /**
660     * This function can be used to get/set options
661     * @return {any} Option value
662     */
663    ResponsiveTabs.prototype.option = function(key, value) {
664        if(value) {
665            this.options[key] = value;
666        }
667        return this.options[key];
668    };
669
670    /** jQuery wrapper */
671    $.fn.responsiveTabs = function ( options ) {
672        var args = arguments;
673        var instance;
674
675        if (options === undefined || typeof options === 'object') {
676            return this.each(function () {
677                if (!$.data(this, 'responsivetabs')) {
678                    $.data(this, 'responsivetabs', new ResponsiveTabs( this, options ));
679                }
680            });
681        } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
682            instance = $.data(this[0], 'responsivetabs');
683
684            // Allow instances to be destroyed via the 'destroy' method
685            if (options === 'destroy') {
686                // TODO: destroy instance classes, etc
687                $.data(this, 'responsivetabs', null);
688            }
689
690            if (instance instanceof ResponsiveTabs && typeof instance[options] === 'function') {
691                return instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
692            } else {
693                return this;
694            }
695        }
696    };
697
698}(jQuery, window));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.