PageRenderTime 28ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/files/jquery.datatables/1.10.9/plugins/colreorder/js/dataTables.colReorder.js

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