UNPKG

9.46 kBJavaScriptView Raw
1
2import { BunnyURL } from './url';
3
4export const PaginationConfig = {
5
6 // HTML tags and attributes
7 tagName: 'pagination',
8 attrLimit: 'limit',
9 attrOuter: 'outer', // boolean attribute - display or not first/last buttons
10
11 tagNameItem: 'li',
12 tagNameLink: 'a',
13 attrLinkUrl: 'href',
14
15 // default attribute values
16 attrLimitDefault: 7,
17 attrOuterDefault: false,
18
19 // CSS styling
20 classItem: 'page-item',
21 classItemDisabled: 'disabled',
22 classItemActive: 'active',
23 classLink: 'page-link',
24
25 // language strings
26 langFirst: 'First',
27 langLast: 'Last',
28 langPrevious: '< Previous',
29 langNext: 'Next >',
30 langStats: '{from}-{to} of total {total} (Page {currentPage} of {lastPage})',
31
32 // HTTP GET param names in URL
33 paramPage: 'page'
34
35};
36
37export const Pagination = {
38
39 _config: PaginationConfig,
40
41 _collection: [],
42 _callbacks: [],
43 _dataCollection: [],
44
45 _attachEventHandlers(pagination) {
46 const items = this.getItems(pagination);
47 [].forEach.call(items, item => {
48 //if (!this.isItemActive(item) && !this.isItemDisabled(item)) {
49 this._attachSingleEventHandler(item);
50 //}
51 });
52 },
53
54 _getIndex(pagination) {
55 for (let k =0; k < this._collection.length; k++) {
56 if (this._collection[k] === pagination) {
57 return k;
58 }
59 }
60 return false;
61 },
62
63 onItemClick(pagination, callback) {
64 const index = this._getIndex(pagination);
65 this._callbacks[index].push(callback);
66 },
67
68 getPaginationByItem(item) {
69 return item.parentNode;
70 },
71
72 _attachSingleEventHandler(item) {
73 const pagination = this.getPaginationByItem(item);
74 const index = this._getIndex(pagination);
75 const link = this.getLinkByItem(item);
76 const callbacks = this._callbacks[index];
77 link.addEventListener('click', (e) => {
78 e.preventDefault();
79 if (callbacks.length > 0) {
80 const url = link.getAttribute('href');
81 const page = this.getPageFromUrl(url);
82 this._callbacks[index].forEach(callback => {
83 callback(page, url);
84 })
85 }
86 })
87 },
88
89 getLimit(pagination) {
90 let limit = pagination.getAttribute(this._config.attrLimit);
91 if (limit === null) {
92 limit = this._config.attrLimitDefault;
93 }
94 return limit;
95 },
96
97 isOuter(pagination) {
98 let outer = pagination.hasAttribute(this._config.attrOuter);
99 if (outer === false) {
100 outer = this._config.attrOuterDefault;
101 }
102 return outer;
103 },
104
105 getItems(pagination) {
106 return pagination.getElementsByClassName(this._config.classItem);
107 },
108
109 isItemDisabled(item) {
110 return item.classList.contains(this._config.classItemDisabled);
111 },
112
113 isItemActive(item) {
114 return item.classList.contains(this._config.classItemActive);
115 },
116
117 setItemActive(item) {
118 item.classList.add(this._config.classItemActive);
119 },
120
121 isItemDisabled(item) {
122 return item.classList.add(this._config.classItemDisabled);
123 },
124
125 getLinkByItem(item) {
126 return item.getElementsByClassName(this._config.classLink)[0];
127 },
128
129 createItem(url, text, active = false, disabled = false) {
130 const item = document.createElement(this._config.tagNameItem);
131 item.classList.add(this._config.classItem);
132 if (active) {
133 item.classList.add(this._config.classItemActive);
134 }
135 if (disabled) {
136 item.classList.add(this._config.classItemDisabled);
137 }
138 const link = document.createElement(this._config.tagNameLink);
139 link.setAttribute(this._config.attrLinkUrl, url);
140 link.classList.add(this._config.classLink);
141 link.textContent = text;
142 item.appendChild(link);
143 return item;
144 },
145
146 createPageItem(pagination, page) {
147 return this.createItem(this.getPageUrl(pagination, page), page, this.isPage(pagination, page));
148 },
149
150 createFirstItem(pagination) {
151 return this.createItem(this.getFirstPageUrl(pagination), this._config.langFirst, false, this.isFirstPage(pagination));
152 },
153
154 createPrevItem(pagination) {
155 return this.createItem(this.getPrevPageUrl(pagination), this._config.langPrevious, false, this.isFirstPage(pagination));
156 },
157
158 createNextItem(pagination) {
159 return this.createItem(this.getNextPageUrl(pagination), this._config.langNext, false, this.isLastPage(pagination));
160 },
161
162 createLastItem(pagination) {
163 return this.createItem(this.getLastPageUrl(pagination), this._config.langLast, false, this.isLastPage(pagination));
164 },
165
166 removeItems(pagination) {
167 while (pagination.firstChild) {
168 pagination.removeChild(pagination.firstChild);
169 }
170 },
171
172 getStats(pagination) {
173 if (!this.hasPages(pagination)) {
174 return '';
175 }
176 let str = this._config.langStats;
177 const data = this.getData(pagination);
178 for (let key in data) {
179 str = str.replace('{' + key + '}', data[key]);
180 }
181 return str;
182 },
183
184 _getPaginationData(data, urlParams = null) {
185 const nextPageUrl = urlParams === null || data.next_page_url === null ? data.next_page_url : BunnyURL.setParams(urlParams, data.next_page_url);
186 const prevPageUrl = urlParams === null || data.prev_page_url === null ? data.prev_page_url : BunnyURL.setParams(urlParams, data.prev_page_url);
187 return {
188 count: parseInt(data.to) - parseInt(data.from) + 1,
189 currentPage: parseInt(data.current_page),
190 lastPage: parseInt(data.last_page),
191 nextPageUrl: nextPageUrl,
192 perPage: parseInt(data.per_page),
193 previousPageUrl: prevPageUrl,
194 total: parseInt(data.total),
195 from: parseInt(data.from),
196 to: parseInt(data.to)
197 };
198 },
199
200 getData(pagination) {
201 return this._dataCollection[this._getIndex(pagination)];
202 },
203
204 hasPages(pagination) {
205 const data = this.getData(pagination);
206 return data.total > data.perPage;
207 },
208
209 getCount(pagination) {
210 return this.getData(pagination).count;
211 },
212
213 getCurrentPage(pagination) {
214 return this.getData(pagination).currentPage;
215 },
216
217 getLastPage(pagination) {
218 return this.getData(pagination).lastPage;
219 },
220
221 getPageUrl(pagination, page) {
222 return this._getUrlForPage(pagination, page);
223 },
224
225 getFirstPageUrl(pagination) {
226 return this._getUrlForPage(pagination, 1);
227 },
228
229 getLastPageUrl(pagination) {
230 return this._getUrlForPage(pagination, this.getLastPage(pagination));
231 },
232
233 getPrevPageUrl(pagination) {
234 return this.getData(pagination).previousPageUrl;
235 },
236
237 getNextPageUrl(pagination) {
238 return this.getData(pagination).nextPageUrl;
239 },
240
241 getTotal(pagination) {
242 return this.getData(pagination).total;
243 },
244
245 getFrom(pagination) {
246 return this.getData(pagination).from;
247 },
248
249 getTo(pagination) {
250 return this.getData(pagination).to;
251 },
252
253 getPerPage(pagination) {
254 return this.getData(pagination).perPage;
255 },
256
257 isLastPage(pagination) {
258 return this.isPage(pagination, this.getLastPage(pagination));
259 },
260
261 isFirstPage(pagination) {
262 return this.isPage(pagination, 1);
263 },
264
265 isPage(pagination, page) {
266 return this.getCurrentPage(pagination) === page;
267 },
268
269 addPageParamToUrl(url, page) {
270 const sep = (url.indexOf('?') === -1) ? '?' : '&';
271 return url + sep + this._config.paramPage + '=' + page;
272 },
273
274 getPageFromUrl(url) {
275 return BunnyURL.getParam(this._config.paramPage, url);
276 },
277
278 _getUrlForPage(pagination, page) {
279 const index = this._getIndex(pagination);
280 const pData = this._dataCollection[index];
281 let url = '';
282 let tmp_page = 1;
283 if (pData.nextPageUrl !== null) {
284 url = pData.nextPageUrl;
285 tmp_page = pData.currentPage + 1;
286 } else if (pData.previousPageUrl !== null) {
287 url = pData.previousPageUrl;
288 tmp_page = pData.currentPage - 1;
289 } else {
290 throw new Error('Bunny Pagination: Server returned null for nextPageUrl and previousPageUrl');
291 }
292 return url.replace('page=' + tmp_page, 'page=' + page);
293 },
294
295 redraw(pagination) {
296 this.removeItems(pagination);
297 if (!this.hasPages(pagination)) {
298 return false;
299 }
300
301 const isOuter = this.isOuter(pagination);
302 const limit = this.getLimit(pagination);
303 const lastPage = this.getLastPage(pagination);
304 const currentPage = this.getCurrentPage(pagination);
305
306 const f = document.createDocumentFragment();
307
308 this.getData(pagination);
309
310 if (isOuter) {
311 f.appendChild(this.createFirstItem(pagination));
312 }
313 f.appendChild(this.createPrevItem(pagination));
314
315 for (let k = 1; k <= lastPage; k++) {
316 const half = Math.floor(limit / 2);
317 let from = currentPage - half;
318 let to = currentPage + half;
319 if (currentPage < half) {
320 to += half - currentPage;
321 }
322 if (lastPage - currentPage < half) {
323 from -= half - (lastPage - currentPage) - 1;
324 }
325 if (from < k && k < to) {
326 f.appendChild(this.createPageItem(pagination, k));
327 }
328 }
329
330 f.appendChild(this.createNextItem(pagination));
331 if (isOuter) {
332 f.appendChild(this.createLastItem(pagination));
333 }
334
335 pagination.appendChild(f);
336
337 return true;
338 },
339
340
341 initOrUpdate(pagination, data, urlParams = null) {
342 const index = this._getIndex(pagination);
343 if (index === false) {
344 this._collection.push(pagination);
345 this._dataCollection.push(this._getPaginationData(data, urlParams));
346 this._callbacks.push([]);
347 } else {
348 this._dataCollection[index] = this._getPaginationData(data, urlParams);
349 this._callbacks[index] = []; // clear callbacks
350 }
351 this.redraw(pagination);
352 this._attachEventHandlers(pagination);
353 },
354};