1 /* 2 * File: ColReorder.js 3 * Version: 1.0.1 4 * CVS: $Id$ 5 * Description: Controls for column visiblity in DataTables 6 * Author: Allan Jardine (www.sprymedia.co.uk) 7 * Created: Wed Sep 15 18:23:29 BST 2010 8 * Modified: $Date$ by $Author$ 9 * Language: Javascript 10 * License: LGPL 11 * Project: DataTables 12 * Contact: www.sprymedia.co.uk/contact 13 * 14 * Copyright 2010 Allan Jardine, all rights reserved. 15 * 16 */ 17 18 19 (function($, window, document) { 20 21 22 /** 23 * Switch the key value pairing of an index array to be value key (i.e. the old value is now the 24 * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. 25 * @method fnInvertKeyValues 26 * @param array aIn Array to switch around 27 * @returns array 28 */ 29 function fnInvertKeyValues( aIn ) 30 { 31 var aRet=[]; 32 for ( var i=0, iLen=aIn.length ; i<iLen ; i++ ) 33 { 34 aRet[ aIn[i] ] = i; 35 } 36 return aRet; 37 } 38 39 40 /** 41 * Modify an array by switching the position of two elements 42 * @method fnArraySwitch 43 * @param array aArray Array to consider, will be modified by reference (i.e. no return) 44 * @param int iFrom From point 45 * @param int iTo Insert point 46 * @returns void 47 */ 48 function fnArraySwitch( aArray, iFrom, iTo ) 49 { 50 var mStore = aArray.splice( iFrom, 1 )[0]; 51 aArray.splice( iTo, 0, mStore ); 52 } 53 54 55 /** 56 * Switch the positions of nodes in a parent node (note this is specifically designed for 57 * table rows). Note this function considers all element nodes under the parent! 58 * @method fnDomSwitch 59 * @param string sTag Tag to consider 60 * @param int iFrom Element to move 61 * @param int Point to element the element to (before this point), can be null for append 62 * @returns void 63 */ 64 function fnDomSwitch( nParent, iFrom, iTo ) 65 { 66 var anTags = []; 67 for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ ) 68 { 69 if ( nParent.childNodes[i].nodeType == 1 ) 70 { 71 anTags.push( nParent.childNodes[i] ); 72 } 73 } 74 var nStore = anTags[ iFrom ]; 75 76 if ( iTo !== null ) 77 { 78 nParent.insertBefore( nStore, anTags[iTo] ); 79 } 80 else 81 { 82 nParent.appendChild( nStore ); 83 } 84 } 85 86 87 88 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 89 * DataTables plug-in API functions 90 * 91 * This are required by ColReorder in order to perform the tasks required, and also keep this 92 * code portable, to be used for other column reordering projects with DataTables, if needed. 93 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 94 95 96 /** 97 * Plug-in for DataTables which will reorder the internal column structure by taking the column 98 * from one position (iFrom) and insert it into a given point (iTo). 99 * @method $.fn.dataTableExt.oApi.fnColReorder 100 * @param object oSettings DataTables settings object - automatically added by DataTables! 101 * @param int iFrom Take the column to be repositioned from this point 102 * @param int iTo and insert it into this point 103 * @returns void 104 */ 105 $.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo ) 106 { 107 var i, iLen, j, jLen, iCols=oSettings.aoColumns.length, nTrs; 108 109 /* Sanity check in the input */ 110 if ( iFrom == iTo ) 111 { 112 /* Pointless reorder */ 113 return; 114 } 115 116 if ( iFrom < 0 || iFrom >= iCols ) 117 { 118 this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom ); 119 return; 120 } 121 122 if ( iTo < 0 || iTo >= iCols ) 123 { 124 this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo ); 125 return; 126 } 127 128 /* 129 * Calculate the new column array index, so we have a mapping between the old and new 130 */ 131 var aiMapping = []; 132 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 133 { 134 aiMapping[i] = i; 135 } 136 fnArraySwitch( aiMapping, iFrom, iTo ); 137 var aiInvertMapping = fnInvertKeyValues( aiMapping ); 138 139 140 /* 141 * Convert all internal indexing to the new column order indexes 142 */ 143 /* Sorting */ 144 for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ ) 145 { 146 oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ]; 147 } 148 149 /* Fixed sorting */ 150 if ( oSettings.aaSortingFixed !== null ) 151 { 152 for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ ) 153 { 154 oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ]; 155 } 156 } 157 158 /* Data column sorting (the column which the sort for a given column should take place on) */ 159 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 160 { 161 oSettings.aoColumns[i].iDataSort = aiInvertMapping[ oSettings.aoColumns[i].iDataSort ]; 162 } 163 164 165 /* 166 * Move the DOM elements 167 */ 168 if ( oSettings.aoColumns[iFrom].bVisible ) 169 { 170 /* Calculate the current visible index and the point to insert the node before. The insert 171 * before needs to take into account that there might not be an element to insert before, 172 * in which case it will be null, and an appendChild should be used 173 */ 174 var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom ); 175 var iInsertBeforeIndex = null; 176 177 i = iTo < iFrom ? iTo : iTo + 1; 178 while ( iInsertBeforeIndex === null && i < iCols ) 179 { 180 iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i ); 181 i++; 182 } 183 184 /* Header */ 185 nTrs = oSettings.nTHead.getElementsByTagName('tr'); 186 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 187 { 188 fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex ); 189 } 190 191 /* Footer */ 192 if ( oSettings.nTFoot !== null ) 193 { 194 nTrs = oSettings.nTFoot.getElementsByTagName('tr'); 195 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 196 { 197 fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex ); 198 } 199 } 200 201 /* Body */ 202 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 203 { 204 fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex ); 205 } 206 } 207 208 209 /* 210 * Move the internal array elements 211 */ 212 /* Columns */ 213 fnArraySwitch( oSettings.aoColumns, iFrom, iTo ); 214 215 /* Search columns */ 216 fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo ); 217 218 /* Array array - internal data anodes cache */ 219 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 220 { 221 fnArraySwitch( oSettings.aoData[i]._aData, iFrom, iTo ); 222 fnArraySwitch( oSettings.aoData[i]._anHidden, iFrom, iTo ); 223 } 224 225 226 /* 227 * Update DataTables' event handlers 228 */ 229 230 /* Sort listener */ 231 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 232 { 233 $(oSettings.aoColumns[i].nTh).unbind('click'); 234 this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i ); 235 } 236 237 238 /* 239 * Any extra operations for the other plug-ins 240 */ 241 if ( typeof ColVis != 'undefined' ) 242 { 243 ColVis.fnRebuild( oSettings.oInstance ); 244 } 245 246 if ( typeof oSettings.oInstance._oPluginFixedHeader != 'undefined' ) 247 { 248 oSettings.oInstance._oPluginFixedHeader.fnUpdate(); 249 } 250 }; 251 252 253 254 255 /** 256 * ColReorder provides column visiblity control for DataTables 257 * @class ColReorder 258 * @constructor 259 * @param {object} DataTables object 260 * @param {object} ColReorder options 261 */ 262 ColReorder = function( oTable, oOpts ) 263 { 264 /* Santiy check that we are a new instance */ 265 if ( !this.CLASS || this.CLASS != "ColReorder" ) 266 { 267 alert( "Warning: ColReorder must be initialised with the keyword 'new'" ); 268 } 269 270 if ( typeof oOpts == 'undefined' ) 271 { 272 oOpts = {}; 273 } 274 275 276 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 277 * Public class variables 278 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 279 280 /** 281 * @namespace Settings object which contains customisable information for ColReorder instance 282 */ 283 this.s = { 284 /** 285 * DataTables settings object 286 * @property dt 287 * @type Object 288 * @default null 289 */ 290 dt: null, 291 292 /** 293 * Initialisation object used for this instance 294 * @property init 295 * @type object 296 * @default {} 297 */ 298 init: oOpts, 299 300 /** 301 * Number of columns to fix (not allow to be reordered) 302 * @property fixed 303 * @type int 304 * @default 0 305 */ 306 fixed: 0, 307 308 /** 309 * @namespace Information used for the mouse drag 310 */ 311 mouse: { 312 startX: -1, 313 startY: -1, 314 offsetX: -1, 315 offsetY: -1, 316 target: -1, 317 targetIndex: -1, 318 fromIndex: -1 319 }, 320 321 /** 322 * Information which is used for positioning the insert cusor and knowing where to do the 323 * insert. Array of objects with the properties: 324 * x: x-axis position 325 * to: insert point 326 * @property aoTargets 327 * @type array 328 * @default [] 329 */ 330 aoTargets: [] 331 }; 332 333 334 /** 335 * @namespace Common and useful DOM elements for the class instance 336 */ 337 this.dom = { 338 /** 339 * Dragging element (the one the mouse is moving) 340 * @property drag 341 * @type element 342 * @default null 343 */ 344 drag: null, 345 346 /** 347 * The insert cursor 348 * @property pointer 349 * @type element 350 * @default null 351 */ 352 pointer: null 353 }; 354 355 356 /* Constructor logic */ 357 this.s.dt = oTable.fnSettings(); 358 this._fnConstruct(); 359 360 /* Store the instance for later use */ 361 ColReorder.aoInstances.push( this ); 362 return this; 363 }; 364 365 366 367 ColReorder.prototype = { 368 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 369 * Public methods 370 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 371 372 fnReset: function () 373 { 374 var a = []; 375 for ( var i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ ) 376 { 377 a.push( this.s.dt.aoColumns[i]._ColReorder_iOrigCol ); 378 } 379 380 this._fnOrderColumns( a ); 381 }, 382 383 384 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 385 * Private methods (they are of course public in JS, but recommended as private) 386 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 387 388 /** 389 * Constructor logic 390 * @method _fnConstruct 391 * @returns void 392 * @private 393 */ 394 _fnConstruct: function () 395 { 396 var that = this; 397 var i, iLen; 398 399 /* Columns discounted from reordering - counting left to right */ 400 if ( typeof this.s.init.iFixedColumns != 'undefined' ) 401 { 402 this.s.fixed = this.s.init.iFixedColumns; 403 } 404 405 /* Add event handlers for the drag and drop, and also mark the original column order */ 406 for ( i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ ) 407 { 408 if ( i > this.s.fixed-1 ) 409 { 410 this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh ); 411 } 412 413 /* Mark the original column order for later reference */ 414 this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i; 415 } 416 417 /* State saving */ 418 this.s.dt.aoStateSave.push( { 419 fn: function (oS, sVal) { 420 return that._fnStateSave.call( that, sVal ); 421 }, 422 sName: "ColReorder_State" 423 } ); 424 425 /* An initial column order has been specified */ 426 var aiOrder = null; 427 if ( typeof this.s.init.aiOrder != 'undefined' ) 428 { 429 aiOrder = this.s.init.aiOrder.slice(); 430 } 431 432 /* State loading, overrides the column order given */ 433 if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' && 434 this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length ) 435 { 436 aiOrder = this.s.dt.oLoadedState.ColReorder; 437 } 438 439 /* If we have an order to apply - do so */ 440 if ( aiOrder ) 441 { 442 /* We might be called during or after the DataTables initialisation. If before, then we need 443 * to wait until the draw is done, if after, then do what we need to do right away 444 */ 445 if ( !that.s.dt._bInitComplete ) 446 { 447 var bDone = false; 448 this.s.dt.aoDrawCallback.push( { 449 fn: function () { 450 if ( !that.s.dt._bInitComplete && !bDone ) 451 { 452 bDone = true; 453 var resort = fnInvertKeyValues( aiOrder ); 454 that._fnOrderColumns.call( that, resort ); 455 } 456 }, 457 sName: "ColReorder_Pre" 458 } ); 459 } 460 else 461 { 462 var resort = fnInvertKeyValues( aiOrder ); 463 that._fnOrderColumns.call( that, resort ); 464 } 465 } 466 }, 467 468 469 /** 470 * Set the column order from an array 471 * @method _fnOrderColumns 472 * @param array a An array of integers which dictate the column order that should be applied 473 * @returns void 474 * @private 475 */ 476 _fnOrderColumns: function ( a ) 477 { 478 if ( a.length != this.s.dt.aoColumns.length ) 479 { 480 this.s.dt.oInstance.oApi._fnLog( oDTSettings, 1, "ColReorder - array reorder does not "+ 481 "match known number of columns. Skipping." ); 482 return; 483 } 484 485 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 486 { 487 var currIndex = $.inArray( i, a ); 488 if ( i != currIndex ) 489 { 490 /* Reorder our switching array */ 491 fnArraySwitch( a, currIndex, i ); 492 493 /* Do the column reorder in the table */ 494 this.s.dt.oInstance.fnColReorder( currIndex, i ); 495 } 496 } 497 498 /* When scrolling we need to recalculate the column sizes to allow for the shift */ 499 if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) 500 { 501 this.s.dt.oInstance.fnAdjustColumnSizing(); 502 } 503 504 /* Save the state */ 505 this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); 506 }, 507 508 509 /** 510 * This function effectively replaces the state saving function in DataTables (this is needed 511 * because otherwise DataTables would state save the columns in their reordered state, not the 512 * original which is needed on first draw). This is sensitive to any changes in the DataTables 513 * state saving method! 514 * @method _fnStateSave 515 * @param string sCurrentVal 516 * @returns string JSON encoded cookie string for DataTables 517 * @private 518 */ 519 _fnStateSave: function ( sCurrentVal ) 520 { 521 var i, iLen, sTmp; 522 var sValue = sCurrentVal.split('"aaSorting"')[0]; 523 var a = []; 524 var oSettings = this.s.dt; 525 526 /* Sorting */ 527 sValue += '"aaSorting":[ '; 528 for ( i=0 ; i<oSettings.aaSorting.length ; i++ ) 529 { 530 sValue += '['+oSettings.aoColumns[ oSettings.aaSorting[i][0] ]._ColReorder_iOrigCol+ 531 ',"'+oSettings.aaSorting[i][1]+'"],'; 532 } 533 sValue = sValue.substring(0, sValue.length-1); 534 sValue += "],"; 535 536 /* Column filter */ 537 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 538 { 539 a[ oSettings.aoColumns[i]._ColReorder_iOrigCol ] = { 540 sSearch: encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch), 541 bRegex: !oSettings.aoPreSearchCols[i].bRegex 542 }; 543 } 544 545 sValue += '"aaSearchCols":[ '; 546 for ( i=0 ; i<a.length ; i++ ) 547 { 548 sValue += '["'+a[i].sSearch+'",'+a[i].bRegex+'],'; 549 } 550 sValue = sValue.substring(0, sValue.length-1); 551 sValue += "],"; 552 553 /* Visibility */ 554 a = []; 555 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 556 { 557 a[ oSettings.aoColumns[i]._ColReorder_iOrigCol ] = oSettings.aoColumns[i].bVisible; 558 } 559 560 sValue += '"abVisCols":[ '; 561 for ( i=0 ; i<a.length ; i++ ) 562 { 563 sValue += a[i]+","; 564 } 565 sValue = sValue.substring(0, sValue.length-1); 566 sValue += "],"; 567 568 /* Column reordering */ 569 a = []; 570 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) { 571 a.push( oSettings.aoColumns[i]._ColReorder_iOrigCol ); 572 } 573 sValue += '"ColReorder":['+a.join(',')+']'; 574 575 return sValue; 576 }, 577 578 579 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 580 * Mouse drop and drag 581 */ 582 583 /** 584 * Add a mouse down listener to a particluar TH element 585 * @method _fnMouseListener 586 * @param int i Column index 587 * @param element nTh TH element clicked on 588 * @returns void 589 * @private 590 */ 591 _fnMouseListener: function ( i, nTh ) 592 { 593 var that = this; 594 $(nTh).bind( 'mousedown.ColReorder', function (e) { 595 that._fnMouseDown.call( that, e, nTh ); 596 return false; 597 } ); 598 }, 599 600 601 /** 602 * Mouse down on a TH element in the table header 603 * @method _fnMouseDown 604 * @param event e Mouse event 605 * @param element nTh TH element to be dragged 606 * @returns void 607 * @private 608 */ 609 _fnMouseDown: function ( e, nTh ) 610 { 611 var 612 that = this, 613 aoColumns = this.s.dt.aoColumns; 614 615 /* Store information about the mouse position */ 616 var nThTarget = e.target.nodeName == "TH" ? e.target : $(e.target).parents('TH')[0]; 617 var offset = $(nThTarget).offset(); 618 this.s.mouse.startX = e.pageX; 619 this.s.mouse.startY = e.pageY; 620 this.s.mouse.offsetX = e.pageX - offset.left; 621 this.s.mouse.offsetY = e.pageY - offset.top; 622 this.s.mouse.target = nTh; 623 this.s.mouse.targetIndex = $('th', nTh.parentNode).index( nTh ); 624 this.s.mouse.fromIndex = this.s.dt.oInstance.oApi._fnVisibleToColumnIndex( this.s.dt, 625 this.s.mouse.targetIndex ); 626 627 /* Calculate a cached array with the points of the column inserts, and the 'to' points */ 628 this.s.aoTargets.splice( 0, this.s.aoTargets.length ); 629 630 this.s.aoTargets.push( { 631 x: $(this.s.dt.nTable).offset().left, 632 to: 0 633 } ); 634 635 var iToPoint = 0; 636 for ( var i=0, iLen=aoColumns.length ; i<iLen ; i++ ) 637 { 638 /* For the column / header in question, we want it's position to remain the same if the 639 * position is just to it's immediate left or right, so we only incremement the counter for 640 * other columns 641 */ 642 if ( i != this.s.mouse.fromIndex ) 643 { 644 iToPoint++; 645 } 646 647 if ( aoColumns[i].bVisible ) 648 { 649 this.s.aoTargets.push( { 650 x: $(aoColumns[i].nTh).offset().left + $(aoColumns[i].nTh).outerWidth(), 651 to: iToPoint 652 } ); 653 } 654 } 655 656 /* Disallow columns for being reordered by drag and drop, counting left to right */ 657 if ( this.s.fixed !== 0 ) 658 { 659 this.s.aoTargets.splice( 0, this.s.fixed ); 660 } 661 662 /* Add event handlers to the document */ 663 $(document).bind( 'mousemove.ColReorder', function (e) { 664 that._fnMouseMove.call( that, e ); 665 } ); 666 667 $(document).bind( 'mouseup.ColReorder', function (e) { 668 that._fnMouseUp.call( that, e ); 669 } ); 670 }, 671 672 673 /** 674 * Deal with a mouse move event while dragging a node 675 * @method _fnMouseMove 676 * @param event e Mouse event 677 * @returns void 678 * @private 679 */ 680 _fnMouseMove: function ( e ) 681 { 682 var that = this; 683 684 if ( this.dom.drag === null ) 685 { 686 /* Only create the drag element if the mouse has moved a specific distance from the start 687 * point - this allows the user to make small mouse movements when sorting and not have a 688 * possibly confusing drag element showing up 689 */ 690 if ( Math.pow( 691 Math.pow(e.pageX - this.s.mouse.startX, 2) + 692 Math.pow(e.pageY - this.s.mouse.startY, 2), 0.5 ) < 5 ) 693 { 694 return; 695 } 696 this._fnCreateDragNode(); 697 } 698 699 /* Position the element - we respect where in the element the click occured */ 700 this.dom.drag.style.left = (e.pageX - this.s.mouse.offsetX) + "px"; 701 this.dom.drag.style.top = (e.pageY - this.s.mouse.offsetY) + "px"; 702 703 /* Based on the current mouse position, calculate where the insert should go */ 704 var bSet = false; 705 for ( var i=1, iLen=this.s.aoTargets.length ; i<iLen ; i++ ) 706 { 707 if ( e.pageX < this.s.aoTargets[i-1].x + ((this.s.aoTargets[i].x-this.s.aoTargets[i-1].x)/2) ) 708 { 709 this.dom.pointer.style.left = this.s.aoTargets[i-1].x +"px"; 710 this.s.mouse.toIndex = this.s.aoTargets[i-1].to; 711 bSet = true; 712 break; 713 } 714 } 715 716 /* The insert element wasn't positioned in the array (less than operator), so we put it at 717 * the end 718 */ 719 if ( !bSet ) 720 { 721 this.dom.pointer.style.left = this.s.aoTargets[this.s.aoTargets.length-1].x +"px"; 722 this.s.mouse.toIndex = this.s.aoTargets[this.s.aoTargets.length-1].to; 723 } 724 }, 725 726 727 /** 728 * Finish off the mouse drag and insert the column where needed 729 * @method _fnMouseUp 730 * @param event e Mouse event 731 * @returns void 732 * @private 733 */ 734 _fnMouseUp: function ( e ) 735 { 736 var that = this; 737 738 $(document).unbind( 'mousemove.ColReorder' ); 739 $(document).unbind( 'mouseup.ColReorder' ); 740 741 if ( this.dom.drag !== null ) 742 { 743 /* Remove the guide elements */ 744 document.body.removeChild( this.dom.drag ); 745 document.body.removeChild( this.dom.pointer ); 746 this.dom.drag = null; 747 this.dom.pointer = null; 748 749 /* Actually do the reorder */ 750 this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex ); 751 752 /* When scrolling we need to recalculate the column sizes to allow for the shift */ 753 if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) 754 { 755 this.s.dt.oInstance.fnAdjustColumnSizing(); 756 } 757 758 /* Save the state */ 759 this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); 760 } 761 }, 762 763 764 /** 765 * Copy the TH element that is being drags so the user has the idea that they are actually 766 * moving it around the page. 767 * @method _fnCreateDragNode 768 * @returns void 769 * @private 770 */ 771 _fnCreateDragNode: function () 772 { 773 var that = this; 774 775 this.dom.drag = $(this.s.dt.nTHead.parentNode).clone(true)[0]; 776 this.dom.drag.className += " DTCR_clonedTable"; 777 while ( this.dom.drag.getElementsByTagName('tbody').length > 0 ) 778 { 779 this.dom.drag.removeChild( this.dom.drag.getElementsByTagName('tbody')[0] ); 780 } 781 while ( this.dom.drag.getElementsByTagName('tfoot').length > 0 ) 782 { 783 this.dom.drag.removeChild( this.dom.drag.getElementsByTagName('tfoot')[0] ); 784 } 785 786 $('thead tr:eq(0)', this.dom.drag).each( function () { 787 $('th:not(:eq('+that.s.mouse.targetIndex+'))', this).remove(); 788 } ); 789 $('tr', this.dom.drag).height( $('tr:eq(0)', that.s.dt.nTHead).height() ); 790 791 $('thead tr:gt(0)', this.dom.drag).remove(); 792 793 $('thead th:eq(0)', this.dom.drag).each( function (i) { 794 this.style.width = $('th:eq('+that.s.mouse.targetIndex+')', that.s.dt.nTHead).width()+"px"; 795 } ); 796 797 this.dom.drag.style.position = "absolute"; 798 this.dom.drag.style.top = "0px"; 799 this.dom.drag.style.left = "0px"; 800 this.dom.drag.style.width = $('th:eq('+that.s.mouse.targetIndex+')', that.s.dt.nTHead).outerWidth()+"px"; 801 802 803 this.dom.pointer = document.createElement( 'div' ); 804 this.dom.pointer.className = "DTCR_pointer"; 805 this.dom.pointer.style.position = "absolute"; 806 807 if ( this.s.dt.oScroll.sX === "" && this.s.dt.oScroll.sY === "" ) 808 { 809 this.dom.pointer.style.top = $(this.s.dt.nTable).offset().top+"px"; 810 this.dom.pointer.style.height = $(this.s.dt.nTable).height()+"px"; 811 } 812 else 813 { 814 this.dom.pointer.style.top = $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top+"px"; 815 this.dom.pointer.style.height = $('div.dataTables_scroll', this.s.dt.nTableWrapper).height()+"px"; 816 } 817 818 document.body.appendChild( this.dom.pointer ); 819 document.body.appendChild( this.dom.drag ); 820 } 821 }; 822 823 824 825 826 827 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 828 * Static parameters 829 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 830 831 /** 832 * Array of all ColReorder instances for later reference 833 * @property ColReorder.aoInstances 834 * @type array 835 * @default [] 836 * @static 837 */ 838 ColReorder.aoInstances = []; 839 840 841 842 843 844 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 845 * Static functions 846 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 847 848 /** 849 * Reset the column ordering for a DataTables instance 850 * @method ColReorder.fnReset 851 * @param object oTable DataTables instance to consider 852 * @returns void 853 * @static 854 */ 855 ColReorder.fnReset = function ( oTable ) 856 { 857 for ( var i=0, iLen=ColReorder.aoInstances.length ; i<iLen ; i++ ) 858 { 859 if ( ColReorder.aoInstances[i].s.dt.oInstance == oTable ) 860 { 861 ColReorder.aoInstances[i].fnReset(); 862 } 863 } 864 }; 865 866 867 868 869 870 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 871 * Constants 872 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 873 874 /** 875 * Name of this class 876 * @constant CLASS 877 * @type String 878 * @default ColReorder 879 */ 880 ColReorder.prototype.CLASS = "ColReorder"; 881 882 883 /** 884 * ColReorder version 885 * @constant VERSION 886 * @type String 887 * @default 1.0.1.dev 888 */ 889 ColReorder.VERSION = "1.0.1"; 890 ColReorder.prototype.VERSION = ColReorder.VERSION; 891 892 893 894 895 896 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 897 * Initialisation 898 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 899 900 /* 901 * Register a new feature with DataTables 902 */ 903 if ( typeof $.fn.dataTable == "function" && 904 typeof $.fn.dataTableExt.fnVersionCheck == "function" && 905 $.fn.dataTableExt.fnVersionCheck('1.7.4') ) 906 { 907 $.fn.dataTableExt.aoFeatures.push( { 908 fnInit: function( oDTSettings ) { 909 var oTable = oDTSettings.oInstance; 910 if ( typeof oTable._oPluginColReorder == 'undefined' ) { 911 var opts = typeof oDTSettings.oInit.oColReorder != 'undefined' ? 912 oDTSettings.oInit.oColReorder : {}; 913 oTable._oPluginColReorder = new ColReorder( oDTSettings.oInstance, opts ); 914 } else { 915 oTable.oApi._fnLog( oDTSettings, 1, "ColReorder attempted to initialise twice. Ignoring second" ); 916 } 917 918 return null; /* No node to insert */ 919 }, 920 cFeature: "R", 921 sFeature: "ColReorder" 922 } ); 923 } 924 else 925 { 926 alert( "Warning: ColReorder requires DataTables 1.7.4 or greater - www.datatables.net/download"); 927 } 928 929 })(jQuery, window, document); 930