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