jquery-datatables 1.10.12

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