|
@@ -0,0 +1,598 @@
|
|
|
+/**
|
|
|
+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
|
|
|
+ * You can set up various options to control how the system will work
|
|
|
+ * Copyright (c) Denis Howlett <denish@isocra.com>
|
|
|
+ * License: MIT.
|
|
|
+ * See https://github.com/isocra/TableDnD
|
|
|
+ */
|
|
|
+!function ($, window, document, undefined) {
|
|
|
+
|
|
|
+var startEvent = 'touchstart mousedown',
|
|
|
+ moveEvent = 'touchmove mousemove',
|
|
|
+ endEvent = 'touchend mouseup';
|
|
|
+
|
|
|
+$(document).ready(function () {
|
|
|
+ function parseStyle(css) {
|
|
|
+ var objMap = {},
|
|
|
+ parts = css.match(/([^;:]+)/g) || [];
|
|
|
+ while (parts.length)
|
|
|
+ objMap[parts.shift()] = parts.shift().trim();
|
|
|
+
|
|
|
+ return objMap;
|
|
|
+ }
|
|
|
+ $('table').each(function () {
|
|
|
+ if ($(this).data('table') === 'dnd') {
|
|
|
+
|
|
|
+ $(this).tableDnD({
|
|
|
+ onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null,
|
|
|
+ onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null,
|
|
|
+ onDragClass: $(this).data('ondragclass') === undefined && "tDnD_whileDrag" || $(this).data('ondragclass'),
|
|
|
+ onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null,
|
|
|
+ onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null,
|
|
|
+ onDragStop: $(this).data('ondragstop') && new Function('table', 'row' ,$(this).data('ondragstop')),
|
|
|
+ scrollAmount: $(this).data('scrollamount') || 5,
|
|
|
+ sensitivity: $(this).data('sensitivity') || 10,
|
|
|
+ hierarchyLevel: $(this).data('hierarchylevel') || 0,
|
|
|
+ indentArtifact: $(this).data('indentartifact') || '<div class="indent"> </div>',
|
|
|
+ autoWidthAdjust: $(this).data('autowidthadjust') || true,
|
|
|
+ autoCleanRelations: $(this).data('autocleanrelations') || true,
|
|
|
+ jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t',
|
|
|
+ serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/,
|
|
|
+ serializeParamName: $(this).data('serializeparamname') || false,
|
|
|
+ dragHandle: $(this).data('draghandle') || null
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+jQuery.tableDnD = {
|
|
|
+ /** Keep hold of the current table being dragged */
|
|
|
+ currentTable: null,
|
|
|
+ /** Keep hold of the current drag object if any */
|
|
|
+ dragObject: null,
|
|
|
+ /** The current mouse offset */
|
|
|
+ mouseOffset: null,
|
|
|
+ /** Remember the old value of X and Y so that we don't do too much processing */
|
|
|
+ oldX: 0,
|
|
|
+ oldY: 0,
|
|
|
+
|
|
|
+ /** Actually build the structure */
|
|
|
+ build: function(options) {
|
|
|
+ // Set up the defaults if any
|
|
|
+
|
|
|
+ this.each(function() {
|
|
|
+ // This is bound to each matching table, set up the defaults and override with user options
|
|
|
+ this.tableDnDConfig = $.extend({
|
|
|
+ onDragStyle: null,
|
|
|
+ onDropStyle: null,
|
|
|
+ // Add in the default class for whileDragging
|
|
|
+ onDragClass: "tDnD_whileDrag",
|
|
|
+ onDrop: null,
|
|
|
+ onDragStart: null,
|
|
|
+ onDragStop: null,
|
|
|
+ scrollAmount: 5,
|
|
|
+ /** Sensitivity setting will throttle the trigger rate for movement detection */
|
|
|
+ sensitivity: 10,
|
|
|
+ /** Hierarchy level to support parent child. 0 switches this functionality off */
|
|
|
+ hierarchyLevel: 0,
|
|
|
+ /** The html artifact to prepend the first cell with as indentation */
|
|
|
+ indentArtifact: '<div class="indent"> </div>',
|
|
|
+ /** Automatically adjust width of first cell */
|
|
|
+ autoWidthAdjust: true,
|
|
|
+ /** Automatic clean-up to ensure relationship integrity */
|
|
|
+ autoCleanRelations: true,
|
|
|
+ /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */
|
|
|
+ jsonPretifySeparator: '\t',
|
|
|
+ /** The regular expression to use to trim row IDs */
|
|
|
+ serializeRegexp: /[^\-]*$/,
|
|
|
+ /** If you want to specify another parameter name instead of the table ID */
|
|
|
+ serializeParamName: false,
|
|
|
+ /** If you give the name of a class here, then only Cells with this class will be draggable */
|
|
|
+ dragHandle: null
|
|
|
+ }, options || {});
|
|
|
+
|
|
|
+ // Now make the rows draggable
|
|
|
+ $.tableDnD.makeDraggable(this);
|
|
|
+ // Prepare hierarchy support
|
|
|
+ this.tableDnDConfig.hierarchyLevel
|
|
|
+ && $.tableDnD.makeIndented(this);
|
|
|
+ });
|
|
|
+
|
|
|
+ // Don't break the chain
|
|
|
+ return this;
|
|
|
+ },
|
|
|
+ makeIndented: function (table) {
|
|
|
+ var config = table.tableDnDConfig,
|
|
|
+ rows = table.rows,
|
|
|
+ firstCell = $(rows).first().find('td:first')[0],
|
|
|
+ indentLevel = 0,
|
|
|
+ cellWidth = 0,
|
|
|
+ longestCell,
|
|
|
+ tableStyle;
|
|
|
+
|
|
|
+ if ($(table).hasClass('indtd'))
|
|
|
+ return null;
|
|
|
+
|
|
|
+ tableStyle = $(table).addClass('indtd').attr('style');
|
|
|
+ $(table).css({whiteSpace: "nowrap"});
|
|
|
+
|
|
|
+ for (var w = 0; w < rows.length; w++) {
|
|
|
+ if (cellWidth < $(rows[w]).find('td:first').text().length) {
|
|
|
+ cellWidth = $(rows[w]).find('td:first').text().length;
|
|
|
+ longestCell = w;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $(firstCell).css({width: 'auto'});
|
|
|
+ for (w = 0; w < config.hierarchyLevel; w++)
|
|
|
+ $(rows[longestCell]).find('td:first').prepend(config.indentArtifact);
|
|
|
+ firstCell && $(firstCell).css({width: firstCell.offsetWidth});
|
|
|
+ tableStyle && $(table).css(tableStyle);
|
|
|
+
|
|
|
+ for (w = 0; w < config.hierarchyLevel; w++)
|
|
|
+ $(rows[longestCell]).find('td:first').children(':first').remove();
|
|
|
+
|
|
|
+ config.hierarchyLevel
|
|
|
+ && $(rows).each(function () {
|
|
|
+ indentLevel = $(this).data('level') || 0;
|
|
|
+ indentLevel <= config.hierarchyLevel
|
|
|
+ && $(this).data('level', indentLevel)
|
|
|
+ || $(this).data('level', 0);
|
|
|
+ for (var i = 0; i < $(this).data('level'); i++)
|
|
|
+ $(this).find('td:first').prepend(config.indentArtifact);
|
|
|
+ });
|
|
|
+
|
|
|
+ return this;
|
|
|
+ },
|
|
|
+ /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
|
|
|
+ makeDraggable: function(table) {
|
|
|
+ var config = table.tableDnDConfig;
|
|
|
+
|
|
|
+ config.dragHandle
|
|
|
+ // We only need to add the event to the specified cells
|
|
|
+ && $(config.dragHandle, table).each(function() {
|
|
|
+ // The cell is bound to "this"
|
|
|
+ $(this).bind(startEvent, function(e) {
|
|
|
+ $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config);
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ })
|
|
|
+ // For backwards compatibility, we add the event to the whole row
|
|
|
+ // get all the rows as a wrapped set
|
|
|
+ || $(table.rows).each(function() {
|
|
|
+ // Iterate through each row, the row is bound to "this"
|
|
|
+ if (! $(this).hasClass("nodrag")) {
|
|
|
+ $(this).bind(startEvent, function(e) {
|
|
|
+ if (e.target.tagName === "TD") {
|
|
|
+ $.tableDnD.initialiseDrag(this, table, this, e, config);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }).css("cursor", "move"); // Store the tableDnD object
|
|
|
+ } else {
|
|
|
+ $(this).css("cursor", ""); // Remove the cursor if we don't have the nodrag class
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ currentOrder: function() {
|
|
|
+ var rows = this.currentTable.rows;
|
|
|
+ return $.map(rows, function (val) {
|
|
|
+ return ($(val).data('level') + val.id).replace(/\s/g, '');
|
|
|
+ }).join('');
|
|
|
+ },
|
|
|
+ initialiseDrag: function(dragObject, table, target, e, config) {
|
|
|
+ this.dragObject = dragObject;
|
|
|
+ this.currentTable = table;
|
|
|
+ this.mouseOffset = this.getMouseOffset(target, e);
|
|
|
+ this.originalOrder = this.currentOrder();
|
|
|
+
|
|
|
+ // Now we need to capture the mouse up and mouse move event
|
|
|
+ // We can use bind so that we don't interfere with other event handlers
|
|
|
+ $(document)
|
|
|
+ .bind(moveEvent, this.mousemove)
|
|
|
+ .bind(endEvent, this.mouseup);
|
|
|
+
|
|
|
+ // Call the onDragStart method if there is one
|
|
|
+ config.onDragStart
|
|
|
+ && config.onDragStart(table, target);
|
|
|
+ },
|
|
|
+ updateTables: function() {
|
|
|
+ this.each(function() {
|
|
|
+ // this is now bound to each matching table
|
|
|
+ if (this.tableDnDConfig)
|
|
|
+ $.tableDnD.makeDraggable(this);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** Get the mouse coordinates from the event (allowing for browser differences) */
|
|
|
+ mouseCoords: function(e) {
|
|
|
+ if (e.originalEvent.changedTouches)
|
|
|
+ return {
|
|
|
+ x: e.originalEvent.changedTouches[0].clientX,
|
|
|
+ y: e.originalEvent.changedTouches[0].clientY
|
|
|
+ };
|
|
|
+
|
|
|
+ if(e.pageX || e.pageY)
|
|
|
+ return {
|
|
|
+ x: e.pageX,
|
|
|
+ y: e.pageY
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ x: e.clientX + document.body.scrollLeft - document.body.clientLeft,
|
|
|
+ y: e.clientY + document.body.scrollTop - document.body.clientTop
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /** Given a target element and a mouse eent, get the mouse offset from that element.
|
|
|
+ To do this we need the element's position and the mouse position */
|
|
|
+ getMouseOffset: function(target, e) {
|
|
|
+ var mousePos,
|
|
|
+ docPos;
|
|
|
+
|
|
|
+ e = e || window.event;
|
|
|
+
|
|
|
+ docPos = this.getPosition(target);
|
|
|
+ mousePos = this.mouseCoords(e);
|
|
|
+
|
|
|
+ return {
|
|
|
+ x: mousePos.x - docPos.x,
|
|
|
+ y: mousePos.y - docPos.y
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /** Get the position of an element by going up the DOM tree and adding up all the offsets */
|
|
|
+ getPosition: function(element) {
|
|
|
+ var left = 0,
|
|
|
+ top = 0;
|
|
|
+
|
|
|
+ // Safari fix -- thanks to Luis Chato for this!
|
|
|
+ // Safari 2 doesn't correctly grab the offsetTop of a table row
|
|
|
+ // this is detailed here:
|
|
|
+ // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
|
|
|
+ // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
|
|
|
+ // note that firefox will return a text node as a first child, so designing a more thorough
|
|
|
+ // solution may need to take that into account, for now this seems to work in firefox, safari, ie
|
|
|
+ if (element.offsetHeight === 0)
|
|
|
+ element = element.firstChild; // a table cell
|
|
|
+
|
|
|
+ while (element.offsetParent) {
|
|
|
+ left += element.offsetLeft;
|
|
|
+ top += element.offsetTop;
|
|
|
+ element = element.offsetParent;
|
|
|
+ }
|
|
|
+
|
|
|
+ left += element.offsetLeft;
|
|
|
+ top += element.offsetTop;
|
|
|
+
|
|
|
+ return {
|
|
|
+ x: left,
|
|
|
+ y: top
|
|
|
+ };
|
|
|
+ },
|
|
|
+ autoScroll: function (mousePos) {
|
|
|
+ var config = this.currentTable.tableDnDConfig,
|
|
|
+ yOffset = window.pageYOffset,
|
|
|
+ windowHeight = window.innerHeight
|
|
|
+ ? window.innerHeight
|
|
|
+ : document.documentElement.clientHeight
|
|
|
+ ? document.documentElement.clientHeight
|
|
|
+ : document.body.clientHeight;
|
|
|
+
|
|
|
+ // Windows version
|
|
|
+ // yOffset=document.body.scrollTop;
|
|
|
+ if (document.all)
|
|
|
+ if (typeof document.compatMode !== 'undefined'
|
|
|
+ && document.compatMode !== 'BackCompat')
|
|
|
+ yOffset = document.documentElement.scrollTop;
|
|
|
+ else if (typeof document.body !== 'undefined')
|
|
|
+ yOffset = document.body.scrollTop;
|
|
|
+
|
|
|
+ mousePos.y - yOffset < config.scrollAmount
|
|
|
+ && window.scrollBy(0, - config.scrollAmount)
|
|
|
+ || windowHeight - (mousePos.y - yOffset) < config.scrollAmount
|
|
|
+ && window.scrollBy(0, config.scrollAmount);
|
|
|
+
|
|
|
+ },
|
|
|
+ moveVerticle: function (moving, currentRow) {
|
|
|
+
|
|
|
+ if (0 !== moving.vertical
|
|
|
+ // If we're over a row then move the dragged row to there so that the user sees the
|
|
|
+ // effect dynamically
|
|
|
+ && currentRow
|
|
|
+ && this.dragObject !== currentRow
|
|
|
+ && this.dragObject.parentNode === currentRow.parentNode)
|
|
|
+ 0 > moving.vertical
|
|
|
+ && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling)
|
|
|
+ || 0 < moving.vertical
|
|
|
+ && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow);
|
|
|
+
|
|
|
+ },
|
|
|
+ moveHorizontal: function (moving, currentRow) {
|
|
|
+ var config = this.currentTable.tableDnDConfig,
|
|
|
+ currentLevel;
|
|
|
+
|
|
|
+ if (!config.hierarchyLevel
|
|
|
+ || 0 === moving.horizontal
|
|
|
+ // We only care if moving left or right on the current row
|
|
|
+ || !currentRow
|
|
|
+ || this.dragObject !== currentRow)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ currentLevel = $(currentRow).data('level');
|
|
|
+
|
|
|
+ 0 < moving.horizontal
|
|
|
+ && currentLevel > 0
|
|
|
+ && $(currentRow).find('td:first').children(':first').remove()
|
|
|
+ && $(currentRow).data('level', --currentLevel);
|
|
|
+
|
|
|
+ 0 > moving.horizontal
|
|
|
+ && currentLevel < config.hierarchyLevel
|
|
|
+ && $(currentRow).prev().data('level') >= currentLevel
|
|
|
+ && $(currentRow).children(':first').prepend(config.indentArtifact)
|
|
|
+ && $(currentRow).data('level', ++currentLevel);
|
|
|
+
|
|
|
+ },
|
|
|
+ mousemove: function(e) {
|
|
|
+ var dragObj = $($.tableDnD.dragObject),
|
|
|
+ config = $.tableDnD.currentTable.tableDnDConfig,
|
|
|
+ currentRow,
|
|
|
+ mousePos,
|
|
|
+ moving,
|
|
|
+ x,
|
|
|
+ y;
|
|
|
+
|
|
|
+ e && e.preventDefault();
|
|
|
+
|
|
|
+ if (!$.tableDnD.dragObject)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // prevent touch device screen scrolling
|
|
|
+ e.type === 'touchmove'
|
|
|
+ && event.preventDefault(); // TODO verify this is event and not really e
|
|
|
+
|
|
|
+ // update the style to show we're dragging
|
|
|
+ config.onDragClass
|
|
|
+ && dragObj.addClass(config.onDragClass)
|
|
|
+ || dragObj.css(config.onDragStyle);
|
|
|
+
|
|
|
+ mousePos = $.tableDnD.mouseCoords(e);
|
|
|
+ x = mousePos.x - $.tableDnD.mouseOffset.x;
|
|
|
+ y = mousePos.y - $.tableDnD.mouseOffset.y;
|
|
|
+
|
|
|
+ // auto scroll the window
|
|
|
+ $.tableDnD.autoScroll(mousePos);
|
|
|
+
|
|
|
+ currentRow = $.tableDnD.findDropTargetRow(dragObj, y);
|
|
|
+ moving = $.tableDnD.findDragDirection(x, y);
|
|
|
+
|
|
|
+ $.tableDnD.moveVerticle(moving, currentRow);
|
|
|
+ $.tableDnD.moveHorizontal(moving, currentRow);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ findDragDirection: function (x,y) {
|
|
|
+ var sensitivity = this.currentTable.tableDnDConfig.sensitivity,
|
|
|
+ oldX = this.oldX,
|
|
|
+ oldY = this.oldY,
|
|
|
+ xMin = oldX - sensitivity,
|
|
|
+ xMax = oldX + sensitivity,
|
|
|
+ yMin = oldY - sensitivity,
|
|
|
+ yMax = oldY + sensitivity,
|
|
|
+ moving = {
|
|
|
+ horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1,
|
|
|
+ vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1
|
|
|
+ };
|
|
|
+
|
|
|
+ // update the old value
|
|
|
+ if (moving.horizontal !== 0)
|
|
|
+ this.oldX = x;
|
|
|
+ if (moving.vertical !== 0)
|
|
|
+ this.oldY = y;
|
|
|
+
|
|
|
+ return moving;
|
|
|
+ },
|
|
|
+ /** We're only worried about the y position really, because we can only move rows up and down */
|
|
|
+ findDropTargetRow: function(draggedRow, y) {
|
|
|
+ var rowHeight = 0,
|
|
|
+ rows = this.currentTable.rows,
|
|
|
+ config = this.currentTable.tableDnDConfig,
|
|
|
+ rowY = 0,
|
|
|
+ row = null;
|
|
|
+
|
|
|
+ for (var i = 0; i < rows.length; i++) {
|
|
|
+ row = rows[i];
|
|
|
+ rowY = this.getPosition(row).y;
|
|
|
+ rowHeight = parseInt(row.offsetHeight) / 2;
|
|
|
+ if (row.offsetHeight === 0) {
|
|
|
+ rowY = this.getPosition(row.firstChild).y;
|
|
|
+ rowHeight = parseInt(row.firstChild.offsetHeight) / 2;
|
|
|
+ }
|
|
|
+ // Because we always have to insert before, we need to offset the height a bit
|
|
|
+ if (y > (rowY - rowHeight) && y < (rowY + rowHeight))
|
|
|
+ // that's the row we're over
|
|
|
+ // If it's the same as the current row, ignore it
|
|
|
+ if (draggedRow.is(row)
|
|
|
+ || (config.onAllowDrop
|
|
|
+ && !config.onAllowDrop(draggedRow, row))
|
|
|
+ // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
|
|
|
+ || $(row).hasClass("nodrop"))
|
|
|
+ return null;
|
|
|
+ else
|
|
|
+ return row;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ processMouseup: function() {
|
|
|
+ if (!this.currentTable || !this.dragObject)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ var config = this.currentTable.tableDnDConfig,
|
|
|
+ droppedRow = this.dragObject,
|
|
|
+ parentLevel = 0,
|
|
|
+ myLevel = 0;
|
|
|
+
|
|
|
+ // Unbind the event handlers
|
|
|
+ $(document)
|
|
|
+ .unbind(moveEvent, this.mousemove)
|
|
|
+ .unbind(endEvent, this.mouseup);
|
|
|
+
|
|
|
+ config.hierarchyLevel
|
|
|
+ && config.autoCleanRelations
|
|
|
+ && $(this.currentTable.rows).first().find('td:first').children().each(function () {
|
|
|
+ myLevel = $(this).parents('tr:first').data('level');
|
|
|
+ myLevel
|
|
|
+ && $(this).parents('tr:first').data('level', --myLevel)
|
|
|
+ && $(this).remove();
|
|
|
+ })
|
|
|
+ && config.hierarchyLevel > 1
|
|
|
+ && $(this.currentTable.rows).each(function () {
|
|
|
+ myLevel = $(this).data('level');
|
|
|
+ if (myLevel > 1) {
|
|
|
+ parentLevel = $(this).prev().data('level');
|
|
|
+ while (myLevel > parentLevel + 1) {
|
|
|
+ $(this).find('td:first').children(':first').remove();
|
|
|
+ $(this).data('level', --myLevel);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // If we have a dragObject, then we need to release it,
|
|
|
+ // The row will already have been moved to the right place so we just reset stuff
|
|
|
+ config.onDragClass
|
|
|
+ && $(droppedRow).removeClass(config.onDragClass)
|
|
|
+ || $(droppedRow).css(config.onDropStyle);
|
|
|
+
|
|
|
+ this.dragObject = null;
|
|
|
+ // Call the onDrop method if there is one
|
|
|
+ config.onDrop
|
|
|
+ && this.originalOrder !== this.currentOrder()
|
|
|
+ && $(droppedRow).hide().fadeIn('fast')
|
|
|
+ && config.onDrop(this.currentTable, droppedRow);
|
|
|
+
|
|
|
+ // Call the onDragStop method if there is one
|
|
|
+ config.onDragStop
|
|
|
+ && config.onDragStop(this.currentTable, droppedRow);
|
|
|
+
|
|
|
+ this.currentTable = null; // let go of the table too
|
|
|
+ },
|
|
|
+ mouseup: function(e) {
|
|
|
+ e && e.preventDefault();
|
|
|
+ $.tableDnD.processMouseup();
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ jsonize: function(pretify) {
|
|
|
+ var table = this.currentTable;
|
|
|
+ if (pretify)
|
|
|
+ return JSON.stringify(
|
|
|
+ this.tableData(table),
|
|
|
+ null,
|
|
|
+ table.tableDnDConfig.jsonPretifySeparator
|
|
|
+ );
|
|
|
+ return JSON.stringify(this.tableData(table));
|
|
|
+ },
|
|
|
+ serialize: function() {
|
|
|
+ return $.param(this.tableData(this.currentTable));
|
|
|
+ },
|
|
|
+ serializeTable: function(table) {
|
|
|
+ var result = "";
|
|
|
+ var paramName = table.tableDnDConfig.serializeParamName || table.id;
|
|
|
+ var rows = table.rows;
|
|
|
+ for (var i=0; i<rows.length; i++) {
|
|
|
+ if (result.length > 0) result += "&";
|
|
|
+ var rowId = rows[i].id;
|
|
|
+ if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
|
|
|
+ rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
|
|
|
+ result += paramName + '[]=' + rowId;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ serializeTables: function() {
|
|
|
+ var result = [];
|
|
|
+ $('table').each(function() {
|
|
|
+ this.id && result.push($.param($.tableDnD.tableData(this)));
|
|
|
+ });
|
|
|
+ return result.join('&');
|
|
|
+ },
|
|
|
+ tableData: function (table) {
|
|
|
+ var config = table.tableDnDConfig,
|
|
|
+ previousIDs = [],
|
|
|
+ currentLevel = 0,
|
|
|
+ indentLevel = 0,
|
|
|
+ rowID = null,
|
|
|
+ data = {},
|
|
|
+ getSerializeRegexp,
|
|
|
+ paramName,
|
|
|
+ currentID,
|
|
|
+ rows;
|
|
|
+
|
|
|
+ if (!table)
|
|
|
+ table = this.currentTable;
|
|
|
+ if (!table || !table.rows || !table.rows.length)
|
|
|
+ return {error: { code: 500, message: "Not a valid table."}};
|
|
|
+ if (!table.id && !config.serializeParamName)
|
|
|
+ return {error: { code: 500, message: "No serializable unique id provided."}};
|
|
|
+
|
|
|
+ rows = config.autoCleanRelations
|
|
|
+ && table.rows
|
|
|
+ || $.makeArray(table.rows);
|
|
|
+ paramName = config.serializeParamName || table.id;
|
|
|
+ currentID = paramName;
|
|
|
+
|
|
|
+ getSerializeRegexp = function (rowId) {
|
|
|
+ if (rowId && config && config.serializeRegexp)
|
|
|
+ return rowId.match(config.serializeRegexp)[0];
|
|
|
+ return rowId;
|
|
|
+ };
|
|
|
+
|
|
|
+ data[currentID] = [];
|
|
|
+ !config.autoCleanRelations
|
|
|
+ && $(rows[0]).data('level')
|
|
|
+ && rows.unshift({id: 'undefined'});
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for (var i=0; i < rows.length; i++) {
|
|
|
+ if (config.hierarchyLevel) {
|
|
|
+ indentLevel = $(rows[i]).data('level') || 0;
|
|
|
+ if (indentLevel === 0) {
|
|
|
+ currentID = paramName;
|
|
|
+ previousIDs = [];
|
|
|
+ }
|
|
|
+ else if (indentLevel > currentLevel) {
|
|
|
+ previousIDs.push([currentID, currentLevel]);
|
|
|
+ currentID = getSerializeRegexp(rows[i-1].id);
|
|
|
+ }
|
|
|
+ else if (indentLevel < currentLevel) {
|
|
|
+ for (var h = 0; h < previousIDs.length; h++) {
|
|
|
+ if (previousIDs[h][1] === indentLevel)
|
|
|
+ currentID = previousIDs[h][0];
|
|
|
+ if (previousIDs[h][1] >= currentLevel)
|
|
|
+ previousIDs[h][1] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ currentLevel = indentLevel;
|
|
|
+
|
|
|
+ if (!$.isArray(data[currentID]))
|
|
|
+ data[currentID] = [];
|
|
|
+ rowID = getSerializeRegexp(rows[i].id);
|
|
|
+ rowID && data[currentID].push(rowID);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ rowID = getSerializeRegexp(rows[i].id);
|
|
|
+ rowID && data[currentID].push(rowID);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+jQuery.fn.extend(
|
|
|
+ {
|
|
|
+ tableDnD : $.tableDnD.build,
|
|
|
+ tableDnDUpdate : $.tableDnD.updateTables,
|
|
|
+ tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD),
|
|
|
+ tableDnDSerializeAll : $.tableDnD.serializeTables,
|
|
|
+ tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD)
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+}(jQuery, window, window.document);
|