@ -15,7 +15,7 @@ window.Modernizr=function(a,b,c){function z(a){j.cssText=a}function A(a,b){retur
if ( typeof define === 'function' && define . amd ) {
define ( [ 'jquery' , 'modernizr' ] , factory ) ;
} else {
factory ( window . $ , window . Modernizr ) ;
factory ( window . jQuery , window . Modernizr ) ;
}
} ) ( function ( $ , Modernizr , undefined ) {
@ -53,10 +53,15 @@ function dashify( prop ) {
var TRANSITION = Modernizr . prefixed ( 'transition' ) ;
var TRANSITION _DELAY = Modernizr . prefixed ( 'transitionDelay' ) ;
var TRANSITION _DURATION = Modernizr . prefixed ( 'transitionDuration' ) ;
// Note(glen): Stock Android 4.1.x browser will fail here because it wrongly
// says it supports non-prefixed transitions.
// https://github.com/Modernizr/Modernizr/issues/897
var TRANSITIONEND = {
'WebkitTransition' : 'webkitTransitionEnd' ,
'transition' : 'transitionend'
} [ TRANSITION ] ;
var TRANSFORM = Modernizr . prefixed ( 'transform' ) ;
var CSS _TRANSFORM = dashify ( TRANSFORM ) ;
@ -64,6 +69,7 @@ var CSS_TRANSFORM = dashify(TRANSFORM);
var CAN _TRANSITION _TRANSFORMS = Modernizr . csstransforms && Modernizr . csstransitions ;
var HAS _TRANSFORMS _3D = Modernizr . csstransforms3d ;
var SHUFFLE = 'shuffle' ;
var COLUMN _THRESHOLD = 0.3 ;
// Configurable. You can change these constants to fit your application.
// The default scale and concealed scale, however, have to be different values.
@ -118,6 +124,14 @@ function defer(fn, context, wait) {
return setTimeout ( $ . proxy ( fn , context ) , wait || 0 ) ;
}
function arrayMax ( array ) {
return Math . max . apply ( Math , array ) ;
}
function arrayMin ( array ) {
return Math . min . apply ( Math , array ) ;
}
// Used for unique instance variables
var id = 0 ;
@ -196,37 +210,40 @@ Shuffle._getItemTransformString = function(x, y, scale) {
} ;
Shuffle . _getPreciseDimension = function ( element , style ) {
var dimension ;
if ( window . getComputedStyle ) {
dimension = window . getComputedStyle ( element , null ) [ style ] ;
} else {
dimension = $ ( element ) . css ( style ) ;
}
return parseFloat ( dimension ) ;
/ * *
* Retrieve the computed style for an element , parsed as a float . This should
* not be used for width or height values because jQuery mangles them and they
* are not precise enough .
* @ param { Element } element Element to get style for .
* @ param { string } style Style property .
* @ return { number } The parsed computed value or zero if that fails because IE
* will return 'auto' when the element doesn ' t have margins instead of
* the computed style .
* @ private
* /
Shuffle . _getNumberStyle = function ( element , style ) {
return parseFloat ( $ ( element ) . css ( style ) ) || 0 ;
} ;
/ * *
* Returns the outer width of an element , optionally including its margins .
* the client rect is used instead of ` offsetWidth ` because Firefox returns an
* integer value instead of a double .
* @ param { Element } element The element .
* @ param { boolean } [ includeMargins ] Whether to include margins . Default is false .
* @ return { number } The width .
* /
Shuffle . _getOuterWidth = function ( element , includeMargins ) {
var width = element . offsetWidth ;
var rect = element . getBoundingClientRect ( ) ;
var width = rect . right - rect . left ;
// Use jQuery here because it uses getComputedStyle internally and is
// cross-browser. Using the style property of the element will only work
// if there are inline styles.
if ( includeMargins ) {
var styles = $ ( element ) . css ( [ 'marginLeft' , 'marginRight' ] ) ;
// Defaults to zero if parsing fails because IE will return 'auto' when
// the element doesn't have margins instead of the computed style.
var marginLeft = parseFloat ( styles . marginLeft ) || 0 ;
var marginRight = parseFloat ( styles . marginRight ) || 0 ;
if ( includeMargins ) {
var marginLeft = Shuffle . _getNumberStyle ( element , 'marginLeft' ) ;
var marginRight = Shuffle . _getNumberStyle ( element , 'marginRight' ) ;
width += marginLeft + marginRight ;
}
@ -243,10 +260,9 @@ Shuffle._getOuterWidth = function( element, includeMargins ) {
Shuffle . _getOuterHeight = function ( element , includeMargins ) {
var height = element . offsetHeight ;
if ( includeMargins ) {
var styles = $ ( element ) . css ( [ 'marginTop' , 'marginBottom' ] ) ;
var marginTop = parseFloat ( styles . marginTop ) || 0 ;
var marginBottom = parseFloat ( styles . marginBottom ) || 0 ;
if ( includeMargins ) {
var marginTop = Shuffle . _getNumberStyle ( element , 'marginTop' ) ;
var marginBottom = Shuffle . _getNumberStyle ( element , 'marginBottom' ) ;
height += marginTop + marginBottom ;
}
@ -556,12 +572,12 @@ Shuffle.prototype._getConcealedItems = function() {
/ * *
* Returns the column size , based on column width and sizer options .
* @ param { number } gutterSize Size of the gutters .
* @ param { number } containerWidth Size of the parent container .
* @ param { number } gutterSize Size of the gutters .
* @ return { number }
* @ private
* /
Shuffle . prototype . _getColumnSize = function ( gutterSize, containerWidth ) {
Shuffle . prototype . _getColumnSize = function ( containerWidth, gutterSize ) {
var size ;
// If the columnWidth property is a function, then the grid is fluid
@ -570,7 +586,7 @@ Shuffle.prototype._getColumnSize = function( gutterSize, containerWidth ) {
// columnWidth option isn't a function, are they using a sizing element?
} else if ( this . useSizer ) {
size = Shuffle . _get PreciseDimension( this . sizer , 'width' ) ;
size = Shuffle . _get OuterWidth( this . sizer ) ;
// if not, how about the explicitly set option?
} else if ( this . columnWidth ) {
@ -605,7 +621,7 @@ Shuffle.prototype._getGutterSize = function( containerWidth ) {
if ( $ . isFunction ( this . gutterWidth ) ) {
size = this . gutterWidth ( containerWidth ) ;
} else if ( this . useSizer ) {
size = Shuffle . _get PreciseDimension ( this . sizer , 'marginLeft' ) ;
size = Shuffle . _get NumberStyle ( this . sizer , 'marginLeft' ) ;
} else {
size = this . gutterWidth ;
}
@ -621,11 +637,11 @@ Shuffle.prototype._getGutterSize = function( containerWidth ) {
Shuffle . prototype . _setColumns = function ( theContainerWidth ) {
var containerWidth = theContainerWidth || Shuffle . _getOuterWidth ( this . element ) ;
var gutter = this . _getGutterSize ( containerWidth ) ;
var columnWidth = this . _getColumnSize ( gutter, containerWidth ) ;
var columnWidth = this . _getColumnSize ( containerWidth, gutter ) ;
var calculatedColumns = ( containerWidth + gutter ) / columnWidth ;
// Widths given from getComputedStyle are not precise enough...
if ( Math . abs ( Math . round ( calculatedColumns ) - calculatedColumns ) < 0.03 ) {
if ( Math . abs ( Math . round ( calculatedColumns ) - calculatedColumns ) < COLUMN _THRESHOLD ) {
// e.g. calculatedColumns = 11.998876
calculatedColumns = Math . round ( calculatedColumns ) ;
}
@ -648,7 +664,7 @@ Shuffle.prototype._setContainerSize = function() {
* @ private
* /
Shuffle . prototype . _getContainerSize = function ( ) {
return Math . max . apply ( Math , this . colYs ) ;
return arrayMax ( this . colYs ) ;
} ;
/ * *
@ -659,6 +675,18 @@ Shuffle.prototype._fire = function( name, args ) {
} ;
/ * *
* Zeros out the y columns array , which is used to determine item placement .
* @ private
* /
Shuffle . prototype . _resetCols = function ( ) {
var i = this . cols ;
this . colYs = [ ] ;
while ( i -- ) {
this . colYs . push ( 0 ) ;
}
} ;
/ * *
* Loops through each item that should be shown and calculates the x , y position .
* @ param { Array . < Element > } items Array of items that will be shown / layed out in order in their array .
@ -667,42 +695,7 @@ Shuffle.prototype._fire = function( name, args ) {
* /
Shuffle . prototype . _layout = function ( items , isOnlyPosition ) {
each ( items , function ( item ) {
var $item = $ ( item ) ;
var itemData = $item . data ( ) ;
var currPos = itemData . position ;
var currScale = itemData . scale ;
var pos = this . _getItemPosition ( $item ) ;
// Save data for shrink
$item . data ( 'position' , pos ) ;
$item . data ( 'scale' , DEFAULT _SCALE ) ;
// If the item will not change its position, do not add it to the render
// queue. Transitions don't fire when setting a property to the same value.
if ( pos . x === currPos . x && pos . y === currPos . y && currScale === DEFAULT _SCALE ) {
return ;
}
var transitionObj = {
$item : $item ,
x : pos . x ,
y : pos . y ,
scale : DEFAULT _SCALE ,
opacity : isOnlyPosition ? 0 : 1 ,
skipTransition : ! ! isOnlyPosition ,
callfront : function ( ) {
if ( ! isOnlyPosition ) {
$item . css ( 'visibility' , 'visible' ) ;
}
} ,
callback : function ( ) {
if ( isOnlyPosition ) {
$item . css ( 'visibility' , 'hidden' ) ;
}
}
} ;
this . styleQueue . push ( transitionObj ) ;
this . _layoutItem ( item , isOnlyPosition ) ;
} , this ) ;
// `_layout` always happens after `_shrink`, so it's safe to process the style
@ -713,88 +706,135 @@ Shuffle.prototype._layout = function( items, isOnlyPosition ) {
this . _setContainerSize ( ) ;
} ;
/ * *
* Zeros out the y columns array , which is used to determine item placement .
* @ private
* /
Shuffle . prototype . _resetCols = function ( ) {
var i = this . cols ;
this . colYs = [ ] ;
while ( i -- ) {
this . colYs . push ( 0 ) ;
Shuffle . prototype . _layoutItem = function ( item , isOnlyPosition ) {
var $item = $ ( item ) ;
var itemData = $item . data ( ) ;
var currPos = itemData . position ;
var currScale = itemData . scale ;
var itemSize = {
width : Shuffle . _getOuterWidth ( item , true ) ,
height : Shuffle . _getOuterHeight ( item , true )
} ;
var pos = this . _getItemPosition ( itemSize ) ;
// Save data for shrink
$item . data ( 'position' , pos ) ;
$item . data ( 'scale' , DEFAULT _SCALE ) ;
// If the item will not change its position, do not add it to the render
// queue. Transitions don't fire when setting a property to the same value.
if ( pos . x === currPos . x && pos . y === currPos . y && currScale === DEFAULT _SCALE ) {
return ;
}
var transitionObj = {
$item : $item ,
x : pos . x ,
y : pos . y ,
scale : DEFAULT _SCALE ,
opacity : isOnlyPosition ? 0 : 1 ,
skipTransition : ! ! isOnlyPosition ,
callfront : function ( ) {
if ( ! isOnlyPosition ) {
$item . css ( 'visibility' , 'visible' ) ;
}
} ,
callback : function ( ) {
if ( isOnlyPosition ) {
$item . css ( 'visibility' , 'hidden' ) ;
}
}
} ;
this . styleQueue . push ( transitionObj ) ;
} ;
Shuffle . prototype . _getItemPosition = function ( $item ) {
var itemWidth = Shuffle . _getOuterWidth ( $item [ 0 ] , true ) ;
var columnSpan = itemWidth / this . colWidth ;
Shuffle . prototype . _getItemPosition = function ( itemSize ) {
var columnSpan = itemSize . width / this . colWidth ;
// If the difference between the rounded column span number and the
// calculated column span number is really small, round the number to
// make it fit.
if ( Math . abs ( Math . round ( columnSpan ) - columnSpan ) < 0.03 ) {
if ( Math . abs ( Math . round ( columnSpan ) - columnSpan ) < COLUMN _THRESHOLD ) {
// e.g. columnSpan = 4.0089945390298745
columnSpan = Math . round ( columnSpan ) ;
}
// How many columns does this item span. Ensure it's not more than the
// amount of columns in the whole layout.
var colSpan = Math . min ( Math . ceil ( columnSpan ) , this . cols ) ;
// Ensure the column span is not more than the amount of columns in the whole layout.
columnSpan = Math . min ( Math . ceil ( columnSpan ) , this . cols ) ;
var setY = this . _getColumnSet ( columnSpan , this . cols ) ;
// Finds the index of the smallest number in the set.
var shortColumnIndex = this . _getShortColumn ( setY , this . buffer ) ;
// Position the item
var position = {
x : Math . round ( ( this . colWidth * shortColumnIndex ) + this . offset . left ) ,
y : Math . round ( setY [ shortColumnIndex ] + this . offset . top )
} ;
// Update the columns array with the new values for each column.
// e.g. before the update the columns could be [250, 0, 0, 0] for an item
// which spans 2 columns. After it would be [250, itemHeight, itemHeight, 0].
var setHeight = setY [ shortColumnIndex ] + itemSize . height ;
var setSpan = this . cols + 1 - setY . length ;
for ( var i = 0 ; i < setSpan ; i ++ ) {
this . colYs [ shortColumnIndex + i ] = setHeight ;
}
return position ;
} ;
/ * *
* Retrieves the column set to use for placement .
* @ param { number } columnSpan The number of columns this current item spans .
* @ param { number } columns The total columns in the grid .
* @ return { Array . < number > } An array of numbers represeting the column set .
* @ private
* /
Shuffle . prototype . _getColumnSet = function ( columnSpan , columns ) {
// The item spans only one column.
if ( colSpan === 1 ) {
return this . _placeItem ( $item , this . colYs ) ;
if ( col umn Span === 1 ) {
return this . colYs;
// The item spans more than one column, figure out how many different
// places it could fit horizontally
} else {
var groupCount = this . cols + 1 - colSpan ;
var groupCount = col umn s + 1 - col umn Span;
var groupY = [ ] ;
var groupColY ;
var i ;
// for each group potential horizontal position
for ( i = 0 ; i < groupCount ; i ++ ) {
for ( var i = 0 ; i < groupCount ; i ++ ) {
// make an array of colY values for that one group
groupColY = this . colYs . slice ( i , i + colSpan ) ;
groupColY = this . colYs . slice ( i , i + col umn Span ) ;
// and get the max value of the array
groupY [ i ] = Math . max . apply ( Math , groupColY ) ;
groupY [ i ] = arrayMax ( groupColY ) ;
}
return this . _placeItem ( $item , groupY ) ;
return groupY ;
}
} ;
// TODO: Cleanup and combine with _getItemPosition.
Shuffle . prototype . _placeItem = function ( $item , setY ) {
// get the minimum Y value from the columns
var minimumY = Math . min . apply ( Math , setY ) ;
var shortCol = 0 ;
// Find index of short column, the first from the left where this item will go
// if ( setY[i] === minimumY ) requires items' height to be exact every time.
// The buffer value is very useful when the height is a percentage of the width
for ( var i = 0 , len = setY . length ; i < len ; i ++ ) {
if ( setY [ i ] >= minimumY - this . buffer && setY [ i ] <= minimumY + this . buffer ) {
shortCol = i ;
break ;
/ * *
* Find index of short column , the first from the left where this item will go .
*
* @ param { Array . < number > } positions The array to search for the smallest number .
* @ param { number } buffer Optional buffer which is very useful when the height
* is a percentage of the width .
* @ return { number } Index of the short column .
* @ private
* /
Shuffle . prototype . _getShortColumn = function ( positions , buffer ) {
var minPosition = arrayMin ( positions ) ;
for ( var i = 0 , len = positions . length ; i < len ; i ++ ) {
if ( positions [ i ] >= minPosition - buffer && positions [ i ] <= minPosition + buffer ) {
return i ;
}
}
// Position the item
var position = {
x : Math . round ( ( this . colWidth * shortCol ) + this . offset . left ) ,
y : Math . round ( minimumY + this . offset . top )
} ;
// Apply setHeight to necessary columns
var setHeight = minimumY + Shuffle . _getOuterHeight ( $item [ 0 ] , true ) ,
setSpan = this . cols + 1 - len ;
for ( i = 0 ; i < setSpan ; i ++ ) {
this . colYs [ shortCol + i ] = setHeight ;
}
return position ;
return 0 ;
} ;
/ * *