| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820 | /*! RowReorder 1.2.7 * 2015-2020 SpryMedia Ltd - datatables.net/license *//** * @summary     RowReorder * @description Row reordering extension for DataTables * @version     1.2.7 * @file        dataTables.rowReorder.js * @author      SpryMedia Ltd (www.sprymedia.co.uk) * @contact     www.sprymedia.co.uk/contact * @copyright   Copyright 2015-2020 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;/** * RowReorder provides the ability in DataTables to click and drag rows to * reorder them. When a row is dropped the data for the rows effected will be * updated to reflect the change. Normally this data point should also be the * column being sorted upon in the DataTable but this does not need to be the * case. RowReorder implements a "data swap" method - so the rows being * reordered take the value of the data point from the row that used to occupy * the row's new position. * * Initialisation is done by either: * * * `rowReorder` parameter in the DataTable initialisation object * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables *   initialisation. *  *  @class *  @param {object} settings DataTables settings object for the host table *  @param {object} [opts] Configuration options *  @requires jQuery 1.7+ *  @requires DataTables 1.10.7+ */var RowReorder = function ( dt, opts ) {	// Sanity check that we are using DataTables 1.10 or newer	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {		throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';	}	// User and defaults configuration object	this.c = $.extend( true, {},		DataTable.defaults.rowReorder,		RowReorder.defaults,		opts	);	// Internal settings	this.s = {		/** @type {integer} Scroll body top cache */		bodyTop: null,		/** @type {DataTable.Api} DataTables' API instance */		dt: new DataTable.Api( dt ),		/** @type {function} Data fetch function */		getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),		/** @type {array} Pixel positions for row insertion calculation */		middles: null,		/** @type {Object} Cached dimension information for use in the mouse move event handler */		scroll: {},		/** @type {integer} Interval object used for smooth scrolling */		scrollInterval: null,		/** @type {function} Data set function */		setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),		/** @type {Object} Mouse down information */		start: {			top: 0,			left: 0,			offsetTop: 0,			offsetLeft: 0,			nodes: []		},		/** @type {integer} Window height cached value */		windowHeight: 0,		/** @type {integer} Document outer height cached value */		documentOuterHeight: 0,		/** @type {integer} DOM clone outer height cached value */		domCloneOuterHeight: 0	};	// DOM items	this.dom = {		/** @type {jQuery} Cloned row being moved around */		clone: null,		/** @type {jQuery} DataTables scrolling container */		dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())	};	// Check if row reorder has already been initialised on this table	var settings = this.s.dt.settings()[0];	var exisiting = settings.rowreorder;	if ( exisiting ) {		return exisiting;	}	if ( !this.dom.dtScroll.length ) {		this.dom.dtScroll = $(this.s.dt.table().container(), 'tbody')	}	settings.rowreorder = this;	this._constructor();};$.extend( RowReorder.prototype, {	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *	 * Constructor	 */	/**	 * Initialise the RowReorder instance	 *	 * @private	 */	_constructor: function ()	{		var that = this;		var dt = this.s.dt;		var table = $( dt.table().node() );		// Need to be able to calculate the row positions relative to the table		if ( table.css('position') === 'static' ) {			table.css( 'position', 'relative' );		}		// listen for mouse down on the target column - we have to implement		// this rather than using HTML5 drag and drop as drag and drop doesn't		// appear to work on table rows at this time. Also mobile browsers are		// not supported.		// Use `table().container()` rather than just the table node for IE8 -		// otherwise it only works once...		$(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {			if ( ! that.c.enable ) {				return;			}			// Ignore excluded children of the selector			if ( $(e.target).is(that.c.excludedChildren) ) {				return true;			}			var tr = $(this).closest('tr');			var row = dt.row( tr );			// Double check that it is a DataTable row			if ( row.any() ) {				that._emitEvent( 'pre-row-reorder', {					node: row.node(),					index: row.index()				} );				that._mouseDown( e, tr );				return false;			}		} );		dt.on( 'destroy.rowReorder', function () {			$(dt.table().container()).off( '.rowReorder' );			dt.off( '.rowReorder' );		} );	},	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *	 * Private methods	 */		/**	 * Cache the measurements that RowReorder needs in the mouse move handler	 * to attempt to speed things up, rather than reading from the DOM.	 *	 * @private	 */	_cachePositions: function ()	{		var dt = this.s.dt;		// Frustratingly, if we add `position:relative` to the tbody, the		// position is still relatively to the parent. So we need to adjust		// for that		var headerHeight = $( dt.table().node() ).find('thead').outerHeight();		// Need to pass the nodes through jQuery to get them in document order,		// not what DataTables thinks it is, since we have been altering the		// order		var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );		var middles = $.map( nodes, function ( node, i ) {			var top = $(node).position().top - headerHeight;			return (top + top + $(node).outerHeight() ) / 2;		} );		this.s.middles = middles;		this.s.bodyTop = $( dt.table().body() ).offset().top;		this.s.windowHeight = $(window).height();		this.s.documentOuterHeight = $(document).outerHeight();	},	/**	 * Clone a row so it can be floated around the screen	 *	 * @param  {jQuery} target Node to be cloned	 * @private	 */	_clone: function ( target )	{		var dt = this.s.dt;		var clone = $( dt.table().node().cloneNode(false) )			.addClass( 'dt-rowReorder-float' )			.append('<tbody/>')			.append( target.clone( false ) );		// Match the table and column widths - read all sizes before setting		// to reduce reflows		var tableWidth = target.outerWidth();		var tableHeight = target.outerHeight();		var sizes = target.children().map( function () {			return $(this).width();		} );		clone			.width( tableWidth )			.height( tableHeight )			.find('tr').children().each( function (i) {				this.style.width = sizes[i]+'px';			} );		// Insert into the document to have it floating around		clone.appendTo( 'body' );		this.dom.clone = clone;		this.s.domCloneOuterHeight = clone.outerHeight();	},	/**	 * Update the cloned item's position in the document	 *	 * @param  {object} e Event giving the mouse's position	 * @private	 */	_clonePosition: function ( e )	{		var start = this.s.start;		var topDiff = this._eventToPage( e, 'Y' ) - start.top;		var leftDiff = this._eventToPage( e, 'X' ) - start.left;		var snap = this.c.snapX;		var left;		var top = topDiff + start.offsetTop;		if ( snap === true ) {			left = start.offsetLeft;		}		else if ( typeof snap === 'number' ) {			left = start.offsetLeft + snap;		}		else {			left = leftDiff + start.offsetLeft;		}		if(top < 0) {			top = 0		}		else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {			top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;		}		this.dom.clone.css( {			top: top,			left: left		} );	},	/**	 * Emit an event on the DataTable for listeners	 *	 * @param  {string} name Event name	 * @param  {array} args Event arguments	 * @private	 */	_emitEvent: function ( name, args )	{		this.s.dt.iterator( 'table', function ( ctx, i ) {			$(ctx.nTable).triggerHandler( name+'.dt', args );		} );	},	/**	 * Get pageX/Y position from an event, regardless of if it is a mouse or	 * touch event.	 *	 * @param  {object} e Event	 * @param  {string} pos X or Y (must be a capital)	 * @private	 */	_eventToPage: function ( e, pos )	{		if ( e.type.indexOf( 'touch' ) !== -1 ) {			return e.originalEvent.touches[0][ 'page'+pos ];		}		return e[ 'page'+pos ];	},	/**	 * Mouse down event handler. Read initial positions and add event handlers	 * for the move.	 *	 * @param  {object} e      Mouse event	 * @param  {jQuery} target TR element that is to be moved	 * @private	 */	_mouseDown: function ( e, target )	{		var that = this;		var dt = this.s.dt;		var start = this.s.start;		var offset = target.offset();		start.top = this._eventToPage( e, 'Y' );		start.left = this._eventToPage( e, 'X' );		start.offsetTop = offset.top;		start.offsetLeft = offset.left;		start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );		this._cachePositions();		this._clone( target );		this._clonePosition( e );		this.dom.target = target;		target.addClass( 'dt-rowReorder-moving' );		$( document )			.on( 'mouseup.rowReorder touchend.rowReorder', function (e) {				that._mouseUp(e);			} )			.on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {				that._mouseMove(e);			} );		// Check if window is x-scrolling - if not, disable it for the duration		// of the drag		if ( $(window).width() === $(document).width() ) {			$(document.body).addClass( 'dt-rowReorder-noOverflow' );		}		// Cache scrolling information so mouse move doesn't need to read.		// This assumes that the window and DT scroller will not change size		// during an row drag, which I think is a fair assumption		var scrollWrapper = this.dom.dtScroll;		this.s.scroll = {			windowHeight: $(window).height(),			windowWidth:  $(window).width(),			dtTop:        scrollWrapper.length ? scrollWrapper.offset().top : null,			dtLeft:       scrollWrapper.length ? scrollWrapper.offset().left : null,			dtHeight:     scrollWrapper.length ? scrollWrapper.outerHeight() : null,			dtWidth:      scrollWrapper.length ? scrollWrapper.outerWidth() : null		};	},	/**	 * Mouse move event handler - move the cloned row and shuffle the table's	 * rows if required.	 *	 * @param  {object} e Mouse event	 * @private	 */	_mouseMove: function ( e )	{		this._clonePosition( e );		// Transform the mouse position into a position in the table's body		var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;		var middles = this.s.middles;		var insertPoint = null;		var dt = this.s.dt;		// Determine where the row should be inserted based on the mouse		// position		for ( var i=0, ien=middles.length ; i<ien ; i++ ) {			if ( bodyY < middles[i] ) {				insertPoint = i;				break;			}		}		if ( insertPoint === null ) {			insertPoint = middles.length;		}		// Perform the DOM shuffle if it has changed from last time		if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {			var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );			if ( insertPoint > this.s.lastInsert ) {				this.dom.target.insertAfter( nodes[ insertPoint-1 ] );			}			else {				this.dom.target.insertBefore( nodes[ insertPoint ] );			}			this._cachePositions();			this.s.lastInsert = insertPoint;		}		this._shiftScroll( e );	},	/**	 * Mouse up event handler - release the event handlers and perform the	 * table updates	 *	 * @param  {object} e Mouse event	 * @private	 */	_mouseUp: function ( e )	{		var that = this;		var dt = this.s.dt;		var i, ien;		var dataSrc = this.c.dataSrc;		this.dom.clone.remove();		this.dom.clone = null;		this.dom.target.removeClass( 'dt-rowReorder-moving' );		//this.dom.target = null;		$(document).off( '.rowReorder' );		$(document.body).removeClass( 'dt-rowReorder-noOverflow' );		clearInterval( this.s.scrollInterval );		this.s.scrollInterval = null;		// Calculate the difference		var startNodes = this.s.start.nodes;		var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );		var idDiff = {};		var fullDiff = [];		var diffNodes = [];		var getDataFn = this.s.getDataFn;		var setDataFn = this.s.setDataFn;		for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {			if ( startNodes[i] !== endNodes[i] ) {				var id = dt.row( endNodes[i] ).id();				var endRowData = dt.row( endNodes[i] ).data();				var startRowData = dt.row( startNodes[i] ).data();				if ( id ) {					idDiff[ id ] = getDataFn( startRowData );				}				fullDiff.push( {					node: endNodes[i],					oldData: getDataFn( endRowData ),					newData: getDataFn( startRowData ),					newPosition: i,					oldPosition: $.inArray( endNodes[i], startNodes )				} );				diffNodes.push( endNodes[i] );			}		}				// Create event args		var eventArgs = [ fullDiff, {			dataSrc:       dataSrc,			nodes:         diffNodes,			values:        idDiff,			triggerRow:    dt.row( this.dom.target ),			originalEvent: e		} ];				// Emit event		this._emitEvent( 'row-reorder', eventArgs );		var update = function () {			if ( that.c.update ) {				for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {					var row = dt.row( fullDiff[i].node );					var rowData = row.data();					setDataFn( rowData, fullDiff[i].newData );					// Invalidate the cell that has the same data source as the dataSrc					dt.columns().every( function () {						if ( this.dataSrc() === dataSrc ) {							dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );						}					} );				}				// Trigger row reordered event				that._emitEvent( 'row-reordered', eventArgs );				dt.draw( false );			}		};		// Editor interface		if ( this.c.editor ) {			// Disable user interaction while Editor is submitting			this.c.enable = false;			this.c.editor				.edit(					diffNodes,					false,					$.extend( {submit: 'changed'}, this.c.formOptions )				)				.multiSet( dataSrc, idDiff )				.one( 'preSubmitCancelled.rowReorder', function () {					that.c.enable = true;					that.c.editor.off( '.rowReorder' );					dt.draw( false );				} )				.one( 'submitUnsuccessful.rowReorder', function () {					dt.draw( false );				} )				.one( 'submitSuccess.rowReorder', function () {					update();				} )				.one( 'submitComplete', function () {					that.c.enable = true;					that.c.editor.off( '.rowReorder' );				} )				.submit();		}		else {			update();		}	},	/**	 * Move the window and DataTables scrolling during a drag to scroll new	 * content into view.	 *	 * This matches the `_shiftScroll` method used in AutoFill, but only	 * horizontal scrolling is considered here.	 *	 * @param  {object} e Mouse move event object	 * @private	 */	_shiftScroll: function ( e )	{		var that = this;		var dt = this.s.dt;		var scroll = this.s.scroll;		var runInterval = false;		var scrollSpeed = 5;		var buffer = 65;		var			windowY = e.pageY - document.body.scrollTop,			windowVert,			dtVert;		// Window calculations - based on the mouse position in the window,		// regardless of scrolling		if ( windowY < $(window).scrollTop() + buffer ) {			windowVert = scrollSpeed * -1;		}		else if ( windowY > scroll.windowHeight + $(window).scrollTop() - buffer ) {			windowVert = scrollSpeed;		}		// DataTables scrolling calculations - based on the table's position in		// the document and the mouse position on the page		if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {			dtVert = scrollSpeed * -1;		}		else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {			dtVert = scrollSpeed;		}		// This is where it gets interesting. We want to continue scrolling		// without requiring a mouse move, so we need an interval to be		// triggered. The interval should continue until it is no longer needed,		// but it must also use the latest scroll commands (for example consider		// that the mouse might move from scrolling up to scrolling left, all		// with the same interval running. We use the `scroll` object to "pass"		// this information to the interval. Can't use local variables as they		// wouldn't be the ones that are used by an already existing interval!		if ( windowVert || dtVert ) {			scroll.windowVert = windowVert;			scroll.dtVert = dtVert;			runInterval = true;		}		else if ( this.s.scrollInterval ) {			// Don't need to scroll - remove any existing timer			clearInterval( this.s.scrollInterval );			this.s.scrollInterval = null;		}		// If we need to run the interval to scroll and there is no existing		// interval (if there is an existing one, it will continue to run)		if ( ! this.s.scrollInterval && runInterval ) {			this.s.scrollInterval = setInterval( function () {				// Don't need to worry about setting scroll <0 or beyond the				// scroll bound as the browser will just reject that.				if ( scroll.windowVert ) {					var top = $(document).scrollTop();					$(document).scrollTop(top + scroll.windowVert);					if ( top !== $(document).scrollTop() ) {						var move = parseFloat(that.dom.clone.css("top"));						that.dom.clone.css("top", move + scroll.windowVert);										}				}				// DataTables scrolling				if ( scroll.dtVert ) {					var scroller = that.dom.dtScroll[0];					if ( scroll.dtVert ) {						scroller.scrollTop += scroll.dtVert;					}				}			}, 20 );		}	}} );/** * RowReorder default settings for initialisation * * @namespace * @name RowReorder.defaults * @static */RowReorder.defaults = {	/**	 * Data point in the host row's data source object for where to get and set	 * the data to reorder. This will normally also be the sorting column.	 *	 * @type {Number}	 */	dataSrc: 0,	/**	 * Editor instance that will be used to perform the update	 *	 * @type {DataTable.Editor}	 */	editor: null,	/**	 * Enable / disable RowReorder's user interaction	 * @type {Boolean}	 */	enable: true,	/**	 * Form options to pass to Editor when submitting a change in the row order.	 * See the Editor `from-options` object for details of the options	 * available.	 * @type {Object}	 */	formOptions: {},	/**	 * Drag handle selector. This defines the element that when dragged will	 * reorder a row.	 *	 * @type {String}	 */	selector: 'td:first-child',	/**	 * Optionally lock the dragged row's x-position. This can be `true` to	 * fix the position match the host table's, `false` to allow free movement	 * of the row, or a number to define an offset from the host table.	 *	 * @type {Boolean|number}	 */	snapX: false,	/**	 * Update the table's data on drop	 *	 * @type {Boolean}	 */	update: true,	/**	 * Selector for children of the drag handle selector that mouseDown events	 * will be passed through to and drag will not activate	 *	 * @type {String}	 */	excludedChildren: 'a'};/* * API */var Api = $.fn.dataTable.Api;// Doesn't do anything - work around for a bug in DT... Not documentedApi.register( 'rowReorder()', function () {	return this;} );Api.register( 'rowReorder.enable()', function ( toggle ) {	if ( toggle === undefined ) {		toggle = true;	}	return this.iterator( 'table', function ( ctx ) {		if ( ctx.rowreorder ) {			ctx.rowreorder.c.enable = toggle;		}	} );} );Api.register( 'rowReorder.disable()', function () {	return this.iterator( 'table', function ( ctx ) {		if ( ctx.rowreorder ) {			ctx.rowreorder.c.enable = false;		}	} );} );/** * Version information * * @name RowReorder.version * @static */RowReorder.version = '1.2.6';$.fn.dataTable.RowReorder = RowReorder;$.fn.DataTable.RowReorder = RowReorder;// Attach a listener to the document which listens for DataTables initialisation// events so we can automatically initialise$(document).on( 'init.dt.dtr', function (e, settings, json) {	if ( e.namespace !== 'dt' ) {		return;	}	var init = settings.oInit.rowReorder;	var defaults = DataTable.defaults.rowReorder;	if ( init || defaults ) {		var opts = $.extend( {}, init, defaults );		if ( init !== false ) {			new RowReorder( settings, opts  );		}	}} );return RowReorder;}));
 |