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