Projet

Général

Profil

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

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

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-processed').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
    dropOnEmpty: true
87
  }, cfg.sortableOptions || {});
88

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

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

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

    
107

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

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

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

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

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

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

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

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

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

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

    
163
    var disabledRegions = false;
164

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
246
  };
247

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
348
  this.createSortContainers();
349

    
350
};
351

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

    
360
  };
361

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

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

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

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

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

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

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

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

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

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

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

    
475
});
476

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

    
492
})(jQuery);