UNPKG

7.66 kBJavaScriptView Raw
1(function ($) {
2
3 var methods = {
4 init : function(options) {
5 var defaults = {
6 onShow: null,
7 swipeable: false,
8 responsiveThreshold: Infinity, // breakpoint for swipeable
9 };
10 options = $.extend(defaults, options);
11
12 return this.each(function() {
13
14 // For each set of tabs, we want to keep track of
15 // which tab is active and its associated content
16 var $this = $(this),
17 window_width = $(window).width();
18
19 var $active, $content, $links = $this.find('li.tab a'),
20 $tabs_width = $this.width(),
21 $tabs_content = $(),
22 $tabs_wrapper,
23 $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length,
24 $indicator,
25 index = prev_index = 0,
26 clicked = false,
27 clickedTimeout,
28 transition = 300;
29
30
31 // Finds right attribute for indicator based on active tab.
32 // el: jQuery Object
33 var calcRightPos = function(el) {
34 return $tabs_width - el.position().left - el.outerWidth() - $this.scrollLeft();
35 };
36
37 // Finds left attribute for indicator based on active tab.
38 // el: jQuery Object
39 var calcLeftPos = function(el) {
40 return el.position().left + $this.scrollLeft();
41 };
42
43 // Animates Indicator to active tab.
44 // prev_index: Number
45 var animateIndicator = function(prev_index) {
46 if ((index - prev_index) >= 0) {
47 $indicator.velocity({"right": calcRightPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
48 $indicator.velocity({"left": calcLeftPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
49
50 } else {
51 $indicator.velocity({"left": calcLeftPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
52 $indicator.velocity({"right": calcRightPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
53 }
54 };
55
56 // Change swipeable according to responsive threshold
57 if (options.swipeable) {
58 if (window_width > options.responsiveThreshold) {
59 options.swipeable = false;
60 }
61 }
62
63
64 // If the location.hash matches one of the links, use that as the active tab.
65 $active = $($links.filter('[href="'+location.hash+'"]'));
66
67 // If no match is found, use the first link or any with class 'active' as the initial active tab.
68 if ($active.length === 0) {
69 $active = $(this).find('li.tab a.active').first();
70 }
71 if ($active.length === 0) {
72 $active = $(this).find('li.tab a').first();
73 }
74
75 $active.addClass('active');
76 index = $links.index($active);
77 if (index < 0) {
78 index = 0;
79 }
80
81 if ($active[0] !== undefined) {
82 $content = $($active[0].hash);
83 $content.addClass('active');
84 }
85
86 // append indicator then set indicator width to tab width
87 if (!$this.find('.indicator').length) {
88 $this.append('<div class="indicator"></div>');
89 }
90 $indicator = $this.find('.indicator');
91
92 // we make sure that the indicator is at the end of the tabs
93 $this.append($indicator);
94
95 if ($this.is(":visible")) {
96 // $indicator.css({"right": $tabs_width - ((index + 1) * $tab_width)});
97 // $indicator.css({"left": index * $tab_width});
98 setTimeout(function() {
99 $indicator.css({"right": calcRightPos($active) });
100 $indicator.css({"left": calcLeftPos($active) });
101 }, 0);
102 }
103 $(window).resize(function () {
104 $tabs_width = $this.width();
105 $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
106 if (index < 0) {
107 index = 0;
108 }
109 if ($tab_width !== 0 && $tabs_width !== 0) {
110 $indicator.css({"right": calcRightPos($active) });
111 $indicator.css({"left": calcLeftPos($active) });
112 }
113 });
114
115 // Initialize Tabs Content.
116 if (options.swipeable) {
117 // TODO: Duplicate calls with swipeable? handle multiple div wrapping.
118 $links.each(function () {
119 var $curr_content = $(Materialize.escapeHash(this.hash));
120 $curr_content.addClass('carousel-item');
121 $tabs_content = $tabs_content.add($curr_content);
122 });
123 $tabs_wrapper = $tabs_content.wrapAll('<div class="tabs-content carousel"></div>');
124 $tabs_content.css('display', '');
125 $('.tabs-content.carousel').carousel({
126 fullWidth: true,
127 noWrap: true,
128 onCycleTo: function(item) {
129 if (!clicked) {
130 var prev_index = index;
131 index = $tabs_wrapper.index(item);
132 $active = $links.eq(index);
133 animateIndicator(prev_index);
134 }
135 },
136 });
137 } else {
138 // Hide the remaining content
139 $links.not($active).each(function () {
140 $(Materialize.escapeHash(this.hash)).hide();
141 });
142 }
143
144
145 // Bind the click event handler
146 $this.on('click', 'a', function(e) {
147 if ($(this).parent().hasClass('disabled')) {
148 e.preventDefault();
149 return;
150 }
151
152 // Act as regular link if target attribute is specified.
153 if (!!$(this).attr("target")) {
154 return;
155 }
156
157 clicked = true;
158 $tabs_width = $this.width();
159 $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
160
161 // Make the old tab inactive.
162 $active.removeClass('active');
163 var $oldContent = $content
164
165 // Update the variables with the new link and content
166 $active = $(this);
167 $content = $(Materialize.escapeHash(this.hash));
168 $links = $this.find('li.tab a');
169 var activeRect = $active.position();
170
171 // Make the tab active.
172 $active.addClass('active');
173 prev_index = index;
174 index = $links.index($(this));
175 if (index < 0) {
176 index = 0;
177 }
178 // Change url to current tab
179 // window.location.hash = $active.attr('href');
180
181 // Swap content
182 if (options.swipeable) {
183 if ($tabs_content.length) {
184 $tabs_content.carousel('set', index);
185 }
186 } else {
187 if ($content !== undefined) {
188 $content.show();
189 $content.addClass('active');
190 if (typeof(options.onShow) === "function") {
191 options.onShow.call(this, $content);
192 }
193 }
194
195 if ($oldContent !== undefined &&
196 !$oldContent.is($content)) {
197 $oldContent.hide();
198 $oldContent.removeClass('active');
199 }
200 }
201
202 // Reset clicked state
203 clickedTimeout = setTimeout(function(){ clicked = false; }, transition);
204
205 // Update indicator
206 animateIndicator(prev_index);
207
208 // Prevent the anchor's default click action
209 e.preventDefault();
210 });
211 });
212
213 },
214 select_tab : function( id ) {
215 this.find('a[href="#' + id + '"]').trigger('click');
216 }
217 };
218
219 $.fn.tabs = function(methodOrOptions) {
220 if ( methods[methodOrOptions] ) {
221 return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
222 } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
223 // Default to "init"
224 return methods.init.apply( this, arguments );
225 } else {
226 $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tabs' );
227 }
228 };
229
230 $(document).ready(function(){
231 $('ul.tabs').tabs();
232 });
233}( jQuery ));