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">&nbsp;</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