cms/drupal/modules/field_ui/field_ui.js
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 /**
       
     2  * @file
       
     3  * Attaches the behaviors for the Field UI module.
       
     4  */
       
     5  
       
     6 (function($) {
       
     7 
       
     8 Drupal.behaviors.fieldUIFieldOverview = {
       
     9   attach: function (context, settings) {
       
    10     $('table#field-overview', context).once('field-overview', function () {
       
    11       Drupal.fieldUIFieldOverview.attachUpdateSelects(this, settings);
       
    12     });
       
    13   }
       
    14 };
       
    15 
       
    16 Drupal.fieldUIFieldOverview = {
       
    17   /**
       
    18    * Implements dependent select dropdowns on the 'Manage fields' screen.
       
    19    */
       
    20   attachUpdateSelects: function(table, settings) {
       
    21     var widgetTypes = settings.fieldWidgetTypes;
       
    22     var fields = settings.fields;
       
    23 
       
    24     // Store the default text of widget selects.
       
    25     $('.widget-type-select', table).each(function () {
       
    26       this.initialValue = this.options[0].text;
       
    27     });
       
    28 
       
    29     // 'Field type' select updates its 'Widget' select.
       
    30     $('.field-type-select', table).each(function () {
       
    31       this.targetSelect = $('.widget-type-select', $(this).closest('tr'));
       
    32 
       
    33       $(this).bind('change keyup', function () {
       
    34         var selectedFieldType = this.options[this.selectedIndex].value;
       
    35         var options = (selectedFieldType in widgetTypes ? widgetTypes[selectedFieldType] : []);
       
    36         this.targetSelect.fieldUIPopulateOptions(options);
       
    37       });
       
    38 
       
    39       // Trigger change on initial pageload to get the right widget options
       
    40       // when field type comes pre-selected (on failed validation).
       
    41       $(this).trigger('change', false);
       
    42     });
       
    43 
       
    44     // 'Existing field' select updates its 'Widget' select and 'Label' textfield.
       
    45     $('.field-select', table).each(function () {
       
    46       this.targetSelect = $('.widget-type-select', $(this).closest('tr'));
       
    47       this.targetTextfield = $('.label-textfield', $(this).closest('tr'));
       
    48       this.targetTextfield
       
    49         .data('field_ui_edited', false)
       
    50         .bind('keyup', function (e) {
       
    51           $(this).data('field_ui_edited', $(this).val() != '');
       
    52         });
       
    53 
       
    54       $(this).bind('change keyup', function (e, updateText) {
       
    55         var updateText = (typeof updateText == 'undefined' ? true : updateText);
       
    56         var selectedField = this.options[this.selectedIndex].value;
       
    57         var selectedFieldType = (selectedField in fields ? fields[selectedField].type : null);
       
    58         var selectedFieldWidget = (selectedField in fields ? fields[selectedField].widget : null);
       
    59         var options = (selectedFieldType && (selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : []);
       
    60         this.targetSelect.fieldUIPopulateOptions(options, selectedFieldWidget);
       
    61 
       
    62         // Only overwrite the "Label" input if it has not been manually
       
    63         // changed, or if it is empty.
       
    64         if (updateText && !this.targetTextfield.data('field_ui_edited')) {
       
    65           this.targetTextfield.val(selectedField in fields ? fields[selectedField].label : '');
       
    66         }
       
    67       });
       
    68 
       
    69       // Trigger change on initial pageload to get the right widget options
       
    70       // and label when field type comes pre-selected (on failed validation).
       
    71       $(this).trigger('change', false);
       
    72     });
       
    73   }
       
    74 };
       
    75 
       
    76 /**
       
    77  * Populates options in a select input.
       
    78  */
       
    79 jQuery.fn.fieldUIPopulateOptions = function (options, selected) {
       
    80   return this.each(function () {
       
    81     var disabled = false;
       
    82     if (options.length == 0) {
       
    83       options = [this.initialValue];
       
    84       disabled = true;
       
    85     }
       
    86 
       
    87     // If possible, keep the same widget selected when changing field type.
       
    88     // This is based on textual value, since the internal value might be
       
    89     // different (options_buttons vs. node_reference_buttons).
       
    90     var previousSelectedText = this.options[this.selectedIndex].text;
       
    91 
       
    92     var html = '';
       
    93     jQuery.each(options, function (value, text) {
       
    94       // Figure out which value should be selected. The 'selected' param
       
    95       // takes precedence.
       
    96       var is_selected = ((typeof selected != 'undefined' && value == selected) || (typeof selected == 'undefined' && text == previousSelectedText));
       
    97       html += '<option value="' + value + '"' + (is_selected ? ' selected="selected"' : '') + '>' + text + '</option>';
       
    98     });
       
    99 
       
   100     $(this).html(html).attr('disabled', disabled ? 'disabled' : false);
       
   101   });
       
   102 };
       
   103 
       
   104 Drupal.behaviors.fieldUIDisplayOverview = {
       
   105   attach: function (context, settings) {
       
   106     $('table#field-display-overview', context).once('field-display-overview', function() {
       
   107       Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIDisplayOverview);
       
   108     });
       
   109   }
       
   110 };
       
   111 
       
   112 Drupal.fieldUIOverview = {
       
   113   /**
       
   114    * Attaches the fieldUIOverview behavior.
       
   115    */
       
   116   attach: function (table, rowsData, rowHandlers) {
       
   117     var tableDrag = Drupal.tableDrag[table.id];
       
   118 
       
   119     // Add custom tabledrag callbacks.
       
   120     tableDrag.onDrop = this.onDrop;
       
   121     tableDrag.row.prototype.onSwap = this.onSwap;
       
   122 
       
   123     // Create row handlers.
       
   124     $('tr.draggable', table).each(function () {
       
   125       // Extract server-side data for the row.
       
   126       var row = this;
       
   127       if (row.id in rowsData) {
       
   128         var data = rowsData[row.id];
       
   129         data.tableDrag = tableDrag;
       
   130 
       
   131         // Create the row handler, make it accessible from the DOM row element.
       
   132         var rowHandler = new rowHandlers[data.rowHandler](row, data);
       
   133         $(row).data('fieldUIRowHandler', rowHandler);
       
   134       }
       
   135     });
       
   136   },
       
   137 
       
   138   /**
       
   139    * Event handler to be attached to form inputs triggering a region change.
       
   140    */
       
   141   onChange: function () {
       
   142     var $trigger = $(this);
       
   143     var row = $trigger.closest('tr').get(0);
       
   144     var rowHandler = $(row).data('fieldUIRowHandler');
       
   145 
       
   146     var refreshRows = {};
       
   147     refreshRows[rowHandler.name] = $trigger.get(0);
       
   148 
       
   149     // Handle region change.
       
   150     var region = rowHandler.getRegion();
       
   151     if (region != rowHandler.region) {
       
   152       // Remove parenting.
       
   153       $('select.field-parent', row).val('');
       
   154       // Let the row handler deal with the region change.
       
   155       $.extend(refreshRows, rowHandler.regionChange(region));
       
   156       // Update the row region.
       
   157       rowHandler.region = region;
       
   158     }
       
   159 
       
   160     // Ajax-update the rows.
       
   161     Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
       
   162   },
       
   163 
       
   164   /**
       
   165    * Lets row handlers react when a row is dropped into a new region.
       
   166    */
       
   167   onDrop: function () {
       
   168     var dragObject = this;
       
   169     var row = dragObject.rowObject.element;
       
   170     var rowHandler = $(row).data('fieldUIRowHandler');
       
   171     if (typeof rowHandler !== 'undefined') {
       
   172       var regionRow = $(row).prevAll('tr.region-message').get(0);
       
   173       var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
       
   174 
       
   175       if (region != rowHandler.region) {
       
   176         // Let the row handler deal with the region change.
       
   177         refreshRows = rowHandler.regionChange(region);
       
   178         // Update the row region.
       
   179         rowHandler.region = region;
       
   180         // Ajax-update the rows.
       
   181         Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
       
   182       }
       
   183     }
       
   184   },
       
   185 
       
   186   /**
       
   187    * Refreshes placeholder rows in empty regions while a row is being dragged.
       
   188    *
       
   189    * Copied from block.js.
       
   190    *
       
   191    * @param table
       
   192    *   The table DOM element.
       
   193    * @param rowObject
       
   194    *   The tableDrag rowObject for the row being dragged.
       
   195    */
       
   196   onSwap: function (draggedRow) {
       
   197     var rowObject = this;
       
   198     $('tr.region-message', rowObject.table).each(function () {
       
   199       // If the dragged row is in this region, but above the message row, swap
       
   200       // it down one space.
       
   201       if ($(this).prev('tr').get(0) == rowObject.group[rowObject.group.length - 1]) {
       
   202         // Prevent a recursion problem when using the keyboard to move rows up.
       
   203         if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) {
       
   204           rowObject.swap('after', this);
       
   205         }
       
   206       }
       
   207       // This region has become empty.
       
   208       if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) {
       
   209         $(this).removeClass('region-populated').addClass('region-empty');
       
   210       }
       
   211       // This region has become populated.
       
   212       else if ($(this).is('.region-empty')) {
       
   213         $(this).removeClass('region-empty').addClass('region-populated');
       
   214       }
       
   215     });
       
   216   },
       
   217 
       
   218   /**
       
   219    * Triggers Ajax refresh of selected rows.
       
   220    *
       
   221    * The 'format type' selects can trigger a series of changes in child rows.
       
   222    * The #ajax behavior is therefore not attached directly to the selects, but
       
   223    * triggered manually through a hidden #ajax 'Refresh' button.
       
   224    *
       
   225    * @param rows
       
   226    *   A hash object, whose keys are the names of the rows to refresh (they
       
   227    *   will receive the 'ajax-new-content' effect on the server side), and
       
   228    *   whose values are the DOM element in the row that should get an Ajax
       
   229    *   throbber.
       
   230    */
       
   231   AJAXRefreshRows: function (rows) {
       
   232     // Separate keys and values.
       
   233     var rowNames = [];
       
   234     var ajaxElements = [];
       
   235     $.each(rows, function (rowName, ajaxElement) {
       
   236       rowNames.push(rowName);
       
   237       ajaxElements.push(ajaxElement);
       
   238     });
       
   239 
       
   240     if (rowNames.length) {
       
   241       // Add a throbber next each of the ajaxElements.
       
   242       var $throbber = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
       
   243       $(ajaxElements)
       
   244         .addClass('progress-disabled')
       
   245         .after($throbber);
       
   246 
       
   247       // Fire the Ajax update.
       
   248       $('input[name=refresh_rows]').val(rowNames.join(' '));
       
   249       $('input#edit-refresh').mousedown();
       
   250 
       
   251       // Disabled elements do not appear in POST ajax data, so we mark the
       
   252       // elements disabled only after firing the request.
       
   253       $(ajaxElements).attr('disabled', true);
       
   254     }
       
   255   }
       
   256 };
       
   257 
       
   258 
       
   259 /**
       
   260  * Row handlers for the 'Manage display' screen.
       
   261  */
       
   262 Drupal.fieldUIDisplayOverview = {};
       
   263 
       
   264 /**
       
   265  * Constructor for a 'field' row handler.
       
   266  *
       
   267  * This handler is used for both fields and 'extra fields' rows.
       
   268  *
       
   269  * @param row
       
   270  *   The row DOM element.
       
   271  * @param data
       
   272  *   Additional data to be populated in the constructed object.
       
   273  */
       
   274 Drupal.fieldUIDisplayOverview.field = function (row, data) {
       
   275   this.row = row;
       
   276   this.name = data.name;
       
   277   this.region = data.region;
       
   278   this.tableDrag = data.tableDrag;
       
   279 
       
   280   // Attach change listener to the 'formatter type' select.
       
   281   this.$formatSelect = $('select.field-formatter-type', row);
       
   282   this.$formatSelect.change(Drupal.fieldUIOverview.onChange);
       
   283 
       
   284   return this;
       
   285 };
       
   286 
       
   287 Drupal.fieldUIDisplayOverview.field.prototype = {
       
   288   /**
       
   289    * Returns the region corresponding to the current form values of the row.
       
   290    */
       
   291   getRegion: function () {
       
   292     return (this.$formatSelect.val() == 'hidden') ? 'hidden' : 'visible';
       
   293   },
       
   294 
       
   295   /**
       
   296    * Reacts to a row being changed regions.
       
   297    *
       
   298    * This function is called when the row is moved to a different region, as a
       
   299    * result of either :
       
   300    * - a drag-and-drop action (the row's form elements then probably need to be
       
   301    *   updated accordingly)
       
   302    * - user input in one of the form elements watched by the
       
   303    *   Drupal.fieldUIOverview.onChange change listener.
       
   304    *
       
   305    * @param region
       
   306    *   The name of the new region for the row.
       
   307    * @return
       
   308    *   A hash object indicating which rows should be Ajax-updated as a result
       
   309    *   of the change, in the format expected by
       
   310    *   Drupal.displayOverview.AJAXRefreshRows().
       
   311    */
       
   312   regionChange: function (region) {
       
   313 
       
   314     // When triggered by a row drag, the 'format' select needs to be adjusted
       
   315     // to the new region.
       
   316     var currentValue = this.$formatSelect.val();
       
   317     switch (region) {
       
   318       case 'visible':
       
   319         if (currentValue == 'hidden') {
       
   320           // Restore the formatter back to the default formatter. Pseudo-fields do
       
   321           // not have default formatters, we just return to 'visible' for those.
       
   322           var value = (typeof this.defaultFormatter !== 'undefined') ? this.defaultFormatter : this.$formatSelect.find('option').val();
       
   323         }
       
   324         break;
       
   325 
       
   326       default:
       
   327         var value = 'hidden';
       
   328         break;
       
   329     }
       
   330     if (value != undefined) {
       
   331       this.$formatSelect.val(value);
       
   332     }
       
   333 
       
   334     var refreshRows = {};
       
   335     refreshRows[this.name] = this.$formatSelect.get(0);
       
   336 
       
   337     return refreshRows;
       
   338   }
       
   339 };
       
   340 
       
   341 })(jQuery);