root / drupal7 / misc / tabledrag.js @ 2196f227
1 |
(function ($) { |
---|---|
2 |
|
3 |
/**
|
4 |
* Drag and drop table rows with field manipulation.
|
5 |
*
|
6 |
* Using the drupal_add_tabledrag() function, any table with weights or parent
|
7 |
* relationships may be made into draggable tables. Columns containing a field
|
8 |
* may optionally be hidden, providing a better user experience.
|
9 |
*
|
10 |
* Created tableDrag instances may be modified with custom behaviors by
|
11 |
* overriding the .onDrag, .onDrop, .row.onSwap, and .row.onIndent methods.
|
12 |
* See blocks.js for an example of adding additional functionality to tableDrag.
|
13 |
*/
|
14 |
Drupal.behaviors.tableDrag = { |
15 |
attach: function (context, settings) { |
16 |
for (var base in settings.tableDrag) { |
17 |
$('#' + base, context).once('tabledrag', function () { |
18 |
// Create the new tableDrag instance. Save in the Drupal variable
|
19 |
// to allow other scripts access to the object.
|
20 |
Drupal.tableDrag[base] = new Drupal.tableDrag(this, settings.tableDrag[base]); |
21 |
}); |
22 |
} |
23 |
} |
24 |
}; |
25 |
|
26 |
/**
|
27 |
* Constructor for the tableDrag object. Provides table and field manipulation.
|
28 |
*
|
29 |
* @param table
|
30 |
* DOM object for the table to be made draggable.
|
31 |
* @param tableSettings
|
32 |
* Settings for the table added via drupal_add_dragtable().
|
33 |
*/
|
34 |
Drupal.tableDrag = function (table, tableSettings) { |
35 |
var self = this; |
36 |
|
37 |
// Required object variables.
|
38 |
this.table = table;
|
39 |
this.tableSettings = tableSettings;
|
40 |
this.dragObject = null; // Used to hold information about a current drag operation. |
41 |
this.rowObject = null; // Provides operations for row manipulation. |
42 |
this.oldRowElement = null; // Remember the previous element. |
43 |
this.oldY = 0; // Used to determine up or down direction from last mouse move. |
44 |
this.changed = false; // Whether anything in the entire table has changed. |
45 |
this.maxDepth = 0; // Maximum amount of allowed parenting. |
46 |
this.rtl = $(this.table).css('direction') == 'rtl' ? -1 : 1; // Direction of the table. |
47 |
|
48 |
// Configure the scroll settings.
|
49 |
this.scrollSettings = { amount: 4, interval: 50, trigger: 70 }; |
50 |
this.scrollInterval = null; |
51 |
this.scrollY = 0; |
52 |
this.windowHeight = 0; |
53 |
|
54 |
// Check this table's settings to see if there are parent relationships in
|
55 |
// this table. For efficiency, large sections of code can be skipped if we
|
56 |
// don't need to track horizontal movement and indentations.
|
57 |
this.indentEnabled = false; |
58 |
for (var group in tableSettings) { |
59 |
for (var n in tableSettings[group]) { |
60 |
if (tableSettings[group][n].relationship == 'parent') { |
61 |
this.indentEnabled = true; |
62 |
} |
63 |
if (tableSettings[group][n].limit > 0) { |
64 |
this.maxDepth = tableSettings[group][n].limit;
|
65 |
} |
66 |
} |
67 |
} |
68 |
if (this.indentEnabled) { |
69 |
this.indentCount = 1; // Total width of indents, set in makeDraggable. |
70 |
// Find the width of indentations to measure mouse movements against.
|
71 |
// Because the table doesn't need to start with any indentations, we
|
72 |
// manually append 2 indentations in the first draggable row, measure
|
73 |
// the offset, then remove.
|
74 |
var indent = Drupal.theme('tableDragIndentation'); |
75 |
var testRow = $('<tr/>').addClass('draggable').appendTo(table); |
76 |
var testCell = $('<td/>').appendTo(testRow).prepend(indent).prepend(indent); |
77 |
this.indentAmount = $('.indentation', testCell).get(1).offsetLeft - $('.indentation', testCell).get(0).offsetLeft; |
78 |
testRow.remove(); |
79 |
} |
80 |
|
81 |
// Make each applicable row draggable.
|
82 |
// Match immediate children of the parent element to allow nesting.
|
83 |
$('> tr.draggable, > tbody > tr.draggable', table).each(function () { self.makeDraggable(this); }); |
84 |
|
85 |
// Add a link before the table for users to show or hide weight columns.
|
86 |
$(table).before($('<a href="#" class="tabledrag-toggle-weight"></a>') |
87 |
.attr('title', Drupal.t('Re-order rows by numerical weight instead of dragging.')) |
88 |
.click(function () {
|
89 |
if ($.cookie('Drupal.tableDrag.showWeight') == 1) { |
90 |
self.hideColumns(); |
91 |
} |
92 |
else {
|
93 |
self.showColumns(); |
94 |
} |
95 |
return false; |
96 |
}) |
97 |
.wrap('<div class="tabledrag-toggle-weight-wrapper"></div>')
|
98 |
.parent() |
99 |
); |
100 |
|
101 |
// Initialize the specified columns (for example, weight or parent columns)
|
102 |
// to show or hide according to user preference. This aids accessibility
|
103 |
// so that, e.g., screen reader users can choose to enter weight values and
|
104 |
// manipulate form elements directly, rather than using drag-and-drop..
|
105 |
self.initColumns(); |
106 |
|
107 |
// Add mouse bindings to the document. The self variable is passed along
|
108 |
// as event handlers do not have direct access to the tableDrag object.
|
109 |
$(document).bind('mousemove pointermove', function (event) { return self.dragRow(event, self); }); |
110 |
$(document).bind('mouseup pointerup', function (event) { return self.dropRow(event, self); }); |
111 |
$(document).bind('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); }); |
112 |
$(document).bind('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); }); |
113 |
}; |
114 |
|
115 |
/**
|
116 |
* Initialize columns containing form elements to be hidden by default,
|
117 |
* according to the settings for this tableDrag instance.
|
118 |
*
|
119 |
* Identify and mark each cell with a CSS class so we can easily toggle
|
120 |
* show/hide it. Finally, hide columns if user does not have a
|
121 |
* 'Drupal.tableDrag.showWeight' cookie.
|
122 |
*/
|
123 |
Drupal.tableDrag.prototype.initColumns = function () { |
124 |
for (var group in this.tableSettings) { |
125 |
// Find the first field in this group.
|
126 |
for (var d in this.tableSettings[group]) { |
127 |
var field = $('.' + this.tableSettings[group][d].target + ':first', this.table); |
128 |
if (field.length && this.tableSettings[group][d].hidden) { |
129 |
var hidden = this.tableSettings[group][d].hidden; |
130 |
var cell = field.closest('td'); |
131 |
break;
|
132 |
} |
133 |
} |
134 |
|
135 |
// Mark the column containing this field so it can be hidden.
|
136 |
if (hidden && cell[0]) { |
137 |
// Add 1 to our indexes. The nth-child selector is 1 based, not 0 based.
|
138 |
// Match immediate children of the parent element to allow nesting.
|
139 |
var columnIndex = $('> td', cell.parent()).index(cell.get(0)) + 1; |
140 |
$('> thead > tr, > tbody > tr, > tr', this.table).each(function () { |
141 |
// Get the columnIndex and adjust for any colspans in this row.
|
142 |
var index = columnIndex;
|
143 |
var cells = $(this).children(); |
144 |
cells.each(function (n) {
|
145 |
if (n < index && this.colSpan && this.colSpan > 1) { |
146 |
index -= this.colSpan - 1; |
147 |
} |
148 |
}); |
149 |
if (index > 0) { |
150 |
cell = cells.filter(':nth-child(' + index + ')'); |
151 |
if (cell[0].colSpan && cell[0].colSpan > 1) { |
152 |
// If this cell has a colspan, mark it so we can reduce the colspan.
|
153 |
cell.addClass('tabledrag-has-colspan');
|
154 |
} |
155 |
else {
|
156 |
// Mark this cell so we can hide it.
|
157 |
cell.addClass('tabledrag-hide');
|
158 |
} |
159 |
} |
160 |
}); |
161 |
} |
162 |
} |
163 |
|
164 |
// Now hide cells and reduce colspans unless cookie indicates previous choice.
|
165 |
// Set a cookie if it is not already present.
|
166 |
if ($.cookie('Drupal.tableDrag.showWeight') === null) { |
167 |
$.cookie('Drupal.tableDrag.showWeight', 0, { |
168 |
path: Drupal.settings.basePath,
|
169 |
// The cookie expires in one year.
|
170 |
expires: 365 |
171 |
}); |
172 |
this.hideColumns();
|
173 |
} |
174 |
// Check cookie value and show/hide weight columns accordingly.
|
175 |
else {
|
176 |
if ($.cookie('Drupal.tableDrag.showWeight') == 1) { |
177 |
this.showColumns();
|
178 |
} |
179 |
else {
|
180 |
this.hideColumns();
|
181 |
} |
182 |
} |
183 |
}; |
184 |
|
185 |
/**
|
186 |
* Hide the columns containing weight/parent form elements.
|
187 |
* Undo showColumns().
|
188 |
*/
|
189 |
Drupal.tableDrag.prototype.hideColumns = function () { |
190 |
// Hide weight/parent cells and headers.
|
191 |
$('.tabledrag-hide', 'table.tabledrag-processed').css('display', 'none'); |
192 |
// Show TableDrag handles.
|
193 |
$('.tabledrag-handle', 'table.tabledrag-processed').css('display', ''); |
194 |
// Reduce the colspan of any effected multi-span columns.
|
195 |
$('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () { |
196 |
this.colSpan = this.colSpan - 1; |
197 |
}); |
198 |
// Change link text.
|
199 |
$('.tabledrag-toggle-weight').text(Drupal.t('Show row weights')); |
200 |
// Change cookie.
|
201 |
$.cookie('Drupal.tableDrag.showWeight', 0, { |
202 |
path: Drupal.settings.basePath,
|
203 |
// The cookie expires in one year.
|
204 |
expires: 365 |
205 |
}); |
206 |
// Trigger an event to allow other scripts to react to this display change.
|
207 |
$('table.tabledrag-processed').trigger('columnschange', 'hide'); |
208 |
}; |
209 |
|
210 |
/**
|
211 |
* Show the columns containing weight/parent form elements
|
212 |
* Undo hideColumns().
|
213 |
*/
|
214 |
Drupal.tableDrag.prototype.showColumns = function () { |
215 |
// Show weight/parent cells and headers.
|
216 |
$('.tabledrag-hide', 'table.tabledrag-processed').css('display', ''); |
217 |
// Hide TableDrag handles.
|
218 |
$('.tabledrag-handle', 'table.tabledrag-processed').css('display', 'none'); |
219 |
// Increase the colspan for any columns where it was previously reduced.
|
220 |
$('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () { |
221 |
this.colSpan = this.colSpan + 1; |
222 |
}); |
223 |
// Change link text.
|
224 |
$('.tabledrag-toggle-weight').text(Drupal.t('Hide row weights')); |
225 |
// Change cookie.
|
226 |
$.cookie('Drupal.tableDrag.showWeight', 1, { |
227 |
path: Drupal.settings.basePath,
|
228 |
// The cookie expires in one year.
|
229 |
expires: 365 |
230 |
}); |
231 |
// Trigger an event to allow other scripts to react to this display change.
|
232 |
$('table.tabledrag-processed').trigger('columnschange', 'show'); |
233 |
}; |
234 |
|
235 |
/**
|
236 |
* Find the target used within a particular row and group.
|
237 |
*/
|
238 |
Drupal.tableDrag.prototype.rowSettings = function (group, row) { |
239 |
var field = $('.' + group, row); |
240 |
for (var delta in this.tableSettings[group]) { |
241 |
var targetClass = this.tableSettings[group][delta].target; |
242 |
if (field.is('.' + targetClass)) { |
243 |
// Return a copy of the row settings.
|
244 |
var rowSettings = {};
|
245 |
for (var n in this.tableSettings[group][delta]) { |
246 |
rowSettings[n] = this.tableSettings[group][delta][n];
|
247 |
} |
248 |
return rowSettings;
|
249 |
} |
250 |
} |
251 |
}; |
252 |
|
253 |
/**
|
254 |
* Take an item and add event handlers to make it become draggable.
|
255 |
*/
|
256 |
Drupal.tableDrag.prototype.makeDraggable = function (item) { |
257 |
var self = this; |
258 |
|
259 |
// Create the handle.
|
260 |
var handle = $('<a href="#" class="tabledrag-handle"><div class="handle"> </div></a>').attr('title', Drupal.t('Drag to re-order')); |
261 |
// Insert the handle after indentations (if any).
|
262 |
if ($('td:first .indentation:last', item).length) { |
263 |
$('td:first .indentation:last', item).after(handle); |
264 |
// Update the total width of indentation in this entire table.
|
265 |
self.indentCount = Math.max($('.indentation', item).length, self.indentCount); |
266 |
} |
267 |
else {
|
268 |
$('td:first', item).prepend(handle); |
269 |
} |
270 |
|
271 |
// Add hover action for the handle.
|
272 |
handle.hover(function () {
|
273 |
self.dragObject == null ? $(this).addClass('tabledrag-handle-hover') : null; |
274 |
}, function () {
|
275 |
self.dragObject == null ? $(this).removeClass('tabledrag-handle-hover') : null; |
276 |
}); |
277 |
|
278 |
// Add the mousedown action for the handle.
|
279 |
handle.bind('mousedown touchstart pointerdown', function (event) { |
280 |
if (event.originalEvent.type == "touchstart") { |
281 |
event = event.originalEvent.touches[0];
|
282 |
} |
283 |
// Create a new dragObject recording the event information.
|
284 |
self.dragObject = {}; |
285 |
self.dragObject.initMouseOffset = self.getMouseOffset(item, event); |
286 |
self.dragObject.initMouseCoords = self.mouseCoords(event); |
287 |
if (self.indentEnabled) {
|
288 |
self.dragObject.indentMousePos = self.dragObject.initMouseCoords; |
289 |
} |
290 |
|
291 |
// If there's a lingering row object from the keyboard, remove its focus.
|
292 |
if (self.rowObject) {
|
293 |
$('a.tabledrag-handle', self.rowObject.element).blur(); |
294 |
} |
295 |
|
296 |
// Create a new rowObject for manipulation of this row.
|
297 |
self.rowObject = new self.row(item, 'mouse', self.indentEnabled, self.maxDepth, true); |
298 |
|
299 |
// Save the position of the table.
|
300 |
self.table.topY = $(self.table).offset().top;
|
301 |
self.table.bottomY = self.table.topY + self.table.offsetHeight; |
302 |
|
303 |
// Add classes to the handle and row.
|
304 |
$(this).addClass('tabledrag-handle-hover'); |
305 |
$(item).addClass('drag'); |
306 |
|
307 |
// Set the document to use the move cursor during drag.
|
308 |
$('body').addClass('drag'); |
309 |
if (self.oldRowElement) {
|
310 |
$(self.oldRowElement).removeClass('drag-previous'); |
311 |
} |
312 |
|
313 |
// Hack for IE6 that flickers uncontrollably if select lists are moved.
|
314 |
if (navigator.userAgent.indexOf('MSIE 6.') != -1) { |
315 |
$('select', this.table).css('display', 'none'); |
316 |
} |
317 |
|
318 |
// Hack for Konqueror, prevent the blur handler from firing.
|
319 |
// Konqueror always gives links focus, even after returning false on mousedown.
|
320 |
self.safeBlur = false;
|
321 |
|
322 |
// Call optional placeholder function.
|
323 |
self.onDrag(); |
324 |
return false; |
325 |
}); |
326 |
|
327 |
// Prevent the anchor tag from jumping us to the top of the page.
|
328 |
handle.click(function () {
|
329 |
return false; |
330 |
}); |
331 |
|
332 |
// Similar to the hover event, add a class when the handle is focused.
|
333 |
handle.focus(function () {
|
334 |
$(this).addClass('tabledrag-handle-hover'); |
335 |
self.safeBlur = true;
|
336 |
}); |
337 |
|
338 |
// Remove the handle class on blur and fire the same function as a mouseup.
|
339 |
handle.blur(function (event) {
|
340 |
$(this).removeClass('tabledrag-handle-hover'); |
341 |
if (self.rowObject && self.safeBlur) {
|
342 |
self.dropRow(event, self); |
343 |
} |
344 |
}); |
345 |
|
346 |
// Add arrow-key support to the handle.
|
347 |
handle.keydown(function (event) {
|
348 |
// If a rowObject doesn't yet exist and this isn't the tab key.
|
349 |
if (event.keyCode != 9 && !self.rowObject) { |
350 |
self.rowObject = new self.row(item, 'keyboard', self.indentEnabled, self.maxDepth, true); |
351 |
} |
352 |
|
353 |
var keyChange = false; |
354 |
switch (event.keyCode) {
|
355 |
case 37: // Left arrow. |
356 |
case 63234: // Safari left arrow. |
357 |
keyChange = true;
|
358 |
self.rowObject.indent(-1 * self.rtl);
|
359 |
break;
|
360 |
case 38: // Up arrow. |
361 |
case 63232: // Safari up arrow. |
362 |
var previousRow = $(self.rowObject.element).prev('tr').get(0); |
363 |
while (previousRow && $(previousRow).is(':hidden')) { |
364 |
previousRow = $(previousRow).prev('tr').get(0); |
365 |
} |
366 |
if (previousRow) {
|
367 |
self.safeBlur = false; // Do not allow the onBlur cleanup. |
368 |
self.rowObject.direction = 'up';
|
369 |
keyChange = true;
|
370 |
|
371 |
if ($(item).is('.tabledrag-root')) { |
372 |
// Swap with the previous top-level row.
|
373 |
var groupHeight = 0; |
374 |
while (previousRow && $('.indentation', previousRow).length) { |
375 |
previousRow = $(previousRow).prev('tr').get(0); |
376 |
groupHeight += $(previousRow).is(':hidden') ? 0 : previousRow.offsetHeight; |
377 |
} |
378 |
if (previousRow) {
|
379 |
self.rowObject.swap('before', previousRow);
|
380 |
// No need to check for indentation, 0 is the only valid one.
|
381 |
window.scrollBy(0, -groupHeight);
|
382 |
} |
383 |
} |
384 |
else if (self.table.tBodies[0].rows[0] != previousRow || $(previousRow).is('.draggable')) { |
385 |
// Swap with the previous row (unless previous row is the first one
|
386 |
// and undraggable).
|
387 |
self.rowObject.swap('before', previousRow);
|
388 |
self.rowObject.interval = null;
|
389 |
self.rowObject.indent(0);
|
390 |
window.scrollBy(0, -parseInt(item.offsetHeight, 10)); |
391 |
} |
392 |
handle.get(0).focus(); // Regain focus after the DOM manipulation. |
393 |
} |
394 |
break;
|
395 |
case 39: // Right arrow. |
396 |
case 63235: // Safari right arrow. |
397 |
keyChange = true;
|
398 |
self.rowObject.indent(1 * self.rtl);
|
399 |
break;
|
400 |
case 40: // Down arrow. |
401 |
case 63233: // Safari down arrow. |
402 |
var nextRow = $(self.rowObject.group).filter(':last').next('tr').get(0); |
403 |
while (nextRow && $(nextRow).is(':hidden')) { |
404 |
nextRow = $(nextRow).next('tr').get(0); |
405 |
} |
406 |
if (nextRow) {
|
407 |
self.safeBlur = false; // Do not allow the onBlur cleanup. |
408 |
self.rowObject.direction = 'down';
|
409 |
keyChange = true;
|
410 |
|
411 |
if ($(item).is('.tabledrag-root')) { |
412 |
// Swap with the next group (necessarily a top-level one).
|
413 |
var groupHeight = 0; |
414 |
var nextGroup = new self.row(nextRow, 'keyboard', self.indentEnabled, self.maxDepth, false); |
415 |
if (nextGroup) {
|
416 |
$(nextGroup.group).each(function () { |
417 |
groupHeight += $(this).is(':hidden') ? 0 : this.offsetHeight; |
418 |
}); |
419 |
var nextGroupRow = $(nextGroup.group).filter(':last').get(0); |
420 |
self.rowObject.swap('after', nextGroupRow);
|
421 |
// No need to check for indentation, 0 is the only valid one.
|
422 |
window.scrollBy(0, parseInt(groupHeight, 10)); |
423 |
} |
424 |
} |
425 |
else {
|
426 |
// Swap with the next row.
|
427 |
self.rowObject.swap('after', nextRow);
|
428 |
self.rowObject.interval = null;
|
429 |
self.rowObject.indent(0);
|
430 |
window.scrollBy(0, parseInt(item.offsetHeight, 10)); |
431 |
} |
432 |
handle.get(0).focus(); // Regain focus after the DOM manipulation. |
433 |
} |
434 |
break;
|
435 |
} |
436 |
|
437 |
if (self.rowObject && self.rowObject.changed == true) { |
438 |
$(item).addClass('drag'); |
439 |
if (self.oldRowElement) {
|
440 |
$(self.oldRowElement).removeClass('drag-previous'); |
441 |
} |
442 |
self.oldRowElement = item; |
443 |
self.restripeTable(); |
444 |
self.onDrag(); |
445 |
} |
446 |
|
447 |
// Returning false if we have an arrow key to prevent scrolling.
|
448 |
if (keyChange) {
|
449 |
return false; |
450 |
} |
451 |
}); |
452 |
|
453 |
// Compatibility addition, return false on keypress to prevent unwanted scrolling.
|
454 |
// IE and Safari will suppress scrolling on keydown, but all other browsers
|
455 |
// need to return false on keypress. http://www.quirksmode.org/js/keys.html
|
456 |
handle.keypress(function (event) {
|
457 |
switch (event.keyCode) {
|
458 |
case 37: // Left arrow. |
459 |
case 38: // Up arrow. |
460 |
case 39: // Right arrow. |
461 |
case 40: // Down arrow. |
462 |
return false; |
463 |
} |
464 |
}); |
465 |
}; |
466 |
|
467 |
/**
|
468 |
* Mousemove event handler, bound to document.
|
469 |
*/
|
470 |
Drupal.tableDrag.prototype.dragRow = function (event, self) { |
471 |
if (self.dragObject) {
|
472 |
self.currentMouseCoords = self.mouseCoords(event); |
473 |
|
474 |
var y = self.currentMouseCoords.y - self.dragObject.initMouseOffset.y;
|
475 |
var x = self.currentMouseCoords.x - self.dragObject.initMouseOffset.x;
|
476 |
|
477 |
// Check for row swapping and vertical scrolling.
|
478 |
if (y != self.oldY) {
|
479 |
self.rowObject.direction = y > self.oldY ? 'down' : 'up'; |
480 |
self.oldY = y; // Update the old value.
|
481 |
|
482 |
// Check if the window should be scrolled (and how fast).
|
483 |
var scrollAmount = self.checkScroll(self.currentMouseCoords.y);
|
484 |
// Stop any current scrolling.
|
485 |
clearInterval(self.scrollInterval); |
486 |
// Continue scrolling if the mouse has moved in the scroll direction.
|
487 |
if (scrollAmount > 0 && self.rowObject.direction == 'down' || scrollAmount < 0 && self.rowObject.direction == 'up') { |
488 |
self.setScroll(scrollAmount); |
489 |
} |
490 |
|
491 |
// If we have a valid target, perform the swap and restripe the table.
|
492 |
var currentRow = self.findDropTargetRow(x, y);
|
493 |
if (currentRow) {
|
494 |
if (self.rowObject.direction == 'down') { |
495 |
self.rowObject.swap('after', currentRow, self);
|
496 |
} |
497 |
else {
|
498 |
self.rowObject.swap('before', currentRow, self);
|
499 |
} |
500 |
self.restripeTable(); |
501 |
} |
502 |
} |
503 |
|
504 |
// Similar to row swapping, handle indentations.
|
505 |
if (self.indentEnabled) {
|
506 |
var xDiff = self.currentMouseCoords.x - self.dragObject.indentMousePos.x;
|
507 |
// Set the number of indentations the mouse has been moved left or right.
|
508 |
var indentDiff = Math.round(xDiff / self.indentAmount);
|
509 |
// Indent the row with our estimated diff, which may be further
|
510 |
// restricted according to the rows around this row.
|
511 |
var indentChange = self.rowObject.indent(indentDiff);
|
512 |
// Update table and mouse indentations.
|
513 |
self.dragObject.indentMousePos.x += self.indentAmount * indentChange * self.rtl; |
514 |
self.indentCount = Math.max(self.indentCount, self.rowObject.indents); |
515 |
} |
516 |
|
517 |
return false; |
518 |
} |
519 |
}; |
520 |
|
521 |
/**
|
522 |
* Mouseup event handler, bound to document.
|
523 |
* Blur event handler, bound to drag handle for keyboard support.
|
524 |
*/
|
525 |
Drupal.tableDrag.prototype.dropRow = function (event, self) { |
526 |
// Drop row functionality shared between mouseup and blur events.
|
527 |
if (self.rowObject != null) { |
528 |
var droppedRow = self.rowObject.element;
|
529 |
// The row is already in the right place so we just release it.
|
530 |
if (self.rowObject.changed == true) { |
531 |
// Update the fields in the dropped row.
|
532 |
self.updateFields(droppedRow); |
533 |
|
534 |
// If a setting exists for affecting the entire group, update all the
|
535 |
// fields in the entire dragged group.
|
536 |
for (var group in self.tableSettings) { |
537 |
var rowSettings = self.rowSettings(group, droppedRow);
|
538 |
if (rowSettings.relationship == 'group') { |
539 |
for (var n in self.rowObject.children) { |
540 |
self.updateField(self.rowObject.children[n], group); |
541 |
} |
542 |
} |
543 |
} |
544 |
|
545 |
self.rowObject.markChanged(); |
546 |
if (self.changed == false) { |
547 |
$(Drupal.theme('tableDragChangedWarning')).insertBefore(self.table).hide().fadeIn('slow'); |
548 |
self.changed = true;
|
549 |
} |
550 |
} |
551 |
|
552 |
if (self.indentEnabled) {
|
553 |
self.rowObject.removeIndentClasses(); |
554 |
} |
555 |
if (self.oldRowElement) {
|
556 |
$(self.oldRowElement).removeClass('drag-previous'); |
557 |
} |
558 |
$(droppedRow).removeClass('drag').addClass('drag-previous'); |
559 |
self.oldRowElement = droppedRow; |
560 |
self.onDrop(); |
561 |
self.rowObject = null;
|
562 |
} |
563 |
|
564 |
// Functionality specific only to mouseup event.
|
565 |
if (self.dragObject != null) { |
566 |
$('.tabledrag-handle', droppedRow).removeClass('tabledrag-handle-hover'); |
567 |
|
568 |
self.dragObject = null;
|
569 |
$('body').removeClass('drag'); |
570 |
clearInterval(self.scrollInterval); |
571 |
|
572 |
// Hack for IE6 that flickers uncontrollably if select lists are moved.
|
573 |
if (navigator.userAgent.indexOf('MSIE 6.') != -1) { |
574 |
$('select', this.table).css('display', 'block'); |
575 |
} |
576 |
} |
577 |
}; |
578 |
|
579 |
/**
|
580 |
* Get the mouse coordinates from the event (allowing for browser differences).
|
581 |
*/
|
582 |
Drupal.tableDrag.prototype.mouseCoords = function (event) { |
583 |
// Complete support for pointer events was only introduced to jQuery in
|
584 |
// version 1.11.1; between versions 1.7 and 1.11.0 pointer events have the
|
585 |
// clientX and clientY properties undefined. In those cases, the properties
|
586 |
// must be retrieved from the event.originalEvent object instead.
|
587 |
var clientX = event.clientX || event.originalEvent.clientX;
|
588 |
var clientY = event.clientY || event.originalEvent.clientY;
|
589 |
|
590 |
if (event.pageX || event.pageY) {
|
591 |
return { x: event.pageX, y: event.pageY }; |
592 |
} |
593 |
|
594 |
return {
|
595 |
x: clientX + document.body.scrollLeft - document.body.clientLeft,
|
596 |
y: clientY + document.body.scrollTop - document.body.clientTop
|
597 |
}; |
598 |
}; |
599 |
|
600 |
/**
|
601 |
* Given a target element and a mouse event, get the mouse offset from that
|
602 |
* element. To do this we need the element's position and the mouse position.
|
603 |
*/
|
604 |
Drupal.tableDrag.prototype.getMouseOffset = function (target, event) { |
605 |
var docPos = $(target).offset(); |
606 |
var mousePos = this.mouseCoords(event); |
607 |
return { x: mousePos.x - docPos.left, y: mousePos.y - docPos.top }; |
608 |
}; |
609 |
|
610 |
/**
|
611 |
* Find the row the mouse is currently over. This row is then taken and swapped
|
612 |
* with the one being dragged.
|
613 |
*
|
614 |
* @param x
|
615 |
* The x coordinate of the mouse on the page (not the screen).
|
616 |
* @param y
|
617 |
* The y coordinate of the mouse on the page (not the screen).
|
618 |
*/
|
619 |
Drupal.tableDrag.prototype.findDropTargetRow = function (x, y) { |
620 |
var rows = $(this.table.tBodies[0].rows).not(':hidden'); |
621 |
for (var n = 0; n < rows.length; n++) { |
622 |
var row = rows[n];
|
623 |
var indentDiff = 0; |
624 |
var rowY = $(row).offset().top; |
625 |
// Because Safari does not report offsetHeight on table rows, but does on
|
626 |
// table cells, grab the firstChild of the row and use that instead.
|
627 |
// http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari.
|
628 |
if (row.offsetHeight == 0) { |
629 |
var rowHeight = parseInt(row.firstChild.offsetHeight, 10) / 2; |
630 |
} |
631 |
// Other browsers.
|
632 |
else {
|
633 |
var rowHeight = parseInt(row.offsetHeight, 10) / 2; |
634 |
} |
635 |
|
636 |
// Because we always insert before, we need to offset the height a bit.
|
637 |
if ((y > (rowY - rowHeight)) && (y < (rowY + rowHeight))) {
|
638 |
if (this.indentEnabled) { |
639 |
// Check that this row is not a child of the row being dragged.
|
640 |
for (var n in this.rowObject.group) { |
641 |
if (this.rowObject.group[n] == row) { |
642 |
return null; |
643 |
} |
644 |
} |
645 |
} |
646 |
else {
|
647 |
// Do not allow a row to be swapped with itself.
|
648 |
if (row == this.rowObject.element) { |
649 |
return null; |
650 |
} |
651 |
} |
652 |
|
653 |
// Check that swapping with this row is allowed.
|
654 |
if (!this.rowObject.isValidSwap(row)) { |
655 |
return null; |
656 |
} |
657 |
|
658 |
// We may have found the row the mouse just passed over, but it doesn't
|
659 |
// take into account hidden rows. Skip backwards until we find a draggable
|
660 |
// row.
|
661 |
while ($(row).is(':hidden') && $(row).prev('tr').is(':hidden')) { |
662 |
row = $(row).prev('tr').get(0); |
663 |
} |
664 |
return row;
|
665 |
} |
666 |
} |
667 |
return null; |
668 |
}; |
669 |
|
670 |
/**
|
671 |
* After the row is dropped, update the table fields according to the settings
|
672 |
* set for this table.
|
673 |
*
|
674 |
* @param changedRow
|
675 |
* DOM object for the row that was just dropped.
|
676 |
*/
|
677 |
Drupal.tableDrag.prototype.updateFields = function (changedRow) { |
678 |
for (var group in this.tableSettings) { |
679 |
// Each group may have a different setting for relationship, so we find
|
680 |
// the source rows for each separately.
|
681 |
this.updateField(changedRow, group);
|
682 |
} |
683 |
}; |
684 |
|
685 |
/**
|
686 |
* After the row is dropped, update a single table field according to specific
|
687 |
* settings.
|
688 |
*
|
689 |
* @param changedRow
|
690 |
* DOM object for the row that was just dropped.
|
691 |
* @param group
|
692 |
* The settings group on which field updates will occur.
|
693 |
*/
|
694 |
Drupal.tableDrag.prototype.updateField = function (changedRow, group) { |
695 |
var rowSettings = this.rowSettings(group, changedRow); |
696 |
|
697 |
// Set the row as its own target.
|
698 |
if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') { |
699 |
var sourceRow = changedRow;
|
700 |
} |
701 |
// Siblings are easy, check previous and next rows.
|
702 |
else if (rowSettings.relationship == 'sibling') { |
703 |
var previousRow = $(changedRow).prev('tr').get(0); |
704 |
var nextRow = $(changedRow).next('tr').get(0); |
705 |
var sourceRow = changedRow;
|
706 |
if ($(previousRow).is('.draggable') && $('.' + group, previousRow).length) { |
707 |
if (this.indentEnabled) { |
708 |
if ($('.indentations', previousRow).length == $('.indentations', changedRow)) { |
709 |
sourceRow = previousRow; |
710 |
} |
711 |
} |
712 |
else {
|
713 |
sourceRow = previousRow; |
714 |
} |
715 |
} |
716 |
else if ($(nextRow).is('.draggable') && $('.' + group, nextRow).length) { |
717 |
if (this.indentEnabled) { |
718 |
if ($('.indentations', nextRow).length == $('.indentations', changedRow)) { |
719 |
sourceRow = nextRow; |
720 |
} |
721 |
} |
722 |
else {
|
723 |
sourceRow = nextRow; |
724 |
} |
725 |
} |
726 |
} |
727 |
// Parents, look up the tree until we find a field not in this group.
|
728 |
// Go up as many parents as indentations in the changed row.
|
729 |
else if (rowSettings.relationship == 'parent') { |
730 |
var previousRow = $(changedRow).prev('tr'); |
731 |
while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) { |
732 |
previousRow = previousRow.prev('tr');
|
733 |
} |
734 |
// If we found a row.
|
735 |
if (previousRow.length) {
|
736 |
sourceRow = previousRow[0];
|
737 |
} |
738 |
// Otherwise we went all the way to the left of the table without finding
|
739 |
// a parent, meaning this item has been placed at the root level.
|
740 |
else {
|
741 |
// Use the first row in the table as source, because it's guaranteed to
|
742 |
// be at the root level. Find the first item, then compare this row
|
743 |
// against it as a sibling.
|
744 |
sourceRow = $(this.table).find('tr.draggable:first').get(0); |
745 |
if (sourceRow == this.rowObject.element) { |
746 |
sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0); |
747 |
} |
748 |
var useSibling = true; |
749 |
} |
750 |
} |
751 |
|
752 |
// Because we may have moved the row from one category to another,
|
753 |
// take a look at our sibling and borrow its sources and targets.
|
754 |
this.copyDragClasses(sourceRow, changedRow, group);
|
755 |
rowSettings = this.rowSettings(group, changedRow);
|
756 |
|
757 |
// In the case that we're looking for a parent, but the row is at the top
|
758 |
// of the tree, copy our sibling's values.
|
759 |
if (useSibling) {
|
760 |
rowSettings.relationship = 'sibling';
|
761 |
rowSettings.source = rowSettings.target; |
762 |
} |
763 |
|
764 |
var targetClass = '.' + rowSettings.target; |
765 |
var targetElement = $(targetClass, changedRow).get(0); |
766 |
|
767 |
// Check if a target element exists in this row.
|
768 |
if (targetElement) {
|
769 |
var sourceClass = '.' + rowSettings.source; |
770 |
var sourceElement = $(sourceClass, sourceRow).get(0); |
771 |
switch (rowSettings.action) {
|
772 |
case 'depth': |
773 |
// Get the depth of the target row.
|
774 |
targetElement.value = $('.indentation', $(sourceElement).closest('tr')).length; |
775 |
break;
|
776 |
case 'match': |
777 |
// Update the value.
|
778 |
targetElement.value = sourceElement.value; |
779 |
break;
|
780 |
case 'order': |
781 |
var siblings = this.rowObject.findSiblings(rowSettings); |
782 |
if ($(targetElement).is('select')) { |
783 |
// Get a list of acceptable values.
|
784 |
var values = [];
|
785 |
$('option', targetElement).each(function () { |
786 |
values.push(this.value);
|
787 |
}); |
788 |
var maxVal = values[values.length - 1]; |
789 |
// Populate the values in the siblings.
|
790 |
$(targetClass, siblings).each(function () { |
791 |
// If there are more items than possible values, assign the maximum value to the row.
|
792 |
if (values.length > 0) { |
793 |
this.value = values.shift();
|
794 |
} |
795 |
else {
|
796 |
this.value = maxVal;
|
797 |
} |
798 |
}); |
799 |
} |
800 |
else {
|
801 |
// Assume a numeric input field.
|
802 |
var weight = parseInt($(targetClass, siblings[0]).val(), 10) || 0; |
803 |
$(targetClass, siblings).each(function () { |
804 |
this.value = weight;
|
805 |
weight++; |
806 |
}); |
807 |
} |
808 |
break;
|
809 |
} |
810 |
} |
811 |
}; |
812 |
|
813 |
/**
|
814 |
* Copy all special tableDrag classes from one row's form elements to a
|
815 |
* different one, removing any special classes that the destination row
|
816 |
* may have had.
|
817 |
*/
|
818 |
Drupal.tableDrag.prototype.copyDragClasses = function (sourceRow, targetRow, group) { |
819 |
var sourceElement = $('.' + group, sourceRow); |
820 |
var targetElement = $('.' + group, targetRow); |
821 |
if (sourceElement.length && targetElement.length) {
|
822 |
targetElement[0].className = sourceElement[0].className; |
823 |
} |
824 |
}; |
825 |
|
826 |
Drupal.tableDrag.prototype.checkScroll = function (cursorY) { |
827 |
var de = document.documentElement;
|
828 |
var b = document.body;
|
829 |
|
830 |
var windowHeight = this.windowHeight = window.innerHeight || (de.clientHeight && de.clientWidth != 0 ? de.clientHeight : b.offsetHeight); |
831 |
var scrollY = this.scrollY = (document.all ? (!de.scrollTop ? b.scrollTop : de.scrollTop) : (window.pageYOffset ? window.pageYOffset : window.scrollY)); |
832 |
var trigger = this.scrollSettings.trigger; |
833 |
var delta = 0; |
834 |
|
835 |
// Return a scroll speed relative to the edge of the screen.
|
836 |
if (cursorY - scrollY > windowHeight - trigger) {
|
837 |
delta = trigger / (windowHeight + scrollY - cursorY); |
838 |
delta = (delta > 0 && delta < trigger) ? delta : trigger;
|
839 |
return delta * this.scrollSettings.amount; |
840 |
} |
841 |
else if (cursorY - scrollY < trigger) { |
842 |
delta = trigger / (cursorY - scrollY); |
843 |
delta = (delta > 0 && delta < trigger) ? delta : trigger;
|
844 |
return -delta * this.scrollSettings.amount; |
845 |
} |
846 |
}; |
847 |
|
848 |
Drupal.tableDrag.prototype.setScroll = function (scrollAmount) { |
849 |
var self = this; |
850 |
|
851 |
this.scrollInterval = setInterval(function () { |
852 |
// Update the scroll values stored in the object.
|
853 |
self.checkScroll(self.currentMouseCoords.y); |
854 |
var aboveTable = self.scrollY > self.table.topY;
|
855 |
var belowTable = self.scrollY + self.windowHeight < self.table.bottomY;
|
856 |
if (scrollAmount > 0 && belowTable || scrollAmount < 0 && aboveTable) { |
857 |
window.scrollBy(0, scrollAmount);
|
858 |
} |
859 |
}, this.scrollSettings.interval);
|
860 |
}; |
861 |
|
862 |
Drupal.tableDrag.prototype.restripeTable = function () { |
863 |
// :even and :odd are reversed because jQuery counts from 0 and
|
864 |
// we count from 1, so we're out of sync.
|
865 |
// Match immediate children of the parent element to allow nesting.
|
866 |
$('> tbody > tr.draggable:visible, > tr.draggable:visible', this.table) |
867 |
.removeClass('odd even')
|
868 |
.filter(':odd').addClass('even').end() |
869 |
.filter(':even').addClass('odd'); |
870 |
}; |
871 |
|
872 |
/**
|
873 |
* Stub function. Allows a custom handler when a row begins dragging.
|
874 |
*/
|
875 |
Drupal.tableDrag.prototype.onDrag = function () { |
876 |
return null; |
877 |
}; |
878 |
|
879 |
/**
|
880 |
* Stub function. Allows a custom handler when a row is dropped.
|
881 |
*/
|
882 |
Drupal.tableDrag.prototype.onDrop = function () { |
883 |
return null; |
884 |
}; |
885 |
|
886 |
/**
|
887 |
* Constructor to make a new object to manipulate a table row.
|
888 |
*
|
889 |
* @param tableRow
|
890 |
* The DOM element for the table row we will be manipulating.
|
891 |
* @param method
|
892 |
* The method in which this row is being moved. Either 'keyboard' or 'mouse'.
|
893 |
* @param indentEnabled
|
894 |
* Whether the containing table uses indentations. Used for optimizations.
|
895 |
* @param maxDepth
|
896 |
* The maximum amount of indentations this row may contain.
|
897 |
* @param addClasses
|
898 |
* Whether we want to add classes to this row to indicate child relationships.
|
899 |
*/
|
900 |
Drupal.tableDrag.prototype.row = function (tableRow, method, indentEnabled, maxDepth, addClasses) { |
901 |
this.element = tableRow;
|
902 |
this.method = method;
|
903 |
this.group = [tableRow];
|
904 |
this.groupDepth = $('.indentation', tableRow).length; |
905 |
this.changed = false; |
906 |
this.table = $(tableRow).closest('table').get(0); |
907 |
this.indentEnabled = indentEnabled;
|
908 |
this.maxDepth = maxDepth;
|
909 |
this.direction = ''; // Direction the row is being moved. |
910 |
|
911 |
if (this.indentEnabled) { |
912 |
this.indents = $('.indentation', tableRow).length; |
913 |
this.children = this.findChildren(addClasses); |
914 |
this.group = $.merge(this.group, this.children); |
915 |
// Find the depth of this entire group.
|
916 |
for (var n = 0; n < this.group.length; n++) { |
917 |
this.groupDepth = Math.max($('.indentation', this.group[n]).length, this.groupDepth); |
918 |
} |
919 |
} |
920 |
}; |
921 |
|
922 |
/**
|
923 |
* Find all children of rowObject by indentation.
|
924 |
*
|
925 |
* @param addClasses
|
926 |
* Whether we want to add classes to this row to indicate child relationships.
|
927 |
*/
|
928 |
Drupal.tableDrag.prototype.row.prototype.findChildren = function (addClasses) { |
929 |
var parentIndentation = this.indents; |
930 |
var currentRow = $(this.element, this.table).next('tr.draggable'); |
931 |
var rows = [];
|
932 |
var child = 0; |
933 |
while (currentRow.length) {
|
934 |
var rowIndentation = $('.indentation', currentRow).length; |
935 |
// A greater indentation indicates this is a child.
|
936 |
if (rowIndentation > parentIndentation) {
|
937 |
child++; |
938 |
rows.push(currentRow[0]);
|
939 |
if (addClasses) {
|
940 |
$('.indentation', currentRow).each(function (indentNum) { |
941 |
if (child == 1 && (indentNum == parentIndentation)) { |
942 |
$(this).addClass('tree-child-first'); |
943 |
} |
944 |
if (indentNum == parentIndentation) {
|
945 |
$(this).addClass('tree-child'); |
946 |
} |
947 |
else if (indentNum > parentIndentation) { |
948 |
$(this).addClass('tree-child-horizontal'); |
949 |
} |
950 |
}); |
951 |
} |
952 |
} |
953 |
else {
|
954 |
break;
|
955 |
} |
956 |
currentRow = currentRow.next('tr.draggable');
|
957 |
} |
958 |
if (addClasses && rows.length) {
|
959 |
$('.indentation:nth-child(' + (parentIndentation + 1) + ')', rows[rows.length - 1]).addClass('tree-child-last'); |
960 |
} |
961 |
return rows;
|
962 |
}; |
963 |
|
964 |
/**
|
965 |
* Ensure that two rows are allowed to be swapped.
|
966 |
*
|
967 |
* @param row
|
968 |
* DOM object for the row being considered for swapping.
|
969 |
*/
|
970 |
Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) { |
971 |
if (this.indentEnabled) { |
972 |
var prevRow, nextRow;
|
973 |
if (this.direction == 'down') { |
974 |
prevRow = row; |
975 |
nextRow = $(row).next('tr').get(0); |
976 |
} |
977 |
else {
|
978 |
prevRow = $(row).prev('tr').get(0); |
979 |
nextRow = row; |
980 |
} |
981 |
this.interval = this.validIndentInterval(prevRow, nextRow); |
982 |
|
983 |
// We have an invalid swap if the valid indentations interval is empty.
|
984 |
if (this.interval.min > this.interval.max) { |
985 |
return false; |
986 |
} |
987 |
} |
988 |
|
989 |
// Do not let an un-draggable first row have anything put before it.
|
990 |
if (this.table.tBodies[0].rows[0] == row && $(row).is(':not(.draggable)')) { |
991 |
return false; |
992 |
} |
993 |
|
994 |
return true; |
995 |
}; |
996 |
|
997 |
/**
|
998 |
* Perform the swap between two rows.
|
999 |
*
|
1000 |
* @param position
|
1001 |
* Whether the swap will occur 'before' or 'after' the given row.
|
1002 |
* @param row
|
1003 |
* DOM element what will be swapped with the row group.
|
1004 |
*/
|
1005 |
Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) { |
1006 |
Drupal.detachBehaviors(this.group, Drupal.settings, 'move'); |
1007 |
$(row)[position](this.group); |
1008 |
Drupal.attachBehaviors(this.group, Drupal.settings);
|
1009 |
this.changed = true; |
1010 |
this.onSwap(row);
|
1011 |
}; |
1012 |
|
1013 |
/**
|
1014 |
* Determine the valid indentations interval for the row at a given position
|
1015 |
* in the table.
|
1016 |
*
|
1017 |
* @param prevRow
|
1018 |
* DOM object for the row before the tested position
|
1019 |
* (or null for first position in the table).
|
1020 |
* @param nextRow
|
1021 |
* DOM object for the row after the tested position
|
1022 |
* (or null for last position in the table).
|
1023 |
*/
|
1024 |
Drupal.tableDrag.prototype.row.prototype.validIndentInterval = function (prevRow, nextRow) { |
1025 |
var minIndent, maxIndent;
|
1026 |
|
1027 |
// Minimum indentation:
|
1028 |
// Do not orphan the next row.
|
1029 |
minIndent = nextRow ? $('.indentation', nextRow).length : 0; |
1030 |
|
1031 |
// Maximum indentation:
|
1032 |
if (!prevRow || $(prevRow).is(':not(.draggable)') || $(this.element).is('.tabledrag-root')) { |
1033 |
// Do not indent:
|
1034 |
// - the first row in the table,
|
1035 |
// - rows dragged below a non-draggable row,
|
1036 |
// - 'root' rows.
|
1037 |
maxIndent = 0;
|
1038 |
} |
1039 |
else {
|
1040 |
// Do not go deeper than as a child of the previous row.
|
1041 |
maxIndent = $('.indentation', prevRow).length + ($(prevRow).is('.tabledrag-leaf') ? 0 : 1); |
1042 |
// Limit by the maximum allowed depth for the table.
|
1043 |
if (this.maxDepth) { |
1044 |
maxIndent = Math.min(maxIndent, this.maxDepth - (this.groupDepth - this.indents)); |
1045 |
} |
1046 |
} |
1047 |
|
1048 |
return { 'min': minIndent, 'max': maxIndent }; |
1049 |
}; |
1050 |
|
1051 |
/**
|
1052 |
* Indent a row within the legal bounds of the table.
|
1053 |
*
|
1054 |
* @param indentDiff
|
1055 |
* The number of additional indentations proposed for the row (can be
|
1056 |
* positive or negative). This number will be adjusted to nearest valid
|
1057 |
* indentation level for the row.
|
1058 |
*/
|
1059 |
Drupal.tableDrag.prototype.row.prototype.indent = function (indentDiff) { |
1060 |
// Determine the valid indentations interval if not available yet.
|
1061 |
if (!this.interval) { |
1062 |
var prevRow = $(this.element).prev('tr').get(0); |
1063 |
var nextRow = $(this.group).filter(':last').next('tr').get(0); |
1064 |
this.interval = this.validIndentInterval(prevRow, nextRow); |
1065 |
} |
1066 |
|
1067 |
// Adjust to the nearest valid indentation.
|
1068 |
var indent = this.indents + indentDiff; |
1069 |
indent = Math.max(indent, this.interval.min);
|
1070 |
indent = Math.min(indent, this.interval.max);
|
1071 |
indentDiff = indent - this.indents;
|
1072 |
|
1073 |
for (var n = 1; n <= Math.abs(indentDiff); n++) { |
1074 |
// Add or remove indentations.
|
1075 |
if (indentDiff < 0) { |
1076 |
$('.indentation:first', this.group).remove(); |
1077 |
this.indents--;
|
1078 |
} |
1079 |
else {
|
1080 |
$('td:first', this.group).prepend(Drupal.theme('tableDragIndentation')); |
1081 |
this.indents++;
|
1082 |
} |
1083 |
} |
1084 |
if (indentDiff) {
|
1085 |
// Update indentation for this row.
|
1086 |
this.changed = true; |
1087 |
this.groupDepth += indentDiff;
|
1088 |
this.onIndent();
|
1089 |
} |
1090 |
|
1091 |
return indentDiff;
|
1092 |
}; |
1093 |
|
1094 |
/**
|
1095 |
* Find all siblings for a row, either according to its subgroup or indentation.
|
1096 |
* Note that the passed-in row is included in the list of siblings.
|
1097 |
*
|
1098 |
* @param settings
|
1099 |
* The field settings we're using to identify what constitutes a sibling.
|
1100 |
*/
|
1101 |
Drupal.tableDrag.prototype.row.prototype.findSiblings = function (rowSettings) { |
1102 |
var siblings = [];
|
1103 |
var directions = ['prev', 'next']; |
1104 |
var rowIndentation = this.indents; |
1105 |
for (var d = 0; d < directions.length; d++) { |
1106 |
var checkRow = $(this.element)[directions[d]](); |
1107 |
while (checkRow.length) {
|
1108 |
// Check that the sibling contains a similar target field.
|
1109 |
if ($('.' + rowSettings.target, checkRow)) { |
1110 |
// Either add immediately if this is a flat table, or check to ensure
|
1111 |
// that this row has the same level of indentation.
|
1112 |
if (this.indentEnabled) { |
1113 |
var checkRowIndentation = $('.indentation', checkRow).length; |
1114 |
} |
1115 |
|
1116 |
if (!(this.indentEnabled) || (checkRowIndentation == rowIndentation)) { |
1117 |
siblings.push(checkRow[0]);
|
1118 |
} |
1119 |
else if (checkRowIndentation < rowIndentation) { |
1120 |
// No need to keep looking for siblings when we get to a parent.
|
1121 |
break;
|
1122 |
} |
1123 |
} |
1124 |
else {
|
1125 |
break;
|
1126 |
} |
1127 |
checkRow = $(checkRow)[directions[d]]();
|
1128 |
} |
1129 |
// Since siblings are added in reverse order for previous, reverse the
|
1130 |
// completed list of previous siblings. Add the current row and continue.
|
1131 |
if (directions[d] == 'prev') { |
1132 |
siblings.reverse(); |
1133 |
siblings.push(this.element);
|
1134 |
} |
1135 |
} |
1136 |
return siblings;
|
1137 |
}; |
1138 |
|
1139 |
/**
|
1140 |
* Remove indentation helper classes from the current row group.
|
1141 |
*/
|
1142 |
Drupal.tableDrag.prototype.row.prototype.removeIndentClasses = function () { |
1143 |
for (var n in this.children) { |
1144 |
$('.indentation', this.children[n]) |
1145 |
.removeClass('tree-child')
|
1146 |
.removeClass('tree-child-first')
|
1147 |
.removeClass('tree-child-last')
|
1148 |
.removeClass('tree-child-horizontal');
|
1149 |
} |
1150 |
}; |
1151 |
|
1152 |
/**
|
1153 |
* Add an asterisk or other marker to the changed row.
|
1154 |
*/
|
1155 |
Drupal.tableDrag.prototype.row.prototype.markChanged = function () { |
1156 |
var marker = Drupal.theme('tableDragChangedMarker'); |
1157 |
var cell = $('td:first', this.element); |
1158 |
if ($('span.tabledrag-changed', cell).length == 0) { |
1159 |
cell.append(marker); |
1160 |
} |
1161 |
}; |
1162 |
|
1163 |
/**
|
1164 |
* Stub function. Allows a custom handler when a row is indented.
|
1165 |
*/
|
1166 |
Drupal.tableDrag.prototype.row.prototype.onIndent = function () { |
1167 |
return null; |
1168 |
}; |
1169 |
|
1170 |
/**
|
1171 |
* Stub function. Allows a custom handler when a row is swapped.
|
1172 |
*/
|
1173 |
Drupal.tableDrag.prototype.row.prototype.onSwap = function (swappedRow) { |
1174 |
return null; |
1175 |
}; |
1176 |
|
1177 |
Drupal.theme.prototype.tableDragChangedMarker = function () { |
1178 |
return '<span class="warning tabledrag-changed">*</span>'; |
1179 |
}; |
1180 |
|
1181 |
Drupal.theme.prototype.tableDragIndentation = function () { |
1182 |
return '<div class="indentation"> </div>'; |
1183 |
}; |
1184 |
|
1185 |
Drupal.theme.prototype.tableDragChangedWarning = function () { |
1186 |
return '<div class="tabledrag-changed-warning messages warning">' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>'; |
1187 |
}; |
1188 |
|
1189 |
})(jQuery); |