Add datatables-1.9.4 and jquery-1.10.2 libraries
[proteocache.git] / webapp / resources / datatables-1.9.4 / extras / Scroller / media / js / dataTables.scroller.js
1 /**
2  * @summary     Scroller
3  * @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  * @contact     www.sprymedia.co.uk/contact
9  *
10  * @copyright Copyright 2011-2012 Allan Jardine, all rights reserved.
11  *
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
16  */
17
18 (/** @lends <global> */function($, window, document) {
19
20
21 /** 
22  * Scroller is a virtual rendering plug-in for DataTables which allows large
23  * datasets to be drawn on screen every quickly. What the virtual rendering means
24  * is that only the visible portion of the table (and a bit to either side to make
25  * the scrolling smooth) is drawn, while the scrolling container gives the 
26  * visual impression that the whole table is visible. This is done by making use
27  * of the pagination abilities of DataTables and moving the table around in the
28  * scrolling container DataTables adds to the page. The scrolling container is
29  * forced to the height it would be for the full table display using an extra 
30  * element. 
31  * 
32  * Note that rows in the table MUST all be the same height. Information in a cell
33  * which expands on to multiple lines will cause some odd behaviour in the scrolling.
34  *
35  * Scroller is initialised by simply including the letter 'S' in the sDom for the
36  * table you want to have this feature enabled on. Note that the 'S' must come
37  * AFTER the 't' parameter in sDom.
38  * 
39  * Key features include:
40  *   <ul class="limit_length">
41  *     <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
42  *     <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>
44  *     <li>Integration with state saving in DataTables (scrolling position is saved)</li>
45  *     <li>Easy to use</li>
46  *   </ul>
47  *
48  *  @class
49  *  @constructor
50  *  @param {object} oDT DataTables settings object
51  *  @param {object} [oOpts={}] Configuration object for FixedColumns. Options are defined by {@link Scroller.oDefaults}
52  * 
53  *  @requires jQuery 1.4+
54  *  @requires DataTables 1.9.0+
55  * 
56  *  @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  *              } );
65  */
66 var Scroller = function ( oDTSettings, oOpts ) {
67         /* Sanity check - you just know it will happen */
68         if ( ! this instanceof Scroller )
69         {
70                 alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
71                 return;
72         }
73         
74         if ( typeof oOpts == 'undefined' )
75         {
76                 oOpts = {};
77         }
78         
79         /**
80          * Settings object which contains customisable information for the Scroller instance
81          * @namespace
82          * @extends Scroller.DEFAULTS
83          */
84         this.s = {
85                 /** 
86                  * DataTables settings object
87                  *  @type     object
88                  *  @default  Passed in as first parameter to constructor
89                  */
90                 "dt": oDTSettings,
91                 
92                 /** 
93                  * Pixel location of the top of the drawn table in the viewport
94                  *  @type     int
95                  *  @default  0
96                  */
97                 "tableTop": 0,
98                 
99                 /** 
100                  * Pixel location of the bottom of the drawn table in the viewport
101                  *  @type     int
102                  *  @default  0
103                  */
104                 "tableBottom": 0,
105                 
106                 /** 
107                  * Pixel location of the boundary for when the next data set should be loaded and drawn
108                  * when scrolling up the way.
109                  *  @type     int
110                  *  @default  0
111                  *  @private
112                  */
113                 "redrawTop": 0,
114                 
115                 /** 
116                  * Pixel location of the boundary for when the next data set should be loaded and drawn
117                  * when scrolling down the way. Note that this is actually caluated as the offset from
118                  * the top.
119                  *  @type     int
120                  *  @default  0
121                  *  @private
122                  */
123                 "redrawBottom": 0,
124                 
125                 /** 
126                  * Height of rows in the table
127                  *  @type     int
128                  *  @default  0
129                  */
130                 "rowHeight": null,
131                 
132                 /** 
133                  * Auto row height or not indicator
134                  *  @type     bool
135                  *  @default  0
136                  */
137                 "autoHeight": true,
138                 
139                 /** 
140                  * Pixel height of the viewport
141                  *  @type     int
142                  *  @default  0
143                  */
144                 "viewportHeight": 0,
145                 
146                 /** 
147                  * Number of rows calculated as visible in the visible viewport
148                  *  @type     int
149                  *  @default  0
150                  */
151                 "viewportRows": 0,
152                 
153                 /** 
154                  * setTimeout reference for state saving, used when state saving is enabled in the DataTable
155                  * and when the user scrolls the viewport in order to stop the cookie set taking too much
156                  * CPU!
157                  *  @type     int
158                  *  @default  0
159                  */
160                 "stateTO": null,
161                 
162                 /** 
163                  * setTimeout reference for the redraw, used when server-side processing is enabled in the
164                  * DataTables in order to prevent DoSing the server
165                  *  @type     int
166                  *  @default  null
167                  */
168                 "drawTO": null
169         };
170         this.s = $.extend( this.s, Scroller.oDefaults, oOpts );
171         
172         /**
173          * DOM elements used by the class instance
174          * @namespace
175          * 
176          */
177         this.dom = {
178                 "force": document.createElement('div'),
179                 "scroller": null,
180                 "table": null
181         };
182
183         /* Attach the instance to the DataTables instance so it can be accessed */
184         this.s.dt.oScroller = this;
185         
186         /* Let's do it */
187         this._fnConstruct();
188 };
189
190
191
192 Scroller.prototype = {
193         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
194          * Public methods
195          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
196         
197         /**
198          * Calculate the pixel position from the top of the scrolling container for a given row
199          *  @param {int} iRow Row number to calculate the position of
200          *  @returns {int} Pixels
201          *  @example
202          *    $(document).ready(function() {
203          *      $('#example').dataTable( {
204          *        "sScrollY": "200px",
205          *        "sAjaxSource": "media/dataset/large.txt",
206          *        "sDom": "frtiS",
207          *        "bDeferRender": true,
208          *        "fnInitComplete": function (o) {
209          *          // Find where row 25 is
210          *          alert( o.oScroller.fnRowToPixels( 25 ) );
211          *        }
212          *      } );
213          *    } );
214          */
215         "fnRowToPixels": function ( iRow )
216         {
217                 return iRow * this.s.rowHeight;
218         },
219
220
221         /**
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
224          *  @returns {int} Row index
225          *  @example
226          *    $(document).ready(function() {
227          *      $('#example').dataTable( {
228          *        "sScrollY": "200px",
229          *        "sAjaxSource": "media/dataset/large.txt",
230          *        "sDom": "frtiS",
231          *        "bDeferRender": true,
232          *        "fnInitComplete": function (o) {
233          *          // Find what row number is at 500px
234          *          alert( o.oScroller.fnPixelsToRow( 500 ) );
235          *        }
236          *      } );
237          *    } );
238          */
239         "fnPixelsToRow": function ( iPixels )
240         {
241                 return parseInt( iPixels / this.s.rowHeight, 10 );
242         },
243
244
245         /**
246          * Calculate the row number that will be found at the given pixel position (y-scroll)
247          *  @param {int} iRow Row index to scroll to
248          *  @param {bool} [bAnimate=true] Animate the transision or not 
249          *  @returns {void}
250          *  @example
251          *    $(document).ready(function() {
252          *      $('#example').dataTable( {
253          *        "sScrollY": "200px",
254          *        "sAjaxSource": "media/dataset/large.txt",
255          *        "sDom": "frtiS",
256          *        "bDeferRender": true,
257          *        "fnInitComplete": function (o) {
258          *          // Immediately scroll to row 1000
259          *          o.oScroller.fnScrollToRow( 1000 );
260          *        }
261          *      } );
262          *      
263          *      // Sometime later on use the following to scroll to row 500...
264          *          var oSettings = $('#example').dataTable().fnSettings();
265          *      oSettings.oScroller.fnScrollToRow( 500 );
266          *    } );
267          */
268         "fnScrollToRow": function ( iRow, bAnimate )
269         {
270                 var px = this.fnRowToPixels( iRow );
271                 if ( typeof bAnimate == 'undefined' || bAnimate )
272                 {
273                         $(this.dom.scroller).animate( {
274                                 "scrollTop": px
275                         } );
276                 }
277                 else
278                 {
279                         $(this.dom.scroller).scrollTop( px );
280                 }
281         },
282         
283         
284         /**
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.
288          *  @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
289          *    the new dimentions forming the basis for the draw. 
290          *  @returns {void}
291          *  @example
292          *    $(document).ready(function() {
293          *      // Make the example container hidden to throw off the browser's sizing
294          *      document.getElementById('container').style.display = "none";
295          *      var oTable = $('#example').dataTable( {
296          *        "sScrollY": "200px",
297          *        "sAjaxSource": "media/dataset/large.txt",
298          *        "sDom": "frtiS",
299          *        "bDeferRender": true,
300          *        "fnInitComplete": function (o) {
301          *          // Immediately scroll to row 1000
302          *          o.oScroller.fnScrollToRow( 1000 );
303          *        }
304          *      } );
305          *      
306          *      setTimeout( function () {
307          *        // Make the example container visible and recalculate the scroller sizes
308          *        document.getElementById('container').style.display = "block";
309          *        oTable.fnSettings().oScroller.fnMeasure();
310          *      }, 3000 );
311          */
312         "fnMeasure": function ( bRedraw )
313         {
314                 if ( this.s.autoHeight )
315                 {
316                         this._fnCalcRowHeight();
317                 }
318
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;
322                 
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                 }
332                 
333                 if ( typeof bRedraw == 'undefined' || bRedraw )
334                 {
335                         this.s.dt.oInstance.fnDraw();
336                 }
337         },
338         
339         
340         
341         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
342          * Private methods (they are of course public in JS, but recommended as private)
343          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
344         
345         /**
346          * Initialisation for Scroller
347          *  @returns {void}
348          *  @private
349          */
350         "_fnConstruct": function ()
351         {
352                 var that = this;
353
354                 /* Insert a div element that we can use to force the DT scrolling container to
355                  * the height that would be required if the whole table was being displayed
356                  */
357                 this.dom.force.style.position = "absolute";
358                 this.dom.force.style.top = "0px";
359                 this.dom.force.style.left = "0px";
360                 this.dom.force.style.width = "1px";
361
362                 this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
363                 this.dom.scroller.appendChild( this.dom.force );
364                 this.dom.scroller.style.position = "relative";
365
366                 this.dom.table = $('>table', this.dom.scroller)[0];
367                 this.dom.table.style.position = "absolute";
368                 this.dom.table.style.top = "0px";
369                 this.dom.table.style.left = "0px";
370
371                 // Add class to 'announce' that we are a Scroller table
372                 $(this.s.dt.nTableWrapper).addClass('DTS');
373
374                 // Add a 'loading' indicator
375                 if ( this.s.loadingIndicator )
376                 {
377                         $(this.dom.scroller.parentNode)
378                                 .css('position', 'relative')
379                                 .append('<div class="DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>');
380                 }
381
382                 /* Initial size calculations */
383                 if ( this.s.rowHeight && this.s.rowHeight != 'auto' )
384                 {
385                         this.s.autoHeight = false;
386                 }
387                 this.fnMeasure( false );
388
389                 /* Scrolling callback to see if a page change is needed */
390                 $(this.dom.scroller).scroll( function () {
391                         that._fnScroll.call( that );
392                 } );
393
394                 /* In iOS we catch the touchstart event incase the user tries to scroll
395                  * while the display is already scrolling
396                  */
397                 $(this.dom.scroller).bind('touchstart', function () {
398                         that._fnScroll.call( that );
399                 } );
400                 
401                 /* Update the scroller when the DataTable is redrawn */
402                 this.s.dt.aoDrawCallback.push( {
403                         "fn": function () {
404                                 if ( that.s.dt.bInitialised ) {
405                                         that._fnDrawCallback.call( that );
406                                 }
407                         },
408                         "sName": "Scroller"
409                 } );
410                 
411                 /* Add a state saving parameter to the DT state saving so we can restore the exact
412                  * position of the scrolling
413                  */
414                 this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
415                         oData.iScroller = that.dom.scroller.scrollTop;
416                 }, "Scroller_State" );
417         },
418
419
420         /**
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.
425          *  @returns {void}
426          *  @private
427          */
428         "_fnScroll": function ()
429         {
430                 var 
431                         that = this,
432                         iScrollTop = this.dom.scroller.scrollTop,
433                         iTopRow;
434
435                 /* If the table has been sorted or filtered, then we use the redraw that
436                  * DataTables as done, rather than performing our own
437                  */
438                 if ( this.s.dt.bFiltered || this.s.dt.bSorted )
439                 {
440                         return;
441                 }
442
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                 /* Update the table's information display for what is now in the viewport */
455                 this._fnInfo();
456
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
459                  */
460                 clearTimeout( this.s.stateTO );
461                 this.s.stateTO = setTimeout( function () {
462                         that.s.dt.oApi._fnSaveState( that.s.dt );
463                 }, 250 );
464
465                 /* Check if the scroll point is outside the trigger boundary which would required
466                  * a DataTables redraw
467                  */
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                         {
474                                 /* At the start of the table */
475                                 iTopRow = 0;
476                         }
477                         else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() )
478                         {
479                                 /* At the end of the table */
480                                 iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
481                                 if ( iTopRow < 0 )
482                                 {
483                                         iTopRow = 0;
484                                 }
485                         }
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                                  */
491                                 iTopRow++;
492                         }
493
494                         if ( iTopRow != this.s.dt._iDisplayStart )
495                         {
496                                 /* Cache the new table position for quick lookups */
497                                 this.s.tableTop = $(this.s.dt.nTable).offset().top;
498                                 this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
499                                 
500                                 /* Do the DataTables redraw based on the calculated start point - note that when
501                                  * using server-side processing we introduce a small delay to not DoS the server...
502                                  */
503                                 if ( this.s.dt.oFeatures.bServerSide ) {
504                                         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 );
510                                 }
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 );
516                                 }
517
518                                 if ( this.s.trace )
519                                 {
520                                         console.log( 'Scroll forcing redraw - top DT render row: '+ iTopRow );
521                                 }
522                         }
523                 }
524         },
525
526
527         /**
528          * Draw callback function which is fired when the DataTable is redrawn. The main function of
529          * this method is to position the drawn table correctly the scrolling container for the rows
530          * that is displays as a result of the scrolling position.
531          *  @returns {void}
532          *  @private
533          */
534         "_fnDrawCallback": function ()
535         {
536                 var
537                         that = this,
538                         iScrollTop = this.dom.scroller.scrollTop,
539                         iScrollBottom = iScrollTop + this.s.viewportHeight;
540                 
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";
545                 
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;
551                 }
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;
555                 }
556
557                 this.dom.table.style.top = iTableTop+"px";
558
559                 /* Cache some information for the scroller */
560                 this.s.tableTop = iTableTop;
561                 this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
562
563                 this.s.redrawTop = iScrollTop - ( (iScrollTop - this.s.tableTop) * this.s.boundaryScale );
564                 this.s.redrawBottom = iScrollTop + ( (this.s.tableBottom - iScrollBottom) * this.s.boundaryScale );
565
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                 }
575
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                  */
580                 setTimeout( function () {
581                         that._fnInfo.call( that );
582                 }, 0 );
583
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                  */
588                 if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
589                          typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
590                 {
591                         if ( (this.s.dt.sAjaxSource !== null && this.s.dt.iDraw == 2) ||
592                              (this.s.dt.sAjaxSource === null && this.s.dt.iDraw == 1) )
593                         {
594                                 setTimeout( function () {
595                                         $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
596                                         that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (that.s.viewportHeight/2);
597                                 }, 0 );
598                         }
599                 }
600         },
601
602
603         /**
604          * Automatic calculation of table row height. This is just a little tricky here as using
605          * initialisation DataTables has tale the table out of the document, so we need to create
606          * a new table and insert it into the document, calculate the row height and then whip the
607          * table out.
608          *  @returns {void}
609          *  @private
610          */
611         "_fnCalcRowHeight": function ()
612         {
613                 var nTable = this.s.dt.nTable.cloneNode( false );
614                 var nContainer = $(
615                         '<div class="'+this.s.dt.oClasses.sWrapper+' DTS">'+
616                                 '<div class="'+this.s.dt.oClasses.sScrollWrapper+'">'+
617                                         '<div class="'+this.s.dt.oClasses.sScrollBody+'"></div>'+
618                                 '</div>'+
619                         '</div>'
620                 )[0];
621
622                 $(nTable).append(
623                         '<tbody>'+
624                                 '<tr>'+
625                                         '<td>&nbsp;</td>'+
626                                 '</tr>'+
627                         '</tbody>'
628                 );
629
630                 $('div.'+this.s.dt.oClasses.sScrollBody, nContainer).append( nTable );
631
632                 document.body.appendChild( nContainer );
633                 this.s.rowHeight = $('tbody tr', nTable).outerHeight();
634                 document.body.removeChild( nContainer );
635         },
636
637
638         /**
639          * Update any information elements that are controlled by the DataTable based on the scrolling
640          * viewport and what rows are visible in it. This function basically acts in the same way as
641          * _fnUpdateInfo in DataTables, and effectively replaces that function.
642          *  @returns {void}
643          *  @private
644          */
645         "_fnInfo": function ()
646         {
647                 if ( !this.s.dt.oFeatures.bInfo )
648                 {
649                         return;
650                 }
651                 
652                 var
653                         dt = this.s.dt,
654                         iScrollTop = this.dom.scroller.scrollTop,
655                         iStart = this.fnPixelsToRow(iScrollTop)+1, 
656                         iMax = dt.fnRecordsTotal(),
657                         iTotal = dt.fnRecordsDisplay(),
658                         iPossibleEnd = this.fnPixelsToRow(iScrollTop+$(this.dom.scroller).height()),
659                         iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
660                         sStart = dt.fnFormatNumber( iStart ),
661                         sEnd = dt.fnFormatNumber( iEnd ),
662                         sMax = dt.fnFormatNumber( iMax ),
663                         sTotal = dt.fnFormatNumber( iTotal ),
664                         sOut;
665                 
666                 if ( dt.fnRecordsDisplay() === 0 && 
667                            dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
668                 {
669                         /* Empty record set */
670                         sOut = dt.oLanguage.sInfoEmpty+ dt.oLanguage.sInfoPostFix;
671                 }
672                 else if ( dt.fnRecordsDisplay() === 0 )
673                 {
674                         /* Rmpty record set after filtering */
675                         sOut = dt.oLanguage.sInfoEmpty +' '+ 
676                                 dt.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
677                                         dt.oLanguage.sInfoPostFix;
678                 }
679                 else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
680                 {
681                         /* Normal record set */
682                         sOut = dt.oLanguage.sInfo.
683                                         replace('_START_', sStart).
684                                         replace('_END_',   sEnd).
685                                         replace('_TOTAL_', sTotal)+ 
686                                 dt.oLanguage.sInfoPostFix;
687                 }
688                 else
689                 {
690                         /* Record set after filtering */
691                         sOut = dt.oLanguage.sInfo.
692                                         replace('_START_', sStart).
693                                         replace('_END_',   sEnd).
694                                         replace('_TOTAL_', sTotal) +' '+ 
695                                 dt.oLanguage.sInfoFiltered.replace('_MAX_', 
696                                         dt.fnFormatNumber(dt.fnRecordsTotal()))+ 
697                                 dt.oLanguage.sInfoPostFix;
698                 }
699                 
700                 var n = dt.aanFeatures.i;
701                 if ( typeof n != 'undefined' )
702                 {
703                         for ( var i=0, iLen=n.length ; i<iLen ; i++ )
704                         {
705                                 $(n[i]).html( sOut );
706                         }
707                 }
708         }
709 };
710
711
712
713 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
714  * Statics
715  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
716
717
718 /**
719  * Scroller default settings for initialisation
720  *  @namespace
721  *  @static
722  */
723 Scroller.oDefaults = {
724         /** 
725          * Indicate if Scroller show show trace information on the console or not. This can be 
726          * useful when debugging Scroller or if just curious as to what it is doing, but should
727          * be turned off for production.
728          *  @type     bool
729          *  @default  false
730          *  @static
731          *  @example
732          *    var oTable = $('#example').dataTable( {
733          *        "sScrollY": "200px",
734          *        "sDom": "frtiS",
735          *        "bDeferRender": true,
736          *        "oScroller": {
737          *          "trace": true
738          *        }
739          *    } );
740          */
741         "trace": false,
742
743         /** 
744          * Scroller will attempt to automatically calculate the height of rows for it's internal
745          * calculations. However the height that is used can be overridden using this parameter.
746          *  @type     int|string
747          *  @default  auto
748          *  @static
749          *  @example
750          *    var oTable = $('#example').dataTable( {
751          *        "sScrollY": "200px",
752          *        "sDom": "frtiS",
753          *        "bDeferRender": true,
754          *        "oScroller": {
755          *          "rowHeight": 30
756          *        }
757          *    } );
758          */
759         "rowHeight": "auto",
760
761         /** 
762          * When using server-side processing, Scroller will wait a small amount of time to allow
763          * the scrolling to finish before requesting more data from the server. This prevents
764          * you from DoSing your own server! The wait time can be configured by this parameter.
765          *  @type     int
766          *  @default  200
767          *  @static
768          *  @example
769          *    var oTable = $('#example').dataTable( {
770          *        "sScrollY": "200px",
771          *        "sDom": "frtiS",
772          *        "bDeferRender": true,
773          *        "oScroller": {
774          *          "serverWait": 100
775          *        }
776          *    } );
777          */
778         "serverWait": 200,
779
780         /** 
781          * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
782          * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
783          * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
784          * The value is based upon the number of rows that can be displayed in the viewport (i.e. 
785          * a value of 1), and will apply the display range to records before before and after the
786          * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
787          * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
788          * of rows after the current viewport. Adjusting this value can be useful for ensuring 
789          * smooth scrolling based on your data set.
790          *  @type     int
791          *  @default  7
792          *  @static
793          *  @example
794          *    var oTable = $('#example').dataTable( {
795          *        "sScrollY": "200px",
796          *        "sDom": "frtiS",
797          *        "bDeferRender": true,
798          *        "oScroller": {
799          *          "displayBuffer": 10
800          *        }
801          *    } );
802          */
803         "displayBuffer": 9,
804
805         /** 
806          * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
807          * typically does before you reach the end of the currently loaded data set (in order to
808          * allow the data to look continuous to a user scrolling through the data). If given as 0
809          * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
810          * redraw the table until the currently loaded data has all been shown. You will want 
811          * something in the middle - the default factor of 0.5 is usually suitable.
812          *  @type     float
813          *  @default  0.5
814          *  @static
815          *  @example
816          *    var oTable = $('#example').dataTable( {
817          *        "sScrollY": "200px",
818          *        "sDom": "frtiS",
819          *        "bDeferRender": true,
820          *        "oScroller": {
821          *          "boundaryScale": 0.75
822          *        }
823          *    } );
824          */
825         "boundaryScale": 0.5,
826
827         /** 
828          * Show (or not) the loading element in the background of the table. Note that you should
829          * include the dataTables.scroller.css file for this to be displayed correctly.
830          *  @type     boolean
831          *  @default  false
832          *  @static
833          *  @example
834          *    var oTable = $('#example').dataTable( {
835          *        "sScrollY": "200px",
836          *        "sDom": "frtiS",
837          *        "bDeferRender": true,
838          *        "oScroller": {
839          *          "loadingIndicator": true
840          *        }
841          *    } );
842          */
843         "loadingIndicator": false
844 };
845
846
847
848 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
849  * Constants
850  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
851
852
853 /**
854  * Name of this class
855  *  @type     String
856  *  @default  Scroller
857  *  @static
858  */
859 Scroller.prototype.CLASS = "Scroller";
860
861
862 /**
863  * Scroller version
864  *  @type      String
865  *  @default   See code
866  *  @static
867  */
868 Scroller.VERSION = "1.1.0";
869 Scroller.prototype.VERSION = Scroller.VERSION;
870
871
872
873 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
874  * Initialisation
875  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
876
877 /*
878  * Register a new feature with DataTables
879  */
880 if ( typeof $.fn.dataTable == "function" &&
881      typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
882      $.fn.dataTableExt.fnVersionCheck('1.9.0') )
883 {
884         $.fn.dataTableExt.aoFeatures.push( {
885                 "fnInit": function( oDTSettings ) {
886                         var init = (typeof oDTSettings.oInit.oScroller == 'undefined') ?
887                                 {} : oDTSettings.oInit.oScroller;
888                         var oScroller = new Scroller( oDTSettings, init );
889                         return oScroller.dom.wrapper;
890                 },
891                 "cFeature": "S",
892                 "sFeature": "Scroller"
893         } );
894 }
895 else
896 {
897         alert( "Warning: Scroller requires DataTables 1.9.0 or greater - www.datatables.net/download");
898 }
899
900
901 // Attach Scroller to DataTables so it can be accessed as an 'extra'
902 $.fn.dataTable.Scroller = Scroller;
903
904 })(jQuery, window, document);