Projet

Général

Profil

Paste
Télécharger (16,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / panels_ipe / js / panels_ipe.js @ 64156087

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
    // Remove any old editors.
30
    for (var i in Drupal.PanelsIPE.editors) {
31
      if (Drupal.settings.PanelsIPECacheKeys.indexOf(i) === -1) {
32
        // Clean-up a little bit and remove it.
33
        Drupal.PanelsIPE.editors[i].editing = false;
34
        Drupal.PanelsIPE.editors[i].changed = false;
35
        delete Drupal.PanelsIPE.editors[i];
36
      }
37
    }
38

    
39
    // Initialize new editors.
40
    for (var i in Drupal.settings.PanelsIPECacheKeys) {
41
      var key = Drupal.settings.PanelsIPECacheKeys[i];
42
      $('div#panels-ipe-display-' + key + ':not(.panels-ipe-processed)')
43
        .addClass('panels-ipe-processed')
44
        .each(function() {
45
          // If we're replacing an old IPE, clean it up a little.
46
          if (Drupal.PanelsIPE.editors[key]) {
47
            Drupal.PanelsIPE.editors[key].editing = false;
48
          }
49
          Drupal.PanelsIPE.editors[key] = new DrupalPanelsIPE(key);
50
          Drupal.PanelsIPE.editors[key].showContainer();
51
        });
52
    }
53
    $('.panels-ipe-hide-bar').once('panels-ipe-hide-bar').click(function() {
54
      Drupal.PanelsIPE.editors[key].hideContainer();
55
    });
56
    Drupal.PanelsIPE.bindClickDelete(context);
57
  }
58
};
59

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

    
90
  this.regions = [];
91
  this.sortables = {};
92

    
93
  $(document).bind('CToolsDetachBehaviors', function() {
94
    // If the IPE is off and the container is not visible, then we need
95
    // to reshow the container on modal close.
96
    if (!$('.panels-ipe-form-container', ipe.control).html() && !ipe.container.is(':visible')) {
97
      ipe.showContainer();
98
      ipe.cancelLock();
99
    }
100

    
101
    // If the IPE is on and we've hidden the bar for a modal, we need to
102
    // re-display it.
103
    if (ipe.topParent && ipe.topParent.hasClass('panels-ipe-editing') && ipe.container.is(':not(visible)')) {
104
      ipe.showContainer();
105
    }
106
  });
107

    
108

    
109
  // If a user navigates away from a locked IPE, cancel the lock in the background.
110
  $(window).bind('beforeunload', function() {
111
    if (!ipe.editing) {
112
      return;
113
    }
114

    
115
    if (ipe.topParent && ipe.topParent.hasClass('changed')) {
116
      ipe.changed = true;
117
    }
118

    
119
    if (ipe.changed) {
120
      return Drupal.t('This will discard all unsaved changes. Are you sure?');
121
    }
122
  });
123

    
124
  // If a user navigates away from a locked IPE, cancel the lock in the background.
125
  $(window).bind('unload', function() {
126
    ipe.cancelLock(true);
127
  });
128

    
129
  /**
130
   * If something caused us to abort what we were doing, send a background
131
   * cancel lock request to the server so that we do not leave stale locks
132
   * hanging around.
133
   */
134
  this.cancelLock = function(sync) {
135
    // If there's a lockpath and an ajax available, inform server to clear lock.
136
    // We borrow the ajax options from the customize this page link.
137
    if (ipe.lockPath && Drupal.ajax['panels-ipe-customize-page']) {
138
      var ajaxOptions = {
139
        type: 'POST',
140
        url: ipe.lockPath
141
      }
142

    
143
      if (sync) {
144
        ajaxOptions.async = false;
145
      }
146

    
147
      // Make sure we don't somehow get another one:
148
      ipe.lockPath = null;
149

    
150
      // Send the request. This is synchronous to prevent being cancelled.
151
      $.ajax(ajaxOptions);
152
    }
153
  }
154

    
155
  this.activateSortable = function(event, ui) {
156
    if (!Drupal.settings.Panels || !Drupal.settings.Panels.RegionLock) {
157
      // don't bother if there are no region locks in play.
158
      return;
159
    }
160

    
161
    var region = event.data.region;
162
    var paneId = ui.item.attr('id').replace('panels-ipe-paneid-', '');
163

    
164
    var disabledRegions = false;
165

    
166
    // Determined if this pane is locked out of this region.
167
    if (!Drupal.settings.Panels.RegionLock[paneId] || Drupal.settings.Panels.RegionLock[paneId][region]) {
168
      ipe.sortables[region].sortable('enable');
169
      ipe.sortables[region].sortable('refresh');
170
    }
171
    else {
172
      disabledRegions = true;
173
      ipe.sortables[region].sortable('disable');
174
      ipe.sortables[region].sortable('refresh');
175
    }
176

    
177
    // If we disabled regions, we need to
178
    if (disabledRegions) {
179
      $(event.srcElement).bind('dragstop', function(event, ui) {
180
        // Go through
181
      });
182
    }
183
  };
184

    
185
  // When dragging is stopped, we need to ensure all sortable regions are enabled.
186
  this.enableRegions = function(event, ui) {
187
    for (var i in ipe.regions) {
188
      ipe.sortables[ipe.regions[i]].sortable('enable');
189
      ipe.sortables[ipe.regions[i]].sortable('refresh');
190
    }
191
  }
192

    
193
  this.initSorting = function() {
194
    var $region = $(this).parents('.panels-ipe-region');
195
    var region = $region.attr('id').replace('panels-ipe-regionid-', '');
196
    ipe.sortables[region] = $(this).sortable(ipe.sortableOptions);
197
    ipe.regions.push(region);
198
    $(this).bind('sortactivate', {region: region}, ipe.activateSortable);
199
  };
200

    
201
  this.initEditing = function(formdata) {
202
    ipe.editing = true;
203
    ipe.topParent = $('div#panels-ipe-display-' + cache_key);
204
    ipe.backup = this.topParent.clone();
205

    
206
    // See http://jqueryui.com/demos/sortable/ for details on the configuration
207
    // parameters used here.
208
    ipe.changed = false;
209

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

    
212
    // Since the connectWith option only does a one-way hookup, iterate over
213
    // all sortable regions to connect them with one another.
214
    $('div.panels-ipe-sort-container', ipe.topParent)
215
      .sortable('option', 'connectWith', ['div.panels-ipe-sort-container']);
216

    
217
    $('div.panels-ipe-sort-container', ipe.topParent).bind('sortupdate', function() {
218
      ipe.changed = true;
219
    });
220

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

    
223
    // Refresh the control jQuery object.
224
    ipe.control = $(ipe.control.selector);
225
    $('.panels-ipe-form-container', ipe.control).append(formdata);
226

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

    
230
      element_settings.url = $(this.form).attr('action');
231
      element_settings.setClick = true;
232
      element_settings.event = 'click';
233
      element_settings.progress = { 'type': 'throbber' };
234
      element_settings.ipe_cache_key = cache_key;
235

    
236
      var base = $(this).attr('id');
237
      Drupal.ajax[ipe.base] = new Drupal.ajax(base, this, element_settings);
238
    });
239

    
240
    // Perform visual effects in a particular sequence.
241
    // .show() + .hide() cannot have speeds associated with them, otherwise
242
    // it clears out inline styles.
243
    $('.panels-ipe-on').show();
244
    ipe.showForm();
245
    $('body').add(ipe.topParent).addClass('panels-ipe-editing');
246

    
247
  };
248

    
249
  this.hideContainer = function() {
250
    ipe.container.slideUp('fast');
251
  };
252

    
253
  this.showContainer = function() {
254
    ipe.container.slideDown('normal');
255
  };
256

    
257
  this.showButtons = function() {
258
    $('.panels-ipe-form-container').hide();
259
    $('.panels-ipe-button-container').show();
260
    ipe.showContainer();
261
  };
262

    
263
  this.showForm = function() {
264
    $('.panels-ipe-button-container').hide();
265
    $('.panels-ipe-form-container').show();
266
    ipe.showContainer();
267
  };
268

    
269
  this.endEditing = function() {
270
    ipe.editing = false;
271
    ipe.lockPath = null;
272
    $('.panels-ipe-form-container').empty();
273
    // Re-show all the IPE non-editing meta-elements
274
    $('div.panels-ipe-off').show('fast');
275

    
276
    // Refresh the container and control jQuery objects.
277
    ipe.container = $(ipe.container.selector);
278
    ipe.control = $(ipe.control.selector);
279

    
280
    ipe.showButtons();
281
    // Re-hide all the IPE meta-elements
282
    $('div.panels-ipe-on').hide();
283

    
284
    $('.panels-ipe-editing').removeClass('panels-ipe-editing');
285
    $('div.panels-ipe-sort-container.ui-sortable', ipe.topParent).sortable("destroy");
286
  };
287

    
288
  this.saveEditing = function() {
289
    $('div.panels-ipe-region', ipe.topParent).each(function() {
290
      var val = '';
291
      var region = $(this).attr('id').split('panels-ipe-regionid-')[1];
292
      $(this).find('div.panels-ipe-portlet-wrapper').each(function() {
293
        var id = $(this).attr('id').split('panels-ipe-paneid-')[1];
294
        if (id) {
295
          if (val) {
296
            val += ',';
297
          }
298
          val += id;
299
        }
300
      });
301
      $('[name="panel[pane][' +  region + ']"]', ipe.control).val(val);
302
    });
303
  }
304

    
305
  this.cancelIPE = function() {
306
    ipe.hideContainer();
307
    ipe.topParent.fadeOut('medium', function() {
308
      ipe.topParent.replaceWith(ipe.backup.clone());
309
      ipe.topParent = $('div#panels-ipe-display-' + ipe.key);
310

    
311
      // Processing of these things got lost in the cloning, but the classes remained behind.
312
      // @todo this isn't ideal but I can't seem to figure out how to keep an unprocessed backup
313
      // that will later get processed.
314
      $('.ctools-use-modal-processed', ipe.topParent).removeClass('ctools-use-modal-processed');
315
      $('.panels-ipe-hide-bar-processed', ipe.topParent).removeClass('panels-ipe-hide-bar-processed');
316
      $('.pane-delete-processed', ipe.topParent).removeClass('pane-delete-processed');
317
      ipe.topParent.fadeIn('medium');
318
      Drupal.attachBehaviors();
319
    });
320
  };
321

    
322
  this.cancelEditing = function() {
323
    if (ipe.topParent.hasClass('changed')) {
324
      ipe.changed = true;
325
    }
326

    
327
    if (!ipe.changed || confirm(Drupal.t('This will discard all unsaved changes. Are you sure?'))) {
328
      this.cancelIPE();
329
      return true;
330
    }
331
    else {
332
      // Cancel the submission.
333
      return false;
334
    }
335
  };
336

    
337
  this.createSortContainers = function() {
338
    $('div.panels-ipe-region', this.topParent).each(function() {
339
      $(this).children('div.panels-ipe-portlet-marker').parent()
340
        .wrapInner('<div class="panels-ipe-sort-container" />');
341

    
342
      // Move our gadgets outside of the sort container so that sortables
343
      // cannot be placed after them.
344
      $('div.panels-ipe-portlet-static', this).each(function() {
345
        $(this).prependTo($(this).parent().parent());
346
      });
347
    });
348
  }
349

    
350
  this.createSortContainers();
351

    
352
};
353

    
354
$(function() {
355
  Drupal.ajax.prototype.commands.initIPE = function(ajax, data, status) {
356
    if (Drupal.PanelsIPE.editors[data.key]) {
357
      Drupal.PanelsIPE.editors[data.key].initEditing(data.data);
358
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
359
    }
360
    Drupal.attachBehaviors();
361

    
362
  };
363

    
364
  Drupal.ajax.prototype.commands.IPEsetLockState = function(ajax, data, status) {
365
    if (Drupal.PanelsIPE.editors[data.key]) {
366
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
367
    }
368
  };
369

    
370
  Drupal.ajax.prototype.commands.addNewPane = function(ajax, data, status) {
371
    if (Drupal.PanelsIPE.editors[data.key]) {
372
      Drupal.PanelsIPE.editors[data.key].changed = true;
373
    }
374
  };
375

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

    
383
  Drupal.ajax.prototype.commands.unlockIPE = function(ajax, data, status) {
384
    if (confirm(data.message)) {
385
      var ajaxOptions = ajax.options;
386
      ajaxOptions.url = data.break_path;
387
      $.ajax(ajaxOptions);
388
    }
389
    else {
390
      Drupal.PanelsIPE.editors[data.key].endEditing();
391
    }
392
  };
393

    
394
  Drupal.ajax.prototype.commands.endIPE = function(ajax, data, status) {
395
    if (Drupal.PanelsIPE.editors[data.key]) {
396
      Drupal.PanelsIPE.editors[data.key].endEditing();
397
    }
398
  };
399

    
400
  Drupal.ajax.prototype.commands.insertNewPane = function(ajax, data, status) {
401
    IPEContainerSelector = '#panels-ipe-regionid-' + data.regionId + ' div.panels-ipe-sort-container';
402
    firstPaneSelector = IPEContainerSelector + ' div.panels-ipe-portlet-wrapper:first';
403
    // Insert the new pane before the first existing pane in the region, if
404
    // any.
405
    if ($(firstPaneSelector).length) {
406
      insertData = {
407
        'method': 'before',
408
        'selector': firstPaneSelector,
409
        'data': data.renderedPane,
410
        'settings': null
411
      }
412
      Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
413
    }
414
    // Else, insert it as a first child of the container. Doing so might fall
415
    // outside of the wrapping markup for the style, but it's the best we can
416
    // do.
417
    else {
418
      insertData = {
419
        'method': 'prepend',
420
        'selector': IPEContainerSelector,
421
        'data': data.renderedPane,
422
        'settings': null
423
      }
424
      Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
425
    }
426
  };
427

    
428
  /**
429
   * Override the eventResponse on ajax.js so we can add a little extra
430
   * behavior.
431
   */
432
  Drupal.ajax.prototype.ipeReplacedEventResponse = Drupal.ajax.prototype.eventResponse;
433
  Drupal.ajax.prototype.eventResponse = function (element, event) {
434
    if (element.ipeCancelThis) {
435
      element.ipeCancelThis = null;
436
      return false;
437
    }
438

    
439
    if ($(this.element).attr('id') == 'panels-ipe-cancel') {
440
      if (!Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].cancelEditing()) {
441
        return false;
442
      }
443
    }
444

    
445
    var retval = this.ipeReplacedEventResponse(element, event);
446
    if (this.ajaxing && this.element_settings.ipe_cache_key) {
447
      // Move the throbber so that it appears outside our container.
448
      if (this.progress.element) {
449
        $(this.progress.element).addClass('ipe-throbber').appendTo($('body'));
450
      }
451
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].hideContainer();
452
    }
453
    // @TODO $('#panels-ipe-throbber-backdrop').remove();
454
    return retval;
455
  };
456

    
457
  /**
458
   * Override the eventResponse on ajax.js so we can add a little extra
459
   * behavior.
460
   */
461
  Drupal.ajax.prototype.ipeReplacedError = Drupal.ajax.prototype.error;
462
  Drupal.ajax.prototype.error = function (response, uri) {
463
    var retval = this.ipeReplacedError(response, uri);
464
    if (this.element_settings.ipe_cache_key) {
465
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].showContainer();
466
    }
467
  };
468

    
469
  Drupal.ajax.prototype.ipeReplacedBeforeSerialize = Drupal.ajax.prototype.beforeSerialize;
470
  Drupal.ajax.prototype.beforeSerialize = function (element_settings, options) {
471
    if ($(this.element).hasClass('panels-ipe-save')) {
472
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].saveEditing();
473
    };
474
    return this.ipeReplacedBeforeSerialize(element_settings, options);
475
  };
476

    
477
});
478

    
479
/**
480
 * Apply margin to bottom of the page.
481
 *
482
 * Note that directly applying marginBottom does not work in IE. To prevent
483
 * flickering/jumping page content with client-side caching, this is a regular
484
 * Drupal behavior.
485
 *
486
 * @see admin_menu.js via https://drupal.org/project/admin_menu
487
 */
488
Drupal.behaviors.panelsIpeMarginBottom = {
489
  attach: function () {
490
    $('body:not(.panels-ipe)').addClass('panels-ipe');
491
  }
492
};
493

    
494
})(jQuery);