Projet

Général

Profil

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

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

1
/**
2
 * @file display_editor.js
3
 *
4
 * Contains the javascript for the Panels display editor.
5
 */
6

    
7
(function ($) {
8

    
9
// randomly lock a pane.
10
// @debug only
11
Drupal.settings.Panels = Drupal.settings.Panels || {};
12

    
13

    
14
/** Delete pane button **/
15
Drupal.Panels.bindClickDelete = function(context) {
16
  $('a.pane-delete:not(.pane-delete-processed)', context)
17
    .addClass('pane-delete-processed')
18
    .click(function() {
19
    if (confirm(Drupal.t('Remove this pane?'))) {
20
      var id = '#' + $(this).attr('id').replace('pane-delete-', '');
21
      $(id).remove();
22
      Drupal.Panels.Draggable.savePositions();
23
    }
24
    return false;
25
  });
26
};
27

    
28
Drupal.Panels.bindPortlet = function() {
29
  var handle = $(this).find('.panel-pane-collapsible > div.pane-title');
30
  var content = $(this).find('.panel-pane-collapsible > div.pane-content');
31
  if (content.length) {
32
    var toggle = $('<span class="toggle toggle-collapsed"></span>');
33
    handle.before(toggle);
34
    toggle.click(function() {
35
      content.slideToggle(20);
36
      toggle.toggleClass('toggle-collapsed');
37
    });
38
    handle.click(function() {
39
      content.slideToggle(20);
40
      toggle.toggleClass('toggle-collapsed');
41
    });
42
    content.hide();
43
  }
44
};
45

    
46
Drupal.Panels.Draggable = {
47
  // The draggable object
48
  object: null,
49

    
50
  // Where objects can be dropped
51
  dropzones: [],
52
  current_dropzone: null,
53

    
54
  // positions within dropzones where an object can be plazed
55
  landing_pads: [],
56
  current_pad: null,
57

    
58
  // Where the object is
59
  mouseOffset: { x: 0, y: 0 },
60
  windowOffset: { x: 0, y: 0 },
61
  offsetDivHeight: 0,
62

    
63
  // original settings to be restored
64
  original: {},
65
  // a placeholder so that if the object is let go but not over a drop zone,
66
  // it can be put back where it belongs
67
  placeholder: {},
68

    
69
  hoverclass: 'hoverclass',
70
  helperclass: 'helperclass',
71
  accept: 'div.panel-region',
72
  handle: 'div.grabber',
73
  draggable: 'div.panel-portlet',
74
  main: 'div#panels-dnd-main',
75

    
76
  // part of the id to remove to get just the number
77
  draggableId: 'panel-pane-',
78

    
79
  // part of the id to remove to get just the number
80
  regionId: 'panel-region-',
81

    
82
  // What to add to the front of a the id to get the form id for a panel
83
  formId: '#edit-',
84

    
85
  maxWidth: 250,
86

    
87
  unsetDropZone: function() {
88
    $(this.current_dropzone.obj).removeClass(this.hoverclass);
89
    this.current_dropzone = null;
90
    for (var i in this.landing_pads) {
91
      $(this.landing_pads[i].obj).remove();
92
    }
93
    this.landing_pads = [];
94
    this.current_pad = null;
95
  },
96

    
97
  createLandingPad: function(where, append) {
98
    var obj = $('<div class="' + this.helperclass +'" id="' +
99
      $(where).attr('id') + '-dropzone">&nbsp;</div>');
100
    if (append) {
101
      $(where).append(obj);
102
    }
103
    else {
104
      $(where).before(obj);
105
    }
106
    var offset = $(obj).offset();
107

    
108
    $(obj).css({
109
      display: 'none'
110
    });
111
    this.landing_pads.push({
112
      centerX: offset.left + ($(obj).innerWidth() / 2),
113
      centerY: offset.top + ($(obj).innerHeight() / 2),
114
      obj: obj
115
    });
116
    return obj;
117
  },
118

    
119
  calculateDropZones: function(event, dropzone) {
120
    var draggable = Drupal.Panels.Draggable;
121
    var dropzones = [];
122
    $(this.accept).each(function() {
123
      var offset = $(this).offset();
124
      offset.obj = this;
125
      offset.region = this.id.replace(draggable.regionId, '');
126
      offset.width = $(this).outerWidth();
127
      offset.height = $(this).outerHeight();
128
      dropzones.push(offset);
129
    });
130
    this.dropzones = dropzones;
131
  },
132

    
133
  reCalculateDropZones: function() {
134
    for (var i in this.dropzones) {
135
      var offset = $(this.dropzones[i].obj).offset();
136
      offset.width = $(this.dropzones[i].obj).outerWidth();
137
      offset.height = $(this.dropzones[i].obj).outerHeight();
138
      $.extend(this.dropzones[i], offset);
139
    }
140
  },
141

    
142
  changeDropZone: function(new_dropzone) {
143
    // Unset our old dropzone.
144
    if (this.current_dropzone) {
145
      this.unsetDropZone();
146
    }
147

    
148
    // Set up our new dropzone.
149
    this.current_dropzone = new_dropzone;
150
    $(this.current_dropzone.obj).addClass(this.hoverclass);
151
    // add a landing pad
152
    this.createLandingPad(this.current_dropzone.obj, true);
153

    
154
    var that = this;
155
    // Create a landing pad before each existing portlet.
156
    $(this.current_dropzone.obj).find(this.draggable).each(function() {
157
      if (that.object.id != this.id) {
158
        that.createLandingPad(this, false);
159
      }
160
    });
161
  },
162

    
163
  findLandingPad: function(x, y) {
164
    var shortest_distance = null;
165
    var nearest_pad = null;
166
    // find the nearest pad.
167
    for (var i in this.landing_pads) {
168
      // This isn't the real distance, this is the square of the
169
      // distance -- no point in spending processing time on
170
      // sqrt.
171
      var dstx = Math.abs(x - this.landing_pads[i].centerX);
172
      var dsty = Math.abs(y - this.landing_pads[i].centerY);
173
      var distance =  (dstx * dstx) + (dsty * dsty);
174
      if (shortest_distance == null || distance < shortest_distance) {
175
        shortest_distance = distance;
176
        nearest_pad = this.landing_pads[i];
177
      }
178
    }
179
    if (nearest_pad != this.current_pad) {
180
      if (this.current_pad) {
181
        $(this.current_pad.obj).hide();
182
      }
183
      this.current_pad = nearest_pad;
184
      $(nearest_pad.obj).show();
185
    }
186
  },
187

    
188
  findDropZone: function(x, y) {
189
    // Go through our dropzones and see if we're over one.
190
    var new_dropzone = null;
191
    for (var i in this.dropzones) {
192
//      console.log('x:' + x + ' left:' + this.dropzones[i].left + ' right: ' + this.dropzones[i].left + this.dropzones[i].width);
193
      if (this.dropzones[i].left < x &&
194
        x < this.dropzones[i].left + this.dropzones[i].width &&
195
        this.dropzones[i].top < y &&
196
        y < this.dropzones[i].top + this.dropzones[i].height) {
197
          new_dropzone = this.dropzones[i];
198
          break;
199
      }
200
    }
201
    // If we're over one, see if it's different.
202
    if (new_dropzone && (!this.regionLock || this.regionLockRegions[new_dropzone.region])) {
203
      var changed = false;
204
      if (!this.current_dropzone || new_dropzone.obj.id != this.current_dropzone.obj.id) {
205
        this.changeDropZone(new_dropzone);
206
        changed = true;
207
      }
208
      this.findLandingPad(x, y);
209
      if (changed)  {
210
        // recalculate the size of our drop zones due to the fact that we're drawing landing pads.
211
        this.reCalculateDropZones();
212
      }
213
    }
214
    // If we're not over one, be sure to unhilite one if we were just
215
    // over it.
216
    else if (this.current_dropzone) {
217
      this.unsetDropZone();
218
    }
219
  },
220

    
221
  /** save button clicked, or pane deleted **/
222
  savePositions: function() {
223
    var draggable = Drupal.Panels.Draggable;
224
    $(draggable.accept).each(function() {
225
      var val = '';
226
      $(this).find(draggable.draggable).each(function() {
227
        if (val) {
228
          val += ',';
229
        }
230

    
231
        val += this.id.replace(draggable.draggableId, '');
232
      });
233
      var region = this.id.replace(draggable.regionId, '');
234
      $('input[name="panel[pane][' +  region + ']"]').val(val);
235
    });
236
    return false;
237
  }
238
};
239

    
240
Drupal.Panels.DraggableHandler = function() {
241
  $(this).addClass('panel-draggable');
242
  var draggable = Drupal.Panels.Draggable;
243
  var scrollBuffer = 10;
244
  var scrollDistance = 10;
245
  var scrollTimer = 30;
246

    
247
  getMouseOffset = function(docPos, mousePos, windowPos) {
248
    return { x: mousePos.x - docPos.x + windowPos.x, y: mousePos.y - docPos.y + windowPos.y};
249
  };
250

    
251
  getMousePos = function(ev) {
252
    ev = ev || window.event;
253

    
254
    if (ev.pageX || ev.pageY) {
255
      return { x:ev.pageX, y:ev.pageY };
256
    }
257
    return {
258
      x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
259
      y:ev.clientY + document.body.scrollTop  - document.body.clientTop
260
    };
261
  };
262

    
263
  getPosition = function(e) {
264
    /*
265
    if (document.defaultView && document.defaultView.getComputedStyle) {
266
      var css = document.defaultView.getComputedStyle(e, null);
267
      return {
268
        x: parseInt(css.getPropertyValue('left')),
269
        y: parseInt(css.getPropertyValue('top'))
270
      };
271
    }
272
    */
273
    var left = 0;
274
    var top  = 0;
275

    
276
    while (e.offsetParent) {
277
      left += e.offsetLeft;
278
      top  += e.offsetTop;
279
      e     = e.offsetParent;
280
    }
281

    
282
    left += e.offsetLeft;
283
    top  += e.offsetTop;
284

    
285
    return { x:left, y:top };
286
  };
287

    
288
  mouseUp = function(e) {
289
    clearTimeout(draggable.timeoutId);
290
    draggable.dropzones = [];
291

    
292
    if (draggable.current_pad) {
293
      // Drop the object where we're hovering
294
      $(draggable.object).insertAfter($(draggable.current_pad.obj));
295
      Drupal.Panels.changed($(draggable.object));
296
    }
297
    else {
298
      // or put it back where it came from
299
      $(draggable.object).insertAfter(draggable.placeholder);
300
    }
301
    // remove the placeholder
302
    draggable.placeholder.remove();
303

    
304
    // restore original settings.
305
    $(draggable.object).css(draggable.original);
306
    if (draggable.current_dropzone) {
307
      draggable.unsetDropZone();
308
    }
309

    
310
    $(document).unbind('mouseup').unbind('mousemove');
311
    draggable.savePositions();
312
  };
313

    
314
  mouseMove = function(e) {
315
    draggable.mousePos = getMousePos(e);
316

    
317
    draggable.findDropZone(draggable.mousePos.x, draggable.mousePos.y);
318

    
319
    var windowMoved = parseInt(draggable.offsetDivHeight - $(draggable.main).innerHeight());
320

    
321
    draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + windowMoved + 'px';
322
    draggable.object.style.left = draggable.mousePos.x - draggable.mouseOffset.x  + 'px';
323
    $(draggable.object).toggleClass('moving');
324
  };
325

    
326
  mouseDown = function(e) {
327
    // If we mouse-downed over something clickable, don't drag!
328
    if (e.target.nodeName == 'A' || e.target.nodeName == 'INPUT' || e.target.parentNode.nodeName == 'A' || e.target.nodeName.nodeName == 'INPUT') {
329
      return;
330
    }
331

    
332
    draggable.object = $(this).parent(draggable.draggable).get(0);
333
    draggable.paneId = draggable.object.id.replace(draggable.draggableId, '');
334

    
335
    // create a placeholder so we can put this object back if dropped in an invalid location.
336
    draggable.placeholder = $('<div class="draggable-placeholder-object" style="display:none"></div>"');
337
    $(draggable.object).after(draggable.placeholder);
338

    
339
    // Store original CSS so we can put it back.
340
    draggable.original = {
341
      position: $(draggable.object).css('position'),
342
      width: 'auto',
343
      left: $(draggable.object).css('left'),
344
      top: $(draggable.object).css('top'),
345
      'z-index': $(draggable.object).css('z-index'),
346
      'margin-bottom': $(draggable.object).css('margin-bottom'),
347
      'margin-top': $(draggable.object).css('margin-top'),
348
      'margin-left': $(draggable.object).css('margin-left'),
349
      'margin-right': $(draggable.object).css('margin-right'),
350
      'padding-bottom': $(draggable.object).css('padding-bottom'),
351
      'padding-top': $(draggable.object).css('padding-top'),
352
      'padding-left': $(draggable.object).css('padding-left'),
353
      'padding-right': $(draggable.object).css('padding-right')
354
    };
355

    
356
    draggable.mousePos = getMousePos(e);
357
    var originalPos = $(draggable.object).offset();
358
    var width = Math.min($(draggable.object).innerWidth(), draggable.maxWidth);
359

    
360
    draggable.offsetDivHeight = $(draggable.main).innerHeight();
361
    draggable.findDropZone(draggable.mousePos.x, draggable.mousePos.y);
362

    
363
    // Make copies of these because in FF3, they actually change when we
364
    // move the item, whereas they did not in FF2.
365

    
366
    if (e.layerX || e.layerY) {
367
      var layerX = e.layerX;
368
      var layerY = e.layerY;
369
    }
370
    else if (e.originalEvent && e.originalEvent.layerX) {
371
      var layerX = e.originalEvent.layerX;
372
      var layerY = e.originalEvent.layerY;
373
    }
374

    
375
    // Make the draggable relative, get it out of the way and make it
376
    // invisible.
377
    $(draggable.object).css({
378
      position: 'relative',
379
      'z-index': 100,
380
      width: width + 'px',
381
      'margin-bottom': (-1 * parseInt($(draggable.object).outerHeight())) + 'px',
382
      'margin-top': 0,
383
      'margin-left': 0,
384
      'margin-right': (-1 * parseInt($(draggable.object).outerWidth())) + 'px',
385
      'padding-bottom': 0,
386
      'padding-top': 0,
387
      'padding-left': 0,
388
      'padding-right': 0,
389
      'left': 0,
390
      'top': 0
391
    })
392
      .insertAfter($(draggable.main));
393
    var newPos = $(draggable.object).offset();
394

    
395
    var windowOffset = { left: originalPos.left - newPos.left, top: originalPos.top - newPos.top }
396

    
397
    // if they grabbed outside the area where we make the draggable smaller, move it
398
    // closer to the cursor.
399
    if (layerX != 'undefined' && layerX > width) {
400
      windowOffset.left += layerX - 10;
401
    }
402
    else if (layerX != 'undefined' && e.offsetX > width) {
403
      windowOffset.left += e.offsetX - 10;
404
    }
405

    
406
    // This is stored so we can move with it.
407
    draggable.mouseOffset = { x: draggable.mousePos.x - windowOffset.left, y: draggable.mousePos.y - windowOffset.top};
408
    draggable.offsetDivHeight = $(draggable.main).innerHeight();
409

    
410
    draggable.object.style.top = windowOffset.top + 'px';
411
    draggable.object.style.left = windowOffset.left + 'px';
412
    $(document).unbind('mouseup').unbind('mousemove').mouseup(mouseUp).mousemove(mouseMove);
413

    
414
    draggable.calculateDropZones(draggable.mousePos, e);
415
    draggable.timeoutId = setTimeout('timer()', scrollTimer);
416

    
417
    // If locking to a particular set of regions, set that:
418
    if (Drupal.settings.Panels && Drupal.settings.Panels.RegionLock && Drupal.settings.Panels.RegionLock[draggable.paneId]) {
419
      draggable.regionLock = true;
420
      draggable.regionLockRegions = Drupal.settings.Panels.RegionLock[draggable.paneId];
421
    }
422
    else {
423
      draggable.regionLock = false;
424
      draggable.regionLockRegions = null;
425
    }
426

    
427
    return false;
428
  };
429

    
430
  timer = function() {
431
    if (!draggable.timeCount) {
432
      draggable.timeCount = 0;
433
    }
434
    draggable.timeCount = draggable.timeCount + 1;
435
    var left = $(window).scrollLeft();
436
    var right = left + $(window).width();
437
    var top = $(window).scrollTop();
438
    var bottom = top + $(window).height();
439

    
440
    if (draggable.mousePos.x < left + scrollBuffer && left > 0) {
441
      window.scrollTo(left - scrollDistance, top);
442
      draggable.mousePos.x -= scrollDistance;
443
      draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px';
444
    }
445
    else if (draggable.mousePos.x > right - scrollBuffer) {
446
      window.scrollTo(left + scrollDistance, top);
447
      draggable.mousePos.x += scrollDistance;
448
      draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px';
449
    }
450
    else if (draggable.mousePos.y < top + scrollBuffer && top > 0) {
451
      window.scrollTo(left, top - scrollDistance);
452
      draggable.mousePos.y -= scrollDistance;
453
      draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px';
454
    }
455
    else if (draggable.mousePos.y > bottom - scrollBuffer) {
456
      window.scrollTo(left, top + scrollDistance);
457
      draggable.mousePos.y += scrollDistance;
458
      draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px';
459
    }
460

    
461
    draggable.timeoutId = setTimeout('timer()', scrollTimer);
462
  }
463

    
464
  $(this).mousedown(mouseDown);
465
};
466

    
467
$.fn.extend({
468
  panelsDraggable: Drupal.Panels.DraggableHandler
469
});
470

    
471
/**
472
 * Implement Drupal behavior for autoattach
473
 */
474
Drupal.behaviors.PanelsDisplayEditor = {
475
  attach: function(context) {
476
    // Show javascript only items.
477
    $('span#panels-js-only').css('display', 'inline');
478

    
479
    $('#panels-dnd-main div.panel-pane:not(.panel-portlet)')
480
      .addClass('panel-portlet')
481
      .each(Drupal.Panels.bindPortlet);
482

    
483
    // The above doesn't work if context IS the pane, so do this to catch that.
484
    if ($(context).hasClass('panel-pane') && !$(context).hasClass('panel-portlet')) {
485
      $(context)
486
        .addClass('panel-portlet')
487
        .each(Drupal.Panels.bindPortlet);
488
    }
489

    
490
    // Make draggables and make sure their positions are saved.
491
    $(context).find('div.grabber:not(.panel-draggable)').panelsDraggable();
492
    Drupal.Panels.Draggable.savePositions();
493

    
494
    // Bind buttons.
495
    $('#panels-hide-all', context).click(Drupal.Panels.clickHideAll);
496
    $('#panels-show-all', context).click(Drupal.Panels.clickShowAll);
497

    
498
    Drupal.Panels.bindClickDelete(context);
499

    
500
    $('#panels-live-preview-button:not(.panels-preview-processed)')
501
      .addClass('panels-preview-processed')
502
      .click(function () {
503
        if (!$('#panels-preview').size()) {
504
          $('#panels-dnd-main').parents('form').after('<div id="panels-preview" class="clearfix"></div>');
505
        }
506
        var html = '';
507
        html += '  <div id="modal-throbber">';
508
        html += '    <div class="modal-throbber-wrapper">';
509
        html +=        Drupal.settings.CToolsModal.throbber;
510
        html += '    </div>';
511
        html += '  </div>';
512

    
513
        $('#panels-preview').html(html);
514
      });
515

    
516
    // Bind modal detach behaviors to cancel current form.
517
    $(document).bind('CToolsDetachBehaviors', function(event, context) {
518
      $('#edit-cancel-style', context).trigger('click');
519
    });
520

    
521
    var setTitleClass = function () {
522
      if ($('#edit-display-title-hide-title').val() == 2) {
523
        $('#panels-dnd-main').removeClass('panels-set-title-hide');
524
      }
525
      else {
526
        $('#panels-dnd-main').addClass('panels-set-title-hide');
527
      }
528
    }
529

    
530
    // The panes have an option to set the display title, but only if
531
    // a select is set to the proper value. This sets a class on the
532
    // main edit div so that the option to set the display title
533
    // is hidden if that is not selected, and visible if it is.
534
    $('#edit-display-title-hide-title:not(.panels-title-processed)')
535
      .addClass('panels-title-processed')
536
      .change(setTitleClass);
537

    
538
    setTitleClass();
539
  }
540
}
541

    
542
$(function() {
543
  /**
544
   * AJAX responder command to render the preview.
545
   */
546
  Drupal.ajax.prototype.commands.panel_preview = function(ajax, command, status) {
547
    $('#panels-preview').html(command.output);
548
  }
549
});
550

    
551
})(jQuery);