5a920362 |
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).parents('tr').eq(0)); |
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).parents('tr').eq(0)); |
47 | this.targetTextfield = $('.label-textfield', $(this).parents('tr').eq(0)); |
48 | |
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); |
56 | |
57 | if (updateText) { |
58 | $(this.targetTextfield).attr('value', (selectedField in fields ? fields[selectedField].label : '')); |
59 | } |
60 | }); |
61 | |
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); |
65 | }); |
66 | } |
67 | }; |
68 | |
69 | /** |
70 | * Populates options in a select input. |
71 | */ |
72 | jQuery.fn.fieldUIPopulateOptions = function (options, selected) { |
73 | return this.each(function () { |
74 | var disabled = false; |
75 | if (options.length == 0) { |
76 | options = [this.initialValue]; |
77 | disabled = true; |
78 | } |
79 | |
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; |
84 | |
85 | var html = ''; |
86 | jQuery.each(options, function (value, text) { |
87 | // Figure out which value should be selected. The 'selected' param |
88 | // takes precedence. |
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>'; |
91 | }); |
92 | |
93 | $(this).html(html).attr('disabled', disabled ? 'disabled' : ''); |
94 | }); |
95 | }; |
96 | |
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); |
101 | }); |
102 | } |
103 | }; |
104 | |
105 | Drupal.fieldUIOverview = { |
106 | /** |
107 | * Attaches the fieldUIOverview behavior. |
108 | */ |
109 | attach: function (table, rowsData, rowHandlers) { |
110 | var tableDrag = Drupal.tableDrag[table.id]; |
111 | |
112 | // Add custom tabledrag callbacks. |
113 | tableDrag.onDrop = this.onDrop; |
114 | tableDrag.row.prototype.onSwap = this.onSwap; |
115 | |
116 | // Create row handlers. |
117 | $('tr.draggable', table).each(function () { |
118 | // Extract server-side data for the row. |
119 | var row = this; |
120 | if (row.id in rowsData) { |
121 | var data = rowsData[row.id]; |
122 | data.tableDrag = tableDrag; |
123 | |
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); |
127 | } |
128 | }); |
129 | }, |
130 | |
131 | /** |
132 | * Event handler to be attached to form inputs triggering a region change. |
133 | */ |
134 | onChange: function () { |
135 | var $trigger = $(this); |
136 | var row = $trigger.parents('tr:first').get(0); |
137 | var rowHandler = $(row).data('fieldUIRowHandler'); |
138 | |
139 | var refreshRows = {}; |
140 | refreshRows[rowHandler.name] = $trigger.get(0); |
141 | |
142 | // Handle region change. |
143 | var region = rowHandler.getRegion(); |
144 | if (region != rowHandler.region) { |
145 | // Remove parenting. |
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; |
151 | } |
152 | |
153 | // Ajax-update the rows. |
154 | Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows); |
155 | }, |
156 | |
157 | /** |
158 | * Lets row handlers react when a row is dropped into a new region. |
159 | */ |
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'); |
167 | |
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); |
175 | } |
176 | } |
177 | }, |
178 | |
179 | /** |
180 | * Refreshes placeholder rows in empty regions while a row is being dragged. |
181 | * |
182 | * Copied from block.js. |
183 | * |
184 | * @param table |
185 | * The table DOM element. |
186 | * @param rowObject |
187 | * The tableDrag rowObject for the row being dragged. |
188 | */ |
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); |
198 | } |
199 | } |
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'); |
203 | } |
204 | // This region has become populated. |
205 | else if ($(this).is('.region-empty')) { |
206 | $(this).removeClass('region-empty').addClass('region-populated'); |
207 | } |
208 | }); |
209 | }, |
210 | |
211 | /** |
212 | * Triggers Ajax refresh of selected rows. |
213 | * |
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. |
217 | * |
218 | * @param rows |
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 |
222 | * throbber. |
223 | */ |
224 | AJAXRefreshRows: function (rows) { |
225 | // Separate keys and values. |
226 | var rowNames = []; |
227 | var ajaxElements = []; |
228 | $.each(rows, function (rowName, ajaxElement) { |
229 | rowNames.push(rowName); |
230 | ajaxElements.push(ajaxElement); |
231 | }); |
232 | |
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>'); |
236 | $(ajaxElements) |
237 | .addClass('progress-disabled') |
238 | .after($throbber); |
239 | |
240 | // Fire the Ajax update. |
241 | $('input[name=refresh_rows]').val(rowNames.join(' ')); |
242 | $('input#edit-refresh').mousedown(); |
243 | |
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); |
247 | } |
248 | } |
249 | }; |
250 | |
251 | |
252 | /** |
253 | * Row handlers for the 'Manage display' screen. |
254 | */ |
255 | Drupal.fieldUIDisplayOverview = {}; |
256 | |
257 | /** |
258 | * Constructor for a 'field' row handler. |
259 | * |
260 | * This handler is used for both fields and 'extra fields' rows. |
261 | * |
262 | * @param row |
263 | * The row DOM element. |
264 | * @param data |
265 | * Additional data to be populated in the constructed object. |
266 | */ |
267 | Drupal.fieldUIDisplayOverview.field = function (row, data) { |
268 | this.row = row; |
269 | this.name = data.name; |
270 | this.region = data.region; |
271 | this.tableDrag = data.tableDrag; |
272 | |
273 | // Attach change listener to the 'formatter type' select. |
274 | this.$formatSelect = $('select.field-formatter-type', row); |
275 | this.$formatSelect.change(Drupal.fieldUIOverview.onChange); |
276 | |
277 | return this; |
278 | }; |
279 | |
280 | Drupal.fieldUIDisplayOverview.field.prototype = { |
281 | /** |
282 | * Returns the region corresponding to the current form values of the row. |
283 | */ |
284 | getRegion: function () { |
285 | return (this.$formatSelect.val() == 'hidden') ? 'hidden' : 'visible'; |
286 | }, |
287 | |
288 | /** |
289 | * Reacts to a row being changed regions. |
290 | * |
291 | * This function is called when the row is moved to a different region, as a |
292 | * result of either : |
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. |
297 | * |
298 | * @param region |
299 | * The name of the new region for the row. |
300 | * @return |
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(). |
304 | */ |
305 | regionChange: function (region) { |
306 | |
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(); |
310 | switch (region) { |
311 | case 'visible': |
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'; |
316 | } |
317 | break; |
318 | |
319 | default: |
320 | var value = 'hidden'; |
321 | break; |
322 | } |
323 | if (value != undefined) { |
324 | this.$formatSelect.val(value); |
325 | } |
326 | |
327 | var refreshRows = {}; |
328 | refreshRows[this.name] = this.$formatSelect.get(0); |
329 | |
330 | return refreshRows; |
331 | } |
332 | }; |
333 | |
334 | })(jQuery); |