Browse Source

Masonry layout working in widget form

pull/14/head
David DeSandro 14 years ago
parent
commit
1c4063b687
  1. 233
      src/jquery.molequul-widget.js

233
src/jquery.molequul-widget.js

@ -56,8 +56,8 @@
}, },
_filterFind: function( selector ) { _filterFind: function( $elems, selector ) {
return selector ? this.find( selector ).add( this.find( selector ) ) : this; return selector ? $elems.filter( selector ).add( $elems.find( selector ) ) : $elems;
}, },
// sets up widget // sets up widget
@ -70,7 +70,7 @@
this.styleQueue = []; this.styleQueue = [];
this.elemCount = 0; this.elemCount = 0;
// need to get atoms // need to get atoms
this.atoms.$all = this.$elem.children().molequul('_filterFind', this.options.itemSelector ); this.atoms.$all = this._filterFind( this.$elem.children(), this.options.itemSelector );
// console.log( 'all atoms', this.atoms.$all.length ) // console.log( 'all atoms', this.atoms.$all.length )
@ -97,16 +97,19 @@
this.usingTransforms = Modernizr.csstransforms && Modernizr.csstransitions && !jQueryAnimation; this.usingTransforms = Modernizr.csstransforms && Modernizr.csstransitions && !jQueryAnimation;
this.positionFn = this.usingTransforms ? Molequul.translate : Molequul.positionAbs; this.positionFn = this.usingTransforms ? this._translate : this._positionAbs;
// sorting // sorting
var originalOrderSorter = { var originalOrderSorter = {
'original-order' : function( $elem ) { 'original-order' : function( $elem ) {
return props.elemCount; return this.elemCount;
} }
}; };
this.options.getSortData = $.extend( originalOrderSorter, this.options.getSortData ); this.options.getSortData = $.extend( originalOrderSorter, this.options.getSortData );
this._setupAtoms( this.atoms.$all );
this.atoms.$all.molequul( '_setupAtoms' ); this.atoms.$all.molequul( '_setupAtoms' );
@ -118,10 +121,9 @@
$cursor.remove(); $cursor.remove();
// add molequul class first time around // add molequul class first time around
var $container = this.$elem, var instance = this;
containerClass = this.options.containerClass;
setTimeout( function() { setTimeout( function() {
$container.addClass( containerClass ); instance.$elem.addClass( instance.options.containerClass );
}, 0 ); }, 0 );
// console.log( this.options.layoutMode ) // console.log( this.options.layoutMode )
@ -133,17 +135,57 @@
// save data // save data
// this.data( 'molequul', props ); // this.data( 'molequul', props );
// bind resize method
if ( this.options.resizeable ) {
$(window).bind('smartresize.molequul', function() { instance.$elem.molequul('resize') } );
}
},
_isNewProp : function( prop ) {
return this.prevOpts ? ( this.options[ prop ] !== this.prevOpts[ prop ] ) : true;
}, },
// _init fires when your instance is first created // _init fires when your instance is first created
// (from the constructor above), and when you // (from the constructor above), and when you
// attempt to initialize the widget again (by the bridge) // attempt to initialize the widget again (by the bridge)
// after it has already been initialized. // after it has already been initialized.
_init: function(){ _init : function( callback ) {
// this.options = $.extend( true, Molequul.options, this.options ); // this.options = $.extend( true, Molequul.options, this.options );
console.log( this ) // check if watched properties are new
var instance = this;
$.each( [ 'filter', 'sortBy', 'sortDir' ], function( i, propName ){
// console.log( propName, this );
instance.isNew[ propName ] = instance._isNewProp( propName );
});
if ( this.isNew.filter ) {
this.atoms.$filtered = this._filter( this.atoms.$all )
} else {
this.atoms.$filtered = this.atoms.$all;
}
if ( this.isNew.filter || this.isNew.sortBy || this.isNew.sortDir ) {
this._sort();
}
// console.log( '_' + this.options.layoutMode + 'ResetLayoutProps' )
// this[ '_' + this.options.layoutMode + 'ResetLayoutProps' ]();
// console.log( $.data( this.$elem, 'molequul') );
// setTimeout( function(){
// this.layout( this.atoms.$filtered, callback );
this.reLayout( callback );
// instance.$elem.molequul( 'layout', instance.atoms.$filtered, callback );
// }, 0)
// console.log( this )
// init code // init code
}, },
@ -171,21 +213,15 @@
isNewProp : function( property, props ) {
if ( !props.initialized ) {
return true;
}
var previousProp = props.prevOpts[ property ];
return ( props.opts[ property ] !== previousProp );
},
// ====================== Adding ====================== // ====================== Adding ======================
addSortData : function( props ) { _addSortData : function( $atoms ) {
return this.each(function(){ var instance = this;
$atoms.each(function(){
var $this = $(this), var $this = $(this),
sortData = {}, sortData = {},
getSortData = props.opts.getSortData, getSortData = instance.options.getSortData,
key; key;
// get value for sort data based on fn( $elem ) passed in // get value for sort data based on fn( $elem ) passed in
for ( key in getSortData ) { for ( key in getSortData ) {
@ -194,58 +230,63 @@
// apply sort data to $element // apply sort data to $element
$this.data( 'molequul-sort-data', sortData ); $this.data( 'molequul-sort-data', sortData );
// increment element count // increment element count
props.elemCount ++; instance.elemCount ++;
}); });
}, },
setupAtoms : function( props ) { _setupAtoms : function( $atoms ) {
// base style for atoms // base style for atoms
var atomStyle = { position: 'absolute' }; var atomStyle = { position: 'absolute' };
if ( props.usingTransforms ) { if ( this.usingTransforms ) {
atomStyle.left = 0; atomStyle.left = 0;
atomStyle.top = 0; atomStyle.top = 0;
} }
$atoms.css( atomStyle );
// add sort data to each elem // add sort data to each elem
return this.molequul( 'addSortData', props ).css( atomStyle ); this._addSortData( $atoms );
// return this.molequul( 'addSortData', props ).css( atomStyle );
}, },
// ====================== Filtering ====================== // ====================== Filtering ======================
filter : function( $atoms ) { _filter : function( $atoms ) {
var props = this.data('molequul'), var $filteredAtoms,
filter = props.opts.filter === '' ? '*' : props.opts.filter; filter = this.options.filter === '' ? '*' : this.options.filter;
if ( !filter ) { if ( !filter ) {
props.atoms.$filtered = $atoms; $filteredAtoms = $atoms;
} else { } else {
var hiddenClass = props.opts.hiddenClass, var hiddenClass = this.options.hiddenClass,
hiddenSelector = '.' + hiddenClass, hiddenSelector = '.' + hiddenClass,
$visibleAtoms = $atoms.not( hiddenSelector ), $visibleAtoms = $atoms.not( hiddenSelector ),
$hiddenAtoms = $atoms.filter( hiddenSelector ), $hiddenAtoms = $atoms.filter( hiddenSelector ),
$atomsToShow = $hiddenAtoms; $atomsToShow = $hiddenAtoms;
props.atoms.$filtered = $atoms.filter( filter ); $filteredAtoms = $atoms.filter( filter );
if ( filter !== '*' ) { if ( filter !== '*' ) {
$atomsToShow = $hiddenAtoms.filter( filter ); $atomsToShow = $hiddenAtoms.filter( filter );
var $atomsToHide = $visibleAtoms.not( filter ).toggleClass( hiddenClass ); var $atomsToHide = $visibleAtoms.not( filter ).toggleClass( hiddenClass );
$atomsToHide.addClass( hiddenClass ); $atomsToHide.addClass( hiddenClass );
props.styleQueue.push({ $el: $atomsToHide, style: props.opts.hiddenStyle }); this.styleQueue.push({ $el: $atomsToHide, style: this.options.hiddenStyle });
} }
props.styleQueue.push({ $el: $atomsToShow, style: props.opts.visibleStyle }); this.styleQueue.push({ $el: $atomsToShow, style: this.options.visibleStyle });
$atomsToShow.removeClass( hiddenClass ); $atomsToShow.removeClass( hiddenClass );
} }
return this; return $filteredAtoms;
}, },
// ====================== Sorting ====================== // ====================== Sorting ======================
getSortFn : function( sortBy, sortDir ) { _getSortFn : function( sortBy, sortDir ) {
var getSorter = function( elem ) { var getSorter = function( elem ) {
return $(elem).data('molequul-sort-data')[ sortBy ]; return $(elem).data('molequul-sort-data')[ sortBy ];
}; };
@ -261,12 +302,11 @@
}, },
// used on all the filtered atoms, $atoms.filtered // used on all the filtered atoms, $atoms.filtered
sort : function( props ) { _sort : function() {
var sortFn = props.opts.sortBy === 'random' ? $.molequul.randomSortFn : var sortFn = this._getSortFn( this.options.sortBy, this.options.sortDir );
$.molequul.getSortFn( props.opts.sortBy, props.opts.sortDir );
props.atoms.$filtered.sort( sortFn ); this.atoms.$filtered.sort( sortFn );
return this; return this;
}, },
@ -275,30 +315,29 @@
// ====================== Layout ====================== // ====================== Layout ======================
translate : function( x, y ) { _translate : function( x, y ) {
return { translate : [ x, y ] }; return { translate : [ x, y ] };
}, },
positionAbs : function( x, y ) { _positionAbs : function( x, y ) {
return { left: x, top: y }; return { left: x, top: y };
}, },
pushPosition : function( x, y, props ) { _pushPosition : function( $elem, x, y ) {
var position = props.positionFn( x, y ); var position = this.positionFn( x, y );
props.styleQueue.push({ $el: this, style: position }); this.styleQueue.push({ $el: $elem, style: position });
return this;
}, },
// ====================== masonry ====================== // ====================== masonry ======================
placeBrick : function( setCount, setY, props ) { _placeBrick : function( $brick, setCount, setY ) {
// here, `this` refers to a child element or "brick" // here, `this` refers to a child element or "brick"
// get the minimum Y value from the columns // get the minimum Y value from the columns
var minimumY = Math.min.apply( Math, setY ), var minimumY = Math.min.apply( Math, setY ),
setHeight = minimumY + this.outerHeight(true), setHeight = minimumY + $brick.outerHeight(true),
i = setY.length, i = setY.length,
shortCol = i, shortCol = i,
setSpan = props.colCount + 1 - i, setSpan = this.colCount + 1 - i,
x, y ; x, y ;
// Which column has the minY value, closest to the left // Which column has the minY value, closest to the left
while (i--) { while (i--) {
@ -308,16 +347,15 @@
} }
// position the brick // position the brick
x = props.colW * shortCol + props.posLeft; x = this.colW * shortCol + this.posLeft;
y = minimumY; y = minimumY;
this.molequul( 'pushPosition', x, y, props ); this._pushPosition( $brick, x, y );
// apply setHeight to necessary columns // apply setHeight to necessary columns
for ( i=0; i < setSpan; i++ ) { for ( i=0; i < setSpan; i++ ) {
props.colYs[ shortCol + i ] = setHeight; this.colYs[ shortCol + i ] = setHeight;
} }
return this;
}, },
masonrySingleColumn : function( props ) { masonrySingleColumn : function( props ) {
@ -327,32 +365,33 @@
}, },
masonryMultiColumn : function( props ) { _masonryMultiColumn : function( $elems ) {
return this.each(function(){ var instance = this;
$elems.each(function(){
var $this = $(this), var $this = $(this),
//how many columns does this brick span //how many columns does this brick span
colSpan = Math.ceil( $this.outerWidth(true) / props.colW ); colSpan = Math.ceil( $this.outerWidth(true) / instance.colW );
colSpan = Math.min( colSpan, props.colCount ); colSpan = Math.min( colSpan, instance.colCount );
if ( colSpan === 1 ) { if ( colSpan === 1 ) {
// if brick spans only one column, just like singleMode // if brick spans only one column, just like singleMode
$this.molequul( 'placeBrick', props.colCount, props.colYs, props ); instance._placeBrick( $this, instance.colCount, instance.colYs );
} else { } else {
// brick spans more than one column // brick spans more than one column
// how many different places could this brick fit horizontally // how many different places could this brick fit horizontally
var groupCount = props.colCount + 1 - colSpan, var groupCount = instance.colCount + 1 - colSpan,
groupY = [], groupY = [],
groupColY; groupColY;
// for each group potential horizontal position // for each group potential horizontal position
for ( var i=0; i < groupCount; i++ ) { for ( var i=0; i < groupCount; i++ ) {
// make an array of colY values for that one group // make an array of colY values for that one group
groupColY = props.colYs.slice( i, i+colSpan ); groupColY = instance.colYs.slice( i, i+colSpan );
// and get the max value of the array // and get the max value of the array
groupY[i] = Math.max.apply( Math, groupColY ); groupY[i] = Math.max.apply( Math, groupColY );
} }
$this.molequul( 'placeBrick', groupCount, groupY, props ); instance._placeBrick( $this, groupCount, groupY );
} }
}); });
}, },
@ -366,43 +405,37 @@
window.console && console.error('Column width calculated to be zero. Stopping Molequul plugin before divide by zero. Check that the width of first child inside the molequul container is not zero.'); window.console && console.error('Column width calculated to be zero. Stopping Molequul plugin before divide by zero. Check that the width of first child inside the molequul container is not zero.');
return this; return this;
} }
this.width = this.$elem.width();
this.colCount = Math.floor( this.width / this.colW ) ; this.colCount = Math.floor( this.width / this.colW ) ;
this.colCount = Math.max( this.colCount, 1 ); this.colCount = Math.max( this.colCount, 1 );
return this; return this;
}, },
masonryResetLayoutProps : function( props ) { _masonryResetLayoutProps : function() {
var i = this.colCount;
var i = props.colCount; this.colYs = [];
props.colYs = [];
while (i--) { while (i--) {
props.colYs.push( props.posTop ); this.colYs.push( this.posTop );
} }
return this; return this;
}, },
masonryResize : function( props ) { _masonryResize : function() {
var prevColCount = props.colCount; var prevColCount = this.colCount;
// get updated colCount // get updated colCount
this.molequul( 'getMasonryColCount', props ); this._getMasonryColCount();
if ( props.colCount !== prevColCount ) { if ( this.colCount !== prevColCount ) {
// if column count has changed, do a new column cound // if column count has changed, do a new column cound
this.molequul( 'reLayout', props ); this.reLayout();
} }
return this; return this;
}, },
masonryMeasureContainerHeight : function( props ) { _masonryMeasureContainerHeight : function() {
props.containerHeight = Math.max.apply( Math, props.colYs ) - props.posTop; this.containerHeight = Math.max.apply( Math, this.colYs ) - this.posTop;
return this;
},
_masonrySetup : function( props ) {
this.molequul('getMasonryColCount', props );
return this;
}, },
@ -447,9 +480,8 @@
return this; return this;
}, },
clearFloatMeasureContainerHeight : function ( props ) { _clearFloatMeasureContainerHeight : function () {
props.containerHeight = props.clearFloat.height; this.containerHeight = this.clearFloat.height;
return this;
}, },
clearFloatResize : function( props ) { clearFloatResize : function( props ) {
@ -465,36 +497,37 @@
// accepts atoms-to-be-laid-out to start with // accepts atoms-to-be-laid-out to start with
layout : function( $elems, callback ) { layout : function( $elems, callback ) {
var props = this.data('molequul'), var layoutMode = this.options.layoutMode,
layoutMode = props.opts.layoutMode,
layoutMethod = layoutMode; layoutMethod = layoutMode;
// layout logic // layout logic
if ( layoutMethod === 'masonry' ) { if ( layoutMethod === 'masonry' ) {
layoutMethod = props.opts.masonrySingleMode ? 'masonrySingleColumn' : 'masonryMultiColumn'; layoutMethod = this.options.masonrySingleMode ? '_masonrySingleColumn' : '_masonryMultiColumn';
} }
$elems.molequul( layoutMethod, props ); this[ layoutMethod ]( $elems );
// $elems.molequul( layoutMethod, props );
// set the height of the container to the tallest column // set the height of the container to the tallest column
this.molequul( layoutMode + 'MeasureContainerHeight', props ); this[ '_' + layoutMode + 'MeasureContainerHeight' ]();
var containerStyle = { height: props.containerHeight }; var containerStyle = { height: this.containerHeight };
props.styleQueue.push({ $el: this, style: containerStyle }); this.styleQueue.push({ $el: this.$elem, style: containerStyle });
// are we animating the layout arrangement? // are we animating the layout arrangement?
// use plugin-ish syntax for css or animate // use plugin-ish syntax for css or animate
var styleFn = ( props.applyStyleFnName === 'animate' && !props.initialized ) ? var styleFn = ( this.applyStyleFnName === 'animate' && !$.data( this.$elem, 'molequul' ) ) ?
'css' : props.applyStyleFnName, 'css' : this.applyStyleFnName,
animOpts = props.opts.animationOptions; animOpts = this.options.animationOptions;
// process styleQueue // process styleQueue
$.each( props.styleQueue, function( i, obj ){ $.each( this.styleQueue, function( i, obj ){
// have to extend animation to play nice with jQuery // have to extend animation to play nice with jQuery
obj.$el[ styleFn ]( obj.style, $.extend( {}, animOpts ) ); obj.$el[ styleFn ]( obj.style, $.extend( {}, animOpts ) );
}); });
@ -502,7 +535,7 @@
// clear out queue for next time // clear out queue for next time
props.styleQueue = []; this.styleQueue = [];
// provide $elems as context for the callback // provide $elems as context for the callback
if ( callback ) { if ( callback ) {
@ -514,23 +547,19 @@
resize : function() { resize : function() {
var props = this.data('molequul'); return this[ '_' + this.options.layoutMode + 'Resize' ]();
return this.molequul( props.opts.layoutMode + 'Resize', props );
}, },
reLayout : function( props ) { reLayout : function( callback ) {
props = props || this.data('molequul'); console.log('layout again')
props.initialized = true; this
return this [ '_' + this.options.layoutMode + 'ResetLayoutProps' ]()
.molequul( props.opts.layoutMode + 'ResetLayoutProps', props ) .layout( this.atoms.$filtered, callback )
.molequul( 'layout', props.atoms.$filtered );
}, },
// ====================== Setup and Init ====================== // ====================== Setup and Init ======================
watchedProps : [ 'filter', 'sortBy', 'sortDir', 'layoutMode' ],
init : function( options, callback ) { init : function( options, callback ) {

Loading…
Cancel
Save