blob: e838eb51d599a0cc4bd3fa5500fad55153a958c0 [file] [log] [blame]
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +00001/*
Felix Meschberger7944e282012-12-04 08:24:13 +00002 * UI Tabs Paging extension - v1.2 (Stable - jQuery 1.8.2 and jQuery UI 1.9.0)
3 *
4 * Copyright (c) 2012, http://seyfertdesign.com/jquery/ui-tabs-paging.html
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * Depends:
25 * jquery.ui.core.js
26 * jquery.ui.widget.js
27 * jquery.ui.tabs.js
28 */
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000029
Felix Meschberger7944e282012-12-04 08:24:13 +000030(function($) {
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000031
Felix Meschberger7944e282012-12-04 08:24:13 +000032// overridden ui.tabs functions
33var uiTabsFuncs = {
34 refresh: $.ui.tabs.prototype.refresh,
35 option: $.ui.tabs.prototype.option
36};
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000037
Felix Meschberger7944e282012-12-04 08:24:13 +000038// DEPRECATED in jQuery UI 1.9
39if ( $.uiBackCompat !== false ) {
40 uiTabsFuncs = $.extend(
41 uiTabsFuncs,
42 {
43 add: $.ui.tabs.prototype.add,
44 remove: $.ui.tabs.prototype.remove
45 }
46 );
47}
48
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000049$.extend($.ui.tabs.prototype, {
Felix Meschberger7944e282012-12-04 08:24:13 +000050 paging: function(options) {
51 var opts = {
52 tabsPerPage: 0, // Max number of tabs to display at one time. 0 automatically sizing.
53 nextButton: '»', // Text displayed for next button.
54 prevButton: '«', // Text displayed for previous button.
55 follow: false, // When clicking next button, automatically make first tab active. When clicking previous button automatically make last tab active.
56 cycle: false, // When at end of list, next button returns to first page. When at beginning of list previous button goes to end of list.
57 activeOnAdd: false, // When new tab is added, make tab active automatically
58 followOnActive: false // When tab is changed to active, automatically go move to that tab group.
59 };
60
61 opts = $.extend(opts, options);
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000062
Felix Meschberger7944e282012-12-04 08:24:13 +000063 var self = this, initialized = false, currentPage,
64 buttonWidth, containerWidth, allTabsWidth, tabWidths,
65 maxPageWidth, pages, resizeTimer = null,
66 windowHeight, windowWidth;
67
68 // initialize paging
69 function init() {
70 destroy();
71
72 windowHeight = $(window).height();
73 windowWidth = $(window).width();
74
75 allTabsWidth = 0, currentPage = 0, maxPageWidth = 0, buttonWidth = 0,
76 pages = new Array(), tabWidths = new Array(), selectedTabWidths = new Array();
77
78 containerWidth = self.element.width();
79
80 // loops through LIs, get width of each tab when selected and unselected.
81 var maxDiff = 0; // the max difference between a selected and unselected tab
82 self.tabs.each(function(i) {
83 if (i == self.options.active) {
84 selectedTabWidths[i] = $(this).outerWidth(true);
85 tabWidths[i] = self.tabs.eq(i).removeClass('ui-tabs-active').outerWidth(true);
86 self.tabs.eq(i).addClass('ui-tabs-active');
87 maxDiff = Math.min(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
88 allTabsWidth += tabWidths[i];
89 } else {
90 tabWidths[i] = $(this).outerWidth(true);
91 selectedTabWidths[i] = self.tabs.eq(i).addClass('ui-tabs-active').outerWidth(true);
92 self.tabs.eq(i).removeClass('ui-tabs-active');
93 maxDiff = Math.max(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
94 allTabsWidth += tabWidths[i];
95 }
96 });
97
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +000098 // fix padding issues with buttons
99 // TODO determine a better way to handle this
Felix Meschberger7944e282012-12-04 08:24:13 +0000100 allTabsWidth += maxDiff + ($.browser.msie?4:0) + 9;
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +0000101
Felix Meschberger7944e282012-12-04 08:24:13 +0000102 // if the width of all tables is greater than the container's width, calculate the pages
103 if (allTabsWidth > containerWidth) {
104 // create next button
105 li = $('<li></li>')
106 .addClass('ui-state-default ui-tabs-paging-next')
107 .append($('<a href="#"></a>')
108 .click(function() { page('next'); return false; })
109 .html(opts.nextButton));
110
111 self.tablist.append(li);
112 buttonWidth = li.outerWidth(true);
113
114 // create prev button
115 li = $('<li></li>')
116 .addClass('ui-state-default ui-tabs-paging-prev')
117 .append($('<a href="#"></a>')
118 .click(function() { page('prev'); return false; })
119 .html(opts.prevButton));
120 self.tablist.prepend(li);
121 buttonWidth += li.outerWidth(true);
122
123 // TODO determine fix for padding issues to next button
124 buttonWidth += 19;
125
126 var pageIndex = 0, pageWidth = 0, maxTabPadding = 0;
127
128 // start calculating pageWidths
129 for (var i = 0; i < tabWidths.length; i++) {
130 // if first tab of page or selected tab's padding larger than the current max, set the maxTabPadding
131 if (pageWidth == 0 || selectedTabWidths[i] - tabWidths[i] > maxTabPadding)
132 maxTabPadding = (selectedTabWidths[i] - tabWidths[i]);
133
134 // if first tab of page, initialize pages variable for page
135 if (pages[pageIndex] == null) {
136 pages[pageIndex] = { start: i };
137
138 } else if ((i > 0 && (i % opts.tabsPerPage) == 0) || (tabWidths[i] + pageWidth + buttonWidth + 12) > containerWidth) {
139 if ((pageWidth + maxTabPadding) > maxPageWidth)
140 maxPageWidth = (pageWidth + maxTabPadding);
141 pageIndex++;
142 pages[pageIndex] = { start: i };
143 pageWidth = 0;
144 }
145 pages[pageIndex].end = i+1;
146 pageWidth += tabWidths[i];
147 if (i == self.options.active) currentPage = pageIndex;
148 }
149 if ((pageWidth + maxTabPadding) > maxPageWidth)
150 maxPageWidth = (pageWidth + maxTabPadding);
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +0000151
Felix Meschberger7944e282012-12-04 08:24:13 +0000152 // hide all tabs then show tabs for current page
153 self.tabs.hide().slice(pages[currentPage].start, pages[currentPage].end).show();
154 if (currentPage == (pages.length - 1) && !opts.cycle)
155 disableButton('next');
156 if (currentPage == 0 && !opts.cycle)
157 disableButton('prev');
158
159 // calculate the right padding for the next button
160 buttonPadding = containerWidth - maxPageWidth - buttonWidth;
161 if (buttonPadding > 0)
162 $('.ui-tabs-paging-next', self.element).css({ paddingRight: buttonPadding + 'px' });
163 } else {
164 destroy();
165 }
166
167 $(window).bind('resize', handleResize);
168
169 initialized = true;
170 }
171
172 // handles paging forward and backward
173 function page(direction) {
174 currentPage = currentPage + (direction == 'prev'?-1:1);
175
176 if ((direction == 'prev' && currentPage < 0 && opts.cycle) ||
177 (direction == 'next' && currentPage >= pages.length && !opts.cycle))
178 currentPage = pages.length - 1;
179 else if ((direction == 'prev' && currentPage < 0) ||
180 (direction == 'next' && currentPage >= pages.length && opts.cycle))
181 currentPage = 0;
182
183 var start = pages[currentPage].start;
184 var end = pages[currentPage].end;
185 self.tabs.hide().slice(start, end).show();
186
187 if (direction == 'prev') {
188 enableButton('next');
189 if (opts.follow && (self.options.active < start || self.options.active > (end-1))) self.option('active', end-1);
190 if (!opts.cycle && start <= 0) disableButton('prev');
191 } else {
192 enableButton('prev');
193 if (opts.follow && (self.options.active < start || self.options.active > (end-1))) self.option('active', start);
194 if (!opts.cycle && end >= self.tabs.length) disableButton('next');
195 }
196 }
197
198 // change styling of next/prev buttons when disabled
199 function disableButton(direction) {
200 $('.ui-tabs-paging-'+direction, self.element).addClass('ui-tabs-paging-disabled');
201 }
202
203 function enableButton(direction) {
204 $('.ui-tabs-paging-'+direction, self.element).removeClass('ui-tabs-paging-disabled');
205 }
206
207 // special function defined to handle IE resize issues
208 function handleResize() {
209 if (resizeTimer) clearTimeout(resizeTimer);
210
211 if (windowHeight != $(window).height() || windowWidth != $(window).width())
212 {
213 resizeTimer = setTimeout(init, 100);
214 }
215 }
216
217 // remove all paging related changes and events
218 function destroy() {
219 // remove buttons
220 $('.ui-tabs-paging-next', self.element).remove();
221 $('.ui-tabs-paging-prev', self.element).remove();
222
223 // show all tabs
224 self.tabs.show();
225
226 initialized = false;
227
228 $(window).unbind('resize', handleResize);
229 }
230
231
232
233 // ------------- OVERRIDDEN PUBLIC FUNCTIONS -------------
234 self.option = function(optionName, value) {
235 uiTabsFuncs.option.apply(this, [optionName, value]);
236
237 // if "followOnActive" is true, then move page when selection changes
238 if (optionName == "active")
239 {
240 // if paging is not initialized or it is not configured to
241 // change pages when a new tab is active, then do nothing
242 if (!initialized || !opts.followOnActive)
243 return this;
244
245 // find the new page based on index of the active tab
246 for (var i in pages) {
247 var start = pages[i].start;
248 var end = pages[i].end;
249 if (value >= start && value < end) {
250 // if the the active tab is not within the currentPage of tabs, then change pages
251 if (i != currentPage) {
252 this.tabs.hide().slice(start, end).show();
253
254 currentPage = parseInt(i);
255 if (currentPage == 0) {
256 enableButton('next');
257 if (!opts.cycle && start <= 0) disableButton('prev');
258 } else {
259 enableButton('prev');
260 if (!opts.cycle && end >= this.tabs.length) disableButton('next');
261 }
262 }
263 break;
264 }
265 }
266 }
267
268 return this;
269 }
270
271 self.refresh = function() {
272 if (initialized)
273 {
274 destroy();
275
276 uiTabsFuncs.refresh.apply(this);
277
278 // re-initialize paging buttons
279 init();
280 }
281
282 uiTabsFuncs.refresh.apply(this);
283 }
284
285
286 // DEPRECATED in jQuery UI 1.9
287 if ( $.uiBackCompat !== false )
288 {
289 // temporarily remove paging buttons before adding a tab
290 self.add = function(url, label, index) {
291 if (initialized)
292 {
293 destroy();
294
295 uiTabsFuncs.add.apply(this, [url, label, index]);
296
297 if (opts.activeOnAdd) {
298 if (index == undefined) index = this.tabs.length-1;
299 this.option('active', index);
300 }
301 // re-initialize paging buttons
302 init();
303
304 return this;
305 }
306
307 return uiTabsFuncs.add.apply(this, [url, label, index]);
308 }
309
310 // temporarily remove paging buttons before removing a tab
311 self.remove = function(index) {
312 if (initialized)
313 {
314 destroy();
315 uiTabsFuncs.remove.apply(this, [index]);
316 init();
317
318 return this;
319 }
320
321 return uiTabsFuncs.remove.apply(this, [index]);
322 }
323 }
324
325
326 // ------------- PUBLIC FUNCTIONS -------------
327 $.extend($.ui.tabs.prototype, {
328 // public function for removing paging
329 pagingDestroy: function() {
330 destroy();
331 return this;
332 },
333
334 // public function to handle resizes that are not on the window
335 pagingResize: function() {
336 init();
337 return this;
338 }
339 });
340
341 // initialize on startup!
342 init();
343 }
Valentin Pavlov Valchevdd973af2010-03-22 12:45:31 +0000344});
Felix Meschberger7944e282012-12-04 08:24:13 +0000345
346
347})(jQuery);