diff --git a/examples/masonry.html b/examples/masonry.html
new file mode 100644
index 0000000..20469a9
--- /dev/null
+++ b/examples/masonry.html
@@ -0,0 +1,222 @@
+
+
+
+
+
+ sorting
+
+
+
+
+
+
+
+ sorting
+
+
+
+
+
+
+
+
+
+
+
83
+
Bi
+
Bismuth
+
208.9804
+
+
+
+
+
+
+
+
+
+
81
+
Tl
+
Thallium
+
204.3833
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/isotope.js b/isotope.js
index 3da2b6b..4b44e70 100644
--- a/isotope.js
+++ b/isotope.js
@@ -13,18 +13,26 @@
// -------------------------- helpers -------------------------- //
+// extend objects
+function extend( a, b ) {
+ for ( var prop in b ) {
+ a[ prop ] = b[ prop ];
+ }
+ return a;
+}
+
+
// -------------------------- isotopeDefinition -------------------------- //
// used for AMD definition and requires
-function isotopeDefinition( Outlayer, getSize, matchesSelector, Item ) {
+function isotopeDefinition( Outlayer, getSize, matchesSelector, Item, layoutMode ) {
// create an Outlayer layout class
var Isotope = Outlayer.create( 'isotope', {
sortAscending: true
});
Isotope.Item = Isotope.prototype.settings.item = Item;
-
- Isotope.layoutModes = {};
+ Isotope.layoutMode = layoutMode;
Isotope.prototype._create = function() {
this.itemGUID = 0;
@@ -34,7 +42,7 @@ function isotopeDefinition( Outlayer, getSize, matchesSelector, Item ) {
// create layout modes
this.modes = {};
// create from registered layout modes
- for ( var name in Isotope.layoutModes ) {
+ for ( var name in layoutMode.modes ) {
this._initLayoutMode( name );
}
// keep of track of sortBys
@@ -62,7 +70,12 @@ function isotopeDefinition( Outlayer, getSize, matchesSelector, Item ) {
// -------------------------- layout -------------------------- //
Isotope.prototype._initLayoutMode = function( name ) {
- var LayoutMode = Isotope.layoutModes[ name ];
+ var LayoutMode = layoutMode.modes[ name ];
+ // set mode options
+ // HACK extend initial options, back-fill in default options
+ this.options[ name ] = LayoutMode.options ?
+ extend( LayoutMode.options, this.options[ name ] || {} ) : {};
+ // init layout mode instance
this.modes[ name ] = new LayoutMode( this );
};
@@ -222,10 +235,11 @@ function isotopeDefinition( Outlayer, getSize, matchesSelector, Item ) {
if ( typeof define === 'function' && define.amd ) {
// AMD
define( [
- 'outlayer',
- 'get-size',
- 'matches-selector',
- './item.js'
+ 'outlayer/outlayer',
+ 'get-size/get-size',
+ 'matches-selector/matches-selector',
+ './item.js',
+ './layout-modes.js'
],
isotopeDefinition );
} else {
@@ -234,7 +248,8 @@ if ( typeof define === 'function' && define.amd ) {
window.Outlayer,
window.getSize,
window.matchesSelector,
- window.Isotope.Item
+ window.Isotope.Item,
+ window.Isotope.layoutMode
);
}
diff --git a/layout-mode.js b/layout-mode.js
index 06af062..4e701c6 100644
--- a/layout-mode.js
+++ b/layout-mode.js
@@ -4,20 +4,62 @@
// -------------------------- -------------------------- //
-var Isotope = window.Isotope;
+// var Isotope = window.Isotope;
-Isotope.createLayoutMode = function( namespace, options ) {
+var layoutMode = {};
+
+layoutMode.options = {};
+layoutMode.modes = {};
+
+layoutMode.create = function( namespace, options ) {
function LayoutMode( isotope ) {
this.isotope = isotope;
- // link options to isotope.options
- this.options = isotope && isotope.options[ this.namespace ];
+ // link properties
+ if ( isotope ) {
+ this.options = isotope.options[ this.namespace ];
+ console.log( this.options );
+ this._getMeasurement = isotope._getMeasurement;
+ this.element = isotope.element;
+ this.items = isotope.items;
+ this.size = isotope.size;
+ // this.getSize = isotope.getSize;
+ // this._getElementOffset = isotope._getElementOffset;
+ }
}
+
+ // default options
+ if ( options ) {
+ LayoutMode.options = options;
+ }
+
+ // Outlayer.prototype._getMeasurement = function( measurement, size ) {
+ // var option = this.options[ measurement ];
+ // var elem;
+ // if ( !option ) {
+ // // default to 0
+ // this[ measurement ] = 0;
+ // } else {
+ // if ( typeof option === 'string' ) {
+ // elem = this.element.querySelector( option );
+ // } else if ( isElement( option ) ) {
+ // elem = option;
+ // }
+ // // use size of element, if element
+ // this[ measurement ] = elem ? getSize( elem )[ size ] : option;
+ // }
+ // };
+
+
+
LayoutMode.prototype.namespace = namespace;
// set default options
- Isotope.prototype.options[ namespace ] = options || {};
+ // layoutMode.options[ namespace ] = options || {};
// register in Isotope
- Isotope.layoutModes[ namespace ] = LayoutMode;
+ layoutMode.modes[ namespace ] = LayoutMode;
return LayoutMode;
};
+var Isotope = window.Isotope = window.Isotope || {};
+Isotope.layoutMode = layoutMode;
+
})( window );
diff --git a/layout-modes/masonry.js b/layout-modes/masonry.js
new file mode 100644
index 0000000..b2738c7
--- /dev/null
+++ b/layout-modes/masonry.js
@@ -0,0 +1,211 @@
+/*!
+ * Masonry v3.1.1
+ * Cascading grid layout library
+ * http://masonry.desandro.com
+ * MIT License
+ * by David DeSandro
+ */
+
+( function( window ) {
+
+'use strict';
+
+// vars
+// var document = window.document;
+
+// -------------------------- helpers -------------------------- //
+
+var indexOf = Array.prototype.indexOf ?
+ function( items, value ) {
+ return items.indexOf( value );
+ } :
+ function ( items, value ) {
+ for ( var i=0, len = items.length; i < len; i++ ) {
+ var item = items[i];
+ if ( item === value ) {
+ return i;
+ }
+ }
+ return -1;
+ };
+
+// -------------------------- masonryDefinition -------------------------- //
+
+// used for AMD definition and requires
+function masonryDefinition( layoutMode, getSize ) {
+ // create an Outlayer layout class
+ var Masonry = layoutMode.create('masonry');
+
+ Masonry.prototype._resetLayout = function() {
+ this.isotope.getSize();
+ this._getMeasurement( 'columnWidth', 'outerWidth' );
+ this._getMeasurement( 'gutter', 'outerWidth' );
+ this.measureColumns();
+
+ // reset column Y
+ var i = this.cols;
+ this.colYs = [];
+ while (i--) {
+ this.colYs.push( 0 );
+ }
+
+ this.maxY = 0;
+ };
+
+ Masonry.prototype.measureColumns = function() {
+ var container = this._getSizingContainer();
+ // if columnWidth is 0, default to outerWidth of first item
+ var firstItem = this.items[0];
+ var firstItemElem = firstItem && firstItem.element;
+ if ( !this.columnWidth ) {
+ // columnWidth fall back to item of first element
+ this.columnWidth = firstItemElem ? getSize( firstItemElem ).outerWidth :
+ // or size of container
+ this.size.innerWidth;
+ }
+ this.columnWidth += this.gutter;
+
+ this._containerWidth = getSize( container ).innerWidth;
+ this.cols = Math.floor( ( this._containerWidth + this.gutter ) / this.columnWidth );
+ this.cols = Math.max( this.cols, 1 );
+ };
+
+ Masonry.prototype._getSizingContainer = function() {
+ return this.options.isFitWidth ? this.element.parentNode : this.element;
+ };
+
+ Masonry.prototype._getItemLayoutPosition = function( item ) {
+ item.getSize();
+ // how many columns does this brick span
+ var colSpan = Math.ceil( item.size.outerWidth / this.columnWidth );
+ colSpan = Math.min( colSpan, this.cols );
+
+ var colGroup = this._getColGroup( colSpan );
+ // get the minimum Y value from the columns
+ var minimumY = Math.min.apply( Math, colGroup );
+ var shortColIndex = indexOf( colGroup, minimumY );
+
+ // position the brick
+ var position = {
+ x: this.columnWidth * shortColIndex,
+ y: minimumY
+ };
+
+ // apply setHeight to necessary columns
+ var setHeight = minimumY + item.size.outerHeight;
+ var setSpan = this.cols + 1 - colGroup.length;
+ for ( var i = 0; i < setSpan; i++ ) {
+ this.colYs[ shortColIndex + i ] = setHeight;
+ }
+
+ return position;
+ };
+
+ /**
+ * @param {Number} colSpan - number of columns the element spans
+ * @returns {Array} colGroup
+ */
+ Masonry.prototype._getColGroup = function( colSpan ) {
+ if ( colSpan === 1 ) {
+ // if brick spans only one column, use all the column Ys
+ return this.colYs;
+ }
+
+ var colGroup = [];
+ // how many different places could this brick fit horizontally
+ var groupCount = this.cols + 1 - colSpan;
+ // for each group potential horizontal position
+ for ( var i = 0; i < groupCount; i++ ) {
+ // make an array of colY values for that one group
+ var groupColYs = this.colYs.slice( i, i + colSpan );
+ // and get the max value of the array
+ colGroup[i] = Math.max.apply( Math, groupColYs );
+ }
+ return colGroup;
+ };
+
+ Masonry.prototype._manageStamp = function( stamp ) {
+ var stampSize = getSize( stamp );
+ var offset = this.isotope._getElementOffset( stamp );
+ // get the columns that this stamp affects
+ var firstX = this.isotope.options.isOriginLeft ? offset.left : offset.right;
+ var lastX = firstX + stampSize.outerWidth;
+ var firstCol = Math.floor( firstX / this.columnWidth );
+ firstCol = Math.max( 0, firstCol );
+ var lastCol = Math.floor( lastX / this.columnWidth );
+ lastCol = Math.min( this.cols - 1, lastCol );
+ // set colYs to bottom of the stamp
+ var stampMaxY = ( this.isotope.options.isOriginTop ? offset.top : offset.bottom ) +
+ stampSize.outerHeight;
+ for ( var i = firstCol; i <= lastCol; i++ ) {
+ this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
+ }
+ };
+
+ Masonry.prototype._getContainerSize = function() {
+ this.maxY = Math.max.apply( Math, this.colYs );
+ var size = {
+ height: this.maxY
+ };
+
+ if ( this.options.isFitWidth ) {
+ size.width = this._getContainerFitWidth();
+ }
+
+ return size;
+ };
+
+ Masonry.prototype._getContainerFitWidth = function() {
+ var unusedCols = 0;
+ // count unused columns
+ var i = this.cols;
+ while ( --i ) {
+ if ( this.colYs[i] !== 0 ) {
+ break;
+ }
+ unusedCols++;
+ }
+ // fit container to columns that have been used
+ return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
+ };
+
+ // debounced, layout on resize
+ // HEADS UP this overwrites Outlayer.resize
+ // Any changes in Outlayer.resize need to be manually added here
+ Masonry.prototype.resize = function() {
+ // don't trigger if size did not change
+ var container = this._getSizingContainer();
+ var size = getSize( container );
+ // check that this.size and size are there
+ // IE8 triggers resize on body size change, so they might not be
+ var hasSizes = this.size && size;
+ if ( hasSizes && size.innerWidth === this._containerWidth ) {
+ return;
+ }
+
+ this.layout();
+
+ delete this.isotope.resizeTimeout;
+ };
+
+ return Masonry;
+}
+
+// -------------------------- transport -------------------------- //
+
+if ( typeof define === 'function' && define.amd ) {
+ // AMD
+ define( [
+ '../isotope',
+ 'get-size/get-size'
+ ],
+ masonryDefinition );
+} else {
+ // browser global
+ masonryDefinition(
+ window.Isotope.layoutMode,
+ window.getSize
+ );
+}
+
+})( window );
diff --git a/notes.md b/notes.md
index c264b32..ffb28b7 100644
--- a/notes.md
+++ b/notes.md
@@ -11,3 +11,10 @@ filter on init doesn't do transition
sortAscending
sortAscending for object
+
+
+---
+
+set default options somewhere
+
+new layout mode comes with those options by default
\ No newline at end of file