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 space
217s 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}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.