paginathing.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /**
  2. * Paginathing
  3. * Paginate Everything
  4. *
  5. * Original author Alfred Crosby <https://github.com/alfredcrosby>
  6. * Inspired from http://esimakin.github.io/twbs-pagination/
  7. * Modified to pure JavaScript and specialised to LibreOffice Help by
  8. * Ilmari Lauhakangas
  9. *
  10. * MIT License (Expat)
  11. *
  12. * Copyright (c) 2018 Alfred Crosby
  13. *
  14. * Permission is hereby granted, free of charge, to any person obtaining a copy
  15. * of this software and associated documentation files (the "Software"), to deal
  16. * in the Software without restriction, including without limitation the rights
  17. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18. * copies of the Software, and to permit persons to whom the Software is
  19. * furnished to do so, subject to the following conditions:
  20. *
  21. * The above copyright notice and this permission notice shall be included in all
  22. * copies or substantial portions of the Software.
  23. *
  24. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30. * SOFTWARE.
  31. */
  32. var options = {
  33. perPage: 20,
  34. limitPagination: 6,
  35. prevNext: true,
  36. firstLast: true,
  37. prevText: '←',
  38. nextText: '→',
  39. firstText: '⇤',
  40. lastText: '⇥',
  41. containerClass: 'pagination-container',
  42. ulClass: 'pagination',
  43. liClass: 'page',
  44. activeClass: 'active',
  45. disabledClass: 'disabled'
  46. };
  47. var Paginator = function(element) {
  48. var el = element;
  49. var startPage = 1;
  50. var currentPage = 1;
  51. var pageDivision = 0;
  52. var totalItems = el.children.length;
  53. var limitPagination = options.limitPagination;
  54. pageDivision = Math.ceil(totalItems / options.perPage);
  55. // let's not display pagination leading nowhere
  56. function pagLimit() {
  57. if (options.limitPagination >= pageDivision) {
  58. limitPagination = pageDivision;
  59. } else {
  60. limitPagination = options.limitPagination;
  61. }
  62. return limitPagination;
  63. }
  64. var totalPages = Math.max(pageDivision, pagLimit());
  65. var existingContainer = document.getElementsByClassName('pagination-container')[0];
  66. if (existingContainer) {
  67. parent = existingContainer.parentNode;
  68. parent.removeChild(existingContainer);
  69. }
  70. var container = document.createElement('nav');
  71. container.setAttribute('class', options.containerClass);
  72. var ul = document.createElement('ul');
  73. ul.setAttribute('class', options.ulClass);
  74. function paginationFunc(type, page) {
  75. var li = document.createElement('li');
  76. var a = document.createElement('a');
  77. a.setAttribute('href', '#');
  78. var cssClass = type === 'number' ? options.liClass : type;
  79. var text = document.createTextNode(type === 'number' ? page : paginationText(type));
  80. li.classList.add(cssClass);
  81. li.setAttribute('data-pagination-type', type);
  82. li.setAttribute('data-page', page);
  83. a.appendChild(text);
  84. li.appendChild(a);
  85. return li;
  86. }
  87. function paginationText(type) {
  88. return options[type + 'Text'];
  89. }
  90. function buildPagination() {
  91. var pagination = [];
  92. var prev = currentPage - 1 < startPage ? startPage : currentPage - 1;
  93. var next = currentPage + 1 > totalPages ? totalPages : currentPage + 1;
  94. var start = 0;
  95. var end = 0;
  96. var limit = limitPagination;
  97. if (limit) {
  98. if (currentPage <= Math.ceil(limit / 2) + 1) {
  99. start = 1;
  100. end = limit;
  101. } else if (currentPage + Math.floor(limit / 2) >= totalPages) {
  102. start = totalPages + 1 - limit;
  103. end = totalPages;
  104. } else {
  105. start = currentPage - Math.ceil(limit / 2);
  106. end = currentPage + Math.floor(limit / 2);
  107. }
  108. } else {
  109. start = startPage;
  110. end = totalPages;
  111. }
  112. // "First" button
  113. if (options.firstLast) {
  114. pagination.push(paginationFunc('first', startPage));
  115. }
  116. // "Prev" button
  117. if (options.prevNext) {
  118. pagination.push(paginationFunc('prev', prev));
  119. }
  120. // Pagination
  121. for (var i = start; i <= end; i++) {
  122. pagination.push(paginationFunc('number', i));
  123. }
  124. // "Next" button
  125. if (options.prevNext) {
  126. pagination.push(paginationFunc('next', next));
  127. }
  128. // "Last" button
  129. if (options.firstLast) {
  130. pagination.push(paginationFunc('last', totalPages));
  131. }
  132. return pagination;
  133. }
  134. function render(page) {
  135. // Remove children before re-render (prevent duplicate)
  136. while (ul.hasChildNodes()) {
  137. ul.removeChild(ul.lastChild);
  138. }
  139. var paginationBuild = buildPagination();
  140. paginationBuild.forEach(function(item) {
  141. ul.appendChild(item);
  142. });
  143. // Manage active DOM
  144. var startAt = page === 1 ? 0 : (page - 1) * options.perPage;
  145. var endAt = page * options.perPage;
  146. var domLi = el.children;
  147. for (var i = 0, len = domLi.length; i < len; i++) {
  148. var item = domLi[i];
  149. if (i >= startAt && i <= endAt) {
  150. item.classList.remove('hidden');
  151. } else {
  152. item.classList.add('hidden');
  153. }
  154. }
  155. // Manage active state
  156. var ulKids = ul.getElementsByTagName("li");
  157. for (var i = 0, len = ulKids.length; i < len; i++) {
  158. var _li = ulKids[i];
  159. var type = _li.getAttribute('data-pagination-type');
  160. switch (type) {
  161. case 'number':
  162. if (parseInt(_li.getAttribute('data-page'), 10) === page) {
  163. _li.classList.add(options.activeClass);
  164. }
  165. break;
  166. case 'first':
  167. page === startPage && _li.classList.toggle(options.disabledClass);
  168. break;
  169. case 'last':
  170. page === totalPages && _li.classList.toggle(options.disabledClass);
  171. break;
  172. case 'prev':
  173. (page - 1) < startPage && _li.classList.toggle(options.disabledClass);
  174. break;
  175. case 'next':
  176. (page + 1) > totalPages && _li.classList.toggle(options.disabledClass);
  177. break;
  178. default:
  179. break;
  180. }
  181. }
  182. el.before(container);
  183. container.appendChild(ul);
  184. }
  185. function handle() {
  186. var pagLi = container.childNodes[0].childNodes;
  187. for (var i = 0, len = pagLi.length; i < len; i++) {
  188. (function() {
  189. var item = pagLi[i];
  190. item.addEventListener('click', function(e) {
  191. e.preventDefault();
  192. var page = parseInt(item.getAttribute('data-page'), 10);
  193. currentPage = page;
  194. // let's prevent the pagination from flowing to two rows
  195. if (currentPage >= 98) {
  196. limitPagination = 4;
  197. } else {
  198. limitPagination = pagLimit();
  199. }
  200. show(page);
  201. });
  202. }());
  203. }
  204. }
  205. function show(page) {
  206. render(page);
  207. handle();
  208. }
  209. show(startPage);
  210. return;
  211. };