Blame | Letzte Änderung | Log anzeigen | RSS feed
/*! RowReorder 1.0.0* 2015 SpryMedia Ltd - datatables.net/license*//*** @summary RowReorder* @description Row reordering extension for DataTables* @version 1.0.0* @file dataTables.rowReorder.js* @author SpryMedia Ltd (www.sprymedia.co.uk)* @contact www.sprymedia.co.uk/contact* @copyright Copyright 2015 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(window, document, undefined) {var factory = function( $, DataTable ) {"use strict";/*** 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 newerif ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';}// User and defaults configuration objectthis.c = $.extend( true, {},DataTable.defaults.rowReorder,RowReorder.defaults,opts);// Internal settingsthis.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 {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};// DOM itemsthis.dom = {/** @type {jQuery} Cloned row being moved around */clone: null};// Check if row reorder has already been initialised on this tablevar settings = this.s.dt.settings()[0];var exisiting = settings.rowreorder;if ( exisiting ) {return exisiting;}settings.rowreorder = this;this._constructor();};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 tableif ( 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$( table ).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {var tr = $(this).closest('tr');// Double check that it is a DataTable rowif ( dt.row( tr ).any() ) {that._mouseDown( e, tr );return false;}} );dt.on( 'destroy', function () {table.off( 'mousedown.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 thatvar 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// ordervar nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );var tops = $.map( nodes, function ( node, i ) {return $(node).position().top - headerHeight;} );var middles = $.map( tops, function ( top, i ) {return tops.length < i-1 ?(top + tops[i+1]) / 2 :(top + top + $( dt.row( ':last-child' ).node() ).outerHeight() ) / 2;} );this.s.middles = middles;this.s.bodyTop = $( dt.table().body() ).offset().top;this.s.windowHeight = $(window).height();},/*** 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 reflowsvar 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 aroundclone.appendTo( 'body' );this.dom.clone = clone;},/*** 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;if ( snap === true ) {left = start.offsetLeft;}else if ( typeof snap === 'number' ) {left = start.offsetLeft + snap;}else {left = leftDiff + start.offsetLeft;}this.dom.clone.css( {top: topDiff + start.offsetTop,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 dragif ( $(window).width() === $(document).width() ) {$(document.body).addClass( 'dt-rowReorder-noOverflow' );}},/*** 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 bodyvar bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;var middles = this.s.middles;var insertPoint = null;var dt = this.s.dt;var body = dt.table().body();// Determine where the row should be inserted based on the mouse// positionfor ( 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 timeif ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {if ( insertPoint === 0 ) {this.dom.target.prependTo( body );}else {var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );if ( insertPoint > this.s.lastInsert ) {this.dom.target.before( nodes[ insertPoint-1 ] );}else {this.dom.target.after( nodes[ insertPoint ] );}}this._cachePositions();this.s.lastInsert = insertPoint;}// scroll window up and down when reaching the edgesvar windowY = this._eventToPage( e, 'Y' ) - document.body.scrollTop;var scrollInterval = this.s.scrollInterval;if ( windowY < 65 ) {if ( ! scrollInterval ) {this.s.scrollInterval = setInterval( function () {document.body.scrollTop -= 5;}, 15 );}}else if ( this.s.windowHeight - windowY < 65 ) {if ( ! scrollInterval ) {this.s.scrollInterval = setInterval( function () {document.body.scrollTop += 5;}, 15 );}}else {clearInterval( scrollInterval );this.s.scrollInterval = null;}},/*** Mouse up event handler - release the event handlers and perform the* table updates** @param {object} e Mouse event* @private*/_mouseUp: function ( e ){var dt = this.s.dt;var i, ien;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' );// Calculate the differencevar 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] );}}// Emit eventthis._emitEvent( 'row-reorder', [ fullDiff, {dataSrc: this.c.dataSrc,nodes: diffNodes,values: idDiff} ] );// Editor interfaceif ( this.c.editor ) {this.c.editor.edit( diffNodes, false, {submit: 'changed'} ).multiSet( this.c.dataSrc, idDiff ).submit();}// Do update if requiredif ( this.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 );row.invalidate( 'data' );}dt.draw( false );}}};/*** 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,/*** 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};/*** Version information** @name RowReorder.version* @static*/RowReorder.version = '1.0.0';$.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;}; // /factory// Define as an AMD module if possibleif ( typeof define === 'function' && define.amd ) {define( ['jquery', 'datatables'], factory );}else if ( typeof exports === 'object' ) {// Node/CommonJSfactory( require('jquery'), require('datatables') );}else if ( jQuery && !jQuery.fn.dataTable.RowReorder ) {// Otherwise simply initialise as normal, stopping multiple evaluationfactory( jQuery, jQuery.fn.dataTable );}})(window, document);