jquery-datatables-rails 1.12.2 → 2.1.10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/dataTables/extras/filler.png +0 -0
  3. data/app/assets/images/dataTables/extras/loading-background.png +0 -0
  4. data/app/assets/images/dataTables/sort_asc_disabled.png +0 -0
  5. data/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +851 -0
  6. data/app/assets/javascripts/dataTables/extras/{ColReorder.js → dataTables.colReorder.js} +558 -223
  7. data/app/assets/javascripts/dataTables/extras/dataTables.colVis.js +1096 -0
  8. data/app/assets/javascripts/dataTables/extras/{FixedColumns.js → dataTables.fixedColumns.js} +163 -113
  9. data/app/assets/javascripts/dataTables/extras/{FixedHeader.js → dataTables.fixedHeader.js} +306 -219
  10. data/app/assets/javascripts/dataTables/extras/{KeyTable.js → dataTables.keyTable.js} +155 -95
  11. data/app/assets/javascripts/dataTables/extras/{Scroller.js → dataTables.scroller.js} +469 -188
  12. data/app/assets/javascripts/dataTables/extras/{TableTools.js → dataTables.tableTools.js} +949 -341
  13. data/app/assets/javascripts/dataTables/jquery.dataTables.foundation.js +4 -4
  14. data/app/assets/javascripts/dataTables/jquery.dataTables.js +10711 -8427
  15. data/app/assets/media/dataTables/extras/swf/copy_csv_xls.swf +0 -0
  16. data/app/assets/media/dataTables/extras/swf/copy_csv_xls_pdf.swf +0 -0
  17. data/app/assets/stylesheets/dataTables/extras/dataTables.autoFill.css.scss +24 -0
  18. data/app/assets/stylesheets/dataTables/extras/dataTables.colReorder.css.scss +14 -0
  19. data/app/assets/stylesheets/dataTables/extras/dataTables.colVis.css.scss +184 -0
  20. data/app/assets/stylesheets/dataTables/extras/dataTables.colvis.jqueryui.css.scss +23 -0
  21. data/app/assets/stylesheets/dataTables/extras/dataTables.fixedColumns.css.scss +24 -0
  22. data/app/assets/stylesheets/dataTables/extras/dataTables.fixedHeader.css.scss +7 -0
  23. data/app/assets/stylesheets/dataTables/extras/dataTables.keyTable.css.scss +7 -0
  24. data/app/assets/stylesheets/dataTables/extras/dataTables.scroller.css.scss +44 -0
  25. data/app/assets/stylesheets/dataTables/extras/{TableTools.css.erb → dataTables.tableTools.css.scss} +30 -15
  26. data/app/assets/stylesheets/dataTables/jquery.dataTables.bootstrap.css.scss +6 -0
  27. data/app/assets/stylesheets/dataTables/jquery.dataTables.css.scss +363 -184
  28. data/app/assets/stylesheets/dataTables/src/jquery.dataTables_themeroller.css +307 -220
  29. data/lib/jquery/datatables/rails/version.rb +1 -1
  30. metadata +42 -22
  31. data/app/assets/javascripts/dataTables/extras/AutoFill.js +0 -820
  32. data/app/assets/javascripts/dataTables/extras/ColVis.js +0 -1005
  33. data/app/assets/javascripts/dataTables/extras/TableTools.min.js +0 -77
  34. data/app/assets/stylesheets/dataTables/extras/ColReorder.css.erb +0 -21
  35. data/app/assets/stylesheets/dataTables/extras/ColVis.css +0 -76
  36. data/app/assets/stylesheets/dataTables/extras/ColVisAlt.css.erb +0 -104
@@ -1,23 +1,32 @@
1
+ /*! Scroller 1.2.1
2
+ * 2011-2014 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
1
5
  /**
2
6
  * @summary Scroller
3
7
  * @description Virtual rendering for DataTables
4
- * @file Scroller.js
5
- * @version 1.1.0
6
- * @author Allan Jardine (www.sprymedia.co.uk)
7
- * @license GPL v2 or BSD 3 point style
8
+ * @version 1.2.1
9
+ * @file dataTables.scroller.js
10
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
8
11
  * @contact www.sprymedia.co.uk/contact
12
+ * @copyright Copyright 2011-2014 SpryMedia Ltd.
13
+ *
14
+ * This source file is free software, available under the following license:
15
+ * MIT license - http://datatables.net/license/mit
9
16
  *
10
- * @copyright Copyright 2011-2012 Allan Jardine, all rights reserved.
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.
11
20
  *
12
- * This source file is free software, under either the GPL v2 license or a
13
- * BSD style license, available at:
14
- * http://datatables.net/license_gpl2
15
- * http://datatables.net/license_bsd
21
+ * For details please refer to: http://www.datatables.net
16
22
  */
17
23
 
18
- (/** @lends <global> */function($, window, document) {
24
+ (function(window, document, undefined){
19
25
 
20
26
 
27
+ var factory = function( $, DataTable ) {
28
+ "use strict";
29
+
21
30
  /**
22
31
  * Scroller is a virtual rendering plug-in for DataTables which allows large
23
32
  * datasets to be drawn on screen every quickly. What the virtual rendering means
@@ -34,34 +43,36 @@
34
43
  *
35
44
  * Scroller is initialised by simply including the letter 'S' in the sDom for the
36
45
  * table you want to have this feature enabled on. Note that the 'S' must come
37
- * AFTER the 't' parameter in sDom.
46
+ * AFTER the 't' parameter in `dom`.
38
47
  *
39
48
  * Key features include:
40
49
  * <ul class="limit_length">
41
50
  * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
42
51
  * <li>Full compatibility with deferred rendering in DataTables 1.9 for maximum speed</li>
43
- * <li>Correct visual scrolling implementation, similar to "infinite scrolling" in DataTable core</li>
52
+ * <li>Display millions of rows</li>
44
53
  * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
45
54
  * <li>Easy to use</li>
46
55
  * </ul>
47
56
  *
48
57
  * @class
49
58
  * @constructor
59
+ * @global
50
60
  * @param {object} oDT DataTables settings object
51
- * @param {object} [oOpts={}] Configuration object for FixedColumns. Options are defined by {@link Scroller.oDefaults}
61
+ * @param {object} [oOpts={}] Configuration object for FixedColumns. Options
62
+ * are defined by {@link Scroller.defaults}
52
63
  *
53
- * @requires jQuery 1.4+
64
+ * @requires jQuery 1.7+
54
65
  * @requires DataTables 1.9.0+
55
66
  *
56
67
  * @example
57
- * $(document).ready(function() {
58
- * $('#example').dataTable( {
59
- * "sScrollY": "200px",
60
- * "sAjaxSource": "media/dataset/large.txt",
61
- * "sDom": "frtiS",
62
- * "bDeferRender": true
63
- * } );
64
- * } );
68
+ * $(document).ready(function() {
69
+ * $('#example').dataTable( {
70
+ * "sScrollY": "200px",
71
+ * "sAjaxSource": "media/dataset/large.txt",
72
+ * "sDom": "frtiS",
73
+ * "bDeferRender": true
74
+ * } );
75
+ * } );
65
76
  */
66
77
  var Scroller = function ( oDTSettings, oOpts ) {
67
78
  /* Sanity check - you just know it will happen */
@@ -79,7 +90,8 @@ var Scroller = function ( oDTSettings, oOpts ) {
79
90
  /**
80
91
  * Settings object which contains customisable information for the Scroller instance
81
92
  * @namespace
82
- * @extends Scroller.DEFAULTS
93
+ * @private
94
+ * @extends Scroller.defaults
83
95
  */
84
96
  this.s = {
85
97
  /**
@@ -122,13 +134,6 @@ var Scroller = function ( oDTSettings, oOpts ) {
122
134
  */
123
135
  "redrawBottom": 0,
124
136
 
125
- /**
126
- * Height of rows in the table
127
- * @type int
128
- * @default 0
129
- */
130
- "rowHeight": null,
131
-
132
137
  /**
133
138
  * Auto row height or not indicator
134
139
  * @type bool
@@ -136,13 +141,6 @@ var Scroller = function ( oDTSettings, oOpts ) {
136
141
  */
137
142
  "autoHeight": true,
138
143
 
139
- /**
140
- * Pixel height of the viewport
141
- * @type int
142
- * @default 0
143
- */
144
- "viewportHeight": 0,
145
-
146
144
  /**
147
145
  * Number of rows calculated as visible in the visible viewport
148
146
  * @type int
@@ -165,12 +163,43 @@ var Scroller = function ( oDTSettings, oOpts ) {
165
163
  * @type int
166
164
  * @default null
167
165
  */
168
- "drawTO": null
166
+ "drawTO": null,
167
+
168
+ heights: {
169
+ jump: null,
170
+ page: null,
171
+ virtual: null,
172
+ scroll: null,
173
+
174
+ /**
175
+ * Height of rows in the table
176
+ * @type int
177
+ * @default 0
178
+ */
179
+ row: null,
180
+
181
+ /**
182
+ * Pixel height of the viewport
183
+ * @type int
184
+ * @default 0
185
+ */
186
+ viewport: null
187
+ },
188
+
189
+ topRowFloat: 0,
190
+ scrollDrawDiff: null
169
191
  };
192
+
193
+ // @todo The defaults should extend a `c` property and the internal settings
194
+ // only held in the `s` property. At the moment they are mixed
170
195
  this.s = $.extend( this.s, Scroller.oDefaults, oOpts );
171
196
 
197
+ // Workaround for row height being read from height object (see above comment)
198
+ this.s.heights.row = this.s.rowHeight;
199
+
172
200
  /**
173
201
  * DOM elements used by the class instance
202
+ * @private
174
203
  * @namespace
175
204
  *
176
205
  */
@@ -189,13 +218,14 @@ var Scroller = function ( oDTSettings, oOpts ) {
189
218
 
190
219
 
191
220
 
192
- Scroller.prototype = {
221
+ Scroller.prototype = /** @lends Scroller.prototype */{
193
222
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
194
223
  * Public methods
195
224
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
196
225
 
197
226
  /**
198
- * Calculate the pixel position from the top of the scrolling container for a given row
227
+ * Calculate the pixel position from the top of the scrolling container for
228
+ * a given row
199
229
  * @param {int} iRow Row number to calculate the position of
200
230
  * @returns {int} Pixels
201
231
  * @example
@@ -212,15 +242,35 @@ Scroller.prototype = {
212
242
  * } );
213
243
  * } );
214
244
  */
215
- "fnRowToPixels": function ( iRow )
245
+ "fnRowToPixels": function ( rowIdx, intParse, virtual )
216
246
  {
217
- return iRow * this.s.rowHeight;
247
+ var pixels;
248
+
249
+ if ( virtual ) {
250
+ pixels = this._domain( 'virtualToPhysical', rowIdx * this.s.heights.row );
251
+ }
252
+ else {
253
+ var diff = rowIdx - this.s.baseRowTop;
254
+ pixels = this.s.baseScrollTop + (diff * this.s.heights.row);
255
+ }
256
+
257
+ return intParse || intParse === undefined ?
258
+ parseInt( pixels, 10 ) :
259
+ pixels;
218
260
  },
219
261
 
220
262
 
221
263
  /**
222
- * Calculate the row number that will be found at the given pixel position (y-scroll)
223
- * @param {int} iPixels Offset from top to caluclate the row number of
264
+ * Calculate the row number that will be found at the given pixel position
265
+ * (y-scroll).
266
+ *
267
+ * Please note that when the height of the full table exceeds 1 million
268
+ * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
269
+ * all of the records into a finite area, but this function returns a linear
270
+ * value (relative to the last non-linear positioning).
271
+ * @param {int} iPixels Offset from top to calculate the row number of
272
+ * @param {int} [intParse=true] If an integer value should be returned
273
+ * @param {int} [virtual=false] Perform the calculations in the virtual domain
224
274
  * @returns {int} Row index
225
275
  * @example
226
276
  * $(document).ready(function() {
@@ -236,9 +286,16 @@ Scroller.prototype = {
236
286
  * } );
237
287
  * } );
238
288
  */
239
- "fnPixelsToRow": function ( iPixels )
289
+ "fnPixelsToRow": function ( pixels, intParse, virtual )
240
290
  {
241
- return parseInt( iPixels / this.s.rowHeight, 10 );
291
+ var diff = pixels - this.s.baseScrollTop;
292
+ var row = virtual ?
293
+ this._domain( 'physicalToVirtual', pixels ) / this.s.heights.row :
294
+ ( diff / this.s.heights.row ) + this.s.baseRowTop;
295
+
296
+ return intParse || intParse === undefined ?
297
+ parseInt( row, 10 ) :
298
+ row;
242
299
  },
243
300
 
244
301
 
@@ -259,7 +316,7 @@ Scroller.prototype = {
259
316
  * o.oScroller.fnScrollToRow( 1000 );
260
317
  * }
261
318
  * } );
262
- *
319
+ *
263
320
  * // Sometime later on use the following to scroll to row 500...
264
321
  * var oSettings = $('#example').dataTable().fnSettings();
265
322
  * oSettings.oScroller.fnScrollToRow( 500 );
@@ -267,11 +324,37 @@ Scroller.prototype = {
267
324
  */
268
325
  "fnScrollToRow": function ( iRow, bAnimate )
269
326
  {
327
+ var that = this;
328
+ var ani = false;
270
329
  var px = this.fnRowToPixels( iRow );
330
+
331
+ // We need to know if the table will redraw or not before doing the
332
+ // scroll. If it will not redraw, then we need to use the currently
333
+ // displayed table, and scroll with the physical pixels. Otherwise, we
334
+ // need to calculate the table's new position from the virtual
335
+ // transform.
336
+ var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
337
+ var drawRow = iRow - preRows;
338
+ if ( drawRow < 0 ) {
339
+ drawRow = 0;
340
+ }
341
+
342
+ if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
343
+ ani = true;
344
+ px = this.fnRowToPixels( iRow, false, true );
345
+ }
346
+
271
347
  if ( typeof bAnimate == 'undefined' || bAnimate )
272
348
  {
349
+ this.s.ani = ani;
273
350
  $(this.dom.scroller).animate( {
274
351
  "scrollTop": px
352
+ }, function () {
353
+ // This needs to happen after the animation has completed and
354
+ // the final scroll event fired
355
+ setTimeout( function () {
356
+ that.s.ani = false;
357
+ }, 0 );
275
358
  } );
276
359
  }
277
360
  else
@@ -282,9 +365,10 @@ Scroller.prototype = {
282
365
 
283
366
 
284
367
  /**
285
- * Calculate and store information about how many rows are to be displayed in the scrolling
286
- * viewport, based on current dimensions in the browser's rendering. This can be particularly
287
- * useful if the table is initially drawn in a hidden element - for example in a tab.
368
+ * Calculate and store information about how many rows are to be displayed
369
+ * in the scrolling viewport, based on current dimensions in the browser's
370
+ * rendering. This can be particularly useful if the table is initially
371
+ * drawn in a hidden element - for example in a tab.
288
372
  * @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
289
373
  * the new dimentions forming the basis for the draw.
290
374
  * @returns {void}
@@ -302,7 +386,7 @@ Scroller.prototype = {
302
386
  * o.oScroller.fnScrollToRow( 1000 );
303
387
  * }
304
388
  * } );
305
- *
389
+ *
306
390
  * setTimeout( function () {
307
391
  * // Make the example container visible and recalculate the scroller sizes
308
392
  * document.getElementById('container').style.display = "block";
@@ -316,19 +400,11 @@ Scroller.prototype = {
316
400
  this._fnCalcRowHeight();
317
401
  }
318
402
 
319
- this.s.viewportHeight = $(this.dom.scroller).height();
320
- this.s.viewportRows = parseInt( this.s.viewportHeight/this.s.rowHeight, 10 )+1;
321
- this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
403
+ var heights = this.s.heights;
322
404
 
323
- if ( this.s.trace )
324
- {
325
- console.log(
326
- 'Row height: '+this.s.rowHeight +' '+
327
- 'Viewport height: '+this.s.viewportHeight +' '+
328
- 'Viewport rows: '+ this.s.viewportRows +' '+
329
- 'Display rows: '+ this.s.dt._iDisplayLength
330
- );
331
- }
405
+ heights.viewport = $(this.dom.scroller).height();
406
+ this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
407
+ this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
332
408
 
333
409
  if ( typeof bRedraw == 'undefined' || bRedraw )
334
410
  {
@@ -351,6 +427,12 @@ Scroller.prototype = {
351
427
  {
352
428
  var that = this;
353
429
 
430
+ /* Sanity check */
431
+ if ( !this.s.dt.oFeatures.bPaginate ) {
432
+ this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
433
+ return;
434
+ }
435
+
354
436
  /* Insert a div element that we can use to force the DT scrolling container to
355
437
  * the height that would be required if the whole table was being displayed
356
438
  */
@@ -380,21 +462,21 @@ Scroller.prototype = {
380
462
  }
381
463
 
382
464
  /* Initial size calculations */
383
- if ( this.s.rowHeight && this.s.rowHeight != 'auto' )
465
+ if ( this.s.heights.row && this.s.heights.row != 'auto' )
384
466
  {
385
467
  this.s.autoHeight = false;
386
468
  }
387
469
  this.fnMeasure( false );
388
470
 
389
471
  /* Scrolling callback to see if a page change is needed */
390
- $(this.dom.scroller).scroll( function () {
472
+ $(this.dom.scroller).on( 'scroll.DTS', function () {
391
473
  that._fnScroll.call( that );
392
474
  } );
393
475
 
394
476
  /* In iOS we catch the touchstart event incase the user tries to scroll
395
477
  * while the display is already scrolling
396
478
  */
397
- $(this.dom.scroller).bind('touchstart', function () {
479
+ $(this.dom.scroller).on('touchstart.DTS', function () {
398
480
  that._fnScroll.call( that );
399
481
  } );
400
482
 
@@ -408,20 +490,49 @@ Scroller.prototype = {
408
490
  "sName": "Scroller"
409
491
  } );
410
492
 
493
+ /* On resize, update the information element, since the number of rows shown might change */
494
+ $(window).on( 'resize.DTS', function () {
495
+ that._fnInfo();
496
+ } );
497
+
411
498
  /* Add a state saving parameter to the DT state saving so we can restore the exact
412
499
  * position of the scrolling
413
500
  */
501
+ var initialStateSave = true;
414
502
  this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
415
- oData.iScroller = that.dom.scroller.scrollTop;
503
+ /* Set iScroller to saved scroll position on initialization.
504
+ */
505
+ if(initialStateSave && that.s.dt.oLoadedState){
506
+ oData.iScroller = that.s.dt.oLoadedState.iScroller;
507
+ initialStateSave = false;
508
+ } else {
509
+ oData.iScroller = that.dom.scroller.scrollTop;
510
+ }
416
511
  }, "Scroller_State" );
512
+
513
+ /* Destructor */
514
+ this.s.dt.aoDestroyCallback.push( {
515
+ "sName": "Scroller",
516
+ "fn": function () {
517
+ $(window).off( 'resize.DTS' );
518
+ $(that.dom.scroller).off('touchstart.DTS scroll.DTS');
519
+ $(that.s.dt.nTableWrapper).removeClass('DTS');
520
+ $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
521
+
522
+ that.dom.table.style.position = "";
523
+ that.dom.table.style.top = "";
524
+ that.dom.table.style.left = "";
525
+ }
526
+ } );
417
527
  },
418
528
 
419
529
 
420
530
  /**
421
- * Scrolling function - fired whenever the scrolling position is changed. This method needs
422
- * to use the stored values to see if the table should be redrawn as we are moving towards
423
- * the end of the information that is currently drawn or not. If needed, then it will redraw
424
- * the table based on the new position.
531
+ * Scrolling function - fired whenever the scrolling position is changed.
532
+ * This method needs to use the stored values to see if the table should be
533
+ * redrawn as we are moving towards the end of the information that is
534
+ * currently drawn or not. If needed, then it will redraw the table based on
535
+ * the new position.
425
536
  * @returns {void}
426
537
  * @private
427
538
  */
@@ -429,33 +540,28 @@ Scroller.prototype = {
429
540
  {
430
541
  var
431
542
  that = this,
543
+ heights = this.s.heights,
432
544
  iScrollTop = this.dom.scroller.scrollTop,
433
545
  iTopRow;
434
546
 
547
+ if ( this.s.skip ) {
548
+ return;
549
+ }
550
+
435
551
  /* If the table has been sorted or filtered, then we use the redraw that
436
552
  * DataTables as done, rather than performing our own
437
553
  */
438
- if ( this.s.dt.bFiltered || this.s.dt.bSorted )
439
- {
554
+ if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
555
+ this.s.lastScrollTop = 0;
440
556
  return;
441
557
  }
442
558
 
443
- if ( this.s.trace )
444
- {
445
- console.log(
446
- 'Scroll: '+iScrollTop+'px - boundaries: '+this.s.redrawTop+' / '+this.s.redrawBottom+'. '+
447
- ' Showing rows '+this.fnPixelsToRow(iScrollTop)+
448
- ' to '+this.fnPixelsToRow(iScrollTop+$(this.dom.scroller).height())+
449
- ' in the viewport, with rows '+this.s.dt._iDisplayStart+
450
- ' to '+(this.s.dt._iDisplayEnd)+' rendered by the DataTable'
451
- );
452
- }
453
-
454
559
  /* Update the table's information display for what is now in the viewport */
455
560
  this._fnInfo();
456
561
 
457
- /* We dont' want to state save on every scroll event - that's heavy handed, so
458
- * use a timeout to update the state saving only when the scrolling has finished
562
+ /* We don't want to state save on every scroll event - that's heavy
563
+ * handed, so use a timeout to update the state saving only when the
564
+ * scrolling has finished
459
565
  */
460
566
  clearTimeout( this.s.stateTO );
461
567
  this.s.stateTO = setTimeout( function () {
@@ -465,60 +571,130 @@ Scroller.prototype = {
465
571
  /* Check if the scroll point is outside the trigger boundary which would required
466
572
  * a DataTables redraw
467
573
  */
468
- if ( iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom )
469
- {
470
- var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
471
- iTopRow = parseInt( iScrollTop / this.s.rowHeight, 10 ) - preRows;
472
- if ( iTopRow < 0 )
473
- {
574
+ if ( iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
575
+ var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
576
+
577
+ if ( Math.abs( iScrollTop - this.s.lastScrollTop ) > heights.viewport || this.s.ani ) {
578
+ iTopRow = parseInt(this._domain( 'physicalToVirtual', iScrollTop ) / heights.row, 10) - preRows;
579
+ this.s.topRowFloat = (this._domain( 'physicalToVirtual', iScrollTop ) / heights.row);
580
+ }
581
+ else {
582
+ iTopRow = this.fnPixelsToRow( iScrollTop ) - preRows;
583
+ this.s.topRowFloat = this.fnPixelsToRow( iScrollTop, false );
584
+ }
585
+
586
+ if ( iTopRow <= 0 ) {
474
587
  /* At the start of the table */
475
588
  iTopRow = 0;
476
589
  }
477
- else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() )
478
- {
590
+ else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
479
591
  /* At the end of the table */
480
592
  iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
481
- if ( iTopRow < 0 )
482
- {
593
+ if ( iTopRow < 0 ) {
483
594
  iTopRow = 0;
484
595
  }
485
596
  }
486
- else if ( iTopRow % 2 !== 0 )
487
- {
488
- /* For the row-striping classes (odd/even) we want only to start on evens
489
- * otherwise the stripes will change between draws and look rubbish
490
- */
597
+ else if ( iTopRow % 2 !== 0 ) {
598
+ // For the row-striping classes (odd/even) we want only to start
599
+ // on evens otherwise the stripes will change between draws and
600
+ // look rubbish
491
601
  iTopRow++;
492
602
  }
493
603
 
494
- if ( iTopRow != this.s.dt._iDisplayStart )
495
- {
604
+ if ( iTopRow != this.s.dt._iDisplayStart ) {
496
605
  /* Cache the new table position for quick lookups */
497
606
  this.s.tableTop = $(this.s.dt.nTable).offset().top;
498
607
  this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
499
608
 
609
+ var draw = function () {
610
+ if ( that.s.scrollDrawReq === null ) {
611
+ that.s.scrollDrawReq = iScrollTop;
612
+ }
613
+
614
+ that.s.dt._iDisplayStart = iTopRow;
615
+ if ( that.s.dt.oApi._fnCalculateEnd ) { // Removed in 1.10
616
+ that.s.dt.oApi._fnCalculateEnd( that.s.dt );
617
+ }
618
+ that.s.dt.oApi._fnDraw( that.s.dt );
619
+ };
620
+
500
621
  /* Do the DataTables redraw based on the calculated start point - note that when
501
622
  * using server-side processing we introduce a small delay to not DoS the server...
502
623
  */
503
624
  if ( this.s.dt.oFeatures.bServerSide ) {
504
625
  clearTimeout( this.s.drawTO );
505
- this.s.drawTO = setTimeout( function () {
506
- that.s.dt._iDisplayStart = iTopRow;
507
- that.s.dt.oApi._fnCalculateEnd( that.s.dt );
508
- that.s.dt.oApi._fnDraw( that.s.dt );
509
- }, this.s.serverWait );
626
+ this.s.drawTO = setTimeout( draw, this.s.serverWait );
510
627
  }
511
- else
512
- {
513
- this.s.dt._iDisplayStart = iTopRow;
514
- this.s.dt.oApi._fnCalculateEnd( this.s.dt );
515
- this.s.dt.oApi._fnDraw( this.s.dt );
628
+ else {
629
+ draw();
516
630
  }
631
+ }
632
+ }
517
633
 
518
- if ( this.s.trace )
519
- {
520
- console.log( 'Scroll forcing redraw - top DT render row: '+ iTopRow );
521
- }
634
+ this.s.lastScrollTop = iScrollTop;
635
+ },
636
+
637
+
638
+ /**
639
+ * Convert from one domain to another. The physical domain is the actual
640
+ * pixel count on the screen, while the virtual is if we had browsers which
641
+ * had scrolling containers of infinite height (i.e. the absolute value)
642
+ *
643
+ * @param {string} dir Domain transform direction, `virtualToPhysical` or
644
+ * `physicalToVirtual`
645
+ * @returns {number} Calculated transform
646
+ * @private
647
+ */
648
+ _domain: function ( dir, val )
649
+ {
650
+ var heights = this.s.heights;
651
+ var coeff;
652
+
653
+ // If the virtual and physical height match, then we use a linear
654
+ // transform between the two, allowing the scrollbar to be linear
655
+ if ( heights.virtual === heights.scroll ) {
656
+ coeff = (heights.virtual-heights.viewport) / (heights.scroll-heights.viewport);
657
+
658
+ if ( dir === 'virtualToPhysical' ) {
659
+ return val / coeff;
660
+ }
661
+ else if ( dir === 'physicalToVirtual' ) {
662
+ return val * coeff;
663
+ }
664
+ }
665
+
666
+ // Otherwise, we want a non-linear scrollbar to take account of the
667
+ // redrawing regions at the start and end of the table, otherwise these
668
+ // can stutter badly - on large tables 30px (for example) scroll might
669
+ // be hundreds of rows, so the table would be redrawing every few px at
670
+ // the start and end. Use a simple quadratic to stop this. It does mean
671
+ // the scrollbar is non-linear, but with such massive data sets, the
672
+ // scrollbar is going to be a best guess anyway
673
+ var xMax = (heights.scroll - heights.viewport) / 2;
674
+ var yMax = (heights.virtual - heights.viewport) / 2;
675
+
676
+ coeff = yMax / ( xMax * xMax );
677
+
678
+ if ( dir === 'virtualToPhysical' ) {
679
+ if ( val < yMax ) {
680
+ return Math.pow(val / coeff, 0.5);
681
+ }
682
+ else {
683
+ val = (yMax*2) - val;
684
+ return val < 0 ?
685
+ heights.scroll :
686
+ (xMax*2) - Math.pow(val / coeff, 0.5);
687
+ }
688
+ }
689
+ else if ( dir === 'physicalToVirtual' ) {
690
+ if ( val < xMax ) {
691
+ return val * val * coeff;
692
+ }
693
+ else {
694
+ val = (xMax*2) - val;
695
+ return val < 0 ?
696
+ heights.virtual :
697
+ (yMax*2) - (val * val * coeff);
522
698
  }
523
699
  }
524
700
  },
@@ -535,71 +711,121 @@ Scroller.prototype = {
535
711
  {
536
712
  var
537
713
  that = this,
714
+ heights = this.s.heights,
538
715
  iScrollTop = this.dom.scroller.scrollTop,
539
- iScrollBottom = iScrollTop + this.s.viewportHeight;
716
+ iActualScrollTop = iScrollTop,
717
+ iScrollBottom = iScrollTop + heights.viewport,
718
+ iTableHeight = $(this.s.dt.nTable).height(),
719
+ displayStart = this.s.dt._iDisplayStart,
720
+ displayLen = this.s.dt._iDisplayLength,
721
+ displayEnd = this.s.dt.fnRecordsDisplay();
722
+
723
+ // Disable the scroll event listener while we are updating the DOM
724
+ this.s.skip = true;
725
+
726
+ // Resize the scroll forcing element
727
+ this._fnScrollForce();
728
+
729
+ // Reposition the scrolling for the updated virtual position if needed
730
+ if ( displayStart === 0 ) {
731
+ // Linear calculation at the top of the table
732
+ iScrollTop = this.s.topRowFloat * heights.row;
733
+ }
734
+ else if ( displayStart + displayLen >= displayEnd ) {
735
+ // Linear calculation that the bottom as well
736
+ iScrollTop = heights.scroll - ((displayEnd - this.s.topRowFloat) * heights.row);
737
+ }
738
+ else {
739
+ // Domain scaled in the middle
740
+ iScrollTop = this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row );
741
+ }
540
742
 
541
- /* Set the height of the scrolling forcer to be suitable for the number of rows
542
- * in this draw
543
- */
544
- this.dom.force.style.height = (this.s.rowHeight * this.s.dt.fnRecordsDisplay())+"px";
743
+ this.dom.scroller.scrollTop = iScrollTop;
545
744
 
546
- /* Calculate the position that the top of the table should be at */
547
- var iTableTop = (this.s.rowHeight*this.s.dt._iDisplayStart);
548
- if ( this.s.dt._iDisplayStart === 0 )
549
- {
550
- iTableTop = 0;
745
+ // Store positional information so positional calculations can be based
746
+ // upon the current table draw position
747
+ this.s.baseScrollTop = iScrollTop;
748
+ this.s.baseRowTop = this.s.topRowFloat;
749
+
750
+ // Position the table in the virtual scroller
751
+ var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
752
+ if ( displayStart === 0 ) {
753
+ tableTop = 0;
551
754
  }
552
- else if ( this.s.dt._iDisplayStart === this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength )
553
- {
554
- iTableTop = this.s.rowHeight * this.s.dt._iDisplayStart;
755
+ else if ( displayStart + displayLen >= displayEnd ) {
756
+ tableTop = heights.scroll - iTableHeight;
555
757
  }
556
758
 
557
- this.dom.table.style.top = iTableTop+"px";
759
+ this.dom.table.style.top = tableTop+'px';
558
760
 
559
761
  /* Cache some information for the scroller */
560
- this.s.tableTop = iTableTop;
561
- this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
762
+ this.s.tableTop = tableTop;
763
+ this.s.tableBottom = iTableHeight + this.s.tableTop;
562
764
 
563
- this.s.redrawTop = iScrollTop - ( (iScrollTop - this.s.tableTop) * this.s.boundaryScale );
564
- this.s.redrawBottom = iScrollTop + ( (this.s.tableBottom - iScrollBottom) * this.s.boundaryScale );
765
+ // Calculate the boundaries for where a redraw will be triggered by the
766
+ // scroll event listener
767
+ var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
768
+ this.s.redrawTop = iScrollTop - boundaryPx;
769
+ this.s.redrawBottom = iScrollTop + boundaryPx;
565
770
 
566
- if ( this.s.trace )
567
- {
568
- console.log(
569
- "Table redraw. Table top: "+iTableTop+"px "+
570
- "Table bottom: "+this.s.tableBottom+" "+
571
- "Scroll boundary top: "+this.s.redrawTop+" "+
572
- "Scroll boundary bottom: "+this.s.redrawBottom+" "+
573
- "Rows drawn: "+this.s.dt._iDisplayLength);
574
- }
771
+ this.s.skip = false;
575
772
 
576
- /* Because of the order of the DT callbacks, the info update will
577
- * take precidence over the one we want here. So a 'thread' break is
578
- * needed
579
- */
773
+ // Because of the order of the DT callbacks, the info update will
774
+ // take precidence over the one we want here. So a 'thread' break is
775
+ // needed
580
776
  setTimeout( function () {
581
777
  that._fnInfo.call( that );
582
778
  }, 0 );
583
779
 
584
- /* Restore the scrolling position that was saved by DataTable's state saving
585
- * Note that this is done on the second draw when data is Ajax sourced, and the
586
- * first draw when DOM soured
587
- */
780
+ // Restore the scrolling position that was saved by DataTable's state
781
+ // saving Note that this is done on the second draw when data is Ajax
782
+ // sourced, and the first draw when DOM soured
588
783
  if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
589
784
  typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
590
785
  {
591
- if ( (this.s.dt.sAjaxSource !== null && this.s.dt.iDraw == 2) ||
592
- (this.s.dt.sAjaxSource === null && this.s.dt.iDraw == 1) )
786
+ var ajaxSourced = this.s.dt.sAjaxSource || that.s.dt.ajax ?
787
+ true :
788
+ false;
789
+
790
+ if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
791
+ (!ajaxSourced && this.s.dt.iDraw == 1) )
593
792
  {
594
793
  setTimeout( function () {
595
794
  $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
596
- that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (that.s.viewportHeight/2);
795
+ that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
597
796
  }, 0 );
598
797
  }
599
798
  }
600
799
  },
601
800
 
602
801
 
802
+ /**
803
+ * Force the scrolling container to have height beyond that of just the
804
+ * table that has been drawn so the user can scroll the whole data set.
805
+ *
806
+ * Note that if the calculated required scrolling height exceeds a maximum
807
+ * value (1 million pixels - hard-coded) the forcing element will be set
808
+ * only to that maximum value and virtual / physical domain transforms will
809
+ * be used to allow Scroller to display tables of any number of records.
810
+ * @returns {void}
811
+ * @private
812
+ */
813
+ _fnScrollForce: function ()
814
+ {
815
+ var heights = this.s.heights;
816
+ var max = 1000000;
817
+
818
+ heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
819
+ heights.scroll = heights.virtual;
820
+
821
+ if ( heights.scroll > max ) {
822
+ heights.scroll = max;
823
+ }
824
+
825
+ this.dom.force.style.height = heights.scroll+"px";
826
+ },
827
+
828
+
603
829
  /**
604
830
  * Automatic calculation of table row height. This is just a little tricky here as using
605
831
  * initialisation DataTables has tale the table out of the document, so we need to create
@@ -610,28 +836,29 @@ Scroller.prototype = {
610
836
  */
611
837
  "_fnCalcRowHeight": function ()
612
838
  {
613
- var nTable = this.s.dt.nTable.cloneNode( false );
614
- var nContainer = $(
839
+ var origTable = this.s.dt.nTable;
840
+ var nTable = origTable.cloneNode( false );
841
+ var tbody = $('<tbody/>').appendTo( nTable );
842
+ var container = $(
615
843
  '<div class="'+this.s.dt.oClasses.sWrapper+' DTS">'+
616
844
  '<div class="'+this.s.dt.oClasses.sScrollWrapper+'">'+
617
845
  '<div class="'+this.s.dt.oClasses.sScrollBody+'"></div>'+
618
846
  '</div>'+
619
847
  '</div>'
620
- )[0];
621
-
622
- $(nTable).append(
623
- '<tbody>'+
624
- '<tr>'+
625
- '<td>&nbsp;</td>'+
626
- '</tr>'+
627
- '</tbody>'
628
848
  );
629
849
 
630
- $('div.'+this.s.dt.oClasses.sScrollBody, nContainer).append( nTable );
850
+ // Want 3 rows in the sizing table so :first-child and :last-child
851
+ // CSS styles don't come into play - take the size of the middle row
852
+ $('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
853
+ while( $('tr', tbody).length < 3 ) {
854
+ tbody.append( '<tr><td>&nbsp;</td></tr>' );
855
+ }
856
+
857
+ $('div.'+this.s.dt.oClasses.sScrollBody, container).append( nTable );
631
858
 
632
- document.body.appendChild( nContainer );
633
- this.s.rowHeight = $('tbody tr', nTable).outerHeight();
634
- document.body.removeChild( nContainer );
859
+ container.appendTo( this.s.dt.nHolding );
860
+ this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
861
+ container.remove();
635
862
  },
636
863
 
637
864
 
@@ -652,10 +879,10 @@ Scroller.prototype = {
652
879
  var
653
880
  dt = this.s.dt,
654
881
  iScrollTop = this.dom.scroller.scrollTop,
655
- iStart = this.fnPixelsToRow(iScrollTop)+1,
882
+ iStart = Math.floor( this.fnPixelsToRow(iScrollTop, false, this.s.ani)+1 ),
656
883
  iMax = dt.fnRecordsTotal(),
657
884
  iTotal = dt.fnRecordsDisplay(),
658
- iPossibleEnd = this.fnPixelsToRow(iScrollTop+$(this.dom.scroller).height()),
885
+ iPossibleEnd = Math.ceil( this.fnPixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
659
886
  iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
660
887
  sStart = dt.fnFormatNumber( iStart ),
661
888
  sEnd = dt.fnFormatNumber( iEnd ),
@@ -718,9 +945,10 @@ Scroller.prototype = {
718
945
  /**
719
946
  * Scroller default settings for initialisation
720
947
  * @namespace
948
+ * @name Scroller.defaults
721
949
  * @static
722
950
  */
723
- Scroller.oDefaults = {
951
+ Scroller.defaults = /** @lends Scroller.defaults */{
724
952
  /**
725
953
  * Indicate if Scroller show show trace information on the console or not. This can be
726
954
  * useful when debugging Scroller or if just curious as to what it is doing, but should
@@ -843,30 +1071,22 @@ Scroller.oDefaults = {
843
1071
  "loadingIndicator": false
844
1072
  };
845
1073
 
1074
+ Scroller.oDefaults = Scroller.defaults;
1075
+
846
1076
 
847
1077
 
848
1078
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
849
1079
  * Constants
850
1080
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
851
1081
 
852
-
853
- /**
854
- * Name of this class
855
- * @type String
856
- * @default Scroller
857
- * @static
858
- */
859
- Scroller.prototype.CLASS = "Scroller";
860
-
861
-
862
1082
  /**
863
1083
  * Scroller version
864
1084
  * @type String
865
1085
  * @default See code
1086
+ * @name Scroller.version
866
1087
  * @static
867
1088
  */
868
- Scroller.VERSION = "1.1.0";
869
- Scroller.prototype.VERSION = Scroller.VERSION;
1089
+ Scroller.version = "1.2.1";
870
1090
 
871
1091
 
872
1092
 
@@ -883,9 +1103,9 @@ if ( typeof $.fn.dataTable == "function" &&
883
1103
  {
884
1104
  $.fn.dataTableExt.aoFeatures.push( {
885
1105
  "fnInit": function( oDTSettings ) {
886
- var init = (typeof oDTSettings.oInit.oScroller == 'undefined') ?
887
- {} : oDTSettings.oInit.oScroller;
888
- var oScroller = new Scroller( oDTSettings, init );
1106
+ var init = oDTSettings.oInit;
1107
+ var opts = init.scroller || init.oScroller || {};
1108
+ var oScroller = new Scroller( oDTSettings, opts );
889
1109
  return oScroller.dom.wrapper;
890
1110
  },
891
1111
  "cFeature": "S",
@@ -900,5 +1120,66 @@ else
900
1120
 
901
1121
  // Attach Scroller to DataTables so it can be accessed as an 'extra'
902
1122
  $.fn.dataTable.Scroller = Scroller;
1123
+ $.fn.DataTable.Scroller = Scroller;
1124
+
1125
+
1126
+ // DataTables 1.10 API method aliases
1127
+ if ( $.fn.dataTable.Api ) {
1128
+ var Api = $.fn.dataTable.Api;
1129
+
1130
+ Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
1131
+ var ctx = this.context;
1132
+
1133
+ if ( ctx.length && ctx[0].oScroller ) {
1134
+ return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
1135
+ }
1136
+ // undefined
1137
+ } );
1138
+
1139
+ Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
1140
+ var ctx = this.context;
1141
+
1142
+ if ( ctx.length && ctx[0].oScroller ) {
1143
+ return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
1144
+ }
1145
+ // undefined
1146
+ } );
1147
+
1148
+ Api.register( 'scroller().scrollToRow()', function ( row, ani ) {
1149
+ this.iterator( 'table', function ( ctx ) {
1150
+ if ( ctx.oScroller ) {
1151
+ ctx.oScroller.fnScrollToRow( row, ani );
1152
+ }
1153
+ } );
1154
+
1155
+ return this;
1156
+ } );
1157
+
1158
+ Api.register( 'scroller().measure()', function ( redraw ) {
1159
+ this.iterator( 'table', function ( ctx ) {
1160
+ if ( ctx.oScroller ) {
1161
+ ctx.oScroller.fnMeasure( redraw );
1162
+ }
1163
+ } );
1164
+
1165
+ return this;
1166
+ } );
1167
+ }
1168
+
1169
+
1170
+ return Scroller;
1171
+ }; // /factory
1172
+
1173
+
1174
+ // Define as an AMD module if possible
1175
+ if ( typeof define === 'function' && define.amd ) {
1176
+ define( 'datatables-scroller', ['jquery', 'datatables'], factory );
1177
+ }
1178
+ else if ( jQuery && !jQuery.fn.dataTable.Scroller ) {
1179
+ // Otherwise simply initialise as normal, stopping multiple evaluation
1180
+ factory( jQuery, jQuery.fn.dataTable );
1181
+ }
1182
+
1183
+
1184
+ })(window, document);
903
1185
 
904
- })(jQuery, window, document);