GPQAPP
1/*! ColReorder 1.5.4
2* ©2010-2021 SpryMedia Ltd - datatables.net/license
3*/
4
5/**
6* @summary ColReorder
7* @description Provide the ability to reorder columns in a DataTable
8* @version 1.5.4
9* @file dataTables.colReorder.js
10* @author SpryMedia Ltd (www.sprymedia.co.uk)
11* @contact www.sprymedia.co.uk/contact
12* @copyright Copyright 2010-2021 SpryMedia Ltd.
13*
14* This source file is free software, available under the following license:
15* MIT license - http://datatables.net/license/mit
16*
17* This source file is distributed in the hope that it will be useful, but
18* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20*
21* For details please refer to: http://www.datatables.net
22*/
23(function( factory ){24if ( typeof define === 'function' && define.amd ) {25// AMD26define( ['jquery', 'datatables.net'], function ( $ ) {27return factory( $, window, document );28} );29}30else if ( typeof exports === 'object' ) {31// CommonJS32module.exports = function (root, $) {33if ( ! root ) {34root = window;35}36
37if ( ! $ || ! $.fn.dataTable ) {38$ = require('datatables.net')(root, $).$;39}40
41return factory( $, root, root.document );42};43}44else {45// Browser46factory( jQuery, window, document );47}48}(function( $, window, document, undefined ) {49'use strict';50var DataTable = $.fn.dataTable;51
52
53/**
54* Switch the key value pairing of an index array to be value key (i.e. the old value is now the
55* key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ].
56* @method fnInvertKeyValues
57* @param array aIn Array to switch around
58* @returns array
59*/
60function fnInvertKeyValues( aIn )61{
62var aRet=[];63for ( var i=0, iLen=aIn.length ; i<iLen ; i++ )64{65aRet[ aIn[i] ] = i;66}67return aRet;68}
69
70
71/**
72* Modify an array by switching the position of two elements
73* @method fnArraySwitch
74* @param array aArray Array to consider, will be modified by reference (i.e. no return)
75* @param int iFrom From point
76* @param int iTo Insert point
77* @returns void
78*/
79function fnArraySwitch( aArray, iFrom, iTo )80{
81var mStore = aArray.splice( iFrom, 1 )[0];82aArray.splice( iTo, 0, mStore );83}
84
85
86/**
87* Switch the positions of nodes in a parent node (note this is specifically designed for
88* table rows). Note this function considers all element nodes under the parent!
89* @method fnDomSwitch
90* @param string sTag Tag to consider
91* @param int iFrom Element to move
92* @param int Point to element the element to (before this point), can be null for append
93* @returns void
94*/
95function fnDomSwitch( nParent, iFrom, iTo )96{
97var anTags = [];98for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ )99{100if ( nParent.childNodes[i].nodeType == 1 )101{102anTags.push( nParent.childNodes[i] );103}104}105var nStore = anTags[ iFrom ];106
107if ( iTo !== null )108{109nParent.insertBefore( nStore, anTags[iTo] );110}111else112{113nParent.appendChild( nStore );114}115}
116
117
118/**
119* Plug-in for DataTables which will reorder the internal column structure by taking the column
120* from one position (iFrom) and insert it into a given point (iTo).
121* @method $.fn.dataTableExt.oApi.fnColReorder
122* @param object oSettings DataTables settings object - automatically added by DataTables!
123* @param int iFrom Take the column to be repositioned from this point
124* @param int iTo and insert it into this point
125* @param bool drop Indicate if the reorder is the final one (i.e. a drop)
126* not a live reorder
127* @param bool invalidateRows speeds up processing if false passed
128* @returns void
129*/
130$.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows )131{
132var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol;133var attrMap = function ( obj, prop, mapping ) {134if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) {135return;136}137
138var a = obj[ prop ].split('.');139var num = a.shift();140
141if ( isNaN( num*1 ) ) {142return;143}144
145obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.');146};147
148/* Sanity check in the input */149if ( iFrom == iTo )150{151/* Pointless reorder */152return;153}154
155if ( iFrom < 0 || iFrom >= iCols )156{157this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom );158return;159}160
161if ( iTo < 0 || iTo >= iCols )162{163this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo );164return;165}166
167/*168* Calculate the new column array index, so we have a mapping between the old and new
169*/
170var aiMapping = [];171for ( i=0, iLen=iCols ; i<iLen ; i++ )172{173aiMapping[i] = i;174}175fnArraySwitch( aiMapping, iFrom, iTo );176var aiInvertMapping = fnInvertKeyValues( aiMapping );177
178
179/*180* Convert all internal indexing to the new column order indexes
181*/
182/* Sorting */183for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )184{185oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ];186}187
188/* Fixed sorting */189if ( oSettings.aaSortingFixed !== null )190{191for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ )192{193oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ];194}195}196
197/* Data column sorting (the column which the sort for a given column should take place on) */198for ( i=0, iLen=iCols ; i<iLen ; i++ )199{200oCol = oSettings.aoColumns[i];201for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ )202{203oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ];204}205
206// Update the column indexes207oCol.idx = aiInvertMapping[ oCol.idx ];208}209
210// Update 1.10 optimised sort class removal variable211$.each( oSettings.aLastSort, function (i, val) {212oSettings.aLastSort[i].src = aiInvertMapping[ val.src ];213} );214
215/* Update the Get and Set functions for each column */216for ( i=0, iLen=iCols ; i<iLen ; i++ )217{218oCol = oSettings.aoColumns[i];219
220if ( typeof oCol.mData == 'number' ) {221oCol.mData = aiInvertMapping[ oCol.mData ];222}223else if ( $.isPlainObject( oCol.mData ) ) {224// HTML5 data sourced225attrMap( oCol.mData, '_', aiInvertMapping );226attrMap( oCol.mData, 'filter', aiInvertMapping );227attrMap( oCol.mData, 'sort', aiInvertMapping );228attrMap( oCol.mData, 'type', aiInvertMapping );229}230}231
232/*233* Move the DOM elements
234*/
235if ( oSettings.aoColumns[iFrom].bVisible )236{237/* Calculate the current visible index and the point to insert the node before. The insert238* before needs to take into account that there might not be an element to insert before,
239* in which case it will be null, and an appendChild should be used
240*/
241var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom );242var iInsertBeforeIndex = null;243
244i = iTo < iFrom ? iTo : iTo + 1;245while ( iInsertBeforeIndex === null && i < iCols )246{247iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i );248i++;249}250
251/* Header */252nTrs = oSettings.nTHead.getElementsByTagName('tr');253for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )254{255fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );256}257
258/* Footer */259if ( oSettings.nTFoot !== null )260{261nTrs = oSettings.nTFoot.getElementsByTagName('tr');262for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )263{264fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );265}266}267
268/* Body */269for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )270{271if ( oSettings.aoData[i].nTr !== null )272{273fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex );274}275}276}277
278/*279* Move the internal array elements
280*/
281/* Columns */282fnArraySwitch( oSettings.aoColumns, iFrom, iTo );283
284// regenerate the get / set functions285for ( i=0, iLen=iCols ; i<iLen ; i++ ) {286oSettings.oApi._fnColumnOptions( oSettings, i, {} );287}288
289/* Search columns */290fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo );291
292/* Array array - internal data anodes cache */293for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )294{295var data = oSettings.aoData[i];296var cells = data.anCells;297
298if ( cells ) {299fnArraySwitch( cells, iFrom, iTo );300
301// Longer term, should this be moved into the DataTables' invalidate302// methods?303for ( j=0, jen=cells.length ; j<jen ; j++ ) {304if ( cells[j] && cells[j]._DT_CellIndex ) {305cells[j]._DT_CellIndex.column = j;306}307}308}309
310// For DOM sourced data, the invalidate will reread the cell into311// the data array, but for data sources as an array, they need to312// be flipped313if ( data.src !== 'dom' && Array.isArray( data._aData ) ) {314fnArraySwitch( data._aData, iFrom, iTo );315}316}317
318/* Reposition the header elements in the header layout array */319for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ )320{321fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo );322}323
324if ( oSettings.aoFooter !== null )325{326for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ )327{328fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo );329}330}331
332if ( invalidateRows || invalidateRows === undefined )333{334$.fn.dataTable.Api( oSettings ).rows().invalidate();335}336
337/*338* Update DataTables' event handlers
339*/
340
341/* Sort listener */342for ( i=0, iLen=iCols ; i<iLen ; i++ )343{344$(oSettings.aoColumns[i].nTh).off('.DT');345this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );346}347
348
349/* Fire an event so other plug-ins can update */350$(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, {351from: iFrom,352to: iTo,353mapping: aiInvertMapping,354drop: drop,355
356// Old style parameters for compatibility357iFrom: iFrom,358iTo: iTo,359aiInvertMapping: aiInvertMapping360} ] );361};362
363/**
364* ColReorder provides column visibility control for DataTables
365* @class ColReorder
366* @constructor
367* @param {object} dt DataTables settings object
368* @param {object} opts ColReorder options
369*/
370var ColReorder = function( dt, opts )371{
372var settings = new $.fn.dataTable.Api( dt ).settings()[0];373
374// Ensure that we can't initialise on the same table twice375if ( settings._colReorder ) {376return settings._colReorder;377}378
379// Allow the options to be a boolean for defaults380if ( opts === true ) {381opts = {};382}383
384// Convert from camelCase to Hungarian, just as DataTables does385var camelToHungarian = $.fn.dataTable.camelToHungarian;386if ( camelToHungarian ) {387camelToHungarian( ColReorder.defaults, ColReorder.defaults, true );388camelToHungarian( ColReorder.defaults, opts || {} );389}390
391
392/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *393* Public class variables
394* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
395
396/**397* @namespace Settings object which contains customisable information for ColReorder instance
398*/
399this.s = {400/**401* DataTables settings object
402* @property dt
403* @type Object
404* @default null
405*/
406"dt": null,407
408/**409* Enable flag
410* @property dt
411* @type Object
412* @default null
413*/
414"enable": null,415
416/**417* Initialisation object used for this instance
418* @property init
419* @type object
420* @default {}
421*/
422"init": $.extend( true, {}, ColReorder.defaults, opts ),423
424/**425* Number of columns to fix (not allow to be reordered)
426* @property fixed
427* @type int
428* @default 0
429*/
430"fixed": 0,431
432/**433* Number of columns to fix counting from right (not allow to be reordered)
434* @property fixedRight
435* @type int
436* @default 0
437*/
438"fixedRight": 0,439
440/**441* Callback function for once the reorder has been done
442* @property reorderCallback
443* @type function
444* @default null
445*/
446"reorderCallback": null,447
448/**449* @namespace Information used for the mouse drag
450*/
451"mouse": {452"startX": -1,453"startY": -1,454"offsetX": -1,455"offsetY": -1,456"target": -1,457"targetIndex": -1,458"fromIndex": -1459},460
461/**462* Information which is used for positioning the insert cusor and knowing where to do the
463* insert. Array of objects with the properties:
464* x: x-axis position
465* to: insert point
466* @property aoTargets
467* @type array
468* @default []
469*/
470"aoTargets": []471};472
473
474/**475* @namespace Common and useful DOM elements for the class instance
476*/
477this.dom = {478/**479* Dragging element (the one the mouse is moving)
480* @property drag
481* @type element
482* @default null
483*/
484"drag": null,485
486/**487* The insert cursor
488* @property pointer
489* @type element
490* @default null
491*/
492"pointer": null493};494
495/* Constructor logic */496this.s.enable = this.s.init.bEnable;497this.s.dt = settings;498this.s.dt._colReorder = this;499this._fnConstruct();500
501return this;502};503
504
505
506$.extend( ColReorder.prototype, {507/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *508* Public methods
509* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
510
511/**512* Enable / disable end user interaction
513*/
514fnEnable: function ( flag )515{516if ( flag === false ) {517return fnDisable();518}519
520this.s.enable = true;521},522
523/**524* Disable end user interaction
525*/
526fnDisable: function ()527{528this.s.enable = false;529},530
531/**532* Reset the column ordering to the original ordering that was detected on
533* start up.
534* @return {this} Returns `this` for chaining.
535*
536* @example
537* // DataTables initialisation with ColReorder
538* var table = $('#example').dataTable( {
539* "sDom": 'Rlfrtip'
540* } );
541*
542* // Add click event to a button to reset the ordering
543* $('#resetOrdering').click( function (e) {
544* e.preventDefault();
545* $.fn.dataTable.ColReorder( table ).fnReset();
546* } );
547*/
548"fnReset": function ()549{550this._fnOrderColumns( this.fnOrder() );551
552return this;553},554
555/**556* `Deprecated` - Get the current order of the columns, as an array.
557* @return {array} Array of column identifiers
558* @deprecated `fnOrder` should be used in preference to this method.
559* `fnOrder` acts as a getter/setter.
560*/
561"fnGetCurrentOrder": function ()562{563return this.fnOrder();564},565
566/**567* Get the current order of the columns, as an array. Note that the values
568* given in the array are unique identifiers for each column. Currently
569* these are the original ordering of the columns that was detected on
570* start up, but this could potentially change in future.
571* @return {array} Array of column identifiers
572*
573* @example
574* // Get column ordering for the table
575* var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder();
576*//**577* Set the order of the columns, from the positions identified in the578* ordering array given. Note that ColReorder takes a brute force approach579* to reordering, so it is possible multiple reordering events will occur580* before the final order is settled upon.581* @param {array} [set] Array of column identifiers in the new order. Note582* that every column must be included, uniquely, in this array.583* @return {this} Returns `this` for chaining.584*585* @example586* // Swap the first and second columns587* $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] );588*589* @example590* // Move the first column to the end for the table `#example`591* var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder();592* var first = curr.shift();593* curr.push( first );594* $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr );595*596* @example597* // Reverse the table's order598* $.fn.dataTable.ColReorder( '#example' ).fnOrder(599* $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse()600* );601*/602"fnOrder": function ( set, original )603{604var a = [], i, ien, j, jen;605var columns = this.s.dt.aoColumns;606
607if ( set === undefined ){608for ( i=0, ien=columns.length ; i<ien ; i++ ) {609a.push( columns[i]._ColReorder_iOrigCol );610}611
612return a;613}614
615// The order given is based on the original indexes, rather than the616// existing ones, so we need to translate from the original to current617// before then doing the order618if ( original ) {619var order = this.fnOrder();620
621for ( i=0, ien=set.length ; i<ien ; i++ ) {622a.push( $.inArray( set[i], order ) );623}624
625set = a;626}627
628this._fnOrderColumns( fnInvertKeyValues( set ) );629
630return this;631},632
633
634/**635* Convert from the original column index, to the original
636*
637* @param {int|array} idx Index(es) to convert
638* @param {string} dir Transpose direction - `fromOriginal` / `toCurrent`
639* or `'toOriginal` / `fromCurrent`
640* @return {int|array} Converted values
641*/
642fnTranspose: function ( idx, dir )643{644if ( ! dir ) {645dir = 'toCurrent';646}647
648var order = this.fnOrder();649var columns = this.s.dt.aoColumns;650
651if ( dir === 'toCurrent' ) {652// Given an original index, want the current653return ! Array.isArray( idx ) ?654$.inArray( idx, order ) :655$.map( idx, function ( index ) {656return $.inArray( index, order );657} );658}659else {660// Given a current index, want the original661return ! Array.isArray( idx ) ?662columns[idx]._ColReorder_iOrigCol :663$.map( idx, function ( index ) {664return columns[index]._ColReorder_iOrigCol;665} );666}667},668
669
670/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *671* Private methods (they are of course public in JS, but recommended as private)
672* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
673
674/**675* Constructor logic
676* @method _fnConstruct
677* @returns void
678* @private
679*/
680"_fnConstruct": function ()681{682var that = this;683var iLen = this.s.dt.aoColumns.length;684var table = this.s.dt.nTable;685var i;686
687/* Columns discounted from reordering - counting left to right */688if ( this.s.init.iFixedColumns )689{690this.s.fixed = this.s.init.iFixedColumns;691}692
693if ( this.s.init.iFixedColumnsLeft )694{695this.s.fixed = this.s.init.iFixedColumnsLeft;696}697
698/* Columns discounted from reordering - counting right to left */699this.s.fixedRight = this.s.init.iFixedColumnsRight ?700this.s.init.iFixedColumnsRight :7010;702
703/* Drop callback initialisation option */704if ( this.s.init.fnReorderCallback )705{706this.s.reorderCallback = this.s.init.fnReorderCallback;707}708
709/* Add event handlers for the drag and drop, and also mark the original column order */710for ( i = 0; i < iLen; i++ )711{712if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight )713{714this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh );715}716
717/* Mark the original column order for later reference */718this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i;719}720
721/* State saving */722this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {723that._fnStateSave.call( that, oData );724}, "ColReorder_State" );725
726/* An initial column order has been specified */727var aiOrder = null;728if ( this.s.init.aiOrder )729{730aiOrder = this.s.init.aiOrder.slice();731}732
733/* State loading, overrides the column order given */734if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' &&735this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length )736{737aiOrder = this.s.dt.oLoadedState.ColReorder;738}739
740/* If we have an order to apply - do so */741if ( aiOrder )742{743/* We might be called during or after the DataTables initialisation. If before, then we need744* to wait until the draw is done, if after, then do what we need to do right away
745*/
746if ( !that.s.dt._bInitComplete )747{748var bDone = false;749$(table).on( 'draw.dt.colReorder', function () {750if ( !that.s.dt._bInitComplete && !bDone )751{752bDone = true;753var resort = fnInvertKeyValues( aiOrder );754that._fnOrderColumns.call( that, resort );755}756} );757}758else759{760var resort = fnInvertKeyValues( aiOrder );761that._fnOrderColumns.call( that, resort );762}763}764else {765this._fnSetColumnIndexes();766}767
768// Destroy clean up769$(table).on( 'destroy.dt.colReorder', function () {770$(table).off( 'destroy.dt.colReorder draw.dt.colReorder' );771
772$.each( that.s.dt.aoColumns, function (i, column) {773$(column.nTh).off('.ColReorder');774$(column.nTh).removeAttr('data-column-index');775} );776
777that.s.dt._colReorder = null;778that.s = null;779} );780},781
782
783/**784* Set the column order from an array
785* @method _fnOrderColumns
786* @param array a An array of integers which dictate the column order that should be applied
787* @returns void
788* @private
789*/
790"_fnOrderColumns": function ( a )791{792var changed = false;793
794if ( a.length != this.s.dt.aoColumns.length )795{796this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+797"match known number of columns. Skipping." );798return;799}800
801for ( var i=0, iLen=a.length ; i<iLen ; i++ )802{803var currIndex = $.inArray( i, a );804if ( i != currIndex )805{806/* Reorder our switching array */807fnArraySwitch( a, currIndex, i );808
809/* Do the column reorder in the table */810this.s.dt.oInstance.fnColReorder( currIndex, i, true, false );811
812changed = true;813}814}815
816this._fnSetColumnIndexes();817
818// Has anything actually changed? If not, then nothing else to do819if ( ! changed ) {820return;821}822
823$.fn.dataTable.Api( this.s.dt ).rows().invalidate();824
825/* When scrolling we need to recalculate the column sizes to allow for the shift */826if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )827{828this.s.dt.oInstance.fnAdjustColumnSizing( false );829}830
831/* Save the state */832this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );833
834if ( this.s.reorderCallback !== null )835{836this.s.reorderCallback.call( this );837}838},839
840
841/**842* Because we change the indexes of columns in the table, relative to their starting point
843* we need to reorder the state columns to what they are at the starting point so we can
844* then rearrange them again on state load!
845* @method _fnStateSave
846* @param object oState DataTables state
847* @returns string JSON encoded cookie string for DataTables
848* @private
849*/
850"_fnStateSave": function ( oState )851{852var i, iLen, aCopy, iOrigColumn;853var oSettings = this.s.dt;854var columns = oSettings.aoColumns;855
856oState.ColReorder = [];857
858/* Sorting */859if ( oState.aaSorting ) {860// 1.10.0-861for ( i=0 ; i<oState.aaSorting.length ; i++ ) {862oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol;863}864
865var aSearchCopy = $.extend( true, [], oState.aoSearchCols );866
867for ( i=0, iLen=columns.length ; i<iLen ; i++ )868{869iOrigColumn = columns[i]._ColReorder_iOrigCol;870
871/* Column filter */872oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i];873
874/* Visibility */875oState.abVisCols[ iOrigColumn ] = columns[i].bVisible;876
877/* Column reordering */878oState.ColReorder.push( iOrigColumn );879}880}881else if ( oState.order ) {882// 1.10.1+883for ( i=0 ; i<oState.order.length ; i++ ) {884oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol;885}886
887var stateColumnsCopy = $.extend( true, [], oState.columns );888
889for ( i=0, iLen=columns.length ; i<iLen ; i++ )890{891iOrigColumn = columns[i]._ColReorder_iOrigCol;892
893/* Columns */894oState.columns[ iOrigColumn ] = stateColumnsCopy[i];895
896/* Column reordering */897oState.ColReorder.push( iOrigColumn );898}899}900},901
902
903/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *904* Mouse drop and drag
905*/
906
907/**908* Add a mouse down listener to a particluar TH element
909* @method _fnMouseListener
910* @param int i Column index
911* @param element nTh TH element clicked on
912* @returns void
913* @private
914*/
915"_fnMouseListener": function ( i, nTh )916{917var that = this;918$(nTh)919.on( 'mousedown.ColReorder', function (e) {920if ( that.s.enable && e.which === 1 ) {921that._fnMouseDown.call( that, e, nTh );922}923} )924.on( 'touchstart.ColReorder', function (e) {925if ( that.s.enable ) {926that._fnMouseDown.call( that, e, nTh );927}928} );929},930
931
932/**933* Mouse down on a TH element in the table header
934* @method _fnMouseDown
935* @param event e Mouse event
936* @param element nTh TH element to be dragged
937* @returns void
938* @private
939*/
940"_fnMouseDown": function ( e, nTh )941{942var that = this;943
944/* Store information about the mouse position */945var target = $(e.target).closest('th, td');946var offset = target.offset();947var idx = parseInt( $(nTh).attr('data-column-index'), 10 );948
949if ( idx === undefined ) {950return;951}952
953this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' );954this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' );955this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left;956this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top;957this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh;//target[0];958this.s.mouse.targetIndex = idx;959this.s.mouse.fromIndex = idx;960
961this._fnRegions();962
963/* Add event handlers to the document */964$(document)965.on( 'mousemove.ColReorder touchmove.ColReorder', function (e) {966that._fnMouseMove.call( that, e );967} )968.on( 'mouseup.ColReorder touchend.ColReorder', function (e) {969that._fnMouseUp.call( that, e );970} );971},972
973
974/**975* Deal with a mouse move event while dragging a node
976* @method _fnMouseMove
977* @param event e Mouse event
978* @returns void
979* @private
980*/
981"_fnMouseMove": function ( e )982{983var that = this;984
985if ( this.dom.drag === null )986{987/* Only create the drag element if the mouse has moved a specific distance from the start988* point - this allows the user to make small mouse movements when sorting and not have a
989* possibly confusing drag element showing up
990*/
991if ( Math.pow(992Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) +993Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 )994{995return;996}997this._fnCreateDragNode();998}999
1000/* Position the element - we respect where in the element the click occured */1001this.dom.drag.css( {1002left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX,1003top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY1004} );1005
1006/* Based on the current mouse position, calculate where the insert should go */1007var target;1008var lastToIndex = this.s.mouse.toIndex;1009var cursorXPosiotion = this._fnCursorPosition(e, 'pageX');1010var targetsPrev = function (i) {1011while (i >= 0) {1012i--;1013
1014if (i <= 0) {1015return null;1016}1017
1018if (that.s.aoTargets[i+1].x !== that.s.aoTargets[i].x) {1019return that.s.aoTargets[i];1020}1021}1022};1023var firstNotHidden = function () {1024for (var i=0 ; i<that.s.aoTargets.length-1 ; i++) {1025if (that.s.aoTargets[i].x !== that.s.aoTargets[i+1].x) {1026return that.s.aoTargets[i];1027}1028}1029};1030var lastNotHidden = function () {1031for (var i=that.s.aoTargets.length-1 ; i>0 ; i--) {1032if (that.s.aoTargets[i].x !== that.s.aoTargets[i-1].x) {1033return that.s.aoTargets[i];1034}1035}1036};1037
1038for (var i = 1; i < this.s.aoTargets.length; i++) {1039var prevTarget = targetsPrev(i);1040if (! prevTarget) {1041prevTarget = firstNotHidden();1042}1043
1044var prevTargetMiddle = prevTarget.x + (this.s.aoTargets[i].x - prevTarget.x) / 2;1045
1046if (this._fnIsLtr()) {1047if (cursorXPosiotion < prevTargetMiddle ) {1048target = prevTarget;1049break;1050}1051}1052else {1053if (cursorXPosiotion > prevTargetMiddle) {1054target = prevTarget;1055break;1056}1057}1058}1059
1060if (target) {1061this.dom.pointer.css('left', target.x);1062this.s.mouse.toIndex = target.to;1063}1064else {1065// The insert element wasn't positioned in the array (less than1066// operator), so we put it at the end1067this.dom.pointer.css( 'left', lastNotHidden().x );1068this.s.mouse.toIndex = lastNotHidden().to;1069}1070
1071// Perform reordering if realtime updating is on and the column has moved1072if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) {1073this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex );1074this.s.mouse.fromIndex = this.s.mouse.toIndex;1075
1076// Not great for performance, but required to keep everything in alignment1077if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )1078{1079this.s.dt.oInstance.fnAdjustColumnSizing( false );1080}1081
1082this._fnRegions();1083}1084},1085
1086
1087/**1088* Finish off the mouse drag and insert the column where needed
1089* @method _fnMouseUp
1090* @param event e Mouse event
1091* @returns void
1092* @private
1093*/
1094"_fnMouseUp": function ( e )1095{1096var that = this;1097
1098$(document).off( '.ColReorder' );1099
1100if ( this.dom.drag !== null )1101{1102/* Remove the guide elements */1103this.dom.drag.remove();1104this.dom.pointer.remove();1105this.dom.drag = null;1106this.dom.pointer = null;1107
1108/* Actually do the reorder */1109this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true );1110this._fnSetColumnIndexes();1111
1112/* When scrolling we need to recalculate the column sizes to allow for the shift */1113if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )1114{1115this.s.dt.oInstance.fnAdjustColumnSizing( false );1116}1117
1118/* Save the state */1119this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );1120
1121if ( this.s.reorderCallback !== null )1122{1123this.s.reorderCallback.call( this );1124}1125}1126},1127
1128
1129/**1130* Calculate a cached array with the points of the column inserts, and the
1131* 'to' points
1132* @method _fnRegions
1133* @returns void
1134* @private
1135*/
1136"_fnRegions": function ()1137{1138var aoColumns = this.s.dt.aoColumns;1139var isLTR = this._fnIsLtr();1140this.s.aoTargets.splice(0, this.s.aoTargets.length);1141var lastBound = $(this.s.dt.nTable).offset().left;1142
1143var aoColumnBounds = [];1144$.each(aoColumns, function (i, column) {1145if (column.bVisible && column.nTh.style.display !== 'none') {1146var nth = $(column.nTh);1147var bound = nth.offset().left;1148
1149if (isLTR) {1150bound += nth.outerWidth();1151}1152
1153aoColumnBounds.push({1154index: i,1155bound: bound1156});1157
1158lastBound = bound;1159}1160else {1161aoColumnBounds.push({1162index: i,1163bound: lastBound1164});1165}1166});1167
1168var firstColumn = aoColumnBounds[0];1169var firstColumnWidth = $(aoColumns[firstColumn.index].nTh).outerWidth();1170
1171this.s.aoTargets.push({1172to: 0,1173x: firstColumn.bound - firstColumnWidth1174});1175
1176for (var i = 0; i < aoColumnBounds.length; i++) {1177var columnBound = aoColumnBounds[i];1178var iToPoint = columnBound.index;1179
1180/* For the column / header in question, we want it's position to remain the same if the1181* position is just to it's immediate left or right, so we only increment the counter for
1182* other columns
1183*/
1184if (columnBound.index < this.s.mouse.fromIndex) {1185iToPoint++;1186}1187
1188this.s.aoTargets.push({1189to: iToPoint,1190x: columnBound.bound1191});1192}1193
1194/* Disallow columns for being reordered by drag and drop, counting right to left */1195if ( this.s.fixedRight !== 0 )1196{1197this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight );1198}1199
1200/* Disallow columns for being reordered by drag and drop, counting left to right */1201if ( this.s.fixed !== 0 )1202{1203this.s.aoTargets.splice( 0, this.s.fixed );1204}1205},1206
1207
1208/**1209* Copy the TH element that is being drags so the user has the idea that they are actually
1210* moving it around the page.
1211* @method _fnCreateDragNode
1212* @returns void
1213* @private
1214*/
1215"_fnCreateDragNode": function ()1216{1217var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "";1218
1219var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh;1220var origTr = origCell.parentNode;1221var origThead = origTr.parentNode;1222var origTable = origThead.parentNode;1223var cloneCell = $(origCell).clone();1224
1225// This is a slightly odd combination of jQuery and DOM, but it is the1226// fastest and least resource intensive way I could think of cloning1227// the table with just a single header cell in it.1228this.dom.drag = $(origTable.cloneNode(false))1229.addClass( 'DTCR_clonedTable' )1230.append(1231$(origThead.cloneNode(false)).append(1232$(origTr.cloneNode(false)).append(1233cloneCell[0]1234)1235)1236)1237.css( {1238position: 'absolute',1239top: 0,1240left: 0,1241width: $(origCell).outerWidth(),1242height: $(origCell).outerHeight()1243} )1244.appendTo( 'body' );1245
1246this.dom.pointer = $('<div></div>')1247.addClass( 'DTCR_pointer' )1248.css( {1249position: 'absolute',1250top: scrolling ?1251$('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top :1252$(this.s.dt.nTable).offset().top,1253height : scrolling ?1254$('div.dataTables_scroll', this.s.dt.nTableWrapper).height() :1255$(this.s.dt.nTable).height()1256} )1257.appendTo( 'body' );1258},1259
1260
1261/**1262* Add a data attribute to the column headers, so we know the index of
1263* the row to be reordered. This allows fast detection of the index, and
1264* for this plug-in to work with FixedHeader which clones the nodes.
1265* @private
1266*/
1267"_fnSetColumnIndexes": function ()1268{1269$.each( this.s.dt.aoColumns, function (i, column) {1270$(column.nTh).attr('data-column-index', i);1271} );1272},1273
1274
1275/**1276* Get cursor position regardless of mouse or touch input
1277* @param {Event} e jQuery Event
1278* @param {string} prop Property to get
1279* @return {number} Value
1280*/
1281_fnCursorPosition: function ( e, prop ) {1282if ( e.type.indexOf('touch') !== -1 ) {1283return e.originalEvent.touches[0][ prop ];1284}1285return e[ prop ];1286},1287
1288_fnIsLtr: function () {1289return $(this.s.dt.nTable).css('direction') !== "rtl";1290}1291} );1292
1293
1294
1295
1296
1297/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1298* Static parameters
1299* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1300
1301
1302/**
1303* ColReorder default settings for initialisation
1304* @namespace
1305* @static
1306*/
1307ColReorder.defaults = {1308/**1309* Predefined ordering for the columns that will be applied automatically
1310* on initialisation. If not specified then the order that the columns are
1311* found to be in the HTML is the order used.
1312* @type array
1313* @default null
1314* @static
1315*/
1316aiOrder: null,1317
1318/**1319* ColReorder enable on initialisation
1320* @type boolean
1321* @default true
1322* @static
1323*/
1324bEnable: true,1325
1326/**1327* Redraw the table's column ordering as the end user draws the column
1328* (`true`) or wait until the mouse is released (`false` - default). Note
1329* that this will perform a redraw on each reordering, which involves an
1330* Ajax request each time if you are using server-side processing in
1331* DataTables.
1332* @type boolean
1333* @default false
1334* @static
1335*/
1336bRealtime: true,1337
1338/**1339* Indicate how many columns should be fixed in position (counting from the
1340* left). This will typically be 1 if used, but can be as high as you like.
1341* @type int
1342* @default 0
1343* @static
1344*/
1345iFixedColumnsLeft: 0,1346
1347/**1348* As `iFixedColumnsRight` but counting from the right.
1349* @type int
1350* @default 0
1351* @static
1352*/
1353iFixedColumnsRight: 0,1354
1355/**1356* Callback function that is fired when columns are reordered. The `column-
1357* reorder` event is preferred over this callback
1358* @type function():void
1359* @default null
1360* @static
1361*/
1362fnReorderCallback: null1363};1364
1365
1366
1367/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1368* Constants
1369* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1370
1371/**
1372* ColReorder version
1373* @constant version
1374* @type String
1375* @default As code
1376*/
1377ColReorder.version = "1.5.4";1378
1379
1380
1381/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1382* DataTables interfaces
1383* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1384
1385// Expose
1386$.fn.dataTable.ColReorder = ColReorder;1387$.fn.DataTable.ColReorder = ColReorder;1388
1389
1390// Register a new feature with DataTables
1391if ( typeof $.fn.dataTable == "function" &&1392typeof $.fn.dataTableExt.fnVersionCheck == "function" &&1393$.fn.dataTableExt.fnVersionCheck('1.10.8') )1394{
1395$.fn.dataTableExt.aoFeatures.push( {1396"fnInit": function( settings ) {1397var table = settings.oInstance;1398
1399if ( ! settings._colReorder ) {1400var dtInit = settings.oInit;1401var opts = dtInit.colReorder || dtInit.oColReorder || {};1402
1403new ColReorder( settings, opts );1404}1405else {1406table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" );1407}1408
1409return null; /* No node for DataTables to insert */1410},1411"cFeature": "R",1412"sFeature": "ColReorder"1413} );1414}
1415else {1416alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download");1417}
1418
1419
1420// Attach a listener to the document which listens for DataTables initialisation
1421// events so we can automatically initialise
1422$(document).on( 'preInit.dt.colReorder', function (e, settings) {1423if ( e.namespace !== 'dt' ) {1424return;1425}1426
1427var init = settings.oInit.colReorder;1428var defaults = DataTable.defaults.colReorder;1429
1430if ( init || defaults ) {1431var opts = $.extend( {}, init, defaults );1432
1433if ( init !== false ) {1434new ColReorder( settings, opts );1435}1436}1437} );1438
1439
1440// API augmentation
1441$.fn.dataTable.Api.register( 'colReorder.reset()', function () {1442return this.iterator( 'table', function ( ctx ) {1443ctx._colReorder.fnReset();1444} );1445} );1446
1447$.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) {1448if ( set ) {1449return this.iterator( 'table', function ( ctx ) {1450ctx._colReorder.fnOrder( set, original );1451} );1452}1453
1454return this.context.length ?1455this.context[0]._colReorder.fnOrder() :1456null;1457} );1458
1459$.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) {1460return this.context.length && this.context[0]._colReorder ?1461this.context[0]._colReorder.fnTranspose( idx, dir ) :1462idx;1463} );1464
1465$.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) {1466if (this.context.length) {1467this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows );1468this.context[0]._colReorder._fnSetColumnIndexes();1469}1470return this;1471} );1472
1473$.fn.dataTable.Api.register( 'colReorder.enable()', function( flag ) {1474return this.iterator( 'table', function ( ctx ) {1475if ( ctx._colReorder ) {1476ctx._colReorder.fnEnable( flag );1477}1478} );1479} );1480
1481$.fn.dataTable.Api.register( 'colReorder.disable()', function() {1482return this.iterator( 'table', function ( ctx ) {1483if ( ctx._colReorder ) {1484ctx._colReorder.fnDisable();1485}1486} );1487} );1488
1489
1490return ColReorder;1491}));1492