/*! ColReorder 1.5.2 * ©2010-2019 SpryMedia Ltd - datatables.net/license */ /** * @summary ColReorder * @description Provide the ability to reorder columns in a DataTable * @version 1.5.2 * @file dataTables.colReorder.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2010-2019 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ (function( factory ){ if ( typeof define === 'function' && define.amd ) { // AMD define( ['jquery', 'datatables.net'], function ( $ ) { return factory( $, window, document ); } ); } else if ( typeof exports === 'object' ) { // CommonJS module.exports = function (root, $) { if ( ! root ) { root = window; } if ( ! $ || ! $.fn.dataTable ) { $ = require('datatables.net')(root, $).$; } return factory( $, root, root.document ); }; } else { // Browser factory( jQuery, window, document ); } }(function( $, window, document, undefined ) { 'use strict'; var DataTable = $.fn.dataTable; /** * Switch the key value pairing of an index array to be value key (i.e. the old value is now the * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. * @method fnInvertKeyValues * @param array aIn Array to switch around * @returns array */ function fnInvertKeyValues( aIn ) { var aRet=[]; for ( var i=0, iLen=aIn.length ; i= iCols ) { this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom ); return; } if ( iTo < 0 || iTo >= iCols ) { this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo ); return; } /* * Calculate the new column array index, so we have a mapping between the old and new */ var aiMapping = []; for ( i=0, iLen=iCols ; i this.s.fixed-1 && i < iLen - this.s.fixedRight ) { this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh ); } /* Mark the original column order for later reference */ this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i; } /* State saving */ this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) { that._fnStateSave.call( that, oData ); }, "ColReorder_State" ); /* An initial column order has been specified */ var aiOrder = null; if ( this.s.init.aiOrder ) { aiOrder = this.s.init.aiOrder.slice(); } /* State loading, overrides the column order given */ if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' && this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length ) { aiOrder = this.s.dt.oLoadedState.ColReorder; } /* If we have an order to apply - do so */ if ( aiOrder ) { /* We might be called during or after the DataTables initialisation. If before, then we need * to wait until the draw is done, if after, then do what we need to do right away */ if ( !that.s.dt._bInitComplete ) { var bDone = false; $(table).on( 'draw.dt.colReorder', function () { if ( !that.s.dt._bInitComplete && !bDone ) { bDone = true; var resort = fnInvertKeyValues( aiOrder ); that._fnOrderColumns.call( that, resort ); } } ); } else { var resort = fnInvertKeyValues( aiOrder ); that._fnOrderColumns.call( that, resort ); } } else { this._fnSetColumnIndexes(); } // Destroy clean up $(table).on( 'destroy.dt.colReorder', function () { $(table).off( 'destroy.dt.colReorder draw.dt.colReorder' ); $.each( that.s.dt.aoColumns, function (i, column) { $(column.nTh).off('.ColReorder'); $(column.nTh).removeAttr('data-column-index'); } ); that.s.dt._colReorder = null; that.s = null; } ); }, /** * Set the column order from an array * @method _fnOrderColumns * @param array a An array of integers which dictate the column order that should be applied * @returns void * @private */ "_fnOrderColumns": function ( a ) { var changed = false; if ( a.length != this.s.dt.aoColumns.length ) { this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+ "match known number of columns. Skipping." ); return; } for ( var i=0, iLen=a.length ; i= 0) { i--; if (i <= 0) { return null; } if (that.s.aoTargets[i+1].x !== that.s.aoTargets[i].x) { return that.s.aoTargets[i]; } } }; var firstNotHidden = function () { for (var i=0 ; i0 ; i--) { if (that.s.aoTargets[i].x !== that.s.aoTargets[i-1].x) { return that.s.aoTargets[i]; } } }; for (var i = 1; i < this.s.aoTargets.length; i++) { var prevTarget = targetsPrev(i); if (! prevTarget) { prevTarget = firstNotHidden(); } var prevTargetMiddle = prevTarget.x + (this.s.aoTargets[i].x - prevTarget.x) / 2; if (this._fnIsLtr()) { if (cursorXPosiotion < prevTargetMiddle ) { target = prevTarget; break; } } else { if (cursorXPosiotion > prevTargetMiddle) { target = prevTarget; break; } } } if (target) { this.dom.pointer.css('left', target.x); this.s.mouse.toIndex = target.to; } else { // The insert element wasn't positioned in the array (less than // operator), so we put it at the end this.dom.pointer.css( 'left', lastNotHidden().x ); this.s.mouse.toIndex = lastNotHidden().to; } // Perform reordering if realtime updating is on and the column has moved if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) { this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex ); this.s.mouse.fromIndex = this.s.mouse.toIndex; // Not great for performance, but required to keep everything in alignment if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) { this.s.dt.oInstance.fnAdjustColumnSizing( false ); } this._fnRegions(); } }, /** * Finish off the mouse drag and insert the column where needed * @method _fnMouseUp * @param event e Mouse event * @returns void * @private */ "_fnMouseUp": function ( e ) { var that = this; $(document).off( '.ColReorder' ); if ( this.dom.drag !== null ) { /* Remove the guide elements */ this.dom.drag.remove(); this.dom.pointer.remove(); this.dom.drag = null; this.dom.pointer = null; /* Actually do the reorder */ this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true ); this._fnSetColumnIndexes(); /* When scrolling we need to recalculate the column sizes to allow for the shift */ if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" ) { this.s.dt.oInstance.fnAdjustColumnSizing( false ); } /* Save the state */ this.s.dt.oInstance.oApi._fnSaveState( this.s.dt ); if ( this.s.reorderCallback !== null ) { this.s.reorderCallback.call( this ); } } }, /** * Calculate a cached array with the points of the column inserts, and the * 'to' points * @method _fnRegions * @returns void * @private */ "_fnRegions": function () { var aoColumns = this.s.dt.aoColumns; var isLTR = this._fnIsLtr(); this.s.aoTargets.splice(0, this.s.aoTargets.length); var lastBound = $(this.s.dt.nTable).offset().left; var aoColumnBounds = []; $.each(aoColumns, function (i, column) { if (column.bVisible && column.nTh.style.display !== 'none') { var nth = $(column.nTh); var bound = nth.offset().left; if (isLTR) { bound += nth.outerWidth(); } aoColumnBounds.push({ index: i, bound: bound }); lastBound = bound; } else { aoColumnBounds.push({ index: i, bound: lastBound }); } }); var firstColumn = aoColumnBounds[0]; var firstColumnWidth = $(aoColumns[firstColumn.index].nTh).outerWidth(); this.s.aoTargets.push({ to: 0, x: firstColumn.bound - firstColumnWidth }); for (var i = 0; i < aoColumnBounds.length; i++) { var columnBound = aoColumnBounds[i]; var iToPoint = columnBound.index; /* For the column / header in question, we want it's position to remain the same if the * position is just to it's immediate left or right, so we only increment the counter for * other columns */ if (columnBound.index < this.s.mouse.fromIndex) { iToPoint++; } this.s.aoTargets.push({ to: iToPoint, x: columnBound.bound }); } /* Disallow columns for being reordered by drag and drop, counting right to left */ if ( this.s.fixedRight !== 0 ) { this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight ); } /* Disallow columns for being reordered by drag and drop, counting left to right */ if ( this.s.fixed !== 0 ) { this.s.aoTargets.splice( 0, this.s.fixed ); } }, /** * Copy the TH element that is being drags so the user has the idea that they are actually * moving it around the page. * @method _fnCreateDragNode * @returns void * @private */ "_fnCreateDragNode": function () { var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== ""; var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh; var origTr = origCell.parentNode; var origThead = origTr.parentNode; var origTable = origThead.parentNode; var cloneCell = $(origCell).clone(); // This is a slightly odd combination of jQuery and DOM, but it is the // fastest and least resource intensive way I could think of cloning // the table with just a single header cell in it. this.dom.drag = $(origTable.cloneNode(false)) .addClass( 'DTCR_clonedTable' ) .append( $(origThead.cloneNode(false)).append( $(origTr.cloneNode(false)).append( cloneCell[0] ) ) ) .css( { position: 'absolute', top: 0, left: 0, width: $(origCell).outerWidth(), height: $(origCell).outerHeight() } ) .appendTo( 'body' ); this.dom.pointer = $('
') .addClass( 'DTCR_pointer' ) .css( { position: 'absolute', top: scrolling ? $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top : $(this.s.dt.nTable).offset().top, height : scrolling ? $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() : $(this.s.dt.nTable).height() } ) .appendTo( 'body' ); }, /** * Add a data attribute to the column headers, so we know the index of * the row to be reordered. This allows fast detection of the index, and * for this plug-in to work with FixedHeader which clones the nodes. * @private */ "_fnSetColumnIndexes": function () { $.each( this.s.dt.aoColumns, function (i, column) { $(column.nTh).attr('data-column-index', i); } ); }, /** * Get cursor position regardless of mouse or touch input * @param {Event} e jQuery Event * @param {string} prop Property to get * @return {number} Value */ _fnCursorPosition: function ( e, prop ) { if ( e.type.indexOf('touch') !== -1 ) { return e.originalEvent.touches[0][ prop ]; } return e[ prop ]; }, _fnIsLtr: function () { return $(this.s.dt.nTable).css('direction') !== "rtl"; } } ); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Static parameters * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * ColReorder default settings for initialisation * @namespace * @static */ ColReorder.defaults = { /** * Predefined ordering for the columns that will be applied automatically * on initialisation. If not specified then the order that the columns are * found to be in the HTML is the order used. * @type array * @default null * @static */ aiOrder: null, /** * ColReorder enable on initialisation * @type boolean * @default true * @static */ bEnable: true, /** * Redraw the table's column ordering as the end user draws the column * (`true`) or wait until the mouse is released (`false` - default). Note * that this will perform a redraw on each reordering, which involves an * Ajax request each time if you are using server-side processing in * DataTables. * @type boolean * @default false * @static */ bRealtime: true, /** * Indicate how many columns should be fixed in position (counting from the * left). This will typically be 1 if used, but can be as high as you like. * @type int * @default 0 * @static */ iFixedColumnsLeft: 0, /** * As `iFixedColumnsRight` but counting from the right. * @type int * @default 0 * @static */ iFixedColumnsRight: 0, /** * Callback function that is fired when columns are reordered. The `column- * reorder` event is preferred over this callback * @type function():void * @default null * @static */ fnReorderCallback: null }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constants * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * ColReorder version * @constant version * @type String * @default As code */ ColReorder.version = "1.5.2"; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DataTables interfaces * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Expose $.fn.dataTable.ColReorder = ColReorder; $.fn.DataTable.ColReorder = ColReorder; // Register a new feature with DataTables if ( typeof $.fn.dataTable == "function" && typeof $.fn.dataTableExt.fnVersionCheck == "function" && $.fn.dataTableExt.fnVersionCheck('1.10.8') ) { $.fn.dataTableExt.aoFeatures.push( { "fnInit": function( settings ) { var table = settings.oInstance; if ( ! settings._colReorder ) { var dtInit = settings.oInit; var opts = dtInit.colReorder || dtInit.oColReorder || {}; new ColReorder( settings, opts ); } else { table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" ); } return null; /* No node for DataTables to insert */ }, "cFeature": "R", "sFeature": "ColReorder" } ); } else { alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download"); } // Attach a listener to the document which listens for DataTables initialisation // events so we can automatically initialise $(document).on( 'preInit.dt.colReorder', function (e, settings) { if ( e.namespace !== 'dt' ) { return; } var init = settings.oInit.colReorder; var defaults = DataTable.defaults.colReorder; if ( init || defaults ) { var opts = $.extend( {}, init, defaults ); if ( init !== false ) { new ColReorder( settings, opts ); } } } ); // API augmentation $.fn.dataTable.Api.register( 'colReorder.reset()', function () { return this.iterator( 'table', function ( ctx ) { ctx._colReorder.fnReset(); } ); } ); $.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) { if ( set ) { return this.iterator( 'table', function ( ctx ) { ctx._colReorder.fnOrder( set, original ); } ); } return this.context.length ? this.context[0]._colReorder.fnOrder() : null; } ); $.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) { return this.context.length && this.context[0]._colReorder ? this.context[0]._colReorder.fnTranspose( idx, dir ) : idx; } ); $.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) { if (this.context.length) { this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows ); this.context[0]._colReorder._fnSetColumnIndexes(); } return this; } ); $.fn.dataTable.Api.register( 'colReorder.enable()', function( flag ) { return this.iterator( 'table', function ( ctx ) { if ( ctx._colReorder ) { ctx._colReorder.fnEnable( flag ); } } ); } ); $.fn.dataTable.Api.register( 'colReorder.disable()', function() { return this.iterator( 'table', function ( ctx ) { if ( ctx._colReorder ) { ctx._colReorder.fnDisable(); } } ); } ); return ColReorder; }));