| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| (function (callback) { |
| if (typeof define === 'function' && define.amd) { |
| define(['core/AbstractWidget'], callback); |
| } |
| else { |
| callback(); |
| } |
| }(function () { |
| |
| (function ($) { |
| |
| /** |
| * A pager widget for jQuery. |
| * |
| * <p>Heavily inspired by the Ruby on Rails will_paginate gem.</p> |
| * |
| * @expects this.target to be a list. |
| * @class PagerWidget |
| * @augments AjaxSolr.AbstractWidget |
| * @todo Don't use the manager to send the request. Request only the results, |
| * not the facets. Update only itself and the results widget. |
| */ |
| AjaxSolr.PagerWidget = AjaxSolr.AbstractWidget.extend( |
| /** @lends AjaxSolr.PagerWidget.prototype */ |
| { |
| /** |
| * @param {Object} [attributes] |
| * @param {Number} [attributes.innerWindow] How many links are shown around |
| * the current page. Defaults to 4. |
| * @param {Number} [attributes.outerWindow] How many links are around the |
| * first and the last page. Defaults to 1. |
| * @param {String} [attributes.prevLabel] The previous page link label. |
| * Defaults to "« Previous". |
| * @param {String} [attributes.nextLabel] The next page link label. Defaults |
| * to "Next »". |
| * @param {String} [attributes.separator] Separator between pagination links. |
| * Defaults to " ". |
| */ |
| constructor: function (attributes) { |
| AjaxSolr.PagerWidget.__super__.constructor.apply(this, arguments); |
| AjaxSolr.extend(this, { |
| innerWindow: 4, |
| outerWindow: 1, |
| prevLabel: '« Previous', |
| nextLabel: 'Next »', |
| separator: ' ', |
| // The current page number. |
| currentPage: null, |
| // The total number of pages. |
| totalPages: null |
| }, attributes); |
| }, |
| |
| /** |
| * @returns {String} The gap in page links, which is represented by: |
| * <span class="pager-gap">…</span> |
| */ |
| gapMarker: function () { |
| return '<span class="pager-gap">…</span>'; |
| }, |
| |
| /** |
| * @returns {Array} The links for the visible page numbers. |
| */ |
| windowedLinks: function () { |
| var links = []; |
| |
| var prev = null; |
| |
| visible = this.visiblePageNumbers(); |
| for (var i = 0, l = visible.length; i < l; i++) { |
| if (prev && visible[i] > prev + 1) links.push(this.gapMarker()); |
| links.push(this.pageLinkOrSpan(visible[i], [ 'pager-current' ])); |
| prev = visible[i]; |
| } |
| |
| return links; |
| }, |
| |
| /** |
| * @returns {Array} The visible page numbers according to the window options. |
| */ |
| visiblePageNumbers: function () { |
| var windowFrom = this.currentPage - this.innerWindow; |
| var windowTo = this.currentPage + this.innerWindow; |
| |
| // If the window is truncated on one side, make the other side longer |
| if (windowTo > this.totalPages) { |
| windowFrom = Math.max(0, windowFrom - (windowTo - this.totalPages)); |
| windowTo = this.totalPages; |
| } |
| if (windowFrom < 1) { |
| windowTo = Math.min(this.totalPages, windowTo + (1 - windowFrom)); |
| windowFrom = 1; |
| } |
| |
| var visible = []; |
| |
| // Always show the first page |
| visible.push(1); |
| // Don't add inner window pages twice |
| for (var i = 2; i <= Math.min(1 + this.outerWindow, windowFrom - 1); i++) { |
| visible.push(i); |
| } |
| // If the gap is just one page, close the gap |
| if (1 + this.outerWindow == windowFrom - 2) { |
| visible.push(windowFrom - 1); |
| } |
| // Don't add the first or last page twice |
| for (var i = Math.max(2, windowFrom); i <= Math.min(windowTo, this.totalPages - 1); i++) { |
| visible.push(i); |
| } |
| // If the gap is just one page, close the gap |
| if (this.totalPages - this.outerWindow == windowTo + 2) { |
| visible.push(windowTo + 1); |
| } |
| // Don't add inner window pages twice |
| for (var i = Math.max(this.totalPages - this.outerWindow, windowTo + 1); i < this.totalPages; i++) { |
| visible.push(i); |
| } |
| // Always show the last page, unless it's the first page |
| if (this.totalPages > 1) { |
| visible.push(this.totalPages); |
| } |
| |
| return visible; |
| }, |
| |
| /** |
| * @param {Number} page A page number. |
| * @param {String} classnames CSS classes to add to the page link. |
| * @param {String} text The inner HTML of the page link (optional). |
| * @returns The link or span for the given page. |
| */ |
| pageLinkOrSpan: function (page, classnames, text) { |
| text = text || page; |
| |
| if (page && page != this.currentPage) { |
| return $('<a href="#"></a>').html(text).attr('rel', this.relValue(page)).addClass(classnames[1]).click(this.clickHandler(page)); |
| } |
| else { |
| return $('<span></span>').html(text).addClass(classnames.join(' ')); |
| } |
| }, |
| |
| /** |
| * @param {Number} page A page number. |
| * @returns {Function} The click handler for the page link. |
| */ |
| clickHandler: function (page) { |
| var self = this; |
| return function () { |
| self.manager.store.get('start').val((page - 1) * self.perPage()); |
| self.doRequest(); |
| return false; |
| } |
| }, |
| |
| /** |
| * @param {Number} page A page number. |
| * @returns {String} The <tt>rel</tt> attribute for the page link. |
| */ |
| relValue: function (page) { |
| switch (page) { |
| case this.previousPage(): |
| return 'prev' + (page == 1 ? 'start' : ''); |
| case this.nextPage(): |
| return 'next'; |
| case 1: |
| return 'start'; |
| default: |
| return ''; |
| } |
| }, |
| |
| /** |
| * @returns {Number} The page number of the previous page or null if no previous page. |
| */ |
| previousPage: function () { |
| return this.currentPage > 1 ? (this.currentPage - 1) : null; |
| }, |
| |
| /** |
| * @returns {Number} The page number of the next page or null if no next page. |
| */ |
| nextPage: function () { |
| return this.currentPage < this.totalPages ? (this.currentPage + 1) : null; |
| }, |
| |
| /** |
| * An abstract hook for child implementations. |
| * |
| * @param {Number} perPage The number of items shown per results page. |
| * @param {Number} offset The index in the result set of the first document to render. |
| * @param {Number} total The total number of documents in the result set. |
| */ |
| renderHeader: function (perPage, offset, total) {}, |
| |
| /** |
| * Render the pagination links. |
| * |
| * @param {Array} links The links for the visible page numbers. |
| */ |
| renderLinks: function (links) { |
| if (this.totalPages) { |
| links.unshift(this.pageLinkOrSpan(this.previousPage(), [ 'pager-disabled', 'pager-prev' ], this.prevLabel)); |
| links.push(this.pageLinkOrSpan(this.nextPage(), [ 'pager-disabled', 'pager-next' ], this.nextLabel)); |
| |
| var $target = $(this.target); |
| $target.empty(); |
| |
| for (var i = 0, l = links.length; i < l; i++) { |
| var $li = $('<li></li>'); |
| if (this.separator && i > 0) { |
| $li.append(this.separator); |
| } |
| $target.append($li.append(links[i])); |
| } |
| } |
| }, |
| |
| /** |
| * @returns {Number} The number of results to display per page. |
| */ |
| perPage: function () { |
| return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params && this.manager.response.responseHeader.params.rows || this.manager.store.get('rows').val() || 10); |
| }, |
| |
| /** |
| * @returns {Number} The Solr offset parameter's value. |
| */ |
| getOffset: function () { |
| return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params && this.manager.response.responseHeader.params.start || this.manager.store.get('start').val() || 0); |
| }, |
| |
| afterRequest: function () { |
| var perPage = this.perPage(); |
| var offset = this.getOffset(); |
| var total = parseInt(this.manager.response.response.numFound); |
| |
| // Normalize the offset to a multiple of perPage. |
| offset = offset - offset % perPage; |
| |
| this.currentPage = Math.ceil((offset + 1) / perPage); |
| this.totalPages = Math.ceil(total / perPage); |
| |
| $(this.target).empty(); |
| |
| this.renderLinks(this.windowedLinks()); |
| this.renderHeader(perPage, offset, total); |
| } |
| }); |
| |
| })(jQuery); |
| |
| })); |