| 6577 |
dpurdie |
1 |
/*! Scroller 1.5.0
|
|
|
2 |
* ©2011-2018 SpryMedia Ltd - datatables.net/license
|
|
|
3 |
*/
|
|
|
4 |
|
|
|
5 |
/**
|
|
|
6 |
* @summary Scroller
|
|
|
7 |
* @description Virtual rendering for DataTables
|
|
|
8 |
* @version 1.5.0
|
|
|
9 |
* @file dataTables.scroller.js
|
|
|
10 |
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
|
|
11 |
* @contact www.sprymedia.co.uk/contact
|
|
|
12 |
* @copyright Copyright 2011-2018 SpryMedia Ltd.
|
|
|
13 |
*
|
|
|
14 |
* This source file is free software, available under the following license:
|
|
|
15 |
* MIT license - http://datatables.net/license/mit
|
|
|
16 |
*
|
|
|
17 |
* This source file is distributed in the hope that it will be useful, but
|
|
|
18 |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
19 |
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
|
|
|
20 |
*
|
|
|
21 |
* For details please refer to: http://www.datatables.net
|
|
|
22 |
*/
|
|
|
23 |
|
|
|
24 |
(function( factory ){
|
|
|
25 |
if ( typeof define === 'function' && define.amd ) {
|
|
|
26 |
// AMD
|
|
|
27 |
define( ['jquery', 'datatables.net'], function ( $ ) {
|
|
|
28 |
return factory( $, window, document );
|
|
|
29 |
} );
|
|
|
30 |
}
|
|
|
31 |
else if ( typeof exports === 'object' ) {
|
|
|
32 |
// CommonJS
|
|
|
33 |
module.exports = function (root, $) {
|
|
|
34 |
if ( ! root ) {
|
|
|
35 |
root = window;
|
|
|
36 |
}
|
|
|
37 |
|
|
|
38 |
if ( ! $ || ! $.fn.dataTable ) {
|
|
|
39 |
$ = require('datatables.net')(root, $).$;
|
|
|
40 |
}
|
|
|
41 |
|
|
|
42 |
return factory( $, root, root.document );
|
|
|
43 |
};
|
|
|
44 |
}
|
|
|
45 |
else {
|
|
|
46 |
// Browser
|
|
|
47 |
factory( jQuery, window, document );
|
|
|
48 |
}
|
|
|
49 |
}(function( $, window, document, undefined ) {
|
|
|
50 |
'use strict';
|
|
|
51 |
var DataTable = $.fn.dataTable;
|
|
|
52 |
|
|
|
53 |
|
|
|
54 |
/**
|
|
|
55 |
* Scroller is a virtual rendering plug-in for DataTables which allows large
|
|
|
56 |
* datasets to be drawn on screen every quickly. What the virtual rendering means
|
|
|
57 |
* is that only the visible portion of the table (and a bit to either side to make
|
|
|
58 |
* the scrolling smooth) is drawn, while the scrolling container gives the
|
|
|
59 |
* visual impression that the whole table is visible. This is done by making use
|
|
|
60 |
* of the pagination abilities of DataTables and moving the table around in the
|
|
|
61 |
* scrolling container DataTables adds to the page. The scrolling container is
|
|
|
62 |
* forced to the height it would be for the full table display using an extra
|
|
|
63 |
* element.
|
|
|
64 |
*
|
|
|
65 |
* Note that rows in the table MUST all be the same height. Information in a cell
|
|
|
66 |
* which expands on to multiple lines will cause some odd behaviour in the scrolling.
|
|
|
67 |
*
|
|
|
68 |
* Scroller is initialised by simply including the letter 'S' in the sDom for the
|
|
|
69 |
* table you want to have this feature enabled on. Note that the 'S' must come
|
|
|
70 |
* AFTER the 't' parameter in `dom`.
|
|
|
71 |
*
|
|
|
72 |
* Key features include:
|
|
|
73 |
* <ul class="limit_length">
|
|
|
74 |
* <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
|
|
|
75 |
* <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
|
|
|
76 |
* <li>Display millions of rows</li>
|
|
|
77 |
* <li>Integration with state saving in DataTables (scrolling position is saved)</li>
|
|
|
78 |
* <li>Easy to use</li>
|
|
|
79 |
* </ul>
|
|
|
80 |
*
|
|
|
81 |
* @class
|
|
|
82 |
* @constructor
|
|
|
83 |
* @global
|
|
|
84 |
* @param {object} dt DataTables settings object or API instance
|
|
|
85 |
* @param {object} [opts={}] Configuration object for FixedColumns. Options
|
|
|
86 |
* are defined by {@link Scroller.defaults}
|
|
|
87 |
*
|
|
|
88 |
* @requires jQuery 1.7+
|
|
|
89 |
* @requires DataTables 1.10.0+
|
|
|
90 |
*
|
|
|
91 |
* @example
|
|
|
92 |
* $(document).ready(function() {
|
|
|
93 |
* $('#example').DataTable( {
|
|
|
94 |
* "scrollY": "200px",
|
|
|
95 |
* "ajax": "media/dataset/large.txt",
|
|
|
96 |
* "dom": "frtiS",
|
|
|
97 |
* "deferRender": true
|
|
|
98 |
* } );
|
|
|
99 |
* } );
|
|
|
100 |
*/
|
|
|
101 |
var Scroller = function ( dt, opts ) {
|
|
|
102 |
/* Sanity check - you just know it will happen */
|
|
|
103 |
if ( ! (this instanceof Scroller) ) {
|
|
|
104 |
alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
|
|
|
105 |
return;
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
if ( opts === undefined ) {
|
|
|
109 |
opts = {};
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
var dtApi = $.fn.dataTable.Api( dt );
|
|
|
113 |
|
|
|
114 |
/**
|
|
|
115 |
* Settings object which contains customisable information for the Scroller instance
|
|
|
116 |
* @namespace
|
|
|
117 |
* @private
|
|
|
118 |
* @extends Scroller.defaults
|
|
|
119 |
*/
|
|
|
120 |
this.s = {
|
|
|
121 |
/**
|
|
|
122 |
* DataTables settings object
|
|
|
123 |
* @type object
|
|
|
124 |
* @default Passed in as first parameter to constructor
|
|
|
125 |
*/
|
|
|
126 |
"dt": dtApi.settings()[0],
|
|
|
127 |
|
|
|
128 |
/**
|
|
|
129 |
* DataTables API instance
|
|
|
130 |
* @type DataTable.Api
|
|
|
131 |
*/
|
|
|
132 |
"dtApi": dtApi,
|
|
|
133 |
|
|
|
134 |
/**
|
|
|
135 |
* Pixel location of the top of the drawn table in the viewport
|
|
|
136 |
* @type int
|
|
|
137 |
* @default 0
|
|
|
138 |
*/
|
|
|
139 |
"tableTop": 0,
|
|
|
140 |
|
|
|
141 |
/**
|
|
|
142 |
* Pixel location of the bottom of the drawn table in the viewport
|
|
|
143 |
* @type int
|
|
|
144 |
* @default 0
|
|
|
145 |
*/
|
|
|
146 |
"tableBottom": 0,
|
|
|
147 |
|
|
|
148 |
/**
|
|
|
149 |
* Pixel location of the boundary for when the next data set should be loaded and drawn
|
|
|
150 |
* when scrolling up the way.
|
|
|
151 |
* @type int
|
|
|
152 |
* @default 0
|
|
|
153 |
* @private
|
|
|
154 |
*/
|
|
|
155 |
"redrawTop": 0,
|
|
|
156 |
|
|
|
157 |
/**
|
|
|
158 |
* Pixel location of the boundary for when the next data set should be loaded and drawn
|
|
|
159 |
* when scrolling down the way. Note that this is actually calculated as the offset from
|
|
|
160 |
* the top.
|
|
|
161 |
* @type int
|
|
|
162 |
* @default 0
|
|
|
163 |
* @private
|
|
|
164 |
*/
|
|
|
165 |
"redrawBottom": 0,
|
|
|
166 |
|
|
|
167 |
/**
|
|
|
168 |
* Auto row height or not indicator
|
|
|
169 |
* @type bool
|
|
|
170 |
* @default 0
|
|
|
171 |
*/
|
|
|
172 |
"autoHeight": true,
|
|
|
173 |
|
|
|
174 |
/**
|
|
|
175 |
* Number of rows calculated as visible in the visible viewport
|
|
|
176 |
* @type int
|
|
|
177 |
* @default 0
|
|
|
178 |
*/
|
|
|
179 |
"viewportRows": 0,
|
|
|
180 |
|
|
|
181 |
/**
|
|
|
182 |
* setTimeout reference for state saving, used when state saving is enabled in the DataTable
|
|
|
183 |
* and when the user scrolls the viewport in order to stop the cookie set taking too much
|
|
|
184 |
* CPU!
|
|
|
185 |
* @type int
|
|
|
186 |
* @default 0
|
|
|
187 |
*/
|
|
|
188 |
"stateTO": null,
|
|
|
189 |
|
|
|
190 |
/**
|
|
|
191 |
* setTimeout reference for the redraw, used when server-side processing is enabled in the
|
|
|
192 |
* DataTables in order to prevent DoSing the server
|
|
|
193 |
* @type int
|
|
|
194 |
* @default null
|
|
|
195 |
*/
|
|
|
196 |
"drawTO": null,
|
|
|
197 |
|
|
|
198 |
heights: {
|
|
|
199 |
jump: null,
|
|
|
200 |
page: null,
|
|
|
201 |
virtual: null,
|
|
|
202 |
scroll: null,
|
|
|
203 |
|
|
|
204 |
/**
|
|
|
205 |
* Height of rows in the table
|
|
|
206 |
* @type int
|
|
|
207 |
* @default 0
|
|
|
208 |
*/
|
|
|
209 |
row: null,
|
|
|
210 |
|
|
|
211 |
/**
|
|
|
212 |
* Pixel height of the viewport
|
|
|
213 |
* @type int
|
|
|
214 |
* @default 0
|
|
|
215 |
*/
|
|
|
216 |
viewport: null
|
|
|
217 |
},
|
|
|
218 |
|
|
|
219 |
topRowFloat: 0,
|
|
|
220 |
scrollDrawDiff: null,
|
|
|
221 |
loaderVisible: false,
|
|
|
222 |
forceReposition: false
|
|
|
223 |
};
|
|
|
224 |
|
|
|
225 |
// @todo The defaults should extend a `c` property and the internal settings
|
|
|
226 |
// only held in the `s` property. At the moment they are mixed
|
|
|
227 |
this.s = $.extend( this.s, Scroller.oDefaults, opts );
|
|
|
228 |
|
|
|
229 |
// Workaround for row height being read from height object (see above comment)
|
|
|
230 |
this.s.heights.row = this.s.rowHeight;
|
|
|
231 |
|
|
|
232 |
/**
|
|
|
233 |
* DOM elements used by the class instance
|
|
|
234 |
* @private
|
|
|
235 |
* @namespace
|
|
|
236 |
*
|
|
|
237 |
*/
|
|
|
238 |
this.dom = {
|
|
|
239 |
"force": document.createElement('div'),
|
|
|
240 |
"scroller": null,
|
|
|
241 |
"table": null,
|
|
|
242 |
"loader": null
|
|
|
243 |
};
|
|
|
244 |
|
|
|
245 |
// Attach the instance to the DataTables instance so it can be accessed in
|
|
|
246 |
// future. Don't initialise Scroller twice on the same table
|
|
|
247 |
if ( this.s.dt.oScroller ) {
|
|
|
248 |
return;
|
|
|
249 |
}
|
|
|
250 |
|
|
|
251 |
this.s.dt.oScroller = this;
|
|
|
252 |
|
|
|
253 |
/* Let's do it */
|
|
|
254 |
this._fnConstruct();
|
|
|
255 |
};
|
|
|
256 |
|
|
|
257 |
|
|
|
258 |
|
|
|
259 |
$.extend( Scroller.prototype, {
|
|
|
260 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
261 |
* Public methods
|
|
|
262 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
263 |
|
|
|
264 |
/**
|
|
|
265 |
* Calculate the pixel position from the top of the scrolling container for
|
|
|
266 |
* a given row
|
|
|
267 |
* @param {int} iRow Row number to calculate the position of
|
|
|
268 |
* @returns {int} Pixels
|
|
|
269 |
* @example
|
|
|
270 |
* $(document).ready(function() {
|
|
|
271 |
* $('#example').dataTable( {
|
|
|
272 |
* "sScrollY": "200px",
|
|
|
273 |
* "sAjaxSource": "media/dataset/large.txt",
|
|
|
274 |
* "sDom": "frtiS",
|
|
|
275 |
* "bDeferRender": true,
|
|
|
276 |
* "fnInitComplete": function (o) {
|
|
|
277 |
* // Find where row 25 is
|
|
|
278 |
* alert( o.oScroller.fnRowToPixels( 25 ) );
|
|
|
279 |
* }
|
|
|
280 |
* } );
|
|
|
281 |
* } );
|
|
|
282 |
*/
|
|
|
283 |
"fnRowToPixels": function ( rowIdx, intParse, virtual )
|
|
|
284 |
{
|
|
|
285 |
var pixels;
|
|
|
286 |
var diff = rowIdx - this.s.baseRowTop;
|
|
|
287 |
|
|
|
288 |
if ( virtual ) {
|
|
|
289 |
pixels = this._domain( 'virtualToPhysical', this.s.baseScrollTop );
|
|
|
290 |
pixels += diff * this.s.heights.row;
|
|
|
291 |
}
|
|
|
292 |
else {
|
|
|
293 |
pixels = this.s.baseScrollTop;
|
|
|
294 |
pixels += diff * this.s.heights.row;
|
|
|
295 |
}
|
|
|
296 |
|
|
|
297 |
return intParse || intParse === undefined ?
|
|
|
298 |
parseInt( pixels, 10 ) :
|
|
|
299 |
pixels;
|
|
|
300 |
},
|
|
|
301 |
|
|
|
302 |
|
|
|
303 |
/**
|
|
|
304 |
* Calculate the row number that will be found at the given pixel position
|
|
|
305 |
* (y-scroll).
|
|
|
306 |
*
|
|
|
307 |
* Please note that when the height of the full table exceeds 1 million
|
|
|
308 |
* pixels, Scroller switches into a non-linear mode for the scrollbar to fit
|
|
|
309 |
* all of the records into a finite area, but this function returns a linear
|
|
|
310 |
* value (relative to the last non-linear positioning).
|
|
|
311 |
* @param {int} iPixels Offset from top to calculate the row number of
|
|
|
312 |
* @param {int} [intParse=true] If an integer value should be returned
|
|
|
313 |
* @param {int} [virtual=false] Perform the calculations in the virtual domain
|
|
|
314 |
* @returns {int} Row index
|
|
|
315 |
* @example
|
|
|
316 |
* $(document).ready(function() {
|
|
|
317 |
* $('#example').dataTable( {
|
|
|
318 |
* "sScrollY": "200px",
|
|
|
319 |
* "sAjaxSource": "media/dataset/large.txt",
|
|
|
320 |
* "sDom": "frtiS",
|
|
|
321 |
* "bDeferRender": true,
|
|
|
322 |
* "fnInitComplete": function (o) {
|
|
|
323 |
* // Find what row number is at 500px
|
|
|
324 |
* alert( o.oScroller.fnPixelsToRow( 500 ) );
|
|
|
325 |
* }
|
|
|
326 |
* } );
|
|
|
327 |
* } );
|
|
|
328 |
*/
|
|
|
329 |
"fnPixelsToRow": function ( pixels, intParse, virtual )
|
|
|
330 |
{
|
|
|
331 |
var diff = pixels - this.s.baseScrollTop;
|
|
|
332 |
var row = virtual ?
|
|
|
333 |
(this._domain( 'physicalToVirtual', this.s.baseScrollTop ) + diff) / this.s.heights.row :
|
|
|
334 |
( diff / this.s.heights.row ) + this.s.baseRowTop;
|
|
|
335 |
|
|
|
336 |
return intParse || intParse === undefined ?
|
|
|
337 |
parseInt( row, 10 ) :
|
|
|
338 |
row;
|
|
|
339 |
},
|
|
|
340 |
|
|
|
341 |
|
|
|
342 |
/**
|
|
|
343 |
* Calculate the row number that will be found at the given pixel position (y-scroll)
|
|
|
344 |
* @param {int} iRow Row index to scroll to
|
|
|
345 |
* @param {bool} [bAnimate=true] Animate the transition or not
|
|
|
346 |
* @returns {void}
|
|
|
347 |
* @example
|
|
|
348 |
* $(document).ready(function() {
|
|
|
349 |
* $('#example').dataTable( {
|
|
|
350 |
* "sScrollY": "200px",
|
|
|
351 |
* "sAjaxSource": "media/dataset/large.txt",
|
|
|
352 |
* "sDom": "frtiS",
|
|
|
353 |
* "bDeferRender": true,
|
|
|
354 |
* "fnInitComplete": function (o) {
|
|
|
355 |
* // Immediately scroll to row 1000
|
|
|
356 |
* o.oScroller.fnScrollToRow( 1000 );
|
|
|
357 |
* }
|
|
|
358 |
* } );
|
|
|
359 |
*
|
|
|
360 |
* // Sometime later on use the following to scroll to row 500...
|
|
|
361 |
* var oSettings = $('#example').dataTable().fnSettings();
|
|
|
362 |
* oSettings.oScroller.fnScrollToRow( 500 );
|
|
|
363 |
* } );
|
|
|
364 |
*/
|
|
|
365 |
"fnScrollToRow": function ( iRow, bAnimate )
|
|
|
366 |
{
|
|
|
367 |
var that = this;
|
|
|
368 |
var ani = false;
|
|
|
369 |
var px = this.fnRowToPixels( iRow );
|
|
|
370 |
|
|
|
371 |
// We need to know if the table will redraw or not before doing the
|
|
|
372 |
// scroll. If it will not redraw, then we need to use the currently
|
|
|
373 |
// displayed table, and scroll with the physical pixels. Otherwise, we
|
|
|
374 |
// need to calculate the table's new position from the virtual
|
|
|
375 |
// transform.
|
|
|
376 |
var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
|
|
|
377 |
var drawRow = iRow - preRows;
|
|
|
378 |
if ( drawRow < 0 ) {
|
|
|
379 |
drawRow = 0;
|
|
|
380 |
}
|
|
|
381 |
|
|
|
382 |
if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
|
|
|
383 |
ani = true;
|
|
|
384 |
px = this._domain( 'virtualToPhysical', iRow * this.s.heights.row );
|
|
|
385 |
|
|
|
386 |
// If we need records outside the current draw region, but the new
|
|
|
387 |
// scrolling position is inside that (due to the non-linear nature
|
|
|
388 |
// for larger numbers of records), we need to force position update.
|
|
|
389 |
if ( this.s.redrawTop < px && px < this.s.redrawBottom ) {
|
|
|
390 |
this.s.forceReposition = true;
|
|
|
391 |
bAnimate = false;
|
|
|
392 |
}
|
|
|
393 |
}
|
|
|
394 |
|
|
|
395 |
if ( typeof bAnimate == 'undefined' || bAnimate )
|
|
|
396 |
{
|
|
|
397 |
this.s.ani = ani;
|
|
|
398 |
$(this.dom.scroller).animate( {
|
|
|
399 |
"scrollTop": px
|
|
|
400 |
}, function () {
|
|
|
401 |
// This needs to happen after the animation has completed and
|
|
|
402 |
// the final scroll event fired
|
|
|
403 |
setTimeout( function () {
|
|
|
404 |
that.s.ani = false;
|
|
|
405 |
}, 25 );
|
|
|
406 |
} );
|
|
|
407 |
}
|
|
|
408 |
else
|
|
|
409 |
{
|
|
|
410 |
$(this.dom.scroller).scrollTop( px );
|
|
|
411 |
}
|
|
|
412 |
},
|
|
|
413 |
|
|
|
414 |
|
|
|
415 |
/**
|
|
|
416 |
* Calculate and store information about how many rows are to be displayed
|
|
|
417 |
* in the scrolling viewport, based on current dimensions in the browser's
|
|
|
418 |
* rendering. This can be particularly useful if the table is initially
|
|
|
419 |
* drawn in a hidden element - for example in a tab.
|
|
|
420 |
* @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
|
|
|
421 |
* the new dimensions forming the basis for the draw.
|
|
|
422 |
* @returns {void}
|
|
|
423 |
* @example
|
|
|
424 |
* $(document).ready(function() {
|
|
|
425 |
* // Make the example container hidden to throw off the browser's sizing
|
|
|
426 |
* document.getElementById('container').style.display = "none";
|
|
|
427 |
* var oTable = $('#example').dataTable( {
|
|
|
428 |
* "sScrollY": "200px",
|
|
|
429 |
* "sAjaxSource": "media/dataset/large.txt",
|
|
|
430 |
* "sDom": "frtiS",
|
|
|
431 |
* "bDeferRender": true,
|
|
|
432 |
* "fnInitComplete": function (o) {
|
|
|
433 |
* // Immediately scroll to row 1000
|
|
|
434 |
* o.oScroller.fnScrollToRow( 1000 );
|
|
|
435 |
* }
|
|
|
436 |
* } );
|
|
|
437 |
*
|
|
|
438 |
* setTimeout( function () {
|
|
|
439 |
* // Make the example container visible and recalculate the scroller sizes
|
|
|
440 |
* document.getElementById('container').style.display = "block";
|
|
|
441 |
* oTable.fnSettings().oScroller.fnMeasure();
|
|
|
442 |
* }, 3000 );
|
|
|
443 |
*/
|
|
|
444 |
"fnMeasure": function ( bRedraw )
|
|
|
445 |
{
|
|
|
446 |
if ( this.s.autoHeight )
|
|
|
447 |
{
|
|
|
448 |
this._fnCalcRowHeight();
|
|
|
449 |
}
|
|
|
450 |
|
|
|
451 |
var heights = this.s.heights;
|
|
|
452 |
|
|
|
453 |
if ( heights.row ) {
|
|
|
454 |
heights.viewport = $.contains(document, this.dom.scroller) ?
|
|
|
455 |
$(this.dom.scroller).height() :
|
|
|
456 |
this._parseHeight($(this.dom.scroller).css('height'));
|
|
|
457 |
|
|
|
458 |
// If collapsed (no height) use the max-height parameter
|
|
|
459 |
if ( ! heights.viewport ) {
|
|
|
460 |
heights.viewport = this._parseHeight($(this.dom.scroller).css('max-height'));
|
|
|
461 |
}
|
|
|
462 |
|
|
|
463 |
this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
|
|
|
464 |
this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
|
|
|
465 |
}
|
|
|
466 |
|
|
|
467 |
if ( bRedraw === undefined || bRedraw )
|
|
|
468 |
{
|
|
|
469 |
this.s.dt.oInstance.fnDraw( false );
|
|
|
470 |
}
|
|
|
471 |
},
|
|
|
472 |
|
|
|
473 |
|
|
|
474 |
/**
|
|
|
475 |
* Get information about current displayed record range. This corresponds to
|
|
|
476 |
* the information usually displayed in the "Info" block of the table.
|
|
|
477 |
*
|
|
|
478 |
* @returns {object} info as an object:
|
|
|
479 |
* {
|
|
|
480 |
* start: {int}, // the 0-indexed record at the top of the viewport
|
|
|
481 |
* end: {int}, // the 0-indexed record at the bottom of the viewport
|
|
|
482 |
* }
|
|
|
483 |
*/
|
|
|
484 |
"fnPageInfo": function()
|
|
|
485 |
{
|
|
|
486 |
var
|
|
|
487 |
dt = this.s.dt,
|
|
|
488 |
iScrollTop = this.dom.scroller.scrollTop,
|
|
|
489 |
iTotal = dt.fnRecordsDisplay(),
|
|
|
490 |
iPossibleEnd = Math.ceil(this.fnPixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
|
|
|
491 |
|
|
|
492 |
return {
|
|
|
493 |
start: Math.floor(this.fnPixelsToRow(iScrollTop, false, this.s.ani)),
|
|
|
494 |
end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
|
|
|
495 |
};
|
|
|
496 |
},
|
|
|
497 |
|
|
|
498 |
|
|
|
499 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
500 |
* Private methods (they are of course public in JS, but recommended as private)
|
|
|
501 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
502 |
|
|
|
503 |
/**
|
|
|
504 |
* Initialisation for Scroller
|
|
|
505 |
* @returns {void}
|
|
|
506 |
* @private
|
|
|
507 |
*/
|
|
|
508 |
"_fnConstruct": function ()
|
|
|
509 |
{
|
|
|
510 |
var that = this;
|
|
|
511 |
var dt = this.s.dtApi;
|
|
|
512 |
|
|
|
513 |
/* Sanity check */
|
|
|
514 |
if ( !this.s.dt.oFeatures.bPaginate ) {
|
|
|
515 |
this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
|
|
|
516 |
return;
|
|
|
517 |
}
|
|
|
518 |
|
|
|
519 |
/* Insert a div element that we can use to force the DT scrolling container to
|
|
|
520 |
* the height that would be required if the whole table was being displayed
|
|
|
521 |
*/
|
|
|
522 |
this.dom.force.style.position = "relative";
|
|
|
523 |
this.dom.force.style.top = "0px";
|
|
|
524 |
this.dom.force.style.left = "0px";
|
|
|
525 |
this.dom.force.style.width = "1px";
|
|
|
526 |
|
|
|
527 |
this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
|
|
|
528 |
this.dom.scroller.appendChild( this.dom.force );
|
|
|
529 |
this.dom.scroller.style.position = "relative";
|
|
|
530 |
|
|
|
531 |
this.dom.table = $('>table', this.dom.scroller)[0];
|
|
|
532 |
this.dom.table.style.position = "absolute";
|
|
|
533 |
this.dom.table.style.top = "0px";
|
|
|
534 |
this.dom.table.style.left = "0px";
|
|
|
535 |
|
|
|
536 |
// Add class to 'announce' that we are a Scroller table
|
|
|
537 |
$(dt.table().container()).addClass('DTS');
|
|
|
538 |
|
|
|
539 |
// Add a 'loading' indicator
|
|
|
540 |
if ( this.s.loadingIndicator )
|
|
|
541 |
{
|
|
|
542 |
this.dom.loader = $('<div class="dataTables_processing DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
|
|
|
543 |
.css('display', 'none');
|
|
|
544 |
|
|
|
545 |
$(this.dom.scroller.parentNode)
|
|
|
546 |
.css('position', 'relative')
|
|
|
547 |
.append( this.dom.loader );
|
|
|
548 |
}
|
|
|
549 |
|
|
|
550 |
/* Initial size calculations */
|
|
|
551 |
if ( this.s.heights.row && this.s.heights.row != 'auto' )
|
|
|
552 |
{
|
|
|
553 |
this.s.autoHeight = false;
|
|
|
554 |
}
|
|
|
555 |
this.fnMeasure( false );
|
|
|
556 |
|
|
|
557 |
// Scrolling callback to see if a page change is needed - use a throttled
|
|
|
558 |
// function for the save save callback so we aren't hitting it on every
|
|
|
559 |
// scroll
|
|
|
560 |
this.s.ingnoreScroll = true;
|
|
|
561 |
this.s.stateSaveThrottle = this.s.dt.oApi._fnThrottle( function () {
|
|
|
562 |
that.s.dtApi.state.save();
|
|
|
563 |
}, 500 );
|
|
|
564 |
$(this.dom.scroller).on( 'scroll.dt-scroller', function (e) {
|
|
|
565 |
that._fnScroll.call( that );
|
|
|
566 |
} );
|
|
|
567 |
|
|
|
568 |
// In iOS we catch the touchstart event in case the user tries to scroll
|
|
|
569 |
// while the display is already scrolling
|
|
|
570 |
$(this.dom.scroller).on('touchstart.dt-scroller', function () {
|
|
|
571 |
that._fnScroll.call( that );
|
|
|
572 |
} );
|
|
|
573 |
|
|
|
574 |
// On resize, update the information element, since the number of rows shown might change
|
|
|
575 |
$(window).on( 'resize.dt-scroller', function () {
|
|
|
576 |
that.fnMeasure( false );
|
|
|
577 |
that._fnInfo();
|
|
|
578 |
} );
|
|
|
579 |
|
|
|
580 |
// Add a state saving parameter to the DT state saving so we can restore the exact
|
|
|
581 |
// position of the scrolling. Slightly surprisingly the scroll position isn't actually
|
|
|
582 |
// stored, but rather tha base units which are needed to calculate it. This allows for
|
|
|
583 |
// virtual scrolling as well.
|
|
|
584 |
var initialStateSave = true;
|
|
|
585 |
var loadedState = dt.state.loaded();
|
|
|
586 |
|
|
|
587 |
dt.on( 'stateSaveParams.scroller', function ( e, settings, data ) {
|
|
|
588 |
// Need to used the saved position on init
|
|
|
589 |
data.scroller = {
|
|
|
590 |
topRow: initialStateSave && loadedState && loadedState.scroller ?
|
|
|
591 |
loadedState.scroller.topRow :
|
|
|
592 |
that.s.topRowFloat,
|
|
|
593 |
baseScrollTop: that.s.baseScrollTop,
|
|
|
594 |
baseRowTop: that.s.baseRowTop
|
|
|
595 |
};
|
|
|
596 |
|
|
|
597 |
initialStateSave = false;
|
|
|
598 |
} );
|
|
|
599 |
|
|
|
600 |
if ( loadedState && loadedState.scroller ) {
|
|
|
601 |
this.s.topRowFloat = loadedState.scroller.topRow;
|
|
|
602 |
this.s.baseScrollTop = loadedState.scroller.baseScrollTop;
|
|
|
603 |
this.s.baseRowTop = loadedState.scroller.baseRowTop;
|
|
|
604 |
}
|
|
|
605 |
|
|
|
606 |
dt.on( 'init.scroller', function () {
|
|
|
607 |
that.fnMeasure( false );
|
|
|
608 |
|
|
|
609 |
that._fnDrawCallback();
|
|
|
610 |
|
|
|
611 |
// Update the scroller when the DataTable is redrawn
|
|
|
612 |
dt.on( 'draw.scroller', function () {
|
|
|
613 |
that._fnDrawCallback();
|
|
|
614 |
});
|
|
|
615 |
} );
|
|
|
616 |
|
|
|
617 |
// Set height before the draw happens, allowing everything else to update
|
|
|
618 |
// on draw complete without worry for roder.
|
|
|
619 |
dt.on( 'preDraw.dt.scroller', function () {
|
|
|
620 |
that._fnScrollForce();
|
|
|
621 |
} );
|
|
|
622 |
|
|
|
623 |
// Destructor
|
|
|
624 |
dt.on( 'destroy.scroller', function () {
|
|
|
625 |
$(window).off( 'resize.dt-scroller' );
|
|
|
626 |
$(that.dom.scroller).off('.dt-scroller');
|
|
|
627 |
$(that.s.dt.nTable).off( '.scroller' );
|
|
|
628 |
|
|
|
629 |
$(that.s.dt.nTableWrapper).removeClass('DTS');
|
|
|
630 |
$('div.DTS_Loading', that.dom.scroller.parentNode).remove();
|
|
|
631 |
|
|
|
632 |
that.dom.table.style.position = "";
|
|
|
633 |
that.dom.table.style.top = "";
|
|
|
634 |
that.dom.table.style.left = "";
|
|
|
635 |
} );
|
|
|
636 |
},
|
|
|
637 |
|
|
|
638 |
|
|
|
639 |
/**
|
|
|
640 |
* Scrolling function - fired whenever the scrolling position is changed.
|
|
|
641 |
* This method needs to use the stored values to see if the table should be
|
|
|
642 |
* redrawn as we are moving towards the end of the information that is
|
|
|
643 |
* currently drawn or not. If needed, then it will redraw the table based on
|
|
|
644 |
* the new position.
|
|
|
645 |
* @returns {void}
|
|
|
646 |
* @private
|
|
|
647 |
*/
|
|
|
648 |
"_fnScroll": function ()
|
|
|
649 |
{
|
|
|
650 |
var
|
|
|
651 |
that = this,
|
|
|
652 |
heights = this.s.heights,
|
|
|
653 |
iScrollTop = this.dom.scroller.scrollTop,
|
|
|
654 |
iTopRow;
|
|
|
655 |
|
|
|
656 |
if ( this.s.skip ) {
|
|
|
657 |
return;
|
|
|
658 |
}
|
|
|
659 |
|
|
|
660 |
if ( this.s.ingnoreScroll ) {
|
|
|
661 |
return;
|
|
|
662 |
}
|
|
|
663 |
|
|
|
664 |
/* If the table has been sorted or filtered, then we use the redraw that
|
|
|
665 |
* DataTables as done, rather than performing our own
|
|
|
666 |
*/
|
|
|
667 |
if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
|
|
|
668 |
this.s.lastScrollTop = 0;
|
|
|
669 |
return;
|
|
|
670 |
}
|
|
|
671 |
|
|
|
672 |
/* Update the table's information display for what is now in the viewport */
|
|
|
673 |
this._fnInfo();
|
|
|
674 |
|
|
|
675 |
/* We don't want to state save on every scroll event - that's heavy
|
|
|
676 |
* handed, so use a timeout to update the state saving only when the
|
|
|
677 |
* scrolling has finished
|
|
|
678 |
*/
|
|
|
679 |
clearTimeout( this.s.stateTO );
|
|
|
680 |
this.s.stateTO = setTimeout( function () {
|
|
|
681 |
that.s.dtApi.state.save();
|
|
|
682 |
}, 250 );
|
|
|
683 |
|
|
|
684 |
/* Check if the scroll point is outside the trigger boundary which would required
|
|
|
685 |
* a DataTables redraw
|
|
|
686 |
*/
|
|
|
687 |
if ( this.s.forceReposition || iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
|
|
|
688 |
|
|
|
689 |
var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
|
|
|
690 |
|
|
|
691 |
iTopRow = parseInt(this._domain( 'physicalToVirtual', iScrollTop ) / heights.row, 10) - preRows;
|
|
|
692 |
this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
|
|
|
693 |
this.s.forceReposition = false;
|
|
|
694 |
|
|
|
695 |
if ( iTopRow <= 0 ) {
|
|
|
696 |
/* At the start of the table */
|
|
|
697 |
iTopRow = 0;
|
|
|
698 |
}
|
|
|
699 |
else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
|
|
|
700 |
/* At the end of the table */
|
|
|
701 |
iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
|
|
|
702 |
if ( iTopRow < 0 ) {
|
|
|
703 |
iTopRow = 0;
|
|
|
704 |
}
|
|
|
705 |
}
|
|
|
706 |
else if ( iTopRow % 2 !== 0 ) {
|
|
|
707 |
// For the row-striping classes (odd/even) we want only to start
|
|
|
708 |
// on evens otherwise the stripes will change between draws and
|
|
|
709 |
// look rubbish
|
|
|
710 |
iTopRow++;
|
|
|
711 |
}
|
|
|
712 |
|
|
|
713 |
if ( iTopRow != this.s.dt._iDisplayStart ) {
|
|
|
714 |
/* Cache the new table position for quick lookups */
|
|
|
715 |
this.s.tableTop = $(this.s.dt.nTable).offset().top;
|
|
|
716 |
this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
|
|
|
717 |
|
|
|
718 |
var draw = function () {
|
|
|
719 |
if ( that.s.scrollDrawReq === null ) {
|
|
|
720 |
that.s.scrollDrawReq = iScrollTop;
|
|
|
721 |
}
|
|
|
722 |
|
|
|
723 |
that.s.dt._iDisplayStart = iTopRow;
|
|
|
724 |
that.s.dt.oApi._fnDraw( that.s.dt );
|
|
|
725 |
};
|
|
|
726 |
|
|
|
727 |
/* Do the DataTables redraw based on the calculated start point - note that when
|
|
|
728 |
* using server-side processing we introduce a small delay to not DoS the server...
|
|
|
729 |
*/
|
|
|
730 |
if ( this.s.dt.oFeatures.bServerSide ) {
|
|
|
731 |
clearTimeout( this.s.drawTO );
|
|
|
732 |
this.s.drawTO = setTimeout( draw, this.s.serverWait );
|
|
|
733 |
}
|
|
|
734 |
else {
|
|
|
735 |
draw();
|
|
|
736 |
}
|
|
|
737 |
|
|
|
738 |
if ( this.dom.loader && ! this.s.loaderVisible ) {
|
|
|
739 |
this.dom.loader.css( 'display', 'block' );
|
|
|
740 |
this.s.loaderVisible = true;
|
|
|
741 |
}
|
|
|
742 |
}
|
|
|
743 |
}
|
|
|
744 |
else {
|
|
|
745 |
this.s.topRowFloat = this.fnPixelsToRow( iScrollTop, false, true );
|
|
|
746 |
}
|
|
|
747 |
|
|
|
748 |
this.s.lastScrollTop = iScrollTop;
|
|
|
749 |
this.s.stateSaveThrottle();
|
|
|
750 |
},
|
|
|
751 |
|
|
|
752 |
|
|
|
753 |
/**
|
|
|
754 |
* Convert from one domain to another. The physical domain is the actual
|
|
|
755 |
* pixel count on the screen, while the virtual is if we had browsers which
|
|
|
756 |
* had scrolling containers of infinite height (i.e. the absolute value)
|
|
|
757 |
*
|
|
|
758 |
* @param {string} dir Domain transform direction, `virtualToPhysical` or
|
|
|
759 |
* `physicalToVirtual`
|
|
|
760 |
* @returns {number} Calculated transform
|
|
|
761 |
* @private
|
|
|
762 |
*/
|
|
|
763 |
_domain: function ( dir, val )
|
|
|
764 |
{
|
|
|
765 |
var heights = this.s.heights;
|
|
|
766 |
var coeff;
|
|
|
767 |
|
|
|
768 |
// If the virtual and physical height match, then we use a linear
|
|
|
769 |
// transform between the two, allowing the scrollbar to be linear
|
|
|
770 |
if ( heights.virtual === heights.scroll ) {
|
|
|
771 |
return val;
|
|
|
772 |
}
|
|
|
773 |
|
|
|
774 |
// Otherwise, we want a non-linear scrollbar to take account of the
|
|
|
775 |
// redrawing regions at the start and end of the table, otherwise these
|
|
|
776 |
// can stutter badly - on large tables 30px (for example) scroll might
|
|
|
777 |
// be hundreds of rows, so the table would be redrawing every few px at
|
|
|
778 |
// the start and end. Use a simple quadratic to stop this. It does mean
|
|
|
779 |
// the scrollbar is non-linear, but with such massive data sets, the
|
|
|
780 |
// scrollbar is going to be a best guess anyway
|
|
|
781 |
var xMax = (heights.scroll - heights.viewport) / 2;
|
|
|
782 |
var yMax = (heights.virtual - heights.viewport) / 2;
|
|
|
783 |
|
|
|
784 |
coeff = yMax / ( xMax * xMax );
|
|
|
785 |
|
|
|
786 |
if ( dir === 'virtualToPhysical' ) {
|
|
|
787 |
if ( val < yMax ) {
|
|
|
788 |
return Math.pow(val / coeff, 0.5);
|
|
|
789 |
}
|
|
|
790 |
else {
|
|
|
791 |
val = (yMax*2) - val;
|
|
|
792 |
return val < 0 ?
|
|
|
793 |
heights.scroll :
|
|
|
794 |
(xMax*2) - Math.pow(val / coeff, 0.5);
|
|
|
795 |
}
|
|
|
796 |
}
|
|
|
797 |
else if ( dir === 'physicalToVirtual' ) {
|
|
|
798 |
if ( val < xMax ) {
|
|
|
799 |
return val * val * coeff;
|
|
|
800 |
}
|
|
|
801 |
else {
|
|
|
802 |
val = (xMax*2) - val;
|
|
|
803 |
return val < 0 ?
|
|
|
804 |
heights.virtual :
|
|
|
805 |
(yMax*2) - (val * val * coeff);
|
|
|
806 |
}
|
|
|
807 |
}
|
|
|
808 |
},
|
|
|
809 |
|
|
|
810 |
/**
|
|
|
811 |
* Parse CSS height property string as number
|
|
|
812 |
*
|
|
|
813 |
* An attempt is made to parse the string as a number. Currently supported units are 'px',
|
|
|
814 |
* 'vh', and 'rem'. 'em' is partially supported; it works as long as the parent element's
|
|
|
815 |
* font size matches the body element. Zero is returned for unrecognized strings.
|
|
|
816 |
* @param {string} cssHeight CSS height property string
|
|
|
817 |
* @returns {number} height
|
|
|
818 |
* @private
|
|
|
819 |
*/
|
|
|
820 |
_parseHeight: function(cssHeight) {
|
|
|
821 |
var height;
|
|
|
822 |
var matches = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|em|rem|vh)$/.exec(cssHeight);
|
|
|
823 |
|
|
|
824 |
if (matches === null) {
|
|
|
825 |
return 0;
|
|
|
826 |
}
|
|
|
827 |
|
|
|
828 |
var value = parseFloat(matches[1]);
|
|
|
829 |
var unit = matches[2];
|
|
|
830 |
|
|
|
831 |
if ( unit === 'px' ) {
|
|
|
832 |
height = value;
|
|
|
833 |
}
|
|
|
834 |
else if ( unit === 'vh' ) {
|
|
|
835 |
height = ( value / 100 ) * $(window).height();
|
|
|
836 |
}
|
|
|
837 |
else if ( unit === 'rem' ) {
|
|
|
838 |
height = value * parseFloat($(':root').css('font-size'));
|
|
|
839 |
}
|
|
|
840 |
else if ( unit === 'em' ) {
|
|
|
841 |
height = value * parseFloat($('body').css('font-size'));
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
return height ?
|
|
|
845 |
height :
|
|
|
846 |
0;
|
|
|
847 |
},
|
|
|
848 |
|
|
|
849 |
|
|
|
850 |
/**
|
|
|
851 |
* Draw callback function which is fired when the DataTable is redrawn. The main function of
|
|
|
852 |
* this method is to position the drawn table correctly the scrolling container for the rows
|
|
|
853 |
* that is displays as a result of the scrolling position.
|
|
|
854 |
* @returns {void}
|
|
|
855 |
* @private
|
|
|
856 |
*/
|
|
|
857 |
"_fnDrawCallback": function ()
|
|
|
858 |
{
|
|
|
859 |
var
|
|
|
860 |
that = this,
|
|
|
861 |
heights = this.s.heights,
|
|
|
862 |
iScrollTop = this.dom.scroller.scrollTop,
|
|
|
863 |
iActualScrollTop = iScrollTop,
|
|
|
864 |
iScrollBottom = iScrollTop + heights.viewport,
|
|
|
865 |
iTableHeight = $(this.s.dt.nTable).height(),
|
|
|
866 |
displayStart = this.s.dt._iDisplayStart,
|
|
|
867 |
displayLen = this.s.dt._iDisplayLength,
|
|
|
868 |
displayEnd = this.s.dt.fnRecordsDisplay();
|
|
|
869 |
|
|
|
870 |
// Disable the scroll event listener while we are updating the DOM
|
|
|
871 |
this.s.skip = true;
|
|
|
872 |
|
|
|
873 |
// If paging is reset
|
|
|
874 |
if ( (this.s.dt.bSorted || this.s.dt.bFiltered) && displayStart === 0 ) {
|
|
|
875 |
this.s.topRowFloat = 0;
|
|
|
876 |
}
|
|
|
877 |
|
|
|
878 |
// Reposition the scrolling for the updated virtual position if needed
|
|
|
879 |
if ( displayStart === 0 ) {
|
|
|
880 |
// Linear calculation at the top of the table
|
|
|
881 |
iScrollTop = this.s.topRowFloat * heights.row;
|
|
|
882 |
}
|
|
|
883 |
else if ( displayStart + displayLen >= displayEnd ) {
|
|
|
884 |
// Linear calculation that the bottom as well
|
|
|
885 |
iScrollTop = heights.scroll - ((displayEnd - this.s.topRowFloat) * heights.row);
|
|
|
886 |
}
|
|
|
887 |
else {
|
|
|
888 |
// Domain scaled in the middle
|
|
|
889 |
iScrollTop = this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row );
|
|
|
890 |
}
|
|
|
891 |
|
|
|
892 |
this.dom.scroller.scrollTop = iScrollTop;
|
|
|
893 |
|
|
|
894 |
// Store positional information so positional calculations can be based
|
|
|
895 |
// upon the current table draw position
|
|
|
896 |
this.s.baseScrollTop = iScrollTop;
|
|
|
897 |
this.s.baseRowTop = this.s.topRowFloat;
|
|
|
898 |
|
|
|
899 |
// Position the table in the virtual scroller
|
|
|
900 |
var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
|
|
|
901 |
if ( displayStart === 0 ) {
|
|
|
902 |
tableTop = 0;
|
|
|
903 |
}
|
|
|
904 |
else if ( displayStart + displayLen >= displayEnd ) {
|
|
|
905 |
tableTop = heights.scroll - iTableHeight;
|
|
|
906 |
}
|
|
|
907 |
|
|
|
908 |
this.dom.table.style.top = tableTop+'px';
|
|
|
909 |
|
|
|
910 |
/* Cache some information for the scroller */
|
|
|
911 |
this.s.tableTop = tableTop;
|
|
|
912 |
this.s.tableBottom = iTableHeight + this.s.tableTop;
|
|
|
913 |
|
|
|
914 |
// Calculate the boundaries for where a redraw will be triggered by the
|
|
|
915 |
// scroll event listener
|
|
|
916 |
var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
|
|
|
917 |
this.s.redrawTop = iScrollTop - boundaryPx;
|
|
|
918 |
this.s.redrawBottom = iScrollTop + boundaryPx > heights.scroll - heights.viewport - heights.row ?
|
|
|
919 |
heights.scroll - heights.viewport - heights.row :
|
|
|
920 |
iScrollTop + boundaryPx;
|
|
|
921 |
|
|
|
922 |
this.s.skip = false;
|
|
|
923 |
|
|
|
924 |
// Restore the scrolling position that was saved by DataTable's state
|
|
|
925 |
// saving Note that this is done on the second draw when data is Ajax
|
|
|
926 |
// sourced, and the first draw when DOM soured
|
|
|
927 |
if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
|
|
|
928 |
typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
|
|
|
929 |
{
|
|
|
930 |
// A quirk of DataTables is that the draw callback will occur on an
|
|
|
931 |
// empty set if Ajax sourced, but not if server-side processing.
|
|
|
932 |
var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
|
|
|
933 |
true :
|
|
|
934 |
false;
|
|
|
935 |
|
|
|
936 |
if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
|
|
|
937 |
(!ajaxSourced && this.s.dt.iDraw == 1) )
|
|
|
938 |
{
|
|
|
939 |
setTimeout( function () {
|
|
|
940 |
$(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
|
|
|
941 |
that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
|
|
|
942 |
|
|
|
943 |
// In order to prevent layout thrashing we need another
|
|
|
944 |
// small delay
|
|
|
945 |
setTimeout( function () {
|
|
|
946 |
that.s.ingnoreScroll = false;
|
|
|
947 |
}, 0 );
|
|
|
948 |
}, 0 );
|
|
|
949 |
}
|
|
|
950 |
}
|
|
|
951 |
else {
|
|
|
952 |
that.s.ingnoreScroll = false;
|
|
|
953 |
}
|
|
|
954 |
|
|
|
955 |
// Because of the order of the DT callbacks, the info update will
|
|
|
956 |
// take precedence over the one we want here. So a 'thread' break is
|
|
|
957 |
// needed. Only add the thread break if bInfo is set
|
|
|
958 |
if ( this.s.dt.oFeatures.bInfo ) {
|
|
|
959 |
setTimeout( function () {
|
|
|
960 |
that._fnInfo.call( that );
|
|
|
961 |
}, 0 );
|
|
|
962 |
}
|
|
|
963 |
|
|
|
964 |
// Hide the loading indicator
|
|
|
965 |
if ( this.dom.loader && this.s.loaderVisible ) {
|
|
|
966 |
this.dom.loader.css( 'display', 'none' );
|
|
|
967 |
this.s.loaderVisible = false;
|
|
|
968 |
}
|
|
|
969 |
},
|
|
|
970 |
|
|
|
971 |
|
|
|
972 |
/**
|
|
|
973 |
* Force the scrolling container to have height beyond that of just the
|
|
|
974 |
* table that has been drawn so the user can scroll the whole data set.
|
|
|
975 |
*
|
|
|
976 |
* Note that if the calculated required scrolling height exceeds a maximum
|
|
|
977 |
* value (1 million pixels - hard-coded) the forcing element will be set
|
|
|
978 |
* only to that maximum value and virtual / physical domain transforms will
|
|
|
979 |
* be used to allow Scroller to display tables of any number of records.
|
|
|
980 |
* @returns {void}
|
|
|
981 |
* @private
|
|
|
982 |
*/
|
|
|
983 |
_fnScrollForce: function ()
|
|
|
984 |
{
|
|
|
985 |
var heights = this.s.heights;
|
|
|
986 |
var max = 1000000;
|
|
|
987 |
|
|
|
988 |
heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
|
|
|
989 |
heights.scroll = heights.virtual;
|
|
|
990 |
|
|
|
991 |
if ( heights.scroll > max ) {
|
|
|
992 |
heights.scroll = max;
|
|
|
993 |
}
|
|
|
994 |
|
|
|
995 |
// Minimum height so there is always a row visible (the 'no rows found'
|
|
|
996 |
// if reduced to zero filtering)
|
|
|
997 |
this.dom.force.style.height = heights.scroll > this.s.heights.row ?
|
|
|
998 |
heights.scroll+'px' :
|
|
|
999 |
this.s.heights.row+'px';
|
|
|
1000 |
},
|
|
|
1001 |
|
|
|
1002 |
|
|
|
1003 |
/**
|
|
|
1004 |
* Automatic calculation of table row height. This is just a little tricky here as using
|
|
|
1005 |
* initialisation DataTables has tale the table out of the document, so we need to create
|
|
|
1006 |
* a new table and insert it into the document, calculate the row height and then whip the
|
|
|
1007 |
* table out.
|
|
|
1008 |
* @returns {void}
|
|
|
1009 |
* @private
|
|
|
1010 |
*/
|
|
|
1011 |
"_fnCalcRowHeight": function ()
|
|
|
1012 |
{
|
|
|
1013 |
var dt = this.s.dt;
|
|
|
1014 |
var origTable = dt.nTable;
|
|
|
1015 |
var nTable = origTable.cloneNode( false );
|
|
|
1016 |
var tbody = $('<tbody/>').appendTo( nTable );
|
|
|
1017 |
var container = $(
|
|
|
1018 |
'<div class="'+dt.oClasses.sWrapper+' DTS">'+
|
|
|
1019 |
'<div class="'+dt.oClasses.sScrollWrapper+'">'+
|
|
|
1020 |
'<div class="'+dt.oClasses.sScrollBody+'"></div>'+
|
|
|
1021 |
'</div>'+
|
|
|
1022 |
'</div>'
|
|
|
1023 |
);
|
|
|
1024 |
|
|
|
1025 |
// Want 3 rows in the sizing table so :first-child and :last-child
|
|
|
1026 |
// CSS styles don't come into play - take the size of the middle row
|
|
|
1027 |
$('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
|
|
|
1028 |
while( $('tr', tbody).length < 3 ) {
|
|
|
1029 |
tbody.append( '<tr><td> </td></tr>' );
|
|
|
1030 |
}
|
|
|
1031 |
|
|
|
1032 |
$('div.'+dt.oClasses.sScrollBody, container).append( nTable );
|
|
|
1033 |
|
|
|
1034 |
// If initialised using `dom`, use the holding element as the insert point
|
|
|
1035 |
var insertEl = this.s.dt.nHolding || origTable.parentNode;
|
|
|
1036 |
|
|
|
1037 |
if ( ! $(insertEl).is(':visible') ) {
|
|
|
1038 |
insertEl = 'body';
|
|
|
1039 |
}
|
|
|
1040 |
|
|
|
1041 |
container.appendTo( insertEl );
|
|
|
1042 |
this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
|
|
|
1043 |
|
|
|
1044 |
container.remove();
|
|
|
1045 |
},
|
|
|
1046 |
|
|
|
1047 |
|
|
|
1048 |
/**
|
|
|
1049 |
* Update any information elements that are controlled by the DataTable based on the scrolling
|
|
|
1050 |
* viewport and what rows are visible in it. This function basically acts in the same way as
|
|
|
1051 |
* _fnUpdateInfo in DataTables, and effectively replaces that function.
|
|
|
1052 |
* @returns {void}
|
|
|
1053 |
* @private
|
|
|
1054 |
*/
|
|
|
1055 |
"_fnInfo": function ()
|
|
|
1056 |
{
|
|
|
1057 |
if ( !this.s.dt.oFeatures.bInfo )
|
|
|
1058 |
{
|
|
|
1059 |
return;
|
|
|
1060 |
}
|
|
|
1061 |
|
|
|
1062 |
var
|
|
|
1063 |
dt = this.s.dt,
|
|
|
1064 |
language = dt.oLanguage,
|
|
|
1065 |
iScrollTop = this.dom.scroller.scrollTop,
|
|
|
1066 |
iStart = Math.floor( this.fnPixelsToRow(iScrollTop, false, this.s.ani)+1 ),
|
|
|
1067 |
iMax = dt.fnRecordsTotal(),
|
|
|
1068 |
iTotal = dt.fnRecordsDisplay(),
|
|
|
1069 |
iPossibleEnd = Math.ceil( this.fnPixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
|
|
|
1070 |
iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
|
|
|
1071 |
sStart = dt.fnFormatNumber( iStart ),
|
|
|
1072 |
sEnd = dt.fnFormatNumber( iEnd ),
|
|
|
1073 |
sMax = dt.fnFormatNumber( iMax ),
|
|
|
1074 |
sTotal = dt.fnFormatNumber( iTotal ),
|
|
|
1075 |
sOut;
|
|
|
1076 |
|
|
|
1077 |
if ( dt.fnRecordsDisplay() === 0 &&
|
|
|
1078 |
dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
|
|
|
1079 |
{
|
|
|
1080 |
/* Empty record set */
|
|
|
1081 |
sOut = language.sInfoEmpty+ language.sInfoPostFix;
|
|
|
1082 |
}
|
|
|
1083 |
else if ( dt.fnRecordsDisplay() === 0 )
|
|
|
1084 |
{
|
|
|
1085 |
/* Empty record set after filtering */
|
|
|
1086 |
sOut = language.sInfoEmpty +' '+
|
|
|
1087 |
language.sInfoFiltered.replace('_MAX_', sMax)+
|
|
|
1088 |
language.sInfoPostFix;
|
|
|
1089 |
}
|
|
|
1090 |
else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
|
|
|
1091 |
{
|
|
|
1092 |
/* Normal record set */
|
|
|
1093 |
sOut = language.sInfo.
|
|
|
1094 |
replace('_START_', sStart).
|
|
|
1095 |
replace('_END_', sEnd).
|
|
|
1096 |
replace('_MAX_', sMax).
|
|
|
1097 |
replace('_TOTAL_', sTotal)+
|
|
|
1098 |
language.sInfoPostFix;
|
|
|
1099 |
}
|
|
|
1100 |
else
|
|
|
1101 |
{
|
|
|
1102 |
/* Record set after filtering */
|
|
|
1103 |
sOut = language.sInfo.
|
|
|
1104 |
replace('_START_', sStart).
|
|
|
1105 |
replace('_END_', sEnd).
|
|
|
1106 |
replace('_MAX_', sMax).
|
|
|
1107 |
replace('_TOTAL_', sTotal) +' '+
|
|
|
1108 |
language.sInfoFiltered.replace(
|
|
|
1109 |
'_MAX_',
|
|
|
1110 |
dt.fnFormatNumber(dt.fnRecordsTotal())
|
|
|
1111 |
)+
|
|
|
1112 |
language.sInfoPostFix;
|
|
|
1113 |
}
|
|
|
1114 |
|
|
|
1115 |
var callback = language.fnInfoCallback;
|
|
|
1116 |
if ( callback ) {
|
|
|
1117 |
sOut = callback.call( dt.oInstance,
|
|
|
1118 |
dt, iStart, iEnd, iMax, iTotal, sOut
|
|
|
1119 |
);
|
|
|
1120 |
}
|
|
|
1121 |
|
|
|
1122 |
var n = dt.aanFeatures.i;
|
|
|
1123 |
if ( typeof n != 'undefined' )
|
|
|
1124 |
{
|
|
|
1125 |
for ( var i=0, iLen=n.length ; i<iLen ; i++ )
|
|
|
1126 |
{
|
|
|
1127 |
$(n[i]).html( sOut );
|
|
|
1128 |
}
|
|
|
1129 |
}
|
|
|
1130 |
|
|
|
1131 |
// DT doesn't actually (yet) trigger this event, but it will in future
|
|
|
1132 |
$(dt.nTable).triggerHandler( 'info.dt' );
|
|
|
1133 |
}
|
|
|
1134 |
} );
|
|
|
1135 |
|
|
|
1136 |
|
|
|
1137 |
|
|
|
1138 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
1139 |
* Statics
|
|
|
1140 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
1141 |
|
|
|
1142 |
|
|
|
1143 |
/**
|
|
|
1144 |
* Scroller default settings for initialisation
|
|
|
1145 |
* @namespace
|
|
|
1146 |
* @name Scroller.defaults
|
|
|
1147 |
* @static
|
|
|
1148 |
*/
|
|
|
1149 |
Scroller.defaults = /** @lends Scroller.defaults */{
|
|
|
1150 |
/**
|
|
|
1151 |
* Indicate if Scroller show show trace information on the console or not. This can be
|
|
|
1152 |
* useful when debugging Scroller or if just curious as to what it is doing, but should
|
|
|
1153 |
* be turned off for production.
|
|
|
1154 |
* @type bool
|
|
|
1155 |
* @default false
|
|
|
1156 |
* @static
|
|
|
1157 |
* @example
|
|
|
1158 |
* var oTable = $('#example').dataTable( {
|
|
|
1159 |
* "sScrollY": "200px",
|
|
|
1160 |
* "sDom": "frtiS",
|
|
|
1161 |
* "bDeferRender": true,
|
|
|
1162 |
* "oScroller": {
|
|
|
1163 |
* "trace": true
|
|
|
1164 |
* }
|
|
|
1165 |
* } );
|
|
|
1166 |
*/
|
|
|
1167 |
"trace": false,
|
|
|
1168 |
|
|
|
1169 |
/**
|
|
|
1170 |
* Scroller will attempt to automatically calculate the height of rows for it's internal
|
|
|
1171 |
* calculations. However the height that is used can be overridden using this parameter.
|
|
|
1172 |
* @type int|string
|
|
|
1173 |
* @default auto
|
|
|
1174 |
* @static
|
|
|
1175 |
* @example
|
|
|
1176 |
* var oTable = $('#example').dataTable( {
|
|
|
1177 |
* "sScrollY": "200px",
|
|
|
1178 |
* "sDom": "frtiS",
|
|
|
1179 |
* "bDeferRender": true,
|
|
|
1180 |
* "oScroller": {
|
|
|
1181 |
* "rowHeight": 30
|
|
|
1182 |
* }
|
|
|
1183 |
* } );
|
|
|
1184 |
*/
|
|
|
1185 |
"rowHeight": "auto",
|
|
|
1186 |
|
|
|
1187 |
/**
|
|
|
1188 |
* When using server-side processing, Scroller will wait a small amount of time to allow
|
|
|
1189 |
* the scrolling to finish before requesting more data from the server. This prevents
|
|
|
1190 |
* you from DoSing your own server! The wait time can be configured by this parameter.
|
|
|
1191 |
* @type int
|
|
|
1192 |
* @default 200
|
|
|
1193 |
* @static
|
|
|
1194 |
* @example
|
|
|
1195 |
* var oTable = $('#example').dataTable( {
|
|
|
1196 |
* "sScrollY": "200px",
|
|
|
1197 |
* "sDom": "frtiS",
|
|
|
1198 |
* "bDeferRender": true,
|
|
|
1199 |
* "oScroller": {
|
|
|
1200 |
* "serverWait": 100
|
|
|
1201 |
* }
|
|
|
1202 |
* } );
|
|
|
1203 |
*/
|
|
|
1204 |
"serverWait": 200,
|
|
|
1205 |
|
|
|
1206 |
/**
|
|
|
1207 |
* The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
|
|
|
1208 |
* for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
|
|
|
1209 |
* rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
|
|
|
1210 |
* The value is based upon the number of rows that can be displayed in the viewport (i.e.
|
|
|
1211 |
* a value of 1), and will apply the display range to records before before and after the
|
|
|
1212 |
* current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
|
|
|
1213 |
* of rows before the current viewport, the current viewport's rows and 1 viewport's worth
|
|
|
1214 |
* of rows after the current viewport. Adjusting this value can be useful for ensuring
|
|
|
1215 |
* smooth scrolling based on your data set.
|
|
|
1216 |
* @type int
|
|
|
1217 |
* @default 7
|
|
|
1218 |
* @static
|
|
|
1219 |
* @example
|
|
|
1220 |
* var oTable = $('#example').dataTable( {
|
|
|
1221 |
* "sScrollY": "200px",
|
|
|
1222 |
* "sDom": "frtiS",
|
|
|
1223 |
* "bDeferRender": true,
|
|
|
1224 |
* "oScroller": {
|
|
|
1225 |
* "displayBuffer": 10
|
|
|
1226 |
* }
|
|
|
1227 |
* } );
|
|
|
1228 |
*/
|
|
|
1229 |
"displayBuffer": 9,
|
|
|
1230 |
|
|
|
1231 |
/**
|
|
|
1232 |
* Scroller uses the boundary scaling factor to decide when to redraw the table - which it
|
|
|
1233 |
* typically does before you reach the end of the currently loaded data set (in order to
|
|
|
1234 |
* allow the data to look continuous to a user scrolling through the data). If given as 0
|
|
|
1235 |
* then the table will be redrawn whenever the viewport is scrolled, while 1 would not
|
|
|
1236 |
* redraw the table until the currently loaded data has all been shown. You will want
|
|
|
1237 |
* something in the middle - the default factor of 0.5 is usually suitable.
|
|
|
1238 |
* @type float
|
|
|
1239 |
* @default 0.5
|
|
|
1240 |
* @static
|
|
|
1241 |
* @example
|
|
|
1242 |
* var oTable = $('#example').dataTable( {
|
|
|
1243 |
* "sScrollY": "200px",
|
|
|
1244 |
* "sDom": "frtiS",
|
|
|
1245 |
* "bDeferRender": true,
|
|
|
1246 |
* "oScroller": {
|
|
|
1247 |
* "boundaryScale": 0.75
|
|
|
1248 |
* }
|
|
|
1249 |
* } );
|
|
|
1250 |
*/
|
|
|
1251 |
"boundaryScale": 0.5,
|
|
|
1252 |
|
|
|
1253 |
/**
|
|
|
1254 |
* Show (or not) the loading element in the background of the table. Note that you should
|
|
|
1255 |
* include the dataTables.scroller.css file for this to be displayed correctly.
|
|
|
1256 |
* @type boolean
|
|
|
1257 |
* @default false
|
|
|
1258 |
* @static
|
|
|
1259 |
* @example
|
|
|
1260 |
* var oTable = $('#example').dataTable( {
|
|
|
1261 |
* "sScrollY": "200px",
|
|
|
1262 |
* "sDom": "frtiS",
|
|
|
1263 |
* "bDeferRender": true,
|
|
|
1264 |
* "oScroller": {
|
|
|
1265 |
* "loadingIndicator": true
|
|
|
1266 |
* }
|
|
|
1267 |
* } );
|
|
|
1268 |
*/
|
|
|
1269 |
"loadingIndicator": false
|
|
|
1270 |
};
|
|
|
1271 |
|
|
|
1272 |
Scroller.oDefaults = Scroller.defaults;
|
|
|
1273 |
|
|
|
1274 |
|
|
|
1275 |
|
|
|
1276 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
1277 |
* Constants
|
|
|
1278 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
1279 |
|
|
|
1280 |
/**
|
|
|
1281 |
* Scroller version
|
|
|
1282 |
* @type String
|
|
|
1283 |
* @default See code
|
|
|
1284 |
* @name Scroller.version
|
|
|
1285 |
* @static
|
|
|
1286 |
*/
|
|
|
1287 |
Scroller.version = "1.5.0";
|
|
|
1288 |
|
|
|
1289 |
|
|
|
1290 |
|
|
|
1291 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
1292 |
* Initialisation
|
|
|
1293 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
1294 |
|
|
|
1295 |
// Legacy `dom` parameter initialisation support
|
|
|
1296 |
if ( typeof $.fn.dataTable == "function" &&
|
|
|
1297 |
typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
|
|
|
1298 |
$.fn.dataTableExt.fnVersionCheck('1.10.0') )
|
|
|
1299 |
{
|
|
|
1300 |
$.fn.dataTableExt.aoFeatures.push( {
|
|
|
1301 |
"fnInit": function( oDTSettings ) {
|
|
|
1302 |
var init = oDTSettings.oInit;
|
|
|
1303 |
var opts = init.scroller || init.oScroller || {};
|
|
|
1304 |
|
|
|
1305 |
new Scroller( oDTSettings, opts );
|
|
|
1306 |
},
|
|
|
1307 |
"cFeature": "S",
|
|
|
1308 |
"sFeature": "Scroller"
|
|
|
1309 |
} );
|
|
|
1310 |
}
|
|
|
1311 |
else
|
|
|
1312 |
{
|
|
|
1313 |
alert( "Warning: Scroller requires DataTables 1.10.0 or greater - www.datatables.net/download");
|
|
|
1314 |
}
|
|
|
1315 |
|
|
|
1316 |
// Attach a listener to the document which listens for DataTables initialisation
|
|
|
1317 |
// events so we can automatically initialise
|
|
|
1318 |
$(document).on( 'preInit.dt.dtscroller', function (e, settings) {
|
|
|
1319 |
if ( e.namespace !== 'dt' ) {
|
|
|
1320 |
return;
|
|
|
1321 |
}
|
|
|
1322 |
|
|
|
1323 |
var init = settings.oInit.scroller;
|
|
|
1324 |
var defaults = DataTable.defaults.scroller;
|
|
|
1325 |
|
|
|
1326 |
if ( init || defaults ) {
|
|
|
1327 |
var opts = $.extend( {}, init, defaults );
|
|
|
1328 |
|
|
|
1329 |
if ( init !== false ) {
|
|
|
1330 |
new Scroller( settings, opts );
|
|
|
1331 |
}
|
|
|
1332 |
}
|
|
|
1333 |
} );
|
|
|
1334 |
|
|
|
1335 |
|
|
|
1336 |
// Attach Scroller to DataTables so it can be accessed as an 'extra'
|
|
|
1337 |
$.fn.dataTable.Scroller = Scroller;
|
|
|
1338 |
$.fn.DataTable.Scroller = Scroller;
|
|
|
1339 |
|
|
|
1340 |
|
|
|
1341 |
// DataTables 1.10 API method aliases
|
|
|
1342 |
var Api = $.fn.dataTable.Api;
|
|
|
1343 |
|
|
|
1344 |
Api.register( 'scroller()', function () {
|
|
|
1345 |
return this;
|
|
|
1346 |
} );
|
|
|
1347 |
|
|
|
1348 |
// Undocumented and deprecated - is it actually useful at all?
|
|
|
1349 |
Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
|
|
|
1350 |
var ctx = this.context;
|
|
|
1351 |
|
|
|
1352 |
if ( ctx.length && ctx[0].oScroller ) {
|
|
|
1353 |
return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
|
|
|
1354 |
}
|
|
|
1355 |
// undefined
|
|
|
1356 |
} );
|
|
|
1357 |
|
|
|
1358 |
// Undocumented and deprecated - is it actually useful at all?
|
|
|
1359 |
Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
|
|
|
1360 |
var ctx = this.context;
|
|
|
1361 |
|
|
|
1362 |
if ( ctx.length && ctx[0].oScroller ) {
|
|
|
1363 |
return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
|
|
|
1364 |
}
|
|
|
1365 |
// undefined
|
|
|
1366 |
} );
|
|
|
1367 |
|
|
|
1368 |
// `scroller().scrollToRow()` is undocumented and deprecated. Use `scroller.toPosition()
|
|
|
1369 |
Api.register( ['scroller().scrollToRow()', 'scroller.toPosition()'], function ( idx, ani ) {
|
|
|
1370 |
this.iterator( 'table', function ( ctx ) {
|
|
|
1371 |
if ( ctx.oScroller ) {
|
|
|
1372 |
ctx.oScroller.fnScrollToRow( idx, ani );
|
|
|
1373 |
}
|
|
|
1374 |
} );
|
|
|
1375 |
|
|
|
1376 |
return this;
|
|
|
1377 |
} );
|
|
|
1378 |
|
|
|
1379 |
Api.register( 'row().scrollTo()', function ( ani ) {
|
|
|
1380 |
var that = this;
|
|
|
1381 |
|
|
|
1382 |
this.iterator( 'row', function ( ctx, rowIdx ) {
|
|
|
1383 |
if ( ctx.oScroller ) {
|
|
|
1384 |
var displayIdx = that
|
|
|
1385 |
.rows( { order: 'applied', search: 'applied' } )
|
|
|
1386 |
.indexes()
|
|
|
1387 |
.indexOf( rowIdx );
|
|
|
1388 |
|
|
|
1389 |
ctx.oScroller.fnScrollToRow( displayIdx, ani );
|
|
|
1390 |
}
|
|
|
1391 |
} );
|
|
|
1392 |
|
|
|
1393 |
return this;
|
|
|
1394 |
} );
|
|
|
1395 |
|
|
|
1396 |
Api.register( 'scroller.measure()', function ( redraw ) {
|
|
|
1397 |
this.iterator( 'table', function ( ctx ) {
|
|
|
1398 |
if ( ctx.oScroller ) {
|
|
|
1399 |
ctx.oScroller.fnMeasure( redraw );
|
|
|
1400 |
}
|
|
|
1401 |
} );
|
|
|
1402 |
|
|
|
1403 |
return this;
|
|
|
1404 |
} );
|
|
|
1405 |
|
|
|
1406 |
Api.register( 'scroller.page()', function() {
|
|
|
1407 |
var ctx = this.context;
|
|
|
1408 |
|
|
|
1409 |
if ( ctx.length && ctx[0].oScroller ) {
|
|
|
1410 |
return ctx[0].oScroller.fnPageInfo();
|
|
|
1411 |
}
|
|
|
1412 |
// undefined
|
|
|
1413 |
} );
|
|
|
1414 |
|
|
|
1415 |
return Scroller;
|
|
|
1416 |
}));
|