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).parents('tr').eq(0));
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).parents('tr').eq(0));
47 this.targetTextfield
= $('.label-textfield', $(this).parents('tr').eq(0));
49 $(this).bind('change keyup', function (e
, updateText
) {
50 var updateText
= (typeof updateText
== 'undefined' ? true : updateText
);
51 var selectedField
= this.options
[this.selectedIndex
].value
;
52 var selectedFieldType
= (selectedField
in fields
? fields
[selectedField
].type
: null);
53 var selectedFieldWidget
= (selectedField
in fields
? fields
[selectedField
].widget
: null);
54 var options
= (selectedFieldType
&& (selectedFieldType
in widgetTypes
) ? widgetTypes
[selectedFieldType
] : []);
55 this.targetSelect
.fieldUIPopulateOptions(options
, selectedFieldWidget
);
58 $(this.targetTextfield
).attr('value', (selectedField
in fields
? fields
[selectedField
].label
: ''));
62 // Trigger change on initial pageload to get the right widget options
63 // and label when field type comes pre-selected (on failed validation).
64 $(this).trigger('change', false);
70 * Populates options in a select input.
72 jQuery
.fn
.fieldUIPopulateOptions = function (options
, selected
) {
73 return this.each(function () {
75 if (options
.length
== 0) {
76 options
= [this.initialValue
];
80 // If possible, keep the same widget selected when changing field type.
81 // This is based on textual value, since the internal value might be
82 // different (options_buttons vs. node_reference_buttons).
83 var previousSelectedText
= this.options
[this.selectedIndex
].text
;
86 jQuery
.each(options
, function (value
, text
) {
87 // Figure out which value should be selected. The 'selected' param
89 var is_selected
= ((typeof selected
!= 'undefined' && value
== selected
) || (typeof selected
== 'undefined' && text
== previousSelectedText
));
90 html
+= '<option value="' + value
+ '"' + (is_selected
? ' selected="selected"' : '') + '>' + text
+ '</option>';
93 $(this).html(html
).attr('disabled', disabled
? 'disabled' : '');
97 Drupal
.behaviors
.fieldUIDisplayOverview
= {
98 attach: function (context
, settings
) {
99 $('table#field-display-overview', context
).once('field-display-overview', function() {
100 Drupal
.fieldUIOverview
.attach(this, settings
.fieldUIRowsData
, Drupal
.fieldUIDisplayOverview
);
105 Drupal
.fieldUIOverview
= {
107 * Attaches the fieldUIOverview behavior.
109 attach: function (table
, rowsData
, rowHandlers
) {
110 var tableDrag
= Drupal
.tableDrag
[table
.id
];
112 // Add custom tabledrag callbacks.
113 tableDrag
.onDrop
= this.onDrop
;
114 tableDrag
.row
.prototype.onSwap
= this.onSwap
;
116 // Create row handlers.
117 $('tr.draggable', table
).each(function () {
118 // Extract server-side data for the row.
120 if (row
.id
in rowsData
) {
121 var data
= rowsData
[row
.id
];
122 data
.tableDrag
= tableDrag
;
124 // Create the row handler, make it accessible from the DOM row element.
125 var rowHandler
= new rowHandlers
[data
.rowHandler
](row
, data
);
126 $(row
).data('fieldUIRowHandler', rowHandler
);
132 * Event handler to be attached to form inputs triggering a region change.
134 onChange: function () {
135 var $trigger
= $(this);
136 var row
= $trigger
.parents('tr:first').get(0);
137 var rowHandler
= $(row
).data('fieldUIRowHandler');
139 var refreshRows
= {};
140 refreshRows
[rowHandler
.name
] = $trigger
.get(0);
142 // Handle region change.
143 var region
= rowHandler
.getRegion();
144 if (region
!= rowHandler
.region
) {
146 $('select.field-parent', row
).val('');
147 // Let the row handler deal with the region change.
148 $.extend(refreshRows
, rowHandler
.regionChange(region
));
149 // Update the row region.
150 rowHandler
.region
= region
;
153 // Ajax-update the rows.
154 Drupal
.fieldUIOverview
.AJAXRefreshRows(refreshRows
);
158 * Lets row handlers react when a row is dropped into a new region.
160 onDrop: function () {
161 var dragObject
= this;
162 var row
= dragObject
.rowObject
.element
;
163 var rowHandler
= $(row
).data('fieldUIRowHandler');
164 if (rowHandler
!== undefined) {
165 var regionRow
= $(row
).prevAll('tr.region-message').get(0);
166 var region
= regionRow
.className
.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
168 if (region
!= rowHandler
.region
) {
169 // Let the row handler deal with the region change.
170 refreshRows
= rowHandler
.regionChange(region
);
171 // Update the row region.
172 rowHandler
.region
= region
;
173 // Ajax-update the rows.
174 Drupal
.fieldUIOverview
.AJAXRefreshRows(refreshRows
);
180 * Refreshes placeholder rows in empty regions while a row is being dragged.
182 * Copied from block.js.
185 * The table DOM element.
187 * The tableDrag rowObject for the row being dragged.
189 onSwap: function (draggedRow
) {
190 var rowObject
= this;
191 $('tr.region-message', rowObject
.table
).each(function () {
192 // If the dragged row is in this region, but above the message row, swap
193 // it down one space.
194 if ($(this).prev('tr').get(0) == rowObject
.group
[rowObject
.group
.length
- 1]) {
195 // Prevent a recursion problem when using the keyboard to move rows up.
196 if ((rowObject
.method
!= 'keyboard' || rowObject
.direction
== 'down')) {
197 rowObject
.swap('after', this);
200 // This region has become empty.
201 if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length
== 0) {
202 $(this).removeClass('region-populated').addClass('region-empty');
204 // This region has become populated.
205 else if ($(this).is('.region-empty')) {
206 $(this).removeClass('region-empty').addClass('region-populated');
212 * Triggers Ajax refresh of selected rows.
214 * The 'format type' selects can trigger a series of changes in child rows.
215 * The #ajax behavior is therefore not attached directly to the selects, but
216 * triggered manually through a hidden #ajax 'Refresh' button.
219 * A hash object, whose keys are the names of the rows to refresh (they
220 * will receive the 'ajax-new-content' effect on the server side), and
221 * whose values are the DOM element in the row that should get an Ajax
224 AJAXRefreshRows: function (rows
) {
225 // Separate keys and values.
227 var ajaxElements
= [];
228 $.each(rows
, function (rowName
, ajaxElement
) {
229 rowNames
.push(rowName
);
230 ajaxElements
.push(ajaxElement
);
233 if (rowNames
.length
) {
234 // Add a throbber next each of the ajaxElements.
235 var $throbber
= $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>');
237 .addClass('progress-disabled')
240 // Fire the Ajax update.
241 $('input[name=refresh_rows]').val(rowNames
.join(' '));
242 $('input#edit-refresh').mousedown();
244 // Disabled elements do not appear in POST ajax data, so we mark the
245 // elements disabled only after firing the request.
246 $(ajaxElements
).attr('disabled', true);
253 * Row handlers for the 'Manage display' screen.
255 Drupal
.fieldUIDisplayOverview
= {};
258 * Constructor for a 'field' row handler.
260 * This handler is used for both fields and 'extra fields' rows.
263 * The row DOM element.
265 * Additional data to be populated in the constructed object.
267 Drupal
.fieldUIDisplayOverview
.field = function (row
, data
) {
269 this.name
= data
.name
;
270 this.region
= data
.region
;
271 this.tableDrag
= data
.tableDrag
;
273 // Attach change listener to the 'formatter type' select.
274 this.$formatSelect
= $('select.field-formatter-type', row
);
275 this.$formatSelect
.change(Drupal
.fieldUIOverview
.onChange
);
280 Drupal
.fieldUIDisplayOverview
.field
.prototype = {
282 * Returns the region corresponding to the current form values of the row.
284 getRegion: function () {
285 return (this.$formatSelect
.val() == 'hidden') ? 'hidden' : 'visible';
289 * Reacts to a row being changed regions.
291 * This function is called when the row is moved to a different region, as a
293 * - a drag-and-drop action (the row's form elements then probably need to be
294 * updated accordingly)
295 * - user input in one of the form elements watched by the
296 * Drupal.fieldUIOverview.onChange change listener.
299 * The name of the new region for the row.
301 * A hash object indicating which rows should be Ajax-updated as a result
302 * of the change, in the format expected by
303 * Drupal.displayOverview.AJAXRefreshRows().
305 regionChange: function (region
) {
307 // When triggered by a row drag, the 'format' select needs to be adjusted
308 // to the new region.
309 var currentValue
= this.$formatSelect
.val();
312 if (currentValue
== 'hidden') {
313 // Restore the formatter back to the default formatter. Pseudo-fields do
314 // not have default formatters, we just return to 'visible' for those.
315 var value
= (this.defaultFormatter
!= undefined) ? this.defaultFormatter
: 'visible';
320 var value
= 'hidden';
323 if (value
!= undefined) {
324 this.$formatSelect
.val(value
);
327 var refreshRows
= {};
328 refreshRows
[this.name
] = this.$formatSelect
.get(0);