Mercurial > hg > rr-repo
comparison sites/all/modules/features/features.js @ 4:ce11bbd8f642
added modules
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Thu, 19 Sep 2013 10:38:44 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
3:b28be78d8160 | 4:ce11bbd8f642 |
---|---|
1 /** | |
2 * jQuery.fn.sortElements | |
3 * -------------- | |
4 * @param Function comparator: | |
5 * Exactly the same behaviour as [1,2,3].sort(comparator) | |
6 * | |
7 * @param Function getSortable | |
8 * A function that should return the element that is | |
9 * to be sorted. The comparator will run on the | |
10 * current collection, but you may want the actual | |
11 * resulting sort to occur on a parent or another | |
12 * associated element. | |
13 * | |
14 * E.g. $('td').sortElements(comparator, function(){ | |
15 * return this.parentNode; | |
16 * }) | |
17 * | |
18 * The <td>'s parent (<tr>) will be sorted instead | |
19 * of the <td> itself. | |
20 * | |
21 * Credit: http://james.padolsey.com/javascript/sorting-elements-with-jquery/ | |
22 * | |
23 */ | |
24 jQuery.fn.sortElements = (function(){ | |
25 | |
26 var sort = [].sort; | |
27 | |
28 return function(comparator, getSortable) { | |
29 | |
30 getSortable = getSortable || function(){return this;}; | |
31 | |
32 var placements = this.map(function(){ | |
33 | |
34 var sortElement = getSortable.call(this), | |
35 parentNode = sortElement.parentNode, | |
36 | |
37 // Since the element itself will change position, we have | |
38 // to have some way of storing its original position in | |
39 // the DOM. The easiest way is to have a 'flag' node: | |
40 nextSibling = parentNode.insertBefore( | |
41 document.createTextNode(''), | |
42 sortElement.nextSibling | |
43 ); | |
44 | |
45 return function() { | |
46 | |
47 if (parentNode === this) { | |
48 throw new Error( | |
49 "You can't sort elements if any one is a descendant of another." | |
50 ); | |
51 } | |
52 | |
53 // Insert before flag: | |
54 parentNode.insertBefore(this, nextSibling); | |
55 // Remove flag: | |
56 parentNode.removeChild(nextSibling); | |
57 | |
58 }; | |
59 | |
60 }); | |
61 | |
62 return sort.call(this, comparator).each(function(i){ | |
63 placements[i].call(getSortable.call(this)); | |
64 }); | |
65 | |
66 }; | |
67 | |
68 })(); | |
69 | |
70 (function ($) { | |
71 Drupal.behaviors.features = { | |
72 attach: function(context, settings) { | |
73 // Features management form | |
74 $('table.features:not(.processed)', context).each(function() { | |
75 $(this).addClass('processed'); | |
76 | |
77 // Check the overridden status of each feature | |
78 Drupal.features.checkStatus(); | |
79 | |
80 // Add some nicer row hilighting when checkboxes change values | |
81 $('input', this).bind('change', function() { | |
82 if (!$(this).attr('checked')) { | |
83 $(this).parents('tr').removeClass('enabled').addClass('disabled'); | |
84 } | |
85 else { | |
86 $(this).parents('tr').addClass('enabled').removeClass('disabled'); | |
87 } | |
88 }); | |
89 }); | |
90 | |
91 // Export form component selector | |
92 $('form.features-export-form select.features-select-components:not(.processed)', context).each(function() { | |
93 $(this) | |
94 .addClass('processed') | |
95 .change(function() { | |
96 var target = $(this).val(); | |
97 $('div.features-select').hide(); | |
98 $('div.features-select-' + target).show(); | |
99 return false; | |
100 }).trigger('change'); | |
101 }); | |
102 | |
103 // Export form machine-readable JS | |
104 $('.feature-name:not(.processed)', context).each(function() { | |
105 $('.feature-name') | |
106 .addClass('processed') | |
107 .after(' <small class="feature-module-name-suffix"> </small>'); | |
108 if ($('.feature-module-name').val() === $('.feature-name').val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_') || $('.feature-module-name').val() === '') { | |
109 $('.feature-module-name').parents('.form-item').hide(); | |
110 $('.feature-name').bind('keyup change', function() { | |
111 var machine = $(this).val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_'); | |
112 if (machine !== '_' && machine !== '') { | |
113 $('.feature-module-name').val(machine); | |
114 $('.feature-module-name-suffix').empty().append(' Machine name: ' + machine + ' [').append($('<a href="#">'+ Drupal.t('Edit') +'</a>').click(function() { | |
115 $('.feature-module-name').parents('.form-item').show(); | |
116 $('.feature-module-name-suffix').hide(); | |
117 $('.feature-name').unbind('keyup'); | |
118 return false; | |
119 })).append(']'); | |
120 } | |
121 else { | |
122 $('.feature-module-name').val(machine); | |
123 $('.feature-module-name-suffix').text(''); | |
124 } | |
125 }); | |
126 $('.feature-name').keyup(); | |
127 } | |
128 }); | |
129 | |
130 //View info dialog | |
131 var infoDialog = $('#features-info-file'); | |
132 if (infoDialog.length != 0) { | |
133 infoDialog.dialog({ | |
134 autoOpen: false, | |
135 modal: true, | |
136 draggable: false, | |
137 resizable: false, | |
138 width: 600, | |
139 height: 480 | |
140 }); | |
141 } | |
142 | |
143 if ((Drupal.settings.features != undefined) && (Drupal.settings.features.info != undefined)) { | |
144 $('#features-info-file textarea').val(Drupal.settings.features.info); | |
145 $('#features-info-file').dialog('open'); | |
146 //To be reset by the button click ajax | |
147 Drupal.settings.features.info = undefined; | |
148 } | |
149 | |
150 // mark any conflicts with a class | |
151 if ((Drupal.settings.features != undefined) && (Drupal.settings.features.conflicts != undefined)) { | |
152 for (var moduleName in Drupal.settings.features.conflicts) { | |
153 moduleConflicts = Drupal.settings.features.conflicts[moduleName]; | |
154 $('#features-export-wrapper input[type=checkbox]', context).each(function() { | |
155 if (!$(this).hasClass('features-checkall')) { | |
156 var key = $(this).attr('name'); | |
157 var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/); | |
158 var component = matches[1]; | |
159 var item = matches[4]; | |
160 if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) { | |
161 $(this).parent().addClass('features-conflict'); | |
162 } | |
163 } | |
164 }); | |
165 } | |
166 } | |
167 | |
168 function _checkAll(value) { | |
169 if (value) { | |
170 $('#features-export-wrapper .component-select input[type=checkbox]:visible', context).each(function() { | |
171 var move_id = $(this).attr('id'); | |
172 $(this).click(); | |
173 $('#'+ move_id).attr('checked', 'checked'); | |
174 }); | |
175 } | |
176 else { | |
177 $('#features-export-wrapper .component-added input[type=checkbox]:visible', context).each(function() { | |
178 var move_id = $(this).attr('id'); | |
179 $('#'+ move_id).removeAttr('checked'); | |
180 $(this).click(); | |
181 $('#'+ move_id).removeAttr('checked'); | |
182 }); | |
183 } | |
184 } | |
185 | |
186 function moveCheckbox(item, section, value) { | |
187 var curParent = item; | |
188 if ($(item).hasClass('form-type-checkbox')) { | |
189 item = $(item).children('input[type=checkbox]'); | |
190 } | |
191 else { | |
192 curParent = $(item).parents('.form-type-checkbox'); | |
193 } | |
194 var newParent = $(curParent).parents('.features-export-parent').find('.form-checkboxes.component-'+section); | |
195 $(curParent).detach(); | |
196 $(curParent).appendTo(newParent); | |
197 var list = ['select', 'added', 'detected', 'included']; | |
198 for (i in list) { | |
199 $(curParent).removeClass('component-' + list[i]); | |
200 $(item).removeClass('component-' + list[i]); | |
201 } | |
202 $(curParent).addClass('component-'+section); | |
203 $(item).addClass('component-'+section); | |
204 if (value) { | |
205 $(item).attr('checked', 'checked'); | |
206 } | |
207 else { | |
208 $(item).removeAttr('checked') | |
209 } | |
210 $(newParent).parent().removeClass('features-export-empty'); | |
211 | |
212 // re-sort new list of checkboxes based on labels | |
213 $(newParent).find('label').sortElements( | |
214 function(a, b){ | |
215 return $(a).text() > $(b).text() ? 1 : -1; | |
216 }, | |
217 function(){ | |
218 return this.parentNode; | |
219 } | |
220 ); | |
221 } | |
222 | |
223 // provide timer for auto-refresh trigger | |
224 var timeoutID = 0; | |
225 var inTimeout = 0; | |
226 function _triggerTimeout() { | |
227 timeoutID = 0; | |
228 _updateDetected(); | |
229 } | |
230 function _resetTimeout() { | |
231 inTimeout++; | |
232 // if timeout is already active, reset it | |
233 if (timeoutID != 0) { | |
234 window.clearTimeout(timeoutID); | |
235 if (inTimeout > 0) inTimeout--; | |
236 } | |
237 timeoutID = window.setTimeout(_triggerTimeout, 500); | |
238 } | |
239 | |
240 function _updateDetected() { | |
241 var autodetect = $('#features-autodetect input[type=checkbox]'); | |
242 if ((autodetect.length > 0) && (!autodetect.is(':checked'))) return; | |
243 // query the server for a list of components/items in the feature and update | |
244 // the auto-detected items | |
245 var items = []; // will contain a list of selected items exported to feature | |
246 var components = {}; // contains object of component names that have checked items | |
247 $('#features-export-wrapper input[type=checkbox]:checked', context).each(function() { | |
248 if (!$(this).hasClass('features-checkall')) { | |
249 var key = $(this).attr('name'); | |
250 var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/); | |
251 components[matches[1]] = matches[1]; | |
252 if (!$(this).hasClass('component-detected')) { | |
253 items.push(key); | |
254 } | |
255 } | |
256 }); | |
257 var featureName = $('#edit-module-name').val(); | |
258 if (featureName == '') { | |
259 featureName = '*'; | |
260 } | |
261 var url = Drupal.settings.basePath + 'features/ajaxcallback/' + featureName; | |
262 var excluded = Drupal.settings.features.excluded; | |
263 var postData = {'items': items, 'excluded': excluded}; | |
264 jQuery.post(url, postData, function(data) { | |
265 if (inTimeout > 0) inTimeout--; | |
266 // if we have triggered another timeout then don't update with old results | |
267 if (inTimeout == 0) { | |
268 // data is an object keyed by component listing the exports of the feature | |
269 for (var component in data) { | |
270 var itemList = data[component]; | |
271 $('#features-export-wrapper .component-' + component + ' input[type=checkbox]', context).each(function() { | |
272 var key = $(this).attr('value'); | |
273 // first remove any auto-detected items that are no longer in component | |
274 if ($(this).hasClass('component-detected')) { | |
275 if (!(key in itemList)) { | |
276 moveCheckbox(this, 'select', false) | |
277 } | |
278 } | |
279 // next, add any new auto-detected items | |
280 else if ($(this).hasClass('component-select')) { | |
281 if (key in itemList) { | |
282 moveCheckbox(this, 'detected', itemList[key]); | |
283 $(this).parent().show(); // make sure it's not hidden from filter | |
284 } | |
285 } | |
286 }); | |
287 } | |
288 // loop over all selected components and check for any that have been completely removed | |
289 for (var component in components) { | |
290 if ((data == null) || !(component in data)) { | |
291 $('#features-export-wrapper .component-' + component + ' input[type=checkbox].component-detected', context).each(function() { | |
292 moveCheckbox(this, 'select', false); | |
293 }); | |
294 } | |
295 } | |
296 } | |
297 }, "json"); | |
298 } | |
299 | |
300 // Handle component selection UI | |
301 $('#features-export-wrapper input[type=checkbox]', context).click(function() { | |
302 _resetTimeout(); | |
303 if ($(this).hasClass('component-select')) { | |
304 moveCheckbox(this, 'added', true); | |
305 } | |
306 else if ($(this).hasClass('component-included')) { | |
307 moveCheckbox(this, 'added', false); | |
308 } | |
309 else if ($(this).hasClass('component-added')) { | |
310 if ($(this).is(':checked')) { | |
311 moveCheckbox(this, 'included', true); | |
312 } | |
313 else { | |
314 moveCheckbox(this, 'select', false); | |
315 } | |
316 } | |
317 }); | |
318 | |
319 // Handle select/unselect all | |
320 $('#features-filter .features-checkall', context).click(function() { | |
321 if ($(this).attr('checked')) { | |
322 _checkAll(true); | |
323 $(this).next().html(Drupal.t('Deselect all')); | |
324 } | |
325 else { | |
326 _checkAll(false); | |
327 $(this).next().html(Drupal.t('Select all')); | |
328 } | |
329 _resetTimeout(); | |
330 }); | |
331 | |
332 // Handle filtering | |
333 | |
334 // provide timer for auto-refresh trigger | |
335 var filterTimeoutID = 0; | |
336 var inFilterTimeout = 0; | |
337 function _triggerFilterTimeout() { | |
338 filterTimeoutID = 0; | |
339 _updateFilter(); | |
340 } | |
341 function _resetFilterTimeout() { | |
342 inFilterTimeout++; | |
343 // if timeout is already active, reset it | |
344 if (filterTimeoutID != 0) { | |
345 window.clearTimeout(filterTimeoutID); | |
346 if (inFilterTimeout > 0) inFilterTimeout--; | |
347 } | |
348 filterTimeoutID = window.setTimeout(_triggerFilterTimeout, 200); | |
349 } | |
350 function _updateFilter() { | |
351 var filter = $('#features-filter input').val(); | |
352 var regex = new RegExp(filter, 'i'); | |
353 // collapse fieldsets | |
354 var newState = {}; | |
355 var currentState = {}; | |
356 $('#features-export-wrapper fieldset.features-export-component', context).each(function() { | |
357 // expand parent fieldset | |
358 var section = $(this).attr('id'); | |
359 currentState[section] = !($(this).hasClass('collapsed')); | |
360 if (!(section in newState)) { | |
361 newState[section] = false; | |
362 } | |
363 | |
364 $(this).find('div.component-select label').each(function() { | |
365 if (filter == '') { | |
366 if (currentState[section]) { | |
367 Drupal.toggleFieldset($('#'+section)); | |
368 currentState[section] = false; | |
369 } | |
370 $(this).parent().show(); | |
371 } | |
372 else if ($(this).text().match(regex)) { | |
373 $(this).parent().show(); | |
374 newState[section] = true; | |
375 } | |
376 else { | |
377 $(this).parent().hide(); | |
378 } | |
379 }); | |
380 }); | |
381 for (section in newState) { | |
382 if (currentState[section] != newState[section]) { | |
383 Drupal.toggleFieldset($('#'+section)); | |
384 } | |
385 } | |
386 } | |
387 $('#features-filter input', context).bind("input", function() { | |
388 _resetFilterTimeout(); | |
389 }); | |
390 $('#features-filter .features-filter-clear', context).click(function() { | |
391 $('#features-filter input').val(''); | |
392 _updateFilter(); | |
393 }); | |
394 | |
395 // show the filter bar | |
396 $('#features-filter', context).removeClass('element-invisible'); | |
397 } | |
398 } | |
399 | |
400 | |
401 Drupal.features = { | |
402 'checkStatus': function() { | |
403 $('table.features tbody tr').not('.processed').filter(':first').each(function() { | |
404 var elem = $(this); | |
405 $(elem).addClass('processed'); | |
406 var uri = $(this).find('a.admin-check').attr('href'); | |
407 if (uri) { | |
408 $.get(uri, [], function(data) { | |
409 $(elem).find('.admin-loading').hide(); | |
410 switch (data.storage) { | |
411 case 3: | |
412 $(elem).find('.admin-rebuilding').show(); | |
413 break; | |
414 case 2: | |
415 $(elem).find('.admin-needs-review').show(); | |
416 break; | |
417 case 1: | |
418 $(elem).find('.admin-overridden').show(); | |
419 break; | |
420 default: | |
421 $(elem).find('.admin-default').show(); | |
422 break; | |
423 } | |
424 Drupal.features.checkStatus(); | |
425 }, 'json'); | |
426 } | |
427 else { | |
428 Drupal.features.checkStatus(); | |
429 } | |
430 }); | |
431 } | |
432 }; | |
433 | |
434 | |
435 })(jQuery); | |
436 | |
437 |