mirror of https://github.com/metafizzy/isotope
Filter & sort magical layouts
http://isotope.metafizzy.co
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
534 lines
16 KiB
534 lines
16 KiB
15 years ago
|
/*************************************************
|
||
15 years ago
|
** jQuery Molequul version 0.1
|
||
15 years ago
|
** Copyright David DeSandro, licensed MIT
|
||
15 years ago
|
** http://desandro.com/resources/jquery-molequul
|
||
15 years ago
|
**************************************************/
|
||
|
(function($){
|
||
|
|
||
15 years ago
|
// ========================= miniModernizr ===============================
|
||
|
// <3<3<3 and thanks to Faruk and Paul for doing the heavy lifting
|
||
15 years ago
|
if ( !Modernizr ) {
|
||
|
|
||
|
var miniModernizr = {},
|
||
|
vendorCSSPrefixes = ' -o- -moz- -ms- -webkit- -khtml- '.split(' '),
|
||
|
classes = [],
|
||
|
docElement = document.documentElement,
|
||
|
m = document.createElement( 'modernizr' ),
|
||
|
m_style = m.style,
|
||
|
test_props = function ( props, callback ) {
|
||
|
for ( var i in props ) {
|
||
|
// IE9 ugliness
|
||
|
try {
|
||
|
m_style[ props[i] ] !== undefined
|
||
|
} catch(e){
|
||
|
continue;
|
||
|
}
|
||
15 years ago
|
|
||
15 years ago
|
console.log( props[i], m_style[ props[i] ] )
|
||
|
|
||
15 years ago
|
if ( m_style[ props[i] ] !== undefined ) {
|
||
|
return true;
|
||
|
}
|
||
15 years ago
|
}
|
||
|
},
|
||
15 years ago
|
test_props_all = function ( prop, callback ) {
|
||
|
var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
|
||
15 years ago
|
|
||
15 years ago
|
props = [ prop, 'Webkit' + uc_prop, 'Moz' + uc_prop,
|
||
|
'O' + uc_prop, 'ms' + uc_prop, 'Khtml' + uc_prop ];
|
||
15 years ago
|
|
||
|
|
||
15 years ago
|
return !!test_props( props, callback );
|
||
15 years ago
|
},
|
||
15 years ago
|
tests = {
|
||
|
csstransforms : function() {
|
||
|
return !!test_props([ 'transformProperty', 'WebkitTransform',
|
||
|
'MozTransform', 'OTransform', 'msTransform' ]);
|
||
|
},
|
||
|
csstransforms3d : function() {
|
||
|
var ret = !!test_props([ 'perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective' ]);
|
||
|
|
||
|
if (ret){
|
||
|
var st = document.createElement('style'),
|
||
|
div = document.createElement('div');
|
||
|
|
||
|
st.textContent = '@media ('+vendorCSSPrefixes.join('transform-3d),(') +
|
||
|
'modernizr){#modernizr{height:3px}}';
|
||
|
document.getElementsByTagName('head')[0].appendChild(st);
|
||
|
div.id = 'modernizr';
|
||
|
docElement.appendChild(div);
|
||
|
|
||
|
ret = div.offsetHeight === 3;
|
||
|
|
||
|
st.parentNode.removeChild(st);
|
||
|
div.parentNode.removeChild(div);
|
||
|
}
|
||
|
return ret;
|
||
|
},
|
||
|
csstransitions : function() {
|
||
|
return test_props_all( 'transitionProperty' );
|
||
|
}
|
||
|
};
|
||
15 years ago
|
|
||
15 years ago
|
// hasOwnProperty shim by kangax needed for Safari 2.0 support
|
||
|
var _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty;
|
||
|
if (typeof _hasOwnProperty !== 'undefined' && typeof _hasOwnProperty.call !== 'undefined') {
|
||
|
hasOwnProperty = function (object, property) {
|
||
|
return _hasOwnProperty.call(object, property);
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
hasOwnProperty = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
|
||
|
return ((property in object) && typeof object.constructor.prototype[property] === 'undefined');
|
||
|
};
|
||
|
}
|
||
15 years ago
|
|
||
15 years ago
|
// Run through all tests and detect their support in the current UA.
|
||
|
for ( var feature in tests ) {
|
||
|
if ( hasOwnProperty( tests, feature ) ) {
|
||
|
// run the test, throw the return value into the Modernizr,
|
||
|
// then based on that boolean, define an appropriate className
|
||
|
// and push it into an array of classes we'll join later.
|
||
|
var test = tests[ feature ]();
|
||
|
miniModernizr[ feature.toLowerCase() ] = test;
|
||
|
var className = ( test ? '' : 'no-' ) + feature.toLowerCase()
|
||
|
classes.push( className );
|
||
|
}
|
||
15 years ago
|
}
|
||
|
|
||
15 years ago
|
// Add the new classes to the <html> element.
|
||
|
docElement.className += ' ' + classes.join( ' ' );
|
||
|
|
||
|
var Modernizr = miniModernizr;
|
||
|
}
|
||
|
|
||
15 years ago
|
|
||
15 years ago
|
// position convience method
|
||
|
// for now, we'll only use transforms in Chrome and Safari
|
||
|
// In Opera, transform removes all text anti-aliasing, crippling legibility
|
||
|
// in FF, you cannot transition transforms in < 4.0
|
||
|
var usingTransforms = Modernizr.csstransforms && $.browser.webkit;
|
||
15 years ago
|
|
||
|
|
||
|
// ========================= smartresize ===============================
|
||
|
|
||
15 years ago
|
/*!
|
||
|
* smartresize: debounced resize event for jQuery
|
||
|
* http://github.com/lrbabe/jquery-smartresize
|
||
|
*
|
||
|
* Copyright (c) 2009 Louis-Remi Babe
|
||
|
* Licensed under the GPL license.
|
||
|
* http://docs.jquery.com/License
|
||
|
*
|
||
|
*/
|
||
|
var $event = $.event,
|
||
|
resizeTimeout;
|
||
|
|
||
|
$event.special.smartresize = {
|
||
|
setup: function() {
|
||
|
$(this).bind( "resize", $event.special.smartresize.handler );
|
||
|
},
|
||
|
teardown: function() {
|
||
|
$(this).unbind( "resize", $event.special.smartresize.handler );
|
||
|
},
|
||
|
handler: function( event, execAsap ) {
|
||
|
// Save the context
|
||
|
var context = this,
|
||
|
args = arguments;
|
||
|
|
||
|
// set correct event type
|
||
|
event.type = "smartresize";
|
||
|
|
||
|
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
|
||
|
resizeTimeout = setTimeout(function() {
|
||
|
jQuery.event.handle.apply( context, args );
|
||
|
}, execAsap === "execAsap"? 0 : 100 );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
$.fn.smartresize = function( fn ) {
|
||
|
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
|
||
|
};
|
||
|
|
||
15 years ago
|
|
||
15 years ago
|
// ========================= molequul ===============================
|
||
15 years ago
|
|
||
15 years ago
|
var molequulMethods = {
|
||
15 years ago
|
|
||
15 years ago
|
|
||
15 years ago
|
filter : function( $cards ) {
|
||
15 years ago
|
var props = this.data('molequul'),
|
||
15 years ago
|
filter = props.opts.filter === '' ? '*' : props.opts.filter;
|
||
15 years ago
|
|
||
|
if ( !filter ) {
|
||
|
props.$cards.filtered = $cards;
|
||
|
} else {
|
||
|
var hiddenClass = props.opts.hiddenClass,
|
||
|
hiddenSelector = '.' + hiddenClass,
|
||
|
$visibleCards = $cards.not( hiddenSelector ),
|
||
|
$hiddenCards = $cards.filter( hiddenSelector ),
|
||
|
$cardsToShow = $hiddenCards;
|
||
|
|
||
|
props.$cards.filtered = $cards.filter( filter );
|
||
|
|
||
15 years ago
|
if ( filter !== '*' ) {
|
||
15 years ago
|
$cardsToShow = $hiddenCards.filter( filter );
|
||
15 years ago
|
|
||
|
var $cardsToHide = $visibleCards.not( filter ).toggleClass( hiddenClass );
|
||
|
$cardsToHide.addClass( hiddenClass );
|
||
15 years ago
|
props.styleQueue.push({ $el: $cardsToHide, style: props.opts.hiddenStyle });
|
||
|
}
|
||
|
|
||
|
props.styleQueue.push({ $el: $cardsToShow, style: props.opts.visibleStyle });
|
||
15 years ago
|
$cardsToShow.removeClass( hiddenClass );
|
||
|
|
||
15 years ago
|
}
|
||
15 years ago
|
|
||
15 years ago
|
return this;
|
||
15 years ago
|
},
|
||
|
|
||
|
// used on all the filtered cards, $cards.filtered
|
||
|
sort : function() {
|
||
|
|
||
|
},
|
||
|
|
||
15 years ago
|
|
||
15 years ago
|
|
||
|
placeCard : function( setCount, setY, props ) {
|
||
|
// here, `this` refers to a child element or "brick"
|
||
|
// get the minimum Y value from the columns
|
||
|
var minimumY = Math.min.apply( Math, setY ),
|
||
|
setHeight = minimumY + this.outerHeight(true),
|
||
|
i = setY.length,
|
||
|
shortCol = i,
|
||
|
setSpan = props.colCount + 1 - i,
|
||
|
animOpts = $.extend( {}, props.opts.animationOptions ),
|
||
15 years ago
|
position, x, y ;
|
||
15 years ago
|
// Which column has the minY value, closest to the left
|
||
|
while (i--) {
|
||
|
if ( setY[i] === minimumY ) {
|
||
|
shortCol = i;
|
||
|
}
|
||
|
}
|
||
15 years ago
|
|
||
|
x = props.colW * shortCol + props.posLeft;
|
||
|
y = minimumY;
|
||
15 years ago
|
position = molequulMethods.position( x, y );
|
||
15 years ago
|
|
||
15 years ago
|
// position the brick
|
||
|
props.styleQueue.push({ $el: this, style: position });
|
||
|
// this[ props.applyStyle ]( position, animOpts );
|
||
15 years ago
|
|
||
|
// apply setHeight to necessary columns
|
||
|
for ( i=0; i < setSpan; i++ ) {
|
||
|
props.colYs[ shortCol + i ] = setHeight;
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
singleColumn : function( colYs, props ) {
|
||
|
return this.each(function(){
|
||
15 years ago
|
$(this).molequul( 'placeCard', props.colCount, colYs, props );
|
||
15 years ago
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
multiColumn : function( colYs, props ) {
|
||
|
return this.each(function(){
|
||
|
var $this = $(this),
|
||
|
//how many columns does this brick span
|
||
|
colSpan = Math.ceil( $this.outerWidth(true) / props.colW );
|
||
|
colSpan = Math.min( colSpan, props.colCount );
|
||
|
|
||
|
if ( colSpan === 1 ) {
|
||
|
// if brick spans only one column, just like singleMode
|
||
15 years ago
|
$this.molequul( 'placeCard', props.colCount, colYs, props );
|
||
15 years ago
|
} else {
|
||
|
// brick spans more than one column
|
||
|
// how many different places could this brick fit horizontally
|
||
|
var groupCount = props.colCount + 1 - colSpan,
|
||
|
groupY = [];
|
||
|
|
||
|
// for each group potential horizontal position
|
||
|
for ( i=0; i < groupCount; i++ ) {
|
||
|
// make an array of colY values for that one group
|
||
|
var groupColY = colYs.slice( i, i+colSpan );
|
||
|
// and get the max value of the array
|
||
|
groupY[i] = Math.max.apply( Math, groupColY );
|
||
|
}
|
||
|
|
||
15 years ago
|
$this.molequul( 'placeCard', groupCount, groupY, props );
|
||
15 years ago
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
15 years ago
|
// parseTransformStyle : function( style ) {
|
||
|
// for ( prop in style ) {
|
||
|
// switch
|
||
|
// }
|
||
|
// return style;
|
||
|
// },
|
||
15 years ago
|
|
||
15 years ago
|
complete : function( props ) {
|
||
15 years ago
|
|
||
|
// are we animating the layout arrangement?
|
||
|
// use plugin-ish syntax for css or animate
|
||
15 years ago
|
var styleFn = ( props.initialized && props.opts.animate ) ? 'animate' : 'css',
|
||
15 years ago
|
animOpts = props.opts.animationOptions;
|
||
15 years ago
|
|
||
15 years ago
|
// process styleQueue
|
||
|
$.each( props.styleQueue, function( i, obj ){
|
||
15 years ago
|
// var style = molequulMethods.parseTransformStyle( obj.style );
|
||
15 years ago
|
for ( var prop in obj.style ) {
|
||
|
console.log( prop, obj.style[prop] )
|
||
|
// switch ( obj.style[prop] ) {
|
||
|
// case 'scale' :
|
||
|
// console.log( obj.style )
|
||
|
// break;
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
// have to extend animation to play nice with jQuery
|
||
15 years ago
|
obj.$el[ styleFn ]( obj.style, $.extend( {}, animOpts ) );
|
||
|
});
|
||
15 years ago
|
|
||
15 years ago
|
// clear out queue for next time
|
||
|
props.styleQueue = [];
|
||
15 years ago
|
|
||
|
// provide props.bricks as context for the callback
|
||
|
// callback = callback || function(){};
|
||
|
// callback.call( props.$bricks );
|
||
|
|
||
|
// set all data so we can retrieve it for appended appendedContent
|
||
|
// or anyone else's crazy jquery fun
|
||
15 years ago
|
this.data( 'molequul', props );
|
||
15 years ago
|
|
||
15 years ago
|
return this;
|
||
|
},
|
||
|
|
||
|
// used on collection of cards (should be filtered, and sorted before )
|
||
|
// accepts cards-to-be-laid-out and colYs to start with
|
||
|
layout : function( $cards, colYs ) {
|
||
|
|
||
15 years ago
|
var props = this.data('molequul');
|
||
15 years ago
|
|
||
15 years ago
|
// console.log( props.opts.hiddenStyle.scale )
|
||
|
|
||
15 years ago
|
// layout logic
|
||
|
var layoutMode = props.opts.singleMode ? 'singleColumn' : 'multiColumn';
|
||
|
|
||
15 years ago
|
$cards.molequul( layoutMode, colYs, props );
|
||
15 years ago
|
|
||
|
// set the height of the container to the tallest column
|
||
|
props.containerHeight = Math.max.apply( Math, props.colYs );
|
||
|
var containerStyle = { height: props.containerHeight - props.posTop };
|
||
|
props.styleQueue.push({ $el: this, style: containerStyle });
|
||
|
|
||
15 years ago
|
// this[ props.applyStyle ]( containerStyle, animOpts ).molequul( 'complete', props );
|
||
15 years ago
|
|
||
15 years ago
|
this.molequul( 'complete', props );
|
||
15 years ago
|
|
||
15 years ago
|
return this;
|
||
15 years ago
|
|
||
|
},
|
||
|
|
||
15 years ago
|
resetColYs : function( props ) {
|
||
|
var colYs = [],
|
||
|
i = props.colCount;
|
||
|
while (i--) {
|
||
|
colYs.push( props.posTop );
|
||
|
}
|
||
|
props.colYs = colYs;
|
||
|
return colYs
|
||
|
},
|
||
|
|
||
15 years ago
|
resize : function() {
|
||
15 years ago
|
// console.log( this.data('molequul') , this[0].id )
|
||
|
var props = this.data('molequul'),
|
||
15 years ago
|
prevColCount = props.colCount;
|
||
|
|
||
15 years ago
|
props.initialized = true;
|
||
15 years ago
|
|
||
15 years ago
|
// get updated colCount
|
||
15 years ago
|
this.molequul( 'getColCount', props );
|
||
15 years ago
|
if ( props.colCount !== prevColCount ) {
|
||
|
// if column count has changed, do a new column cound
|
||
15 years ago
|
var colYs = molequulMethods.resetColYs( props );
|
||
|
this.molequul( 'layout', props.$cards.filtered, colYs );
|
||
15 years ago
|
}
|
||
|
|
||
|
return this;
|
||
15 years ago
|
},
|
||
|
|
||
|
append : function() {
|
||
|
|
||
|
},
|
||
|
|
||
15 years ago
|
getColCount : function( props ) {
|
||
|
props.colCount = Math.floor( this.width() / props.colW ) ;
|
||
|
props.colCount = Math.max( props.colCount, 1 );
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// only run though on initial init
|
||
|
setup : function() {
|
||
15 years ago
|
var props = this.data('molequul');
|
||
15 years ago
|
props.$cards = {};
|
||
15 years ago
|
props.styleQueue = [];
|
||
15 years ago
|
// need to get cards
|
||
|
props.$cards.all = props.opts.selector ?
|
||
|
this.find( props.opts.selector ) :
|
||
15 years ago
|
this.children();
|
||
15 years ago
|
|
||
|
props.colW = props.opts.columnWidth || props.$cards.all.outerWidth(true);
|
||
|
|
||
|
// if colW == 0, back out before divide by zero
|
||
|
if ( !props.colW ) {
|
||
15 years ago
|
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.');
|
||
15 years ago
|
return this;
|
||
|
}
|
||
|
|
||
15 years ago
|
this.css('position', 'relative').molequul( 'getColCount', props );
|
||
15 years ago
|
|
||
15 years ago
|
cardStyle = { position: 'absolute' };
|
||
|
if ( usingTransforms ) {
|
||
|
cardStyle.left = 0;
|
||
|
cardStyle.top = 0;
|
||
|
}
|
||
|
props.$cards.all.css( cardStyle );
|
||
15 years ago
|
|
||
|
// get top left position of where the bricks should be
|
||
|
var $cursor = $( document.createElement('div') );
|
||
|
this.prepend( $cursor );
|
||
|
props.posTop = Math.round( $cursor.position().top );
|
||
|
props.posLeft = Math.round( $cursor.position().left );
|
||
|
$cursor.remove();
|
||
|
|
||
15 years ago
|
// add molequul class first time around
|
||
15 years ago
|
var $container = this;
|
||
|
setTimeout(function(){
|
||
15 years ago
|
$container.addClass('molequul');
|
||
15 years ago
|
}, 1 );
|
||
|
|
||
15 years ago
|
return this;
|
||
|
},
|
||
|
|
||
15 years ago
|
|
||
15 years ago
|
|
||
15 years ago
|
init : function( options ) {
|
||
15 years ago
|
|
||
|
return this.each(function() {
|
||
|
|
||
15 years ago
|
var $this = $(this),
|
||
15 years ago
|
data = $this.data('molequul'),
|
||
15 years ago
|
props = data || {};
|
||
|
|
||
|
// checks if masonry has been called before on this object
|
||
15 years ago
|
props.initialized = !!data;
|
||
15 years ago
|
|
||
15 years ago
|
var previousOptions = props.initialized ? data.opts : {};
|
||
15 years ago
|
|
||
|
props.opts = $.extend(
|
||
|
{},
|
||
15 years ago
|
$.fn.molequul.defaults,
|
||
15 years ago
|
previousOptions,
|
||
|
options
|
||
|
);
|
||
|
|
||
15 years ago
|
$this.data( 'molequul', props );
|
||
15 years ago
|
|
||
15 years ago
|
if ( !props.initialized ) {
|
||
15 years ago
|
$this.molequul( 'setup' );
|
||
15 years ago
|
}
|
||
|
|
||
15 years ago
|
var colYs = molequulMethods.resetColYs( props );
|
||
15 years ago
|
$this
|
||
15 years ago
|
.molequul( 'filter', props.$cards.all )
|
||
|
.molequul( 'layout', props.$cards.filtered, colYs );
|
||
15 years ago
|
|
||
|
|
||
|
// binding window resizing
|
||
|
if ( props.opts.resizeable ) {
|
||
15 years ago
|
$(window).bind('smartresize.molequul', function() { $this.molequul( 'resize' ); } );
|
||
15 years ago
|
} else if ( !props.opts.resizeable && !!previousOptions.resizeable ) {
|
||
15 years ago
|
$(window).unbind('smartresize.molequul');
|
||
15 years ago
|
}
|
||
15 years ago
|
|
||
|
});
|
||
|
|
||
15 years ago
|
},
|
||
15 years ago
|
|
||
15 years ago
|
transform : function( value ) {
|
||
|
return {
|
||
|
'-webkit-transform' : value,
|
||
|
'-moz-transform' : value,
|
||
|
'-o-transform' : value,
|
||
|
'transform' : value
|
||
|
}
|
||
|
},
|
||
|
|
||
|
translate : function( x, y ) {
|
||
15 years ago
|
return molequulMethods.transform('translate(' + x + 'px, ' + y + 'px) scale(1)')
|
||
15 years ago
|
},
|
||
|
|
||
|
translate3d : function( x, y ) {
|
||
15 years ago
|
return molequulMethods.transform('translate3d(' + x + 'px, ' + y + 'px, 0) scale(1)')
|
||
15 years ago
|
},
|
||
|
|
||
|
positionAbs : function( x, y ) {
|
||
|
return { left: x, top: y }
|
||
|
}
|
||
15 years ago
|
|
||
|
};
|
||
|
|
||
15 years ago
|
|
||
|
if ( usingTransforms ) {
|
||
|
var translateMethod = Modernizr.csstransforms3d ? 'translate3d' : 'translate';
|
||
15 years ago
|
molequulMethods.position = molequulMethods[ translateMethod ];
|
||
15 years ago
|
} else {
|
||
15 years ago
|
molequulMethods.position = molequulMethods.positionAbs;
|
||
15 years ago
|
}
|
||
15 years ago
|
// molequulMethods.position = Modernizr.csstransforms3d ? molequulMethods.translate3d : molequulMethods.positionAbs;
|
||
15 years ago
|
|
||
15 years ago
|
// molequul code begin
|
||
|
$.fn.molequul = function( firstArg ) {
|
||
15 years ago
|
|
||
|
// Method calling logic
|
||
15 years ago
|
var method = molequulMethods[ firstArg ];
|
||
15 years ago
|
if ( method ) {
|
||
|
// remove firstArg, which is a string of the function name, from arguments
|
||
|
var args = Array.prototype.slice.call( arguments, 1 );
|
||
|
return method.apply( this, args );
|
||
|
|
||
|
} else if ( !firstArg || typeof firstArg === 'object' ) {
|
||
15 years ago
|
return molequulMethods.init.apply( this, arguments );
|
||
15 years ago
|
}
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
// Default plugin options
|
||
15 years ago
|
$.fn.molequul.defaults = {
|
||
15 years ago
|
// singleMode: false,
|
||
|
// columnWidth: undefined,
|
||
|
// itemSelector: undefined,
|
||
|
// appendedContent: undefined,
|
||
|
// saveOptions: true,
|
||
15 years ago
|
resizeable: true,
|
||
15 years ago
|
hiddenClass : 'molequul-hidden',
|
||
15 years ago
|
hiddenStyle : {
|
||
15 years ago
|
opacity : 0,
|
||
|
scale: 0.001
|
||
15 years ago
|
},
|
||
|
visibleStyle : {
|
||
15 years ago
|
opacity : 1,
|
||
|
scale: 1
|
||
15 years ago
|
},
|
||
|
animationOptions: {
|
||
|
queue: false
|
||
15 years ago
|
}
|
||
15 years ago
|
};
|
||
|
|
||
|
})(jQuery);
|