Add datatables-1.9.4 and jquery-1.10.2 libraries
[proteocache.git] / webapp / resources / datatables-1.9.4 / extras / AutoFill / media / js / AutoFill.js
1 /*
2  * File:        AutoFill.js
3  * Version:     1.1.2
4  * CVS:         $Id$
5  * Description: AutoFill for DataTables
6  * Author:      Allan Jardine (www.sprymedia.co.uk)
7  * Created:     Mon  6 Sep 2010 16:54:41 BST
8  * Modified:    $Date$ by $Author$
9  * Language:    Javascript
10  * License:     GPL v2 or BSD 3 point
11  * Project:     DataTables
12  * Contact:     www.sprymedia.co.uk/contact
13  * 
14  * Copyright 2010-2011 Allan Jardine, all rights reserved.
15  *
16  * This source file is free software, under either the GPL v2 license or a
17  * BSD style license, available at:
18  *   http://datatables.net/license_gpl2
19  *   http://datatables.net/license_bsd
20  *
21  */
22
23 /* Global scope for AutoFill */
24 var AutoFill;
25
26 (function($) {
27
28 /** 
29  * AutoFill provides Excel like auto fill features for a DataTable
30  * @class AutoFill
31  * @constructor
32  * @param {object} DataTables settings object
33  * @param {object} Configuration object for AutoFill
34  */
35 AutoFill = function( oDT, oConfig )
36 {
37         /* Santiy check that we are a new instance */
38         if ( !this.CLASS || this.CLASS != "AutoFill" )
39         {
40                 alert( "Warning: AutoFill must be initialised with the keyword 'new'" );
41                 return;
42         }
43
44         if ( !$.fn.dataTableExt.fnVersionCheck('1.7.0') )
45         {
46                 alert( "Warning: AutoFill requires DataTables 1.7 or greater - www.datatables.net/download");
47                 return;
48         }
49         
50         
51         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
52          * Public class variables
53          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
54         
55         /**
56          * @namespace Settings object which contains customisable information for AutoFill instance
57          */
58         this.s = {
59                 /**
60                  * @namespace Cached information about the little dragging icon (the filler)
61                  */
62                 "filler": {
63                         "height": 0,
64                         "width": 0
65                 },
66                 
67                 /**
68                  * @namespace Cached information about the border display
69                  */
70                 "border": {
71                         "width": 2
72                 },
73                 
74                 /**
75                  * @namespace Store for live information for the current drag
76                  */
77                 "drag": {
78                         "startX": -1,
79                         "startY": -1,
80                         "startTd": null,
81                         "endTd": null,
82                         "dragging": false
83                 },
84                 
85                 /**
86                  * @namespace Data cache for information that we need for scrolling the screen when we near
87                  *   the edges
88                  */
89                 "screen": {
90                         "interval": null,
91                         "y": 0,
92                         "height": 0,
93                         "scrollTop": 0
94                 },
95                 
96                 /**
97                  * @namespace Data cache for the position of the DataTables scrolling element (when scrolling
98                  *   is enabled)
99                  */
100                 "scroller": {
101                         "top": 0,
102                         "bottom": 0
103                 },
104                 
105                 
106                 /**
107                  * @namespace Information stored for each column. An array of objects
108                  */
109                 "columns": []
110         };
111         
112         
113         /**
114          * @namespace Common and useful DOM elements for the class instance
115          */
116         this.dom = {
117                 "table": null,
118                 "filler": null,
119                 "borderTop": null,
120                 "borderRight": null,
121                 "borderBottom": null,
122                 "borderLeft": null,
123                 "currentTarget": null
124         };
125         
126         
127         
128         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
129          * Public class methods
130          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
131         
132         /**
133          * Retreieve the settings object from an instance
134          *  @method fnSettings
135          *  @returns {object} AutoFill settings object
136          */
137         this.fnSettings = function () {
138                 return this.s;
139         };
140         
141         
142         /* Constructor logic */
143         this._fnInit( oDT, oConfig );
144         return this;
145 };
146
147
148
149 AutoFill.prototype = {
150         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
151          * Private methods (they are of course public in JS, but recommended as private)
152          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
153         
154         /**
155          * Initialisation
156          *  @method _fnInit
157          *  @param {object} oDT DataTables settings object
158          *  @param {object} oConfig Configuration object for AutoFill
159          *  @returns void
160          */
161         "_fnInit": function ( oDT, oConfig )
162         {
163                 var
164                         that = this,
165                         i, iLen;
166                 
167                 /*
168                  * Settings
169                  */
170                 this.s.dt = oDT.fnSettings();
171                 
172                 this.dom.table = this.s.dt.nTable;
173                 
174                 /* Add and configure the columns */
175                 for ( i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ )
176                 {
177                         this._fnAddColumn( i );
178                 }
179                 
180                 if ( typeof oConfig != 'undefined' && typeof oConfig.aoColumnDefs != 'undefined' )
181                 {
182                         this._fnColumnDefs( oConfig.aoColumnDefs );
183                 }
184                 
185                 if ( typeof oConfig != 'undefined' && typeof oConfig.aoColumns != 'undefined' )
186                 {
187                         this._fnColumnsAll( oConfig.aoColumns );
188                 }
189                 
190                 
191                 /*
192                  * DOM
193                  */
194                 
195                 /* Auto Fill click and drag icon */
196                 var filler = document.createElement('div');
197                 filler.className = "AutoFill_filler";
198                 document.body.appendChild( filler );
199                 this.dom.filler = filler;
200                 
201                 filler.style.display = "block";
202                 this.s.filler.height = $(filler).height();
203                 this.s.filler.width = $(filler).width();
204                 filler.style.display = "none";
205                 
206                 /* Border display - one div for each side. We can't just use a single one with a border, as
207                  * we want the events to effectively pass through the transparent bit of the box
208                  */
209                 var border;
210                 var appender = document.body;
211                 if ( that.s.dt.oScroll.sY !== "" )
212                 {
213                         that.s.dt.nTable.parentNode.style.position = "relative";
214                         appender = that.s.dt.nTable.parentNode;
215                 }
216                 
217                 border = document.createElement('div');
218                 border.className = "AutoFill_border";
219                 appender.appendChild( border );
220                 this.dom.borderTop = border;
221                 
222                 border = document.createElement('div');
223                 border.className = "AutoFill_border";
224                 appender.appendChild( border );
225                 this.dom.borderRight = border;
226                 
227                 border = document.createElement('div');
228                 border.className = "AutoFill_border";
229                 appender.appendChild( border );
230                 this.dom.borderBottom = border;
231                 
232                 border = document.createElement('div');
233                 border.className = "AutoFill_border";
234                 appender.appendChild( border );
235                 this.dom.borderLeft = border;
236                 
237                 /*
238                  * Events
239                  */
240                 
241                 $(filler).mousedown( function (e) {
242                         this.onselectstart = function() { return false; };
243                         that._fnFillerDragStart.call( that, e );
244                         return false;
245                 } );
246                 
247                 $('tbody>tr>td', this.dom.table).live( 'mouseover mouseout', function (e) {
248                         that._fnFillerDisplay.call( that, e );
249                 } );
250         },
251         
252         
253         "_fnColumnDefs": function ( aoColumnDefs )
254         {
255                 var
256                         i, j, k, iLen, jLen, kLen,
257                         aTargets;
258                 
259                 /* Loop over the column defs array - loop in reverse so first instace has priority */
260                 for ( i=aoColumnDefs.length-1 ; i>=0 ; i-- )
261                 {
262                         /* Each column def can target multiple columns, as it is an array */
263                         aTargets = aoColumnDefs[i].aTargets;
264                         for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
265                         {
266                                 if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 )
267                                 {
268                                         /* 0+ integer, left to right column counting. */
269                                         this._fnColumnOptions( aTargets[j], aoColumnDefs[i] );
270                                 }
271                                 else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 )
272                                 {
273                                         /* Negative integer, right to left column counting */
274                                         this._fnColumnOptions( this.s.dt.aoColumns.length+aTargets[j], aoColumnDefs[i] );
275                                 }
276                                 else if ( typeof aTargets[j] == 'string' )
277                                 {
278                                         /* Class name matching on TH element */
279                                         for ( k=0, kLen=this.s.dt.aoColumns.length ; k<kLen ; k++ )
280                                         {
281                                                 if ( aTargets[j] == "_all" ||
282                                                      this.s.dt.aoColumns[k].nTh.className.indexOf( aTargets[j] ) != -1 )
283                                                 {
284                                                         this._fnColumnOptions( k, aoColumnDefs[i] );
285                                                 }
286                                         }
287                                 }
288                         }
289                 }
290         },
291                 
292                 
293         "_fnColumnsAll": function ( aoColumns )
294         {
295                 for ( var i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ )
296                 {
297                         this._fnColumnOptions( i, aoColumns[i] );
298                 }
299         },
300         
301         
302         "_fnAddColumn": function ( i )
303         {
304                 this.s.columns[i] = {
305                         "enable": true,
306                         "read": this._fnReadCell,
307                         "write": this._fnWriteCell,
308                         "step": this._fnStep,
309                         "complete": null
310                 };
311         },
312         
313         "_fnColumnOptions": function ( i, opts )
314         {
315                 if ( typeof opts.bEnable != 'undefined' )
316                 {
317                         this.s.columns[i].enable = opts.bEnable;
318                 }
319                 
320                 if ( typeof opts.fnRead != 'undefined' )
321                 {
322                         this.s.columns[i].read = opts.fnRead;
323                 }
324                 
325                 if ( typeof opts.fnWrite != 'undefined' )
326                 {
327                         this.s.columns[i].write = opts.fnWrite;
328                 }
329                 
330                 if ( typeof opts.fnStep != 'undefined' )
331                 {
332                         this.s.columns[i].step = opts.fnStep;
333                 }
334                 
335                 if ( typeof opts.fnCallback != 'undefined' )
336                 {
337                         this.s.columns[i].complete = opts.fnCallback;
338                 }
339         },
340         
341         
342         /**
343          * Find out the coordinates of a given TD cell in a table
344          *  @method  _fnTargetCoords
345          *  @param   {Node} nTd
346          *  @returns {Object} x and y properties, for the position of the cell in the tables DOM
347          */
348         "_fnTargetCoords": function ( nTd )
349         {
350                 var nTr = $(nTd).parents('tr')[0];
351                 
352                 return {
353                         "x": $('td', nTr).index(nTd),
354                         "y": $('tr', nTr.parentNode).index(nTr)
355                 };
356         },
357         
358         
359         /**
360          * Display the border around one or more cells (from start to end)
361          *  @method  _fnUpdateBorder
362          *  @param   {Node} nStart Starting cell
363          *  @param   {Node} nEnd Ending cell
364          *  @returns void
365          */
366         "_fnUpdateBorder": function ( nStart, nEnd )
367         {
368                 var
369                         border = this.s.border.width,
370                         offsetStart = $(nStart).offset(),
371                         offsetEnd = $(nEnd).offset(),
372                         x1 = offsetStart.left - border,
373                         x2 = offsetEnd.left + $(nEnd).outerWidth(),
374                         y1 = offsetStart.top - border,
375                         y2 = offsetEnd.top + $(nEnd).outerHeight(),
376                         width = offsetEnd.left + $(nEnd).outerWidth() - offsetStart.left + (2*border),
377                         height = offsetEnd.top + $(nEnd).outerHeight() - offsetStart.top + (2*border),
378                         oStyle;
379                 
380                 if ( this.s.dt.oScroll.sY !== "" )
381                 {
382                         /* The border elements are inside the DT scroller - so position relative to that */
383                         var
384                                 offsetScroll = $(this.s.dt.nTable.parentNode).offset(),
385                                 scrollTop = $(this.s.dt.nTable.parentNode).scrollTop(),
386                                 scrollLeft = $(this.s.dt.nTable.parentNode).scrollLeft();
387                         
388                         x1 -= offsetScroll.left - scrollLeft;
389                         x2 -= offsetScroll.left - scrollLeft;
390                         y1 -= offsetScroll.top - scrollTop;
391                         y2 -= offsetScroll.top - scrollTop;
392                 }
393                 
394                 /* Top */
395                 oStyle = this.dom.borderTop.style;
396                 oStyle.top = y1+"px";
397                 oStyle.left = x1+"px";
398                 oStyle.height = this.s.border.width+"px";
399                 oStyle.width = width+"px";
400                 
401                 /* Bottom */
402                 oStyle = this.dom.borderBottom.style;
403                 oStyle.top = y2+"px";
404                 oStyle.left = x1+"px";
405                 oStyle.height = this.s.border.width+"px";
406                 oStyle.width = width+"px";
407                 
408                 /* Left */
409                 oStyle = this.dom.borderLeft.style;
410                 oStyle.top = y1+"px";
411                 oStyle.left = x1+"px";
412                 oStyle.height = height+"px";
413                 oStyle.width = this.s.border.width+"px";
414                 
415                 /* Right */
416                 oStyle = this.dom.borderRight.style;
417                 oStyle.top = y1+"px";
418                 oStyle.left = x2+"px";
419                 oStyle.height = height+"px";
420                 oStyle.width = this.s.border.width+"px";
421         },
422         
423         
424         /**
425          * Mouse down event handler for starting a drag
426          *  @method  _fnFillerDragStart
427          *  @param   {Object} e Event object
428          *  @returns void
429          */
430         "_fnFillerDragStart": function (e)
431         {
432                 var that = this;
433                 var startingTd = this.dom.currentTarget;
434                 
435                 this.s.drag.dragging = true;
436                 
437                 that.dom.borderTop.style.display = "block";
438                 that.dom.borderRight.style.display = "block";
439                 that.dom.borderBottom.style.display = "block";
440                 that.dom.borderLeft.style.display = "block";
441                 
442                 var coords = this._fnTargetCoords( startingTd );
443                 this.s.drag.startX = coords.x;
444                 this.s.drag.startY = coords.y;
445                 
446                 this.s.drag.startTd = startingTd;
447                 this.s.drag.endTd = startingTd;
448                 
449                 this._fnUpdateBorder( startingTd, startingTd );
450                 
451                 $(document).bind('mousemove.AutoFill', function (e) {
452                         that._fnFillerDragMove.call( that, e );
453                 } );
454                 
455                 $(document).bind('mouseup.AutoFill', function (e) {
456                         that._fnFillerFinish.call( that, e );
457                 } );
458                 
459                 /* Scrolling information cache */
460                 this.s.screen.y = e.pageY;
461                 this.s.screen.height = $(window).height();
462                 this.s.screen.scrollTop = $(document).scrollTop();
463                 
464                 if ( this.s.dt.oScroll.sY !== "" )
465                 {
466                         this.s.scroller.top = $(this.s.dt.nTable.parentNode).offset().top;
467                         this.s.scroller.bottom = this.s.scroller.top + $(this.s.dt.nTable.parentNode).height();
468                 }
469                 
470                 /* Scrolling handler - we set an interval (which is cancelled on mouse up) which will fire
471                  * regularly and see if we need to do any scrolling
472                  */
473                 this.s.screen.interval = setInterval( function () {
474                         var iScrollTop = $(document).scrollTop();
475                         var iScrollDelta = iScrollTop - that.s.screen.scrollTop;
476                         that.s.screen.y += iScrollDelta;
477                         
478                         if ( that.s.screen.height - that.s.screen.y + iScrollTop < 50 )
479                         {
480                                 $('html, body').animate( {
481                                         "scrollTop": iScrollTop + 50
482                                 }, 240, 'linear' );
483                         }
484                         else if ( that.s.screen.y - iScrollTop < 50 )
485                         {
486                                 $('html, body').animate( {
487                                         "scrollTop": iScrollTop - 50
488                                 }, 240, 'linear' );
489                         }
490                         
491                         if ( that.s.dt.oScroll.sY !== "" )
492                         {
493                                 if ( that.s.screen.y > that.s.scroller.bottom - 50 )
494                                 {
495                                         $(that.s.dt.nTable.parentNode).animate( {
496                                                 "scrollTop": $(that.s.dt.nTable.parentNode).scrollTop() + 50
497                                         }, 240, 'linear' );
498                                 }
499                                 else if ( that.s.screen.y < that.s.scroller.top + 50 )
500                                 {
501                                         $(that.s.dt.nTable.parentNode).animate( {
502                                                 "scrollTop": $(that.s.dt.nTable.parentNode).scrollTop() - 50
503                                         }, 240, 'linear' );
504                                 }
505                         }
506                 }, 250 );
507         },
508         
509         
510         /**
511          * Mouse move event handler for during a move. See if we want to update the display based on the
512          * new cursor position
513          *  @method  _fnFillerDragMove
514          *  @param   {Object} e Event object
515          *  @returns void
516          */
517         "_fnFillerDragMove": function (e)
518         {
519                 if ( e.target && e.target.nodeName.toUpperCase() == "TD" &&
520                         e.target != this.s.drag.endTd )
521                 {
522                         var coords = this._fnTargetCoords( e.target );
523                         
524                         if ( coords.x != this.s.drag.startX )
525                         {
526                                 e.target = $('tbody>tr:eq('+coords.y+')>td:eq('+this.s.drag.startX+')', this.dom.table)[0];
527                                 coords = this._fnTargetCoords( e.target );
528                         }
529                         
530                         if ( coords.x == this.s.drag.startX )
531                         {
532                                 var drag = this.s.drag;
533                                 drag.endTd = e.target;
534                                 
535                                 if ( coords.y >= this.s.drag.startY )
536                                 {
537                                         this._fnUpdateBorder( drag.startTd, drag.endTd );
538                                 }
539                                 else
540                                 {
541                                         this._fnUpdateBorder( drag.endTd, drag.startTd );
542                                 }
543                                 this._fnFillerPosition( e.target );
544                         }
545                 }
546                 
547                 /* Update the screen information so we can perform scrolling */
548                 this.s.screen.y = e.pageY;
549                 this.s.screen.scrollTop = $(document).scrollTop();
550                 
551                 if ( this.s.dt.oScroll.sY !== "" )
552                 {
553                         this.s.scroller.scrollTop = $(this.s.dt.nTable.parentNode).scrollTop();
554                         this.s.scroller.top = $(this.s.dt.nTable.parentNode).offset().top;
555                         this.s.scroller.bottom = this.s.scroller.top + $(this.s.dt.nTable.parentNode).height();
556                 }
557         },
558         
559         
560         /**
561          * Mouse release handler - end the drag and take action to update the cells with the needed values
562          *  @method  _fnFillerFinish
563          *  @param   {Object} e Event object
564          *  @returns void
565          */
566         "_fnFillerFinish": function (e)
567         {
568                 var that = this;
569                 
570                 $(document).unbind('mousemove.AutoFill');
571                 $(document).unbind('mouseup.AutoFill');
572                 
573                 this.dom.borderTop.style.display = "none";
574                 this.dom.borderRight.style.display = "none";
575                 this.dom.borderBottom.style.display = "none";
576                 this.dom.borderLeft.style.display = "none";
577                 
578                 this.s.drag.dragging = false;
579                 
580                 clearInterval( this.s.screen.interval );
581                 
582                 var coordsStart = this._fnTargetCoords( this.s.drag.startTd );
583                 var coordsEnd = this._fnTargetCoords( this.s.drag.endTd );
584                 var aTds = [];
585                 var bIncrement;
586                 
587                 if ( coordsStart.y <= coordsEnd.y )
588                 {
589                         bIncrement = true;
590                         for ( i=coordsStart.y ; i<=coordsEnd.y ; i++ )
591                         {
592                                 aTds.push( $('tbody>tr:eq('+i+')>td:eq('+coordsStart.x+')', this.dom.table)[0] );
593                         }
594                 }
595                 else
596                 {
597                         bIncrement = false;
598                         for ( i=coordsStart.y ; i>=coordsEnd.y ; i-- )
599                         {
600                                 aTds.push( $('tbody>tr:eq('+i+')>td:eq('+coordsStart.x+')', this.dom.table)[0] );
601                         }
602                 }
603                 
604                 
605                 var iColumn = coordsStart.x;
606                 var bLast = false;
607                 var aoEdited = [];
608                 var sStart = this.s.columns[iColumn].read.call( this, this.s.drag.startTd );
609                 var oPrepped = this._fnPrep( sStart );
610                 
611                 for ( i=0, iLen=aTds.length ; i<iLen ; i++ )
612                 {
613                         if ( i==iLen-1 )
614                         {
615                                 bLast = true;
616                         }
617                         
618                         var original = this.s.columns[iColumn].read.call( this, aTds[i] );
619                         var step = this.s.columns[iColumn].step.call( this, aTds[i], oPrepped, i, bIncrement, 
620                                 'SPRYMEDIA_AUTOFILL_STEPPER' );
621                         this.s.columns[iColumn].write.call( this, aTds[i], step, bLast );
622                         
623                         aoEdited.push( {
624                                 "td": aTds[i],
625                                 "newValue": step,
626                                 "oldValue": original
627                         } );
628                 }
629                 
630                 if ( this.s.columns[iColumn].complete !== null )
631                 {
632                         this.s.columns[iColumn].complete.call( this, aoEdited );
633                 }
634         },
635         
636         
637         /**
638          * Chunk a string such that it can be filled in by the stepper function
639          *  @method  _fnPrep
640          *  @param   {String} sStr String to prep
641          *  @returns {Object} with parameters, iStart, sStr and sPostFix
642          */
643         "_fnPrep": function ( sStr )
644         {
645                 var aMatch = sStr.match(/[\d\.]+/g);
646                 if ( !aMatch || aMatch.length === 0 )
647                 {
648                         return {
649                                 "iStart": 0,
650                                 "sStr": sStr,
651                                 "sPostFix": ""
652                         };
653                 }
654                 
655                 var sLast = aMatch[ aMatch.length-1 ];
656                 var num = parseInt(sLast, 10);
657                 var regex = new RegExp( '^(.*)'+sLast+'(.*?)$' );
658                 var decimal = sLast.match(/\./) ? "."+sLast.split('.')[1] : "";
659                 
660                 return {
661                         "iStart": num,
662                         "sStr": sStr.replace(regex, "$1SPRYMEDIA_AUTOFILL_STEPPER$2"),
663                         "sPostFix": decimal
664                 };
665         },
666         
667         
668         /**
669          * Render a string for it's position in the table after the drag (incrememt numbers)
670          *  @method  _fnStep
671          *  @param   {Node} nTd Cell being written to
672          *  @param   {Object} oPrepped Prepared object for the stepper (from _fnPrep)
673          *  @param   {Int} iDiff Step difference
674          *  @param   {Boolean} bIncrement Increment (true) or decriment (false)
675          *  @param   {String} sToken Token to replace
676          *  @returns {String} Rendered information
677          */
678         "_fnStep": function ( nTd, oPrepped, iDiff, bIncrement, sToken )
679         {
680                 var iReplace = bIncrement ? (oPrepped.iStart+iDiff) : (oPrepped.iStart-iDiff);
681                 if ( isNaN(iReplace) )
682                 {
683                         iReplace = "";
684                 }
685                 return oPrepped.sStr.replace( sToken, iReplace+oPrepped.sPostFix );
686         },
687         
688         
689         /**
690          * Read informaiton from a cell, possibly using live DOM elements if suitable
691          *  @method  _fnReadCell
692          *  @param   {Node} nTd Cell to read
693          *  @returns {String} Read value
694          */
695         "_fnReadCell": function ( nTd )
696         {
697                 var jq = $('input', nTd);
698                 if ( jq.length > 0 )
699                 {
700                         return $(jq).val();
701                 }
702                 
703                 jq = $('select', nTd);
704                 if ( jq.length > 0 )
705                 {
706                         return $(jq).val();
707                 }
708                 
709                 return nTd.innerHTML;
710         },
711         
712         
713         /**
714          * Write informaiton to a cell, possibly using live DOM elements if suitable
715          *  @method  _fnWriteCell
716          *  @param   {Node} nTd Cell to write
717          *  @param   {String} sVal Value to write
718          *  @param   {Boolean} bLast Flag to show if this is that last update
719          *  @returns void
720          */
721         "_fnWriteCell": function ( nTd, sVal, bLast )
722         {
723                 var jq = $('input', nTd);
724                 if ( jq.length > 0 )
725                 {
726                         $(jq).val( sVal );
727                         return;
728                 }
729                 
730                 jq = $('select', nTd);
731                 if ( jq.length > 0 )
732                 {
733                         $(jq).val( sVal );
734                         return;
735                 }
736                 
737                 var pos = this.s.dt.oInstance.fnGetPosition( nTd );
738                 this.s.dt.oInstance.fnUpdate( sVal, pos[0], pos[2], bLast );
739         },
740         
741         
742         /**
743          * Display the drag handle on mouse over cell
744          *  @method  _fnFillerDisplay
745          *  @param   {Object} e Event object
746          *  @returns void
747          */
748         "_fnFillerDisplay": function (e)
749         {
750                 /* Don't display automatically when dragging */
751                 if ( this.s.drag.dragging)
752                 {
753                         return;
754                 }
755                 
756                 /* Check that we are allowed to AutoFill this column or not */
757                 var nTd = (e.target.nodeName.toLowerCase() == 'td') ? e.target : $(e.target).parents('td')[0];
758                 var iX = this._fnTargetCoords(nTd).x;
759                 if ( !this.s.columns[iX].enable )
760                 {
761                         return;
762                 }
763                 
764                 var filler = this.dom.filler;
765                 if (e.type == 'mouseover')
766                 {
767                         this.dom.currentTarget = nTd;
768                         this._fnFillerPosition( nTd );
769                         
770                         filler.style.display = "block";
771                 }
772                 else if ( !e.relatedTarget || !e.relatedTarget.className.match(/AutoFill/) )
773                 {
774                         filler.style.display = "none";
775                 }
776         },
777         
778         
779         /**
780          * Position the filler icon over a cell
781          *  @method  _fnFillerPosition
782          *  @param   {Node} nTd Cell to position filler icon over
783          *  @returns void
784          */
785         "_fnFillerPosition": function ( nTd )
786         {
787                 var offset = $(nTd).offset();
788                 var filler = this.dom.filler;
789                 filler.style.top = (offset.top - (this.s.filler.height / 2)-1 + $(nTd).outerHeight())+"px";
790                 filler.style.left = (offset.left - (this.s.filler.width / 2)-1 + $(nTd).outerWidth())+"px";
791         }
792 };
793
794
795
796
797 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
798  * Constants
799  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
800
801 /**
802  * Name of this class
803  *  @constant CLASS
804  *  @type     String
805  *  @default  AutoFill
806  */
807 AutoFill.prototype.CLASS = "AutoFill";
808
809
810 /**
811  * AutoFill version
812  *  @constant  VERSION
813  *  @type      String
814  *  @default   1.1.2
815  */
816 AutoFill.VERSION = "1.1.2";
817 AutoFill.prototype.VERSION = AutoFill.VERSION;
818
819
820 })(jQuery);