PluginProbe
Tagembed: Social Media Feeds and Customer Reviews Widget / 3.9
Tagembed: Social Media Feeds and Customer Reviews Widget v3.9
7.8 7.5 7.6 7.7 7.4 trunk 3.10 3.9 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 All 43 releases
tagembed-widget / assets / js / packery-mode.pkgd.js

packery-mode.pkgd.js in Tagembed: Social Media Feeds and Customer Reviews Widget 3.9, at assets/js/packery-mode.pkgd.js

1,240 lines 30.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * Packery layout mode PACKAGED v2.0.1
3 * sub-classes Packery
4 */
5
6 /**
7 * Rect
8 * low-level utility class for basic geometry
9 */
10
11 ( function( window, factory ) {
12 // universal module definition
13 /* jshint strict: false */ /* globals define, module */
14 if ( typeof define == 'function' && define.amd ) {
15 // AMD
16 define( 'packery/js/rect',factory );
17 } else if ( typeof module == 'object' && module.exports ) {
18 // CommonJS
19 module.exports = factory();
20 } else {
21 // browser global
22 window.Packery = window.Packery || {};
23 window.Packery.Rect = factory();
24 }
25
26 }( window, function factory() {
27
28
29 // -------------------------- Rect -------------------------- //
30
31 function Rect( props ) {
32 // extend properties from defaults
33 for ( var prop in Rect.defaults ) {
34 this[ prop ] = Rect.defaults[ prop ];
35 }
36
37 for ( prop in props ) {
38 this[ prop ] = props[ prop ];
39 }
40
41 }
42
43 Rect.defaults = {
44 x: 0,
45 y: 0,
46 width: 0,
47 height: 0
48 };
49
50 var proto = Rect.prototype;
51
52 /**
53 * Determines whether or not this rectangle wholly encloses another rectangle or point.
54 * @param {Rect} rect
55 * @returns {Boolean}
56 **/
57 proto.contains = function( rect ) {
58 // points don't have width or height
59 var otherWidth = rect.width || 0;
60 var otherHeight = rect.height || 0;
61 return this.x <= rect.x &&
62 this.y <= rect.y &&
63 this.x + this.width >= rect.x + otherWidth &&
64 this.y + this.height >= rect.y + otherHeight;
65 };
66
67 /**
68 * Determines whether or not the rectangle intersects with another.
69 * @param {Rect} rect
70 * @returns {Boolean}
71 **/
72 proto.overlaps = function( rect ) {
73 var thisRight = this.x + this.width;
74 var thisBottom = this.y + this.height;
75 var rectRight = rect.x + rect.width;
76 var rectBottom = rect.y + rect.height;
77
78 // http://stackoverflow.com/a/306332
79 return this.x < rectRight &&
80 thisRight > rect.x &&
81 this.y < rectBottom &&
82 thisBottom > rect.y;
83 };
84
85 /**
86 * @param {Rect} rect - the overlapping rect
87 * @returns {Array} freeRects - rects representing the area around the rect
88 **/
89 proto.getMaximalFreeRects = function( rect ) {
90
91 // if no intersection, return false
92 if ( !this.overlaps( rect ) ) {
93 return false;
94 }
95
96 var freeRects = [];
97 var freeRect;
98
99 var thisRight = this.x + this.width;
100 var thisBottom = this.y + this.height;
101 var rectRight = rect.x + rect.width;
102 var rectBottom = rect.y + rect.height;
103
104 // top
105 if ( this.y < rect.y ) {
106 freeRect = new Rect({
107 x: this.x,
108 y: this.y,
109 width: this.width,
110 height: rect.y - this.y
111 });
112 freeRects.push( freeRect );
113 }
114
115 // right
116 if ( thisRight > rectRight ) {
117 freeRect = new Rect({
118 x: rectRight,
119 y: this.y,
120 width: thisRight - rectRight,
121 height: this.height
122 });
123 freeRects.push( freeRect );
124 }
125
126 // bottom
127 if ( thisBottom > rectBottom ) {
128 freeRect = new Rect({
129 x: this.x,
130 y: rectBottom,
131 width: this.width,
132 height: thisBottom - rectBottom
133 });
134 freeRects.push( freeRect );
135 }
136
137 // left
138 if ( this.x < rect.x ) {
139 freeRect = new Rect({
140 x: this.x,
141 y: this.y,
142 width: rect.x - this.x,
143 height: this.height
144 });
145 freeRects.push( freeRect );
146 }
147
148 return freeRects;
149 };
150
151 proto.canFit = function( rect ) {
152 return this.width >= rect.width && this.height >= rect.height;
153 };
154
155 return Rect;
156
157 }));
158
159 /**
160 * Packer
161 * bin-packing algorithm
162 */
163
164 ( function( window, factory ) {
165 // universal module definition
166 /* jshint strict: false */ /* globals define, module, require */
167 if ( typeof define == 'function' && define.amd ) {
168 // AMD
169 define( 'packery/js/packer',[ './rect' ], factory );
170 } else if ( typeof module == 'object' && module.exports ) {
171 // CommonJS
172 module.exports = factory(
173 require('./rect')
174 );
175 } else {
176 // browser global
177 var Packery = window.Packery = window.Packery || {};
178 Packery.Packer = factory( Packery.Rect );
179 }
180
181 }( window, function factory( Rect ) {
182
183
184 // -------------------------- Packer -------------------------- //
185
186 /**
187 * @param {Number} width
188 * @param {Number} height
189 * @param {String} sortDirection
190 * topLeft for vertical, leftTop for horizontal
191 */
192 function Packer( width, height, sortDirection ) {
193 this.width = width || 0;
194 this.height = height || 0;
195 this.sortDirection = sortDirection || 'downwardLeftToRight';
196
197 this.reset();
198 }
199
200 var proto = Packer.prototype;
201
202 proto.reset = function() {
203 this.spaces = [];
204
205 var initialSpace = new Rect({
206 x: 0,
207 y: 0,
208 width: this.width,
209 height: this.height
210 });
211
212 this.spaces.push( initialSpace );
213 // set sorter
214 this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
215 };
216
217 // change x and y of rect to fit with in Packer's available spaces
218 proto.pack = function( rect ) {
219 for ( var i=0; i < this.spaces.length; i++ ) {
220 var space = this.spaces[i];
221 if ( space.canFit( rect ) ) {
222 this.placeInSpace( rect, space );
223 break;
224 }
225 }
226 };
227
228 proto.columnPack = function( rect ) {
229 for ( var i=0; i < this.spaces.length; i++ ) {
230 var space = this.spaces[i];
231 var canFitInSpaceColumn = space.x <= rect.x &&
232 space.x + space.width >= rect.x + rect.width &&
233 space.height >= rect.height - 0.01; // fudge number for rounding error
234 if ( canFitInSpaceColumn ) {
235 rect.y = space.y;
236 this.placed( rect );
237 break;
238 }
239 }
240 };
241
242 proto.rowPack = function( rect ) {
243 for ( var i=0; i < this.spaces.length; i++ ) {
244 var space = this.spaces[i];
245 var canFitInSpaceRow = space.y <= rect.y &&
246 space.y + space.height >= rect.y + rect.height &&
247 space.width >= rect.width - 0.01; // fudge number for rounding error
248 if ( canFitInSpaceRow ) {
249 rect.x = space.x;
250 this.placed( rect );
251 break;
252 }
253 }
254 };
255
256 proto.placeInSpace = function( rect, space ) {
257 // place rect in space
258 rect.x = space.x;
259 rect.y = space.y;
260
261 this.placed( rect );
262 };
263
264 // update spaces with placed rect
265 proto.placed = function( rect ) {
266 // update spaces
267 var revisedSpaces = [];
268 for ( var i=0; i < this.spaces.length; i++ ) {
269 var space = this.spaces[i];
270 var newSpaces = space.getMaximalFreeRects( rect );
271 // add either the original space or the new spaces to the revised spaces
272 if ( newSpaces ) {
273 revisedSpaces.push.apply( revisedSpaces, newSpaces );
274 } else {
275 revisedSpaces.push( space );
276 }
277 }
278
279 this.spaces = revisedSpaces;
280
281 this.mergeSortSpaces();
282 };
283
284 proto.mergeSortSpaces = function() {
285 // remove redundant spaces
286 Packer.mergeRects( this.spaces );
287 this.spaces.sort( this.sorter );
288 };
289
290 // add a space back
291 proto.addSpace = function( rect ) {
292 this.spaces.push( rect );
293 this.mergeSortSpaces();
294 };
295
296 // -------------------------- utility functions -------------------------- //
297
298 /**
299 * Remove redundant rectangle from array of rectangles
300 * @param {Array} rects: an array of Rects
301 * @returns {Array} rects: an array of Rects
302 **/
303 Packer.mergeRects = function( rects ) {
304 var i = 0;
305 var rect = rects[i];
306
307 rectLoop:
308 while ( rect ) {
309 var j = 0;
310 var compareRect = rects[ i + j ];
311
312 while ( compareRect ) {
313 if ( compareRect == rect ) {
314 j++; // next
315 } else if ( compareRect.contains( rect ) ) {
316 // remove rect
317 rects.splice( i, 1 );
318 rect = rects[i]; // set next rect
319 continue rectLoop; // bail on compareLoop
320 } else if ( rect.contains( compareRect ) ) {
321 // remove compareRect
322 rects.splice( i + j, 1 );
323 } else {
324 j++;
325 }
326 compareRect = rects[ i + j ]; // set next compareRect
327 }
328 i++;
329 rect = rects[i];
330 }
331
332 return rects;
333 };
334
335
336 // -------------------------- sorters -------------------------- //
337
338 // functions for sorting rects in order
339 var sorters = {
340 // top down, then left to right
341 downwardLeftToRight: function( a, b ) {
342 return a.y - b.y || a.x - b.x;
343 },
344 // left to right, then top down
345 rightwardTopToBottom: function( a, b ) {
346 return a.x - b.x || a.y - b.y;
347 }
348 };
349
350
351 // -------------------------- -------------------------- //
352
353 return Packer;
354
355 }));
356
357 /**
358 * Packery Item Element
359 **/
360
361 ( function( window, factory ) {
362 // universal module definition
363 /* jshint strict: false */ /* globals define, module, require */
364 if ( typeof define == 'function' && define.amd ) {
365 // AMD
366 define( 'packery/js/item',[
367 'outlayer/outlayer',
368 './rect'
369 ],
370 factory );
371 } else if ( typeof module == 'object' && module.exports ) {
372 // CommonJS
373 module.exports = factory(
374 require('outlayer'),
375 require('./rect')
376 );
377 } else {
378 // browser global
379 window.Packery.Item = factory(
380 window.Outlayer,
381 window.Packery.Rect
382 );
383 }
384
385 }( window, function factory( Outlayer, Rect ) {
386
387
388 // -------------------------- Item -------------------------- //
389
390 var docElemStyle = document.documentElement.style;
391
392 var transformProperty = typeof docElemStyle.transform == 'string' ?
393 'transform' : 'WebkitTransform';
394
395 // sub-class Item
396 var Item = function PackeryItem() {
397 Outlayer.Item.apply( this, arguments );
398 };
399
400 var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
401
402 var __create = proto._create;
403 proto._create = function() {
404 // call default _create logic
405 __create.call( this );
406 this.rect = new Rect();
407 };
408
409 var _moveTo = proto.moveTo;
410 proto.moveTo = function( x, y ) {
411 // don't shift 1px while dragging
412 var dx = Math.abs( this.position.x - x );
413 var dy = Math.abs( this.position.y - y );
414
415 var canHackGoTo = this.layout.dragItemCount && !this.isPlacing &&
416 !this.isTransitioning && dx < 1 && dy < 1;
417 if ( canHackGoTo ) {
418 this.goTo( x, y );
419 return;
420 }
421 _moveTo.apply( this, arguments );
422 };
423
424 // -------------------------- placing -------------------------- //
425
426 proto.enablePlacing = function() {
427 this.removeTransitionStyles();
428 // remove transform property from transition
429 if ( this.isTransitioning && transformProperty ) {
430 this.element.style[ transformProperty ] = 'none';
431 }
432 this.isTransitioning = false;
433 this.getSize();
434 this.layout._setRectSize( this.element, this.rect );
435 this.isPlacing = true;
436 };
437
438 proto.disablePlacing = function() {
439 this.isPlacing = false;
440 };
441
442 // ----- ----- //
443
444 // remove element from DOM
445 proto.removeElem = function() {
446 this.element.parentNode.removeChild( this.element );
447 // add space back to packer
448 this.layout.packer.addSpace( this.rect );
449 this.emitEvent( 'remove', [ this ] );
450 };
451
452 // ----- dropPlaceholder ----- //
453
454 proto.showDropPlaceholder = function() {
455 var dropPlaceholder = this.dropPlaceholder;
456 if ( !dropPlaceholder ) {
457 // create dropPlaceholder
458 dropPlaceholder = this.dropPlaceholder = document.createElement('div');
459 dropPlaceholder.className = 'packery-drop-placeholder';
460 dropPlaceholder.style.position = 'absolute';
461 }
462
463 dropPlaceholder.style.width = this.size.width + 'px';
464 dropPlaceholder.style.height = this.size.height + 'px';
465 this.positionDropPlaceholder();
466 this.layout.element.appendChild( dropPlaceholder );
467 };
468
469 proto.positionDropPlaceholder = function() {
470 this.dropPlaceholder.style[ transformProperty ] = 'translate(' +
471 this.rect.x + 'px, ' + this.rect.y + 'px)';
472 };
473
474 proto.hideDropPlaceholder = function() {
475 this.layout.element.removeChild( this.dropPlaceholder );
476 };
477
478 // ----- ----- //
479
480 return Item;
481
482 }));
483
484 /*!
485 * Packery v2.0.0
486 * Gapless, draggable grid layouts
487 *
488 * Licensed GPLv3 for open source use
489 * or Packery Commercial License for commercial use
490 *
491 * http://packery.metafizzy.co
492 * Copyright 2016 Metafizzy
493 */
494
495 ( function( window, factory ) {
496 // universal module definition
497 /* jshint strict: false */ /* globals define, module, require */
498 if ( typeof define == 'function' && define.amd ) {
499 // AMD
500 define( 'packery/js/packery',[
501 'get-size/get-size',
502 'outlayer/outlayer',
503 './rect',
504 './packer',
505 './item'
506 ],
507 factory );
508 } else if ( typeof module == 'object' && module.exports ) {
509 // CommonJS
510 module.exports = factory(
511 require('get-size'),
512 require('outlayer'),
513 require('./rect'),
514 require('./packer'),
515 require('./item')
516 );
517 } else {
518 // browser global
519 window.Packery = factory(
520 window.getSize,
521 window.Outlayer,
522 window.Packery.Rect,
523 window.Packery.Packer,
524 window.Packery.Item
525 );
526 }
527
528 }( window, function factory( getSize, Outlayer, Rect, Packer, Item ) {
529
530
531 // ----- Rect ----- //
532
533 // allow for pixel rounding errors IE8-IE11 & Firefox; #227
534 Rect.prototype.canFit = function( rect ) {
535 return this.width >= rect.width - 1 && this.height >= rect.height - 1;
536 };
537
538 // -------------------------- Packery -------------------------- //
539
540 // create an Outlayer layout class
541 var Packery = Outlayer.create('packery');
542 Packery.Item = Item;
543
544 var proto = Packery.prototype;
545
546 proto._create = function() {
547 // call super
548 Outlayer.prototype._create.call( this );
549
550 // initial properties
551 this.packer = new Packer();
552 // packer for drop targets
553 this.shiftPacker = new Packer();
554 this.isEnabled = true;
555
556 this.dragItemCount = 0;
557
558 // create drag handlers
559 var _this = this;
560 this.handleDraggabilly = {
561 dragStart: function() {
562 _this.itemDragStart( this.element );
563 },
564 dragMove: function() {
565 _this.itemDragMove( this.element, this.position.x, this.position.y );
566 },
567 dragEnd: function() {
568 _this.itemDragEnd( this.element );
569 }
570 };
571
572 this.handleUIDraggable = {
573 start: function handleUIDraggableStart( event, ui ) {
574 // HTML5 may trigger dragstart, dismiss HTML5 dragging
575 if ( !ui ) {
576 return;
577 }
578 _this.itemDragStart( event.currentTarget );
579 },
580 drag: function handleUIDraggableDrag( event, ui ) {
581 if ( !ui ) {
582 return;
583 }
584 _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
585 },
586 stop: function handleUIDraggableStop( event, ui ) {
587 if ( !ui ) {
588 return;
589 }
590 _this.itemDragEnd( event.currentTarget );
591 }
592 };
593
594 };
595
596
597 // ----- init & layout ----- //
598
599 /**
600 * logic before any new layout
601 */
602 proto._resetLayout = function() {
603 this.getSize();
604
605 this._getMeasurements();
606
607 // reset packer
608 var width, height, sortDirection;
609 // packer settings, if horizontal or vertical
610 if ( this._getOption('horizontal') ) {
611 width = Infinity;
612 height = this.size.innerHeight + this.gutter;
613 sortDirection = 'rightwardTopToBottom';
614 } else {
615 width = this.size.innerWidth + this.gutter;
616 height = Infinity;
617 sortDirection = 'downwardLeftToRight';
618 }
619
620 this.packer.width = this.shiftPacker.width = width;
621 this.packer.height = this.shiftPacker.height = height;
622 this.packer.sortDirection = this.shiftPacker.sortDirection = sortDirection;
623
624 this.packer.reset();
625
626 // layout
627 this.maxY = 0;
628 this.maxX = 0;
629 };
630
631 /**
632 * update columnWidth, rowHeight, & gutter
633 * @private
634 */
635 proto._getMeasurements = function() {
636 this._getMeasurement( 'columnWidth', 'width' );
637 this._getMeasurement( 'rowHeight', 'height' );
638 this._getMeasurement( 'gutter', 'width' );
639 };
640
641 proto._getItemLayoutPosition = function( item ) {
642 this._setRectSize( item.element, item.rect );
643 if ( this.isShifting || this.dragItemCount > 0 ) {
644 var packMethod = this._getPackMethod();
645 this.packer[ packMethod ]( item.rect );
646 } else {
647 this.packer.pack( item.rect );
648 }
649
650 this._setMaxXY( item.rect );
651 return item.rect;
652 };
653
654 proto.shiftLayout = function() {
655 this.isShifting = true;
656 this.layout();
657 delete this.isShifting;
658 };
659
660 proto._getPackMethod = function() {
661 return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
662 };
663
664
665 /**
666 * set max X and Y value, for size of container
667 * @param {Packery.Rect} rect
668 * @private
669 */
670 proto._setMaxXY = function( rect ) {
671 this.maxX = Math.max( rect.x + rect.width, this.maxX );
672 this.maxY = Math.max( rect.y + rect.height, this.maxY );
673 };
674
675 /**
676 * set the width and height of a rect, applying columnWidth and rowHeight
677 * @param {Element} elem
678 * @param {Packery.Rect} rect
679 */
680 proto._setRectSize = function( elem, rect ) {
681 var size = getSize( elem );
682 var w = size.outerWidth;
683 var h = size.outerHeight;
684 // size for columnWidth and rowHeight, if available
685 // only check if size is non-zero, #177
686 if ( w || h ) {
687 w = this._applyGridGutter( w, this.columnWidth );
688 h = this._applyGridGutter( h, this.rowHeight );
689 }
690 // rect must fit in packer
691 rect.width = Math.min( w, this.packer.width );
692 rect.height = Math.min( h, this.packer.height );
693 };
694
695 /**
696 * fits item to columnWidth/rowHeight and adds gutter
697 * @param {Number} measurement - item width or height
698 * @param {Number} gridSize - columnWidth or rowHeight
699 * @returns measurement
700 */
701 proto._applyGridGutter = function( measurement, gridSize ) {
702 // just add gutter if no gridSize
703 if ( !gridSize ) {
704 return measurement + this.gutter;
705 }
706 gridSize += this.gutter;
707 // fit item to columnWidth/rowHeight
708 var remainder = measurement % gridSize;
709 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
710 measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
711 return measurement;
712 };
713
714 proto._getContainerSize = function() {
715 if ( this._getOption('horizontal') ) {
716 return {
717 width: this.maxX - this.gutter
718 };
719 } else {
720 return {
721 height: this.maxY - this.gutter
722 };
723 }
724 };
725
726
727 // -------------------------- stamp -------------------------- //
728
729 /**
730 * makes space for element
731 * @param {Element} elem
732 */
733 proto._manageStamp = function( elem ) {
734
735 var item = this.getItem( elem );
736 var rect;
737 if ( item && item.isPlacing ) {
738 rect = item.rect;
739 } else {
740 var offset = this._getElementOffset( elem );
741 rect = new Rect({
742 x: this._getOption('originLeft') ? offset.left : offset.right,
743 y: this._getOption('originTop') ? offset.top : offset.bottom
744 });
745 }
746
747 this._setRectSize( elem, rect );
748 // save its space in the packer
749 this.packer.placed( rect );
750 this._setMaxXY( rect );
751 };
752
753 // -------------------------- methods -------------------------- //
754
755 function verticalSorter( a, b ) {
756 return a.position.y - b.position.y || a.position.x - b.position.x;
757 }
758
759 function horizontalSorter( a, b ) {
760 return a.position.x - b.position.x || a.position.y - b.position.y;
761 }
762
763 proto.sortItemsByPosition = function() {
764 var sorter = this._getOption('horizontal') ? horizontalSorter : verticalSorter;
765 this.items.sort( sorter );
766 };
767
768 /**
769 * Fit item element in its current position
770 * Packery will position elements around it
771 * useful for expanding elements
772 *
773 * @param {Element} elem
774 * @param {Number} x - horizontal destination position, optional
775 * @param {Number} y - vertical destination position, optional
776 */
777 proto.fit = function( elem, x, y ) {
778 var item = this.getItem( elem );
779 if ( !item ) {
780 return;
781 }
782
783 // stamp item to get it out of layout
784 this.stamp( item.element );
785 // set placing flag
786 item.enablePlacing();
787 this.updateShiftTargets( item );
788 // fall back to current position for fitting
789 x = x === undefined ? item.rect.x: x;
790 y = y === undefined ? item.rect.y: y;
791 // position it best at its destination
792 this.shift( item, x, y );
793 this._bindFitEvents( item );
794 item.moveTo( item.rect.x, item.rect.y );
795 // layout everything else
796 this.shiftLayout();
797 // return back to regularly scheduled programming
798 this.unstamp( item.element );
799 this.sortItemsByPosition();
800 item.disablePlacing();
801 };
802
803 /**
804 * emit event when item is fit and other items are laid out
805 * @param {Packery.Item} item
806 * @private
807 */
808 proto._bindFitEvents = function( item ) {
809 var _this = this;
810 var ticks = 0;
811 function onLayout() {
812 ticks++;
813 if ( ticks != 2 ) {
814 return;
815 }
816 _this.dispatchEvent( 'fitComplete', null, [ item ] );
817 }
818 // when item is laid out
819 item.once( 'layout', onLayout );
820 // when all items are laid out
821 this.once( 'layoutComplete', onLayout );
822 };
823
824 // -------------------------- resize -------------------------- //
825
826 // debounced, layout on resize
827 proto.resize = function() {
828 // don't trigger if size did not change
829 // or if resize was unbound. See #285, outlayer#9
830 if ( !this.isResizeBound || !this.needsResizeLayout() ) {
831 return;
832 }
833
834 if ( this.options.shiftPercentResize ) {
835 this.resizeShiftPercentLayout();
836 } else {
837 this.layout();
838 }
839 };
840
841 /**
842 * check if layout is needed post layout
843 * @returns Boolean
844 */
845 proto.needsResizeLayout = function() {
846 var size = getSize( this.element );
847 var innerSize = this._getOption('horizontal') ? 'innerHeight' : 'innerWidth';
848 return size[ innerSize ] != this.size[ innerSize ];
849 };
850
851 proto.resizeShiftPercentLayout = function() {
852 var items = this._getItemsForLayout( this.items );
853
854 var isHorizontal = this._getOption('horizontal');
855 var coord = isHorizontal ? 'y' : 'x';
856 var measure = isHorizontal ? 'height' : 'width';
857 var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
858 var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';
859
860 // proportional re-align items
861 var previousSegment = this[ segmentName ];
862 previousSegment = previousSegment && previousSegment + this.gutter;
863
864 if ( previousSegment ) {
865 this._getMeasurements();
866 var currentSegment = this[ segmentName ] + this.gutter;
867 items.forEach( function( item ) {
868 var seg = Math.round( item.rect[ coord ] / previousSegment );
869 item.rect[ coord ] = seg * currentSegment;
870 });
871 } else {
872 var currentSize = getSize( this.element )[ innerSize ] + this.gutter;
873 var previousSize = this.packer[ measure ];
874 items.forEach( function( item ) {
875 item.rect[ coord ] = ( item.rect[ coord ] / previousSize ) * currentSize;
876 });
877 }
878
879 this.shiftLayout();
880 };
881
882 // -------------------------- drag -------------------------- //
883
884 /**
885 * handle an item drag start event
886 * @param {Element} elem
887 */
888 proto.itemDragStart = function( elem ) {
889 if ( !this.isEnabled ) {
890 return;
891 }
892 this.stamp( elem );
893 // this.ignore( elem );
894 var item = this.getItem( elem );
895 if ( !item ) {
896 return;
897 }
898
899 item.enablePlacing();
900 item.showDropPlaceholder();
901 this.dragItemCount++;
902 this.updateShiftTargets( item );
903 };
904
905 proto.updateShiftTargets = function( dropItem ) {
906 this.shiftPacker.reset();
907
908 // pack stamps
909 this._getBoundingRect();
910 var isOriginLeft = this._getOption('originLeft');
911 var isOriginTop = this._getOption('originTop');
912 this.stamps.forEach( function( stamp ) {
913 // ignore dragged item
914 var item = this.getItem( stamp );
915 if ( item && item.isPlacing ) {
916 return;
917 }
918 var offset = this._getElementOffset( stamp );
919 var rect = new Rect({
920 x: isOriginLeft ? offset.left : offset.right,
921 y: isOriginTop ? offset.top : offset.bottom
922 });
923 this._setRectSize( stamp, rect );
924 // save its space in the packer
925 this.shiftPacker.placed( rect );
926 }, this );
927
928 // reset shiftTargets
929 var isHorizontal = this._getOption('horizontal');
930 var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
931 var measure = isHorizontal ? 'height' : 'width';
932
933 this.shiftTargetKeys = [];
934 this.shiftTargets = [];
935 var boundsSize;
936 var segment = this[ segmentName ];
937 segment = segment && segment + this.gutter;
938
939 if ( segment ) {
940 var segmentSpan = Math.ceil( dropItem.rect[ measure ] / segment );
941 var segs = Math.floor( ( this.shiftPacker[ measure ] + this.gutter ) / segment );
942 boundsSize = ( segs - segmentSpan ) * segment;
943 // add targets on top
944 for ( var i=0; i < segs; i++ ) {
945 this._addShiftTarget( i * segment, 0, boundsSize );
946 }
947 } else {
948 boundsSize = ( this.shiftPacker[ measure ] + this.gutter ) - dropItem.rect[ measure ];
949 this._addShiftTarget( 0, 0, boundsSize );
950 }
951
952 // pack each item to measure where shiftTargets are
953 var items = this._getItemsForLayout( this.items );
954 var packMethod = this._getPackMethod();
955 items.forEach( function( item ) {
956 var rect = item.rect;
957 this._setRectSize( item.element, rect );
958 this.shiftPacker[ packMethod ]( rect );
959
960 // add top left corner
961 this._addShiftTarget( rect.x, rect.y, boundsSize );
962 // add bottom left / top right corner
963 var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
964 var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
965 this._addShiftTarget( cornerX, cornerY, boundsSize );
966
967 if ( segment ) {
968 // add targets for each column on bottom / row on right
969 var segSpan = Math.round( rect[ measure ] / segment );
970 for ( var i=1; i < segSpan; i++ ) {
971 var segX = isHorizontal ? cornerX : rect.x + segment * i;
972 var segY = isHorizontal ? rect.y + segment * i : cornerY;
973 this._addShiftTarget( segX, segY, boundsSize );
974 }
975 }
976 }, this );
977
978 };
979
980 proto._addShiftTarget = function( x, y, boundsSize ) {
981 var checkCoord = this._getOption('horizontal') ? y : x;
982 if ( checkCoord !== 0 && checkCoord > boundsSize ) {
983 return;
984 }
985 // create string for a key, easier to keep track of what targets
986 var key = x + ',' + y;
987 var hasKey = this.shiftTargetKeys.indexOf( key ) != -1;
988 if ( hasKey ) {
989 return;
990 }
991 this.shiftTargetKeys.push( key );
992 this.shiftTargets.push({ x: x, y: y });
993 };
994
995 // -------------------------- drop -------------------------- //
996
997 proto.shift = function( item, x, y ) {
998 var shiftPosition;
999 var minDistance = Infinity;
1000 var position = { x: x, y: y };
1001 this.shiftTargets.forEach( function( target ) {
1002 var distance = getDistance( target, position );
1003 if ( distance < minDistance ) {
1004 shiftPosition = target;
1005 minDistance = distance;
1006 }
1007 });
1008 item.rect.x = shiftPosition.x;
1009 item.rect.y = shiftPosition.y;
1010 };
1011
1012 function getDistance( a, b ) {
1013 var dx = b.x - a.x;
1014 var dy = b.y - a.y;
1015 return Math.sqrt( dx * dx + dy * dy );
1016 }
1017
1018 // -------------------------- drag move -------------------------- //
1019
1020 var DRAG_THROTTLE_TIME = 120;
1021
1022 /**
1023 * handle an item drag move event
1024 * @param {Element} elem
1025 * @param {Number} x - horizontal change in position
1026 * @param {Number} y - vertical change in position
1027 */
1028 proto.itemDragMove = function( elem, x, y ) {
1029 var item = this.isEnabled && this.getItem( elem );
1030 if ( !item ) {
1031 return;
1032 }
1033
1034 x -= this.size.paddingLeft;
1035 y -= this.size.paddingTop;
1036
1037 var _this = this;
1038 function onDrag() {
1039 _this.shift( item, x, y );
1040 item.positionDropPlaceholder();
1041 _this.layout();
1042 }
1043
1044 // throttle
1045 var now = new Date();
1046 if ( this._itemDragTime && now - this._itemDragTime < DRAG_THROTTLE_TIME ) {
1047 clearTimeout( this.dragTimeout );
1048 this.dragTimeout = setTimeout( onDrag, DRAG_THROTTLE_TIME );
1049 } else {
1050 onDrag();
1051 this._itemDragTime = now;
1052 }
1053 };
1054
1055 // -------------------------- drag end -------------------------- //
1056
1057 /**
1058 * handle an item drag end event
1059 * @param {Element} elem
1060 */
1061 proto.itemDragEnd = function( elem ) {
1062 var item = this.isEnabled && this.getItem( elem );
1063 if ( !item ) {
1064 return;
1065 }
1066
1067 clearTimeout( this.dragTimeout );
1068 item.element.classList.add('is-positioning-post-drag');
1069
1070 var completeCount = 0;
1071 var _this = this;
1072 function onDragEndLayoutComplete() {
1073 completeCount++;
1074 if ( completeCount != 2 ) {
1075 return;
1076 }
1077 // reset drag item
1078 item.element.classList.remove('is-positioning-post-drag');
1079 item.hideDropPlaceholder();
1080 _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
1081 }
1082
1083 item.once( 'layout', onDragEndLayoutComplete );
1084 this.once( 'layoutComplete', onDragEndLayoutComplete );
1085 item.moveTo( item.rect.x, item.rect.y );
1086 this.layout();
1087 this.dragItemCount = Math.max( 0, this.dragItemCount - 1 );
1088 this.sortItemsByPosition();
1089 item.disablePlacing();
1090 this.unstamp( item.element );
1091 };
1092
1093 /**
1094 * binds Draggabilly events
1095 * @param {Draggabilly} draggie
1096 */
1097 proto.bindDraggabillyEvents = function( draggie ) {
1098 this._bindDraggabillyEvents( draggie, 'on' );
1099 };
1100
1101 proto.unbindDraggabillyEvents = function( draggie ) {
1102 this._bindDraggabillyEvents( draggie, 'off' );
1103 };
1104
1105 proto._bindDraggabillyEvents = function( draggie, method ) {
1106 var handlers = this.handleDraggabilly;
1107 draggie[ method ]( 'dragStart', handlers.dragStart );
1108 draggie[ method ]( 'dragMove', handlers.dragMove );
1109 draggie[ method ]( 'dragEnd', handlers.dragEnd );
1110 };
1111
1112 /**
1113 * binds jQuery UI Draggable events
1114 * @param {jQuery} $elems
1115 */
1116 proto.bindUIDraggableEvents = function( $elems ) {
1117 this._bindUIDraggableEvents( $elems, 'on' );
1118 };
1119
1120 proto.unbindUIDraggableEvents = function( $elems ) {
1121 this._bindUIDraggableEvents( $elems, 'off' );
1122 };
1123
1124 proto._bindUIDraggableEvents = function( $elems, method ) {
1125 var handlers = this.handleUIDraggable;
1126 $elems
1127 [ method ]( 'dragstart', handlers.start )
1128 [ method ]( 'drag', handlers.drag )
1129 [ method ]( 'dragstop', handlers.stop );
1130 };
1131
1132 // ----- destroy ----- //
1133
1134 var _destroy = proto.destroy;
1135 proto.destroy = function() {
1136 _destroy.apply( this, arguments );
1137 // disable flag; prevent drag events from triggering. #72
1138 this.isEnabled = false;
1139 };
1140
1141 // ----- ----- //
1142
1143 Packery.Rect = Rect;
1144 Packery.Packer = Packer;
1145
1146 return Packery;
1147
1148 }));
1149
1150 /*!
1151 * Packery layout mode v2.0.1
1152 * sub-classes Packery
1153 */
1154
1155 /*jshint browser: true, strict: true, undef: true, unused: true */
1156
1157 ( function( window, factory ) {
1158
1159 // universal module definition
1160 if ( typeof define == 'function' && define.amd ) {
1161 // AMD
1162 define( [
1163 'isotope-layout/js/layout-mode',
1164 'packery/js/packery'
1165 ],
1166 factory );
1167 } else if ( typeof module == 'object' && module.exports ) {
1168 // CommonJS
1169 module.exports = factory(
1170 require('isotope-layout/js/layout-mode'),
1171 require('packery')
1172 );
1173 } else {
1174 // browser global
1175 factory(
1176 window.Isotope.LayoutMode,
1177 window.Packery
1178 );
1179 }
1180
1181 }( window, function factor( LayoutMode, Packery ) {
1182
1183
1184 // create an Outlayer layout class
1185 var PackeryMode = LayoutMode.create('packery');
1186 var proto = PackeryMode.prototype;
1187
1188 var keepModeMethods = {
1189 _getElementOffset: true,
1190 _getMeasurement: true
1191 };
1192
1193 // inherit Packery prototype
1194 for ( var method in Packery.prototype ) {
1195 // do not inherit mode methods
1196 if ( !keepModeMethods[ method ] ) {
1197 proto[ method ] = Packery.prototype[ method ];
1198 }
1199 }
1200
1201 // set packer in _resetLayout
1202 var _resetLayout = proto._resetLayout;
1203 proto._resetLayout = function() {
1204 this.packer = this.packer || new Packery.Packer();
1205 this.shiftPacker = this.shiftPacker || new Packery.Packer();
1206 _resetLayout.apply( this, arguments );
1207 };
1208
1209 var _getItemLayoutPosition = proto._getItemLayoutPosition;
1210 proto._getItemLayoutPosition = function( item ) {
1211 // set packery rect
1212 item.rect = item.rect || new Packery.Rect();
1213 return _getItemLayoutPosition.call( this, item );
1214 };
1215
1216 // needsResizeLayout for vertical or horizontal
1217 var _needsResizeLayout = proto.needsResizeLayout;
1218 proto.needsResizeLayout = function() {
1219 if ( this._getOption('horizontal') ) {
1220 return this.needsVerticalResizeLayout();
1221 } else {
1222 return _needsResizeLayout.call( this );
1223 }
1224 };
1225
1226 // point to mode options for horizontal
1227 var _getOption = proto._getOption;
1228 proto._getOption = function( option ) {
1229 if ( option == 'horizontal' ) {
1230 return this.options.isHorizontal !== undefined ?
1231 this.options.isHorizontal : this.options.horizontal;
1232 }
1233 return _getOption.apply( this.isotope, arguments );
1234 };
1235
1236 return PackeryMode;
1237
1238 }));
1239
1240