|
@@ -67,222 +67,173 @@ Object.assign($.fn.bootstrapTable.events, {
|
|
|
'cached-data-reset.bs.table': 'onCachedDataReset'
|
|
'cached-data-reset.bs.table': 'onCachedDataReset'
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const BootstrapTable = $.fn.bootstrapTable.Constructor
|
|
|
|
|
-const _init = BootstrapTable.prototype.init
|
|
|
|
|
-const _onSearch = BootstrapTable.prototype.onSearch
|
|
|
|
|
-const _onSort = BootstrapTable.prototype.onSort
|
|
|
|
|
-const _onPageListChange = BootstrapTable.prototype.onPageListChange
|
|
|
|
|
-
|
|
|
|
|
-BootstrapTable.prototype.init = function (...args) {
|
|
|
|
|
- // needs to be called before initServer()
|
|
|
|
|
- this.initPipeline()
|
|
|
|
|
- _init.apply(this, Array.prototype.slice.apply(args))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-BootstrapTable.prototype.initPipeline = function () {
|
|
|
|
|
- this.cacheRequestJSON = {}
|
|
|
|
|
- this.cacheWindows = []
|
|
|
|
|
- this.currWindow = 0
|
|
|
|
|
- this.resetCache = true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-BootstrapTable.prototype.onSearch = function () {
|
|
|
|
|
- /* force a cache reset on search */
|
|
|
|
|
- if (this.options.usePipeline) {
|
|
|
|
|
- this.resetCache = true
|
|
|
|
|
|
|
+$.BootstrapTable = class extends $.BootstrapTable {
|
|
|
|
|
+ // needs to be called before initServer
|
|
|
|
|
+ init (...args) {
|
|
|
|
|
+ if (this.options.usePipeline) {
|
|
|
|
|
+ this.initPipeline()
|
|
|
|
|
+ }
|
|
|
|
|
+ super.init(...args)
|
|
|
}
|
|
}
|
|
|
- _onSearch.apply(this, Array.prototype.slice.apply(arguments))
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-BootstrapTable.prototype.onSort = function () {
|
|
|
|
|
- /* force a cache reset on sort */
|
|
|
|
|
- if (this.options.usePipeline) {
|
|
|
|
|
|
|
+ initPipeline () {
|
|
|
|
|
+ this.cacheRequestJSON = {}
|
|
|
|
|
+ this.cacheWindows = []
|
|
|
|
|
+ this.currWindow = 0
|
|
|
this.resetCache = true
|
|
this.resetCache = true
|
|
|
}
|
|
}
|
|
|
- _onSort.apply(this, Array.prototype.slice.apply(arguments))
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-BootstrapTable.prototype.onPageListChange = function (event) {
|
|
|
|
|
- /* rebuild cache window on page size change */
|
|
|
|
|
- const target = $(event.currentTarget)
|
|
|
|
|
- const newPageSize = parseInt(target.text(), 10)
|
|
|
|
|
-
|
|
|
|
|
- this.options.pipelineSize = this.calculatePipelineSize(this.options.pipelineSize, newPageSize)
|
|
|
|
|
- this.resetCache = true
|
|
|
|
|
- _onPageListChange.apply(this, Array.prototype.slice.apply(arguments))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-BootstrapTable.prototype.calculatePipelineSize = (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
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ // force a cache reset on search
|
|
|
|
|
+ onSearch (...args) {
|
|
|
|
|
+ if (this.options.usePipeline) {
|
|
|
|
|
+ this.resetCache = true
|
|
|
|
|
+ }
|
|
|
|
|
+ super.onSearch(...args)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-BootstrapTable.prototype.setCacheWindows = function () {
|
|
|
|
|
- /* set cache windows based on the total number of rows returned by server side
|
|
|
|
|
- * request and the pipelineSize */
|
|
|
|
|
- this.cacheWindows = []
|
|
|
|
|
- const numWindows = this.options.totalRows / this.options.pipelineSize
|
|
|
|
|
|
|
+ // force a cache reset on sort
|
|
|
|
|
+ onSort (...args) {
|
|
|
|
|
+ if (this.options.usePipeline) {
|
|
|
|
|
+ this.resetCache = true
|
|
|
|
|
+ }
|
|
|
|
|
+ super.onSort(...args)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for (let i = 0; i <= numWindows; i++) {
|
|
|
|
|
- const b = i * this.options.pipelineSize
|
|
|
|
|
|
|
+ // rebuild cache window on page size change
|
|
|
|
|
+ onPageListChange (event) {
|
|
|
|
|
+ const target = $(event.currentTarget)
|
|
|
|
|
+ const newPageSize = parseInt(target.text(), 10)
|
|
|
|
|
|
|
|
- this.cacheWindows[i] = { lower: b, upper: b + this.options.pipelineSize - 1 }
|
|
|
|
|
|
|
+ this.options.pipelineSize = this.calculatePipelineSize(this.options.pipelineSize, newPageSize)
|
|
|
|
|
+ this.resetCache = true
|
|
|
|
|
+ super.onPageListChange(event)
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-BootstrapTable.prototype.setCurrWindow = function (offset) {
|
|
|
|
|
- /* set the current cache window index, based on where the current offset falls */
|
|
|
|
|
- this.currWindow = 0
|
|
|
|
|
- for (let i = 0; i < this.cacheWindows.length; i++) {
|
|
|
|
|
- if (this.cacheWindows[i].lower <= offset && offset <= this.cacheWindows[i].upper) {
|
|
|
|
|
- this.currWindow = i
|
|
|
|
|
- break
|
|
|
|
|
|
|
+ // calculate pipeline size by rounding up to
|
|
|
|
|
+ // the nearest value evenly divisible by the pageSize
|
|
|
|
|
+ calculatePipelineSize (pipelineSize, pageSize) {
|
|
|
|
|
+ if (pageSize === 0) {
|
|
|
|
|
+ return 0
|
|
|
}
|
|
}
|
|
|
|
|
+ return Math.ceil(pipelineSize / pageSize) * pageSize
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-BootstrapTable.prototype.drawFromCache = function (offset, limit) {
|
|
|
|
|
- /* draw rows from the cache using offset and limit */
|
|
|
|
|
- const res = Utils.extend(true, {}, this.cacheRequestJSON)
|
|
|
|
|
- const drawStart = offset - this.cacheWindows[this.currWindow].lower
|
|
|
|
|
- const drawEnd = drawStart + limit
|
|
|
|
|
|
|
+ // set cache windows based on the total number of rows returned
|
|
|
|
|
+ // by server side request and the pipelineSize
|
|
|
|
|
+ setCacheWindows () {
|
|
|
|
|
+ this.cacheWindows = []
|
|
|
|
|
|
|
|
- res.rows = res.rows.slice(drawStart, drawEnd)
|
|
|
|
|
- return res
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ for (let i = 0; i <= this.options.totalRows / this.options.pipelineSize; i++) {
|
|
|
|
|
+ const lower = i * this.options.pipelineSize
|
|
|
|
|
|
|
|
-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
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- let data = {}
|
|
|
|
|
- const index = this.header.fields.indexOf(this.options.sortName)
|
|
|
|
|
-
|
|
|
|
|
- let params = {
|
|
|
|
|
- searchText: this.searchText,
|
|
|
|
|
- sortName: this.options.sortName,
|
|
|
|
|
- sortOrder: this.options.sortOrder
|
|
|
|
|
|
|
+ this.cacheWindows[i] = { lower, upper: lower + this.options.pipelineSize - 1 }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let request = null
|
|
|
|
|
|
|
+ // set the current cache window index, based on where the current offset falls
|
|
|
|
|
+ setCurrWindow (offset) {
|
|
|
|
|
+ this.currWindow = 0
|
|
|
|
|
|
|
|
- if (this.header.sortNames[index]) {
|
|
|
|
|
- params.sortName = this.header.sortNames[index]
|
|
|
|
|
|
|
+ for (let i = 0; i < this.cacheWindows.length; i++) {
|
|
|
|
|
+ if (this.cacheWindows[i].lower <= offset && offset <= this.cacheWindows[i].upper) {
|
|
|
|
|
+ this.currWindow = i
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- 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
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // draw rows from the cache using offset and limit
|
|
|
|
|
+ drawFromCache (offset, limit) {
|
|
|
|
|
+ const res = Utils.extend(true, {}, this.cacheRequestJSON)
|
|
|
|
|
+ const drawStart = offset - this.cacheWindows[this.currWindow].lower
|
|
|
|
|
+ const drawEnd = drawStart + limit
|
|
|
|
|
|
|
|
- if (!(url || this.options.url) && !this.options.ajax) {
|
|
|
|
|
- return
|
|
|
|
|
|
|
+ res.rows = res.rows.slice(drawStart, drawEnd)
|
|
|
|
|
+ return res
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let useAjax = true
|
|
|
|
|
-
|
|
|
|
|
- if (this.options.queryParamsType === 'limit') {
|
|
|
|
|
- params = {
|
|
|
|
|
- searchText: params.searchText,
|
|
|
|
|
- sortName: params.sortName,
|
|
|
|
|
- sortOrder: params.sortOrder
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * 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
|
|
|
|
|
+ */
|
|
|
|
|
+ initServer (silent, query, url) {
|
|
|
|
|
+ if (!this.options.usePipeline) {
|
|
|
|
|
+ return super.initServer(silent, query, url)
|
|
|
}
|
|
}
|
|
|
- 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) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let useAjax = true
|
|
|
|
|
+ const params = {}
|
|
|
|
|
+
|
|
|
|
|
+ if (
|
|
|
|
|
+ this.options.queryParamsType === 'limit' &&
|
|
|
|
|
+ this.options.pagination &&
|
|
|
|
|
+ this.options.sidePagination === 'server'
|
|
|
|
|
+ ) {
|
|
|
|
|
+ // same as parent initServer: params.offset
|
|
|
|
|
+ params.offset = this.options.pageSize === this.options.formatAllRows() ?
|
|
|
|
|
+ 0 : this.options.pageSize * (this.options.pageNumber - 1)
|
|
|
|
|
+ params.limit = this.options.pageSize
|
|
|
|
|
+
|
|
|
|
|
+ // 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 {
|
|
|
|
|
+ const 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 re-query for preceding or succeeding cache window
|
|
|
|
|
+ // also handle case
|
|
|
|
|
+ if (this.resetCache || (params.offset < w.lower || params.offset > w.upper)) {
|
|
|
useAjax = true
|
|
useAjax = true
|
|
|
|
|
+ this.setCurrWindow(params.offset)
|
|
|
|
|
+ // store the relative offset for drawing the page data afterwards
|
|
|
params.drawOffset = params.offset
|
|
params.drawOffset = params.offset
|
|
|
- // cache exists: determine if the page request is entirely within the current cached window
|
|
|
|
|
|
|
+ // 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 {
|
|
} else {
|
|
|
- const 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 re-query 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
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 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) {
|
|
|
|
|
- const res = this.drawFromCache(params.offset, params.limit)
|
|
|
|
|
|
|
+ // force an ajax call - this is on search, sort or page size change
|
|
|
|
|
+ if (this.resetCache) {
|
|
|
|
|
+ useAjax = true
|
|
|
|
|
+ this.resetCache = false
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- 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)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (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
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- data = Utils.calculateObjectValue(this.options, this.options.queryParams, [params], data)
|
|
|
|
|
|
|
+ // cached results can be used
|
|
|
|
|
+ if (!useAjax) {
|
|
|
|
|
+ const res = this.drawFromCache(params.offset, params.limit)
|
|
|
|
|
|
|
|
- Utils.extend(data, query || {})
|
|
|
|
|
|
|
+ this.load(res)
|
|
|
|
|
+ this.trigger('load-success', res)
|
|
|
|
|
+ this.trigger('cached-data-hit', res)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // false to stop request
|
|
|
|
|
- if (data === false) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!this.pipelineResponseHandler) {
|
|
|
|
|
+ this.pipelineResponseHandler = this.options.responseHandler
|
|
|
|
|
|
|
|
- if (!silent) {
|
|
|
|
|
- this.$showLoading()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ this.options.responseHandler = (_res, jqXHR) => {
|
|
|
|
|
+ let res = Utils.calculateObjectValue(this.options, this.pipelineResponseHandler, [_res, jqXHR], _res)
|
|
|
|
|
|
|
|
- request = Utils.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: res => {
|
|
|
|
|
- res = Utils.calculateObjectValue(this.options, this.options.responseHandler, [res], res)
|
|
|
|
|
- // cache results if using pipelining
|
|
|
|
|
- if (this.options.usePipeline) {
|
|
|
|
|
// store entire request in cache
|
|
// store entire request in cache
|
|
|
this.cacheRequestJSON = Utils.extend(true, {}, res)
|
|
this.cacheRequestJSON = Utils.extend(true, {}, res)
|
|
|
// this gets set in load() also but needs to be set before
|
|
// this gets set in load() also but needs to be set before
|
|
@@ -292,39 +243,20 @@ BootstrapTable.prototype.initServer = function (silent, query, url) {
|
|
|
// so cache windows need to be rebuilt. Otherwise it
|
|
// so cache windows need to be rebuilt. Otherwise it
|
|
|
// will come out the same
|
|
// will come out the same
|
|
|
this.setCacheWindows()
|
|
this.setCacheWindows()
|
|
|
- this.setCurrWindow(params.drawOffset)
|
|
|
|
|
// just load data for the page
|
|
// just load data for the page
|
|
|
res = this.drawFromCache(params.drawOffset, params.drawLimit)
|
|
res = this.drawFromCache(params.drawOffset, params.drawLimit)
|
|
|
this.trigger('cached-data-reset', res)
|
|
this.trigger('cached-data-reset', res)
|
|
|
- }
|
|
|
|
|
- this.load(res)
|
|
|
|
|
- this.trigger('load-success', res)
|
|
|
|
|
- if (!silent) {
|
|
|
|
|
- this.hideLoading()
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- error: res => {
|
|
|
|
|
- let data = []
|
|
|
|
|
-
|
|
|
|
|
- if (this.options.sidePagination === 'server') {
|
|
|
|
|
- data = {}
|
|
|
|
|
- data[this.options.totalField] = 0
|
|
|
|
|
- data[this.options.dataField] = []
|
|
|
|
|
- }
|
|
|
|
|
- this.load(data)
|
|
|
|
|
- this.trigger('load-error', res.status, res)
|
|
|
|
|
- if (!silent) {
|
|
|
|
|
- this.hideLoading()
|
|
|
|
|
|
|
+ return res
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
|
|
|
|
|
- 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)
|
|
|
|
|
|
|
+ return super.initServer(silent, { ...query, ...params }, url)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ destroy (...args) {
|
|
|
|
|
+ this.options.responseHandler = this.pipelineResponseHandler
|
|
|
|
|
+ this.pipelineResponseHandler = null
|
|
|
|
|
+
|
|
|
|
|
+ super.destroy(...args)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|