diff options
Diffstat (limited to 'modules/field_ui/field_ui.js')
-rw-r--r-- | modules/field_ui/field_ui.js | 446 |
1 files changed, 275 insertions, 171 deletions
diff --git a/modules/field_ui/field_ui.js b/modules/field_ui/field_ui.js index 86a8515fb..fda06d34d 100644 --- a/modules/field_ui/field_ui.js +++ b/modules/field_ui/field_ui.js @@ -2,61 +2,71 @@ (function($) { -Drupal.behaviors.fieldManageFields = { - attach: function (context) { - attachUpdateSelects(context); +Drupal.behaviors.fieldUIFieldOverview = { + attach: function (context, settings) { + $('table#field-overview', context).once('field-overview', function () { + Drupal.fieldUIFieldOverview.attachUpdateSelects(this, settings); + }); } }; -function attachUpdateSelects(context) { - var widgetTypes = Drupal.settings.fieldWidgetTypes; - var fields = Drupal.settings.fields; +Drupal.fieldUIFieldOverview = { + /** + * Implements dependent select dropdowns on the 'Manage fields' screen. + */ + attachUpdateSelects: function(table, settings) { + var widgetTypes = settings.fieldWidgetTypes; + var fields = settings.fields; + + // Store the default text of widget selects. + $('.widget-type-select', table).each(function () { + this.initialValue = this.options[0].text; + }); - // Store the default text of widget selects. - $('#field-overview .widget-type-select', context).each(function () { - this.initialValue = this.options[0].text; - }); + // 'Field type' select updates its 'Widget' select. + $('.field-type-select', table).each(function () { + this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); - // 'Field type' select updates its 'Widget' select. - $('#field-overview .field-type-select', context).each(function () { - this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); + $(this).bind('change keyup', function () { + var selectedFieldType = this.options[this.selectedIndex].value; + var options = (selectedFieldType in widgetTypes ? widgetTypes[selectedFieldType] : []); + this.targetSelect.fieldUIPopulateOptions(options); + }); - $(this).bind('change keyup', function () { - var selectedFieldType = this.options[this.selectedIndex].value; - var options = (selectedFieldType in widgetTypes ? widgetTypes[selectedFieldType] : []); - this.targetSelect.fieldPopulateOptions(options); + // Trigger change on initial pageload to get the right widget options + // when field type comes pre-selected (on failed validation). + $(this).trigger('change', false); }); - // Trigger change on initial pageload to get the right widget options - // when field type comes pre-selected (on failed validation). - $(this).trigger('change', false); - }); + // 'Existing field' select updates its 'Widget' select and 'Label' textfield. + $('.field-select', table).each(function () { + this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); + this.targetTextfield = $('.label-textfield', $(this).parents('tr').eq(0)); + + $(this).bind('change keyup', function (e, updateText) { + var updateText = (typeof updateText == 'undefined' ? true : updateText); + var selectedField = this.options[this.selectedIndex].value; + var selectedFieldType = (selectedField in fields ? fields[selectedField].type : null); + var selectedFieldWidget = (selectedField in fields ? fields[selectedField].widget : null); + var options = (selectedFieldType && (selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : []); + this.targetSelect.fieldUIPopulateOptions(options, selectedFieldWidget); + + if (updateText) { + $(this.targetTextfield).attr('value', (selectedField in fields ? fields[selectedField].label : '')); + } + }); - // 'Existing field' select updates its 'Widget' select and 'Label' textfield. - $('#field-overview .field-select', context).each(function () { - this.targetSelect = $('.widget-type-select', $(this).parents('tr').eq(0)); - this.targetTextfield = $('.label-textfield', $(this).parents('tr').eq(0)); - - $(this).bind('change keyup', function (e, updateText) { - var updateText = (typeof updateText == 'undefined' ? true : updateText); - var selectedField = this.options[this.selectedIndex].value; - var selectedFieldType = (selectedField in fields ? fields[selectedField].type : null); - var selectedFieldWidget = (selectedField in fields ? fields[selectedField].widget : null); - var options = (selectedFieldType && (selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : []); - this.targetSelect.fieldPopulateOptions(options, selectedFieldWidget); - - if (updateText) { - $(this.targetTextfield).attr('value', (selectedField in fields ? fields[selectedField].label : '')); - } + // Trigger change on initial pageload to get the right widget options + // and label when field type comes pre-selected (on failed validation). + $(this).trigger('change', false); }); + }, +}; - // Trigger change on initial pageload to get the right widget options - // and label when field type comes pre-selected (on failed validation). - $(this).trigger('change', false); - }); -} - -jQuery.fn.fieldPopulateOptions = function (options, selected) { +/** + * Populates options in a select input. + */ +jQuery.fn.fieldUIPopulateOptions = function (options, selected) { return this.each(function () { var disabled = false; if (options.length == 0) { @@ -81,145 +91,239 @@ jQuery.fn.fieldPopulateOptions = function (options, selected) { }); }; -/** - * Moves a field in the display settings table from visible to hidden. - * - * This behavior is dependent on the tableDrag behavior, since it uses the - * objects initialized in that behavior to update the row. - */ -Drupal.behaviors.fieldManageDisplayDrag = { +Drupal.behaviors.fieldUIDisplayOverview = { attach: function (context, settings) { - // tableDrag is required for this behavior. - if (!$('table.field-display-overview', context).length || typeof Drupal.tableDrag == 'undefined') { - return; + $('table#field-display-overview', context).once('field-display-overview', function() { + Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIDisplayOverview); + }); + } +}; + +Drupal.fieldUIOverview = { + /** + * Attaches the fieldUIOverview behavior. + */ + attach: function (table, rowsData, rowHandlers) { + var tableDrag = Drupal.tableDrag[table.id]; + + // Add custom tabledrag callbacks. + tableDrag.onDrop = this.onDrop; + tableDrag.row.prototype.onSwap = this.onSwap; + + // Create row handlers. + $('tr.draggable', table).each(function () { + // Extract server-side data for the row. + var row = this; + var data = rowsData[row.id]; + data.tableDrag = tableDrag; + + // Create the row handler, make it accessible from the DOM row element. + var rowHandler = eval('new rowHandlers.' + data.rowHandler + '(row, data);'); + $(row).data('fieldUIRowHandler', rowHandler); + }); + }, + + /** + * Event handler to be attached to form inputs triggering a region change. + */ + onChange: function () { + var $trigger = $(this); + var row = $trigger.parents('tr:first').get(0); + var rowHandler = $(row).data('fieldUIRowHandler'); + + var refreshRows = {}; + refreshRows[rowHandler.name] = $trigger.get(0); + + // Handle region change. + var region = rowHandler.getRegion(); + if (region != rowHandler.region) { + // Remove parenting. + $('select.field-parent', row).val(''); + // Let the row handler deal with the region change. + $.extend(refreshRows, rowHandler.regionChange(region)); + // Update the row region. + rowHandler.region = region; } - var defaultFormatters = Drupal.settings.fieldDefaultFormatters; - var tableDrag = Drupal.tableDrag['field-display-overview']; - - // Add a handler for when a row is swapped, update empty regions. - tableDrag.row.prototype.onSwap = function (swappedRow) { - checkEmptyRegions(this.table, this); - }; - - // Add a handler to update the formatter selector when a row is dropped in - // or out of the 'Hidden' section. - tableDrag.onDrop = function () { - var dragObject = this; - var regionRow = $(dragObject.rowObject.element).prevAll('tr.region-message').get(0); - var visibility = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); - - // Update the 'format' selector if the visibility changed. - var $select = $('select.field-formatter-type', dragObject.rowObject.element); - var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); - if (visibility != oldVisibility) { - $select.removeClass('field-display-' + oldVisibility).addClass('field-display-' + visibility); - - // Update the selected formatter if coming from an actual drag. - if (!$select.data('noUpdate')) { - if (visibility == 'visible') { - // Restore the formatter back to the previously selected one if - // available, or to the default formatter. - var value = $select.data('oldFormatter'); - if (typeof value == 'undefined') { - // Extract field name from the name of the select. - var fieldName = $select[0].className.match(/\bfield-name-(\S+)\b/)[1].replace('-', '_'); - // Pseudo-fields do not have an entry in the defaultFormatters - // array, we just return to 'visible' for those. - value = (fieldName in defaultFormatters) ? defaultFormatters[fieldName] : 'visible'; - } - $select.data('oldFormatter', value); - } - else { - var value = 'hidden'; - } - $select.val(value); - // Fire AJAX update of formatter settings. - $select.change(); + // Ajax-update the rows. + Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows); + }, + + /** + * Lets row handlers react when a row is dropped into a new region. + */ + onDrop: function () { + var dragObject = this; + var row = dragObject.rowObject.element; + var rowHandler = $(row).data('fieldUIRowHandler'); + + var regionRow = $(row).prevAll('tr.region-message').get(0); + var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); + + if (region != rowHandler.region) { + // Let the row handler deal with the region change. + refreshRows = rowHandler.regionChange(region); + // Update the row region. + rowHandler.region = region; + // AJAX-update the rows. + Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows); + } + }, + + /** + * Refreshes placeholder rows in empty regions while a row is being dragged. + * + * Copied from block.js. + * + * @param table + * The table DOM element. + * @param rowObject + * The tableDrag rowObject for the row being dragged. + */ + onSwap: function (draggedRow) { + var rowObject = this; + $('tr.region-message', rowObject.table).each(function () { + // If the dragged row is in this region, but above the message row, swap + // it down one space. + if ($(this).prev('tr').get(0) == rowObject.group[rowObject.group.length - 1]) { + // Prevent a recursion problem when using the keyboard to move rows up. + if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) { + rowObject.swap('after', this); } - $select.removeData('noUpdate'); } - }; - - // Add the behavior to each formatter select list. - $('select.field-formatter-type', context).once('field-formatter-type', function () { - // Initialize 'previously selected formatter' as the incoming value. - if ($(this).val() != 'hidden') { - $(this).data('oldFormatter', $(this).val()); + // This region has become empty. + if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) { + $(this).removeClass('region-populated').addClass('region-empty'); } + // This region has become populated. + else if ($(this).is('.region-empty')) { + $(this).removeClass('region-empty').addClass('region-populated'); + } + }); + }, + + /** + * Triigers AJAX refresh of selected rows. + * + * The 'format type' selects can trigger a series of changes in child rows. + * The #ajax behavior is therefore not attached directly to the selects, but + * triggered manually through a hidden #ajax 'Refresh' button. + * + * @param rows + * A hash object, whose keys are the names of the rows to refresh (they + * will receive the 'ajax-new-content' effect on the server side), and + * whose values are the DOM element in the row that should get an AJAX + * throbber. + */ + AJAXRefreshRows: function (rows) { + // Separate keys and values. + var rowNames = []; + var ajaxElements = []; + $.each(rows, function (rowName, ajaxElement) { + rowNames.push(rowName); + ajaxElements.push(ajaxElement); + }); - // Add change listener. - $(this).change(function (event) { - var $select = $(this); - var value = $select.val(); + if (rowNames.length) { + // Add a throbber next each of the ajaxElements. + var $throbber = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); + $(ajaxElements) + .addClass('progress-disabled') + .after($throbber); - // Keep track of the last selected formatter. - if (value != 'hidden') { - $select.data('oldFormatter', value); - } + // Fire the AJAX update. + $('input[name=refresh_rows]').val(rowNames.join(' ')); + $('input#edit-refresh').mousedown(); - var visibility = (value == 'hidden') ? 'hidden' : 'visible'; - var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); - if (visibility != oldVisibility) { - // Prevent the onDrop handler from overriding the selected option. - $select.data('noUpdate', true); - - // Make our new row and select field. - var $row = $(this).parents('tr:first'); - var $table = $(this).parents('table'); - var tableDrag = Drupal.tableDrag[$table.attr('id')]; - tableDrag.rowObject = new tableDrag.row($row); - - // Move the row at the bottom of the new section. - if (visibility == 'hidden') { - $('tr:last', tableDrag.table).after($row); - } - else { - $('tr.region-title-hidden', tableDrag.table).before($row); - } - - // Manually update weights and restripe. - tableDrag.updateFields($row.get(0)); - tableDrag.rowObject.changed = true; - if (tableDrag.oldRowElement) { - $(tableDrag.oldRowElement).removeClass('drag-previous'); - } - tableDrag.oldRowElement = $row.get(0); - tableDrag.restripeTable(); - tableDrag.rowObject.markChanged(); - tableDrag.oldRowElement = $row; - $row.addClass('drag-previous'); - - // Modify empty regions with added or removed fields. - checkEmptyRegions($table, tableDrag.rowObject); - } + // Disabled elements do not appear in POST ajax data, so we mark the + // elements disabled only after firing the request. + $(ajaxElements).attr('disabled', true); + } + }, +}; - // Remove focus from selectbox. - $select.get(0).blur(); - }); - }); - var checkEmptyRegions = function ($table, rowObject) { - $('tr.region-message', $table).each(function () { - // If the dragged row is in this region, but above the message row, swap - // it down one space. - if ($(this).prev('tr').get(0) == rowObject.element) { - // Prevent a recursion problem when using the keyboard to move rows up. - if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) { - rowObject.swap('after', this); - } - } - // This region has become empty. - if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) { - $(this).removeClass('region-populated').addClass('region-empty'); - } - // This region has become populated. - else if ($(this).is('.region-empty')) { - $(this).removeClass('region-empty').addClass('region-populated'); +/** + * Row handlers for the 'Manage display' screen. + */ +Drupal.fieldUIDisplayOverview = {}; + +/** + * Constructor for a 'field' row handler. + * + * This handler is used for both fields and 'extra fields' rows. + * + * @param row + * The row DOM element. + * @param data + * Additional data to be populated in the constructed object. + */ +Drupal.fieldUIDisplayOverview.field = function (row, data) { + this.row = row; + this.name = data.name; + this.region = data.region; + this.tableDrag = data.tableDrag; + + // Attach change listener to the 'formatter type' select. + this.$formatSelect = $('select.field-formatter-type', row); + this.$formatSelect.change(Drupal.fieldUIOverview.onChange); + + return this; +}; + +Drupal.fieldUIDisplayOverview.field.prototype = { + /** + * Returns the region corresponding to the current form values of the row. + */ + getRegion: function () { + return (this.$formatSelect.val() == 'hidden') ? 'hidden' : 'visible'; + }, + + /** + * Reacts to a row being changed regions. + * + * This function is called when the row is moved to a different region, as a + * result of either : + * - a drag-and-drop action (the row's form elements then probably need to be + * updated accordingly) + * - user input in one of the form elements watched by the + * Drupal.fieldUIOverview.onChange change listener. + * + * @param region + * The name of the new region for the row. + * @return + * A hash object indicating which rows should be AJAX-updated as a result + * of the change, in the format expected by + * Drupal.displayOverview.AJAXRefreshRows(). + */ + regionChange: function (region) { + $.debug(this.region + ' --> ' + region, this.name); + + // When triggered by a row drag, the 'format' select needs to be adjusted + // to the new region. + var currentValue = this.$formatSelect.val(); + switch (region) { + case 'visible': + if (currentValue == 'hidden') { + // Restore the formatter back to the default formatter. Pseudo-fields do + // not have default formatters, we just return to 'visible' for those. + var value = (this.defaultFormatter != undefined) ? this.defaultFormatter : 'visible'; } - }); - }; - } + break; + + default: + var value = 'hidden'; + break; + } + if (value != undefined) { + this.$formatSelect.val(value); + } + + var refreshRows = {}; + refreshRows[this.name] = this.$formatSelect.get(0); + + return refreshRows; + }, }; })(jQuery); |