-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/lib/testcases/tcEdit.php b/lib/testcases/tcEdit.php
index ce9728dfae..c9afdc2b11 100644
--- a/lib/testcases/tcEdit.php
+++ b/lib/testcases/tcEdit.php
@@ -310,6 +310,8 @@ function init_args(&$cfgObj,$otName,&$tcaseMgr) {
$args = new stdClass();
$_REQUEST = strings_stripSlashes($_REQUEST);
+ $args->stepSeq = isset($_REQUEST["stepSeq"])? $_REQUEST["stepSeq"] : "";
+
$rightlist_html_name = $otName . "_newRight";
$args->assigned_keywords_list = isset($_REQUEST[$rightlist_html_name])? $_REQUEST[$rightlist_html_name] : "";
$args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0;
diff --git a/lib/testcases/testcaseCommands.class.php b/lib/testcases/testcaseCommands.class.php
index 48e047af45..750f0a8f19 100644
--- a/lib/testcases/testcaseCommands.class.php
+++ b/lib/testcases/testcaseCommands.class.php
@@ -976,13 +976,22 @@ function doResequenceSteps(&$argsObj,$request) {
$this->initTestCaseBasicInfo($argsObj,$guiObj);
- // Get all existent steps - info needed to do renumbering
- $stepNumberSet = array();
- $stepSet = $this->tcaseMgr->get_steps($argsObj->tcversion_id);
- $stepsQty = count($stepSet);
- for($idx=0; $idx < $stepsQty; $idx++) {
- $renumbered[$stepSet[$idx]['id']] = $idx+1;
+ if ($argsObj->stepSeq != '') {
+ $xx = explode('&', $argsObj->stepSeq);
+ $point = 1;
+ foreach($xx as $step_id) {
+ $renumbered[$step_id] = $point++;
+ }
+ } else {
+ // Get all existent steps - info needed to do renumbering
+ $stepNumberSet = array();
+ $stepSet = $this->tcaseMgr->get_steps($argsObj->tcversion_id);
+ $stepsQty = count($stepSet);
+ for($idx=0; $idx < $stepsQty; $idx++) {
+ $renumbered[$stepSet[$idx]['id']] = $idx+1;
+ }
}
+
$this->tcaseMgr->set_step_number($renumbered);
$guiObj->template =
diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
new file mode 100644
index 0000000000..4f34416d7f
--- /dev/null
+++ b/node_modules/.yarn-integrity
@@ -0,0 +1,16 @@
+{
+ "systemParams": "darwin-x64-72",
+ "modulesFolders": [
+ "node_modules"
+ ],
+ "flags": [],
+ "linkedModules": [],
+ "topLevelPatterns": [
+ "tablednd@^1.0.3"
+ ],
+ "lockfileEntries": {
+ "tablednd@^1.0.3": "https://registry.yarnpkg.com/tablednd/-/tablednd-1.0.3.tgz#0c6992bc7fe3ec5e880344684479c9df90c0442b"
+ },
+ "files": [],
+ "artifacts": {}
+}
\ No newline at end of file
diff --git a/node_modules/tablednd/.hound.yml b/node_modules/tablednd/.hound.yml
new file mode 100644
index 0000000000..9c56266719
--- /dev/null
+++ b/node_modules/tablednd/.hound.yml
@@ -0,0 +1,3 @@
+jshint:
+ config_file: .jshintrc
+ ignore_file: .jshintignore
diff --git a/node_modules/tablednd/.jshintignore b/node_modules/tablednd/.jshintignore
new file mode 100644
index 0000000000..00df06f324
--- /dev/null
+++ b/node_modules/tablednd/.jshintignore
@@ -0,0 +1,6 @@
+Gruntfile.js
+node_modules/**/*.js
+**/*.min.js
+**/*.rc1.js
+
+
diff --git a/node_modules/tablednd/.jshintrc b/node_modules/tablednd/.jshintrc
new file mode 100644
index 0000000000..be03f9c952
--- /dev/null
+++ b/node_modules/tablednd/.jshintrc
@@ -0,0 +1,11 @@
+{
+ "laxbreak": true,
+ "expr": true,
+
+ "globals": {
+ "jQuery": true,
+ "console": true,
+ "module": true,
+ "document": true
+ }
+}
diff --git a/node_modules/tablednd/.npmignore b/node_modules/tablednd/.npmignore
new file mode 100644
index 0000000000..8c53f03933
--- /dev/null
+++ b/node_modules/tablednd/.npmignore
@@ -0,0 +1,53 @@
+
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+*.iml
+
+## Directory-based project format:
+.idea/
+# if you remove the above rule, at least ignore the following:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+# Mongo Explorer plugin:
+# .idea/mongoSettings.xml
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+
+
+
+node_modules
\ No newline at end of file
diff --git a/node_modules/tablednd/.scannerwork/.sonar_lock b/node_modules/tablednd/.scannerwork/.sonar_lock
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/node_modules/tablednd/.scannerwork/report-task.txt b/node_modules/tablednd/.scannerwork/report-task.txt
new file mode 100644
index 0000000000..728c4b2a66
--- /dev/null
+++ b/node_modules/tablednd/.scannerwork/report-task.txt
@@ -0,0 +1,7 @@
+organization=isocra
+projectKey=TableDnD
+serverUrl=https://sonarcloud.io
+serverVersion=7.0.0.35052
+dashboardUrl=https://sonarcloud.io/dashboard/index/TableDnD
+ceTaskId=AWCoUo8vzhfPH6zNRdlG
+ceTaskUrl=https://sonarcloud.io/api/ce/task?id=AWCoUo8vzhfPH6zNRdlG
diff --git a/node_modules/tablednd/Gruntfile.js b/node_modules/tablednd/Gruntfile.js
new file mode 100644
index 0000000000..ad310a6c97
--- /dev/null
+++ b/node_modules/tablednd/Gruntfile.js
@@ -0,0 +1,37 @@
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('bower.json'),
+ uglify: {
+ options: {
+ banner: '/*! jquery.tablednd.js <%= grunt.template.today("dd-mm-yyyy") %> */\n'
+ },
+ dist: {
+ files: {
+ 'dist/jquery.tablednd.min.js': ['js/jquery.tablednd.js']
+ }
+ }
+ },
+ jshint: {
+ all: {
+ options: {
+ reporterOutput: "",
+ jshintrc: true
+ },
+ src: 'js/*.js'
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+
+ grunt.registerTask('copyToDist', function() {
+ grunt.file.copy('js/jquery.tablednd.js', 'dist/jquery.tablednd.js');
+ });
+
+ grunt.registerTask('test', ['jshint']);
+
+ grunt.registerTask('default', ['jshint', 'copyToDist', 'uglify']);
+
+};
diff --git a/node_modules/tablednd/MIT-LICENSE.txt b/node_modules/tablednd/MIT-LICENSE.txt
new file mode 100644
index 0000000000..fb40abd8e6
--- /dev/null
+++ b/node_modules/tablednd/MIT-LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) Denis Howlett
+Copyright 2012 Nick Lombard - nickl- and other contributors
+https://github.com/isocra/TableDnD
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/tablednd/README.md b/node_modules/tablednd/README.md
new file mode 100644
index 0000000000..43a6b30bb2
--- /dev/null
+++ b/node_modules/tablednd/README.md
@@ -0,0 +1,59 @@
+# TableDnD
+[](https://badge.fury.io/js/tablednd)
+[](https://cdnjs.com/libraries/TableDnD)
+[](https://www.jsdelivr.com/package/gh/isocra/TableDnD)
+
+## Installation
+
+TableDnD is easy to install:
+```
+npm install --save tablednd
+```
+or
+```
+yarn add tablednd
+```
+or
+```
+bower install https://github.com/isocra/TableDnD.git
+```
+Alternatively you can simply reference from CDNJS:
+```html
+
+```
+or
+```html
+
+```
+You'll also need to include [jQuery](https://jquery.com) before you include this plugin (so that jQuery is defined).
+
+---
+
+## Getting Started
+
+Let's create a simple table. The HTML for the table is very straight forward (no Javascript, pure HTML, we haven't added `thead` or `tbody` elements, but it works fine with these too):
+
+```html
+
+
1
One
some text
+
2
Two
some text
+
3
Three
some text
+
4
Four
some text
+
5
Five
some text
+
6
Six
some text
+
+```
+To add in the "draggability" all we need to do is add a line to the `$(document).ready(...)` function as follows:
+```html
+
+```
+Basically we get the table element and call `tableDnD`. If you try this, you'll see that the rows are now draggable.
+
+In the example above we're not setting any parameters at all so we get the default settings. There are a number of parameters you can set in order to control the look and feel of the table and also to add custom behaviour on drag or on drop. The parameters are specified as a map in the usual way and are described the [full documentation](http://isocra.github.io/TableDnD):
+
+You can also play and experiment with TableDnD using this [jsFiddle](http://jsfiddle.net/DenisHo/dxpLrcd9/embedded/result/). Here you get the documentation, plus live examples.
diff --git a/node_modules/tablednd/__tests__/test.jquery.tablednd.js b/node_modules/tablednd/__tests__/test.jquery.tablednd.js
new file mode 100644
index 0000000000..e90a6fe092
--- /dev/null
+++ b/node_modules/tablednd/__tests__/test.jquery.tablednd.js
@@ -0,0 +1,30 @@
+const $ = require('jquery')
+window.jQuery = $;
+const tableDnD = require('../js/jquery.tablednd');
+
+beforeEach(function() {
+ document.body.innerHTML =
+ '
' +
+ ' ' +
+ '
Col1
' +
+ ' ' +
+ ' ' +
+ '
Row1
' +
+ '
Row2
' +
+ '
Row3
' +
+ '';
+});
+
+test('Creates a TableDnD table', function() {
+ var $table = $('#table1');
+ var table = $table.tableDnD();
+ expect(table).not.toBeUndefined();
+});
+
+test('Simulate drag and drop', function() {
+ var $table = $('#table1');
+ var table = $table.tableDnD();
+ var $row = $('#row1');
+ TestUtils.Simulate.dragStart($row, {dataTransfer: null});
+
+});
diff --git a/node_modules/tablednd/bower.json b/node_modules/tablednd/bower.json
new file mode 100644
index 0000000000..3b8db3187e
--- /dev/null
+++ b/node_modules/tablednd/bower.json
@@ -0,0 +1,14 @@
+{
+ "name": "TableDnD",
+ "version": "0.9.1",
+ "main": [
+ "./js/jquery.tablednd.js"
+ ],
+ "ignore": [
+ "LICENCE", "README.md"
+ ],
+ "dependencies": {
+ "jquery": ">= 1.7.0"
+ },
+ "homepage": "https://github.com/isocra/TableDnD"
+}
\ No newline at end of file
diff --git a/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js
new file mode 100644
index 0000000000..27a1be5e98
--- /dev/null
+++ b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js
@@ -0,0 +1,675 @@
+/**
+ * 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
+ * Licensed like jQuery, see http://docs.jquery.com/License.
+ *
+ * Configuration options:
+ *
+ * onDragStyle
+ * This is the style that is assigned to the row during drag. There are limitations to the styles that can be
+ * associated with a row (such as you can't assign a border--well you can, but it won't be
+ * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
+ * a map (as used in the jQuery css(...) function).
+ * onDropStyle
+ * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
+ * to what you can do. Also this replaces the original style, so again consider using onDragClass which
+ * is simply added and then removed on drop.
+ * onDragClass
+ * This class is added for the duration of the drag and then removed when the row is dropped. It is more
+ * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
+ * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
+ * stylesheet.
+ * onDrop
+ * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
+ * and the row that was dropped. You can work out the new order of the rows by using
+ * table.rows.
+ * onDragStart
+ * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
+ * table and the row which the user has started to drag.
+ * onDragStop
+ * Pass a function that will be called when the user stops dragging regardless of if the rows have been
+ * rearranged. The function takes 2 parameters: the table and the row which the user was dragging.
+ * onAllowDrop
+ * Pass a function that will be called as a row is over another row. If the function returns true, allow
+ * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
+ * the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
+ * scrollAmount
+ * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
+ * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
+ * FF3 beta
+ * dragHandle
+ * This is a jQuery mach string for one or more cells in each row that is draggable. If you
+ * specify this, then you are responsible for setting cursor: move in the CSS and only these cells
+ * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
+ * the whole row is draggable.
+ *
+ * Other ways to control behaviour:
+ *
+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
+ * that you don't want to be draggable.
+ *
+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
+ * []=&[]= so that you can send this back to the server. The table must have
+ * an ID as must all the rows.
+ *
+ * Other methods:
+ *
+ * $("...").tableDnDUpdate()
+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
+ * The table maintains the original configuration (so you don't have to specify it again).
+ *
+ * $("...").tableDnDSerialize()
+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
+ *
+ * Known problems:
+ * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
+ *
+ * Version 0.2: 2008-02-20 First public version
+ * Version 0.3: 2008-02-07 Added onDragStart option
+ * Made the scroll amount configurable (default is 5 as before)
+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
+ * Added onAllowDrop to control dropping
+ * Fixed a bug which meant that you couldn't set the scroll amount in both directions
+ * Added serialize method
+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
+ * draggable
+ * Improved the serialize method to use a default (and settable) regular expression.
+ * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
+ * Version 0.6: 2011-12-02 Added support for touch devices
+ * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces
+ */
+!function ($, window, document, undefined) {
+// Determine if this is a touch device
+var hasTouch = 'ontouchstart' in document.documentElement,
+ 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') || '
',
+ 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: '
',
+ /** 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 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);
diff --git a/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js
new file mode 100644
index 0000000000..d6adb5ce1d
--- /dev/null
+++ b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js
@@ -0,0 +1,2 @@
+/*! jquery.tablednd.js 27-12-2017 */
+!function(a,b,c,d){var e=("ontouchstart"in c.documentElement,"touchstart mousedown"),f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"==a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")==d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:" ",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),void(0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d))):null},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),a.tableDnD.dragObject?("touchmove"==b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1):!1},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&g>=a?0:a>d?-1:1,vertical:b>=h&&i>=b?0:b>e?-1:1};return 0!=j.horizontal&&(this.oldX=a),0!=j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&g+d>c)return b.is(h)||f.onAllowDrop&&!f.onAllowDrop(b,h)||a(h).hasClass("nodrop")?null:h;return null},processMouseup:function(){if(!this.currentTable||!this.dragObject)return null;var b=this.currentTable.tableDnDConfig,d=this.dragObject,e=0,h=0;a(c).unbind(f,this.mousemove).unbind(g,this.mouseup),b.hierarchyLevel&&b.autoCleanRelations&&a(this.currentTable.rows).first().find("td:first").children().each(function(){h=a(this).parents("tr:first").data("level"),h&&a(this).parents("tr:first").data("level",--h)&&a(this).remove()})&&b.hierarchyLevel>1&&a(this.currentTable.rows).each(function(){if(h=a(this).data("level"),h>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!=this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(i>j)for(var n=0;n=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else k=c(f[m].id),k&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document);
\ No newline at end of file
diff --git a/node_modules/tablednd/dist/jquery.tablednd.js b/node_modules/tablednd/dist/jquery.tablednd.js
new file mode 100644
index 0000000000..d91f17532c
--- /dev/null
+++ b/node_modules/tablednd/dist/jquery.tablednd.js
@@ -0,0 +1,601 @@
+/**
+ * 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
+ * License: MIT.
+ * See https://github.com/isocra/TableDnD
+ */
+
+/*jshint -W054 */
+
+!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') || '
',
+ 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: '
',
+ /** 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 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);
diff --git a/node_modules/tablednd/dist/jquery.tablednd.min.js b/node_modules/tablednd/dist/jquery.tablednd.min.js
new file mode 100644
index 0000000000..225739f4b3
--- /dev/null
+++ b/node_modules/tablednd/dist/jquery.tablednd.min.js
@@ -0,0 +1,2 @@
+/*! jquery.tablednd.js 30-12-2017 */
+!function(a,b,c,d){var e="touchstart mousedown",f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"===a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")===d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d)},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),!!a.tableDnD.dragObject&&("touchmove"===b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1)},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&a<=g?0:a>d?-1:1,vertical:b>=h&&b<=i?0:b>e?-1:1};return 0!==j.horizontal&&(this.oldX=a),0!==j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&c1&&a(this.currentTable.rows).each(function(){if((h=a(this).data("level"))>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!==this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(j=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else(k=c(f[m].id))&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document);
\ No newline at end of file
diff --git a/node_modules/tablednd/images/updown2.gif b/node_modules/tablednd/images/updown2.gif
new file mode 100644
index 0000000000..3cb4482d69
Binary files /dev/null and b/node_modules/tablednd/images/updown2.gif differ
diff --git a/node_modules/tablednd/index.html b/node_modules/tablednd/index.html
new file mode 100644
index 0000000000..d3c380b31b
--- /dev/null
+++ b/node_modules/tablednd/index.html
@@ -0,0 +1,527 @@
+
+
+
+ Table Drag and Drop jQuery plugin
+
+
+
+
+
+
Table Drag and Drop jQuery plugin
+
This page contains documentation and tests for the TableDnD jQuery plug-in. For more information and
+to post comments, please go to isocra.com.
+
+
If you have issues or bug reports, then you can post them at the TableDnD plug page.
+
+
How do I use it?
+
+
Since TableDnD is a jquery pligin you will need to include jquery in your page first.
+
No need for any downloads simply reference jQuery from the Google CDN
+ (Content Distribution Network)
+ All scripts are included at the bottom of the page, to facilitate quicker rendering of the HTML for more responsive pages.
+ The following is the way we are linking to jQuery in the examples and this method can be recommended for use in your implementations too.
You will also need a copy of the TableDnD plugin (current version 0.9) which you can reference in
+ the normal fashion, anywhere after jQuery.
+
In true jQuery style, the typical way to initialise the tabes is in the $(document).ready code black function.
+ Use a selector to select your table and then call the following (You can optionally specify a set of properties (described below).
+
tableDnD()
+
+
+
+
A basic table
+
+
+
+
1
One
some text
+
2
Two
some text
+
3
Three
some text
+
4
Four
some text
+
5
Five
some text
+
6
Six
some text
+
+
+
The HTML for the table is very straight forward (no Javascript, pure HTML):
In the example above we're not setting any parameters at all so we get the default settings. There are a number
+ of parameters you can set in order to control the look and feel of the table and also to add custom behaviour
+ on drag or on drop. The parameters are specified as a map in the usual way and are described below:
+
+
Settings
+
+
onDragStyle
+
This is the style that is assigned to the row during drag. There are limitations to the styles that can be
+ associated with a row (such as you can't assign a border—well you can, but it won't be
+ displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
+ a map (as used in the jQuery css(...) function).
+
onDropStyle
+
This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
+ to what you can do. Also this replaces the original style, so again consider using onDragClass which
+ is simply added and then removed on drop.
+
onDragClass
+
This class is added for the duration of the drag and then removed when the row is dropped. It is more
+ flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
+ is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
+ stylesheet.
+
onDrop
+
Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
+ and the row that was dropped. You can work out the new order of the rows by using
+ table.tBodies[0].rows.
+
onDragStart
+
Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
+ table and the row which the user has started to drag.
+
scrollAmount
+
This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
+ window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
+ FF3 beta)
+
+
+
OnDrag custom table
+
This second table has has an onDrop function applied as well as an onDragClass. The javascript to set this up is
+as follows:
+
+
+$(document).ready(function() {
+
+ // Initialise the first table (as before)
+ $("#table-1").tableDnD();
+
+ // Make a nice striped effect on the table
+ $("#table-2 tr:even').addClass('alt')");
+
+ // Initialise the second table specifying a dragClass and an onDrop function that will display an alert
+ $("#table-2").tableDnD({
+ onDragClass: "myDragClass",
+ onDrop: function(table, row) {
+ var rows = table.tBodies[0].rows;
+ var debugStr = "Row dropped was "+row.id+". New order: ";
+ for (var i=0; i<rows.length; i++) {
+ debugStr += rows[i].id+" ";
+ }
+ $(table).parent().find('.result').text(debugStr);
+ },
+ onDragStart: function(table, row) {
+ $(table).parent().find('.result').text("Started dragging row "+row.id);
+ }
+ });
+});
+
+
+
+
1
One
V
C
N
+
2
Two
V
C
N
+
3
Three
V
C
N
+
4
Four
V
C
N
+
5
Five
V
C
N
+
6
Six
V
C
N
+
7
Seven
V
C
N
+
8
Eight
V
C
N
+
9
Nine
V
C
N
+
10
Ten
V
C
N
+
11
Eleven
V
C
N
+
12
Twelve
V
C
N
+
13
Thirteen
V
C
N
+
14
Fourteen
V
C
N
+
+
+
+
Communicating with the back-end
+
Generally once the user has dropped a row, you need to inform the server of the new order. To do this, we've
+ added a method called serialize(). It takes no parameters but knows the current table from the
+ context. The method returns a string of the form tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3...
+ You can then use this as part of an Ajax load.
+
+
+ Since version 0.9, instead of manually creating the serialized data string we instead use jQuery's param method which has the added benefit of url encoding the data string as well.
+
+
This third table demonstrates calling the serialize function inside onDrop (as shown below). It also
+ demonstrates the "nodrop" class on row 3 and "nodrag" class on row 5, so you can't pick up row 5 and
+ you can't drop any row on row 3 (but you can drag it).
Drag and drop in this table to test out serialise and using JQuery.load()
+
+
+
1
One
+
2
Two
+
3
Three (Can't drop on this row)
+
4
Four (Can't drop on this row)
+
5
Five
+
6
Six (Can't drag this row)
+
7
Seven
+
+
+
+
Multiple tbody table
+
This table has multiple TBODYs. The functionality isn't quite working properly. You can only drag the rows inside their
+own TBODY, you can't drag them outside it. Now this might or might not be what you want, but unfortunately if you then drop a row outside its TBODY you get a Javascript error because inserting after a sibling doesn't work. This will be fixed in the next version. The header rows all have the classes "nodrop" and "nodrag" so that they can't be dragged or dropped on.
+
+
+
+
H1
H2
H3
+
+
+
4.1
One
+
4.2
Two
+
4.3
Three
+
4.4
Four
+
4.5
Five
+
4.6
Six
+
+
+
H1
H2
H3
+
5.1
One
+
5.2
Two
+
5.3
Three
+
5.4
Four
+
5.5
Five
+
5.6
Six
+
+
+
H1
H2
H3
+
6.1
One
+
6.2
Two
+
6.3
Three
+
6.4
Four
+
6.5
Five
+
6.6
Six
+
+
+
+
Identify rows
+
+The following table demonstrates the use of the default regular expression. The rows have IDs of the
+form table5-row-1, table5-row-2, etc., but the regular expression is /[^\-]*$/ (this is the same
+as used in the NestedSortable plugin for consistency).
+This removes everything before and including the last hyphen, so the serialised string just has 1, 2, 3 etc.
+You can replace the regular expression by setting the serializeRegexp option, you can also just set it
+to null to stop this behaviour.
+
In fact you will notice that I have also set the dragHandle on this table. This has two effects: firstly only
+the cell with the drag handle class is draggable and secondly it doesn't automatically add the cursor: move
+style to the row (or the drag handle cell), so you are responsible for setting up the style as you see fit.
+
Here I've actually added an extra effect which adds a background image to the first cell in the row whenever
+you enter it using the jQuery hover function as follows:
This table allows row order to be dragged horizontally and placed in a hierarchy under a parent row (since version 0.9). We also get a chance to look at the new jsonize method for JSON serialized form of the data.
+
In the onDrop event handler we pass the JSON as data to jquery through a HTTP POST ajax call to the server:
On the back-end we have a PHP example that simply retrieves the JSON POST data from the built in stream php://input, decodes the payload and proceeds to build the hierarchy through recursion.
+
To keep the data simple and also stay compatible with the http variable methods as mentioned previously the data structure is formed with separate collections. If a parent has children the children first level are listed and if any of the children have subsequent children an additional collection is created for the first level of these.
+
The following hierarchy for example would generate 3 collections:
We use the setting hierarchyLevel to indicate how many levels are supported, the example uses 4 levels deep. When populating the table you can use the the data-leve tag to indicate at which level the current row is represented at.
+
+
+
+
+
Ajax result
+
Drag and drop in this table to test out hierarcies and using JSON payload.
+
+
+
+
+
+
+
+
diff --git a/node_modules/tablednd/js/jquery.tablednd.0.6.min.js b/node_modules/tablednd/js/jquery.tablednd.0.6.min.js
new file mode 100644
index 0000000000..7201855a3c
--- /dev/null
+++ b/node_modules/tablednd/js/jquery.tablednd.0.6.min.js
@@ -0,0 +1 @@
+var hasTouch='ontouchstart'in document.documentElement,startEvent=hasTouch?'touchstart':'mousedown',moveEvent=hasTouch?'touchmove':'mousemove',endEvent=hasTouch?'touchend':'mouseup';jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery("td."+table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(this.parentNode,table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=='touchmove'){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;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}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},serialize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},serializeTable:function(table){var result="";var tableId=table.id;var rows=table.rows;for(var i=0;i0)result+="&";var rowId=rows[i].id;if(rowId&&rowId&&table.tableDnDConfig&&table.tableDnDConfig.serializeRegexp){rowId=rowId.match(table.tableDnDConfig.serializeRegexp)[0]}result+=tableId+'[]='+rowId}return result},serializeTables:function(){var result="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables});
\ No newline at end of file
diff --git a/node_modules/tablednd/js/jquery.tablednd.0.7.min.js b/node_modules/tablednd/js/jquery.tablednd.0.7.min.js
new file mode 100644
index 0000000000..09b0bedb14
--- /dev/null
+++ b/node_modules/tablednd/js/jquery.tablednd.0.7.min.js
@@ -0,0 +1 @@
+var hasTouch='ontouchstart'in document.documentElement,startEvent=hasTouch?'touchstart':'mousedown',moveEvent=hasTouch?'touchmove':'mousemove',endEvent=hasTouch?'touchend':'mouseup';if(hasTouch){$.each("touchstart touchmove touchend".split(" "),function(i,name){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks});alert("has Touch")}jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery(table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(jQuery(this).parents('tr')[0],table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=='touchmove'){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;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}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},serialize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},serializeTable:function(table){var result="";var tableId=table.id;var rows=table.rows;for(var i=0;i0)result+="&";var rowId=rows[i].id;if(rowId&&rowId&&table.tableDnDConfig&&table.tableDnDConfig.serializeRegexp){rowId=rowId.match(table.tableDnDConfig.serializeRegexp)[0]}result+=tableId+'[]='+rowId}return result},serializeTables:function(){var result="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables});
\ No newline at end of file
diff --git a/node_modules/tablednd/js/jquery.tablednd.0.8.min.js b/node_modules/tablednd/js/jquery.tablednd.0.8.min.js
new file mode 100644
index 0000000000..681ae71188
--- /dev/null
+++ b/node_modules/tablednd/js/jquery.tablednd.0.8.min.js
@@ -0,0 +1 @@
+(function($){var hasTouch="ontouchstart" in document.documentElement,startEvent=hasTouch?"touchstart":"mousedown",moveEvent=hasTouch?"touchmove":"mousemove",endEvent=hasTouch?"touchend":"mouseup";if(hasTouch){$.each("touchstart touchmove touchend".split(" "),function(i,name){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks})}jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery(table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(jQuery(this).parents("tr")[0],table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=="touchmove"){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;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}}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else{if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},jsonize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.jsonizeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},jsonizeTable:function(table){var result="{";var tableId=table.id;var rows=table.rows;result+='"'+tableId+'" : [';for(var i=0;i0){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="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables})})(jQuery);
\ No newline at end of file
diff --git a/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js b/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js
new file mode 100644
index 0000000000..8eb2cf0ac3
--- /dev/null
+++ b/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js
@@ -0,0 +1,664 @@
+/**
+ * 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
+ * Licensed like jQuery, see http://docs.jquery.com/License.
+ *
+ * Configuration options:
+ *
+ * onDragStyle
+ * This is the style that is assigned to the row during drag. There are limitations to the styles that can be
+ * associated with a row (such as you can't assign a border--well you can, but it won't be
+ * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
+ * a map (as used in the jQuery css(...) function).
+ * onDropStyle
+ * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
+ * to what you can do. Also this replaces the original style, so again consider using onDragClass which
+ * is simply added and then removed on drop.
+ * onDragClass
+ * This class is added for the duration of the drag and then removed when the row is dropped. It is more
+ * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
+ * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
+ * stylesheet.
+ * onDrop
+ * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
+ * and the row that was dropped. You can work out the new order of the rows by using
+ * table.rows.
+ * onDragStart
+ * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
+ * table and the row which the user has started to drag.
+ * onAllowDrop
+ * Pass a function that will be called as a row is over another row. If the function returns true, allow
+ * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
+ * the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
+ * scrollAmount
+ * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
+ * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
+ * FF3 beta
+ * dragHandle
+ * This is a jQuery mach string for one or more cells in each row that is draggable. If you
+ * specify this, then you are responsible for setting cursor: move in the CSS and only these cells
+ * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
+ * the whole row is draggable.
+ *
+ * Other ways to control behaviour:
+ *
+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
+ * that you don't want to be draggable.
+ *
+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
+ * []=&[]= so that you can send this back to the server. The table must have
+ * an ID as must all the rows.
+ *
+ * Other methods:
+ *
+ * $("...").tableDnDUpdate()
+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
+ * The table maintains the original configuration (so you don't have to specify it again).
+ *
+ * $("...").tableDnDSerialize()
+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
+ *
+ * Known problems:
+ * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
+ *
+ * Version 0.2: 2008-02-20 First public version
+ * Version 0.3: 2008-02-07 Added onDragStart option
+ * Made the scroll amount configurable (default is 5 as before)
+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
+ * Added onAllowDrop to control dropping
+ * Fixed a bug which meant that you couldn't set the scroll amount in both directions
+ * Added serialize method
+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
+ * draggable
+ * Improved the serialize method to use a default (and settable) regular expression.
+ * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
+ * Version 0.6: 2011-12-02 Added support for touch devices
+ * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces
+ */
+!function ($, window, document, undefined) {
+// Determine if this is a touch device
+var hasTouch = 'ontouchstart' in document.documentElement,
+ startEvent = hasTouch ? 'touchstart' : 'mousedown',
+ moveEvent = hasTouch ? 'touchmove' : 'mousemove',
+ endEvent = hasTouch ? 'touchend' : 'mouseup';
+
+// If we're on a touch device, then wire up the events
+// see http://stackoverflow.com/a/8456194/1316086
+hasTouch
+ && $.each("touchstart touchmove touchend".split(" "), function(i, name) {
+ $.event.fixHooks[name] = $.event.mouseHooks;
+ });
+
+
+$(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,
+ scrollAmount: $(this).data('scrollamount') || 5,
+ sensitivity: $(this).data('sensitivity') || 10,
+ hierarchyLevel: $(this).data('hierarchylevel') || 0,
+ indentArtifact: $(this).data('indentartifact') || '
',
+ 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
+ });
+ }
+
+
+ });
+});
+
+window.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,
+ 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: '
',
+ /** 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
+ }
+ });
+ },
+ 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.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 (row == draggedRow
+ || (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() {
+ var config = this.currentTable.tableDnDConfig,
+ droppedRow = this.dragObject,
+ parentLevel = 0,
+ myLevel = 0;
+
+ if (!this.currentTable || !droppedRow)
+ return null;
+
+ // 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);
+
+ 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 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(this.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.id || !table.rows || !table.rows.length)
+ return {error: { code: 500, message: "Not a valid table, 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;
+ }
+};
+
+window.jQuery.fn.extend(
+ {
+ tableDnD : $.tableDnD.build,
+ tableDnDUpdate : $.tableDnD.updateTables,
+ tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD),
+ tableDnDSerializeAll : $.tableDnD.serializeTables,
+ tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD)
+ }
+);
+
+}(window.jQuery, window, window.document);
diff --git a/node_modules/tablednd/js/jquery.tablednd.js b/node_modules/tablednd/js/jquery.tablednd.js
new file mode 100644
index 0000000000..d91f17532c
--- /dev/null
+++ b/node_modules/tablednd/js/jquery.tablednd.js
@@ -0,0 +1,601 @@
+/**
+ * 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
+ * License: MIT.
+ * See https://github.com/isocra/TableDnD
+ */
+
+/*jshint -W054 */
+
+!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') || '
',
+ 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: '
',
+ /** 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 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);
diff --git a/node_modules/tablednd/package.json b/node_modules/tablednd/package.json
new file mode 100644
index 0000000000..ace4bbaec9
--- /dev/null
+++ b/node_modules/tablednd/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "tablednd",
+ "version": "1.0.3",
+ "description": "JQuery plugin for dragging and droping rows in a table",
+ "main": "Gruntfile.js",
+ "dependencies": {},
+ "devDependencies": {
+ "grunt": "^0.4.5",
+ "grunt-contrib-jshint": "^0.10.0",
+ "grunt-contrib-uglify": "^0.6.0",
+ "jshint": "^2.9.3",
+ "release-it": "^5.2.0"
+ },
+ "scripts": {
+ "test": "grunt test",
+ "release": "release-it"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/isocra/TableDnD.git"
+ },
+ "keywords": [
+ "tablednd",
+ "drag",
+ "and",
+ "drop",
+ "jquery"
+ ],
+ "author": "DenisH",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/isocra/TableDnD/issues"
+ },
+ "homepage": "https://github.com/isocra/TableDnD#readme"
+}
diff --git a/node_modules/tablednd/server/ajaxJSONTest.php b/node_modules/tablednd/server/ajaxJSONTest.php
new file mode 100644
index 0000000000..60b2a63fce
--- /dev/null
+++ b/node_modules/tablednd/server/ajaxJSONTest.php
@@ -0,0 +1,13 @@
+The server says: your row order was
+";
+ if (isset($result["$value"]))
+ show_results($result, $value, $indent.implode(' ', array_fill(0, 12, '')));
+ }
+}
+?>
+See the PHP Source
diff --git a/node_modules/tablednd/server/ajaxJSONTest_php.html b/node_modules/tablednd/server/ajaxJSONTest_php.html
new file mode 100644
index 0000000000..cc648a7252
--- /dev/null
+++ b/node_modules/tablednd/server/ajaxJSONTest_php.html
@@ -0,0 +1,3 @@
+
+The server says: your row order was<br/> <?php $result = json_decode(file_get_contents('php://input'), true); show_results($result, "table-7"); function show_results($result, $id, $indent = null) { foreach($result[$id] as $value) { echo "$indent$value<br/>"; if (isset($result["$value"])) show_results($result, $value, $indent.implode(' ', array_fill(0, 12, ''))); } } ?> See the <a href="server/ajaxJSONTest_php.html" target="_BLANK">PHP Source</a><br/>
+
\ No newline at end of file
diff --git a/node_modules/tablednd/server/ajaxTest.php b/node_modules/tablednd/server/ajaxTest.php
new file mode 100644
index 0000000000..6bf4f45bac
--- /dev/null
+++ b/node_modules/tablednd/server/ajaxTest.php
@@ -0,0 +1,8 @@
+The server says: your row order was
+";
+}
+?>
+See the PHP Source
\ No newline at end of file
diff --git a/node_modules/tablednd/server/ajaxTest_php.html b/node_modules/tablednd/server/ajaxTest_php.html
new file mode 100644
index 0000000000..e3b23064cb
--- /dev/null
+++ b/node_modules/tablednd/server/ajaxTest_php.html
@@ -0,0 +1,4 @@
+
+
+The server says: your row order was<br/> <?php $result = $_REQUEST["table-3"]; foreach($result as $value) { echo "$value<br/>"; } ?> See the <a href="server/ajaxTest_php.html" target="_BLANK">PHP Source</a><br/>
+
diff --git a/node_modules/tablednd/serverExample.html b/node_modules/tablednd/serverExample.html
new file mode 100644
index 0000000000..72ca39ff30
--- /dev/null
+++ b/node_modules/tablednd/serverExample.html
@@ -0,0 +1,39 @@
+
+
+
+
+ TableDnD Server Example
+
+
+
+
+
+
+
+
1
One
some text
+
2
Two
some text
+
3
Three
some text
+
4
Four
some text
+
5
Five
some text
+
6
Six
some text
+
+
+
+
+
+
+
+
diff --git a/node_modules/tablednd/stripe.html b/node_modules/tablednd/stripe.html
new file mode 100644
index 0000000000..1bc5cc24f0
--- /dev/null
+++ b/node_modules/tablednd/stripe.html
@@ -0,0 +1,294 @@
+
+
+
+ Table Drag and Drop jQuery plugin
+
+
+
+
+
Table Drag and Drop jQuery plugin
+
This page contains documentation and tests for the TableDnD jQuery plug-in. For more information and
+to post comments, please go to isocra.com.
+
+
If you have issues or bug reports, then you can post them at the TableDnD plug page
+at plugins.jquery.com
Reference both scripts in your HTML page in the normal way.
+
In true jQuery style, the typical way to initialise the tabes is in the $(document).ready function. Use a selector to select your table and then call tableDnD(). You can optionally specify a set of properties (described below).
+
+
+
+
+
1
One
some text
+
2
Two
some text
+
3
Three
some text
+
4
Four
some text
+
5
Five
some text
+
6
Six
some text
+
+
+
The HTML for the table is very straight forward (no Javascript, pure HTML):
In the example above we're not setting any parameters at all so we get the default settings. There are a number
+ of parameters you can set in order to control the look and feel of the table and also to add custom behaviour
+ on drag or on drop. The parameters are specified as a map in the usual way and are described below:
+
+
+
onDragStyle
+
This is the style that is assigned to the row during drag. There are limitations to the styles that can be
+ associated with a row (such as you can't assign a border—well you can, but it won't be
+ displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
+ a map (as used in the jQuery css(...) function).
+
onDropStyle
+
This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
+ to what you can do. Also this replaces the original style, so again consider using onDragClass which
+ is simply added and then removed on drop.
+
onDragClass
+
This class is added for the duration of the drag and then removed when the row is dropped. It is more
+ flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
+ is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
+ stylesheet.
+
onDrop
+
Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
+ and the row that was dropped. You can work out the new order of the rows by using
+ table.tBodies[0].rows.
+
onDragStart
+
Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
+ table and the row which the user has started to drag.
+
scrollAmount
+
This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
+ window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
+ FF3 beta)
+
+
This second table has has an onDrop function applied as well as an onDragClass. The javascript to set this up is
+as follows:
+
+$(document).ready(function() {
+
+ // Initialise the first table (as before)
+ $("#table-1").tableDnD();
+
+ // Make a nice striped effect on the table
+ $("#table-2 tr:even').addClass('alt')");
+
+ // Initialise the second table specifying a dragClass and an onDrop function that will display an alert
+ $("#table-2").tableDnD({
+ onDragClass: "myDragClass",
+ onDrop: function(table, row) {
+ var rows = table.tBodies[0].rows;
+ var debugStr = "Row dropped was "+row.id+". New order: ";
+ for (var i=0; i<rows.length; i++) {
+ debugStr += rows[i].id+" ";
+ }
+ $(#debugArea).html(debugStr);
+ },
+ onDragStart: function(table, row) {
+ $(#debugArea).html("Started dragging row "+row.id);
+ }
+ });
+});
+
+
+
+
+
1
One
+
2
Two
+
3
Three
+
4
Four
+
5
Five
+
6
Six
+
7
Seven
+
8
Eight
+
9
Nine
+
10
Ten
+
11
Eleven
+
12
Twelve
+
13
Thirteen
+
14
Fourteen
+
+
+
What to do afterwards?
+
Generally once the user has dropped a row, you need to inform the server of the new order. To do this, we've
+ added a method called serialise(). It takes no parameters but knows the current table from the
+ context. The method returns a string of the form tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3...
+ You can then use this as part of an Ajax load.
+
+
This third table demonstrates calling the serialise function inside onDrop (as shown below). It also
+ demonstrates the "nodrop" class on row 3 and "nodrag" class on row 5, so you can't pick up row 5 and
+ you can't drop any row on row 3 (but you can drag it).
Drag and drop in this table to test out serialise and using JQuery.load()
+
+
+
1
One
+
2
Two
+
3
Three (Can't drop on this row)
+
4
Four
+
5
Five (Can't drag this row)
+
6
Six
+
+
+
This table has multiple TBODYs. The functionality isn't quite working properly. You can only drag the rows inside their
+own TBODY, you can't drag them outside it. Now this might or might not be what you want, but unfortunately if you then drop a row outside its TBODY you get a Javascript error because inserting after a sibling doesn't work. This will be fixed in the next version. The header rows all have the classes "nodrop" and "nodrag" so that they can't be dragged or dropped on.
+
+
+
+
H1
H2
H3
+
4.1
One
+
4.2
Two
+
4.3
Three
+
4.4
Four
+
4.5
Five
+
4.6
Six
+
+
+
H1
H2
H3
+
5.1
One
+
5.2
Two
+
5.3
Three
+
5.4
Four
+
5.5
Five
+
5.6
Six
+
+
+
H1
H2
H3
+
6.1
One
+
6.2
Two
+
6.3
Three
+
6.4
Four
+
6.5
Five
+
6.6
Six
+
+
+
+
+The following table demonstrates the use of the default regular expression. The rows have IDs of the
+form table5-row-1, table5-row-2, etc., but the regular expression is /[^\-]*$/ (this is the same
+as used in the NestedSortable plugin for consistency).
+This removes everything before and including the last hyphen, so the serialised string just has 1, 2, 3 etc.
+You can replace the regular expression by setting the serializeRegexp option, you can also just set it
+to null to stop this behaviour.
+
In fact you will notice that I have also set the dragHandle on this table. This has two effects: firstly only
+the cell with the drag handle class is draggable and secondly it doesn't automatically add the cursor: move
+style to the row (or the drag handle cell), so you are responsible for setting up the style as you see fit.
+
Here I've actually added an extra effect which adds a background image to the first cell in the row whenever
+you enter it using the jQuery hover function as follows:
This provides a better visualisation of what you can do to the row and where you need to go to drag it (I hope).
+
Version History
+
+
0.2
2008-02-20
First public release
+
0.3
2008-02-27
Added onDragStart option Made the scroll amount configurable (default is 5 as before)
+
0.4
2008-03-28
Fixed the scrollAmount so that if you set this to zero then it switches off this functionality Fixed the auto-scrolling in IE6 thanks to Phil Changed the NoDrop attribute to the class "nodrop" (so any row with this class won't allow dropping) Changed the NoDrag attribute to the class "nodrag" (so any row with this class can't be dragged) Added support for multiple TBODYs--though it's still not perfect Added onAllowDrop to allow the developer to customise this behaviour Added a serialize() method to return the order of the rows in a form suitable for POSTing back to the server
+
0.5
2008-06-04
Changed so that if you specify a dragHandle class it doesn't make the whole row draggable Improved the serialize method to use a default (and settable) regular expression. Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table