blob: ef7dc3e64cc8db122ede860e4badee2ca970ea57 [file] [log] [blame]
Valentin Valchev9b5df5f2010-03-22 12:45:31 +00001/*
2Copyright (c) 2009, http://seyfertdesign.com/jquery/ui-tabs-paging.html
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23$.extend($.ui.tabs.prototype, {
24 paging: function(options) {
25 var opts = {
26 tabsPerPage: 0,
27 nextButton: '»',
28 prevButton: '«',
29 follow: false,
30 cycle: false,
31 selectOnAdd: false,
32 followOnSelect: false
33 };
34
35 opts = $.extend(opts, options);
36
37 var self = this, initialized = false, currentPage,
38 buttonWidth, containerWidth, allTabsWidth, tabWidths,
39 maxPageWidth, pages, resizeTimer = null,
40 windowHeight = $(window).height(), windowWidth = $(window).width();
41
42 function init() {
43 destroy();
44
45 allTabsWidth = 0, currentPage = 0, maxPageWidth = 0, buttonWidth = 0,
46 pages = new Array(), tabWidths = new Array(), selectedTabWidths = new Array();
47
48 containerWidth = self.element.width();
49
50 // loops through LIs, get width of each tab when selected and unselected.
51 var maxDiff = 0; // the max difference between a selected and unselected tab
52 self.lis.each(function(i) {
53 if (i == self.options.selected) {
54 selectedTabWidths[i] = $(this).outerWidth({ margin: true });
55 tabWidths[i] = self.lis.eq(i).removeClass('ui-tabs-selected').outerWidth({ margin: true });
56 self.lis.eq(i).addClass('ui-tabs-selected');
57 maxDiff = Math.min(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
58 allTabsWidth += tabWidths[i];
59 } else {
60 tabWidths[i] = $(this).outerWidth({ margin: true });
61 selectedTabWidths[i] = self.lis.eq(i).addClass('ui-tabs-selected').outerWidth({ margin: true });
62 self.lis.eq(i).removeClass('ui-tabs-selected');
63 maxDiff = Math.max(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
64 allTabsWidth += tabWidths[i];
65 }
66 });
67 // fix padding issues with buttons
68 // TODO determine a better way to handle this
69 allTabsWidth += maxDiff + ($.browser.msie?4:0) + 9;
70
71 // if the width of all tables is greater than the container's width, calculate the pages
72 if (allTabsWidth > containerWidth) {
73 // create next button
74 li = $('<li></li>')
75 .addClass('ui-state-default ui-tabs-paging-next')
76 .append($('<a href="#"></a>')
77 .click(function() { page('next'); return false; })
78 .html(opts.nextButton));
79
80 self.lis.eq(self.length()-1).after(li);
81 buttonWidth = li.outerWidth({ margin: true });
82
83 // create prev button
84 li = $('<li></li>')
85 .addClass('ui-state-default ui-tabs-paging-prev')
86 .append($('<a href="#"></a>')
87 .click(function() { page('prev'); return false; })
88 .html(opts.prevButton));
89 self.lis.eq(0).before(li);
90 buttonWidth += li.outerWidth({ margin: true });
91
92 // TODO determine fix for padding issues to next button
93 buttonWidth += 19;
94
95 var pageIndex = 0, pageWidth = 0, maxTabPadding = 0;
96
97 // start calculating pageWidths
98 for (var i = 0; i < tabWidths.length; i++) {
99 // if first tab of page or selected tab's padding larger than the current max, set the maxTabPadding
100 if (pageWidth == 0 || selectedTabWidths[i] - tabWidths[i] > maxTabPadding)
101 maxTabPadding = (selectedTabWidths[i] - tabWidths[i]);
102
103 // if first tab of page, initialize pages variable for page
104 if (pages[pageIndex] == null) {
105 pages[pageIndex] = { start: i };
106
107 } else if ((i > 0 && (i % opts.tabsPerPage) == 0) || (tabWidths[i] + pageWidth + buttonWidth + 12) > containerWidth) {
108 if ((pageWidth + maxTabPadding) > maxPageWidth)
109 maxPageWidth = (pageWidth + maxTabPadding);
110 pageIndex++;
111 pages[pageIndex] = { start: i };
112 pageWidth = 0;
113 }
114 pages[pageIndex].end = i+1;
115 pageWidth += tabWidths[i];
116 if (i == self.options.selected) currentPage = pageIndex;
117 }
118 if ((pageWidth + maxTabPadding) > maxPageWidth)
119 maxPageWidth = (pageWidth + maxTabPadding);
120
121 // hide all tabs then show tabs for current page
122 self.lis.hide().slice(pages[currentPage].start, pages[currentPage].end).show();
123 if (currentPage == (pages.length - 1) && !opts.cycle)
124 disableButton('next');
125 if (currentPage == 0 && !opts.cycle)
126 disableButton('prev');
127
128 // calculate the right padding for the next button
129 buttonPadding = containerWidth - maxPageWidth - buttonWidth;
130 if (buttonPadding > 0)
131 $('.ui-tabs-paging-next', self.element).css({ paddingRight: buttonPadding + 'px' });
132
133 initialized = true;
134 } else {
135 destroy();
136 }
137
138 $(window).bind('resize', handleResize);
139 }
140
141 function page(direction) {
142 currentPage = currentPage + (direction == 'prev'?-1:1);
143
144 if ((direction == 'prev' && currentPage < 0 && opts.cycle) ||
145 (direction == 'next' && currentPage >= pages.length && !opts.cycle))
146 currentPage = pages.length - 1;
147 else if ((direction == 'prev' && currentPage < 0) ||
148 (direction == 'next' && currentPage >= pages.length && opts.cycle))
149 currentPage = 0;
150
151 var start = pages[currentPage].start;
152 var end = pages[currentPage].end;
153 self.lis.hide().slice(start, end).show();
154
155 if (direction == 'prev') {
156 enableButton('next');
157 if (opts.follow && (self.options.selected < start || self.options.selected > (end-1))) self.select(end-1);
158 if (!opts.cycle && start <= 0) disableButton('prev');
159 } else {
160 enableButton('prev');
161 if (opts.follow && (self.options.selected < start || self.options.selected > (end-1))) self.select(start);
162 if (!opts.cycle && end >= self.length()) disableButton('next');
163 }
164 }
165
166 function disableButton(direction) {
167 $('.ui-tabs-paging-'+direction, self.element).addClass('ui-tabs-paging-disabled');
168 }
169
170 function enableButton(direction) {
171 $('.ui-tabs-paging-'+direction, self.element).removeClass('ui-tabs-paging-disabled');
172 }
173
174 // special function defined to handle IE6 and IE7 resize issues
175 function handleResize() {
176 if (resizeTimer) clearTimeout(resizeTimer);
177
178 if (windowHeight != $(window).height() || windowWidth != $(window).width())
179 resizeTimer = setTimeout(reinit, 100);
180 }
181
182 function reinit() {
183 windowHeight = $(window).height();
184 windowWidth = $(window).width();
185 init();
186 }
187
188 function destroy() {
189 // remove buttons
190 $('.ui-tabs-paging-next', self.element).remove();
191 $('.ui-tabs-paging-prev', self.element).remove();
192
193 // show all tabs
194 self.lis.show();
195
196 initialized = false;
197
198 $(window).unbind('resize', handleResize);
199 }
200
201 // reconfigure "ui.tabs" add/remove events to reinit paging
202 var tabsAdd = self.add;
203 self.add = function(url, label, index) {
204 // remove paging buttons before adding a tab
205 if (initialized)
206 destroy();
207
208 tabsAdd.apply(this, [url, label, index]);
209
210 if (opts.selectOnAdd) {
211 if (index == undefined) index = this.lis.length-1;
212 this.select(index);
213 }
214 // re-initialize paging buttons
215 init();
216 };
217 var tabsRemove = self.remove;
218 self.remove = function(index) {
219 // remove paging buttons before removing a tab
220 if (initialized)
221 destroy();
222
223 tabsRemove.apply(this, [index]);
224
225 // re-initialize paging buttons
226 init();
227 };
228 // reconfigure "ui.tabs" select event to change pages if new tab is selected
229 var tabsSelect = self.select;
230 self.select = function(index) {
231 tabsSelect.apply(this, [index]);
232
233 // if paging is not initialized or it is not configured to
234 // change pages when a new tab is selected, then do nothing
235 if (!initialized || !opts.followOnSelect)
236 return;
237
238 // find the new page based on index of the tab selected
239 for (i in pages) {
240 var start = pages[i].start;
241 var end = pages[i].end;
242 if (index >= start && index < end) {
243 // if the the tab selected is not within the currentPage of tabs, then change pages
244 if (i != currentPage) {
245 self.lis.hide().slice(start, end).show();
246
247 currentPage = parseInt(i);
248 if (currentPage == 0) {
249 enableButton('next');
250 if (!opts.cycle && start <= 0) disableButton('prev');
251 } else {
252 enableButton('prev');
253 if (!opts.cycle && end >= self.length()) disableButton('next');
254 }
255 }
256 break;
257 }
258 }
259 };
260
261 // add, remove, and destroy functions specific for paging
262 $.extend($.ui.tabs.prototype, {
263 pagingDestroy: function() {
264 destroy();
265 }
266 });
267
268 init();
269 }
270});