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