1/** 2 * @license 3 * File: ColReorderWithResize.js 4 * Version: 3.0 5 * CVS: $Id$ 6 * Description: Allow columns to be reordered in a DataTable 7 * Author: Allan Jardine (www.sprymedia.co.uk) 8 * Author: Christophe Battarel (www.altairis.fr) 9 * Created: Wed Sep 15 18:23:29 BST 2010 10 * Modified: July 2011 by Christophe Battarel - [email protected] (columns resizable) 11 * Modified: February 2012 by Martin Marchetta - [email protected] 12 * 1. Made the "hot area" for resizing a little wider (it was a little difficult to hit the exact border of a column for resizing) 13 * 2. Resizing didn't work at all when using scroller (that plugin splits the table into 2 different tables: one for the header and another one for the body, so when you resized the header, the data columns didn't follow) 14 * 3. Fixed collateral effects of sorting feature 15 * 4. If sScrollX is enabled (i.e. horizontal scrolling), when resizing a column the width of the other columns is not changed, but the whole 16 * table is resized to give an Excel-like behavior (good suggestion by Allan) 17 * Modified: February 2012 by Christophe Battarel - [email protected] (ColReorder v1.0.5 adaptation) 18 * Modified: September 16th 2012 by Hassan Kamara - [email protected] 19 * Modified: June 2017 by Jeff Walter - [email protected] 20 * 1. ColReorder v1.3.3 adaptation. 21 * 2. Fixed issues with column width calculations which allowed column headers to become misaligned with table body when using scroller plugin. 22 * Modified: June 2018 by Jeff Walter - [email protected] 23 * 1. Took a second stab at this plugin. Made things work for both scroller and non-scroller tables. 24 * Language: Javascript 25 * License: MIT 26 * Project: DataTables 27 * 28 */ 29 30(function( factory ){ 31 if ( typeof define === 'function' && define.amd ) { 32 // AMD 33 define( ['jquery', 'datatables.net'], function ( $ ) { 34 return factory( $, window, document ); 35 } ); 36 } 37 else if ( typeof exports === 'object' ) { 38 // CommonJS 39 module.exports = function (root, $) { 40 if ( ! root ) { 41 root = window; 42 } 43 44 if ( ! $ || ! $.fn.dataTable ) { 45 $ = require('datatables.net')(root, $).$; 46 } 47 48 return factory( $, root, root.document ); 49 }; 50 } 51 else { 52 // Browser 53 factory( jQuery, window, document ); 54 } 55}(function( $, window, document, undefined ) { 56'use strict'; 57var DataTable = $.fn.dataTable; 58 59 60 61/** 62 * Switch the key value pairing of an index array to be value key (i.e. the old value is now the 63 * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. 64 * @method fnInvertKeyValues 65 * @param array aIn Array to switch around 66 * @returns array 67 */ 68function fnInvertKeyValues( aIn ) 69{ 70 var aRet=[]; 71 for ( var i=0, iLen=aIn.length ; i<iLen ; i++ ) 72 { 73 aRet[ aIn[i] ] = i; 74 } 75 return aRet; 76} 77 78/** 79 * Modify an array by switching the position of two elements 80 * @method fnArraySwitch 81 * @param array aArray Array to consider, will be modified by reference (i.e. no return) 82 * @param int iFrom From point 83 * @param int iTo Insert point 84 * @returns void 85 */ 86function fnArraySwitch( aArray, iFrom, iTo ) 87{ 88 var mStore = aArray.splice( iFrom, 1 )[0]; 89 aArray.splice( iTo, 0, mStore ); 90} 91 92/** 93 * Switch the positions of nodes in a parent node (note this is specifically designed for 94 * table rows). Note this function considers all element nodes under the parent! 95 * @method fnDomSwitch 96 * @param string sTag Tag to consider 97 * @param int iFrom Element to move 98 * @param int Point to element the element to (before this point), can be null for append 99 * @returns void 100 */ 101function fnDomSwitch( nParent, iFrom, iTo ) 102{ 103 var anTags = []; 104 for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ ) 105 { 106 if ( nParent.childNodes[i].nodeType == 1 ) 107 { 108 anTags.push( nParent.childNodes[i] ); 109 } 110 } 111 var nStore = anTags[ iFrom ]; 112 113 if ( iTo !== null ) 114 { 115 nParent.insertBefore( nStore, anTags[iTo] ); 116 } 117 else 118 { 119 nParent.appendChild( nStore ); 120 } 121} 122 123/**
124 * Plug-in for DataTables which will reorder the internal column structure by taking the column 125 * from one position (iFrom) and insert it into a given point (iTo). 126 * @method $.fn.dataTableExt.oApi.fnColReorder 127 * @param object oSettings DataTables settings object - automatically added by DataTables! 128 * @param int iFrom Take the column to be repositioned from this point 129 * @param int iTo and insert it into this point 130 * @param bool drop Indicate if the reorder is the final one (i.e. a drop) 131 * not a live reorder 132 * @param bool invalidateRows speeds up processing if false passed 133 * @returns void 134 */ 135$.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows ) 136{ 137 var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol; 138 var attrMap = function ( obj, prop, mapping ) { 139 if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) { 140 return; 141 } 142 143 var a = obj[ prop ].split('.'); 144 var num = a.shift(); 145 146 if ( isNaN( num*1 ) ) { 147 return; 148 } 149 150 obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.'); 151 }; 152 153 /* Sanity check in the input */ 154 if ( iFrom == iTo ) 155 { 156 /* Pointless reorder */ 157 return; 158 } 159 160 if ( iFrom < 0 || iFrom >= iCols ) 161 { 162 this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom ); 163 return; 164 } 165 166 if ( iTo < 0 || iTo >= iCols ) 167 { 168 this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo ); 169 return; 170 } 171 172 /* 173 * Calculate the new column array index, so we have a mapping between the old and new 174 */ 175 var aiMapping = []; 176 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 177 { 178 aiMapping[i] = i; 179 } 180 fnArraySwitch( aiMapping, iFrom, iTo ); 181 var aiInvertMapping = fnInvertKeyValues( aiMapping ); 182 183 184 /* 185 * Convert all internal indexing to the new column order indexes 186 */ 187 /* Sorting */ 188 for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ ) 189 { 190 oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ]; 191 } 192 193 /* Fixed sorting */ 194 if ( oSettings.aaSortingFixed !== null ) 195 { 196 for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ ) 197 { 198 oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ]; 199 } 200 } 201 202 /* Data column sorting (the column which the sort for a given column should take place on) */ 203 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 204 { 205 oCol = oSettings.aoColumns[i]; 206 for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ ) 207 { 208 oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ]; 209 } 210 211 // Update the column indexes 212 oCol.idx = aiInvertMapping[ oCol.idx ]; 213 } 214 215 // Update 1.10 optimised sort class removal variable 216 $.each( oSettings.aLastSort, function (i, val) { 217 oSettings.aLastSort[i].src = aiInvertMapping[ val.src ]; 218 } ); 219 220 /* Update the Get and Set functions for each column */ 221 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 222 { 223 oCol = oSettings.aoColumns[i]; 224 225 if ( typeof oCol.mData == 'number' ) { 226 oCol.mData = aiInvertMapping[ oCol.mData ]; 227 } 228 else if ( $.isPlainObject( oCol.mData ) ) { 229 // HTML5 data sourced 230 attrMap( oCol.mData, '_', aiInvertMapping ); 231 attrMap( oCol.mData, 'filter', aiInvertMapping ); 232 attrMap( oCol.mData, 'sort', aiInvertMapping ); 233 attrMap( oCol.mData, 'type', aiInvertMapping ); 234 } 235 } 236 237 /* 238 * Move the DOM elements 239 */ 240 if ( oSettings.aoColumns[iFrom].bVisible ) 241 { 242 /* Calculate the current visible index and the point to insert the node before. The insert 243 * before needs to take into account that there might not be an element to insert before, 244 * in which case it will be null, and an appendChild should be used 245 */ 246 var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom ); 247 var iInsertBeforeIndex = null; 248 249 i = iTo < iFrom ? iTo : iTo + 1; 250 while ( iInsertBeforeIndex === null && i < iCols ) 251 { 252 iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i ); 253 i++; 254 } 255 256 /* Header */ 257 nTrs = oSettings.nTHead.getElementsByTagName('tr'); 258 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 259 {
260 fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex ); 261 } 262 263 /* Footer */ 264 if ( oSettings.nTFoot !== null ) 265 { 266 nTrs = oSettings.nTFoot.getElementsByTagName('tr'); 267 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 268 { 269 fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex ); 270 } 271 } 272 273 /* Body */ 274 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 275 { 276 if ( oSettings.aoData[i].nTr !== null ) 277 { 278 fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex ); 279 } 280 } 281 } 282 283 /* 284 * Move the internal array elements 285 */ 286 /* Columns */ 287 fnArraySwitch( oSettings.aoColumns, iFrom, iTo ); 288 289 // regenerate the get / set functions 290 for ( i=0, iLen=iCols ; i<iLen ; i++ ) { 291 oSettings.oApi._fnColumnOptions( oSettings, i, {} ); 292 } 293 294 /* Search columns */ 295 fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo ); 296 297 /* Array array - internal data anodes cache */ 298 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 299 { 300 var data = oSettings.aoData[i]; 301 var cells = data.anCells; 302 303 if ( cells ) { 304 fnArraySwitch( cells, iFrom, iTo ); 305 306 // Longer term, should this be moved into the DataTables' invalidate 307 // methods? 308 for ( j=0, jen=cells.length ; j<jen ; j++ ) { 309 if ( cells[j] && cells[j]._DT_CellIndex ) { 310 cells[j]._DT_CellIndex.column = j; 311 } 312 } 313 } 314 315 // For DOM sourced data, the invalidate will reread the cell into 316 // the data array, but for data sources as an array, they need to 317 // be flipped 318 if ( data.src !== 'dom' && $.isArray( data._aData ) ) { 319 fnArraySwitch( data._aData, iFrom, iTo ); 320 } 321 } 322 323 /* Reposition the header elements in the header layout array */ 324 for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ ) 325 { 326 fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo ); 327 } 328 329 if ( oSettings.aoFooter !== null ) 330 { 331 for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ ) 332 { 333 fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo ); 334 } 335 } 336 337 if ( invalidateRows || invalidateRows === undefined ) 338 { 339 $.fn.dataTable.Api( oSettings ).rows().invalidate(); 340 } 341 342 /* 343 * Update DataTables' event handlers 344 */ 345 346 /* Sort listener */ 347 for ( i=0, iLen=iCols ; i<iLen ; i++ ) 348 { 349 $(oSettings.aoColumns[i].nTh).off('click.DT'); 350 this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i ); 351 } 352 353 /* Fire an event so other plug-ins can update */ 354 $(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, { 355 from: iFrom, 356 to: iTo, 357 mapping: aiInvertMapping, 358 drop: drop, 359 360 // Old style parameters for compatibility 361 iFrom: iFrom, 362 iTo: iTo, 363 aiInvertMapping: aiInvertMapping 364 } ] ); 365}; 366 367/** 368 * ColReorder provides column visibility control for DataTables 369 * @class ColReorder 370 * @constructor 371 * @param {object} dt DataTables settings object 372 * @param {object} opts ColReorder options 373 */ 374var ColReorder = function( dt, opts ) 375{ 376 var settings = new $.fn.dataTable.Api( dt ).settings()[0]; 377 378 // Ensure that we can't initialise on the same table twice 379 if ( settings._colReorder ) { 380 return settings._colReorder; 381 } 382 383 // Allow the options to be a boolean for defaults 384 if ( opts === true ) { 385 opts = {}; 386 } 387 388 // Convert from camelCase to Hungarian, just as DataTables does 389 var camelToHungarian = $.fn.dataTable.camelToHungarian; 390 if ( camelToHungarian ) { 391 camelToHungarian( ColReorder.defaults, ColReorder.defaults, true ); 392 camelToHungarian( ColReorder.defaults, opts || {} ); 393 } 394 395 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 396 * Public class variables 397 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 398 399 /** 400 * @namespace Settings object which contains customisable information for ColReorder instance 401 */ 402 this.s = { 403 /** 404 * DataTables settings object 405 * @property dt 406 * @type Object 407 * @default null 408 */ 409 "dt": null, 410 411 /** 412 * Initialisation object used for this instance 413 * @property init 414 * @type object 415 * @default {} 416 */ 417 "init": $.extend( true, {}, ColReorder.defaults, opts ), 418 419 /** 420 * Allow Reorder functionnality 421 * @property allowReorder 422 * @type boolean 423 * @default true 424 */ 425 "allowReorder": true, 426 427 /** 428 * Allow Resize functionnality 429 * @property allowResize 430 * @type boolean 431 * @default true 432 */ 433 "allowResize": true, 434 435 /** 436 * Number of columns to fix (not allow to be reordered) 437 * @property fixed 438 * @type int 439 * @default 0 440 */ 441 "fixed": 0, 442 443 /** 444 * Number of columns to fix counting from right (not allow to be reordered) 445 * @property fixedRight 446 * @type int 447 * @default 0 448 */ 449 "fixedRight": 0, 450 451 /** 452 * Callback function for once the reorder has been done 453 * @property reorderCallback 454 * @type function 455 * @default null 456 */ 457 "reorderCallback": null, 458 459 /** 460 * Callback function for once the resize has been done 461 * @property resizeCallback 462 * @type function 463 * @default null 464 */ 465 "resizeCallback": null, 466 467 /** 468 * @namespace Information used for the mouse drag 469 */ 470 "mouse": { 471 "startX": -1, 472 "startY": -1, 473 "offsetX": -1, 474 "offsetY": -1, 475 "target": -1, 476 "targetIndex": -1, 477 "fromIndex": -1 478 }, 479 480 /** 481 * Information which is used for positioning the insert cusor and knowing where to do the 482 * insert. Array of objects with the properties: 483 * x: x-axis position 484 * to: insert point 485 * @property aoTargets 486 * @type array 487 * @default [] 488 */ 489 "aoTargets": [] 490 }; 491 492 493 /** 494 * @namespace Common and useful DOM elements for the class instance 495 */ 496 this.dom = { 497 /** 498 * Dragging element (the one the mouse is moving) 499 * @property drag 500 * @type element 501 * @default null 502 */ 503 "drag": null, 504 505 /** 506 * Resizing a column 507 * @property drag 508 * @type element 509 * @default null 510 */ 511 "resize": null, 512 513 /** 514 * The insert cursor 515 * @property pointer 516 * @type element 517 * @default null 518 */ 519 "pointer": null 520 }; 521 522 /* Constructor logic */ 523 this.s.dt = settings; 524 525 // Keep the current table's size in order to resize it if columns are resized and scrollX is enabled. 526 if(this.s.dt.oInit.sScrollX === undefined) { 527 this.table_size = -1; 528 } 529 530 this.s.dt._colReorder = this; 531 this._fnConstruct(); 532 533 return this; 534}; 535 536 537 538$.extend( ColReorder.prototype, { 539 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 540 * Public methods 541 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 542 543 /** 544 * Reset the column ordering to the original ordering that was detected on 545 * start up. 546 * @return {this} Returns `this` for chaining. 547 * 548 * @example 549 * // DataTables initialisation with ColReorder 550 * var table = $('#example').dataTable( { 551 * "sDom": 'Rlfrtip' 552 * } ); 553 * 554 * // Add click event to a button to reset the ordering 555 * $('#resetOrdering').click( function (e) { 556 * e.preventDefault(); 557 * $.fn.dataTable.ColReorder( table ).fnReset(); 558 * } ); 559 */ 560 "fnReset": function () 561 { 562 this._fnOrderColumns( this.fnOrder() ); 563 564 return this; 565 }, 566 567 /** 568 * `Deprecated` - Get the current order of the columns, as an array.
569 * @return {array} Array of column identifiers 570 * @deprecated `fnOrder` should be used in preference to this method. 571 * `fnOrder` acts as a getter/setter. 572 */ 573 "fnGetCurrentOrder": function () 574 { 575 return this.fnOrder(); 576 }, 577 578 /** 579 * Get the current order of the columns, as an array. Note that the values 580 * given in the array are unique identifiers for each column. Currently 581 * these are the original ordering of the columns that was detected on 582 * start up, but this could potentially change in future. 583 * @return {array} Array of column identifiers 584 * 585 * @example 586 * // Get column ordering for the table 587 * var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder(); 588 *//** 589 * Set the order of the columns, from the positions identified in the 590 * ordering array given. Note that ColReorder takes a brute force approach 591 * to reordering, so it is possible multiple reordering events will occur 592 * before the final order is settled upon. 593 * @param {array} [set] Array of column identifiers in the new order. Note 594 * that every column must be included, uniquely, in this array. 595 * @return {this} Returns `this` for chaining. 596 * 597 * @example 598 * // Swap the first and second columns 599 * $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] ); 600 * 601 * @example 602 * // Move the first column to the end for the table `#example` 603 * var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder(); 604 * var first = curr.shift(); 605 * curr.push( first ); 606 * $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr ); 607 * 608 * @example 609 * // Reverse the table's order 610 * $.fn.dataTable.ColReorder( '#example' ).fnOrder( 611 * $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse() 612 * ); 613 */ 614 "fnOrder": function ( set, original ) 615 { 616 var a = [], i, ien, j, jen; 617 var columns = this.s.dt.aoColumns; 618 619 if ( set === undefined ){ 620 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 621 a.push( columns[i]._ColReorder_iOrigCol ); 622 } 623 624 return a; 625 } 626 627 // The order given is based on the original indexes, rather than the 628 // existing ones, so we need to translate from the original to current 629 // before then doing the order 630 if ( original ) { 631 var order = this.fnOrder(); 632 633 for ( i=0, ien=set.length ; i<ien ; i++ ) { 634 a.push( $.inArray( set[i], order ) ); 635 } 636 637 set = a; 638 } 639 640 this._fnOrderColumns( fnInvertKeyValues( set ) ); 641 642 return this; 643 }, 644 645 /** 646 * Convert from the original column index, to the original 647 * 648 * @param {int|array} idx Index(es) to convert 649 * @param {string} dir Transpose direction - `fromOriginal` / `toCurrent` 650 * or `'toOriginal` / `fromCurrent` 651 * @return {int|array} Converted values 652 */ 653 fnTranspose: function ( idx, dir ) 654 { 655 if ( ! dir ) { 656 dir = 'toCurrent'; 657 } 658 659 var order = this.fnOrder(); 660 var columns = this.s.dt.aoColumns; 661 662 if ( dir === 'toCurrent' ) { 663 // Given an original index, want the current 664 return ! $.isArray( idx ) ? 665 $.inArray( idx, order ) : 666 $.map( idx, function ( index ) { 667 return $.inArray( index, order ); 668 } ); 669 } 670 else { 671 // Given a current index, want the original 672 return ! $.isArray( idx ) ? 673 columns[idx]._ColReorder_iOrigCol : 674 $.map( idx, function ( index ) { 675 return columns[index]._ColReorder_iOrigCol; 676 } ); 677 } 678 }, 679 680 681 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 682 * Private methods (they are of course public in JS, but recommended as private) 683 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 684 685 /** 686 * Constructor logic 687 * @method _fnConstruct 688 * @returns void 689 * @private 690 */ 691 "_fnConstruct": function () 692 { 693 var that = this; 694 var iLen = this.s.dt.aoColumns.length; 695 var table = this.s.dt.nTable; 696 var i; 697 698 /* allow reorder */ 699 if ( this.s.init.allowReorder != undefined) 700 { 701 this.s.allowReorder = this.s.init.allowReorder; 702 } 703 704 /* allow resize */ 705 if ( this.s.init.allowResize != undefined ) 706 { 707 this.s.allowResize = this.s.init.allowResize; 708 } 709 710 /* Columns discounted from reordering - counting left to right */ 711 if ( this.s.init.iFixedColumns ) 712 { 713 this.s.fixed = this.s.init.iFixedColumns; 714 } 715 716 if ( this.s.init.iFixedColumnsLeft ) 717 { 718 this.s.fixed = this.s.init.iFixedColumnsLeft; 719 } 720 721 /* Classnames added to elements */ 722 if ( this.s.init.classNameClonedTable ) 723 { 724 this.s.classNameClonedTable = this.s.init.classNameClonedTable; 725 } 726 727 if ( this.s.init.classNamePointer ) 728 { 729 this.s.classNamePointer = this.s.init.classNamePointer; 730 } 731 732 if ( this.s.init.classNameTableHeader ) 733 { 734 this.s.classNameTableHeader = this.s.init.classNameTableHeader; 735 } 736 737 if ( this.s.init.classNameTableHeaderHover ) 738 { 739 this.s.classNameTableHeaderHover = this.s.init.classNameTableHeaderHover; 740 } 741 742 /* Columns discounted from reordering - counting right to left */ 743 this.s.fixedRight = this.s.init.iFixedColumnsRight ? 744 this.s.init.iFixedColumnsRight : 745 0; 746 747 /* Drop callback initialisation option */ 748 if ( this.s.init.fnReorderCallback ) 749 { 750 this.s.reorderCallback = this.s.init.fnReorderCallback; 751 } 752 753 /* Reorder callback initialisation option */ 754 if ( this.s.init.fnResizeCallback ) 755 { 756 this.s.resizeCallback = this.s.init.fnResizeCallback; 757 } 758 759 /* Add event handlers for the drag and drop, and also mark the original column order */ 760 for ( i = 0; i < iLen; i++ ) 761 { 762 if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight ) 763 { 764 this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh ); 765 } 766 767 /* Mark the original column order for later reference */ 768 this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i; 769 } 770 771 /* State saving */ 772 this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) { 773 that._fnStateSave.call( that, oData ); 774 }, "ColReorder_State" ); 775 776 /* An initial column order has been specified */ 777 var aiOrder = null; 778 if ( this.s.init.aiOrder ) 779 { 780 aiOrder = this.s.init.aiOrder.slice(); 781 } 782 783 /* State loading, overrides the column order given */ 784 if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' && 785 this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length ) 786 { 787 aiOrder = this.s.dt.oLoadedState.ColReorder; 788 } 789 790 /* If we have an order to apply - do so */ 791 if ( aiOrder ) 792 { 793 /* We might be called during or after the DataTables initialisation. If before, then we need 794 * to wait until the draw is done, if after, then do what we need to do right away 795 */ 796 if ( !that.s.dt._bInitComplete ) 797 { 798 var bDone = false;
799 $(table).on( 'draw.dt.colReorder', function () { 800 if ( !that.s.dt._bInitComplete && !bDone ) 801 { 802 bDone = true; 803 var resort = fnInvertKeyValues( aiOrder ); 804 that._fnOrderColumns.call( that, resort ); 805 } 806 } ); 807 } 808 else 809 { 810 var resort = fnInvertKeyValues( aiOrder ); 811 that._fnOrderColumns.call( that, resort ); 812 } 813 } 814 else { 815 this._fnSetColumnIndexes(); 816 } 817 818 // Destroy clean up 819 $(table).on( 'destroy.dt.colReorder', function () { 820 $(table).off( 'destroy.dt.colReorder draw.dt.colReorder' ); 821 $(that.s.dt.nTHead).find( '*' ).off( '.ColReorder' ); 822 823 $.each( that.s.dt.aoColumns, function (i, column) { 824 $(column.nTh).removeAttr('data-column-index'); 825 } ); 826 827 that.s.dt._colReorder = null; 828 that.s = null; 829 } ); 830 }, 831 832 833 /** 834 * Set the column order from an array 835 * @method _fnOrderColumns 836 * @param array a An array of integers which dictate the column order that should be applied 837 * @returns void 838 * @private 839 */ 840 "_fnOrderColumns": function ( a ) 841 { 842 var changed = false; 843 844 if ( a.length != this.s.dt.aoColumns.length ) 845 { 846 this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+ 847 "match known number of columns. Skipping." ); 848 return; 849 } 850 851 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 852 { 853 var currIndex = $.inArray( i, a ); 854 if ( i != currIndex ) 855 { 856 /* Reorder our switching array */ 857 fnArraySwitch( a, currIndex, i ); 858 859 /* Do the column reorder in the table */ 860 this.s.dt.oInstance.fnColReorder( currIndex, i, true, false ); 861 862 changed = true; 863 } 864 } 865 866 $.fn.dataTable.Api( this.s.dt ).rows().invalidate(); 867 868 this._fnSetColumnIndexes(); 869 870 // Has anything actually changed? If not, then nothing else to do 871 if ( ! changed ) { 872 return; 873 } 874 875 /* When scrolling we need to recalculate the column sizes to allow for the shift */ 876 if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) 877 { 878 this.s.dt.oInstance.fnAdjustColumnSizing( false ); 879 } 880 881 /* Save the state */ 882 this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); 883 884 if ( this.s.reorderCallback !== null ) 885 { 886 this.s.reorderCallback.call( this ); 887 } 888 }, 889 890 891 /** 892 * Because we change the indexes of columns in the table, relative to their starting point 893 * we need to reorder the state columns to what they are at the starting point so we can 894 * then rearrange them again on state load! 895 * @method _fnStateSave 896 * @param object oState DataTables state 897 * @returns string JSON encoded cookie string for DataTables 898 * @private 899 */ 900 "_fnStateSave": function ( oState ) 901 { 902 var i, iLen, aCopy, iOrigColumn; 903 var oSettings = this.s.dt; 904 var columns = oSettings.aoColumns; 905 906 oState.ColReorder = []; 907 908 /* Sorting */ 909 if ( oState.aaSorting ) { 910 // 1.10.0- 911 for ( i=0 ; i<oState.aaSorting.length ; i++ ) { 912 oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol; 913 } 914 915 var aSearchCopy = $.extend( true, [], oState.aoSearchCols ); 916 917 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) 918 { 919 iOrigColumn = columns[i]._ColReorder_iOrigCol; 920 921 /* Column filter */ 922 oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i]; 923 924 /* Visibility */ 925 oState.abVisCols[ iOrigColumn ] = columns[i].bVisible; 926 927 /* Column reordering */ 928 oState.ColReorder.push( iOrigColumn ); 929 } 930 } 931 else if ( oState.order ) { 932 // 1.10.1+ 933 for ( i=0 ; i<oState.order.length ; i++ ) { 934 oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol; 935 } 936 937 var stateColumnsCopy = $.extend( true, [], oState.columns ); 938 939 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) 940 { 941 iOrigColumn = columns[i]._ColReorder_iOrigCol; 942 943 /* Columns */ 944 oState.columns[ iOrigColumn ] = stateColumnsCopy[i]; 945 946 /* Column reordering */ 947 oState.ColReorder.push( iOrigColumn ); 948 } 949 } 950 }, 951 952 953 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 954 * Mouse drop and drag 955 */ 956 957 /** 958 * Add a mouse down listener to a particluar TH element 959 * @method _fnMouseListener 960 * @param int i Column index 961 * @param element nTh TH element clicked on 962 * @returns void 963 * @private 964 */
965 "_fnMouseListener": function ( i, nTh ) 966 { 967 var that = this; 968 var aoColumns = this.s.dt.aoColumns; 969 var bSort = that.s.dt.oFeatures.bSort 970 971 var tableHeaderHoverClassname = this.s.classNameTableHeaderHover; 972 var tableHeaderClassname = this.s.classNameTableHeader; 973 974 // Rebind events since after column re-order they use wrong column indices. 975 $(nTh).off('.ColReorder'); 976 977 // listen to mousemove event for resize 978 if (this.s.allowResize) { 979 $(nTh).on( 'mousemove.ColReorder', function (e) { 980 if (that.dom.drag === null && that.dom.resize === null) 981 { 982 /* Store information about the mouse position */ 983 var nThTarget = e.target.nodeName == "TH" ? e.target : $(e.target).parents('TH')[0]; 984 var offset = $(nThTarget).offset(); 985 var nLength = $(nThTarget).innerWidth(); 986 987 /* are we on the col border (if so, resize col) */ 988 if (Math.abs(e.pageX - Math.round(offset.left + nLength)) <= 5) 989 { 990 $(nThTarget).css({'cursor': 'col-resize'}); 991 $(nThTarget).removeClass( tableHeaderClassname ); 992 $(nThTarget).addClass( tableHeaderHoverClassname ); 993 } 994 else { 995 $(nThTarget).css({'cursor': 'pointer'}); 996 $(nThTarget).removeClass( tableHeaderHoverClassname ); 997 $(nThTarget).addClass( tableHeaderClassname ); 998 } 999 } 1000 } ); 1001 } 1002 1003 $(nTh) 1004 .on( 'mousedown.ColReorder', function (e) { 1005 e.preventDefault(); 1006 if (e.which == 1) { 1007 that._fnMouseDown.call( that, e, nTh, i ); 1008 } 1009 } ) 1010 .on( 'touchstart.ColReorder', function (e) { 1011 that._fnMouseDown.call( that, e, nTh, i ); 1012 } ); 1013 }, 1014 1015 1016 /** 1017 * Mouse down on a TH element in the table header 1018 * @method _fnMouseDown 1019 * @param event e Mouse event 1020 * @param element nTh TH element to be dragged 1021 * @param i The column that's resized/dragged 1022 * @returns void 1023 * @private 1024 */ 1025 "_fnMouseDown": function ( e, nTh, i ) 1026 { 1027 var 1028 that = this, 1029 aoColumns = this.s.dt.aoColumns; 1030 1031 1032 function addEventsHandler() { 1033 /* Add event handlers to the document */ 1034 $(document) 1035 .on( 'mousemove.ColReorder touchmove.ColReorder', function (e) { 1036 // Added index of the call being dragged or resized. 1037 that._fnMouseMove.call( that, e, i); 1038 } ) 1039 .on( 'mouseup.ColReorder touchend.ColReorder', function (e) { 1040 // Added this small delay in order to prevent collision with column sort feature (there must be a better 1041 // way of doing this, but I don't have more time to digg into it). 1042 setTimeout(function() { that._fnMouseUp.call( that, e, i ); }, 10); 1043 } ); 1044 } 1045 1046 /* are we resizing a column ? */ 1047 if ($(nTh).css('cursor') == 'col-resize') { 1048 this.s.mouse.startX = e.pageX; 1049 this.s.mouse.startWidth = $(nTh).width(); 1050 this.s.mouse.resizeElem = $(nTh); 1051 1052 var nThNext = $(nTh).next(); 1053 this.s.mouse.nextStartWidth = $(nThNext).width(); 1054 that.dom.resize = true; 1055 // Disable column sorting in order to avoid issues when finishing column resizing. 1056 aoColumns[i].CRbSortableCache = aoColumns[i].bSortable; 1057 aoColumns[i].bSortable = false; 1058 // Disable Autowidth feature (now the user is in charge of setting column width so keeping this enabled looses changes after operations). 1059 this.s.dt.oFeatures.bAutoWidth = false; 1060 addEventsHandler(); 1061 } 1062 else if (this.s.allowReorder) { 1063 if (aoColumns[i].bReorderable === false) { 1064 return false;
1065 } 1066 1067 that.dom.resize = null; 1068 /* Store information about the mouse position */ 1069 var target = $(e.target).closest('th, td'); 1070 var offset = target.offset(); 1071 var idx = parseInt( $(nTh).attr('data-column-index'), 10 ); 1072 1073 if ( idx === undefined ) { 1074 return; 1075 } 1076 1077 this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' ); 1078 this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' ); 1079 this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left; 1080 this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top; 1081 this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh; 1082 this.s.mouse.targetIndex = idx; 1083 this.s.mouse.fromIndex = idx; 1084 1085 this._fnRegions(); 1086 addEventsHandler(); 1087 } 1088 1089 }, 1090 1091 1092 /** 1093 * Deal with a mouse move event while dragging a node 1094 * @method _fnMouseMove 1095 * @param event e Mouse event 1096 * @param colResized Index of the column that's being dragged or resized (index within the internal model, not the visible order) 1097 * @returns void 1098 * @private 1099 */ 1100 "_fnMouseMove": function ( e, colResized ) 1101 { 1102 var that = this; 1103 1104 // Handle column resizing. 1105 if(this.dom.resize) { 1106 // Since some columns might have been hidden, find the correct one to resize in the table's body 1107 var currentColumnIndex; 1108 var visibleColumnIndex = -1; 1109 for(currentColumnIndex=-1; currentColumnIndex < this.s.dt.aoColumns.length-1 && currentColumnIndex != colResized; currentColumnIndex++) { 1110 if(this.s.dt.aoColumns[currentColumnIndex+1].bVisible) { 1111 visibleColumnIndex++; 1112 } 1113 } 1114 visibleColumnIndex++; 1115 1116 var $nTh = $(this.s.mouse.resizeElem); 1117 var $nThNext = $($nTh.next('th')); 1118 var $body = $(this.s.dt.nTBody); 1119 var $head = $(this.s.dt.nTHead); 1120 1121 var nThInnerWidth = $nTh.first().innerWidth(); 1122 var nThNextInnerWidth = $nThNext.first().innerWidth(); 1123 1124 var moveLength = e.pageX-this.s.mouse.startX; 1125 var nThWidth = parseInt(this.s.mouse.startWidth + moveLength); 1126 var nThNextWidth = parseInt(this.s.mouse.nextStartWidth - moveLength); 1127 1128 var scrollXEnabled = this.s.dt.oInit.sScrollX !== undefined; 1129 var scrollYEnabled = this.s.dt.oInit.sScrollY !== undefined; 1130 1131 // Smart scrolling is not enabled. 1132 if(!scrollXEnabled && !scrollYEnabled) { 1133 if(moveLength < 0) { 1134 // Hack to calculate the minimum width of the column based on contents. 1135 $nTh.width('1%'); 1136 var minNThWidth = $nTh.width(); 1137 $nTh.width(this.s.mouse.startWidth); 1138 1139 if(nThWidth >= minNThWidth) { 1140 $nTh.width(nThWidth); 1141 } 1142 else { 1143 $nTh.width(minNThWidth); 1144 } 1145 } 1146 else { 1147 // Hack to calculate the minimum width of the column based on contents. 1148 $nThNext.width('1%'); 1149 var minNThNextWidth = $nThNext.width(); 1150 $nThNext.width(this.s.mouse.nextStartWidth); 1151 1152 if(nThNextInnerWidth <= minNThNextWidth) { return; } 1153 1154 $nTh.width(nThWidth); 1155 } 1156 } 1157 // Smart scrolling is enabled. 1158 else { 1159 // Keep the current table's width (used in case sScrollX is enabled to resize the whole table, giving an Excel-like behavior) 1160 var $scrollHead = $(this.s.dt.nScrollHead); 1161 var $scrollBody = $(this.s.dt.nScrollBody); 1162 1163 // Keep the current table's width (used in case sScrollX is enabled to resize the whole table, giving an Excel-like behavior) 1164 if(scrollXEnabled) { 1165 var $scrollHeadInner = $scrollHead.find('div.dataTables_scrollHeadInner'); 1166 var $scrollHeadTableWrapper = $(this.s.dt.nTableWrapper); 1167 if((this.table_size === undefined || this.table_size < 0) && $scrollHeadTableWrapper.length > 0) { 1168 this.table_size = $($scrollHead[0].childNodes[0].childNodes[0]).width(); 1169 } 1170 1171 if(this.table_size + moveLength > $scrollHeadTableWrapper.width()) { 1172 // Resize the header too (if sScrollX is enabled). 1173 if($scrollHeadTableWrapper.length) { 1174 $($scrollHeadTableWrapper[0].childNodes[0].childNodes[0]).width(this.table_size + moveLength); 1175 } 1176 $($scrollHeadInner).width(this.table_size + moveLength); 1177 1178 // Resize the table too (if sScrollX is enabled). 1179 var new_table_size = this.table_size; 1180 if(this.table_size + moveLength > $scrollHeadTableWrapper.width()) { 1181 new_table_size += moveLength; 1182 } 1183 $scrollBody.closest('.dataTables_scroll').find('.dataTables_scrollHead table').first().width(new_table_size); 1184 $scrollBody.closest('.dataTables_scroll').find('.dataTables_scrollBody table').first().width(new_table_size); 1185 } 1186 } 1187 1188 // When resizing the header, also resize the table's body (when enabling the Scroller, the table's header and 1189 // body are split into different tables, so the column resizing doesn't work anymore). 1190 // Since some columns might have been hidden, find the correct one to resize in the table's body 1191 1192 // Get the first row in the scrollBody thead. 1193 var $scrollBodyTheadTr = $scrollBody.find('thead').first('tr'); 1194 var $scrollBodyNTh = $scrollBodyTheadTr.find('th:nth-child('+visibleColumnIndex+')'); 1195 var $scrollBodyNThNext = $scrollBodyTheadTr.find('th:nth-child('+(visibleColumnIndex+1)+')'); 1196 var $scrollBodyThSizing = $scrollBodyNTh.find('.dataTables_sizing'); 1197 var $scrollBodyThNextSizing = $scrollBodyNThNext.find('.dataTables_sizing'); 1198 1199 // Get the first row in the scrollBody tbody. 1200 var $scrollBodyTbodyTr = $scrollBody.find('tbody').first('tr'); 1201 var $scrollBodyNTd = $scrollBodyTbodyTr.find('td:nth-child('+visibleColumnIndex+')'); 1202 var $scrollBodyNTdNext = $scrollBodyTbodyTr.find('td:nth-child('+(visibleColumnIndex+1)+')'); 1203 1204 if(moveLength < 0) { 1205 // Hack to calculate the minimum width of the column based on contents. 1206 $scrollBodyNTh.width('1%'); 1207 var minNThWidth = $scrollBodyNTh.width(); 1208 $scrollBodyNTh.width(this.s.mouse.startWidth); 1209 1210 if(nThWidth >= minNThWidth) { 1211 $nTh.width(nThWidth); 1212 $scrollBodyNTh.width(nThWidth); 1213 } 1214 else { 1215 $nTh.width(minNThWidth); 1216 $scrollBodyNTh.width(minNThWidth); 1217 } 1218 } 1219 else { 1220 // Hack to calculate the minimum width of the column based on contents. 1221 $scrollBodyNThNext.width('1%'); 1222 var minNThNextWidth = $scrollBodyNThNext.width(); 1223 $scrollBodyNThNext.width(this.s.mouse.nextStartWidth); 1224 1225 if($scrollBodyNThNext.width() <= minNThNextWidth) { return; } 1226 1227 $nTh.width(nThWidth); 1228 $scrollBodyNTh.width(nThWidth); 1229 } 1230 } 1231 } 1232 else if(this.s.allowReorder) { 1233 if( this.dom.drag === null ) { 1234 /* Only create the drag element if the mouse has moved a specific distance from the start 1235 * point - this allows the user to make small mouse movements when sorting and not have a 1236 * possibly confusing drag element showing up 1237 */ 1238 if ( Math.pow( 1239 Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) + 1240 Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 ) 1241 { 1242 return; 1243 } 1244 this._fnCreateDragNode(); 1245 } 1246 1247 /* Position the element - we respect where in the element the click occured */ 1248 this.dom.drag.css( { 1249 left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX, 1250 top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY 1251 } ); 1252 1253 /* Based on the current mouse position, calculate where the insert should go */ 1254 var bSet = false;
1255 var lastToIndex = this.s.mouse.toIndex; 1256 1257 for ( var i=1, iLen=this.s.aoTargets.length ; i<iLen ; i++ ) 1258 { 1259 if ( this._fnCursorPosition(e, 'pageX') < this.s.aoTargets[i-1].x + ((this.s.aoTargets[i].x-this.s.aoTargets[i-1].x)/2) ) 1260 { 1261 this.dom.pointer.css( 'left', this.s.aoTargets[i-1].x ); 1262 this.s.mouse.toIndex = this.s.aoTargets[i-1].to; 1263 bSet = true; 1264 break; 1265 } 1266 } 1267 1268 // The insert element wasn't positioned in the array (less than 1269 // operator), so we put it at the end 1270 if ( !bSet ) 1271 { 1272 this.dom.pointer.css( 'left', this.s.aoTargets[this.s.aoTargets.length-1].x ); 1273 this.s.mouse.toIndex = this.s.aoTargets[this.s.aoTargets.length-1].to; 1274 } 1275 1276 // Perform reordering if realtime updating is on and the column has moved 1277 if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) { 1278 this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, false ); 1279 this.s.mouse.fromIndex = this.s.mouse.toIndex; 1280 this._fnRegions(); 1281 } 1282 } 1283 }, 1284 1285 1286 /** 1287 * Finish off the mouse drag and insert the column where needed 1288 * @method _fnMouseUp 1289 * @param event e Mouse event 1290 * @param colResized The index of the column that was just dragged or resized (index within the internal model, not the visible order). 1291 * @returns void 1292 * @private 1293 */ 1294 "_fnMouseUp": function ( e, colResized) 1295 { 1296 var that = this; 1297 1298 $(document).off('.ColReorder'); 1299 1300 if ( this.dom.drag !== null ) 1301 { 1302 /* Remove the guide elements */ 1303 this.dom.drag.remove(); 1304 this.dom.pointer.remove(); 1305 this.dom.drag = null; 1306 this.dom.pointer = null; 1307 1308 /* Actually do the reorder */ 1309 this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true ); 1310 this._fnSetColumnIndexes(); 1311 1312 /* When scrolling we need to recalculate the column sizes to allow for the shift */ 1313 if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) 1314 { 1315 this.s.dt.oInstance.fnAdjustColumnSizing( false ); 1316 } 1317 1318 this.s.dt.oInstance.trigger('column-reorder.dt.mouseup', [ this.s.dt ] ); 1319 1320 /* Save the state */ 1321 this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); 1322 1323 if ( this.s.reorderCallback !== null ) 1324 { 1325 this.s.reorderCallback.call( this ); 1326 } 1327 } 1328 else if(this.dom.resize !== null) { 1329 var i; 1330 var j; 1331 var currentColumn; 1332 var nextVisibleColumnIndex; 1333 var previousVisibleColumnIndex; 1334 var scrollXEnabled; 1335 1336 //Re-enable column sorting 1337 this.s.dt.aoColumns[colResized].bSortable = this.s.dt.aoColumns[colResized].CRbSortableCache; 1338 1339 //Save the new resized column's width 1340 this.s.dt.aoColumns[colResized].sWidth = $(this.s.mouse.resizeElem).width() + "px"; 1341 1342 //If other columns might have changed their size, save their size too 1343 scrollXEnabled = this.s.dt.oInit.sScrollX === "" ? false : true; 1344 if(!scrollXEnabled) { 1345 //The colResized index (internal model) here might not match the visible index since some columns might have been hidden 1346 for(nextVisibleColumnIndex=colResized+1; nextVisibleColumnIndex < this.s.dt.aoColumns.length; nextVisibleColumnIndex++) { 1347 if(this.s.dt.aoColumns[nextVisibleColumnIndex].bVisible) { 1348 break; 1349 } 1350 } 1351 1352 for(previousVisibleColumnIndex=colResized-1; previousVisibleColumnIndex >= 0; previousVisibleColumnIndex--) { 1353 if(this.s.dt.aoColumns[previousVisibleColumnIndex].bVisible) { 1354 break; 1355 } 1356 } 1357 1358 if(this.s.dt.aoColumns.length > nextVisibleColumnIndex) { 1359 this.s.dt.aoColumns[nextVisibleColumnIndex].sWidth = $(this.s.mouse.resizeElem).next().width() + "px"; 1360 } 1361 // The column resized is the right-most, so save the sizes of all the columns at the left 1362 else { 1363 currentColumn = this.s.mouse.resizeElem; 1364 for(i = previousVisibleColumnIndex; i > 0; i--) { 1365 if(this.s.dt.aoColumns[i].bVisible) { 1366 currentColumn = $(currentColumn).prev(); 1367 this.s.dt.aoColumns[i].sWidth = $(currentColumn).width() + "px"; 1368 } 1369 } 1370 } 1371 } 1372 1373 // Update the internal storage of the table's width (in case we changed it because the user resized some column and scrollX was enabled 1374 if(scrollXEnabled && $(this.s.dt.nScrollHead, this.s.dt.nTableWrapper) != undefined){ 1375 if($(this.s.dt.nScrollHead, this.s.dt.nTableWrapper).length > 0) 1376 this.table_size = $($(this.s.dt.nScrollHead, this.s.dt.nTableWrapper)[0].childNodes[0].childNodes[0]).width(); 1377 } 1378 1379 this.s.dt.oInstance.trigger('column-resize.dt.mouseup', [ this.s.dt ] ); 1380 1381 /* Save the state */ 1382 this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); 1383 1384 /* When scrolling we need to recalculate the column sizes to allow for the shift */ 1385 if (scrollXEnabled && (this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )) 1386 { 1387 this.s.dt.oInstance.fnAdjustColumnSizing( false ); 1388 } 1389 1390 if ( this.s.resizeCallback !== null ) 1391 { 1392 this.s.resizeCallback.call( this ); 1393 } 1394 } 1395 1396 this.dom.resize = null; 1397 }, 1398 1399 1400 /** 1401 * Calculate a cached array with the points of the column inserts, and the 1402 * 'to' points 1403 * @method _fnRegions 1404 * @returns void 1405 * @private 1406 */ 1407 "_fnRegions": function () 1408 { 1409 var aoColumns = this.s.dt.aoColumns; 1410 1411 this.s.aoTargets.splice( 0, this.s.aoTargets.length ); 1412 1413 this.s.aoTargets.push( { 1414 "x": $(this.s.dt.nTable).offset().left, 1415 "to": 0 1416 } ); 1417 1418 var iToPoint = 0; 1419 var total = this.s.aoTargets[0].x; 1420 1421 for ( var i=0, iLen=aoColumns.length ; i<iLen ; i++ ) 1422 { 1423 /* For the column / header in question, we want it's position to remain the same if the 1424 * position is just to it's immediate left or right, so we only increment the counter for 1425 * other columns 1426 */ 1427 if ( i != this.s.mouse.fromIndex ) 1428 { 1429 iToPoint++; 1430 } 1431 1432 if ( aoColumns[i].bVisible && aoColumns[i].nTh.style.display !=='none' ) 1433 { 1434 total += $(aoColumns[i].nTh).outerWidth(); 1435 1436 this.s.aoTargets.push( { 1437 "x": total, 1438 "to": iToPoint 1439 } ); 1440 } 1441 } 1442 1443 /* Disallow columns for being reordered by drag and drop, counting right to left */ 1444 if ( this.s.fixedRight !== 0 ) 1445 { 1446 this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight ); 1447 } 1448 1449 /* Disallow columns for being reordered by drag and drop, counting left to right */ 1450 if ( this.s.fixed !== 0 ) 1451 { 1452 this.s.aoTargets.splice( 0, this.s.fixed ); 1453 } 1454 }, 1455 1456 1457 /** 1458 * Copy the TH element that is being drags so the user has the idea that they are actually 1459 * moving it around the page. 1460 * @method _fnCreateDragNode 1461 * @returns void 1462 * @private 1463 */ 1464 "_fnCreateDragNode": function () 1465 { 1466 var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== ""; 1467 1468 var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh; 1469 var origTr = origCell.parentNode; 1470 var origThead = origTr.parentNode; 1471 var origTable = origThead.parentNode; 1472 var cloneCell = $(origCell).clone(); 1473 1474 // This is a slightly odd combination of jQuery and DOM, but it is the 1475 // fastest and least resource intensive way I could think of cloning 1476 // the table with just a single header cell in it. 1477 this.dom.drag = $(origTable.cloneNode(false)) 1478 .addClass( this.s.classNameClonedTable ) 1479 .append( 1480 $(origThead.cloneNode(false)).append( 1481 $(origTr.cloneNode(false)).append( 1482 cloneCell[0] 1483 ) 1484 ) 1485 ) 1486 .css( { 1487 position: 'absolute', 1488 top: 0, 1489 left: 0, 1490 width: $(origCell).outerWidth(), 1491 height: $(origCell).outerHeight() 1492 } ) 1493 .appendTo( 'body' ); 1494
1495 this.dom.pointer = $('<div></div>') 1496 .addClass( this.s.classNamePointer ) 1497 .css( { 1498 position: 'absolute', 1499 top: scrolling ? 1500 $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top : 1501 $(this.s.dt.nTable).offset().top, 1502 height : scrolling ? 1503 $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() : 1504 $(this.s.dt.nTable).height() 1505 } ) 1506 .appendTo( 'body' ); 1507 }, 1508 1509 1510 /** 1511 * Add a data attribute to the column headers, so we know the index of 1512 * the row to be reordered. This allows fast detection of the index, and 1513 * for this plug-in to work with FixedHeader which clones the nodes. 1514 * @private 1515 */ 1516 "_fnSetColumnIndexes": function () 1517 { 1518 $.each( this.s.dt.aoColumns, function (i, column) { 1519 $(column.nTh).attr('data-column-index', i); 1520 } ); 1521 }, 1522 1523 1524 /** 1525 * Get cursor position regardless of mouse or touch input 1526 * @param {Event} e jQuery Event 1527 * @param {string} prop Property to get 1528 * @return {number} Value 1529 */ 1530 _fnCursorPosition: function ( e, prop ) { 1531 if ( e.type.indexOf('touch') !== -1 ) { 1532 return e.originalEvent.touches[0][ prop ]; 1533 } 1534 return e[ prop ]; 1535 } 1536} ); 1537 1538 1539 1540/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1541 * Static parameters 1542 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1543 1544/** 1545 * ColReorder default settings for initialisation 1546 * @namespace 1547 * @static 1548 */ 1549ColReorder.defaults = { 1550 /** 1551 * Predefined ordering for the columns that will be applied automatically 1552 * on initialisation. If not specified then the order that the columns are 1553 * found to be in the HTML is the order used. 1554 * @type array 1555 * @default null 1556 * @static 1557 */ 1558 aiOrder: null, 1559 1560 /** 1561 * Redraw the table's column ordering as the end user draws the column 1562 * (`true`) or wait until the mouse is released (`false` - default). Note 1563 * that this will perform a redraw on each reordering, which involves an 1564 * Ajax request each time if you are using server-side processing in 1565 * DataTables. 1566 * @type boolean 1567 * @default false 1568 * @static 1569 */ 1570 bRealtime: true, 1571 1572 /** 1573 * Indicate how many columns should be fixed in position (counting from the 1574 * left). This will typically be 1 if used, but can be as high as you like. 1575 * @type int 1576 * @default 0 1577 * @static 1578 */ 1579 iFixedColumnsLeft: 0, 1580 1581 /** 1582 * As `iFixedColumnsRight` but counting from the right. 1583 * @type int 1584 * @default 0 1585 * @static 1586 */ 1587 iFixedColumnsRight: 0, 1588 1589 /** 1590 * Callback function that is fired when columns are reordered. The `column- 1591 * reorder` event is preferred over this callback 1592 * @type function():void 1593 * @default null 1594 * @static 1595 */ 1596 fnReorderCallback: null, 1597 1598 /** 1599 * Callback function that is fired when columns are resized. The `column- 1600 * resize` event is preferred over this callback 1601 * @type function():void 1602 * @default null 1603 * @static 1604 */ 1605 fnResizeCallback: null, 1606 1607 /** 1608 * Allow Reorder functionnality 1609 * @property allowReorder 1610 * @type boolean 1611 * @default true 1612 */ 1613 allowReorder: true, 1614 1615 /** 1616 * Allow Resize functionnality 1617 * @property allowResize 1618 * @type boolean 1619 * @default true 1620 */ 1621 allowResize: true, 1622 1623 /** 1624 * Classname added to cloned element 1625 * @type string 1626 * @default 'DTCR_clonedTable' 1627 * @static 1628 */ 1629 classNameClonedTable: 'DTCR_clonedTable', 1630 1631 /** 1632 * Classname added to the cloned element wrapper 1633 * @type string 1634 * @default 'DTCR_pointer' 1635 * @static 1636 */ 1637 classNamePointer: 'DTCR_pointer', 1638 1639 /** 1640 * Classname added to the resizable th 1641 * @type string 1642 * @default 'DTCR_tableHeader' 1643 * @static 1644 */ 1645 classNameTableHeader: 'DTCR_tableHeader', 1646 1647 /** 1648 * Classname added to the resizable th when hovered 1649 * @type string 1650 * @default 'DTCR_tableHeaderHover' 1651 * @static 1652 */ 1653 classNameTableHeaderHover: 'DTCR_tableHeaderHover' 1654}; 1655 1656 1657 1658/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1659 * Constants 1660 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1661
1662/** 1663 * ColReorder version 1664 * @constant version 1665 * @type String 1666 * @default As code 1667 */ 1668ColReorder.version = "1.3.3"; 1669 1670/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1671 * DataTables interfaces 1672 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1673 1674// Expose 1675$.fn.dataTable.ColReorder = ColReorder; 1676$.fn.DataTable.ColReorder = ColReorder; 1677 1678 1679// Register a new feature with DataTables 1680if ( typeof $.fn.dataTable == "function" && 1681 typeof $.fn.dataTableExt.fnVersionCheck == "function" && 1682 $.fn.dataTableExt.fnVersionCheck('1.10.8') ) 1683{ 1684 $.fn.dataTableExt.aoFeatures.push( { 1685 "fnInit": function( settings ) { 1686 var table = settings.oInstance; 1687 1688 if ( ! settings._colReorder ) { 1689 var dtInit = settings.oInit; 1690 var opts = dtInit.colReorder || dtInit.oColReorder || {}; 1691 1692 new ColReorder( settings, opts ); 1693 } 1694 else { 1695 table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" ); 1696 } 1697 1698 return null; /* No node for DataTables to insert */ 1699 }, 1700 "cFeature": "R", 1701 "sFeature": "ColReorder" 1702 } ); 1703} 1704else { 1705 alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download"); 1706} 1707 1708 1709// Attach a listener to the document which listens for DataTables initialisation 1710// events so we can automatically initialise 1711$(document).on( 'preInit.dt.colReorder', function (e, settings) { 1712 if ( e.namespace !== 'dt' ) { 1713 return; 1714 } 1715 1716 var init = settings.oInit.colReorder; 1717 var defaults = DataTable.defaults.colReorder; 1718 1719 if ( init || defaults ) { 1720 var opts = $.extend( {}, init, defaults ); 1721 1722 if ( init !== false ) { 1723 new ColReorder( settings, opts ); 1724 } 1725 } 1726} ); 1727 1728 1729// API augmentation 1730$.fn.dataTable.Api.register( 'colReorder.reset()', function () { 1731 return this.iterator( 'table', function ( ctx ) { 1732 ctx._colReorder.fnReset(); 1733 } ); 1734} ); 1735 1736$.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) { 1737 if ( set ) { 1738 return this.iterator( 'table', function ( ctx ) { 1739 ctx._colReorder.fnOrder( set, original ); 1740 } ); 1741 } 1742 1743 return this.context.length ? 1744 this.context[0]._colReorder.fnOrder() : 1745 null; 1746} ); 1747 1748$.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) { 1749 return this.context.length && this.context[0]._colReorder ? 1750 this.context[0]._colReorder.fnTranspose( idx, dir ) : 1751 idx; 1752} ); 1753 1754 1755return ColReorder; 1756}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.