root / drupal7 / modules / field_ui / field_ui.js @ 6eb57d7f
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"> </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); |