Mercurial > hg > rr-repo
comparison misc/tabledrag.js @ 0:ff03f76ab3fe
initial version
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Wed, 21 Aug 2013 18:51:11 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:ff03f76ab3fe |
---|---|
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); |