123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- (function (global, factory) {
- if (typeof define === "function" && define.amd) {
- define([], factory);
- } else if (typeof exports !== "undefined") {
- factory();
- } else {
- var mod = {
- exports: {}
- };
- factory();
- global.bootstrapTablePipeline = mod.exports;
- }
- })(this, function () {
- 'use strict';
- /**
- * @author doug-the-guy
- * @version v1.0.0
- *
- * Boostrap Table Pipeline
- * -----------------------
- *
- * This plugin enables client side data caching for server side requests which will
- * eliminate the need to issue a new request every page change. This will allow
- * for a performance balance for a large data set between returning all data at once
- * (client side paging) and a new server side request (server side paging).
- *
- * There are two new options:
- * - usePipeline: enables this feature
- * - pipelineSize: the size of each cache window
- *
- * The size of the pipeline must be evenly divisible by the current page size. This is
- * assured by rounding up to the nearest evenly divisible value. For example, if
- * the pipeline size is 4990 and the current page size is 25, then pipeline size will
- * be dynamically set to 5000.
- *
- * The cache windows are computed based on the pipeline size and the total number of rows
- * returned by the server side query. For example, with pipeline size 500 and total rows
- * 1300, the cache windows will be:
- *
- * [{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
- *
- * Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
- * **MUST** return only the data in the requested cache window **AND** the total number of rows.
- * To wit, the server side code must use the offset and limit parameters to prepare the response
- * data.
- *
- * On a page change, the new offset is checked if it is within the current cache window. If so,
- * the requested page data is returned from the cached data set. Otherwise, a new server side
- * request will be issued for the new cache window.
- *
- * The current cached data is only invalidated on these events:
- * * sorting
- * * searching
- * * page size change
- * * page change moves into a new cache window
- *
- * There are two new events:
- * - cached-data-hit.bs.table: issued when cached data is used on a page change
- * - cached-data-reset.bs.table: issued when the cached data is invalidated and a
- * new server side request is issued
- *
- **/
- (function ($) {
- 'use strict';
- var Utils = $.fn.bootstrapTable.utils;
- $.extend($.fn.bootstrapTable.defaults, {
- usePipeline: false,
- pipelineSize: 1000,
- onCachedDataHit: function onCachedDataHit(data) {
- return false;
- },
- onCachedDataReset: function onCachedDataReset(data) {
- return false;
- }
- });
- $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
- 'cached-data-hit.bs.table': 'onCachedDataHit',
- 'cached-data-reset.bs.table': 'onCachedDataReset'
- });
- var BootstrapTable = $.fn.bootstrapTable.Constructor,
- _init = BootstrapTable.prototype.init,
- _initServer = BootstrapTable.prototype.initServer,
- _onSearch = BootstrapTable.prototype.onSearch,
- _onSort = BootstrapTable.prototype.onSort,
- _onPageListChange = BootstrapTable.prototype.onPageListChange;
- BootstrapTable.prototype.init = function () {
- // needs to be called before initServer()
- this.initPipeline();
- _init.apply(this, Array.prototype.slice.apply(arguments));
- };
- BootstrapTable.prototype.initPipeline = function () {
- this.cacheRequestJSON = {};
- this.cacheWindows = [];
- this.currWindow = 0;
- this.resetCache = true;
- };
- BootstrapTable.prototype.onSearch = function (event) {
- /* force a cache reset on search */
- if (this.options.usePipeline) {
- this.resetCache = true;
- }
- _onSearch.apply(this, Array.prototype.slice.apply(arguments));
- };
- BootstrapTable.prototype.onSort = function (event) {
- /* force a cache reset on sort */
- if (this.options.usePipeline) {
- this.resetCache = true;
- }
- _onSort.apply(this, Array.prototype.slice.apply(arguments));
- };
- BootstrapTable.prototype.onPageListChange = function (event) {
- /* rebuild cache window on page size change */
- var target = $(event.currentTarget);
- var newPageSize = parseInt(target.text());
- this.options.pipelineSize = this.calculatePipelineSize(this.options.pipelineSize, newPageSize);
- this.resetCache = true;
- _onPageListChange.apply(this, Array.prototype.slice.apply(arguments));
- };
- BootstrapTable.prototype.calculatePipelineSize = function (pipelineSize, pageSize) {
- /* calculate pipeline size by rounding up to the nearest value evenly divisible
- * by the pageSize */
- if (pageSize == 0) return 0;
- return Math.ceil(pipelineSize / pageSize) * pageSize;
- };
- BootstrapTable.prototype.setCacheWindows = function () {
- /* set cache windows based on the total number of rows returned by server side
- * request and the pipelineSize */
- this.cacheWindows = [];
- var numWindows = this.options.totalRows / this.options.pipelineSize;
- for (var i = 0; i <= numWindows; i++) {
- var b = i * this.options.pipelineSize;
- this.cacheWindows[i] = { 'lower': b, 'upper': b + this.options.pipelineSize - 1 };
- }
- };
- BootstrapTable.prototype.setCurrWindow = function (offset) {
- /* set the current cache window index, based on where the current offset falls */
- this.currWindow = 0;
- for (var i = 0; i < this.cacheWindows.length; i++) {
- if (this.cacheWindows[i].lower <= offset && offset <= this.cacheWindows[i].upper) {
- this.currWindow = i;
- break;
- }
- }
- };
- BootstrapTable.prototype.drawFromCache = function (offset, limit) {
- /* draw rows from the cache using offset and limit */
- var res = $.extend(true, {}, this.cacheRequestJSON);
- var drawStart = offset - this.cacheWindows[this.currWindow].lower;
- var drawEnd = drawStart + limit;
- res.rows = res.rows.slice(drawStart, drawEnd);
- return res;
- };
- BootstrapTable.prototype.initServer = function (silent, query, url) {
- /* determine if requested data is in cache (on paging) or if
- * a new ajax request needs to be issued (sorting, searching, paging
- * moving outside of cached data, page size change)
- * initial version of this extension will entirely override base initServer
- **/
- var data = {};
- var index = this.header.fields.indexOf(this.options.sortName);
- var params = {
- searchText: this.searchText,
- sortName: this.options.sortName,
- sortOrder: this.options.sortOrder
- };
- var request = null;
- if (this.header.sortNames[index]) {
- params.sortName = this.header.sortNames[index];
- }
- if (this.options.pagination && this.options.sidePagination === 'server') {
- params.pageSize = this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize;
- params.pageNumber = this.options.pageNumber;
- }
- if (!(url || this.options.url) && !this.options.ajax) {
- return;
- }
- var useAjax = true;
- if (this.options.queryParamsType === 'limit') {
- params = {
- searchText: params.searchText,
- sortName: params.sortName,
- sortOrder: params.sortOrder
- };
- if (this.options.pagination && this.options.sidePagination === 'server') {
- params.limit = this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize;
- params.offset = (this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize) * (this.options.pageNumber - 1);
- if (this.options.usePipeline) {
- // if cacheWindows is empty, this is the initial request
- if (!this.cacheWindows.length) {
- useAjax = true;
- params.drawOffset = params.offset;
- // cache exists: determine if the page request is entirely within the current cached window
- } else {
- var w = this.cacheWindows[this.currWindow];
- // case 1: reset cache but stay within current window (e.g. column sort)
- // case 2: move outside of the current window (e.g. search or paging)
- // since each cache window is aligned with the current page size
- // checking if params.offset is outside the current window is sufficient.
- // need to requery for preceding or succeeding cache window
- // also handle case
- if (this.resetCache || params.offset < w.lower || params.offset > w.upper) {
- useAjax = true;
- this.setCurrWindow(params.offset);
- // store the relative offset for drawing the page data afterwards
- params.drawOffset = params.offset;
- // now set params.offset to the lower bound of the new cache window
- // the server will return that whole cache window
- params.offset = this.cacheWindows[this.currWindow].lower;
- // within current cache window
- } else {
- useAjax = false;
- }
- }
- } else {
- if (params.limit === 0) {
- delete params.limit;
- }
- }
- }
- }
- // force an ajax call - this is on search, sort or page size change
- if (this.resetCache) {
- useAjax = true;
- this.resetCache = false;
- }
- if (this.options.usePipeline && useAjax) {
- /* in this scenario limit is used on the server to get the cache window
- * and drawLimit is used to get the page data afterwards */
- params.drawLimit = params.limit;
- params.limit = this.options.pipelineSize;
- }
- // cached results can be used
- if (!useAjax) {
- var res = this.drawFromCache(params.offset, params.limit);
- this.load(res);
- this.trigger('load-success', res);
- this.trigger('cached-data-hit', res);
- return;
- }
- // cached results can't be used
- // continue base initServer code
- if (!$.isEmptyObject(this.filterColumnsPartial)) {
- params.filter = JSON.stringify(this.filterColumnsPartial, null);
- }
- data = Utils.calculateObjectValue(this.options, this.options.queryParams, [params], data);
- $.extend(data, query || {});
- // false to stop request
- if (data === false) {
- return;
- }
- if (!silent) {
- this.$tableLoading.show();
- }
- var self = this;
- request = $.extend({}, Utils.calculateObjectValue(null, this.options.ajaxOptions), {
- type: this.options.method,
- url: url || this.options.url,
- data: this.options.contentType === 'application/json' && this.options.method === 'post' ? JSON.stringify(data) : data,
- cache: this.options.cache,
- contentType: this.options.contentType,
- dataType: this.options.dataType,
- success: function success(res) {
- res = Utils.calculateObjectValue(self.options, self.options.responseHandler, [res], res);
- // cache results if using pipelining
- if (self.options.usePipeline) {
- // store entire request in cache
- self.cacheRequestJSON = $.extend(true, {}, res);
- // this gets set in load() also but needs to be set before
- // setting cacheWindows
- self.options.totalRows = res[self.options.totalField];
- // if this is a search, potentially less results will be returned
- // so cache windows need to be rebuilt. Otherwise it
- // will come out the same
- self.setCacheWindows();
- self.setCurrWindow(params.drawOffset);
- // just load data for the page
- res = self.drawFromCache(params.drawOffset, params.drawLimit);
- self.trigger('cached-data-reset', res);
- }
- self.load(res);
- self.trigger('load-success', res);
- if (!silent) self.$tableLoading.hide();
- },
- error: function error(res) {
- var data = [];
- if (self.options.sidePagination === 'server') {
- data = {};
- data[self.options.totalField] = 0;
- data[self.options.dataField] = [];
- }
- self.load(data);
- self.trigger('load-error', res.status, res);
- if (!silent) self.$tableLoading.hide();
- }
- });
- if (this.options.ajax) {
- Utils.calculateObjectValue(this, this.options.ajax, [request], null);
- } else {
- if (this._xhr && this._xhr.readyState !== 4) {
- this._xhr.abort();
- }
- this._xhr = $.ajax(request);
- }
- };
- $.fn.bootstrapTable.methods.push();
- })(jQuery);
- });
|