|
1 /** |
|
2 * TableDnD plug-in for JQuery, allows you to drag and drop table rows |
|
3 * You can set up various options to control how the system will work |
|
4 * Copyright (c) Denis Howlett <denish@isocra.com> |
|
5 * Licensed like jQuery, see http://docs.jquery.com/License. |
|
6 * |
|
7 * Configuration options: |
|
8 * |
|
9 * onDragStyle |
|
10 * This is the style that is assigned to the row during drag. There are limitations to the styles that can be |
|
11 * associated with a row (such as you can't assign a border--well you can, but it won't be |
|
12 * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as |
|
13 * a map (as used in the jQuery css(...) function). |
|
14 * onDropStyle |
|
15 * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations |
|
16 * to what you can do. Also this replaces the original style, so again consider using onDragClass which |
|
17 * is simply added and then removed on drop. |
|
18 * onDragClass |
|
19 * This class is added for the duration of the drag and then removed when the row is dropped. It is more |
|
20 * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default |
|
21 * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your |
|
22 * stylesheet. |
|
23 * onDrop |
|
24 * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table |
|
25 * and the row that was dropped. You can work out the new order of the rows by using |
|
26 * table.rows. |
|
27 * onDragStart |
|
28 * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the |
|
29 * table and the row which the user has started to drag. |
|
30 * onAllowDrop |
|
31 * Pass a function that will be called as a row is over another row. If the function returns true, allow |
|
32 * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under |
|
33 * the cursor. It returns a boolean: true allows the drop, false doesn't allow it. |
|
34 * scrollAmount |
|
35 * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the |
|
36 * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, |
|
37 * FF3 beta |
|
38 * dragHandle |
|
39 * This is a jQuery mach string for one or more cells in each row that is draggable. If you |
|
40 * specify this, then you are responsible for setting cursor: move in the CSS and only these cells |
|
41 * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where |
|
42 * the whole row is draggable. |
|
43 * |
|
44 * Other ways to control behaviour: |
|
45 * |
|
46 * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows |
|
47 * that you don't want to be draggable. |
|
48 * |
|
49 * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form |
|
50 * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have |
|
51 * an ID as must all the rows. |
|
52 * |
|
53 * Other methods: |
|
54 * |
|
55 * $("...").tableDnDUpdate() |
|
56 * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells). |
|
57 * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again. |
|
58 * The table maintains the original configuration (so you don't have to specify it again). |
|
59 * |
|
60 * $("...").tableDnDSerialize() |
|
61 * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be |
|
62 * called from anywhere and isn't dependent on the currentTable being set up correctly before calling |
|
63 * |
|
64 * Known problems: |
|
65 * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0 |
|
66 * |
|
67 * Version 0.2: 2008-02-20 First public version |
|
68 * Version 0.3: 2008-02-07 Added onDragStart option |
|
69 * Made the scroll amount configurable (default is 5 as before) |
|
70 * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes |
|
71 * Added onAllowDrop to control dropping |
|
72 * Fixed a bug which meant that you couldn't set the scroll amount in both directions |
|
73 * Added serialize method |
|
74 * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row |
|
75 * draggable |
|
76 * Improved the serialize method to use a default (and settable) regular expression. |
|
77 * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table |
|
78 * Version 0.6: 2011-12-02 Added support for touch devices |
|
79 * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces |
|
80 */ |
|
81 !function ($, window, document, undefined) { |
|
82 // Determine if this is a touch device |
|
83 var hasTouch = 'ontouchstart' in document.documentElement, |
|
84 startEvent = 'touchstart mousedown', |
|
85 moveEvent = 'touchmove mousemove', |
|
86 endEvent = 'touchend mouseup'; |
|
87 |
|
88 // If we're on a touch device, then wire up the events |
|
89 // see http://stackoverflow.com/a/8456194/1316086 |
|
90 hasTouch |
|
91 && $.each("touchstart touchmove touchend".split(" "), function(i, name) { |
|
92 $.event.fixHooks[name] = $.event.mouseHooks; |
|
93 }); |
|
94 |
|
95 |
|
96 $(document).ready(function () { |
|
97 function parseStyle(css) { |
|
98 var objMap = {}, |
|
99 parts = css.match(/([^;:]+)/g) || []; |
|
100 while (parts.length) |
|
101 objMap[parts.shift()] = parts.shift().trim(); |
|
102 |
|
103 return objMap; |
|
104 } |
|
105 $('table').each(function () { |
|
106 if ($(this).data('table') == 'dnd') { |
|
107 |
|
108 $(this).tableDnD({ |
|
109 onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, |
|
110 onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null, |
|
111 onDragClass: $(this).data('ondragclass') == undefined && "tDnD_whileDrag" || $(this).data('ondragclass'), |
|
112 onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null, |
|
113 onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null, |
|
114 scrollAmount: $(this).data('scrollamount') || 5, |
|
115 sensitivity: $(this).data('sensitivity') || 10, |
|
116 hierarchyLevel: $(this).data('hierarchylevel') || 0, |
|
117 indentArtifact: $(this).data('indentartifact') || '<div class="indent"> </div>', |
|
118 autoWidthAdjust: $(this).data('autowidthadjust') || true, |
|
119 autoCleanRelations: $(this).data('autocleanrelations') || true, |
|
120 jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t', |
|
121 serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/, |
|
122 serializeParamName: $(this).data('serializeparamname') || false, |
|
123 dragHandle: $(this).data('draghandle') || null |
|
124 }); |
|
125 } |
|
126 |
|
127 |
|
128 }); |
|
129 }); |
|
130 |
|
131 jQuery.tableDnD = { |
|
132 /** Keep hold of the current table being dragged */ |
|
133 currentTable: null, |
|
134 /** Keep hold of the current drag object if any */ |
|
135 dragObject: null, |
|
136 /** The current mouse offset */ |
|
137 mouseOffset: null, |
|
138 /** Remember the old value of X and Y so that we don't do too much processing */ |
|
139 oldX: 0, |
|
140 oldY: 0, |
|
141 |
|
142 /** Actually build the structure */ |
|
143 build: function(options) { |
|
144 // Set up the defaults if any |
|
145 |
|
146 this.each(function() { |
|
147 // This is bound to each matching table, set up the defaults and override with user options |
|
148 this.tableDnDConfig = $.extend({ |
|
149 onDragStyle: null, |
|
150 onDropStyle: null, |
|
151 // Add in the default class for whileDragging |
|
152 onDragClass: "tDnD_whileDrag", |
|
153 onDrop: null, |
|
154 onDragStart: null, |
|
155 scrollAmount: 5, |
|
156 /** Sensitivity setting will throttle the trigger rate for movement detection */ |
|
157 sensitivity: 10, |
|
158 /** Hierarchy level to support parent child. 0 switches this functionality off */ |
|
159 hierarchyLevel: 0, |
|
160 /** The html artifact to prepend the first cell with as indentation */ |
|
161 indentArtifact: '<div class="indent"> </div>', |
|
162 /** Automatically adjust width of first cell */ |
|
163 autoWidthAdjust: true, |
|
164 /** Automatic clean-up to ensure relationship integrity */ |
|
165 autoCleanRelations: true, |
|
166 /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */ |
|
167 jsonPretifySeparator: '\t', |
|
168 /** The regular expression to use to trim row IDs */ |
|
169 serializeRegexp: /[^\-]*$/, |
|
170 /** If you want to specify another parameter name instead of the table ID */ |
|
171 serializeParamName: false, |
|
172 /** If you give the name of a class here, then only Cells with this class will be draggable */ |
|
173 dragHandle: null |
|
174 }, options || {}); |
|
175 |
|
176 // Now make the rows draggable |
|
177 $.tableDnD.makeDraggable(this); |
|
178 // Prepare hierarchy support |
|
179 this.tableDnDConfig.hierarchyLevel |
|
180 && $.tableDnD.makeIndented(this); |
|
181 }); |
|
182 |
|
183 // Don't break the chain |
|
184 return this; |
|
185 }, |
|
186 makeIndented: function (table) { |
|
187 var config = table.tableDnDConfig, |
|
188 rows = table.rows, |
|
189 firstCell = $(rows).first().find('td:first')[0], |
|
190 indentLevel = 0, |
|
191 cellWidth = 0, |
|
192 longestCell, |
|
193 tableStyle; |
|
194 |
|
195 if ($(table).hasClass('indtd')) |
|
196 return null; |
|
197 |
|
198 tableStyle = $(table).addClass('indtd').attr('style'); |
|
199 $(table).css({whiteSpace: "nowrap"}); |
|
200 |
|
201 for (var w = 0; w < rows.length; w++) { |
|
202 if (cellWidth < $(rows[w]).find('td:first').text().length) { |
|
203 cellWidth = $(rows[w]).find('td:first').text().length; |
|
204 longestCell = w; |
|
205 } |
|
206 } |
|
207 $(firstCell).css({width: 'auto'}); |
|
208 for (w = 0; w < config.hierarchyLevel; w++) |
|
209 $(rows[longestCell]).find('td:first').prepend(config.indentArtifact); |
|
210 firstCell && $(firstCell).css({width: firstCell.offsetWidth}); |
|
211 tableStyle && $(table).css(tableStyle); |
|
212 |
|
213 for (w = 0; w < config.hierarchyLevel; w++) |
|
214 $(rows[longestCell]).find('td:first').children(':first').remove(); |
|
215 |
|
216 config.hierarchyLevel |
|
217 && $(rows).each(function () { |
|
218 indentLevel = $(this).data('level') || 0; |
|
219 indentLevel <= config.hierarchyLevel |
|
220 && $(this).data('level', indentLevel) |
|
221 || $(this).data('level', 0); |
|
222 for (var i = 0; i < $(this).data('level'); i++) |
|
223 $(this).find('td:first').prepend(config.indentArtifact); |
|
224 }); |
|
225 |
|
226 return this; |
|
227 }, |
|
228 /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ |
|
229 makeDraggable: function(table) { |
|
230 var config = table.tableDnDConfig; |
|
231 |
|
232 config.dragHandle |
|
233 // We only need to add the event to the specified cells |
|
234 && $(config.dragHandle, table).each(function() { |
|
235 // The cell is bound to "this" |
|
236 $(this).bind(startEvent, function(e) { |
|
237 $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config); |
|
238 return false; |
|
239 }); |
|
240 }) |
|
241 // For backwards compatibility, we add the event to the whole row |
|
242 // get all the rows as a wrapped set |
|
243 || $(table.rows).each(function() { |
|
244 // Iterate through each row, the row is bound to "this" |
|
245 if (! $(this).hasClass("nodrag")) { |
|
246 $(this).bind(startEvent, function(e) { |
|
247 if (e.target.tagName == "TD") { |
|
248 $.tableDnD.initialiseDrag(this, table, this, e, config); |
|
249 return false; |
|
250 } |
|
251 }).css("cursor", "move"); // Store the tableDnD object |
|
252 } |
|
253 }); |
|
254 }, |
|
255 currentOrder: function() { |
|
256 var rows = this.currentTable.rows; |
|
257 return $.map(rows, function (val) { |
|
258 return ($(val).data('level') + val.id).replace(/\s/g, ''); |
|
259 }).join(''); |
|
260 }, |
|
261 initialiseDrag: function(dragObject, table, target, e, config) { |
|
262 this.dragObject = dragObject; |
|
263 this.currentTable = table; |
|
264 this.mouseOffset = this.getMouseOffset(target, e); |
|
265 this.originalOrder = this.currentOrder(); |
|
266 |
|
267 // Now we need to capture the mouse up and mouse move event |
|
268 // We can use bind so that we don't interfere with other event handlers |
|
269 $(document) |
|
270 .bind(moveEvent, this.mousemove) |
|
271 .bind(endEvent, this.mouseup); |
|
272 |
|
273 // Call the onDragStart method if there is one |
|
274 config.onDragStart |
|
275 && config.onDragStart(table, target); |
|
276 }, |
|
277 updateTables: function() { |
|
278 this.each(function() { |
|
279 // this is now bound to each matching table |
|
280 if (this.tableDnDConfig) |
|
281 $.tableDnD.makeDraggable(this); |
|
282 }); |
|
283 }, |
|
284 /** Get the mouse coordinates from the event (allowing for browser differences) */ |
|
285 mouseCoords: function(e) { |
|
286 e = e || window.event; |
|
287 |
|
288 if (e.changedTouches) |
|
289 return { |
|
290 x: e.changedTouches[0].clientX, |
|
291 y: e.changedTouches[0].clientY |
|
292 }; |
|
293 |
|
294 if(e.pageX || e.pageY) |
|
295 return { |
|
296 x: e.pageX, |
|
297 y: e.pageY |
|
298 }; |
|
299 |
|
300 return { |
|
301 x: e.clientX + document.body.scrollLeft - document.body.clientLeft, |
|
302 y: e.clientY + document.body.scrollTop - document.body.clientTop |
|
303 }; |
|
304 }, |
|
305 /** Given a target element and a mouse eent, get the mouse offset from that element. |
|
306 To do this we need the element's position and the mouse position */ |
|
307 getMouseOffset: function(target, e) { |
|
308 var mousePos, |
|
309 docPos; |
|
310 |
|
311 e = e || window.event; |
|
312 |
|
313 docPos = this.getPosition(target); |
|
314 mousePos = this.mouseCoords(e); |
|
315 |
|
316 return { |
|
317 x: mousePos.x - docPos.x, |
|
318 y: mousePos.y - docPos.y |
|
319 }; |
|
320 }, |
|
321 /** Get the position of an element by going up the DOM tree and adding up all the offsets */ |
|
322 getPosition: function(element) { |
|
323 var left = 0, |
|
324 top = 0; |
|
325 |
|
326 // Safari fix -- thanks to Luis Chato for this! |
|
327 // Safari 2 doesn't correctly grab the offsetTop of a table row |
|
328 // this is detailed here: |
|
329 // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ |
|
330 // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. |
|
331 // note that firefox will return a text node as a first child, so designing a more thorough |
|
332 // solution may need to take that into account, for now this seems to work in firefox, safari, ie |
|
333 if (element.offsetHeight == 0) |
|
334 element = element.firstChild; // a table cell |
|
335 |
|
336 while (element.offsetParent) { |
|
337 left += element.offsetLeft; |
|
338 top += element.offsetTop; |
|
339 element = element.offsetParent; |
|
340 } |
|
341 |
|
342 left += element.offsetLeft; |
|
343 top += element.offsetTop; |
|
344 |
|
345 return { |
|
346 x: left, |
|
347 y: top |
|
348 }; |
|
349 }, |
|
350 autoScroll: function (mousePos) { |
|
351 var config = this.currentTable.tableDnDConfig, |
|
352 yOffset = window.pageYOffset, |
|
353 windowHeight = window.innerHeight |
|
354 ? window.innerHeight |
|
355 : document.documentElement.clientHeight |
|
356 ? document.documentElement.clientHeight |
|
357 : document.body.clientHeight; |
|
358 |
|
359 // Windows version |
|
360 // yOffset=document.body.scrollTop; |
|
361 if (document.all) |
|
362 if (typeof document.compatMode != 'undefined' |
|
363 && document.compatMode != 'BackCompat') |
|
364 yOffset = document.documentElement.scrollTop; |
|
365 else if (typeof document.body != 'undefined') |
|
366 yOffset = document.body.scrollTop; |
|
367 |
|
368 mousePos.y - yOffset < config.scrollAmount |
|
369 && window.scrollBy(0, - config.scrollAmount) |
|
370 || windowHeight - (mousePos.y - yOffset) < config.scrollAmount |
|
371 && window.scrollBy(0, config.scrollAmount); |
|
372 |
|
373 }, |
|
374 moveVerticle: function (moving, currentRow) { |
|
375 |
|
376 if (0 != moving.vertical |
|
377 // If we're over a row then move the dragged row to there so that the user sees the |
|
378 // effect dynamically |
|
379 && currentRow |
|
380 && this.dragObject != currentRow |
|
381 && this.dragObject.parentNode == currentRow.parentNode) |
|
382 0 > moving.vertical |
|
383 && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling) |
|
384 || 0 < moving.vertical |
|
385 && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow); |
|
386 |
|
387 }, |
|
388 moveHorizontal: function (moving, currentRow) { |
|
389 var config = this.currentTable.tableDnDConfig, |
|
390 currentLevel; |
|
391 |
|
392 if (!config.hierarchyLevel |
|
393 || 0 == moving.horizontal |
|
394 // We only care if moving left or right on the current row |
|
395 || !currentRow |
|
396 || this.dragObject != currentRow) |
|
397 return null; |
|
398 |
|
399 currentLevel = $(currentRow).data('level'); |
|
400 |
|
401 0 < moving.horizontal |
|
402 && currentLevel > 0 |
|
403 && $(currentRow).find('td:first').children(':first').remove() |
|
404 && $(currentRow).data('level', --currentLevel); |
|
405 |
|
406 0 > moving.horizontal |
|
407 && currentLevel < config.hierarchyLevel |
|
408 && $(currentRow).prev().data('level') >= currentLevel |
|
409 && $(currentRow).children(':first').prepend(config.indentArtifact) |
|
410 && $(currentRow).data('level', ++currentLevel); |
|
411 |
|
412 }, |
|
413 mousemove: function(e) { |
|
414 var dragObj = $($.tableDnD.dragObject), |
|
415 config = $.tableDnD.currentTable.tableDnDConfig, |
|
416 currentRow, |
|
417 mousePos, |
|
418 moving, |
|
419 x, |
|
420 y; |
|
421 |
|
422 e && e.preventDefault(); |
|
423 |
|
424 if (!$.tableDnD.dragObject) |
|
425 return false; |
|
426 |
|
427 // prevent touch device screen scrolling |
|
428 e.type == 'touchmove' |
|
429 && event.preventDefault(); // TODO verify this is event and not really e |
|
430 |
|
431 // update the style to show we're dragging |
|
432 config.onDragClass |
|
433 && dragObj.addClass(config.onDragClass) |
|
434 || dragObj.css(config.onDragStyle); |
|
435 |
|
436 mousePos = $.tableDnD.mouseCoords(e); |
|
437 x = mousePos.x - $.tableDnD.mouseOffset.x; |
|
438 y = mousePos.y - $.tableDnD.mouseOffset.y; |
|
439 |
|
440 // auto scroll the window |
|
441 $.tableDnD.autoScroll(mousePos); |
|
442 |
|
443 currentRow = $.tableDnD.findDropTargetRow(dragObj, y); |
|
444 moving = $.tableDnD.findDragDirection(x, y); |
|
445 |
|
446 $.tableDnD.moveVerticle(moving, currentRow); |
|
447 $.tableDnD.moveHorizontal(moving, currentRow); |
|
448 |
|
449 return false; |
|
450 }, |
|
451 findDragDirection: function (x,y) { |
|
452 var sensitivity = this.currentTable.tableDnDConfig.sensitivity, |
|
453 oldX = this.oldX, |
|
454 oldY = this.oldY, |
|
455 xMin = oldX - sensitivity, |
|
456 xMax = oldX + sensitivity, |
|
457 yMin = oldY - sensitivity, |
|
458 yMax = oldY + sensitivity, |
|
459 moving = { |
|
460 horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1, |
|
461 vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1 |
|
462 }; |
|
463 |
|
464 // update the old value |
|
465 if (moving.horizontal != 0) |
|
466 this.oldX = x; |
|
467 if (moving.vertical != 0) |
|
468 this.oldY = y; |
|
469 |
|
470 return moving; |
|
471 }, |
|
472 /** We're only worried about the y position really, because we can only move rows up and down */ |
|
473 findDropTargetRow: function(draggedRow, y) { |
|
474 var rowHeight = 0, |
|
475 rows = this.currentTable.rows, |
|
476 config = this.currentTable.tableDnDConfig, |
|
477 rowY = 0, |
|
478 row = null; |
|
479 |
|
480 for (var i = 0; i < rows.length; i++) { |
|
481 row = rows[i]; |
|
482 rowY = this.getPosition(row).y; |
|
483 rowHeight = parseInt(row.offsetHeight) / 2; |
|
484 if (row.offsetHeight == 0) { |
|
485 rowY = this.getPosition(row.firstChild).y; |
|
486 rowHeight = parseInt(row.firstChild.offsetHeight) / 2; |
|
487 } |
|
488 // Because we always have to insert before, we need to offset the height a bit |
|
489 if (y > (rowY - rowHeight) && y < (rowY + rowHeight)) |
|
490 // that's the row we're over |
|
491 // If it's the same as the current row, ignore it |
|
492 if (draggedRow.is(row) |
|
493 || (config.onAllowDrop |
|
494 && !config.onAllowDrop(draggedRow, row)) |
|
495 // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) |
|
496 || $(row).hasClass("nodrop")) |
|
497 return null; |
|
498 else |
|
499 return row; |
|
500 } |
|
501 return null; |
|
502 }, |
|
503 processMouseup: function() { |
|
504 if (!this.currentTable || !this.dragObject) |
|
505 return null; |
|
506 |
|
507 var config = this.currentTable.tableDnDConfig, |
|
508 droppedRow = this.dragObject, |
|
509 parentLevel = 0, |
|
510 myLevel = 0; |
|
511 |
|
512 // Unbind the event handlers |
|
513 $(document) |
|
514 .unbind(moveEvent, this.mousemove) |
|
515 .unbind(endEvent, this.mouseup); |
|
516 |
|
517 config.hierarchyLevel |
|
518 && config.autoCleanRelations |
|
519 && $(this.currentTable.rows).first().find('td:first').children().each(function () { |
|
520 myLevel = $(this).parents('tr:first').data('level'); |
|
521 myLevel |
|
522 && $(this).parents('tr:first').data('level', --myLevel) |
|
523 && $(this).remove(); |
|
524 }) |
|
525 && config.hierarchyLevel > 1 |
|
526 && $(this.currentTable.rows).each(function () { |
|
527 myLevel = $(this).data('level'); |
|
528 if (myLevel > 1) { |
|
529 parentLevel = $(this).prev().data('level'); |
|
530 while (myLevel > parentLevel + 1) { |
|
531 $(this).find('td:first').children(':first').remove(); |
|
532 $(this).data('level', --myLevel); |
|
533 } |
|
534 } |
|
535 }); |
|
536 |
|
537 // If we have a dragObject, then we need to release it, |
|
538 // The row will already have been moved to the right place so we just reset stuff |
|
539 config.onDragClass |
|
540 && $(droppedRow).removeClass(config.onDragClass) |
|
541 || $(droppedRow).css(config.onDropStyle); |
|
542 |
|
543 this.dragObject = null; |
|
544 // Call the onDrop method if there is one |
|
545 config.onDrop |
|
546 && this.originalOrder != this.currentOrder() |
|
547 && $(droppedRow).hide().fadeIn('fast') |
|
548 && config.onDrop(this.currentTable, droppedRow); |
|
549 |
|
550 this.currentTable = null; // let go of the table too |
|
551 }, |
|
552 mouseup: function(e) { |
|
553 e && e.preventDefault(); |
|
554 $.tableDnD.processMouseup(); |
|
555 return false; |
|
556 }, |
|
557 jsonize: function(pretify) { |
|
558 var table = this.currentTable; |
|
559 if (pretify) |
|
560 return JSON.stringify( |
|
561 this.tableData(table), |
|
562 null, |
|
563 table.tableDnDConfig.jsonPretifySeparator |
|
564 ); |
|
565 return JSON.stringify(this.tableData(table)); |
|
566 }, |
|
567 serialize: function() { |
|
568 return $.param(this.tableData(this.currentTable)); |
|
569 }, |
|
570 serializeTable: function(table) { |
|
571 var result = ""; |
|
572 var paramName = table.tableDnDConfig.serializeParamName || table.id; |
|
573 var rows = table.rows; |
|
574 for (var i=0; i<rows.length; i++) { |
|
575 if (result.length > 0) result += "&"; |
|
576 var rowId = rows[i].id; |
|
577 if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { |
|
578 rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; |
|
579 result += paramName + '[]=' + rowId; |
|
580 } |
|
581 } |
|
582 return result; |
|
583 }, |
|
584 serializeTables: function() { |
|
585 var result = []; |
|
586 $('table').each(function() { |
|
587 this.id && result.push($.param(this.tableData(this))); |
|
588 }); |
|
589 return result.join('&'); |
|
590 }, |
|
591 tableData: function (table) { |
|
592 var config = table.tableDnDConfig, |
|
593 previousIDs = [], |
|
594 currentLevel = 0, |
|
595 indentLevel = 0, |
|
596 rowID = null, |
|
597 data = {}, |
|
598 getSerializeRegexp, |
|
599 paramName, |
|
600 currentID, |
|
601 rows; |
|
602 |
|
603 if (!table) |
|
604 table = this.currentTable; |
|
605 if (!table || !table.id || !table.rows || !table.rows.length) |
|
606 return {error: { code: 500, message: "Not a valid table, no serializable unique id provided."}}; |
|
607 |
|
608 rows = config.autoCleanRelations |
|
609 && table.rows |
|
610 || $.makeArray(table.rows); |
|
611 paramName = config.serializeParamName || table.id; |
|
612 currentID = paramName; |
|
613 |
|
614 getSerializeRegexp = function (rowId) { |
|
615 if (rowId && config && config.serializeRegexp) |
|
616 return rowId.match(config.serializeRegexp)[0]; |
|
617 return rowId; |
|
618 }; |
|
619 |
|
620 data[currentID] = []; |
|
621 !config.autoCleanRelations |
|
622 && $(rows[0]).data('level') |
|
623 && rows.unshift({id: 'undefined'}); |
|
624 |
|
625 |
|
626 |
|
627 for (var i=0; i < rows.length; i++) { |
|
628 if (config.hierarchyLevel) { |
|
629 indentLevel = $(rows[i]).data('level') || 0; |
|
630 if (indentLevel == 0) { |
|
631 currentID = paramName; |
|
632 previousIDs = []; |
|
633 } |
|
634 else if (indentLevel > currentLevel) { |
|
635 previousIDs.push([currentID, currentLevel]); |
|
636 currentID = getSerializeRegexp(rows[i-1].id); |
|
637 } |
|
638 else if (indentLevel < currentLevel) { |
|
639 for (var h = 0; h < previousIDs.length; h++) { |
|
640 if (previousIDs[h][1] == indentLevel) |
|
641 currentID = previousIDs[h][0]; |
|
642 if (previousIDs[h][1] >= currentLevel) |
|
643 previousIDs[h][1] = 0; |
|
644 } |
|
645 } |
|
646 currentLevel = indentLevel; |
|
647 |
|
648 if (!$.isArray(data[currentID])) |
|
649 data[currentID] = []; |
|
650 rowID = getSerializeRegexp(rows[i].id); |
|
651 rowID && data[currentID].push(rowID); |
|
652 } |
|
653 else { |
|
654 rowID = getSerializeRegexp(rows[i].id); |
|
655 rowID && data[currentID].push(rowID); |
|
656 } |
|
657 } |
|
658 return data; |
|
659 } |
|
660 }; |
|
661 |
|
662 jQuery.fn.extend( |
|
663 { |
|
664 tableDnD : $.tableDnD.build, |
|
665 tableDnDUpdate : $.tableDnD.updateTables, |
|
666 tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), |
|
667 tableDnDSerializeAll : $.tableDnD.serializeTables, |
|
668 tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) |
|
669 } |
|
670 ); |
|
671 |
|
672 }(jQuery, window, window.document); |