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