Projet

Général

Profil

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

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

1

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

    
5
// randomly lock a pane.
6
// @debug only
7
Drupal.settings.Panels = Drupal.settings.Panels || {};
8
Drupal.settings.Panels.RegionLock = {
9
  10: { 'top': false, 'left': true, 'middle': true }
10
}
11

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

    
31

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

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

    
83
  this.regions = [];
84
  this.sortables = {};
85

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

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

    
101

    
102
  // If a user navigates away from a locked IPE, cancel the lock in the background.
103
  $(window).bind('beforeunload', function() {
104
    if (!ipe.editing) {
105
      return;
106
    }
107

    
108
    if (ipe.topParent && ipe.topParent.hasClass('changed')) {
109
      ipe.changed = true;
110
    }
111

    
112
    if (ipe.changed) {
113
      return Drupal.t('This will discard all unsaved changes. Are you sure?');
114
    }
115
  });
116

    
117
  // If a user navigates away from a locked IPE, cancel the lock in the background.
118
  $(window).bind('unload', function() {
119
    ipe.cancelLock(true);
120
  });
121

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

    
136
      if (sync) {
137
        ajaxOptions.async = false;
138
      }
139

    
140
      // Make sure we don't somehow get another one:
141
      ipe.lockPath = null;
142

    
143
      // Send the request. This is synchronous to prevent being cancelled.
144
      $.ajax(ajaxOptions);
145
    }
146
  }
147

    
148
  this.activateSortable = function(event, ui) {
149
    if (!Drupal.settings.Panels || !Drupal.settings.Panels.RegionLock) {
150
      // don't bother if there are no region locks in play.
151
      return;
152
    }
153

    
154
    var region = event.data.region;
155
    var paneId = ui.item.attr('id').replace('panels-ipe-paneid-', '');
156

    
157
    var disabledRegions = false;
158

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

    
170
    // If we disabled regions, we need to
171
    if (disabledRegions) {
172
      $(event.srcElement).bind('dragstop', function(event, ui) {
173
        // Go through
174
      });
175
    }
176
  };
177

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

    
186
  this.initSorting = function() {
187
    var $region = $(this).parents('.panels-ipe-region');
188
    var region = $region.attr('id').replace('panels-ipe-regionid-', '');
189
    ipe.sortables[region] = $(this).sortable(ipe.sortableOptions);
190
    ipe.regions.push(region);
191
    $(this).bind('sortactivate', {region: region}, ipe.activateSortable);
192
  };
193

    
194
  this.initEditing = function(formdata) {
195
    ipe.editing = true;
196
    ipe.topParent = $('div#panels-ipe-display-' + cache_key);
197
    ipe.backup = this.topParent.clone();
198

    
199
    // See http://jqueryui.com/demos/sortable/ for details on the configuration
200
    // parameters used here.
201
    ipe.changed = false;
202

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

    
205
    // Since the connectWith option only does a one-way hookup, iterate over
206
    // all sortable regions to connect them with one another.
207
    $('div.panels-ipe-sort-container', ipe.topParent)
208
      .sortable('option', 'connectWith', ['div.panels-ipe-sort-container']);
209

    
210
    $('div.panels-ipe-sort-container', ipe.topParent).bind('sortupdate', function() {
211
      ipe.changed = true;
212
    });
213

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

    
216
    $('.panels-ipe-form-container', ipe.control).append(formdata);
217

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

    
221
      element_settings.url = $(this.form).attr('action');
222
      element_settings.setClick = true;
223
      element_settings.event = 'click';
224
      element_settings.progress = { 'type': 'throbber' };
225
      element_settings.ipe_cache_key = cache_key;
226

    
227
      var base = $(this).attr('id');
228
      Drupal.ajax[ipe.base] = new Drupal.ajax(base, this, element_settings);
229
    });
230

    
231
    // Perform visual effects in a particular sequence.
232
    // .show() + .hide() cannot have speeds associated with them, otherwise
233
    // it clears out inline styles.
234
    $('.panels-ipe-on').show();
235
    ipe.showForm();
236
    ipe.topParent.addClass('panels-ipe-editing');
237

    
238
  };
239

    
240
  this.hideContainer = function() {
241
    ipe.container.slideUp('fast');
242
  };
243

    
244
  this.showContainer = function() {
245
    ipe.container.slideDown('normal');
246
  };
247

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

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

    
260
  this.endEditing = function() {
261
    ipe.editing = false;
262
    ipe.lockPath = null;
263
    $('.panels-ipe-form-container').empty();
264
    // Re-show all the IPE non-editing meta-elements
265
    $('div.panels-ipe-off').show('fast');
266

    
267
    ipe.showButtons();
268
    // Re-hide all the IPE meta-elements
269
    $('div.panels-ipe-on').hide();
270

    
271
    $('.panels-ipe-editing').removeClass('panels-ipe-editing');
272
    $('div.panels-ipe-sort-container.ui-sortable', ipe.topParent).sortable("destroy");
273
  };
274

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

    
292
  this.cancelIPE = function() {
293
    ipe.hideContainer();
294
    ipe.topParent.fadeOut('medium', function() {
295
      ipe.topParent.replaceWith(ipe.backup.clone());
296
      ipe.topParent = $('div#panels-ipe-display-' + ipe.key);
297

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

    
308
  this.cancelEditing = function() {
309
    if (ipe.topParent.hasClass('changed')) {
310
      ipe.changed = true;
311
    }
312

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

    
323
  this.createSortContainers = function() {
324
    $('div.panels-ipe-region', this.topParent).each(function() {
325
      $(this).children('div.panels-ipe-portlet-marker').parent()
326
        .wrapInner('<div class="panels-ipe-sort-container" />');
327

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

    
336
  this.createSortContainers();
337

    
338
};
339

    
340
$(function() {
341
  Drupal.ajax.prototype.commands.initIPE = function(ajax, data, status) {
342
    if (Drupal.PanelsIPE.editors[data.key]) {
343
      Drupal.PanelsIPE.editors[data.key].initEditing(data.data);
344
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
345
    }
346
    Drupal.attachBehaviors();
347

    
348
  };
349

    
350
  Drupal.ajax.prototype.commands.IPEsetLockState = function(ajax, data, status) {
351
    if (Drupal.PanelsIPE.editors[data.key]) {
352
      Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
353
    }
354
  };
355

    
356
  Drupal.ajax.prototype.commands.addNewPane = function(ajax, data, status) {
357
    if (Drupal.PanelsIPE.editors[data.key]) {
358
      Drupal.PanelsIPE.editors[data.key].changed = true;
359
    }
360
  };
361

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

    
369
  Drupal.ajax.prototype.commands.unlockIPE = function(ajax, data, status) {
370
    if (confirm(data.message)) {
371
      var ajaxOptions = ajax.options;
372
      ajaxOptions.url = data.break_path;
373
      $.ajax(ajaxOptions);
374
    }
375
    else {
376
      Drupal.PanelsIPE.editors[data.key].endEditing();
377
    }
378
  };
379

    
380
  Drupal.ajax.prototype.commands.endIPE = function(ajax, data, status) {
381
    if (Drupal.PanelsIPE.editors[data.key]) {
382
      Drupal.PanelsIPE.editors[data.key].endEditing();
383
    }
384
  };
385

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

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

    
425
    if ($(this.element).attr('id') == 'panels-ipe-cancel') {
426
      if (!Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].cancelEditing()) {
427
        return false;
428
      }
429
    }
430

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

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

    
455
  Drupal.ajax.prototype.ipeReplacedBeforeSerialize = Drupal.ajax.prototype.beforeSerialize;
456
  Drupal.ajax.prototype.beforeSerialize = function (element_settings, options) {
457
    if ($(this.element).hasClass('panels-ipe-save')) {
458
      Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].saveEditing();
459
    };
460
    return this.ipeReplacedBeforeSerialize(element_settings, options);
461
  };
462

    
463
});
464

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

    
480
})(jQuery);