From 6ef72a033b9b64d5330c328b33db065b4b2d4a01 Mon Sep 17 00:00:00 2001 From: fappels Date: Tue, 7 Nov 2017 16:15:59 +0100 Subject: [PATCH 1/4] Update tablednd jquery plugin to latest git master --- .../jquery/plugins/tablednd/README.txt | 2 +- .../plugins/tablednd/jquery.tablednd.0.6.js | 400 ----------- .../tablednd/jquery.tablednd.0.6.min.js | 1 - .../plugins/tablednd/jquery.tablednd.js | 675 ++++++++++++++++++ .../plugins/tablednd/jquery.tablednd.min.js | 1 + htdocs/main.inc.php | 2 +- 6 files changed, 678 insertions(+), 403 deletions(-) delete mode 100644 htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.js delete mode 100644 htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.min.js create mode 100644 htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js create mode 100644 htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js diff --git a/htdocs/includes/jquery/plugins/tablednd/README.txt b/htdocs/includes/jquery/plugins/tablednd/README.txt index 1fab924b7f0..d6c3c823993 100644 --- a/htdocs/includes/jquery/plugins/tablednd/README.txt +++ b/htdocs/includes/jquery/plugins/tablednd/README.txt @@ -1,3 +1,3 @@ ----- jquery.tablednd ----- - New versions are found into directory js of https://github.com/isocra/TableDnD -- Version 0.7, 0.8 seems to be not compatible. \ No newline at end of file +- Version 0.9.1 not compatible with jquery 3.0+, so master git branch used. \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.js deleted file mode 100644 index 34f3c88232b..00000000000 --- a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.js +++ /dev/null @@ -1,400 +0,0 @@ -/** - * 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 the name of a class that you assign to one or more cells in each row that is draggable. If you - * specify this class, 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 - */ -// 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'; - -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 Y so that we don't do too much processing */ - 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 = jQuery.extend({ - onDragStyle: null, - onDropStyle: null, - // Add in the default class for whileDragging - onDragClass: "tDnD_whileDrag", - onDrop: null, - onDragStart: null, - scrollAmount: 5, - - serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs - serializeParamName: null, // If you want to specify another parameter name instead of the table ID - dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable - }, options || {}); - // Now make the rows draggable - jQuery.tableDnD.makeDraggable(this); - }); - - // Don't break the chain - 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; - if (config.dragHandle) { - // We only need to add the event to the specified cells - var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table); - cells.each(function() { - // The cell is bound to "this" - jQuery(this).bind(startEvent, function(ev) { - jQuery.tableDnD.initialiseDrag(this.parentNode, table, this, ev, config); - return false; - }); - }) - } else { - // For backwards compatibility, we add the event to the whole row - var rows = jQuery("tr", table); // get all the rows as a wrapped set - rows.each(function() { - // Iterate through each row, the row is bound to "this" - 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"); // Store the tableDnD object - } - }); - } - }, - - 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(); - // 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 - jQuery(document) - .bind(moveEvent, jQuery.tableDnD.mousemove) - .bind(endEvent, jQuery.tableDnD.mouseup); - if (config.onDragStart) { - // Call the onDragStart method if there is one - config.onDragStart(table, target); - } - }, - - updateTables: function() { - this.each(function() { - // this is now bound to each matching table - if (this.tableDnDConfig) { - jQuery.tableDnD.makeDraggable(this); - } - }) - }, - - /** Get the mouse coordinates from the event (allowing for browser differences) */ - 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 - }; - }, - - /** Given a target element and a mouse event, get the mouse offset from that element. - To do this we need the element's position and the mouse position */ - 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}; - }, - - /** Get the position of an element by going up the DOM tree and adding up all the offsets */ - getPosition: function(e){ - var left = 0; - var top = 0; - /** Safari fix -- thanks to Luis Chato for this! */ - if (e.offsetHeight == 0) { - /** 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 */ - e = e.firstChild; // a table cell - } - - 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') { - // prevent touch device screen scrolling - 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; - //auto scroll the window - var yOffset = window.pageYOffset; - if (document.all) { - // Windows version - //yOffset=document.body.scrollTop; - 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-yOffset < config.scrollAmount) { - window.scrollBy(0, -config.scrollAmount); - } else { - var windowHeight = window.innerHeight ? window.innerHeight - : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; - if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) { - window.scrollBy(0, config.scrollAmount); - } - } - - - if (y != jQuery.tableDnD.oldY) { - // work out if we're going up or down... - var movingDown = y > jQuery.tableDnD.oldY; - // update the old value - jQuery.tableDnD.oldY = y; - // update the style to show we're dragging - if (config.onDragClass) { - dragObj.addClass(config.onDragClass); - } else { - dragObj.css(config.onDragStyle); - } - // If we're over a row then move the dragged row to there so that the user sees the - // effect dynamically - var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); - if (currentRow) { - // TODO worry about what happens when there are multiple TBODIES - 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; - }, - - /** We're only worried about the y position really, because we can only move rows up and down */ - findDropTargetRow: function(draggedRow, y) { - var rows = jQuery.tableDnD.currentTable.rows; - for (var i=0; i 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) {return null;} - var config = jQuery.tableDnD.currentTable.tableDnDConfig; - if (config.onAllowDrop) { - if (config.onAllowDrop(draggedRow, row)) { - return row; - } else { - return null; - } - } else { - // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) - 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) { - // Unbind the event handlers - jQuery(document) - .unbind(moveEvent, jQuery.tableDnD.mousemove) - .unbind(endEvent, jQuery.tableDnD.mouseup); - var droppedRow = jQuery.tableDnD.dragObject; - var config = jQuery.tableDnD.currentTable.tableDnDConfig; - // 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 - 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)) { - // Call the onDrop method if there is one - config.onDrop(jQuery.tableDnD.currentTable, droppedRow); - } - jQuery.tableDnD.currentTable = null; // let go of the table too - } - }, - - 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; i 0) 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() { - // this is now bound to each matching table - 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/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.min.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.min.js deleted file mode 100644 index 7201855a3c9..00000000000 --- a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.0.6.min.js +++ /dev/null @@ -1 +0,0 @@ -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/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js new file mode 100644 index 00000000000..5ac2cce6524 --- /dev/null +++ b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.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); \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js new file mode 100644 index 00000000000..be2c3a66b19 --- /dev/null +++ b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js @@ -0,0 +1 @@ +!function(t,e,a,n){a.documentElement;t(a).ready(function(){function e(t){for(var e={},a=t.match(/([^;:]+)/g)||[];a.length;)e[a.shift()]=a.shift().trim();return e}t("table").each(function(){"dnd"==t(this).data("table")&&t(this).tableDnD({onDragStyle:t(this).data("ondragstyle")&&e(t(this).data("ondragstyle"))||null,onDropStyle:t(this).data("ondropstyle")&&e(t(this).data("ondropstyle"))||null,onDragClass:void 0==t(this).data("ondragclass")&&"tDnD_whileDrag"||t(this).data("ondragclass"),onDrop:t(this).data("ondrop")&&new Function("table","row",t(this).data("ondrop")),onDragStart:t(this).data("ondragstart")&&new Function("table","row",t(this).data("ondragstart")),onDragStop:t(this).data("ondragstop")&&new Function("table","row",t(this).data("ondragstop")),scrollAmount:t(this).data("scrollamount")||5,sensitivity:t(this).data("sensitivity")||10,hierarchyLevel:t(this).data("hierarchylevel")||0,indentArtifact:t(this).data("indentartifact")||'
 
',autoWidthAdjust:t(this).data("autowidthadjust")||!0,autoCleanRelations:t(this).data("autocleanrelations")||!0,jsonPretifySeparator:t(this).data("jsonpretifyseparator")||"\t",serializeRegexp:t(this).data("serializeregexp")&&new RegExp(t(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:t(this).data("serializeparamname")||!1,dragHandle:t(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(e){return this.each(function(){this.tableDnDConfig=t.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
 
',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},e||{}),t.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&t.tableDnD.makeIndented(this)}),this},makeIndented:function(e){var a,n,i=e.tableDnDConfig,r=e.rows,l=t(r).first().find("td:first")[0],o=0,s=0;if(t(e).hasClass("indtd"))return null;n=t(e).addClass("indtd").attr("style"),t(e).css({whiteSpace:"nowrap"});for(var d=0;dt.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,e.nextSibling)||00&&t(a).find("td:first").children(":first").remove()&&t(a).data("level",--n),0>e.horizontal&&n=n&&t(a).children(":first").prepend(i.indentArtifact)&&t(a).data("level",++n)},mousemove:function(e){var a,n,i,r,l,o=t(t.tableDnD.dragObject),s=t.tableDnD.currentTable.tableDnDConfig;return e&&e.preventDefault(),!!t.tableDnD.dragObject&&("touchmove"==e.type&&event.preventDefault(),s.onDragClass&&o.addClass(s.onDragClass)||o.css(s.onDragStyle),n=t.tableDnD.mouseCoords(e),r=n.x-t.tableDnD.mouseOffset.x,l=n.y-t.tableDnD.mouseOffset.y,t.tableDnD.autoScroll(n),a=t.tableDnD.findDropTargetRow(o,l),i=t.tableDnD.findDragDirection(r,l),t.tableDnD.moveVerticle(i,a),t.tableDnD.moveHorizontal(i,a),!1)},findDragDirection:function(t,e){var a=this.currentTable.tableDnDConfig.sensitivity,n=this.oldX,i=this.oldY,r=n+a,l=i-a,o=i+a,s={horizontal:t>=n-a&&t<=r?0:t>n?-1:1,vertical:e>=l&&e<=o?0:e>i?-1:1};return 0!=s.horizontal&&(this.oldX=t),0!=s.vertical&&(this.oldY=e),s},findDropTargetRow:function(e,a){for(var n=0,i=this.currentTable.rows,r=this.currentTable.tableDnDConfig,l=0,o=null,s=0;sl-n&&a1&&t(this.currentTable.rows).each(function(){if((r=t(this).data("level"))>1)for(i=t(this).prev().data("level");r>i+1;)t(this).find("td:first").children(":first").remove(),t(this).data("level",--r)}),e.onDragClass&&t(n).removeClass(e.onDragClass)||t(n).css(e.onDropStyle),this.dragObject=null,e.onDrop&&this.originalOrder!=this.currentOrder()&&t(n).hide().fadeIn("fast")&&e.onDrop(this.currentTable,n),e.onDragStop&&e.onDragStop(this.currentTable,n),this.currentTable=null},mouseup:function(e){return e&&e.preventDefault(),t.tableDnD.processMouseup(),!1},jsonize:function(t){var e=this.currentTable;return t?JSON.stringify(this.tableData(e),null,e.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(e))},serialize:function(){return t.param(this.tableData(this.currentTable))},serializeTable:function(t){for(var e="",a=t.tableDnDConfig.serializeParamName||t.id,n=t.rows,i=0;i0&&(e+="&");var r=n[i].id;r&&t.tableDnDConfig&&t.tableDnDConfig.serializeRegexp&&(e+=a+"[]="+(r=r.match(t.tableDnDConfig.serializeRegexp)[0]))}return e},serializeTables:function(){var e=[];return t("table").each(function(){this.id&&e.push(t.param(t.tableDnD.tableData(this)))}),e.join("&")},tableData:function(e){var a,n,i,r,l=e.tableDnDConfig,o=[],s=0,d=0,h=null,u={};if(e||(e=this.currentTable),!e||!e.rows||!e.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!e.id&&!l.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};r=l.autoCleanRelations&&e.rows||t.makeArray(e.rows),a=function(t){return t&&l&&l.serializeRegexp?t.match(l.serializeRegexp)[0]:t},u[i=n=l.serializeParamName||e.id]=[],!l.autoCleanRelations&&t(r[0]).data("level")&&r.unshift({id:"undefined"});for(var c=0;cs)o.push([i,s]),i=a(r[c-1].id);else if(d=s&&(o[f][1]=0);s=d,t.isArray(u[i])||(u[i]=[]),(h=a(r[c].id))&&u[i].push(h)}else(h=a(r[c].id))&&u[i].push(h);return u}},jQuery.fn.extend({tableDnD:t.tableDnD.build,tableDnDUpdate:t.tableDnD.updateTables,tableDnDSerialize:t.proxy(t.tableDnD.serialize,t.tableDnD),tableDnDSerializeAll:t.tableDnD.serializeTables,tableDnDData:t.proxy(t.tableDnD.tableData,t.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index e98a306390e..f43a2f935c8 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1223,7 +1223,7 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs } if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) print ''."\n"; else print ''."\n"; - if (! defined('DISABLE_JQUERY_TABLEDND')) print ''."\n"; + if (! defined('DISABLE_JQUERY_TABLEDND')) print ''."\n"; // jQuery jnotify if (empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && ! defined('DISABLE_JQUERY_JNOTIFY')) { From 14bc0664544db895984ba681967213f85610cc5a Mon Sep 17 00:00:00 2001 From: fappels Date: Tue, 7 Nov 2017 16:25:57 +0100 Subject: [PATCH 2/4] Update for new tablednd compatibility --- ChangeLog | 2 ++ htdocs/core/tpl/ajaxrow.tpl.php | 6 +++--- htdocs/variants/list.php | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index aa600bdacef..04e1c5fbcc8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,8 @@ Following changes may create regressions for some external modules, but were nec * The parameter note into method cloture() is added at end of private note (previously in v6, it replaced) * The parameter $user is now mandatory for method createFromOrder and createFromPropal. * Remove js library fileupload that was not used by core code. +* Jquery plugin tableDnd updated. You now need to use decodeURI on the return value of tableDnDSerialize() + and add 'td.' to the beginning of the dragHandle match string. ***** ChangeLog for 6.0.3 compared to 6.0.2 ***** diff --git a/htdocs/core/tpl/ajaxrow.tpl.php b/htdocs/core/tpl/ajaxrow.tpl.php index a5d8ab039b8..49d69d6afcb 100644 --- a/htdocs/core/tpl/ajaxrow.tpl.php +++ b/htdocs/core/tpl/ajaxrow.tpl.php @@ -46,8 +46,8 @@ $(document).ready(function(){ onDrop: function(table, row) { var reloadpage = ""; console.log("tableDND onDrop"); - console.log($("#").tableDnDSerialize()); - var roworder = cleanSerialize($("#").tableDnDSerialize()); + console.log(decodeURI($("#").tableDnDSerialize())); + var roworder = cleanSerialize(decodeURI($("#").tableDnDSerialize())); var table_element_line = ""; var fk_element = ""; var element_id = ""; @@ -75,7 +75,7 @@ $(document).ready(function(){ }); }, onDragClass: "dragClass", - dragHandle: "tdlineupdown" + dragHandle: "td.tdlineupdown" }); $(".tdlineupdown").hover( function() { $(this).addClass('showDragHandle'); }, function() { $(this).removeClass('showDragHandle'); } diff --git a/htdocs/variants/list.php b/htdocs/variants/list.php index ca9595da131..2e97dda0a04 100644 --- a/htdocs/variants/list.php +++ b/htdocs/variants/list.php @@ -81,7 +81,7 @@ $forcereloadpage=empty($conf->global->MAIN_FORCE_RELOAD_PAGE)?0:1; onDrop: function(table, row) { console.log('drop'); var reloadpage = ""; - var roworder = cleanSerialize($("#tablelines").tableDnDSerialize()); + var roworder = cleanSerialize(decodeURI($("#tablelines").tableDnDSerialize())); $.post("/variants/ajax/orderAttribute.php", { roworder: roworder @@ -100,7 +100,7 @@ $forcereloadpage=empty($conf->global->MAIN_FORCE_RELOAD_PAGE)?0:1; }); }, onDragClass: "dragClass", - dragHandle: "tdlineupdown" + dragHandle: "td.tdlineupdown" }); }); From 37caa28e7e883ddc82c86b5e9bad0688cbe6eb29 Mon Sep 17 00:00:00 2001 From: fappels Date: Mon, 20 Nov 2017 10:25:02 +0100 Subject: [PATCH 3/4] Update tablednd to v1.0 RC --- .../jquery/plugins/tablednd/README.txt | 4 +- .../plugins/tablednd/jquery.tablednd.js | 1245 ++++++++--------- .../plugins/tablednd/jquery.tablednd.min.js | 3 +- 3 files changed, 591 insertions(+), 661 deletions(-) diff --git a/htdocs/includes/jquery/plugins/tablednd/README.txt b/htdocs/includes/jquery/plugins/tablednd/README.txt index d6c3c823993..9de4f5db79e 100644 --- a/htdocs/includes/jquery/plugins/tablednd/README.txt +++ b/htdocs/includes/jquery/plugins/tablednd/README.txt @@ -1,3 +1,5 @@ ----- jquery.tablednd ----- - New versions are found into directory js of https://github.com/isocra/TableDnD -- Version 0.9.1 not compatible with jquery 3.0+, so master git branch used. \ No newline at end of file +- Distribution available on 'npm install tablednd' +- Current version V1.0 RC for 7.0 alpha +- TODO update to V1.0 for 7.0 beta \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js index 5ac2cce6524..f00d3940456 100644 --- a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js +++ b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js @@ -2,674 +2,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 - * 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 + * License: MIT. + * See https://github.com/isocra/TableDnD */ + +/*jshint -W054 */ + !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; +// 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 + }); } - $('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; - } +}); + +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; - }, - 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; - } + + 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; } - return result; - }, - serializeTables: function() { - var result = []; - $('table').each(function() { - this.id && result.push($.param($.tableDnD.tableData(this))); + } + $(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 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; + + 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; } - } - 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); + }).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; } - return data; + // 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; } - }; - - jQuery.fn.extend( - { - tableDnD : $.tableDnD.build, - tableDnDUpdate : $.tableDnD.updateTables, - tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), - tableDnDSerializeAll : $.tableDnD.serializeTables, - tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) + 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; + } } - ); - - }(jQuery, window, window.document); \ No newline at end of file + 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/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js index be2c3a66b19..c07ff347456 100644 --- a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js +++ b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.min.js @@ -1 +1,2 @@ -!function(t,e,a,n){a.documentElement;t(a).ready(function(){function e(t){for(var e={},a=t.match(/([^;:]+)/g)||[];a.length;)e[a.shift()]=a.shift().trim();return e}t("table").each(function(){"dnd"==t(this).data("table")&&t(this).tableDnD({onDragStyle:t(this).data("ondragstyle")&&e(t(this).data("ondragstyle"))||null,onDropStyle:t(this).data("ondropstyle")&&e(t(this).data("ondropstyle"))||null,onDragClass:void 0==t(this).data("ondragclass")&&"tDnD_whileDrag"||t(this).data("ondragclass"),onDrop:t(this).data("ondrop")&&new Function("table","row",t(this).data("ondrop")),onDragStart:t(this).data("ondragstart")&&new Function("table","row",t(this).data("ondragstart")),onDragStop:t(this).data("ondragstop")&&new Function("table","row",t(this).data("ondragstop")),scrollAmount:t(this).data("scrollamount")||5,sensitivity:t(this).data("sensitivity")||10,hierarchyLevel:t(this).data("hierarchylevel")||0,indentArtifact:t(this).data("indentartifact")||'
 
',autoWidthAdjust:t(this).data("autowidthadjust")||!0,autoCleanRelations:t(this).data("autocleanrelations")||!0,jsonPretifySeparator:t(this).data("jsonpretifyseparator")||"\t",serializeRegexp:t(this).data("serializeregexp")&&new RegExp(t(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:t(this).data("serializeparamname")||!1,dragHandle:t(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(e){return this.each(function(){this.tableDnDConfig=t.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
 
',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},e||{}),t.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&t.tableDnD.makeIndented(this)}),this},makeIndented:function(e){var a,n,i=e.tableDnDConfig,r=e.rows,l=t(r).first().find("td:first")[0],o=0,s=0;if(t(e).hasClass("indtd"))return null;n=t(e).addClass("indtd").attr("style"),t(e).css({whiteSpace:"nowrap"});for(var d=0;dt.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,e.nextSibling)||00&&t(a).find("td:first").children(":first").remove()&&t(a).data("level",--n),0>e.horizontal&&n=n&&t(a).children(":first").prepend(i.indentArtifact)&&t(a).data("level",++n)},mousemove:function(e){var a,n,i,r,l,o=t(t.tableDnD.dragObject),s=t.tableDnD.currentTable.tableDnDConfig;return e&&e.preventDefault(),!!t.tableDnD.dragObject&&("touchmove"==e.type&&event.preventDefault(),s.onDragClass&&o.addClass(s.onDragClass)||o.css(s.onDragStyle),n=t.tableDnD.mouseCoords(e),r=n.x-t.tableDnD.mouseOffset.x,l=n.y-t.tableDnD.mouseOffset.y,t.tableDnD.autoScroll(n),a=t.tableDnD.findDropTargetRow(o,l),i=t.tableDnD.findDragDirection(r,l),t.tableDnD.moveVerticle(i,a),t.tableDnD.moveHorizontal(i,a),!1)},findDragDirection:function(t,e){var a=this.currentTable.tableDnDConfig.sensitivity,n=this.oldX,i=this.oldY,r=n+a,l=i-a,o=i+a,s={horizontal:t>=n-a&&t<=r?0:t>n?-1:1,vertical:e>=l&&e<=o?0:e>i?-1:1};return 0!=s.horizontal&&(this.oldX=t),0!=s.vertical&&(this.oldY=e),s},findDropTargetRow:function(e,a){for(var n=0,i=this.currentTable.rows,r=this.currentTable.tableDnDConfig,l=0,o=null,s=0;sl-n&&a1&&t(this.currentTable.rows).each(function(){if((r=t(this).data("level"))>1)for(i=t(this).prev().data("level");r>i+1;)t(this).find("td:first").children(":first").remove(),t(this).data("level",--r)}),e.onDragClass&&t(n).removeClass(e.onDragClass)||t(n).css(e.onDropStyle),this.dragObject=null,e.onDrop&&this.originalOrder!=this.currentOrder()&&t(n).hide().fadeIn("fast")&&e.onDrop(this.currentTable,n),e.onDragStop&&e.onDragStop(this.currentTable,n),this.currentTable=null},mouseup:function(e){return e&&e.preventDefault(),t.tableDnD.processMouseup(),!1},jsonize:function(t){var e=this.currentTable;return t?JSON.stringify(this.tableData(e),null,e.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(e))},serialize:function(){return t.param(this.tableData(this.currentTable))},serializeTable:function(t){for(var e="",a=t.tableDnDConfig.serializeParamName||t.id,n=t.rows,i=0;i0&&(e+="&");var r=n[i].id;r&&t.tableDnDConfig&&t.tableDnDConfig.serializeRegexp&&(e+=a+"[]="+(r=r.match(t.tableDnDConfig.serializeRegexp)[0]))}return e},serializeTables:function(){var e=[];return t("table").each(function(){this.id&&e.push(t.param(t.tableDnD.tableData(this)))}),e.join("&")},tableData:function(e){var a,n,i,r,l=e.tableDnDConfig,o=[],s=0,d=0,h=null,u={};if(e||(e=this.currentTable),!e||!e.rows||!e.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!e.id&&!l.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};r=l.autoCleanRelations&&e.rows||t.makeArray(e.rows),a=function(t){return t&&l&&l.serializeRegexp?t.match(l.serializeRegexp)[0]:t},u[i=n=l.serializeParamName||e.id]=[],!l.autoCleanRelations&&t(r[0]).data("level")&&r.unshift({id:"undefined"});for(var c=0;cs)o.push([i,s]),i=a(r[c-1].id);else if(d=s&&(o[f][1]=0);s=d,t.isArray(u[i])||(u[i]=[]),(h=a(r[c].id))&&u[i].push(h)}else(h=a(r[c].id))&&u[i].push(h);return u}},jQuery.fn.extend({tableDnD:t.tableDnD.build,tableDnDUpdate:t.tableDnD.updateTables,tableDnDSerialize:t.proxy(t.tableDnD.serialize,t.tableDnD),tableDnDSerializeAll:t.tableDnD.serializeTables,tableDnDData:t.proxy(t.tableDnD.tableData,t.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file +/*! jquery.tablednd.js 19-11-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:a(this).data("autowidthadjust")||!0,autoCleanRelations:a(this).data("autocleanrelations")||!0,jsonPretifySeparator:a(this).data("jsonpretifyseparator")||" ",serializeRegexp:a(this).data("serializeregexp")&&new RegExp(a(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:a(this).data("serializeparamname")||!1,dragHandle:a(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(b){return this.each(function(){this.tableDnDConfig=a.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,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 From 8d318337d9f11ac586233112d449ddf501962341 Mon Sep 17 00:00:00 2001 From: fappels Date: Mon, 20 Nov 2017 14:56:12 +0100 Subject: [PATCH 4/4] Check hound barking --- .../jquery/plugins/tablednd/jquery.tablednd.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js index f00d3940456..4a6923f1336 100644 --- a/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js +++ b/htdocs/includes/jquery/plugins/tablednd/jquery.tablednd.js @@ -7,6 +7,8 @@ */ /*jshint -W054 */ +/*jshint laxbreak: true */ +/*jshint expr: true */ !function ($, window, document, undefined) { // Determine if this is a touch device @@ -25,7 +27,7 @@ $(document).ready(function () { return objMap; } $('table').each(function () { - if ($(this).data('table') == 'dnd') { + if ($(this).data('table') === 'dnd') { $(this).tableDnD({ onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, @@ -168,7 +170,7 @@ jQuery.tableDnD = { // Iterate through each row, the row is bound to "this" if (! $(this).hasClass("nodrag")) { $(this).bind(startEvent, function(e) { - if (e.target.tagName == "TD") { + if (e.target.tagName === "TD") { $.tableDnD.initialiseDrag(this, table, this, e, config); return false; } @@ -283,10 +285,10 @@ jQuery.tableDnD = { // Windows version // yOffset=document.body.scrollTop; if (document.all) - if (typeof document.compatMode != 'undefined' - && document.compatMode != 'BackCompat') + if (typeof document.compatMode !== 'undefined' + && document.compatMode !== 'BackCompat') yOffset = document.documentElement.scrollTop; - else if (typeof document.body != 'undefined') + else if (typeof document.body !== 'undefined') yOffset = document.body.scrollTop; mousePos.y - yOffset < config.scrollAmount