Projet

Général

Profil

Paste
Télécharger (15,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / panels_ipe / js / panels_ipe.js @ 136a805a

1

    
2
// Ensure the $ alias is owned by jQuery.
3
(function($) {
4

    
5
Drupal.settings.Panels = Drupal.settings.Panels || {};
6

    
7
Drupal.PanelsIPE = {
8
  editors: {},
9
  bindClickDelete: function(context) {
10
    $('a.pane-delete:not(.pane-delete-processed)', context)
11
      .addClass('pane-delete-processed')
12
      .click(function() {
13
        if (confirm(Drupal.t('Remove this pane?'))) {
14
          $(this).parents('div.panels-ipe-portlet-wrapper').fadeOut('medium', function() {
15
            var $sortable = $(this).closest('.ui-sortable');
16
            $(this).empty().remove();
17
            $sortable.trigger('sortremove');
18
          });
19
          $(this).parents('div.panels-ipe-display-container').addClass('changed');
20
        }
21
        return false;
22
      });
23
  }
24
}
25

    
26

    
27
Drupal.behaviors.PanelsIPE = {
28
  attach: function(context) {
29
    for (var i in Drupal.settings.PanelsIPECacheKeys) {
30
      var key = Drupal.settings.PanelsIPECacheKeys[i];
31
      $('div#panels-ipe-display-' + key + ':not(.panels-ipe-processed)')
32
        .addClass('panels-ipe-processed')
33
        .each(function() {
34
          // If we're replacing an old IPE, clean it up a little.
35
          if (Drupal.PanelsIPE.editors[key]) {
36
            Drupal.PanelsIPE.editors[key].editing = false;
37
          }
38
          Drupal.PanelsIPE.editors[key] = new DrupalPanelsIPE(key);
39
          Drupal.PanelsIPE.editors[key].showContainer();
40
        });
41
    }
42
    $('.panels-ipe-hide-bar').once('panels-ipe-hide-bar-processed').click(function() {
43
      Drupal.PanelsIPE.editors[key].hideContainer();
44
    });
45
    Drupal.PanelsIPE.bindClickDelete(context);
46
  }
47
};
48

    
49
/**
50
 * Base object (class) definition for the Panels In-Place Editor.
51
 *
52
 * A new instance of this object is instanciated for every unique IPE on a given
53
 * page.
54
 *
55
 * Note that this form is provisional, and we hope to replace it with a more
56
 * flexible, loosely-coupled model that utilizes separate controllers for the
57
 * discrete IPE elements. This will result in greater IPE flexibility.
58
 */
59
function DrupalPanelsIPE(cache_key, cfg) {
60
  cfg = cfg || {};
61
  var ipe = this;
62
  this.key = cache_key;
63
  this.lockPath = null;
64
  this.state = {};
65
  this.container = $('#panels-ipe-control-container');
66
  this.control = $('div#panels-ipe-control-' + cache_key);
67
  this.initButton = $('div.panels-ipe-startedit', this.control);
68
  this.cfg = cfg;
69
  this.changed = false;
70
  this.sortableOptions = $.extend({
71
    opacity: 0.75, // opacity of sortable while sorting
72
    items: 'div.panels-ipe-portlet-wrapper',
73
    handle: 'div.panels-ipe-draghandle',
74
    cancel: '.panels-ipe-nodrag',
75
    dropOnEmpty: true
76
  }, cfg.sortableOptions || {});
77

    
78
  this.regions = [];
79
  this.sortables = {};
80

    
81
  $(document).bind('CToolsDetachBehaviors', function() {
82
    // If the IPE is off and the container is not visible, then we need
83
    // to reshow the container on modal close.
84
    if (!$('.panels-ipe-form-container', ipe.control).html() && !ipe.container.is(':visible')) {
85
      ipe.showContainer();
86
      ipe.cancelLock();
87
    }
88

    
89
    // If the IPE is on and we've hidden the bar for a modal, we need to
90
    // re-display it.
91
    if (ipe.topParent && ipe.topParent.hasClass('panels-ipe-editing') && ipe.container.is(':not(visible)')) {
92
      ipe.showContainer();
93
    }
94
  });
95

    
96

    
97
  // If a user navigates away from a locked IPE, cancel the lock in the background.
98
  $(window).bind('beforeunload', function() {
99
    if (!ipe.editing) {
100
      return;
101
    }
102

    
103
    if (ipe.topParent && ipe.topParent.hasClass('changed')) {
104
      ipe.changed = true;
105
    }
106

    
107
    if (ipe.changed) {
108
      return Drupal.t('This will discard all unsaved changes. Are you sure?');
109
    }
110
  });
111

    
112
  // If a user navigates away from a locked IPE, cancel the lock in the background.
113
  $(window).bind('unload', function() {
114
    ipe.cancelLock(true);
115
  });
116

    
117
  /**
118
   * If something caused us to abort what we were doing, send a background
119
   * cancel lock request to the server so that we do not leave stale locks
120
   * hanging around.
121
   */
122
  this.cancelLock = function(sync) {
123
    // If there's a lockpath and an ajax available, inform server to clear lock.
124
    // We borrow the ajax options from the customize this page link.
125
    if (ipe.lockPath && Drupal.ajax['panels-ipe-customize-page']) {
126
      var ajaxOptions = {
127
        type: 'POST',
128
        url: ipe.lockPath
129
      }
130

    
131
      if (sync) {
132
        ajaxOptions.async = false;
133
      }
134

    
135
      // Make sure we don't somehow get another one:
136
      ipe.lockPath = null;
137

    
138
      // Send the request. This is synchronous to prevent being cancelled.
139
      $.ajax(ajaxOptions);
140
    }
141
  }
142

    
143
  this.activateSortable = function(event, ui) {
144
    if (!Drupal.settings.Panels || !Drupal.settings.Panels.RegionLock) {
145
      // don't bother if there are no region locks in play.
146
      return;
147
    }
148

    
149
    var region = event.data.region;
150
    var paneId = ui.item.attr('id').replace('panels-ipe-paneid-', '');
151

    
152
    var disabledRegions = false;
153

    
154
    // Determined if this pane is locked out of this region.
155
    if (!Drupal.settings.Panels.RegionLock[paneId] || Drupal.settings.Panels.RegionLock[paneId][region]) {
156
      ipe.sortables[region].sortable('enable');
157
      ipe.sortables[region].sortable('refresh');
158
    }
159
    else {
160
      disabledRegions = true;
161
      ipe.sortables[region].sortable('disable');
162
      ipe.sortables[region].sortable('refresh');
163
    }
164

    
165
    // If we disabled regions, we need to
166
    if (disabledRegions) {
167
      $(event.srcElement).bind('dragstop', function(event, ui) {
168
        // Go through
169
      });
170
    }
171
  };
172

    
173
  // When dragging is stopped, we need to ensure all sortable regions are enabled.
174
  this.enableRegions = function(event, ui) {
175
    for (var i in ipe.regions) {
176
      ipe.sortables[ipe.regions[i]].sortable('enable');
177
      ipe.sortables[ipe.regions[i]].sortable('refresh');
178
    }
179
  }
180

    
181
  this.initSorting = function() {
182
    var $region = $(this).parents('.panels-ipe-region');
183
    var region = $region.attr('id').replace('panels-ipe-regionid-', '');
184
    ipe.sortables[region] = $(this).sortable(ipe.sortableOptions);
185
    ipe.regions.push(region);
186
    $(this).bind('sortactivate', {region: region}, ipe.activateSortable);
187
  };
188

    
189
  this.initEditing = function(formdata) {
190
    ipe.editing = true;
191
    ipe.topParent = $('div#panels-ipe-display-' + cache_key);
192
    ipe.backup = this.topParent.clone();
193

    
194
    // See http://jqueryui.com/demos/sortable/ for details on the configuration
195
    // parameters used here.
196
    ipe.changed = false;
197

    
198
    $('div.panels-ipe-sort-container', ipe.topParent).each(ipe.initSorting);
199

    
200
    // Since the connectWith option only does a one-way hookup, iterate over
201
    // all sortable regions to connect them with one another.
202
    $('div.panels-ipe-sort-container', ipe.topParent)
203
      .sortable('option', 'connectWith', ['div.panels-ipe-sort-container']);
204

    
205
    $('div.panels-ipe-sort-container', ipe.topParent).bind('sortupdate', function() {
206
      ipe.changed = true;
207
    });
208

    
209
    $('div.panels-ipe-sort-container', ipe.topParent).bind('sortstop', this.enableRegions);
210

    
211
    $('.panels-ipe-form-container', ipe.control).append(formdata);
212

    
213
    $('input:submit:not(.ajax-processed), button:not(.ajax-processed)', ipe.control).addClass('ajax-processed').each(function() {
214
      var element_settings = {};
215

    
216
      element_settings.url = $(this.form).attr('action');
217
      element_settings.setClick = true;
218
      element_settings.event = 'click';
219
      element_settings.progress = { 'type': 'throbber' };
220
      element_settings.ipe_cache_key = cache_key;
221

    
222
      var base = $(this).attr('id');
223
      Drupal.ajax[ipe.base] = new Drupal.ajax(base, this, element_settings);
224
    });
225

    
226
    // Perform visual effects in a particular sequence.
227
    // .show() + .hide() cannot have speeds associated with them, otherwise
228
    // it clears out inline styles.
229
    $('.panels-ipe-on').show();
230
    ipe.showForm();
231
    $('body').add(ipe.topParent).addClass('panels-ipe-editing');
232

    
233
  };
234

    
235
  this.hideContainer = function() {
236
    ipe.container.slideUp('fast');
237
  };
238

    
239
  this.showContainer = function() {
240
    ipe.container.slideDown('normal');
241
  };
242

    
243
  this.showButtons = function() {
244
    $('.panels-ipe-form-container').hide();
245
    $('.panels-ipe-button-container').show();
246
    ipe.showContainer();
247
  };
248

    
249
  this.showForm = function() {
250
    $('.panels-ipe-button-container').hide();
251
    $('.panels-ipe-form-container').show();
252
    ipe.showContainer();
253
  };
254

    
255
  this.endEditing = function() {
256
    ipe.editing = false;
257
    ipe.lockPath = null;
258
    $('.panels-ipe-form-container').empty();
259
    // Re-show all the IPE non-editing meta-elements
260
    $('div.panels-ipe-off').show('fast');
261

    
262
    ipe.showButtons();
263
    // Re-hide all the IPE meta-elements
264
    $('div.panels-ipe-on').hide();
265

    
266
    $('.panels-ipe-editing').removeClass('panels-ipe-editing');
267
    $('div.panels-ipe-sort-container.ui-sortable', ipe.topParent).sortable("destroy");
268
  };
269

    
270
  this.saveEditing = function() {
271
    $('div.panels-ipe-region', ipe.topParent).each(function() {
272
      var val = '';
273
      var region = $(this).attr('id').split('panels-ipe-regionid-')[1];
274
      $(this).find('div.panels-ipe-portlet-wrapper').each(function() {
275
        var id = $(this).attr('id').split('panels-ipe-paneid-')[1];
276
        if (id) {
277
          if (val) {
278
            val += ',';
279
          }
280
          val += id;
281
        }
282
      });
283
      $('[name="panel[pane][' +  region + ']"]', ipe.control).val(val);
284
    });
285
  }
286

    
287
  this.cancelIPE = function() {
288
    ipe.hideContainer();
289
    ipe.topParent.fadeOut('medium', function() {
290
      ipe.topParent.replaceWith(ipe.backup.clone());
291
      ipe.topParent = $('div#panels-ipe-display-' + ipe.key);
292

    
293
      // Processing of these things got lost in the cloning, but the classes remained behind.
294
      // @todo this isn't ideal but I can't seem to figure out how to keep an unprocessed backup
295
      // that will later get processed.
296
      $('.ctools-use-modal-processed', ipe.topParent).removeClass('ctools-use-modal-processed');
297
      $('.pane-delete-processed', ipe.topParent).removeClass('pane-delete-processed');
298
      ipe.topParent.fadeIn('medium');
299
      Drupal.attachBehaviors();
300
    });
301
  };
302

    
303
  this.cancelEditing = function() {
304
    if (ipe.topParent.hasClass('changed')) {
305
      ipe.changed = true;
306
    }
307

    
308
    if (!ipe.changed || confirm(Drupal.t('This will discard all unsaved changes. Are you sure?'))) {
309
      this.cancelIPE();
310
      return true;
311
    }
312
    else {
313
      // Cancel the submission.
314
      return false;
315
    }
316
  };
317

    
318
  this.createSortContainers = function() {
319
    $('div.panels-ipe-region', this.topParent).each(function() {
320
      $(this).children('div.panels-ipe-portlet-marker').parent()
321
        .wrapInner('<div class="panels-ipe-sort-container" />');
322

    
323
      // Move our gadgets outside of the sort container so that sortables
324
      // cannot be placed after them.
325
      $('div.panels-ipe-portlet-static', this).each(function() {
326
        $(this).prependTo($(this).parent().parent());
327
      });
328
    });
329
  }
330

    
331
  this.createSortContainers();
332

    
333
};
334

    
335
$(function() {
336
  Drupal.ajax.prototype.commands.initIPE = function(ajax, data, status) {
337
    if (Drupal.PanelsIPE.editors[data.key]) {
338
      Drupal.PanelsIPE.editors[data.key].initEditing(data.data);
339
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
340
    }
341
    Drupal.attachBehaviors();
342

    
343
  };
344

    
345
  Drupal.ajax.prototype.commands.IPEsetLockState = function(ajax, data, status) {
346
    if (Drupal.PanelsIPE.editors[data.key]) {
347
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
348
    }
349
  };
350

    
351
  Drupal.ajax.prototype.commands.addNewPane = function(ajax, data, status) {
352
    if (Drupal.PanelsIPE.editors[data.key]) {
353
      Drupal.PanelsIPE.editors[data.key].changed = true;
354
    }
355
  };
356

    
357
  Drupal.ajax.prototype.commands.cancelIPE = function(ajax, data, status) {
358
    if (Drupal.PanelsIPE.editors[data.key]) {
359
      Drupal.PanelsIPE.editors[data.key].cancelIPE();
360
      Drupal.PanelsIPE.editors[data.key].endEditing();
361
    }
362
  };
363

    
364
  Drupal.ajax.prototype.commands.unlockIPE = function(ajax, data, status) {
365
    if (confirm(data.message)) {
366
      var ajaxOptions = ajax.options;
367
      ajaxOptions.url = data.break_path;
368
      $.ajax(ajaxOptions);
369
    }
370
    else {
371
      Drupal.PanelsIPE.editors[data.key].endEditing();
372
    }
373
  };
374

    
375
  Drupal.ajax.prototype.commands.endIPE = function(ajax, data, status) {
376
    if (Drupal.PanelsIPE.editors[data.key]) {
377
      Drupal.PanelsIPE.editors[data.key].endEditing();
378
    }
379
  };
380

    
381
  Drupal.ajax.prototype.commands.insertNewPane = function(ajax, data, status) {
382
    IPEContainerSelector = '#panels-ipe-regionid-' + data.regionId + ' div.panels-ipe-sort-container';
383
    firstPaneSelector = IPEContainerSelector + ' div.panels-ipe-portlet-wrapper:first';
384
    // Insert the new pane before the first existing pane in the region, if
385
    // any.
386
    if ($(firstPaneSelector).length) {
387
      insertData = {
388
        'method': 'before',
389
        'selector': firstPaneSelector,
390
        'data': data.renderedPane,
391
        'settings': null
392
      }
393
      Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
394
    }
395
    // Else, insert it as a first child of the container. Doing so might fall
396
    // outside of the wrapping markup for the style, but it's the best we can
397
    // do.
398
    else {
399
      insertData = {
400
        'method': 'prepend',
401
        'selector': IPEContainerSelector,
402
        'data': data.renderedPane,
403
        'settings': null
404
      }
405
      Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
406
    }
407
  };
408

    
409
  /**
410
   * Override the eventResponse on ajax.js so we can add a little extra
411
   * behavior.
412
   */
413
  Drupal.ajax.prototype.ipeReplacedEventResponse = Drupal.ajax.prototype.eventResponse;
414
  Drupal.ajax.prototype.eventResponse = function (element, event) {
415
    if (element.ipeCancelThis) {
416
      element.ipeCancelThis = null;
417
      return false;
418
    }
419

    
420
    if ($(this.element).attr('id') == 'panels-ipe-cancel') {
421
      if (!Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].cancelEditing()) {
422
        return false;
423
      }
424
    }
425

    
426
    var retval = this.ipeReplacedEventResponse(element, event);
427
    if (this.ajaxing && this.element_settings.ipe_cache_key) {
428
      // Move the throbber so that it appears outside our container.
429
      if (this.progress.element) {
430
        $(this.progress.element).addClass('ipe-throbber').appendTo($('body'));
431
      }
432
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].hideContainer();
433
    }
434
    // @TODO $('#panels-ipe-throbber-backdrop').remove();
435
    return retval;
436
  };
437

    
438
  /**
439
   * Override the eventResponse on ajax.js so we can add a little extra
440
   * behavior.
441
   */
442
  Drupal.ajax.prototype.ipeReplacedError = Drupal.ajax.prototype.error;
443
  Drupal.ajax.prototype.error = function (response, uri) {
444
    var retval = this.ipeReplacedError(response, uri);
445
    if (this.element_settings.ipe_cache_key) {
446
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].showContainer();
447
    }
448
  };
449

    
450
  Drupal.ajax.prototype.ipeReplacedBeforeSerialize = Drupal.ajax.prototype.beforeSerialize;
451
  Drupal.ajax.prototype.beforeSerialize = function (element_settings, options) {
452
    if ($(this.element).hasClass('panels-ipe-save')) {
453
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].saveEditing();
454
    };
455
    return this.ipeReplacedBeforeSerialize(element_settings, options);
456
  };
457

    
458
});
459

    
460
/**
461
 * Apply margin to bottom of the page.
462
 *
463
 * Note that directly applying marginBottom does not work in IE. To prevent
464
 * flickering/jumping page content with client-side caching, this is a regular
465
 * Drupal behavior.
466
 *
467
 * @see admin_menu.js via https://drupal.org/project/admin_menu
468
 */
469
Drupal.behaviors.panelsIpeMarginBottom = {
470
  attach: function () {
471
    $('body:not(.panels-ipe)').addClass('panels-ipe');
472
  }
473
};
474

    
475
})(jQuery);