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