// $Id$ /** * Private namespace for local methods. */ Drupal.contentRemoveButtons = Drupal.contentRemoveButtons || {}; /** * Manipulation of content remove buttons. * * TableDrag objects for multiple value fields (and multigroups) are scanned * to find 'remove' checkboxes. These checkboxes are hidden when javascript is * enabled (using the Global CSS Killswitch, html.js, defined in drupal.js). * A new 'remove' button is created here in place of these checkboxes aimed to * provide a more user-friendly method to remove items. */ Drupal.behaviors.contentRemoveButtons = function(context) { var self = Drupal.contentRemoveButtons; $('table.content-multiple-table', context).not('.content-remove-buttons-processed').addClass('content-remove-buttons-processed').each(function() { var table = this, tableDrag = Drupal.tableDrag[$(table).attr('id')]; // Replace remove checkboxes with buttons. $('input.content-multiple-remove-checkbox', table).each(function() { var $checkbox = $(this), $row = $checkbox.parents('tr:first'); var isRemoved = $checkbox.attr('checked'); var $button = $(Drupal.theme('contentRemoveButton', tableDrag.getRemoveButtonTitle(isRemoved))); // Bind the onClick event to the remove button. $button.bind('click', function(event) { self.onClick($button, $checkbox, $row, tableDrag); return false; }); // Attach the new button to the DOM tree. $checkbox.parent().append($button); // If the row is removed, then hide the contents of the cells and show // the removed warning on the cell next to the drag'n'drop cell. if (isRemoved) { self.getCellWrappers($row).hide(); self.showRemovedWarning($row, tableDrag); // FAPI not rendering the form on errors - case #1: // If the form has been submitted and any error was found, FAPI will // send back the same exact form that was submitted to show the error // messages, but it will not invoke the rendering engine which is where // we actually assign the removed class to the row, so we need to check // this situation here and add the class if it is not present. if (!$row.hasClass('content-multiple-removed-row')) { $row.addClass('content-multiple-removed-row'); } } else { // FAPI not rendering the form on errors - case #2: // Similar issue than #1, but this time caused when user removes an // item, previews, FAPI renders the new form with the removed class, // then user changes anything in the form that causes an error, and // also restores the previously removed item. This time, FAPI will // send the form validation error with the item not flagged for removal // but having the removed class that was present when the form was // rendered in the previous step. So we need to remove this class here, // if present, because the item is not really flagged for removal. if ($row.hasClass('content-multiple-removed-row')) { $row.removeClass('content-multiple-removed-row'); } } }); }); }; /** * onClick handler for remove buttons. * * @param $button * The jQuery object of the remove button. * @param $checkbox * The jQuery object of the remove checkbox. * @param $row * The jQuery object of the table row. * @param tableDrag * The tableDrag object where the row is. */ Drupal.contentRemoveButtons.onClick = function($button, $checkbox, $row, tableDrag) { var self = Drupal.contentRemoveButtons; // Prevent the user from firing this event while another one is still being // processed. This flag is (should be) restored at end of animations. // Note that this technique is required because the browser may experience // delays while performing the animation, for whatever reason, and if this // process it fired more than once at the same time for the same row, then // it may cause unexpected behavior because the state of the elements being // manipulated would be unknown. if ($row.animating) { return; } $row.animating = true; // Toggle the state of the checkbox. var isRemoved = !$checkbox.attr('checked'); $checkbox.attr('checked', isRemoved); // Toggle the row class. if (isRemoved) { $row.addClass('content-multiple-removed-row'); } else { $row.removeClass('content-multiple-removed-row'); } // Toggle the button title. $button.attr('title', tableDrag.getRemoveButtonTitle(isRemoved)); // Get the list of cell wrappers in this row. var $cellWrappers = self.getCellWrappers($row); // If for whatever reason this row doesn't have cells with elements, // then we are done, but we still need to reset the global busy flag // and display the tableDrag changed warning. if (!$cellWrappers.size()) { tableDrag.displayChangedWarning(); $row.animating = false; return; } // Toggle the visible state of the row cells. $cellWrappers.each(function() { var $cellWrapper = $(this); // Drop the removed warning during restore operation. if (!isRemoved) { self.hideRemovedWarning($row); } // Toggle the visibility state of the contents of cells. $cellWrapper.animate({opacity: (isRemoved ? 'hide' : 'show')}, 'fast', function() { var $cell = $cellWrapper.parent(); // Show the removed warning during remove operation. if (isRemoved && $cell.prev(':first').hasClass('content-multiple-drag')) { self.showRemovedWarning($row, tableDrag); } // Disable the busy flag when animation of last cell has finished. if ($cell.next(':first').hasClass('delta-order')) { tableDrag.displayChangedWarning(); $row.animating = false; } }); }); }; /** * Show the removed warning on the given row. * * @param $row * The jQuery object of the table row. * @param tableDrag * The tableDrag object where the row is. */ Drupal.contentRemoveButtons.showRemovedWarning = function($row, tableDrag) { $('.content-multiple-drag', $row).next(':first').append(Drupal.theme('contentRemovedWarning', tableDrag.getRemovedWarning())); }; /** * Hide the removed warning from the given row. * * @param $row * The jQuery object of the table row. */ Drupal.contentRemoveButtons.hideRemovedWarning = function($row) { if ($('.content-multiple-removed-warning', $row).size()) { $('.content-multiple-removed-warning', $row).remove(); } }; /** * Get cell wrappers for the given row. * * @param $row * The jQuery object of the table row. */ Drupal.contentRemoveButtons.getCellWrappers = function($row) { // Create cell wrappers if this row has not already been processed. if (!$('.content-multiple-cell-content-wrapper', $row).size()) { // Wrap the contents of all cells (except the drag'n'drop, weight and // remove button cells) with a dummy block element. This operation makes // animations faster because we just need to show/hide a single element // per cell, and it also prevents from creating more than one warning // element per row. $row.children('td:not(.content-multiple-drag):not(.delta-order):not(.content-multiple-remove-cell)').each(function() { var $cell = $(this); $cell.wrapInner('
'); }); } return $('.content-multiple-cell-content-wrapper', $row); }; /** * Display table change warning when appropriate. */ Drupal.tableDrag.prototype.displayChangedWarning = function() { if (this.changed == false) { $(Drupal.theme('tableDragChangedWarning')).insertAfter(this.table).hide().fadeIn('slow'); this.changed = true; } }; /** * Get the title of the remove button. * * This method is an extension of the tableDrag class. This means a separate * module can override this method for a particular tableDrag instance. For * example, the multigroup module can change the text to read 'Remove this * group of items', another module could change it to 'Remove this image', * and so on... * To override this function: * * @code * var tableId = $(table).attr('id'); * Drupal.tableDrag[tableId].getRemoveButtonTitle = function(isRemoved) { * return (isRemoved ? Drupal.t('Restore this foo') : Drupal.t('Remove this foo')); * }; * @endcode * * @param isRemoved * A flag that indicates the state of the button. */ Drupal.tableDrag.prototype.getRemoveButtonTitle = function(isRemoved) { return (isRemoved ? Drupal.t('Restore this item') : Drupal.t('Remove this item')); }; /** * Get the item removed warning. * * This method is an extension of the tableDrag class. It can be overridden by * a separate module. See getRemoveButtonTitle() for further information. */ Drupal.tableDrag.prototype.getRemovedWarning = function() { return Drupal.t('Removed'); }; /** * Theme the remove button. */ Drupal.theme.prototype.contentRemoveButton = function(title) { return ''; }; /** * Theme the item removed warning. */ Drupal.theme.prototype.contentRemovedWarning = function(warning) { return '
'+ warning +'
'; };