3 * Attaches the behaviors for the Field UI module.
8 Drupal
.behaviors
.fieldUIFieldOverview
= {
9 attach: function (context
, settings
) {
10 $('table#field-overview', context
).once('field-overview', function () {
11 Drupal
.fieldUIFieldOverview
.attachUpdateSelects(this, settings
);
16 Drupal
.fieldUIFieldOverview
= {
18 * Implements dependent select dropdowns on the 'Manage fields' screen.
20 attachUpdateSelects: function(table
, settings
) {
21 var widgetTypes
= settings
.fieldWidgetTypes
;
22 var fields
= settings
.fields
;
24 // Store the default text of widget selects.
25 $('.widget-type-select', table
).each(function () {
26 this.initialValue
= this.options
[0].text
;
29 // 'Field type' select updates its 'Widget' select.
30 $('.field-type-select', table
).each(function () {
31 this.targetSelect
= $('.widget-type-select', $(this).closest('tr'));
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
);
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);
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'));
49 .data('field_ui_edited', false)
50 .bind('keyup', function (e
) {
51 $(this).data('field_ui_edited', $(this).val() != '');
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
);
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
: '');
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);
77 * Populates options in a select input.
79 jQuery
.fn
.fieldUIPopulateOptions = function (options
, selected
) {
80 return this.each(function () {
82 if (options
.length
== 0) {
83 options
= [this.initialValue
];
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
;
93 jQuery
.each(options
, function (value
, text
) {
94 // Figure out which value should be selected. The 'selected' param
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>';
100 $(this).html(html
).attr('disabled', disabled
? 'disabled' : false);
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
);
112 Drupal
.fieldUIOverview
= {
114 * Attaches the fieldUIOverview behavior.
116 attach: function (table
, rowsData
, rowHandlers
) {
117 var tableDrag
= Drupal
.tableDrag
[table
.id
];
119 // Add custom tabledrag callbacks.
120 tableDrag
.onDrop
= this.onDrop
;
121 tableDrag
.row
.prototype.onSwap
= this.onSwap
;
123 // Create row handlers.
124 $('tr.draggable', table
).each(function () {
125 // Extract server-side data for the row.
127 if (row
.id
in rowsData
) {
128 var data
= rowsData
[row
.id
];
129 data
.tableDrag
= tableDrag
;
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
);
139 * Event handler to be attached to form inputs triggering a region change.
141 onChange: function () {
142 var $trigger
= $(this);
143 var row
= $trigger
.closest('tr').get(0);
144 var rowHandler
= $(row
).data('fieldUIRowHandler');
146 var refreshRows
= {};
147 refreshRows
[rowHandler
.name
] = $trigger
.get(0);
149 // Handle region change.
150 var region
= rowHandler
.getRegion();
151 if (region
!= rowHandler
.region
) {
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
;
160 // Ajax-update the rows.
161 Drupal
.fieldUIOverview
.AJAXRefreshRows(refreshRows
);
165 * Lets row handlers react when a row is dropped into a new region.
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');
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
);
187 * Refreshes placeholder rows in empty regions while a row is being dragged.
189 * Copied from block.js.
192 * The table DOM element.
194 * The tableDrag rowObject for the row being dragged.
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);
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');
211 // This region has become populated.
212 else if ($(this).is('.region-empty')) {
213 $(this).removeClass('region-empty').addClass('region-populated');
219 * Triggers Ajax refresh of selected rows.
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.
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
231 AJAXRefreshRows: function (rows
) {
232 // Separate keys and values.
234 var ajaxElements
= [];
235 $.each(rows
, function (rowName
, ajaxElement
) {
236 rowNames
.push(rowName
);
237 ajaxElements
.push(ajaxElement
);
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"> </div></div>');
244 .addClass('progress-disabled')
247 // Fire the Ajax update.
248 $('input[name=refresh_rows]').val(rowNames
.join(' '));
249 $('input#edit-refresh').mousedown();
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);
260 * Row handlers for the 'Manage display' screen.
262 Drupal
.fieldUIDisplayOverview
= {};
265 * Constructor for a 'field' row handler.
267 * This handler is used for both fields and 'extra fields' rows.
270 * The row DOM element.
272 * Additional data to be populated in the constructed object.
274 Drupal
.fieldUIDisplayOverview
.field = function (row
, data
) {
276 this.name
= data
.name
;
277 this.region
= data
.region
;
278 this.tableDrag
= data
.tableDrag
;
280 // Attach change listener to the 'formatter type' select.
281 this.$formatSelect
= $('select.field-formatter-type', row
);
282 this.$formatSelect
.change(Drupal
.fieldUIOverview
.onChange
);
287 Drupal
.fieldUIDisplayOverview
.field
.prototype = {
289 * Returns the region corresponding to the current form values of the row.
291 getRegion: function () {
292 return (this.$formatSelect
.val() == 'hidden') ? 'hidden' : 'visible';
296 * Reacts to a row being changed regions.
298 * This function is called when the row is moved to a different region, as a
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.
306 * The name of the new region for the row.
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().
312 regionChange: function (region
) {
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();
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();
327 var value
= 'hidden';
330 if (value
!= undefined) {
331 this.$formatSelect
.val(value
);
334 var refreshRows
= {};
335 refreshRows
[this.name
] = this.$formatSelect
.get(0);