ui.tabs.paging.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * UI Tabs Paging extension - v1.2.2 (for jQuery 1.9.0 and jQuery UI 1.9.0)
  3. *
  4. * Copyright (c) 2013, 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. */
  29. (function($) {
  30. // overridden ui.tabs functions
  31. var uiTabsFuncs = {
  32. refresh: $.ui.tabs.prototype.refresh,
  33. option: $.ui.tabs.prototype.option
  34. };
  35. // DEPRECATED in jQuery UI 1.9
  36. if ( $.uiBackCompat !== false ) {
  37. uiTabsFuncs = $.extend(
  38. uiTabsFuncs,
  39. {
  40. add: $.ui.tabs.prototype.add,
  41. remove: $.ui.tabs.prototype.remove
  42. }
  43. );
  44. }
  45. $.extend($.ui.tabs.prototype, {
  46. paging: function(options) {
  47. var opts = {
  48. tabsPerPage: 0, // Max number of tabs to display at one time. 0 automatically sizing.
  49. nextButton: '»', // Text displayed for next button.
  50. prevButton: '«', // Text displayed for previous button.
  51. follow: false, // When clicking next button, automatically make first tab active. When clicking previous button automatically make last tab active.
  52. 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.
  53. activeOnAdd: false, // When new tab is added, make tab active automatically
  54. followOnActive: false // When tab is changed to active, automatically go move to that tab group.
  55. };
  56. opts = $.extend(opts, options);
  57. var self = this, initialized = false, currentPage,
  58. buttonWidth, containerWidth, allTabsWidth, tabWidths,
  59. maxPageWidth, pages, resizeTimer = null,
  60. windowHeight, windowWidth;
  61. // initialize paging
  62. function init() {
  63. destroy();
  64. windowHeight = $(window).height();
  65. windowWidth = $(window).width();
  66. allTabsWidth = 0, currentPage = 0, maxPageWidth = 0, buttonWidth = 0,
  67. pages = new Array(), tabWidths = new Array(), selectedTabWidths = new Array();
  68. containerWidth = self.element.width();
  69. // loops through LIs, get width of each tab when selected and unselected.
  70. var maxDiff = 0; // the max difference between a selected and unselected tab
  71. self.tabs.each(function(i) {
  72. if (i == self.options.active) {
  73. selectedTabWidths[i] = $(this).outerWidth(true);
  74. tabWidths[i] = self.tabs.eq(i).removeClass('ui-tabs-active').outerWidth(true);
  75. self.tabs.eq(i).addClass('ui-tabs-active');
  76. maxDiff = Math.min(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
  77. allTabsWidth += tabWidths[i];
  78. } else {
  79. tabWidths[i] = $(this).outerWidth(true);
  80. selectedTabWidths[i] = self.tabs.eq(i).addClass('ui-tabs-active').outerWidth(true);
  81. self.tabs.eq(i).removeClass('ui-tabs-active');
  82. maxDiff = Math.max(maxDiff, Math.abs(selectedTabWidths[i] - tabWidths[i]));
  83. allTabsWidth += tabWidths[i];
  84. }
  85. });
  86. // fix padding issues with buttons
  87. // TODO determine a better way to handle this
  88. allTabsWidth += maxDiff + 9;
  89. // if the width of all tables is greater than the container's width, calculate the pages
  90. if (allTabsWidth > containerWidth) {
  91. // create next button
  92. li = $('<li></li>')
  93. .addClass('ui-state-default ui-tabs-paging-next')
  94. .append($('<a href="#"></a>')
  95. .click(function() { page('next'); return false; })
  96. .html(opts.nextButton));
  97. self.tablist.append(li);
  98. buttonWidth = li.outerWidth(true);
  99. // create prev button
  100. li = $('<li></li>')
  101. .addClass('ui-state-default ui-tabs-paging-prev')
  102. .append($('<a href="#"></a>')
  103. .click(function() { page('prev'); return false; })
  104. .html(opts.prevButton));
  105. self.tablist.prepend(li);
  106. buttonWidth += li.outerWidth(true);
  107. // TODO determine fix for padding issues to next button
  108. buttonWidth += 19;
  109. var pageIndex = 0, pageWidth = 0, maxTabPadding = 0;
  110. // start calculating pageWidths
  111. for (var i = 0; i < tabWidths.length; i++) {
  112. // if first tab of page or selected tab's padding larger than the current max, set the maxTabPadding
  113. if (pageWidth == 0 || selectedTabWidths[i] - tabWidths[i] > maxTabPadding)
  114. maxTabPadding = (selectedTabWidths[i] - tabWidths[i]);
  115. // if first tab of page, initialize pages variable for page
  116. if (pages[pageIndex] == null) {
  117. pages[pageIndex] = { start: i };
  118. } else if ((i > 0 && (i % opts.tabsPerPage) == 0) || (tabWidths[i] + pageWidth + buttonWidth + 12) > containerWidth) {
  119. if ((pageWidth + maxTabPadding) > maxPageWidth)
  120. maxPageWidth = (pageWidth + maxTabPadding);
  121. pageIndex++;
  122. pages[pageIndex] = { start: i };
  123. pageWidth = 0;
  124. }
  125. pages[pageIndex].end = i+1;
  126. pageWidth += tabWidths[i];
  127. if (i == self.options.active) currentPage = pageIndex;
  128. }
  129. if ((pageWidth + maxTabPadding) > maxPageWidth)
  130. maxPageWidth = (pageWidth + maxTabPadding);
  131. // hide all tabs then show tabs for current page
  132. self.tabs.hide().slice(pages[currentPage].start, pages[currentPage].end).show();
  133. if (currentPage == (pages.length - 1) && !opts.cycle)
  134. disableButton('next');
  135. if (currentPage == 0 && !opts.cycle)
  136. disableButton('prev');
  137. // calculate the right padding for the next button
  138. buttonPadding = containerWidth - maxPageWidth - buttonWidth;
  139. if (buttonPadding > 0)
  140. $('.ui-tabs-paging-next', self.element).css({ paddingRight: buttonPadding + 'px' });
  141. } else {
  142. destroy();
  143. }
  144. $(window).bind('resize', handleResize);
  145. initialized = true;
  146. }
  147. // handles paging forward and backward
  148. function page(direction) {
  149. currentPage = currentPage + (direction == 'prev'?-1:1);
  150. if ((direction == 'prev' && currentPage < 0 && opts.cycle) ||
  151. (direction == 'next' && currentPage >= pages.length && !opts.cycle))
  152. currentPage = pages.length - 1;
  153. else if ((direction == 'prev' && currentPage < 0) ||
  154. (direction == 'next' && currentPage >= pages.length && opts.cycle))
  155. currentPage = 0;
  156. var start = pages[currentPage].start;
  157. var end = pages[currentPage].end;
  158. self.tabs.hide().slice(start, end).show();
  159. if (direction == 'prev') {
  160. enableButton('next');
  161. if (opts.follow && (self.options.active < start || self.options.active > (end-1))) self.option('active', end-1);
  162. if (!opts.cycle && start <= 0) disableButton('prev');
  163. } else {
  164. enableButton('prev');
  165. if (opts.follow && (self.options.active < start || self.options.active > (end-1))) self.option('active', start);
  166. if (!opts.cycle && end >= self.tabs.length) disableButton('next');
  167. }
  168. }
  169. // change styling of next/prev buttons when disabled
  170. function disableButton(direction) {
  171. $('.ui-tabs-paging-'+direction, self.element).addClass('ui-tabs-paging-disabled');
  172. }
  173. function enableButton(direction) {
  174. $('.ui-tabs-paging-'+direction, self.element).removeClass('ui-tabs-paging-disabled');
  175. }
  176. // special function defined to handle IE resize issues
  177. function handleResize() {
  178. if (resizeTimer) clearTimeout(resizeTimer);
  179. if (windowHeight != $(window).height() || windowWidth != $(window).width())
  180. {
  181. resizeTimer = setTimeout(init, 100);
  182. }
  183. }
  184. // remove all paging related changes and events
  185. function destroy() {
  186. // remove buttons
  187. $('.ui-tabs-paging-next', self.element).remove();
  188. $('.ui-tabs-paging-prev', self.element).remove();
  189. // show all tabs
  190. self.tabs.show();
  191. initialized = false;
  192. $(window).unbind('resize', handleResize);
  193. }
  194. // ------------- OVERRIDDEN PUBLIC FUNCTIONS -------------
  195. self.option = function(optionName, value) {
  196. var retVal = uiTabsFuncs.option.apply(this, [optionName, value]);
  197. // if "followOnActive" is true, then move page when selection changes
  198. if (optionName == "active")
  199. {
  200. // if paging is not initialized or it is not configured to
  201. // change pages when a new tab is active, then do nothing
  202. if (!initialized || !opts.followOnActive)
  203. return retVal;
  204. // find the new page based on index of the active tab
  205. for (var i in pages) {
  206. var start = pages[i].start;
  207. var end = pages[i].end;
  208. if (value >= start && value < end) {
  209. // if the the active tab is not within the currentPage of tabs, then change pages
  210. if (i != currentPage) {
  211. this.tabs.hide().slice(start, end).show();
  212. currentPage = parseInt(i);
  213. if (currentPage == 0) {
  214. enableButton('next');
  215. if (!opts.cycle && start <= 0) disableButton('prev');
  216. } else {
  217. enableButton('prev');
  218. if (!opts.cycle && end >= this.tabs.length) disableButton('next');
  219. }
  220. }
  221. break;
  222. }
  223. }
  224. }
  225. return retVal;
  226. }
  227. self.refresh = function() {
  228. if (initialized)
  229. {
  230. destroy();
  231. uiTabsFuncs.refresh.apply(this);
  232. // re-initialize paging buttons
  233. init();
  234. }
  235. uiTabsFuncs.refresh.apply(this);
  236. }
  237. // DEPRECATED in jQuery UI 1.9
  238. if ( $.uiBackCompat !== false )
  239. {
  240. // temporarily remove paging buttons before adding a tab
  241. self.add = function(url, label, index) {
  242. if (initialized)
  243. {
  244. destroy();
  245. uiTabsFuncs.add.apply(this, [url, label, index]);
  246. if (opts.activeOnAdd) {
  247. if (index == undefined) index = this.tabs.length-1;
  248. this.option('active', index);
  249. }
  250. // re-initialize paging buttons
  251. init();
  252. return this;
  253. }
  254. return uiTabsFuncs.add.apply(this, [url, label, index]);
  255. }
  256. // temporarily remove paging buttons before removing a tab
  257. self.remove = function(index) {
  258. if (initialized)
  259. {
  260. destroy();
  261. uiTabsFuncs.remove.apply(this, [index]);
  262. init();
  263. return this;
  264. }
  265. return uiTabsFuncs.remove.apply(this, [index]);
  266. }
  267. }
  268. // ------------- PUBLIC FUNCTIONS -------------
  269. $.extend($.ui.tabs.prototype, {
  270. // public function for removing paging
  271. pagingDestroy: function() {
  272. destroy();
  273. return this;
  274. },
  275. // public function to handle resizes that are not on the window
  276. pagingResize: function() {
  277. init();
  278. return this;
  279. }
  280. });
  281. // initialize on startup!
  282. init();
  283. }
  284. });
  285. })(jQuery);