├── .github
├── contributing.md
└── issue_template.md
├── .gitignore
├── .jshintrc
├── README.md
├── bower.json
├── dist
├── packery.pkgd.js
└── packery.pkgd.min.js
├── gulpfile.js
├── js
├── item.js
├── packer.js
├── packery.js
└── rect.js
├── package.json
├── sandbox
├── add-remove.html
├── basics.html
├── browserify
│ ├── browserify.html
│ ├── jquery-main.js
│ └── main.js
├── css
│ ├── basics.css
│ └── examples.css
├── draggabilly.html
├── examples.js
├── fit.html
├── fluid.html
├── horizontal.html
├── jquery-ui-draggable.html
├── jquery.html
├── merge-rects.html
├── origin.html
├── requirejs
│ ├── index.html
│ └── main.js
├── save-positions.html
├── shift-drag-uneven.html
├── shift-drag.html
└── single.html
└── test
├── .jshintrc
├── helpers.js
├── index.html
├── test.css
└── unit
├── add-items.js
├── basics.js
├── consecutive.js
├── container-size.js
├── declarative.js
├── defaults-empty.js
├── draggable.js
├── fit.js
├── get-items.js
├── grid.js
├── hidden-items.js
├── is-init-layout.js
├── jquery-plugin.js
├── layout-extra-big.js
├── layout.js
├── prepend.js
├── remove.js
├── stamped.js
├── sub-pixel-fit.js
├── test-packer.js
└── test-rect.js
/.github/contributing.md:
--------------------------------------------------------------------------------
1 | ## Submitting issues
2 |
3 | ### Reduced test case required
4 |
5 | All bug reports and problem issues require a [**reduced test case**](http://css-tricks.com/reduced-test-cases/). Create one by forking any one of the [CodePen examples](http://codepen.io/desandro/pens/tags/?grid_type=list&selected_tag=packery-docs) from [the docs](http://packery.metafizzy.co).
6 |
7 | **CodePens**
8 |
9 | + [Basic layout](http://codepen.io/desandro/pen/QyrEgX)
10 | + [Fluid layout](http://codepen.io/desandro/pen/xZjOXq)
11 | + [Images](http://codepen.io/desandro/pen/pgVbLv)
12 | + [Draggable](http://codepen.io/desandro/pen/aGvIq)
13 |
14 | **Test cases**
15 |
16 | + A reduced test case clearly demonstrates the bug or issue.
17 | + It contains the bare minimum HTML, CSS, and JavaScript required to demonstrate the bug.
18 | + A link to your production site is **not** a reduced test case.
19 |
20 | Providing a reduced test case is the best way to get your issue addressed. Without a reduced test case, your issue may be closed.
21 |
22 | ## Pull requests
23 |
24 | Contributions are welcome! Please note: your code may be used as part of a commercial product if merged. Be clear about what license applies to your patch. [The MIT license](http://choosealicense.com/licenses/mit/) or [public domain unlicense](http://choosealicense.com/licenses/unlicense/) are permissive, and allow integration of your patch into Packery as part of a commercial product.
25 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Test case:** http://codepen.io/desandro/pen/QyrEgX
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "browser": true,
3 | "devel": false,
4 | "strict": true,
5 | "undef": true,
6 | "unused": true,
7 | "globals": {
8 | "Packery": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Packery
2 |
3 | _Bin-packing layout library_
4 |
5 | See [packery.metafizzy.co](https://packery.metafizzy.co) for complete docs and demos
6 |
7 | ## Install
8 |
9 | ### Download
10 |
11 | + [packery.pkgd.js](https://unpkg.com/packery@3/dist/packery.pkgd.js) un-minified, or
12 | + [packery.pkgd.min.js](https://unpkg.com/packery@3/dist/packery.pkgd.min.js) minified
13 |
14 | ### CDN
15 |
16 | Link directly to Packery files on [unpkg](https://unpkg.com).
17 |
18 | ``` html
19 |
20 |
21 |
22 | ```
23 |
24 | ### Package managers
25 |
26 | [npm](https://www.npmjs.com/package/packery): `npm install packery --save`
27 |
28 | Bower: `bower install packery --save`
29 |
30 | ## License
31 |
32 | Packery v3 is licensed under the MIT license.
33 |
34 | ## Initialize
35 |
36 | With jQuery
37 |
38 | ``` js
39 | $('.grid').packery({
40 | // options...
41 | itemSelector: '.grid-item'
42 | });
43 | ```
44 |
45 | With vanilla JavaScript
46 |
47 | ``` js
48 | // vanilla JS
49 | var grid = document.querySelector('.grid');
50 | // initialize with element
51 | var pckry = new Packery( grid, {
52 | // options...
53 | itemSelector: '.grid-item'
54 | });
55 |
56 | // initialize with selector string
57 | var pckry = new Packery('.grid', {
58 | // options...
59 | });
60 | ```
61 |
62 | With HTML
63 |
64 | Add a `data-packery` attribute to your element. Options can be set in JSON in the value.
65 |
66 | ``` html
67 |
72 | ```
73 |
74 | ---
75 |
76 | By [Metafizzy](http://metafizzy.co)
77 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "packery",
3 | "author": "David DeSandro",
4 | "description": "Gapless, draggable grid layouts",
5 | "main": "js/packery.js",
6 | "dependencies": {
7 | "get-size": "^2.0.2",
8 | "outlayer": "^2.0.0"
9 | },
10 | "devDependencies": {
11 | "draggabilly": "^2.1.0",
12 | "jquery": ">=2 <4",
13 | "jquery-ui-draggable": "https://gist.github.com/4985610.git",
14 | "jquery-bridget": "~2.0.0",
15 | "qunit": "^1.15"
16 | },
17 | "ignore": [
18 | "**/.*",
19 | "contributing.md",
20 | "package.json",
21 | "gulpfile.js",
22 | "sandbox/",
23 | "node_modules",
24 | "bower_components",
25 | "test",
26 | "tests"
27 | ],
28 | "homepage": "https://packery.metafizzy.co",
29 | "authors": [
30 | "David DeSandro "
31 | ],
32 | "moduleType": [
33 | "amd",
34 | "globals",
35 | "node"
36 | ],
37 | "keywords": [
38 | "layout",
39 | "grid",
40 | "draggable"
41 | ],
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/dist/packery.pkgd.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Packery v3.0.0
3 | * Gapless, draggable grid layouts
4 | * MIT License
5 | * https://packery.metafizzy.co
6 | * Copyright 2013-2025 Metafizzy
7 | */
8 |
9 | !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,s,a){function h(t,e,n){var o,s="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void r(i+" not initialized. Cannot call methods, i.e. "+s);var c=u[e];if(!c||"_"==e.charAt(0))return void r(s+" is not a valid method");var d=c.apply(u,n);o=void 0===o?d:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new s(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(s.prototype.option||(s.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,s=t.console,r="undefined"==typeof s?function(){}:function(t){s.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("get-size/get-size",e):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict";function t(t){var e=parseFloat(t),i=-1==t.indexOf("%")&&!isNaN(e);return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;u>e;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See https://bit.ly/getsizebug1"),e}function o(){if(!c){c=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);r=200==Math.round(t(o.width)),s.isBoxSizeOuter=r,i.removeChild(e)}}function s(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var s=n(e);if("none"==s.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var c=a.isBorderBox="border-box"==s.boxSizing,d=0;u>d;d++){var l=h[d],f=s[l],p=parseFloat(f);a[l]=isNaN(p)?0:p}var g=a.paddingLeft+a.paddingRight,m=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,x=a.borderTopWidth+a.borderBottomWidth,b=c&&r,E=t(s.width);E!==!1&&(a.width=E+(b?0:g+_));var w=t(s.height);return w!==!1&&(a.height=w+(b?0:m+x)),a.innerWidth=a.width-(g+_),a.innerHeight=a.height-(m+x),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var r,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,c=!1;return s}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var n=this._onceEvents&&this._onceEvents[t],o=0;o=t.x+e&&this.y+this.height>=t.y+i},e.overlaps=function(t){var e=this.x+this.width,i=this.y+this.height,n=t.x+t.width,o=t.y+t.height;return this.xt.x&&this.yt.y},e.getMaximalFreeRects=function(e){if(!this.overlaps(e))return!1;var i,n=[],o=this.x+this.width,s=this.y+this.height,r=e.x+e.width,a=e.y+e.height;return this.yr&&(i=new t({x:r,y:this.y,width:o-r,height:this.height}),n.push(i)),s>a&&(i=new t({x:this.x,y:a,width:this.width,height:s-a}),n.push(i)),this.x=t.width&&this.height>=t.height},t}),function(t,e){if("function"==typeof define&&define.amd)define("packery/js/packer",["./rect"],e);else if("object"==typeof module&&module.exports)module.exports=e(require("./rect"));else{var i=t.Packery=t.Packery||{};i.Packer=e(i.Rect)}}(window,function(t){"use strict";function e(t,e,i){this.width=t||0,this.height=e||0,this.sortDirection=i||"downwardLeftToRight",this.reset()}var i=e.prototype;i.reset=function(){this.spaces=[];var e=new t({x:0,y:0,width:this.width,height:this.height});this.spaces.push(e),this.sorter=n[this.sortDirection]||n.downwardLeftToRight},i.pack=function(t){for(var e=0;e=t.x+t.width&&i.height>=t.height-.01;if(n){t.y=i.y,this.placed(t);break}}},i.rowPack=function(t){for(var e=0;e=t.y+t.height&&i.width>=t.width-.01;if(n){t.x=i.x,this.placed(t);break}}},i.placeInSpace=function(t,e){t.x=e.x,t.y=e.y,this.placed(t)},i.placed=function(t){for(var e=[],i=0;ii&&1>n;return o?void this.goTo(t,e):void a.apply(this,arguments)},s.enablePlacing=function(){this.removeTransitionStyles(),this.isTransitioning&&n&&(this.element.style[n]="none"),this.isTransitioning=!1,this.getSize(),this.layout._setRectSize(this.element,this.rect),this.isPlacing=!0},s.disablePlacing=function(){this.isPlacing=!1},s.removeElem=function(){var t=this.element.parentNode;t&&t.removeChild(this.element),this.layout.packer.addSpace(this.rect),this.emitEvent("remove",[this])},s.showDropPlaceholder=function(){var t=this.dropPlaceholder;t||(t=this.dropPlaceholder=document.createElement("div"),t.className="packery-drop-placeholder",t.style.position="absolute"),t.style.width=this.size.width+"px",t.style.height=this.size.height+"px",this.positionDropPlaceholder(),this.layout.element.appendChild(t)},s.positionDropPlaceholder=function(){this.dropPlaceholder.style[n]="translate("+this.rect.x+"px, "+this.rect.y+"px)"},s.hideDropPlaceholder=function(){var t=this.dropPlaceholder.parentNode;t&&t.removeChild(this.dropPlaceholder)},o}),function(t,e){"function"==typeof define&&define.amd?define(["get-size/get-size","outlayer/outlayer","packery/js/rect","packery/js/packer","packery/js/item"],e):"object"==typeof module&&module.exports?module.exports=e(require("get-size"),require("outlayer"),require("./rect"),require("./packer"),require("./item")):t.Packery=e(t.getSize,t.Outlayer,t.Packery.Rect,t.Packery.Packer,t.Packery.Item)}(window,function(t,e,i,n,o){"use strict";function s(t,e){return t.position.y-e.position.y||t.position.x-e.position.x}function r(t,e){return t.position.x-e.position.x||t.position.y-e.position.y}function a(t,e){var i=e.x-t.x,n=e.y-t.y;return Math.sqrt(i*i+n*n)}i.prototype.canFit=function(t){return this.width>=t.width-1&&this.height>=t.height-1};var h=e.create("packery");h.Item=o;var u=h.prototype;u._create=function(){e.prototype._create.call(this),this.packer=new n,this.shiftPacker=new n,this.isEnabled=!0,this.dragItemCount=0;var t=this;this.handleDraggabilly={dragStart:function(){t.itemDragStart(this.element)},dragMove:function(){t.itemDragMove(this.element,this.position.x,this.position.y)},dragEnd:function(){t.itemDragEnd(this.element)}},this.handleUIDraggable={start:function(e,i){i&&t.itemDragStart(e.currentTarget)},drag:function(e,i){i&&t.itemDragMove(e.currentTarget,i.position.left,i.position.top)},stop:function(e,i){i&&t.itemDragEnd(e.currentTarget)}}},u._resetLayout=function(){this.getSize(),this._getMeasurements();var t,e,i;this._getOption("horizontal")?(t=1/0,e=this.size.innerHeight+this.gutter,i="rightwardTopToBottom"):(t=this.size.innerWidth+this.gutter,e=1/0,i="downwardLeftToRight"),this.packer.width=this.shiftPacker.width=t,this.packer.height=this.shiftPacker.height=e,this.packer.sortDirection=this.shiftPacker.sortDirection=i,this.packer.reset(),this.maxY=0,this.maxX=0},u._getMeasurements=function(){this._getMeasurement("columnWidth","width"),this._getMeasurement("rowHeight","height"),this._getMeasurement("gutter","width")},u._getItemLayoutPosition=function(t){if(this._setRectSize(t.element,t.rect),this.isShifting||this.dragItemCount>0){var e=this._getPackMethod();this.packer[e](t.rect)}else this.packer.pack(t.rect);return this._setMaxXY(t.rect),t.rect},u.shiftLayout=function(){this.isShifting=!0,this.layout(),delete this.isShifting},u._getPackMethod=function(){return this._getOption("horizontal")?"rowPack":"columnPack"},u._setMaxXY=function(t){this.maxX=Math.max(t.x+t.width,this.maxX),this.maxY=Math.max(t.y+t.height,this.maxY)},u._setRectSize=function(e,i){var n=t(e),o=n.outerWidth,s=n.outerHeight;(o||s)&&(o=this._applyGridGutter(o,this.columnWidth),s=this._applyGridGutter(s,this.rowHeight)),i.width=Math.min(o,this.packer.width),i.height=Math.min(s,this.packer.height)},u._applyGridGutter=function(t,e){if(!e)return t+this.gutter;e+=this.gutter;var i=t%e,n=i&&1>i?"round":"ceil";return t=Math[n](t/e)*e},u._getContainerSize=function(){return this._getOption("horizontal")?{width:this.maxX-this.gutter}:{height:this.maxY-this.gutter}},u._manageStamp=function(t){var e,n=this.getItem(t);if(n&&n.isPlacing)e=n.rect;else{var o=this._getElementOffset(t);e=new i({x:this._getOption("originLeft")?o.left:o.right,y:this._getOption("originTop")?o.top:o.bottom})}this._setRectSize(t,e),this.packer.placed(e),this._setMaxXY(e)},u.sortItemsByPosition=function(){var t=this._getOption("horizontal")?r:s;this.items.sort(t)},u.fit=function(t,e,i){var n=this.getItem(t);n&&(this.stamp(n.element),n.enablePlacing(),this.updateShiftTargets(n),e=void 0===e?n.rect.x:e,i=void 0===i?n.rect.y:i,this.shift(n,e,i),this._bindFitEvents(n),n.moveTo(n.rect.x,n.rect.y),this.shiftLayout(),this.unstamp(n.element),this.sortItemsByPosition(),n.disablePlacing())},u._bindFitEvents=function(t){function e(){n++,2==n&&i.dispatchEvent("fitComplete",null,[t])}var i=this,n=0;t.once("layout",e),this.once("layoutComplete",e)},u.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&(this.options.shiftPercentResize?this.resizeShiftPercentLayout():this.layout())},u.needsResizeLayout=function(){var e=t(this.element),i=this._getOption("horizontal")?"innerHeight":"innerWidth";return e[i]!=this.size[i]},u.resizeShiftPercentLayout=function(){var e=this._getItemsForLayout(this.items),i=this._getOption("horizontal"),n=i?"y":"x",o=i?"height":"width",s=i?"rowHeight":"columnWidth",r=i?"innerHeight":"innerWidth",a=this[s];if(a=a&&a+this.gutter){this._getMeasurements();var h=this[s]+this.gutter;e.forEach(function(t){var e=Math.round(t.rect[n]/a);t.rect[n]=e*h})}else{var u=t(this.element)[r]+this.gutter,c=this.packer[o];e.forEach(function(t){t.rect[n]=t.rect[n]/c*u})}this.shiftLayout()},u.itemDragStart=function(t){if(this.isEnabled){this.stamp(t);var e=this.getItem(t);e&&(e.enablePlacing(),e.showDropPlaceholder(),this.dragItemCount++,this.updateShiftTargets(e))}},u.updateShiftTargets=function(t){this.shiftPacker.reset(),this._getBoundingRect();var e=this._getOption("originLeft"),n=this._getOption("originTop");this.stamps.forEach(function(t){var o=this.getItem(t);if(!o||!o.isPlacing){var s=this._getElementOffset(t),r=new i({x:e?s.left:s.right,y:n?s.top:s.bottom});this._setRectSize(t,r),this.shiftPacker.placed(r)}},this);var o=this._getOption("horizontal"),s=o?"rowHeight":"columnWidth",r=o?"height":"width";this.shiftTargetKeys=[],this.shiftTargets=[];var a,h=this[s];if(h=h&&h+this.gutter){var u=Math.ceil(t.rect[r]/h),c=Math.floor((this.shiftPacker[r]+this.gutter)/h);a=(c-u)*h;for(var d=0;c>d;d++){var l=o?0:d*h,f=o?d*h:0;this._addShiftTarget(l,f,a)}}else a=this.shiftPacker[r]+this.gutter-t.rect[r],this._addShiftTarget(0,0,a);var p=this._getItemsForLayout(this.items),g=this._getPackMethod();p.forEach(function(t){var e=t.rect;this._setRectSize(t.element,e),this.shiftPacker[g](e),this._addShiftTarget(e.x,e.y,a);var i=o?e.x+e.width:e.x,n=o?e.y:e.y+e.height;if(this._addShiftTarget(i,n,a),h)for(var s=Math.round(e[r]/h),u=1;s>u;u++){var c=o?i:e.x+h*u,d=o?e.y+h*u:n;this._addShiftTarget(c,d,a)}},this)},u._addShiftTarget=function(t,e,i){var n=this._getOption("horizontal")?e:t;if(!(0!==n&&n>i)){var o=t+","+e,s=-1!=this.shiftTargetKeys.indexOf(o);s||(this.shiftTargetKeys.push(o),this.shiftTargets.push({x:t,y:e}))}},u.shift=function(t,e,i){var n,o=1/0,s={x:e,y:i};this.shiftTargets.forEach(function(t){
10 | var e=a(t,s);o>e&&(n=t,o=e)}),t.rect.x=n.x,t.rect.y=n.y};var c=120;u.itemDragMove=function(t,e,i){function n(){s.shift(o,e,i),o.positionDropPlaceholder(),s.layout()}var o=this.isEnabled&&this.getItem(t);if(o){e-=this.size.paddingLeft,i-=this.size.paddingTop;var s=this,r=new Date,a=this._itemDragTime&&r-this._itemDragTime 'packery/rect'
81 | .replace( /'.\//g, "'packery/js/" );
82 | }) )
83 | .pipe( replace( "define( 'packery/", "define( 'packery/js/" ) )
84 | // add banner
85 | .pipe( addBanner( banner ) )
86 | .pipe( rename('packery.pkgd.js') )
87 | .pipe( gulp.dest('dist') );
88 | });
89 |
90 |
91 | // ----- uglify ----- //
92 |
93 | var uglify = require('gulp-uglify');
94 |
95 | gulp.task( 'uglify', [ 'requirejs' ], function() {
96 | var banner = getBanner();
97 | gulp.src('dist/packery.pkgd.js')
98 | .pipe( uglify() )
99 | // add banner
100 | .pipe( addBanner( banner ) )
101 | .pipe( rename('packery.pkgd.min.js') )
102 | .pipe( gulp.dest('dist') );
103 | });
104 |
105 | // ----- version ----- //
106 |
107 | // set version in source files
108 |
109 | var minimist = require('minimist');
110 | var gutil = require('gulp-util');
111 | var chalk = require('chalk');
112 |
113 | // use gulp version -t 1.2.3
114 | gulp.task( 'version', function() {
115 | var args = minimist( process.argv.slice(3) );
116 | var version = args.t;
117 | if ( !version || !/^\d\.\d+\.\d+/.test( version ) ) {
118 | gutil.log( 'invalid version: ' + chalk.red( version ) );
119 | return;
120 | }
121 | gutil.log( 'ticking version to ' + chalk.green( version ) );
122 |
123 | gulp.src('js/packery.js')
124 | .pipe( replace( /Packery v\d\.\d+\.\d+/, 'Packery v' + version ) )
125 | .pipe( gulp.dest('js') );
126 |
127 | gulp.src( [ 'package.json' ] )
128 | .pipe( replace( /"version": "\d\.\d+\.\d+"/, '"version": "' + version + '"' ) )
129 | .pipe( gulp.dest('.') );
130 | // replace CDN links in README
131 | var minorVersion = version.match( /^\d\.\d+/ )[0];
132 | gulp.src('README.md')
133 | .pipe( replace( /packery@\d\.\d+/g, 'packery@' + minorVersion ))
134 | .pipe( gulp.dest('.') );
135 | });
136 |
137 | // ----- default ----- //
138 |
139 | gulp.task( 'default', [
140 | 'hint',
141 | 'uglify'
142 | ]);
143 |
--------------------------------------------------------------------------------
/js/item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Packery Item Element
3 | **/
4 |
5 | ( function( window, factory ) {
6 | // universal module definition
7 | /* jshint strict: false */ /* globals define, module, require */
8 | if ( typeof define == 'function' && define.amd ) {
9 | // AMD
10 | define( [
11 | 'outlayer/outlayer',
12 | './rect'
13 | ],
14 | factory );
15 | } else if ( typeof module == 'object' && module.exports ) {
16 | // CommonJS
17 | module.exports = factory(
18 | require('outlayer'),
19 | require('./rect')
20 | );
21 | } else {
22 | // browser global
23 | window.Packery.Item = factory(
24 | window.Outlayer,
25 | window.Packery.Rect
26 | );
27 | }
28 |
29 | }( window, function factory( Outlayer, Rect ) {
30 | 'use strict';
31 |
32 | // -------------------------- Item -------------------------- //
33 |
34 | var docElemStyle = document.documentElement.style;
35 |
36 | var transformProperty = typeof docElemStyle.transform == 'string' ?
37 | 'transform' : 'WebkitTransform';
38 |
39 | // sub-class Item
40 | var Item = function PackeryItem() {
41 | Outlayer.Item.apply( this, arguments );
42 | };
43 |
44 | var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
45 |
46 | var __create = proto._create;
47 | proto._create = function() {
48 | // call default _create logic
49 | __create.call( this );
50 | this.rect = new Rect();
51 | };
52 |
53 | var _moveTo = proto.moveTo;
54 | proto.moveTo = function( x, y ) {
55 | // don't shift 1px while dragging
56 | var dx = Math.abs( this.position.x - x );
57 | var dy = Math.abs( this.position.y - y );
58 |
59 | var canHackGoTo = this.layout.dragItemCount && !this.isPlacing &&
60 | !this.isTransitioning && dx < 1 && dy < 1;
61 | if ( canHackGoTo ) {
62 | this.goTo( x, y );
63 | return;
64 | }
65 | _moveTo.apply( this, arguments );
66 | };
67 |
68 | // -------------------------- placing -------------------------- //
69 |
70 | proto.enablePlacing = function() {
71 | this.removeTransitionStyles();
72 | // remove transform property from transition
73 | if ( this.isTransitioning && transformProperty ) {
74 | this.element.style[ transformProperty ] = 'none';
75 | }
76 | this.isTransitioning = false;
77 | this.getSize();
78 | this.layout._setRectSize( this.element, this.rect );
79 | this.isPlacing = true;
80 | };
81 |
82 | proto.disablePlacing = function() {
83 | this.isPlacing = false;
84 | };
85 |
86 | // ----- ----- //
87 |
88 | // remove element from DOM
89 | proto.removeElem = function() {
90 | var parent = this.element.parentNode;
91 | if ( parent ) {
92 | parent.removeChild( this.element );
93 | }
94 | // add space back to packer
95 | this.layout.packer.addSpace( this.rect );
96 | this.emitEvent( 'remove', [ this ] );
97 | };
98 |
99 | // ----- dropPlaceholder ----- //
100 |
101 | proto.showDropPlaceholder = function() {
102 | var dropPlaceholder = this.dropPlaceholder;
103 | if ( !dropPlaceholder ) {
104 | // create dropPlaceholder
105 | dropPlaceholder = this.dropPlaceholder = document.createElement('div');
106 | dropPlaceholder.className = 'packery-drop-placeholder';
107 | dropPlaceholder.style.position = 'absolute';
108 | }
109 |
110 | dropPlaceholder.style.width = this.size.width + 'px';
111 | dropPlaceholder.style.height = this.size.height + 'px';
112 | this.positionDropPlaceholder();
113 | this.layout.element.appendChild( dropPlaceholder );
114 | };
115 |
116 | proto.positionDropPlaceholder = function() {
117 | this.dropPlaceholder.style[ transformProperty ] = 'translate(' +
118 | this.rect.x + 'px, ' + this.rect.y + 'px)';
119 | };
120 |
121 | proto.hideDropPlaceholder = function() {
122 | // only remove once, #333
123 | var parent = this.dropPlaceholder.parentNode;
124 | if ( parent ) {
125 | parent.removeChild( this.dropPlaceholder );
126 | }
127 | };
128 |
129 | // ----- ----- //
130 |
131 | return Item;
132 |
133 | }));
134 |
--------------------------------------------------------------------------------
/js/packer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Packer
3 | * bin-packing algorithm
4 | */
5 |
6 | ( function( window, factory ) {
7 | // universal module definition
8 | /* jshint strict: false */ /* globals define, module, require */
9 | if ( typeof define == 'function' && define.amd ) {
10 | // AMD
11 | define( [ './rect' ], factory );
12 | } else if ( typeof module == 'object' && module.exports ) {
13 | // CommonJS
14 | module.exports = factory(
15 | require('./rect')
16 | );
17 | } else {
18 | // browser global
19 | var Packery = window.Packery = window.Packery || {};
20 | Packery.Packer = factory( Packery.Rect );
21 | }
22 |
23 | }( window, function factory( Rect ) {
24 | 'use strict';
25 |
26 | // -------------------------- Packer -------------------------- //
27 |
28 | /**
29 | * @param {Number} width
30 | * @param {Number} height
31 | * @param {String} sortDirection
32 | * topLeft for vertical, leftTop for horizontal
33 | */
34 | function Packer( width, height, sortDirection ) {
35 | this.width = width || 0;
36 | this.height = height || 0;
37 | this.sortDirection = sortDirection || 'downwardLeftToRight';
38 |
39 | this.reset();
40 | }
41 |
42 | var proto = Packer.prototype;
43 |
44 | proto.reset = function() {
45 | this.spaces = [];
46 |
47 | var initialSpace = new Rect({
48 | x: 0,
49 | y: 0,
50 | width: this.width,
51 | height: this.height
52 | });
53 |
54 | this.spaces.push( initialSpace );
55 | // set sorter
56 | this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
57 | };
58 |
59 | // change x and y of rect to fit with in Packer's available spaces
60 | proto.pack = function( rect ) {
61 | for ( var i=0; i < this.spaces.length; i++ ) {
62 | var space = this.spaces[i];
63 | if ( space.canFit( rect ) ) {
64 | this.placeInSpace( rect, space );
65 | break;
66 | }
67 | }
68 | };
69 |
70 | proto.columnPack = function( rect ) {
71 | for ( var i=0; i < this.spaces.length; i++ ) {
72 | var space = this.spaces[i];
73 | var canFitInSpaceColumn = space.x <= rect.x &&
74 | space.x + space.width >= rect.x + rect.width &&
75 | space.height >= rect.height - 0.01; // fudge number for rounding error
76 | if ( canFitInSpaceColumn ) {
77 | rect.y = space.y;
78 | this.placed( rect );
79 | break;
80 | }
81 | }
82 | };
83 |
84 | proto.rowPack = function( rect ) {
85 | for ( var i=0; i < this.spaces.length; i++ ) {
86 | var space = this.spaces[i];
87 | var canFitInSpaceRow = space.y <= rect.y &&
88 | space.y + space.height >= rect.y + rect.height &&
89 | space.width >= rect.width - 0.01; // fudge number for rounding error
90 | if ( canFitInSpaceRow ) {
91 | rect.x = space.x;
92 | this.placed( rect );
93 | break;
94 | }
95 | }
96 | };
97 |
98 | proto.placeInSpace = function( rect, space ) {
99 | // place rect in space
100 | rect.x = space.x;
101 | rect.y = space.y;
102 |
103 | this.placed( rect );
104 | };
105 |
106 | // update spaces with placed rect
107 | proto.placed = function( rect ) {
108 | // update spaces
109 | var revisedSpaces = [];
110 | for ( var i=0; i < this.spaces.length; i++ ) {
111 | var space = this.spaces[i];
112 | var newSpaces = space.getMaximalFreeRects( rect );
113 | // add either the original space or the new spaces to the revised spaces
114 | if ( newSpaces ) {
115 | revisedSpaces.push.apply( revisedSpaces, newSpaces );
116 | } else {
117 | revisedSpaces.push( space );
118 | }
119 | }
120 |
121 | this.spaces = revisedSpaces;
122 |
123 | this.mergeSortSpaces();
124 | };
125 |
126 | proto.mergeSortSpaces = function() {
127 | // remove redundant spaces
128 | Packer.mergeRects( this.spaces );
129 | this.spaces.sort( this.sorter );
130 | };
131 |
132 | // add a space back
133 | proto.addSpace = function( rect ) {
134 | this.spaces.push( rect );
135 | this.mergeSortSpaces();
136 | };
137 |
138 | // -------------------------- utility functions -------------------------- //
139 |
140 | /**
141 | * Remove redundant rectangle from array of rectangles
142 | * @param {Array} rects: an array of Rects
143 | * @returns {Array} rects: an array of Rects
144 | **/
145 | Packer.mergeRects = function( rects ) {
146 | var i = 0;
147 | var rect = rects[i];
148 |
149 | rectLoop:
150 | while ( rect ) {
151 | var j = 0;
152 | var compareRect = rects[ i + j ];
153 |
154 | while ( compareRect ) {
155 | if ( compareRect == rect ) {
156 | j++; // next
157 | } else if ( compareRect.contains( rect ) ) {
158 | // remove rect
159 | rects.splice( i, 1 );
160 | rect = rects[i]; // set next rect
161 | continue rectLoop; // bail on compareLoop
162 | } else if ( rect.contains( compareRect ) ) {
163 | // remove compareRect
164 | rects.splice( i + j, 1 );
165 | } else {
166 | j++;
167 | }
168 | compareRect = rects[ i + j ]; // set next compareRect
169 | }
170 | i++;
171 | rect = rects[i];
172 | }
173 |
174 | return rects;
175 | };
176 |
177 |
178 | // -------------------------- sorters -------------------------- //
179 |
180 | // functions for sorting rects in order
181 | var sorters = {
182 | // top down, then left to right
183 | downwardLeftToRight: function( a, b ) {
184 | return a.y - b.y || a.x - b.x;
185 | },
186 | // left to right, then top down
187 | rightwardTopToBottom: function( a, b ) {
188 | return a.x - b.x || a.y - b.y;
189 | }
190 | };
191 |
192 |
193 | // -------------------------- -------------------------- //
194 |
195 | return Packer;
196 |
197 | }));
198 |
--------------------------------------------------------------------------------
/js/packery.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Packery v3.0.0
3 | * Gapless, draggable grid layouts
4 | * MIT License
5 | * https://packery.metafizzy.co
6 | * Copyright 2013-2025 Metafizzy
7 | */
8 |
9 | ( function( window, factory ) {
10 | // universal module definition
11 | /* jshint strict: false */ /* globals define, module, require */
12 | if ( typeof define == 'function' && define.amd ) {
13 | // AMD
14 | define( [
15 | 'get-size/get-size',
16 | 'outlayer/outlayer',
17 | './rect',
18 | './packer',
19 | './item'
20 | ],
21 | factory );
22 | } else if ( typeof module == 'object' && module.exports ) {
23 | // CommonJS
24 | module.exports = factory(
25 | require('get-size'),
26 | require('outlayer'),
27 | require('./rect'),
28 | require('./packer'),
29 | require('./item')
30 | );
31 | } else {
32 | // browser global
33 | window.Packery = factory(
34 | window.getSize,
35 | window.Outlayer,
36 | window.Packery.Rect,
37 | window.Packery.Packer,
38 | window.Packery.Item
39 | );
40 | }
41 |
42 | }( window, function factory( getSize, Outlayer, Rect, Packer, Item ) {
43 | 'use strict';
44 |
45 | // ----- Rect ----- //
46 |
47 | // allow for pixel rounding errors IE8-IE11 & Firefox; #227
48 | Rect.prototype.canFit = function( rect ) {
49 | return this.width >= rect.width - 1 && this.height >= rect.height - 1;
50 | };
51 |
52 | // -------------------------- Packery -------------------------- //
53 |
54 | // create an Outlayer layout class
55 | var Packery = Outlayer.create('packery');
56 | Packery.Item = Item;
57 |
58 | var proto = Packery.prototype;
59 |
60 | proto._create = function() {
61 | // call super
62 | Outlayer.prototype._create.call( this );
63 |
64 | // initial properties
65 | this.packer = new Packer();
66 | // packer for drop targets
67 | this.shiftPacker = new Packer();
68 | this.isEnabled = true;
69 |
70 | this.dragItemCount = 0;
71 |
72 | // create drag handlers
73 | var _this = this;
74 | this.handleDraggabilly = {
75 | dragStart: function() {
76 | _this.itemDragStart( this.element );
77 | },
78 | dragMove: function() {
79 | _this.itemDragMove( this.element, this.position.x, this.position.y );
80 | },
81 | dragEnd: function() {
82 | _this.itemDragEnd( this.element );
83 | }
84 | };
85 |
86 | this.handleUIDraggable = {
87 | start: function handleUIDraggableStart( event, ui ) {
88 | // HTML5 may trigger dragstart, dismiss HTML5 dragging
89 | if ( !ui ) {
90 | return;
91 | }
92 | _this.itemDragStart( event.currentTarget );
93 | },
94 | drag: function handleUIDraggableDrag( event, ui ) {
95 | if ( !ui ) {
96 | return;
97 | }
98 | _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
99 | },
100 | stop: function handleUIDraggableStop( event, ui ) {
101 | if ( !ui ) {
102 | return;
103 | }
104 | _this.itemDragEnd( event.currentTarget );
105 | }
106 | };
107 |
108 | };
109 |
110 |
111 | // ----- init & layout ----- //
112 |
113 | /**
114 | * logic before any new layout
115 | */
116 | proto._resetLayout = function() {
117 | this.getSize();
118 |
119 | this._getMeasurements();
120 |
121 | // reset packer
122 | var width, height, sortDirection;
123 | // packer settings, if horizontal or vertical
124 | if ( this._getOption('horizontal') ) {
125 | width = Infinity;
126 | height = this.size.innerHeight + this.gutter;
127 | sortDirection = 'rightwardTopToBottom';
128 | } else {
129 | width = this.size.innerWidth + this.gutter;
130 | height = Infinity;
131 | sortDirection = 'downwardLeftToRight';
132 | }
133 |
134 | this.packer.width = this.shiftPacker.width = width;
135 | this.packer.height = this.shiftPacker.height = height;
136 | this.packer.sortDirection = this.shiftPacker.sortDirection = sortDirection;
137 |
138 | this.packer.reset();
139 |
140 | // layout
141 | this.maxY = 0;
142 | this.maxX = 0;
143 | };
144 |
145 | /**
146 | * update columnWidth, rowHeight, & gutter
147 | * @private
148 | */
149 | proto._getMeasurements = function() {
150 | this._getMeasurement( 'columnWidth', 'width' );
151 | this._getMeasurement( 'rowHeight', 'height' );
152 | this._getMeasurement( 'gutter', 'width' );
153 | };
154 |
155 | proto._getItemLayoutPosition = function( item ) {
156 | this._setRectSize( item.element, item.rect );
157 | if ( this.isShifting || this.dragItemCount > 0 ) {
158 | var packMethod = this._getPackMethod();
159 | this.packer[ packMethod ]( item.rect );
160 | } else {
161 | this.packer.pack( item.rect );
162 | }
163 |
164 | this._setMaxXY( item.rect );
165 | return item.rect;
166 | };
167 |
168 | proto.shiftLayout = function() {
169 | this.isShifting = true;
170 | this.layout();
171 | delete this.isShifting;
172 | };
173 |
174 | proto._getPackMethod = function() {
175 | return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
176 | };
177 |
178 |
179 | /**
180 | * set max X and Y value, for size of container
181 | * @param {Packery.Rect} rect
182 | * @private
183 | */
184 | proto._setMaxXY = function( rect ) {
185 | this.maxX = Math.max( rect.x + rect.width, this.maxX );
186 | this.maxY = Math.max( rect.y + rect.height, this.maxY );
187 | };
188 |
189 | /**
190 | * set the width and height of a rect, applying columnWidth and rowHeight
191 | * @param {Element} elem
192 | * @param {Packery.Rect} rect
193 | */
194 | proto._setRectSize = function( elem, rect ) {
195 | var size = getSize( elem );
196 | var w = size.outerWidth;
197 | var h = size.outerHeight;
198 | // size for columnWidth and rowHeight, if available
199 | // only check if size is non-zero, #177
200 | if ( w || h ) {
201 | w = this._applyGridGutter( w, this.columnWidth );
202 | h = this._applyGridGutter( h, this.rowHeight );
203 | }
204 | // rect must fit in packer
205 | rect.width = Math.min( w, this.packer.width );
206 | rect.height = Math.min( h, this.packer.height );
207 | };
208 |
209 | /**
210 | * fits item to columnWidth/rowHeight and adds gutter
211 | * @param {Number} measurement - item width or height
212 | * @param {Number} gridSize - columnWidth or rowHeight
213 | * @returns measurement
214 | */
215 | proto._applyGridGutter = function( measurement, gridSize ) {
216 | // just add gutter if no gridSize
217 | if ( !gridSize ) {
218 | return measurement + this.gutter;
219 | }
220 | gridSize += this.gutter;
221 | // fit item to columnWidth/rowHeight
222 | var remainder = measurement % gridSize;
223 | var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
224 | measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
225 | return measurement;
226 | };
227 |
228 | proto._getContainerSize = function() {
229 | if ( this._getOption('horizontal') ) {
230 | return {
231 | width: this.maxX - this.gutter
232 | };
233 | } else {
234 | return {
235 | height: this.maxY - this.gutter
236 | };
237 | }
238 | };
239 |
240 |
241 | // -------------------------- stamp -------------------------- //
242 |
243 | /**
244 | * makes space for element
245 | * @param {Element} elem
246 | */
247 | proto._manageStamp = function( elem ) {
248 |
249 | var item = this.getItem( elem );
250 | var rect;
251 | if ( item && item.isPlacing ) {
252 | rect = item.rect;
253 | } else {
254 | var offset = this._getElementOffset( elem );
255 | rect = new Rect({
256 | x: this._getOption('originLeft') ? offset.left : offset.right,
257 | y: this._getOption('originTop') ? offset.top : offset.bottom
258 | });
259 | }
260 |
261 | this._setRectSize( elem, rect );
262 | // save its space in the packer
263 | this.packer.placed( rect );
264 | this._setMaxXY( rect );
265 | };
266 |
267 | // -------------------------- methods -------------------------- //
268 |
269 | function verticalSorter( a, b ) {
270 | return a.position.y - b.position.y || a.position.x - b.position.x;
271 | }
272 |
273 | function horizontalSorter( a, b ) {
274 | return a.position.x - b.position.x || a.position.y - b.position.y;
275 | }
276 |
277 | proto.sortItemsByPosition = function() {
278 | var sorter = this._getOption('horizontal') ? horizontalSorter : verticalSorter;
279 | this.items.sort( sorter );
280 | };
281 |
282 | /**
283 | * Fit item element in its current position
284 | * Packery will position elements around it
285 | * useful for expanding elements
286 | *
287 | * @param {Element} elem
288 | * @param {Number} x - horizontal destination position, optional
289 | * @param {Number} y - vertical destination position, optional
290 | */
291 | proto.fit = function( elem, x, y ) {
292 | var item = this.getItem( elem );
293 | if ( !item ) {
294 | return;
295 | }
296 |
297 | // stamp item to get it out of layout
298 | this.stamp( item.element );
299 | // set placing flag
300 | item.enablePlacing();
301 | this.updateShiftTargets( item );
302 | // fall back to current position for fitting
303 | x = x === undefined ? item.rect.x: x;
304 | y = y === undefined ? item.rect.y: y;
305 | // position it best at its destination
306 | this.shift( item, x, y );
307 | this._bindFitEvents( item );
308 | item.moveTo( item.rect.x, item.rect.y );
309 | // layout everything else
310 | this.shiftLayout();
311 | // return back to regularly scheduled programming
312 | this.unstamp( item.element );
313 | this.sortItemsByPosition();
314 | item.disablePlacing();
315 | };
316 |
317 | /**
318 | * emit event when item is fit and other items are laid out
319 | * @param {Packery.Item} item
320 | * @private
321 | */
322 | proto._bindFitEvents = function( item ) {
323 | var _this = this;
324 | var ticks = 0;
325 | function onLayout() {
326 | ticks++;
327 | if ( ticks != 2 ) {
328 | return;
329 | }
330 | _this.dispatchEvent( 'fitComplete', null, [ item ] );
331 | }
332 | // when item is laid out
333 | item.once( 'layout', onLayout );
334 | // when all items are laid out
335 | this.once( 'layoutComplete', onLayout );
336 | };
337 |
338 | // -------------------------- resize -------------------------- //
339 |
340 | // debounced, layout on resize
341 | proto.resize = function() {
342 | // don't trigger if size did not change
343 | // or if resize was unbound. See #285, outlayer#9
344 | if ( !this.isResizeBound || !this.needsResizeLayout() ) {
345 | return;
346 | }
347 |
348 | if ( this.options.shiftPercentResize ) {
349 | this.resizeShiftPercentLayout();
350 | } else {
351 | this.layout();
352 | }
353 | };
354 |
355 | /**
356 | * check if layout is needed post layout
357 | * @returns Boolean
358 | */
359 | proto.needsResizeLayout = function() {
360 | var size = getSize( this.element );
361 | var innerSize = this._getOption('horizontal') ? 'innerHeight' : 'innerWidth';
362 | return size[ innerSize ] != this.size[ innerSize ];
363 | };
364 |
365 | proto.resizeShiftPercentLayout = function() {
366 | var items = this._getItemsForLayout( this.items );
367 |
368 | var isHorizontal = this._getOption('horizontal');
369 | var coord = isHorizontal ? 'y' : 'x';
370 | var measure = isHorizontal ? 'height' : 'width';
371 | var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
372 | var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';
373 |
374 | // proportional re-align items
375 | var previousSegment = this[ segmentName ];
376 | previousSegment = previousSegment && previousSegment + this.gutter;
377 |
378 | if ( previousSegment ) {
379 | this._getMeasurements();
380 | var currentSegment = this[ segmentName ] + this.gutter;
381 | items.forEach( function( item ) {
382 | var seg = Math.round( item.rect[ coord ] / previousSegment );
383 | item.rect[ coord ] = seg * currentSegment;
384 | });
385 | } else {
386 | var currentSize = getSize( this.element )[ innerSize ] + this.gutter;
387 | var previousSize = this.packer[ measure ];
388 | items.forEach( function( item ) {
389 | item.rect[ coord ] = ( item.rect[ coord ] / previousSize ) * currentSize;
390 | });
391 | }
392 |
393 | this.shiftLayout();
394 | };
395 |
396 | // -------------------------- drag -------------------------- //
397 |
398 | /**
399 | * handle an item drag start event
400 | * @param {Element} elem
401 | */
402 | proto.itemDragStart = function( elem ) {
403 | if ( !this.isEnabled ) {
404 | return;
405 | }
406 | this.stamp( elem );
407 | // this.ignore( elem );
408 | var item = this.getItem( elem );
409 | if ( !item ) {
410 | return;
411 | }
412 |
413 | item.enablePlacing();
414 | item.showDropPlaceholder();
415 | this.dragItemCount++;
416 | this.updateShiftTargets( item );
417 | };
418 |
419 | proto.updateShiftTargets = function( dropItem ) {
420 | this.shiftPacker.reset();
421 |
422 | // pack stamps
423 | this._getBoundingRect();
424 | var isOriginLeft = this._getOption('originLeft');
425 | var isOriginTop = this._getOption('originTop');
426 | this.stamps.forEach( function( stamp ) {
427 | // ignore dragged item
428 | var item = this.getItem( stamp );
429 | if ( item && item.isPlacing ) {
430 | return;
431 | }
432 | var offset = this._getElementOffset( stamp );
433 | var rect = new Rect({
434 | x: isOriginLeft ? offset.left : offset.right,
435 | y: isOriginTop ? offset.top : offset.bottom
436 | });
437 | this._setRectSize( stamp, rect );
438 | // save its space in the packer
439 | this.shiftPacker.placed( rect );
440 | }, this );
441 |
442 | // reset shiftTargets
443 | var isHorizontal = this._getOption('horizontal');
444 | var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
445 | var measure = isHorizontal ? 'height' : 'width';
446 |
447 | this.shiftTargetKeys = [];
448 | this.shiftTargets = [];
449 | var boundsSize;
450 | var segment = this[ segmentName ];
451 | segment = segment && segment + this.gutter;
452 |
453 | if ( segment ) {
454 | var segmentSpan = Math.ceil( dropItem.rect[ measure ] / segment );
455 | var segs = Math.floor( ( this.shiftPacker[ measure ] + this.gutter ) / segment );
456 | boundsSize = ( segs - segmentSpan ) * segment;
457 | // add targets on top
458 | for ( var i=0; i < segs; i++ ) {
459 | var initialX = isHorizontal ? 0 : i * segment;
460 | var initialY = isHorizontal ? i * segment : 0;
461 | this._addShiftTarget( initialX, initialY, boundsSize );
462 | }
463 | } else {
464 | boundsSize = ( this.shiftPacker[ measure ] + this.gutter ) - dropItem.rect[ measure ];
465 | this._addShiftTarget( 0, 0, boundsSize );
466 | }
467 |
468 | // pack each item to measure where shiftTargets are
469 | var items = this._getItemsForLayout( this.items );
470 | var packMethod = this._getPackMethod();
471 | items.forEach( function( item ) {
472 | var rect = item.rect;
473 | this._setRectSize( item.element, rect );
474 | this.shiftPacker[ packMethod ]( rect );
475 |
476 | // add top left corner
477 | this._addShiftTarget( rect.x, rect.y, boundsSize );
478 | // add bottom left / top right corner
479 | var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
480 | var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
481 | this._addShiftTarget( cornerX, cornerY, boundsSize );
482 |
483 | if ( segment ) {
484 | // add targets for each column on bottom / row on right
485 | var segSpan = Math.round( rect[ measure ] / segment );
486 | for ( var i=1; i < segSpan; i++ ) {
487 | var segX = isHorizontal ? cornerX : rect.x + segment * i;
488 | var segY = isHorizontal ? rect.y + segment * i : cornerY;
489 | this._addShiftTarget( segX, segY, boundsSize );
490 | }
491 | }
492 | }, this );
493 |
494 | };
495 |
496 | proto._addShiftTarget = function( x, y, boundsSize ) {
497 | var checkCoord = this._getOption('horizontal') ? y : x;
498 | if ( checkCoord !== 0 && checkCoord > boundsSize ) {
499 | return;
500 | }
501 | // create string for a key, easier to keep track of what targets
502 | var key = x + ',' + y;
503 | var hasKey = this.shiftTargetKeys.indexOf( key ) != -1;
504 | if ( hasKey ) {
505 | return;
506 | }
507 | this.shiftTargetKeys.push( key );
508 | this.shiftTargets.push({ x: x, y: y });
509 | };
510 |
511 | // -------------------------- drop -------------------------- //
512 |
513 | proto.shift = function( item, x, y ) {
514 | var shiftPosition;
515 | var minDistance = Infinity;
516 | var position = { x: x, y: y };
517 | this.shiftTargets.forEach( function( target ) {
518 | var distance = getDistance( target, position );
519 | if ( distance < minDistance ) {
520 | shiftPosition = target;
521 | minDistance = distance;
522 | }
523 | });
524 | item.rect.x = shiftPosition.x;
525 | item.rect.y = shiftPosition.y;
526 | };
527 |
528 | function getDistance( a, b ) {
529 | var dx = b.x - a.x;
530 | var dy = b.y - a.y;
531 | return Math.sqrt( dx * dx + dy * dy );
532 | }
533 |
534 | // -------------------------- drag move -------------------------- //
535 |
536 | var DRAG_THROTTLE_TIME = 120;
537 |
538 | /**
539 | * handle an item drag move event
540 | * @param {Element} elem
541 | * @param {Number} x - horizontal change in position
542 | * @param {Number} y - vertical change in position
543 | */
544 | proto.itemDragMove = function( elem, x, y ) {
545 | var item = this.isEnabled && this.getItem( elem );
546 | if ( !item ) {
547 | return;
548 | }
549 |
550 | x -= this.size.paddingLeft;
551 | y -= this.size.paddingTop;
552 |
553 | var _this = this;
554 | function onDrag() {
555 | _this.shift( item, x, y );
556 | item.positionDropPlaceholder();
557 | _this.layout();
558 | }
559 |
560 | // throttle
561 | var now = new Date();
562 | var isThrottled = this._itemDragTime && now - this._itemDragTime < DRAG_THROTTLE_TIME;
563 | if ( isThrottled ) {
564 | clearTimeout( this.dragTimeout );
565 | this.dragTimeout = setTimeout( onDrag, DRAG_THROTTLE_TIME );
566 | } else {
567 | onDrag();
568 | this._itemDragTime = now;
569 | }
570 | };
571 |
572 | // -------------------------- drag end -------------------------- //
573 |
574 | /**
575 | * handle an item drag end event
576 | * @param {Element} elem
577 | */
578 | proto.itemDragEnd = function( elem ) {
579 | var item = this.isEnabled && this.getItem( elem );
580 | if ( !item ) {
581 | return;
582 | }
583 |
584 | clearTimeout( this.dragTimeout );
585 | item.element.classList.add('is-positioning-post-drag');
586 |
587 | var completeCount = 0;
588 | var _this = this;
589 | function onDragEndLayoutComplete() {
590 | completeCount++;
591 | if ( completeCount != 2 ) {
592 | return;
593 | }
594 | // reset drag item
595 | item.element.classList.remove('is-positioning-post-drag');
596 | item.hideDropPlaceholder();
597 | _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
598 | }
599 |
600 | item.once( 'layout', onDragEndLayoutComplete );
601 | this.once( 'layoutComplete', onDragEndLayoutComplete );
602 | item.moveTo( item.rect.x, item.rect.y );
603 | this.layout();
604 | this.dragItemCount = Math.max( 0, this.dragItemCount - 1 );
605 | this.sortItemsByPosition();
606 | item.disablePlacing();
607 | this.unstamp( item.element );
608 | };
609 |
610 | /**
611 | * binds Draggabilly events
612 | * @param {Draggabilly} draggie
613 | */
614 | proto.bindDraggabillyEvents = function( draggie ) {
615 | this._bindDraggabillyEvents( draggie, 'on' );
616 | };
617 |
618 | proto.unbindDraggabillyEvents = function( draggie ) {
619 | this._bindDraggabillyEvents( draggie, 'off' );
620 | };
621 |
622 | proto._bindDraggabillyEvents = function( draggie, method ) {
623 | var handlers = this.handleDraggabilly;
624 | draggie[ method ]( 'dragStart', handlers.dragStart );
625 | draggie[ method ]( 'dragMove', handlers.dragMove );
626 | draggie[ method ]( 'dragEnd', handlers.dragEnd );
627 | };
628 |
629 | /**
630 | * binds jQuery UI Draggable events
631 | * @param {jQuery} $elems
632 | */
633 | proto.bindUIDraggableEvents = function( $elems ) {
634 | this._bindUIDraggableEvents( $elems, 'on' );
635 | };
636 |
637 | proto.unbindUIDraggableEvents = function( $elems ) {
638 | this._bindUIDraggableEvents( $elems, 'off' );
639 | };
640 |
641 | proto._bindUIDraggableEvents = function( $elems, method ) {
642 | var handlers = this.handleUIDraggable;
643 | $elems
644 | [ method ]( 'dragstart', handlers.start )
645 | [ method ]( 'drag', handlers.drag )
646 | [ method ]( 'dragstop', handlers.stop );
647 | };
648 |
649 | // ----- destroy ----- //
650 |
651 | var _destroy = proto.destroy;
652 | proto.destroy = function() {
653 | _destroy.apply( this, arguments );
654 | // disable flag; prevent drag events from triggering. #72
655 | this.isEnabled = false;
656 | };
657 |
658 | // ----- ----- //
659 |
660 | Packery.Rect = Rect;
661 | Packery.Packer = Packer;
662 |
663 | return Packery;
664 |
665 | }));
666 |
--------------------------------------------------------------------------------
/js/rect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rect
3 | * low-level utility class for basic geometry
4 | */
5 |
6 | ( function( window, factory ) {
7 | // universal module definition
8 | /* jshint strict: false */ /* globals define, module */
9 | if ( typeof define == 'function' && define.amd ) {
10 | // AMD
11 | define( factory );
12 | } else if ( typeof module == 'object' && module.exports ) {
13 | // CommonJS
14 | module.exports = factory();
15 | } else {
16 | // browser global
17 | window.Packery = window.Packery || {};
18 | window.Packery.Rect = factory();
19 | }
20 |
21 | }( window, function factory() {
22 | 'use strict';
23 |
24 | // -------------------------- Rect -------------------------- //
25 |
26 | function Rect( props ) {
27 | // extend properties from defaults
28 | for ( var prop in Rect.defaults ) {
29 | this[ prop ] = Rect.defaults[ prop ];
30 | }
31 |
32 | for ( prop in props ) {
33 | this[ prop ] = props[ prop ];
34 | }
35 |
36 | }
37 |
38 | Rect.defaults = {
39 | x: 0,
40 | y: 0,
41 | width: 0,
42 | height: 0
43 | };
44 |
45 | var proto = Rect.prototype;
46 |
47 | /**
48 | * Determines whether or not this rectangle wholly encloses another rectangle or point.
49 | * @param {Rect} rect
50 | * @returns {Boolean}
51 | **/
52 | proto.contains = function( rect ) {
53 | // points don't have width or height
54 | var otherWidth = rect.width || 0;
55 | var otherHeight = rect.height || 0;
56 | return this.x <= rect.x &&
57 | this.y <= rect.y &&
58 | this.x + this.width >= rect.x + otherWidth &&
59 | this.y + this.height >= rect.y + otherHeight;
60 | };
61 |
62 | /**
63 | * Determines whether or not the rectangle intersects with another.
64 | * @param {Rect} rect
65 | * @returns {Boolean}
66 | **/
67 | proto.overlaps = function( rect ) {
68 | var thisRight = this.x + this.width;
69 | var thisBottom = this.y + this.height;
70 | var rectRight = rect.x + rect.width;
71 | var rectBottom = rect.y + rect.height;
72 |
73 | // http://stackoverflow.com/a/306332
74 | return this.x < rectRight &&
75 | thisRight > rect.x &&
76 | this.y < rectBottom &&
77 | thisBottom > rect.y;
78 | };
79 |
80 | /**
81 | * @param {Rect} rect - the overlapping rect
82 | * @returns {Array} freeRects - rects representing the area around the rect
83 | **/
84 | proto.getMaximalFreeRects = function( rect ) {
85 |
86 | // if no intersection, return false
87 | if ( !this.overlaps( rect ) ) {
88 | return false;
89 | }
90 |
91 | var freeRects = [];
92 | var freeRect;
93 |
94 | var thisRight = this.x + this.width;
95 | var thisBottom = this.y + this.height;
96 | var rectRight = rect.x + rect.width;
97 | var rectBottom = rect.y + rect.height;
98 |
99 | // top
100 | if ( this.y < rect.y ) {
101 | freeRect = new Rect({
102 | x: this.x,
103 | y: this.y,
104 | width: this.width,
105 | height: rect.y - this.y
106 | });
107 | freeRects.push( freeRect );
108 | }
109 |
110 | // right
111 | if ( thisRight > rectRight ) {
112 | freeRect = new Rect({
113 | x: rectRight,
114 | y: this.y,
115 | width: thisRight - rectRight,
116 | height: this.height
117 | });
118 | freeRects.push( freeRect );
119 | }
120 |
121 | // bottom
122 | if ( thisBottom > rectBottom ) {
123 | freeRect = new Rect({
124 | x: this.x,
125 | y: rectBottom,
126 | width: this.width,
127 | height: thisBottom - rectBottom
128 | });
129 | freeRects.push( freeRect );
130 | }
131 |
132 | // left
133 | if ( this.x < rect.x ) {
134 | freeRect = new Rect({
135 | x: this.x,
136 | y: this.y,
137 | width: rect.x - this.x,
138 | height: this.height
139 | });
140 | freeRects.push( freeRect );
141 | }
142 |
143 | return freeRects;
144 | };
145 |
146 | proto.canFit = function( rect ) {
147 | return this.width >= rect.width && this.height >= rect.height;
148 | };
149 |
150 | return Rect;
151 |
152 | }));
153 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "packery",
3 | "version": "3.0.0",
4 | "description": "Gapless, draggable grid layouts",
5 | "main": "js/packery.js",
6 | "dependencies": {
7 | "get-size": "^2.0.2",
8 | "outlayer": "^2.0.0"
9 | },
10 | "devDependencies": {
11 | "chalk": "^1.1.1",
12 | "draggabilly": "^2.1.0",
13 | "gulp": "^3.9.0",
14 | "gulp-jshint": "^2.0.0",
15 | "gulp-json-lint": "^0.1.0",
16 | "gulp-rename": "^1.2.2",
17 | "gulp-replace": "^0.5.4",
18 | "gulp-requirejs-optimize": "github:metafizzy/gulp-requirejs-optimize",
19 | "gulp-uglify": "^1.5.1",
20 | "gulp-util": "^3.0.7",
21 | "jquery": ">=2 <4",
22 | "jquery-bridget": "~2.0.0",
23 | "jshint": "^2.9.1",
24 | "minimist": "^1.2.0",
25 | "qunitjs": "^1.15"
26 | },
27 | "directories": {
28 | "test": "test"
29 | },
30 | "scripts": {
31 | "test": "echo \"Error: no test specified\" && exit 1"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git://github.com/metafizzy/packery.git"
36 | },
37 | "keywords": [
38 | "DOM",
39 | "browser",
40 | "layout",
41 | "bin",
42 | "binpacking",
43 | "packing",
44 | "masonry",
45 | "gapless",
46 | "draggable"
47 | ],
48 | "author": "Metafizzy",
49 | "bugs": {
50 | "url": "https://github.com/metafizzy/packery/issues"
51 | },
52 | "homepage": "https://packery.metafizzy.co",
53 | "license": "MIT"
54 | }
55 |
--------------------------------------------------------------------------------
/sandbox/add-remove.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | add/remove
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | add/remove
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/sandbox/basics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Packery basic examples
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Packery basic examples
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
187 |
188 |
189 |
190 |
--------------------------------------------------------------------------------
/sandbox/browserify/browserify.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Browserify
7 |
8 |
9 |
10 |
11 |
12 |
13 | Browserify
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/sandbox/browserify/jquery-main.js:
--------------------------------------------------------------------------------
1 | // ----- jQuery ----- //
2 |
3 | var Packery = require('../../js/packery');
4 | var Draggabilly = require('draggabilly');
5 | var $ = require('jquery');
6 | var jQBridget = require('jquery-bridget');
7 |
8 | $.bridget( 'packery', Packery );
9 |
10 | var $container = $('#basic').packery({
11 | columnWidth: 50,
12 | rowHeight: 50
13 | });
14 |
15 | var pckry = $container.data('packery');
16 |
17 | $.each( pckry.items, function( i, item ) {
18 | var draggie = new Draggabilly( item.element );
19 | $container.packery( 'bindDraggabillyEvents', draggie );
20 | });
21 |
22 | $container.packery( 'on', 'dragItemPositioned', function( pckry, item ) {
23 | console.log( 'drag item positioned', item.position.x, item.position.y );
24 | });
25 |
--------------------------------------------------------------------------------
/sandbox/browserify/main.js:
--------------------------------------------------------------------------------
1 | // ----- vanilla JS ----- //
2 |
3 | var Packery = require('../../js/packery');
4 | var Draggabilly = require('draggabilly');
5 |
6 | var pckry = new Packery( '#basic', {
7 | columnWidth: 50,
8 | rowHeight: 50
9 | });
10 |
11 | var draggies = [];
12 | var item, draggie;
13 |
14 | for ( var i=0, len = pckry.items.length; i < len; i++ ) {
15 | item = pckry.items[i].element;
16 | draggie = new Draggabilly( item );
17 | pckry.bindDraggabillyEvents( draggie );
18 | draggies.push( draggie );
19 | }
20 |
21 | pckry.on( 'dragItemPositioned', function( pckry, item ) {
22 | console.log( 'drag item positioned', item.position.x, item.position.y );
23 | });
24 |
--------------------------------------------------------------------------------
/sandbox/css/basics.css:
--------------------------------------------------------------------------------
1 |
2 | #ex5 .item {
3 | margin-left: 1%;
4 | margin-right: 1%;
5 | width: 7.9%;
6 | }
7 |
8 | #ex5 .item.w2 { width: 17.9%; }
9 | #ex5 .item.w4 { width: 37.9%; }
10 |
11 | @media ( max-width: 800px ) {
12 | body { background: red ;}
13 |
14 | #ex5 .item { width: 17.9%; }
15 | #ex5 .item.w2 { width: 37.9%; }
16 | #ex5 .item.w4 { width: 57.9%; }
17 |
18 | }
19 |
20 | .bogey {
21 | background: red;
22 | position: absolute;
23 | }
24 |
25 | #ex6 .bogey1 {
26 | width: 30%;
27 | height: 140px;
28 | left: 20%;
29 | top: 20px;
30 | }
31 |
32 | #ex6 .bogey2 {
33 | width: 140px;
34 | height: 180px;
35 | right: 20px;
36 | top: 140px;
37 | }
38 |
39 | #fit-demo {
40 | width: 200px;
41 | }
42 |
43 | /*
44 | #fit-demo .item {
45 | width: 40px;
46 | height: 40px;
47 | }
48 | */
49 |
50 | #fit-demo .item.gigante {
51 | width: 150px;
52 | height: 100px;
53 | }
54 |
--------------------------------------------------------------------------------
/sandbox/css/examples.css:
--------------------------------------------------------------------------------
1 | * { box-sizing: border-box; }
2 |
3 | body {
4 | font-family: sans-serif;
5 | }
6 |
7 | .container {
8 | width: 500px;
9 | margin-bottom: 20px;
10 | background: #EEE;
11 | }
12 |
13 | .item {
14 | float: left;
15 | width: 50px;
16 | height: 50px;
17 | border: 1px solid;
18 | background: #ACE;
19 | }
20 |
21 | .item.w2 { width: 100px; }
22 | .item.h2 { height: 100px; }
23 | .item.w4 { width: 200px; }
24 | .item.h4 { height: 200px; }
25 |
26 | .fluid {
27 | width: 50%;
28 | }
29 |
30 | .has-padding {
31 | padding: 10px 20px 30px 40px;
32 | }
33 |
34 | /* dragging */
35 | .is-dragging,
36 | .is-positioning-post-drag,
37 | .ui-draggable-dragging {
38 | z-index: 10;
39 | background: orange;
40 | }
41 |
--------------------------------------------------------------------------------
/sandbox/draggabilly.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Draggabilly
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 | Draggabilly
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/sandbox/examples.js:
--------------------------------------------------------------------------------
1 | function appendRandomSizedItems( container ) {
2 | var frag = document.createDocumentFragment();
3 | for ( var i=0; i < 35; i++ ) {
4 | var item = document.createElement('div');
5 | item.className = 'item';
6 | var w = Math.floor( Math.random() * Math.random() * 180 ) + 20;
7 | var h = Math.floor( Math.random() * Math.random() * 180 ) + 20;
8 | item.style.width = w + 'px';
9 | item.style.height = h + 'px';
10 | frag.appendChild( item );
11 | }
12 |
13 | container.appendChild( frag );
14 | }
15 |
--------------------------------------------------------------------------------
/sandbox/fit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | fit
7 |
8 |
9 |
34 |
35 |
36 |
37 |
38 | fit
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/sandbox/fluid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | fluid
8 |
9 |
43 |
44 |
45 |
46 |
47 | fluid
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Draggable
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/sandbox/horizontal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Packery horizontal examples
7 |
8 |
9 |
10 |
39 |
40 |
41 |
42 |
43 |
44 | horizontal examples
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/sandbox/jquery-ui-draggable.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | jQuery UI Draggable
7 |
8 |
9 |
10 |
11 |
12 |
13 | jQuery UI Draggable
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/sandbox/jquery.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Packery with jQuery
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Packery with jQuery
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/sandbox/merge-rects.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | merge rects
8 |
9 |
10 |
11 |
12 | merge rects
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/sandbox/origin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Packery basic examples
7 |
8 |
9 |
10 |
11 |
51 |
52 |
53 |
54 |
55 | Packery basic examples
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/sandbox/requirejs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | require js
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | require js
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/sandbox/requirejs/main.js:
--------------------------------------------------------------------------------
1 | /*global requirejs: false*/
2 |
3 | /*
4 | // bower components
5 | requirejs.config({
6 | baseUrl: '../../bower_components'
7 | });
8 |
9 | requirejs( [ '../../js/packery' ], function( Packery ) {
10 |
11 | new Packery( document.querySelector('#basic') );
12 |
13 | });
14 | // */
15 |
16 | /*
17 | // packery.pkgd.js
18 | requirejs( [
19 | './../dist/packery.pkgd.js',
20 | 'require'
21 | ], function( pkgd, require ) {
22 | require( ['packery/js/packery'], function ( Packery ) {
23 | new Packery('#basic');
24 | });
25 | });
26 | // */
27 |
28 | requirejs( [
29 | '../../dist/packery.pkgd.js'
30 | ], function( Packery ) {
31 | new Packery('#basic');
32 | });
33 |
--------------------------------------------------------------------------------
/sandbox/save-positions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | save positions
8 |
9 |
57 |
58 |
59 |
60 |
61 | save positions
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/sandbox/shift-drag-uneven.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | shift drag uneven
8 |
9 |
37 |
38 |
39 |
40 |
41 | shift drag uneven
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/sandbox/shift-drag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | shift drag
8 |
9 |
35 |
36 |
37 |
38 |
39 | shift drag
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/sandbox/single.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | single
8 |
9 |
29 |
30 |
31 |
32 |
33 | single
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "browser": true,
3 | "undef": true,
4 | "unused": true,
5 | "globals": {
6 | "appendRandomSizedItems": true,
7 | "gimmeAnItemElement": true,
8 | "Packery": false,
9 | "QUnit": false
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/helpers.js:
--------------------------------------------------------------------------------
1 | ( function( window ) {
2 |
3 | 'use strict';
4 |
5 | window.appendRandomSizedItems = function( container ) {
6 | var frag = document.createDocumentFragment();
7 | var item;
8 | for ( var i=0; i < 9; i++ ) {
9 | item = document.createElement('div');
10 | item.className = 'item';
11 | var w = Math.floor( Math.random() * Math.random() * 70 ) + 10;
12 | var h = Math.floor( Math.random() * Math.random() * 70 ) + 10;
13 | item.style.width = w + 'px';
14 | item.style.height = h + 'px';
15 | frag.appendChild( item );
16 | }
17 |
18 | // last one isn't random, but is needed for checking
19 | // bigger than colum width and stuff
20 | item = document.createElement('div');
21 | item.className = 'item';
22 | item.style.width = '72px';
23 | item.style.height = '25px';
24 | frag.appendChild( item );
25 |
26 | container.appendChild( frag );
27 | };
28 |
29 | window.gimmeAnItemElement = function() {
30 | var elem = document.createElement('div');
31 | elem.className = 'item';
32 | return elem;
33 | };
34 |
35 | })( window );
36 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Packery tests
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Packery tests
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Layout extra big
68 |
69 |
76 |
77 | Consecutive
78 |
79 |
85 |
86 |
90 |
91 |
95 |
96 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | Placed borders
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Adding
163 |
164 |
168 |
169 | Prepend
170 |
171 |
175 |
176 | Draggable
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | Fitting
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 | Declarative
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | jQuery
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 | isInitLayout option
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 | Sub-pixel fit
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
--------------------------------------------------------------------------------
/test/test.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | }
4 |
5 | .container {
6 | width: 80px;
7 | margin-bottom: 20px;
8 | background: #EEE;
9 | }
10 |
11 | .container:after {
12 | content: '';
13 | display: table;
14 | }
15 |
16 | .item {
17 | float: left;
18 | width: 18px;
19 | height: 18px;
20 | margin: 1px;
21 | background: #444;
22 | }
23 |
24 | .item.w2 { width: 38px; }
25 | .item.h2 { height: 38px; }
26 | .item.w4 { width: 78px; }
27 | .item.h4 { height: 78px; }
28 | .item.w5 { width: 98px; }
29 |
30 | .gridded .grid-sizer {
31 | width: 30px;
32 | height: 25px;
33 | }
34 |
35 | .gridded .gutter-sizer {
36 | width: 10px;
37 | }
38 |
39 | .stamp {
40 | background: red;
41 | }
42 |
43 | .has-stamps {
44 | position: relative;
45 | }
46 |
47 | .place-stamps .stamp {
48 | position: absolute;
49 | }
50 |
51 | .place-stamps .stamp1 {
52 | width: 30px;
53 | height: 25px;
54 | left: 22px;
55 | top: 10px;
56 | }
57 |
58 | .place-stamps .stamp2 {
59 | width: 10px;
60 | height: 45px;
61 | left: 3px;
62 | top: 30px;
63 | }
64 |
65 | #stamped2 .stamp1 {
66 | position: relative;
67 | left: 8px;
68 | top: 8px;
69 | }
70 |
71 | #stamped2 .stamp2 {
72 | position: absolute;
73 | right: 8px;
74 | top: 23px;
75 | }
76 |
77 | #stamped-borders {
78 | border-left: 10px solid;
79 | border-top: 15px solid;
80 | }
81 |
82 | #stamped-borders .stamp1 {
83 | width: 50px;
84 | height: 30px;
85 | }
86 |
87 | .dragger {
88 | background: green;
89 | }
90 |
91 | /*#fitting {
92 | position: absolute;
93 | top: 0;
94 | }*/
95 |
96 | #hidden-items .hidden {
97 | display: none;
98 | }
99 |
100 | #sub-pixel-fit {
101 | width: 290px;
102 | }
103 |
104 | #sub-pixel-fit .item,
105 | #sub-pixel-fit .grid-sizer {
106 | width: 20%;
107 | }
108 |
109 | #sub-pixel-fit .item {
110 | margin: 0;
111 | }
112 |
113 | #sub-pixel-fit .item.w2 { width: 40%; }
114 |
--------------------------------------------------------------------------------
/test/unit/add-items.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'addItems', function( assert ) {
2 | var container = document.querySelector('#adding');
3 | var pckry = new Packery( container, {
4 | itemSelector: '.item'
5 | });
6 |
7 | var elem = gimmeAnItemElement();
8 | var items = pckry.addItems( elem );
9 |
10 | assert.equal( items.length, 1, 'method return array of 1' );
11 | assert.equal( pckry.items[2].element, elem, 'item was added, element matches' );
12 | assert.equal( items[0] instanceof Packery.Item, true, 'item is instance of Packery.Item' );
13 | assert.equal( pckry.items.length, 3, 'item added to items' );
14 |
15 | // try it with an array
16 | var elems = [ gimmeAnItemElement(), gimmeAnItemElement(), document.createElement('div') ];
17 | items = pckry.addItems( elems );
18 | assert.equal( items.length, 2, 'method return array of 2' );
19 | assert.equal( pckry.items[3].element, elems[0], 'item was added, element matches' );
20 | assert.equal( pckry.items.length, 5, 'two items added to items' );
21 |
22 | // try it with HTMLCollection / NodeList
23 | var fragment = document.createDocumentFragment();
24 | fragment.appendChild( gimmeAnItemElement() );
25 | fragment.appendChild( document.createElement('div') );
26 | fragment.appendChild( gimmeAnItemElement() );
27 |
28 | var divs = fragment.querySelectorAll('div');
29 | items = pckry.addItems( divs );
30 | assert.equal( items.length, 2, 'method return array of 2' );
31 | assert.equal( pckry.items[5].element, divs[0], 'item was added, element matches' );
32 | assert.equal( pckry.items.length, 7, 'two items added to items' );
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/test/unit/basics.js:
--------------------------------------------------------------------------------
1 | QUnit.module('Packery');
2 |
3 | QUnit.test( 'basics', function( assert ) {
4 | assert.equal( typeof Packery, 'function', 'Packery is a function' );
5 | // TODO pckry should be null or something
6 | var pckry1 = new Packery();
7 | // console.log( pckry, typeof pckry );
8 | assert.ok( !pckry1._isLayoutInited, 'packery not inited on undefined' );
9 |
10 | // var pckry2 = new Packery({});
11 | // console.log( pckry, typeof pckry );
12 | // FIXME Outlayer should throw error up top
13 | // assert.ok( !pckry2._isLayoutInited, 'packery not inited on object' );
14 | });
15 |
--------------------------------------------------------------------------------
/test/unit/consecutive.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'consecutive', function( assert ) {
2 | var container = document.querySelector('#consecutive');
3 | var pckry = new Packery( container );
4 |
5 | var i0 = container.querySelector('.i0');
6 | var i1 = container.querySelector('.i1');
7 | var i3 = container.querySelector('.i3');
8 | i1.style.width = '28px';
9 | i1.style.height = '28px';
10 | i1.style.background = 'blue';
11 |
12 | var done = assert.async();
13 |
14 | pckry.once( 'layoutComplete', function() {
15 | assert.ok( true, 'layoutComplete triggered' );
16 | assert.equal( i3.style.left, '0px', 'i3.style.left' );
17 | assert.equal( i3.style.top, '20px', 'i3.style.top' );
18 | setTimeout( fit1 );
19 | });
20 |
21 | pckry.layout();
22 |
23 | function fit1() {
24 | pckry.once( 'layoutComplete', function() {
25 | assert.equal( i3.style.left, '60px', 'i3.style.left' );
26 | assert.equal( i3.style.top, '30px', 'i3.style.top' );
27 | // all done
28 | done();
29 | });
30 | i0.style.width = '38px';
31 | i0.style.height = '38px';
32 | i0.style.background = 'orange';
33 | pckry.layout();
34 | }
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/test/unit/container-size.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'container size', function( assert ) {
2 | var container = document.querySelector('#container-size');
3 | var pckry = new Packery( container );
4 | assert.equal( container.style.height, '40px', 'default height' );
5 |
6 | pckry.options.gutter = 4;
7 | pckry._getMeasurements();
8 | pckry.layout();
9 | assert.equal( container.style.height, '40px', 'added gutter, height same' );
10 |
11 | // addPaddingBorders() {
12 | container.style.padding = '1px 2px 3px 4px';
13 | container.style.borderStyle = 'solid';
14 | container.style.borderWidth = '1px 2px 3px 4px';
15 | pckry.layout();
16 | assert.equal( container.style.height, '40px', 'add padding, height same' );
17 |
18 | // border box
19 | container.style.WebkitBoxSizing = 'border-box';
20 | container.style.MozBoxSizing = 'border-box';
21 | container.style.boxSizing = 'border-box';
22 | pckry.layout();
23 | assert.equal( container.style.height, '48px', 'border-box, height + padding + border' );
24 | });
25 |
--------------------------------------------------------------------------------
/test/unit/declarative.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'declarative', function( assert ) {
2 |
3 | var $ = window.jQuery;
4 |
5 | // no data-packery-options
6 | var container1 = document.querySelector('#declarative');
7 | var pckry1 = Packery.data( container1 );
8 | assert.ok( pckry1 instanceof Packery, 'Packery instance retrieved from element' );
9 | assert.deepEqual( pckry1.options, Packery.defaults, 'options match defaults' );
10 | assert.ok( pckry1._isLayoutInited, 'Packer was initialized' );
11 |
12 | // has data-packery-options, but bad JSON
13 | var container2 = document.querySelector('#declarative-bad-json');
14 | var pckry2 = Packery.data( container2 );
15 | assert.ok( !pckry2, 'bad JSON in data-packery-options does not init Packery' );
16 | assert.ok( !container2.packeryGUID, 'no expando property on element' );
17 |
18 | // has good data-packery-options
19 | var container3 = document.querySelector('#declarative-good-json');
20 | var pckry3 = Packery.data( container3 );
21 | assert.ok( pckry3 instanceof Packery, 'Packery instance retrieved from element, with good JSON in data-packery-options' );
22 | assert.strictEqual( pckry3.options.columnWidth, 25, 'columnWidth option was set' );
23 | assert.strictEqual( pckry3.options.rowHeight, 30, 'rowHeight option was set' );
24 | assert.strictEqual( pckry3.options.transitionDuration, '1.2s', 'transitionDuration option was set' );
25 | assert.strictEqual( pckry3.options.isResizable, false, 'isResizable option was set' );
26 |
27 | assert.equal( $.data( container3, 'packery' ), pckry3, '$.data( elem, "packery") returns Packery instance' );
28 |
29 | });
30 |
--------------------------------------------------------------------------------
/test/unit/defaults-empty.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'defaults / empty', function( assert ) {
2 | var empty = document.querySelector('#empty');
3 | var pckry = new Packery( empty );
4 | var done = assert.async();
5 |
6 | assert.deepEqual( pckry.options, Packery.defaults, 'default options match prototype' );
7 | assert.equal( pckry.items.length, 0, 'zero items' );
8 | assert.equal( pckry.stamps.length, 0, 'zero stamped elements' );
9 | assert.equal( Packery.data( empty ), pckry, 'data method returns instance' );
10 | assert.ok( pckry.isResizeBound, 'isResizeBound' );
11 |
12 | pckry.once( 'layoutComplete', function( items ) {
13 | assert.ok( true, 'layoutComplete triggered with no items' );
14 | assert.equal( items.length, 0, 'no items' );
15 | done();
16 | });
17 |
18 | // add gutter, to check that container size doesn't get negative number
19 | pckry.options.gutter = 20;
20 | pckry.layout();
21 | });
22 |
--------------------------------------------------------------------------------
/test/unit/draggable.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'draggable', function( assert ) {
2 | var container = document.querySelector('#draggable');
3 | var itemElems = container.querySelectorAll('.item');
4 | var dragElem = container.querySelector('.dragger');
5 |
6 | var pckry = new Packery( container, {
7 | transitionDuration: '0.2s'
8 | });
9 |
10 | var done = assert.async();
11 |
12 | function simulateDrag( x, y ) {
13 | pckry.itemDragStart( dragElem );
14 | pckry.itemDragMove( dragElem, x, y );
15 | pckry.itemDragEnd( dragElem );
16 | }
17 |
18 | function checkItemPosition( itemElem, x, y, message ) {
19 | var actual = itemElem.style.left + ' ' + itemElem.style.top;
20 | var expected = x + 'px ' + y + 'px';
21 | message = message || 'item position';
22 | assert.equal( actual, expected, message );
23 | }
24 |
25 | // simulate drag to middle
26 | pckry.once( 'dragItemPositioned', function() {
27 | assert.ok( true, 'dragItemPositioned did trigger, 4th item moved to 35, 15' );
28 | checkItemPosition( itemElems[0], 0, 0, '1st item' );
29 | checkItemPosition( itemElems[1], 20, 0, '2nd item' );
30 | checkItemPosition( itemElems[2], 40, 0, '3rd item' );
31 | checkItemPosition( itemElems[6], 40, 40, '7th item, moved down below dragged item' );
32 | checkItemPosition( itemElems[7], 60, 0, '8th item, moved up' );
33 | checkItemPosition( dragElem, 40, 20, 'drag item, 2nd row, 3rd column' );
34 | assert.equal( pckry.items[6].element, dragElem, 'dragged elem in now 7th in items' );
35 | // HACK setTimeout because of Packery bug
36 | setTimeout( dragOutside );
37 | });
38 |
39 | simulateDrag( 35, 15 );
40 |
41 | function dragOutside() {
42 | pckry.once( 'dragItemPositioned', function() {
43 | assert.ok( true, 'dragItemPositioned event did trigger' );
44 | checkItemPosition( dragElem, 60, 0, 'drag item, 1st row, 4th column' );
45 | checkItemPosition( itemElems[4], 0, 20, '5th item, did not move' );
46 | checkItemPosition( itemElems[6], 40, 20, '7th item, moved back up' );
47 | checkItemPosition( itemElems[7], 60, 20, '8th item, moved back down' );
48 | assert.equal( pckry.items[3].element, dragElem, 'dragged elem in now 4th in items' );
49 |
50 | setTimeout( dragWithGrid );
51 | });
52 |
53 | simulateDrag( 300, -60 );
54 | }
55 |
56 | function dragWithGrid() {
57 | pckry.options.columnWidth = 25;
58 | pckry.options.rowHeight = 25;
59 | // disable transition, layout, re-enable transition
60 | pckry.options.transitionDuration = 0;
61 | pckry.layout();
62 | pckry.options.transitionDuration = '0.2s';
63 |
64 | pckry.once( 'dragItemPositioned', function() {
65 | checkItemPosition( dragElem, 25, 25, 'drag item, 2nd row, 2nd column' );
66 | checkItemPosition( itemElems[4], 25, 50, '5th item, 3rd row, 2nd column, moved down' );
67 | checkItemPosition( itemElems[5], 50, 25, '6th item, 2nd row, 3rd column, did not move' );
68 | checkItemPosition( itemElems[6], 0, 25, '7th item, 2nd row, 1st column, moved up' );
69 | checkItemPosition( itemElems[7], 25, 75, '7th item, 4th row, 2nd column, moved down' );
70 |
71 | setTimeout( dragOutsideWithGrid );
72 | });
73 | simulateDrag( 35, 35 );
74 | }
75 |
76 | function dragOutsideWithGrid() {
77 | pckry.once( 'dragItemPositioned', function() {
78 | checkItemPosition( dragElem, 50, 0, 'dragged, top right corner in grid' );
79 | done();
80 | });
81 | simulateDrag( 300, -30 );
82 | }
83 |
84 | });
85 |
--------------------------------------------------------------------------------
/test/unit/fit.js:
--------------------------------------------------------------------------------
1 | QUnit.test( '.fit()', function( assert ) {
2 | var container = document.querySelector('#fitting');
3 | var pckry = new Packery( container, {
4 | transitionDuration: '0.2s'
5 | });
6 |
7 | var done = assert.async();
8 |
9 | var elem1 = container.querySelector('.i1');
10 | var elem2 = container.querySelector('.i2');
11 | var elem3 = container.querySelector('.i3');
12 | var elem5 = container.querySelector('.i5');
13 | var elem6 = container.querySelector('.i6');
14 | var item3 = pckry.getItem( elem3 );
15 |
16 | function checkItemPosition( itemElem, x, y, message ) {
17 | var actual = itemElem.style.left + ' ' + itemElem.style.top;
18 | var expected = x + 'px ' + y + 'px';
19 | message = message || 'item position';
20 | assert.equal( actual, expected, message );
21 | }
22 |
23 | // expand item3
24 | elem3.style.width = '48px';
25 | elem3.style.height = '28px';
26 | elem3.style.background = 'blue';
27 |
28 | // quickie async
29 | var isFit, isLaidOut;
30 | function resetAsync() {
31 | isFit = false;
32 | isLaidOut = false;
33 | }
34 |
35 | // -------------------------- fit -------------------------- //
36 |
37 | function ready1() {
38 | if ( !isFit || !isLaidOut ) {
39 | return;
40 | }
41 | checkItemPosition( elem1, 20, 30, 'elem1 shifted down' );
42 | checkItemPosition( elem2, 40, 30, 'elem2 shifted down' );
43 | checkItemPosition( elem5, 20, 50, 'elem5 shifted down, 2nd row' );
44 | resetAsync();
45 | // HACK setTimeout for Packery bug
46 | setTimeout( fit2 );
47 | }
48 |
49 | pckry.once( 'fitComplete', function( item ) {
50 | assert.ok( true, 'fitComplete event emitted' );
51 | assert.equal( item, item3, 'item argument returned' );
52 | checkItemPosition( elem3, 20, 0, 'fit elem3 shifted into 2nd spot' );
53 | isFit = true;
54 | ready1();
55 | });
56 |
57 | pckry.once( 'layoutComplete', function() {
58 | assert.ok( true, 'layoutComplete event emitted' );
59 | isLaidOut = true;
60 | ready1();
61 | });
62 |
63 | // fit it
64 | pckry.fit( elem3 );
65 |
66 | // -------------------------- fit into spot -------------------------- //
67 |
68 | function ready2() {
69 | if ( !isFit || !isLaidOut ) {
70 | return;
71 | }
72 | resetAsync();
73 |
74 | setTimeout( fit3 );
75 | }
76 |
77 | function fit2() {
78 | // reset small size
79 | elem3.style.width = '18px';
80 | elem3.style.height = '18px';
81 |
82 | pckry.once( 'fitComplete', function() {
83 | assert.ok( true, 'fitComplete event emitted' );
84 | checkItemPosition( elem3, 40, 20, 'fit item in 40, 20' );
85 | isFit = true;
86 | ready2();
87 | });
88 |
89 | pckry.once( 'layoutComplete', function() {
90 | assert.ok( true, 'layoutComplete event emitted' );
91 | checkItemPosition( elem3, 40, 20, 'fit item in 40, 20' );
92 | checkItemPosition( elem1, 20, 0, 'elem1 shifted up' );
93 | checkItemPosition( elem2, 40, 0, 'elem2 shifted up' );
94 | checkItemPosition( elem5, 20, 20, 'elem5 shifted up' );
95 | checkItemPosition( elem6, 40, 40, 'elem6 shifted down' );
96 | isLaidOut = true;
97 | ready2();
98 | });
99 |
100 | // fit to spot
101 | pckry.fit( elem3, 40, 20 );
102 | }
103 |
104 | // -------------------------- fit outside container -------------------------- //
105 |
106 | function ready3() {
107 | if ( !isFit || !isLaidOut ) {
108 | return;
109 | }
110 | resetAsync();
111 |
112 | setTimeout( fit4 );
113 | }
114 |
115 | function fit3() {
116 | pckry.once( 'fitComplete', function() {
117 | checkItemPosition( elem3, 40, 40, 'fit elem in 3rd row, 3rd column' );
118 | isFit = true;
119 | ready3();
120 | });
121 | pckry.once( 'layoutComplete', function() {
122 | isLaidOut = true;
123 | ready3();
124 | });
125 |
126 | // try to position item outside container
127 | pckry.fit( elem3, 120, 120 );
128 | }
129 |
130 | // -------------------------- columnWidth & rowHeight -------------------------- //
131 |
132 | // fit with columnWidth and rowHeight
133 | function fit4() {
134 | pckry.options.columnWidth = 25;
135 | pckry.options.rowHeight = 30;
136 | // disable transition, trigger layout, re-enable transition
137 | pckry.options.transitionDuration = 0;
138 | pckry.layout();
139 | pckry.options.transitionDuration = '0.2s';
140 |
141 | function ready4() {
142 | if ( !isFit || !isLaidOut ) {
143 | return;
144 | }
145 | done();
146 | }
147 |
148 | pckry.on( 'fitComplete', function() {
149 | assert.ok( true, 'fitComplete event emitted' );
150 | checkItemPosition( elem3, 50, 30, 'fit item, 2nd row, 3rd column' );
151 | isFit = true;
152 | ready4();
153 | });
154 |
155 | pckry.on( 'layoutComplete', function() {
156 | checkItemPosition( elem5, 50, 60, 'elem5 shifted down' );
157 | isLaidOut = true;
158 | ready4();
159 | });
160 |
161 | pckry.fit( elem3, 55, 28 );
162 | }
163 |
164 | });
165 |
--------------------------------------------------------------------------------
/test/unit/get-items.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'getItems', function( assert ) {
2 | var defaultElem = document.querySelector('#default');
3 | var defaultPckry = new Packery( defaultElem );
4 |
5 | var filtered = document.querySelector('#filtered');
6 | var filteredPckry = new Packery( filtered, {
7 | itemSelector: '.item'
8 | });
9 |
10 | var found = document.querySelector('#found');
11 | var foundPckry = new Packery( found, {
12 | itemSelector: '.item'
13 | });
14 |
15 | var filterFound = document.querySelector('#filter-found');
16 | var filterFoundPckry = new Packery( filterFound, {
17 | itemSelector: '.item'
18 | });
19 |
20 | assert.equal( defaultPckry.items.length, defaultElem.children.length, 'no itemSelector, all children' );
21 | assert.equal( filteredPckry.items.length, 6, 'filtered, itemSelector = .item, not all children' );
22 | assert.equal( foundPckry.items.length, 4, 'found itemSelector = .item, querySelectoring' );
23 | assert.equal( filterFoundPckry.items.length, 5, 'filter found' );
24 | });
25 |
--------------------------------------------------------------------------------
/test/unit/grid.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'layout with columnWidth and rowHeight', function( assert ) {
2 | var container = document.querySelector('#gridded1');
3 | appendRandomSizedItems( container );
4 |
5 | var pckry = new Packery( container, {
6 | itemSelector: '.item',
7 | columnWidth: 25,
8 | rowHeight: 30
9 | });
10 |
11 | var done = assert.async();
12 |
13 | function checkPackeryGrid() {
14 | var colW = pckry.columnWidth + pckry.gutter;
15 | var rowH = pckry.rowHeight + pckry.gutter;
16 | for ( var i=0, len = pckry.items.length; i < len; i++ ) {
17 | var elem = pckry.items[i].element;
18 | var x = parseInt( elem.style.left, 10 );
19 | var y = parseInt( elem.style.top, 10 );
20 | assert.equal( x % colW, 0, 'item ' + i + ' x position is multiple of columnWidth' );
21 | assert.equal( y % rowH, 0, 'item ' + i + ' y position is multiple of rowHeight' );
22 | }
23 | }
24 |
25 | assert.equal( pckry.options.columnWidth, 25, 'columnWidth option is set' );
26 | assert.equal( pckry.options.rowHeight, 30, 'rowHeight option is set' );
27 | assert.equal( pckry.columnWidth, 25, 'columnWidth is set' );
28 | assert.equal( pckry.rowHeight, 30, 'rowHeight is set' );
29 | checkPackeryGrid( pckry );
30 |
31 | var gridSizer = container.querySelector('.grid-sizer');
32 |
33 | pckry.options.columnWidth = gridSizer;
34 | pckry.options.rowHeight = gridSizer;
35 | pckry.once( 'layoutComplete', function() {
36 | checkPackeryGrid( pckry );
37 | setTimeout( setGutter );
38 | });
39 |
40 | pckry.layout();
41 |
42 | assert.equal( pckry.columnWidth, 30, 'columnWidth is set from element width, in px' );
43 | assert.equal( pckry.rowHeight, 25, 'rowHeight is set from element height, in px' );
44 |
45 | function setGutter() {
46 | pckry.options.gutter = container.querySelector('.gutter-sizer');
47 | pckry.once( 'layoutComplete', function() {
48 | checkPackeryGrid( pckry );
49 | setTimeout( setPercentageGrid );
50 | });
51 | pckry.layout();
52 | assert.equal( pckry.gutter, 10, 'gutter set from element width, in px' );
53 | }
54 |
55 | function setPercentageGrid() {
56 | gridSizer.style.width = '40%';
57 | pckry.once( 'layoutComplete', function() {
58 | checkPackeryGrid( pckry );
59 | done();
60 | });
61 | pckry.layout();
62 | assert.equal( pckry.columnWidth, 32, 'columnWidth is set from element width, in percentage' );
63 | }
64 |
65 | });
66 |
67 | QUnit.test( 'columnWidth, rowHeight, gutter via selector', function( assert ) {
68 | var container = document.querySelector('#gridded2');
69 | appendRandomSizedItems( container );
70 |
71 | var pckry = new Packery( container, {
72 | itemSelector: '.item',
73 | columnWidth: '.grid-sizer',
74 | rowHeight: '.grid-sizer',
75 | gutter: '.gutter-sizer'
76 | });
77 |
78 | assert.equal( pckry.columnWidth, 30, 'columnWidth' );
79 | assert.equal( pckry.rowHeight, 25, 'rowHeight' );
80 | assert.equal( pckry.gutter, 10, 'gutter' );
81 | });
82 |
--------------------------------------------------------------------------------
/test/unit/hidden-items.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'hidden items', function( assert ) {
2 | var container = document.querySelector('#hidden-items');
3 | var pckry = new Packery( container );
4 | var itemElem1 = pckry.items[1].element;
5 | var itemElem2 = pckry.items[2].element;
6 | assert.equal( itemElem1.style.left, '0px', '2nd item on left' );
7 | assert.equal( itemElem2.style.top, '0px', '3rd item on top' );
8 | });
9 |
--------------------------------------------------------------------------------
/test/unit/is-init-layout.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'initLayout', function( assert ) {
2 | var container = document.querySelector('#is-init-layout');
3 | var pckry = new Packery( container, {
4 | initLayout: false
5 | });
6 | assert.ok( !pckry._isLayoutInited, 'packy is not layout initialized' );
7 | assert.equal( container.children[0].style.left, '', 'no style on first child' );
8 | });
9 |
--------------------------------------------------------------------------------
/test/unit/jquery-plugin.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'jQuery plugin', function( assert ) {
2 | var $ = window.jQuery;
3 | var done = assert.async();
4 | var $elem = $('#jquery');
5 |
6 | assert.ok( $.fn.packery, '.packery is in jQuery.fn namespace' );
7 | assert.equal( typeof $elem.packery, 'function', '.packery is a plugin' );
8 | $elem.packery();
9 | var pckry = $elem.data('packery');
10 | assert.ok( pckry, 'packery instance via .data()' );
11 | assert.equal( pckry, Packery.data( $elem[0] ), 'instance matches the same one via Packery.data()' );
12 |
13 | var item4 = $elem.children()[4];
14 | assert.equal( item4.style.left, '0px', '5th item left' );
15 | assert.equal( item4.style.top, '40px', '5th item top' );
16 |
17 | $elem.children().first().css({
18 | width: 48,
19 | height: 8,
20 | background: 'blue'
21 | });
22 |
23 | $elem.packery( 'on', 'layoutComplete', function() {
24 | assert.ok( true, 'layoutComplete event emitted' );
25 | assert.equal( item4.style.left, '20px', '4th item left after layout' );
26 | assert.equal( item4.style.top, '30px', '4th item top after layout' );
27 | done();
28 | });
29 |
30 | $elem.packery();
31 | });
32 |
--------------------------------------------------------------------------------
/test/unit/layout-extra-big.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'layout extra big', function( assert ) {
2 | var container = document.querySelector('#layout-extra-big');
3 | var pckry = new Packery( container );
4 |
5 | var elem1 = pckry.items[1].element;
6 | var elem2 = pckry.items[2].element;
7 | var elem3 = pckry.items[3].element;
8 | var elem4 = pckry.items[4].element;
9 |
10 | assert.equal( elem1.style.top, '20px', '2nd item top' );
11 | assert.equal( elem2.style.top, '40px', '3rd item top' );
12 | assert.equal( elem3.style.top, '20px', '4th item top' );
13 | assert.equal( elem4.style.top, '60px', '5th item top' );
14 | });
15 |
--------------------------------------------------------------------------------
/test/unit/layout.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'layout', function( assert ) {
2 |
3 | var done = assert.async();
4 |
5 | function checkItemPosition( elem, left, top, message ) {
6 | message = message || '';
7 | var actual = elem.style.left + ' ' + elem.style.top;
8 | var expected = left + 'px ' + top + 'px';
9 | assert.equal( actual, expected, expected + ' ' + message );
10 | }
11 |
12 | var container = document.querySelector('#layout');
13 | var pckry = new Packery( container );
14 | var elem0 = pckry.items[0].element;
15 | var elem1 = pckry.items[1].element;
16 | var elem2 = pckry.items[2].element;
17 | var elem3 = pckry.items[3].element;
18 |
19 | checkItemPosition( elem0, 0, 0, 'first item' );
20 | checkItemPosition( elem1, 40, 0, '2nd item' );
21 | checkItemPosition( elem2, 0, 20, '3rd item' );
22 | assert.equal( container.style.height, '60px', 'height set' );
23 |
24 | // change size of elems to change layout
25 | elem0.style.width = '18px';
26 | elem3.style.height = '58px';
27 | var items = pckry._getItemsForLayout( pckry.items );
28 | pckry.once( 'layoutComplete', function( completeItems ) {
29 | assert.equal( true, true, 'layoutComplete event did fire' );
30 | assert.equal( completeItems.length, items.length, 'event-emitted items matches layout items length' );
31 | assert.strictEqual( completeItems[0], items[0], 'event-emitted items has same first item' );
32 | var len = completeItems.length - 1;
33 | assert.strictEqual( completeItems[ len ], items[ len ], 'event-emitted items has same last item' );
34 | checkItemPosition( elem1, 20, 0, '2nd item' );
35 | checkItemPosition( elem2, 40, 0, '3nd item' );
36 |
37 | setTimeout( checkHorizontal );
38 | });
39 |
40 | pckry.layout();
41 | assert.equal( container.style.height, '80px', 'height set' );
42 |
43 | function checkHorizontal() {
44 | // disable transition
45 | pckry.options.transitionDuration = 0;
46 | pckry.options.horizontal = true;
47 | pckry.layout();
48 | checkItemPosition( elem0, 0, 0, 'horizontal, first item' );
49 | checkItemPosition( elem1, 0, 20, 'horizontal, 2nd item' );
50 | checkItemPosition( elem2, 0, 60, 'horizontal, 2nd item' );
51 | assert.equal( container.style.width, '60px', 'width set' );
52 |
53 | done();
54 | }
55 | });
56 |
--------------------------------------------------------------------------------
/test/unit/prepend.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'prepend', function( assert ) {
2 | var container = document.querySelector('#prepend');
3 | var pckry = new Packery( container );
4 | var itemElemA = pckry.items[0].element;
5 | var itemElemB = pckry.items[1].element;
6 | var itemElemC = gimmeAnItemElement();
7 | itemElemC.style.background = 'orange';
8 | var itemElemD = gimmeAnItemElement();
9 | itemElemD.style.background = 'magenta';
10 |
11 | var done = assert.async();
12 | var ticks = 0;
13 |
14 | pckry.on( 'layoutComplete', function() {
15 | assert.ok( true, 'layoutComplete triggered' );
16 | ticks++;
17 | if ( ticks == 2 ) {
18 | assert.ok( true, '2 layoutCompletes triggered' );
19 | done();
20 | }
21 | });
22 |
23 |
24 | var fragment = document.createDocumentFragment();
25 | fragment.appendChild( itemElemC );
26 | fragment.appendChild( itemElemD );
27 | container.insertBefore( fragment, container.firstChild );
28 | pckry.prepended([ itemElemC, itemElemD ]);
29 |
30 | assert.equal( pckry.items[0].element, itemElemC, 'item C is first' );
31 | assert.equal( pckry.items[1].element, itemElemD, 'item D is second' );
32 | assert.equal( pckry.items[2].element, itemElemA, 'item A is third' );
33 | assert.equal( pckry.items[3].element, itemElemB, 'item B is fourth' );
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/test/unit/remove.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'remove', function( assert ) {
2 | var done = assert.async();
3 | var container = document.querySelector('#add-remove');
4 | // packery starts with 4 items
5 | var pckry = new Packery( container, {
6 | itemSelector: '.item'
7 | });
8 | // remove two items
9 | var w2Elems = container.querySelectorAll('.w2');
10 | pckry.on( 'removeComplete', function( removedItems ) {
11 | assert.equal( true, true, 'removeComplete event did fire' );
12 | assert.equal( removedItems.length, w2Elems.length, 'remove elems length matches 2nd argument length' );
13 | for ( var i=0, len = removedItems.length; i < len; i++ ) {
14 | assert.equal( removedItems[i].element, w2Elems[i], 'removedItems element matches' );
15 | }
16 | assert.equal( container.children.length, 2, 'elements removed from DOM' );
17 | assert.equal( container.querySelectorAll('.w2').length, 0, 'matched elements were removed' );
18 | done();
19 | });
20 |
21 | pckry.remove( w2Elems );
22 | assert.equal( pckry.items.length, 2, 'items removed from Packery instance' );
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/test/unit/stamped.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'stamped1', function( assert ) {
2 | var container = document.querySelector('#stamped1');
3 | var stamps = container.querySelectorAll('.stamp');
4 | var pckry = new Packery( container, {
5 | itemSelector: '.item',
6 | stamp: stamps
7 | });
8 |
9 | assert.equal( pckry.stamps.length, 2, '2 stamped elements' );
10 | var elem0 = pckry.items[0].element;
11 | assert.equal( elem0.style.left, '0px', '1st item left' );
12 | assert.equal( elem0.style.top, '0px', '1st item top' );
13 | var elem1 = pckry.items[1].element;
14 | assert.equal( elem1.style.left, '52px', '2nd item left' );
15 | assert.equal( elem1.style.top, '0px', '2nd item top' );
16 | var elem2 = pckry.items[2].element;
17 | assert.equal( elem2.style.left, '52px', '3rd item left' );
18 | assert.equal( elem2.style.top, '20px', '3rd item top' );
19 | var elem3 = pckry.items[3].element;
20 | assert.equal( elem3.style.left, '13px', '4th item left' );
21 | assert.equal( elem3.style.top, '35px', '4th item top' );
22 |
23 | assert.equal( container.style.height, '75px', 'container height' );
24 |
25 | // unstamp first stamp
26 | pckry.unstamp( stamps[1] );
27 | assert.equal( pckry.stamps.length, 1, 'element was unstamped' );
28 | // stamp it back
29 | pckry.stamp( stamps[1] );
30 | assert.equal( pckry.stamps.length, 2, 'element was stamped back' );
31 |
32 | });
33 |
34 | QUnit.test( 'stamped2, items are stamped', function( assert ) {
35 | var container = document.querySelector('#stamped2');
36 | var stamps = container.querySelectorAll('.stamp');
37 | var pckry = new Packery( container, {
38 | itemSelector: '.item',
39 | stamp: stamps
40 | });
41 |
42 | var done = assert.async();
43 | var layoutItems = pckry._getItemsForLayout( pckry.items );
44 |
45 | assert.equal( layoutItems.length, 7, '7 layout items' );
46 | var elem0 = layoutItems[0].element;
47 | assert.equal( elem0.style.left, '28px', '1st item left' );
48 | assert.equal( elem0.style.top, '0px', '1st item top' );
49 | var elem3 = layoutItems[3].element;
50 | assert.equal( elem3.style.left, '0px', '4th item left' );
51 | assert.equal( elem3.style.top, '28px', '4th item top' );
52 | var elem4 = layoutItems[4].element;
53 | assert.equal( elem4.style.left, '20px', '5th item left' );
54 | assert.equal( elem4.style.top, '40px', '5th item top' );
55 |
56 | // unplacing
57 | pckry.unstamp( stamps );
58 | layoutItems = pckry._getItemsForLayout( pckry.items );
59 | assert.equal( layoutItems.length, 9, '9 layout items' );
60 | assert.equal( pckry.stamps.length, 0, '0 stamps items' );
61 |
62 | pckry.on( 'layoutComplete', function() {
63 | var elem0 = pckry.items[0].element;
64 | assert.equal( elem0.style.left, '0px', '1st item left' );
65 | assert.equal( elem0.style.top, '0px', '1st item top' );
66 | var elem4 = pckry.items[4].element;
67 | assert.equal( elem4.style.left, '0px', '5th item left' );
68 | assert.equal( elem4.style.top, '20px', '5th item top' );
69 | done();
70 | });
71 |
72 | pckry.layout();
73 | });
74 |
75 | QUnit.test( 'stamped3, stamp with selector string ', function( assert ) {
76 | var container3 = document.querySelector('#stamped3');
77 | var pckry3 = new Packery( container3, {
78 | itemSelector: '.item',
79 | stamp: '.stamp'
80 | });
81 |
82 | assert.equal( pckry3.stamps.length, 2, '2 stamped elements' );
83 |
84 | assert.equal( pckry3.stamps.length, 2, '2 stamped elements' );
85 | var elem0 = pckry3.items[0].element;
86 | assert.equal( elem0.style.left, '0px', '1st item left' );
87 | assert.equal( elem0.style.top, '0px', '1st item top' );
88 | var elem1 = pckry3.items[1].element;
89 | assert.equal( elem1.style.left, '52px', '2nd item left' );
90 | assert.equal( elem1.style.top, '0px', '2nd item top' );
91 | var elem2 = pckry3.items[2].element;
92 | assert.equal( elem2.style.left, '52px', '3rd item left' );
93 | assert.equal( elem2.style.top, '20px', '3rd item top' );
94 | var elem3 = pckry3.items[3].element;
95 | assert.equal( elem3.style.left, '13px', '4th item left' );
96 | assert.equal( elem3.style.top, '35px', '4th item top' );
97 |
98 | assert.equal( container3.style.height, '75px', 'container height' );
99 |
100 | var container4 = document.querySelector('#stamped4');
101 | var pckry4 = new Packery( container4, {
102 | itemSelector: '.item',
103 | stamp: 'foobar'
104 | });
105 |
106 | assert.ok( pckry4._isLayoutInited, 'bad selector didnt cause error' );
107 | });
108 |
109 | QUnit.test( 'stamped with borders', function( assert ) {
110 | var container = document.querySelector('#stamped-borders');
111 | var pckry = new Packery( container, {
112 | itemSelector: '.item',
113 | stamp: '.stamp'
114 | });
115 |
116 | var elem0 = pckry.items[0].element;
117 | var elem1 = pckry.items[1].element;
118 | var elem2 = pckry.items[2].element;
119 |
120 | assert.equal( elem0.style.left, '50px', '1st item left' );
121 | assert.equal( elem1.style.left, '50px', '2nd item left' );
122 | assert.equal( elem2.style.top, '30px', '3rd item top' );
123 |
124 | });
125 |
--------------------------------------------------------------------------------
/test/unit/sub-pixel-fit.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'sub-pixel fit', function( assert ) {
2 |
3 | var pckry = new Packery( '#sub-pixel-fit', {
4 | itemSelector: '.item',
5 | transitionDuration: 0
6 | });
7 |
8 | function getItemsTotalY() {
9 | var y = 0;
10 | for ( var i=0, len = pckry.items.length; i < len; i++ ) {
11 | var item = pckry.items[i];
12 | y += item.rect.y;
13 | }
14 | return y;
15 | }
16 |
17 | // iterate over multiple container widths
18 | for ( var containerWidth = 290; containerWidth < 310; containerWidth++ ) {
19 | pckry.element.style.width = containerWidth + 'px';
20 | pckry.layout();
21 | assert.equal( 0, getItemsTotalY(), 'items fit at ' + containerWidth + 'px' );
22 | }
23 |
24 | // set grid sizer and do it again
25 | pckry.options.columnWidth = '.grid-sizer';
26 |
27 | for ( containerWidth = 290; containerWidth < 310; containerWidth++ ) {
28 | pckry.element.style.width = containerWidth + 'px';
29 | pckry.layout();
30 | assert.equal( 0, getItemsTotalY(), 'items fit with columnWidth at ' + containerWidth + 'px' );
31 | }
32 |
33 | });
34 |
--------------------------------------------------------------------------------
/test/unit/test-packer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Packer tests
3 | */
4 |
5 | ( function() {
6 |
7 | QUnit.module('Packer');
8 |
9 | var Packer = Packery.Packer;
10 | var Rect = Packery.Rect;
11 |
12 | QUnit.test( 'basics', function( assert ) {
13 | assert.equal( typeof Packer === 'function', true, 'Packery is a function' );
14 | });
15 |
16 | QUnit.test( 'packing', function( assert ) {
17 | var packr = new Packer( 30, 100 );
18 |
19 | // 112
20 | // 352
21 | // 344
22 | // xxx
23 | // xxx
24 |
25 | var rect1 = new Rect({ width: 20, height: 10 });
26 | var rect2 = new Rect({ width: 10, height: 20 });
27 | var rect3 = new Rect({ width: 10, height: 20 });
28 | var rect4 = new Rect({ width: 20, height: 10 });
29 | var rect5 = new Rect({ width: 10, height: 10 });
30 |
31 | packr.pack( rect1 );
32 | packr.pack( rect2 );
33 | packr.pack( rect3 );
34 | packr.pack( rect4 );
35 | packr.pack( rect5 );
36 |
37 | assert.equal( rect1.x, 0, 'rect1.x top left' );
38 | assert.equal( rect1.y, 0, 'rect1.y top left' );
39 | assert.equal( rect2.x, 20, 'rect2.x top right' );
40 | assert.equal( rect2.y, 0, 'rect2.y top right' );
41 | assert.equal( rect3.x, 0, 'rect3.x bottom left' );
42 | assert.equal( rect3.y, 10, 'rect3.y bottom left' );
43 | assert.equal( rect4.x, 10, 'rect4.x bottom right' );
44 | assert.equal( rect4.y, 20, 'rect4.y bottom right' );
45 | assert.equal( rect5.x, 10, 'rect5.x packed in center' );
46 | assert.equal( rect5.y, 10, 'rect5.y packed in center' );
47 |
48 | // bottom space is open
49 | assert.equal( packr.spaces.length, 1, 'one space open' );
50 | var space = packr.spaces[0];
51 | assert.equal( space.width, 30, 'space.width' );
52 | assert.equal( space.height, 70, 'space.height' );
53 | assert.equal( space.x, 0, 'space.x' );
54 | assert.equal( space.y, 30, 'space.y' );
55 |
56 | });
57 | QUnit.test( 'packing with a placed', function( assert ) {
58 | var packr = new Packer( 30, 100 );
59 |
60 | // 225
61 | // 311
62 | // 34x
63 | // x4x
64 | // xxx
65 | // xxx
66 |
67 | var rect1 = new Rect({
68 | width: 20,
69 | height: 10,
70 | x: 10,
71 | y: 10
72 | });
73 | var rect2 = new Rect({ width: 20, height: 10 });
74 | var rect3 = new Rect({ width: 10, height: 20 });
75 | var rect4 = new Rect({ width: 10, height: 20 });
76 | var rect5 = new Rect({ width: 10, height: 10 });
77 |
78 | packr.placed( rect1 );
79 | packr.pack( rect2 );
80 | packr.pack( rect3 );
81 | packr.pack( rect4 );
82 | packr.pack( rect5 );
83 |
84 | assert.equal( rect2.x, 0, 'rect2.x top left' );
85 | assert.equal( rect2.y, 0, 'rect2.y top left' );
86 | assert.equal( rect3.x, 0, 'rect3.x left side' );
87 | assert.equal( rect3.y, 10, 'rect3.y left side' );
88 | assert.equal( rect4.x, 10, 'rect4.x bottom center' );
89 | assert.equal( rect4.y, 20, 'rect4.y bottom center' );
90 | assert.equal( rect5.x, 20, 'rect5.x packed in top right' );
91 | assert.equal( rect5.y, 0, 'rect5.y packed in top right' );
92 |
93 | assert.equal( packr.spaces.length, 3, '3 spaces left' );
94 |
95 | });
96 |
97 | QUnit.test( 'packing horizontal', function( assert ) {
98 |
99 | function checkRect( rect, x, y ) {
100 | assert.equal( rect.x, x, 'x: ' + x );
101 | assert.equal( rect.y, y, 'y: ' + y );
102 | }
103 |
104 | var packr = new Packer( 100, 30, 'rightwardTopToBottom' );
105 |
106 | // 133xx
107 | // 154xx
108 | // 224xx
109 |
110 | var rect1 = new Rect({ width: 10, height: 20 });
111 | var rect2 = new Rect({ width: 20, height: 10 });
112 | var rect3 = new Rect({ width: 20, height: 10 });
113 | var rect4 = new Rect({ width: 10, height: 20 });
114 | var rect5 = new Rect({ width: 10, height: 10 });
115 |
116 | packr.pack( rect1 );
117 | packr.pack( rect2 );
118 | packr.pack( rect3 );
119 | packr.pack( rect4 );
120 | packr.pack( rect5 );
121 |
122 | checkRect( rect1, 0, 0 );
123 | checkRect( rect2, 0, 20 );
124 | checkRect( rect3, 10, 0 );
125 | checkRect( rect4, 20, 10 );
126 | checkRect( rect5, 10, 10 );
127 |
128 | // bottom space is open
129 | assert.equal( packr.spaces.length, 1, 'one space open' );
130 | var space = packr.spaces[0];
131 | assert.equal( space.width, 70, 'space.width' );
132 | assert.equal( space.height, 30, 'space.height' );
133 | checkRect( space, 30, 0 );
134 |
135 | });
136 |
137 | })();
138 |
--------------------------------------------------------------------------------
/test/unit/test-rect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rect tests
3 | **/
4 |
5 | ( function() {
6 |
7 | var Rect = Packery.Rect;
8 |
9 | QUnit.module('Rect');
10 |
11 | QUnit.test( 'Rect defaults', function( assert ) {
12 | var rect = new Rect();
13 | assert.equal( rect.x, 0, 'rect.x = 0' );
14 | assert.equal( rect.y, 0, 'rect.y = 0' );
15 | assert.equal( rect.width, 0, 'rect.width = 0' );
16 | assert.equal( rect.height, 0, 'rect.height = 0' );
17 | });
18 |
19 | QUnit.test( 'set properties with initial argument object', function( assert ) {
20 | var rect = new Rect({
21 | x: 40,
22 | y: 390,
23 | width: 103,
24 | height: -4
25 | });
26 | assert.equal( rect.x, 40, 'x' );
27 | assert.equal( rect.y, 390, 'y' );
28 | assert.equal( rect.width, 103, 'width' );
29 | assert.equal( rect.height, -4, 'default height property' );
30 | });
31 |
32 | QUnit.test( 'contains', function( assert ) {
33 |
34 | var rectA = new Rect({
35 | x: 10,
36 | y: 30,
37 | width: 100,
38 | height: 400
39 | });
40 |
41 | var rectB = new Rect({
42 | x: 40,
43 | y: 60,
44 | width: 10,
45 | height: 20
46 | });
47 |
48 | assert.strictEqual( rectA.contains( rectB ), true, 'A clearly contains B' );
49 |
50 | rectB = new Rect({
51 | x: 500,
52 | y: 40,
53 | width: 40,
54 | height: 20
55 | });
56 |
57 | assert.strictEqual( rectA.contains( rectB ), false, 'A clearly does not contain B' );
58 |
59 | rectB = new Rect({
60 | x: 20,
61 | y: 40
62 | });
63 |
64 | assert.strictEqual( rectA.contains( rectB ), true,
65 | 'A contains B, which has no width or height' );
66 |
67 | rectB = new Rect({
68 | x: 20,
69 | y: 50,
70 | width: 60,
71 | height: 150
72 | });
73 |
74 | assert.strictEqual( rectA.contains( rectB ), true, 'B is at upper left corner of A' );
75 |
76 | rectB = new Rect({
77 | x: rectA.x,
78 | y: rectA.y,
79 | width: rectA.width,
80 | height: rectA.height
81 | });
82 |
83 | assert.strictEqual( rectA.contains( rectB ), true, 'A contains B. B is equal to A' );
84 |
85 | rectB = new Rect({
86 | x: rectA.x - 20,
87 | y: rectA.y,
88 | width: rectA.width,
89 | height: rectA.height
90 | });
91 |
92 | assert.strictEqual( rectA.contains( rectB ), false,
93 | 'A does not contain B. B same size A, but offset' );
94 |
95 | });
96 |
97 |
98 | QUnit.test( 'overlaps', function( assert ) {
99 |
100 | var rectA = new Rect({
101 | x: 100,
102 | y: 50,
103 | width: 300,
104 | height: 200
105 | });
106 |
107 | var rectB = new Rect({
108 | x: 150,
109 | y: 100,
110 | width: 100,
111 | height: 50
112 | });
113 |
114 | assert.strictEqual( rectA.overlaps( rectB ), true, 'B is inside A, A overlaps B' );
115 | assert.strictEqual( rectB.overlaps( rectA ), true, 'B is inside A, B overlaps A' );
116 |
117 | rectB.x = 50;
118 |
119 | assert.strictEqual( rectA.overlaps( rectB ), true,
120 | 'B overlaps left edge of A, A overlaps B' );
121 | assert.strictEqual( rectB.overlaps( rectA ), true,
122 | 'B overlaps left edge of A, B overlaps A' );
123 |
124 | rectB.y = 25;
125 |
126 | assert.strictEqual( rectA.overlaps( rectB ), true,
127 | 'B overlaps left top corner of A, A overlaps B' );
128 | assert.strictEqual( rectB.overlaps( rectA ), true,
129 | 'B overlaps left top corner of A, B overlaps A' );
130 |
131 | rectB.x = 0;
132 | rectB.y = 0;
133 |
134 | assert.strictEqual( rectA.overlaps( rectB ), false,
135 | 'B bottom right corner meets A top left corner, A DOES NOT overlap B' );
136 | assert.strictEqual( rectB.overlaps( rectA ), false,
137 | 'B bottom right corner meets A top left corner, B DOES NOT overlap A' );
138 |
139 | rectB.x = rectA.x - rectB.width;
140 | rectB.y = rectA.y;
141 | rectB.height = rectA.height;
142 |
143 | assert.strictEqual( rectA.overlaps( rectB ), false,
144 | 'B is completely adjacent to A, A DOES NOT overlap B' );
145 | assert.strictEqual( rectB.overlaps( rectA ), false,
146 | 'B is completely adjacent to A, B DOES NOT overlap A' );
147 |
148 | });
149 |
150 | })();
151 |
--------------------------------------------------------------------------------