| // Licensed 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. |
| |
| import _ from 'lodash'; |
| import $ from 'jquery'; |
| import Backbone from "backbone"; |
| import app from '../../../app/app'; |
| |
| //PagingCollection |
| //---------------- |
| |
| // A PagingCollection knows how to build appropriate requests to the |
| // CouchDB-like server and how to fetch. The Collection will always contain a |
| // single page of documents. |
| |
| export const PagingCollection = Backbone.Collection.extend({ |
| |
| // initialize parameters and page size |
| constructor: function() { |
| Backbone.Collection.apply(this, arguments); |
| this.configure.apply(this, arguments); |
| }, |
| |
| configure: function(collections, options) { |
| var querystring = _.result(this, "url").split("?")[1] || ""; |
| this.paging = _.defaults((options.paging || {}), { |
| defaultParams: _.defaults({}, options.params, this._parseQueryString(querystring)), |
| hasNext: false, |
| hasPrevious: false, |
| params: {}, |
| pageSize: 20, |
| direction: undefined |
| }); |
| |
| this.paging.params = _.clone(this.paging.defaultParams); |
| this.updateUrlQuery(this.paging.defaultParams); |
| }, |
| |
| calculateParams: function(currentParams, skipIncrement, limitIncrement) { |
| |
| var params = _.clone(currentParams); |
| params.skip = (parseInt(currentParams.skip, 10) || 0) + skipIncrement; |
| |
| // guard against hard limits |
| if (this.paging.defaultParams.limit) { |
| params.limit = Math.min(this.paging.defaultParams.limit, params.limit); |
| } |
| // request an extra row so we know that there are more results |
| params.limit = limitIncrement + 1; |
| // prevent illegal skip values |
| params.skip = Math.max(params.skip, 0); |
| |
| return params; |
| }, |
| |
| pageSizeReset: function(pageSize, opts) { |
| var options = _.defaults((opts || {}), {fetch: true}); |
| this.paging.direction = undefined; |
| this.paging.pageSize = pageSize; |
| this.paging.params = this.paging.defaultParams; |
| this.paging.params.limit = pageSize; |
| this.updateUrlQuery(this.paging.params); |
| if (options.fetch) { |
| return this.fetch(); |
| } |
| }, |
| |
| _parseQueryString: function(uri) { |
| var queryString = decodeURI(uri).split(/&/); |
| |
| return _.reduce(queryString, function (parsedQuery, item) { |
| var nameValue = item.split(/=/); |
| if (nameValue.length === 2) { |
| parsedQuery[nameValue[0]] = nameValue[1]; |
| } |
| |
| return parsedQuery; |
| }, {}); |
| }, |
| |
| _iterate: function(offset, opts) { |
| var options = _.defaults((opts || {}), {fetch: true}); |
| |
| this.paging.params = this.calculateParams(this.paging.params, offset, this.paging.pageSize); |
| |
| // Fetch the next page of documents |
| this.updateUrlQuery(this.paging.params); |
| if (options.fetch) { |
| return this.fetch({reset: true}); |
| } |
| }, |
| |
| // `next` is called with the number of items for the next page. |
| // It returns the fetch promise. |
| next: function(options) { |
| this.paging.direction = "next"; |
| return this._iterate(this.paging.pageSize, options); |
| }, |
| |
| // `previous` is called with the number of items for the previous page. |
| // It returns the fetch promise. |
| previous: function(options) { |
| this.paging.direction = "previous"; |
| return this._iterate(0 - this.paging.pageSize, options); |
| }, |
| |
| shouldStringify: function (val) { |
| try { |
| JSON.parse(val); |
| return false; |
| } catch (e) { |
| return true; |
| } |
| }, |
| |
| // Encodes the parameters so that couchdb will understand them |
| // and then sets the url with the new url. |
| updateUrlQuery: function (params) { |
| var url = _.result(this, "url").split("?")[0]; |
| |
| _.each(['startkey', 'endkey', 'key'], (key) => { |
| if (_.has(params, key) && this.shouldStringify(params[key])) { |
| params[key] = JSON.stringify(params[key]); |
| } |
| }); |
| |
| this.url = url + '?' + app.utils.queryParams(params); |
| }, |
| |
| fetch: function () { |
| // if this is a fetch for the first time, fetch one extra to see if there is a next |
| if (!this.paging.direction && this.paging.params.limit > 0) { |
| this.paging.direction = 'fetch'; |
| this.paging.params.limit = this.paging.params.limit + 1; |
| this.updateUrlQuery(this.paging.params); |
| } |
| |
| return Backbone.Collection.prototype.fetch.apply(this, arguments); |
| }, |
| |
| parse: function (resp) { |
| var rows = resp.rows; |
| |
| this.paging.hasNext = this.paging.hasPrevious = false; |
| |
| this.viewMeta = { |
| total_rows: resp.total_rows, |
| offset: resp.offset, |
| update_seq: resp.update_seq |
| }; |
| |
| if (this.paging.params.skip > 0) { |
| this.paging.hasPrevious = true; |
| } |
| |
| if (rows.length === this.paging.pageSize + 1) { |
| this.paging.hasNext = true; |
| |
| // remove the next page marker result |
| rows.pop(); |
| this.viewMeta.total_rows = this.viewMeta.total_rows - 1; |
| } |
| return rows; |
| }, |
| |
| hasNext: function() { |
| return this.paging.hasNext; |
| }, |
| |
| hasPrevious: function() { |
| return this.paging.hasPrevious; |
| } |
| }); |
| |
| export default PagingCollection; |
| |
| |
| // if (exports) { |
| // // Overload the Backbone.ajax method, this allows PagingCollection to be able to |
| // // work in node.js |
| // exports.setAjax = function (ajax) { |
| // Backbone.ajax = ajax; |
| // }; |
| |
| // exports.PagingCollection = PagingCollection; |
| // } |
| |
| // return PagingCollection; |
| // })); |
| |
| |