META-INF.resources.bower_components.datatables.net-rowreorder.js.dataTables.rowReorder.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jwebmp-data-tables Show documentation
Show all versions of jwebmp-data-tables Show documentation
The JWebSwing implementation for Data Tables
The newest version!
/*! RowReorder 1.2.5
* 2015-2018 SpryMedia Ltd - datatables.net/license
*/
/**
* @summary RowReorder
* @description Row reordering extension for DataTables
* @version 1.2.5
* @file dataTables.rowReorder.js
* @author SpryMedia Ltd (www.sprymedia.co.uk)
* @contact www.sprymedia.co.uk/contact
* @copyright Copyright 2015-2018 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;
}
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 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();
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('')
.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;
var body = dt.table().body();
// 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) {
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.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)
}];
// 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 < buffer) {
windowVert = scrollSpeed * -1;
}
else if (windowY > scroll.windowHeight - 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) {
document.body.scrollTop += 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 documented
Api.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.5';
$.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;
}));
© 2015 - 2025 Weber Informatics LLC | Privacy Policy