masonry-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +109 -0
- data/LICENSE.txt +20 -0
- data/Masonry.mdown +51 -0
- data/README.md +53 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/_config.yml +6 -0
- data/lib/masonry-rails.rb +7 -0
- data/masonry-rails.gemspec +107 -0
- data/minify.sh +16 -0
- data/spec/_layouts/default.html +75 -0
- data/spec/_posts/demos/2011-05-01-basic-single-column.html +206 -0
- data/spec/_posts/demos/2011-05-02-basic-multi-column.html +213 -0
- data/spec/_posts/demos/2011-05-03-images.html +57 -0
- data/spec/_posts/demos/2011-05-04-tumblelog.html +226 -0
- data/spec/_posts/demos/2011-05-05-animating-jquery.html +144 -0
- data/spec/_posts/demos/2011-05-06-animating-css-transitions.html +132 -0
- data/spec/_posts/demos/2011-05-07-animating-modernizr.html +136 -0
- data/spec/_posts/demos/2011-05-08-adding-items.html +89 -0
- data/spec/_posts/demos/2011-05-09-infinite-scroll.html +268 -0
- data/spec/_posts/demos/2011-05-10-gutters.html +209 -0
- data/spec/_posts/demos/2011-05-11-right-to-left.html +135 -0
- data/spec/_posts/demos/2011-05-17-centered.html +137 -0
- data/spec/_posts/demos/2011-07-26-fluid.html +219 -0
- data/spec/_posts/demos/2011-10-07-corner-stamp.html +249 -0
- data/spec/_posts/docs/2011-05-01-intro.mdown +149 -0
- data/spec/_posts/docs/2011-05-02-options.mdown +157 -0
- data/spec/_posts/docs/2011-05-03-methods.mdown +155 -0
- data/spec/_posts/docs/2011-05-04-animating.mdown +96 -0
- data/spec/_posts/docs/2011-05-30-help.mdown +139 -0
- data/spec/_posts/pages/2011-05-02-2.html +189 -0
- data/spec/_posts/pages/2011-05-03-3.html +174 -0
- data/spec/_posts/pages/2011-05-04-4.html +159 -0
- data/spec/_posts/pages/2011-05-05-5.html +167 -0
- data/spec/_posts/tests/2011-01-01-index.html +14 -0
- data/spec/_posts/tests/2011-05-14-lots-of-bricks.html +1438 -0
- data/spec/_posts/tests/2011-05-17-dual.html +247 -0
- data/spec/_posts/tests/2011-05-18-first-child-no-width.html +217 -0
- data/spec/_posts/tests/2011-05-19-empty.html +205 -0
- data/spec/_posts/tests/2011-08-08-destroy.html +214 -0
- data/spec/_posts/tests/2011-09-27-centered-few.html +50 -0
- data/spec/index.html +58 -0
- data/spec/spec_helper.rb +12 -0
- data/vendor/assets/javascripts/masonry/box-maker.js +38 -0
- data/vendor/assets/javascripts/masonry/jquery.event.drag-2.2.js +402 -0
- data/vendor/assets/javascripts/masonry/jquery.infinitescroll.min.js +47 -0
- data/vendor/assets/javascripts/masonry/jquery.masonry.js +682 -0
- data/vendor/assets/javascripts/masonry/jquery.masonry.min.js +10 -0
- data/vendor/assets/javascripts/masonry/modernizr-transitions.js +2 -0
- data/vendor/assets/stylesheets/masonry/style.css +616 -0
- metadata +199 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
/*
|
2
|
+
--------------------------------
|
3
|
+
Infinite Scroll
|
4
|
+
--------------------------------
|
5
|
+
+ https://github.com/paulirish/infinitescroll
|
6
|
+
+ version 2.0b2.110713
|
7
|
+
+ Copyright 2011 Paul Irish & Luke Shumard
|
8
|
+
+ Licensed under the MIT license
|
9
|
+
|
10
|
+
+ Documentation: http://infinite-scroll.com/
|
11
|
+
|
12
|
+
*/
|
13
|
+
|
14
|
+
(function(window,$,undefined){$.infinitescroll=function infscr(options,callback,element){this.element=$(element);this._create(options,callback);};$.infinitescroll.defaults={loading:{finished:undefined,finishedMsg:"<em>Congratulations, you've reached the end of the internet.</em>",img:"http://www.infinite-scroll.com/loading.gif",msg:null,msgText:"<em>Loading the next set of posts...</em>",selector:null,speed:'fast',start:undefined},state:{isDuringAjax:false,isInvalidPage:false,isDestroyed:false,isDone:false,isPaused:false,currPage:1},callback:undefined,debug:false,behavior:undefined,binder:$(window),nextSelector:"div.navigation a:first",navSelector:"div.navigation",contentSelector:null,extraScrollPx:150,itemSelector:"div.post",animate:false,pathParse:undefined,dataType:'html',appendCallback:true,bufferPx:40,errorCallback:function(){},infid:0,pixelsFromNavToBottom:undefined,path:undefined};$.infinitescroll.prototype={_binding:function infscr_binding(binding){var instance=this,opts=instance.options;if(!!opts.behavior&&this['_binding_'+opts.behavior]!==undefined){this['_binding_'+opts.behavior].call(this);return;}
|
15
|
+
if(binding!=='bind'&&binding!=='unbind'){this._debug('Binding value '+binding+' not valid')
|
16
|
+
return false;}
|
17
|
+
if(binding=='unbind'){(this.options.binder).unbind('smartscroll.infscr.'+instance.options.infid);}else{(this.options.binder)[binding]('smartscroll.infscr.'+instance.options.infid,function(){instance.scroll();});};this._debug('Binding',binding);},_create:function infscr_create(options,callback){if(!this._validate(options)){return false;}
|
18
|
+
var opts=this.options=$.extend(true,{},$.infinitescroll.defaults,options),relurl=/(.*?\/\/).*?(\/.*)/,path=$(opts.nextSelector).attr('href');opts.contentSelector=opts.contentSelector||this.element;opts.loading.selector=opts.loading.selector||opts.contentSelector;if(!path){this._debug('Navigation selector not found');return;}
|
19
|
+
opts.path=this._determinepath(path);opts.loading.msg=$('<div id="infscr-loading"><img alt="Loading..." src="'+opts.loading.img+'" /><div>'+opts.loading.msgText+'</div></div>');(new Image()).src=opts.loading.img;opts.pixelsFromNavToBottom=$(document).height()-$(opts.navSelector).offset().top;opts.loading.start=opts.loading.start||function(){$(opts.navSelector).hide();opts.loading.msg.appendTo(opts.loading.selector).show(opts.loading.speed,function(){beginAjax(opts);});};opts.loading.finished=opts.loading.finished||function(){opts.loading.msg.fadeOut('normal');};opts.callback=function(instance,data){if(!!opts.behavior&&instance['_callback_'+opts.behavior]!==undefined){instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0],data);}
|
20
|
+
if(callback){callback.call($(opts.contentSelector)[0],data);}};this._setup();},_debug:function infscr_debug(){if(this.options.debug){return window.console&&console.log.call(console,arguments);}},_determinepath:function infscr_determinepath(path){var opts=this.options;if(!!opts.behavior&&this['_determinepath_'+opts.behavior]!==undefined){this['_determinepath_'+opts.behavior].call(this,path);return;}
|
21
|
+
if(!!opts.pathParse){this._debug('pathParse manual');return opts.pathParse;}else if(path.match(/^(.*?)\b2\b(.*?$)/)){path=path.match(/^(.*?)\b2\b(.*?$)/).slice(1);}else if(path.match(/^(.*?)2(.*?$)/)){if(path.match(/^(.*?page=)2(\/.*|$)/)){path=path.match(/^(.*?page=)2(\/.*|$)/).slice(1);return path;}
|
22
|
+
path=path.match(/^(.*?)2(.*?$)/).slice(1);}else{if(path.match(/^(.*?page=)1(\/.*|$)/)){path=path.match(/^(.*?page=)1(\/.*|$)/).slice(1);return path;}else{this._debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.');opts.state.isInvalidPage=true;}}
|
23
|
+
this._debug('determinePath',path);return path;},_error:function infscr_error(xhr){var opts=this.options;if(!!opts.behavior&&this['_error_'+opts.behavior]!==undefined){this['_error_'+opts.behavior].call(this,xhr);return;}
|
24
|
+
if(xhr!=='destroy'&&xhr!=='end'){xhr='unknown';}
|
25
|
+
this._debug('Error',xhr);if(xhr=='end'){this._showdonemsg();}
|
26
|
+
opts.state.isDone=true;opts.state.currPage=1;opts.state.isPaused=false;this._binding('unbind');},_loadcallback:function infscr_loadcallback(box,data){var opts=this.options,callback=this.options.callback,result=(opts.state.isDone)?'done':(!opts.appendCallback)?'no-append':'append',frag;if(!!opts.behavior&&this['_loadcallback_'+opts.behavior]!==undefined){this['_loadcallback_'+opts.behavior].call(this,box,data);return;}
|
27
|
+
switch(result){case'done':this._showdonemsg();return false;break;case'no-append':if(opts.dataType=='html'){data='<div>'+data+'</div>';data=$(data).find(opts.itemSelector);};break;case'append':var children=box.children();if(children.length==0){return this._error('end');}
|
28
|
+
frag=document.createDocumentFragment();while(box[0].firstChild){frag.appendChild(box[0].firstChild);}
|
29
|
+
this._debug('contentSelector',$(opts.contentSelector)[0])
|
30
|
+
$(opts.contentSelector)[0].appendChild(frag);data=children.get();break;}
|
31
|
+
opts.loading.finished.call($(opts.contentSelector)[0],opts)
|
32
|
+
if(opts.animate){var scrollTo=$(window).scrollTop()+$('#infscr-loading').height()+opts.extraScrollPx+'px';$('html,body').animate({scrollTop:scrollTo},800,function(){opts.state.isDuringAjax=false;});}
|
33
|
+
if(!opts.animate)opts.state.isDuringAjax=false;callback(this,data);},_nearbottom:function infscr_nearbottom(){var opts=this.options,pixelsFromWindowBottomToBottom=0+$(document).height()-(opts.binder.scrollTop())-$(window).height();if(!!opts.behavior&&this['_nearbottom_'+opts.behavior]!==undefined){this['_nearbottom_'+opts.behavior].call(this);return;}
|
34
|
+
this._debug('math:',pixelsFromWindowBottomToBottom,opts.pixelsFromNavToBottom);return(pixelsFromWindowBottomToBottom-opts.bufferPx<opts.pixelsFromNavToBottom);},_pausing:function infscr_pausing(pause){var opts=this.options;if(!!opts.behavior&&this['_pausing_'+opts.behavior]!==undefined){this['_pausing_'+opts.behavior].call(this,pause);return;}
|
35
|
+
if(pause!=='pause'&&pause!=='resume'&&pause!==null){this._debug('Invalid argument. Toggling pause value instead');};pause=(pause&&(pause=='pause'||pause=='resume'))?pause:'toggle';switch(pause){case'pause':opts.state.isPaused=true;break;case'resume':opts.state.isPaused=false;break;case'toggle':opts.state.isPaused=!opts.state.isPaused;break;}
|
36
|
+
this._debug('Paused',opts.state.isPaused);return false;},_setup:function infscr_setup(){var opts=this.options;if(!!opts.behavior&&this['_setup_'+opts.behavior]!==undefined){this['_setup_'+opts.behavior].call(this);return;}
|
37
|
+
this._binding('bind');return false;},_showdonemsg:function infscr_showdonemsg(){var opts=this.options;if(!!opts.behavior&&this['_showdonemsg_'+opts.behavior]!==undefined){this['_showdonemsg_'+opts.behavior].call(this);return;}
|
38
|
+
opts.loading.msg.find('img').hide().parent().find('div').html(opts.loading.finishedMsg).animate({opacity:1},2000,function(){$(this).parent().fadeOut('normal');});opts.errorCallback.call($(opts.contentSelector)[0],'done');},_validate:function infscr_validate(opts){for(var key in opts){if(key.indexOf&&key.indexOf('Selector')>-1&&$(opts[key]).length===0){this._debug('Your '+key+' found no elements.');return false;}
|
39
|
+
return true;}},bind:function infscr_bind(){this._binding('bind');},destroy:function infscr_destroy(){this.options.state.isDestroyed=true;return this._error('destroy');},pause:function infscr_pause(){this._pausing('pause');},resume:function infscr_resume(){this._pausing('resume');},retrieve:function infscr_retrieve(pageNum){var instance=this,opts=instance.options,path=opts.path,box,frag,desturl,method,condition,pageNum=pageNum||null,getPage=(!!pageNum)?pageNum:opts.state.currPage;beginAjax=function infscr_ajax(opts){opts.state.currPage++;instance._debug('heading into ajax',path);box=$(opts.contentSelector).is('table')?$('<tbody/>'):$('<div/>');desturl=path.join(opts.state.currPage);method=(opts.dataType=='html'||opts.dataType=='json')?opts.dataType:'html+callback';if(opts.appendCallback&&opts.dataType=='html')method+='+callback'
|
40
|
+
switch(method){case'html+callback':instance._debug('Using HTML via .load() method');box.load(desturl+' '+opts.itemSelector,null,function infscr_ajax_callback(responseText){instance._loadcallback(box,responseText);});break;case'html':case'json':instance._debug('Using '+(method.toUpperCase())+' via $.ajax() method');$.ajax({url:desturl,dataType:opts.dataType,complete:function infscr_ajax_callback(jqXHR,textStatus){condition=(typeof(jqXHR.isResolved)!=='undefined')?(jqXHR.isResolved()):(textStatus==="success"||textStatus==="notmodified");(condition)?instance._loadcallback(box,jqXHR.responseText):instance._error('end');}});break;}};if(!!opts.behavior&&this['retrieve_'+opts.behavior]!==undefined){this['retrieve_'+opts.behavior].call(this,pageNum);return;}
|
41
|
+
if(opts.state.isDestroyed){this._debug('Instance is destroyed');return false;};opts.state.isDuringAjax=true;opts.loading.start.call($(opts.contentSelector)[0],opts);},scroll:function infscr_scroll(){var opts=this.options,state=opts.state;if(!!opts.behavior&&this['scroll_'+opts.behavior]!==undefined){this['scroll_'+opts.behavior].call(this);return;}
|
42
|
+
if(state.isDuringAjax||state.isInvalidPage||state.isDone||state.isDestroyed||state.isPaused)return;if(!this._nearbottom())return;this.retrieve();},toggle:function infscr_toggle(){this._pausing();},unbind:function infscr_unbind(){this._binding('unbind');},update:function infscr_options(key){if($.isPlainObject(key)){this.options=$.extend(true,this.options,key);}}}
|
43
|
+
$.fn.infinitescroll=function infscr_init(options,callback){var thisCall=typeof options;switch(thisCall){case'string':var args=Array.prototype.slice.call(arguments,1);this.each(function(){var instance=$.data(this,'infinitescroll');if(!instance){return false;}
|
44
|
+
if(!$.isFunction(instance[options])||options.charAt(0)==="_"){return false;}
|
45
|
+
instance[options].apply(instance,args);});break;case'object':this.each(function(){var instance=$.data(this,'infinitescroll');if(instance){instance.update(options);}else{$.data(this,'infinitescroll',new $.infinitescroll(options,callback,this));}});break;}
|
46
|
+
return this;};var event=$.event,scrollTimeout;event.special.smartscroll={setup:function(){$(this).bind("scroll",event.special.smartscroll.handler);},teardown:function(){$(this).unbind("scroll",event.special.smartscroll.handler);},handler:function(event,execAsap){var context=this,args=arguments;event.type="smartscroll";if(scrollTimeout){clearTimeout(scrollTimeout);}
|
47
|
+
scrollTimeout=setTimeout(function(){$.event.handle.apply(context,args);},execAsap==="execAsap"?0:100);}};$.fn.smartscroll=function(fn){return fn?this.bind("smartscroll",fn):this.trigger("smartscroll",["execAsap"]);};})(window,jQuery);
|
@@ -0,0 +1,682 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery Masonry v2.1.05
|
3
|
+
* A dynamic layout plugin for jQuery
|
4
|
+
* The flip-side of CSS Floats
|
5
|
+
* http://masonry.desandro.com
|
6
|
+
*
|
7
|
+
* Licensed under the MIT license.
|
8
|
+
* Copyright 2012 David DeSandro
|
9
|
+
*/
|
10
|
+
|
11
|
+
/*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */
|
12
|
+
/*global jQuery: false */
|
13
|
+
|
14
|
+
(function( window, $, undefined ){
|
15
|
+
|
16
|
+
'use strict';
|
17
|
+
|
18
|
+
/*
|
19
|
+
* smartresize: debounced resize event for jQuery
|
20
|
+
*
|
21
|
+
* latest version and complete README available on Github:
|
22
|
+
* https://github.com/louisremi/jquery.smartresize.js
|
23
|
+
*
|
24
|
+
* Copyright 2011 @louis_remi
|
25
|
+
* Licensed under the MIT license.
|
26
|
+
*/
|
27
|
+
|
28
|
+
var $event = $.event,
|
29
|
+
resizeTimeout;
|
30
|
+
|
31
|
+
$event.special.smartresize = {
|
32
|
+
setup: function() {
|
33
|
+
$(this).bind( "resize", $event.special.smartresize.handler );
|
34
|
+
},
|
35
|
+
teardown: function() {
|
36
|
+
$(this).unbind( "resize", $event.special.smartresize.handler );
|
37
|
+
},
|
38
|
+
handler: function( event, execAsap ) {
|
39
|
+
// Save the context
|
40
|
+
var context = this,
|
41
|
+
args = arguments;
|
42
|
+
|
43
|
+
// set correct event type
|
44
|
+
event.type = "smartresize";
|
45
|
+
|
46
|
+
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
|
47
|
+
resizeTimeout = setTimeout(function() {
|
48
|
+
$.event.handle.apply( context, args );
|
49
|
+
}, execAsap === "execAsap"? 0 : 100 );
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
$.fn.smartresize = function( fn ) {
|
54
|
+
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
|
55
|
+
};
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
// ========================= Masonry ===============================
|
60
|
+
|
61
|
+
|
62
|
+
// our "Widget" object constructor
|
63
|
+
$.Mason = function( options, element ){
|
64
|
+
this.element = $( element );
|
65
|
+
|
66
|
+
this._create( options );
|
67
|
+
this._init();
|
68
|
+
};
|
69
|
+
|
70
|
+
$.Mason.settings = {
|
71
|
+
isResizable: true,
|
72
|
+
isDraggable: false,
|
73
|
+
dragHandleSelector: null,
|
74
|
+
dragClass: null,
|
75
|
+
isAnimated: false,
|
76
|
+
animationOptions: {
|
77
|
+
queue: false,
|
78
|
+
duration: 500
|
79
|
+
},
|
80
|
+
gutterWidth: 0,
|
81
|
+
isRTL: false,
|
82
|
+
isFitWidth: false,
|
83
|
+
containerStyle: {
|
84
|
+
position: 'relative'
|
85
|
+
}
|
86
|
+
};
|
87
|
+
|
88
|
+
$.Mason.prototype = {
|
89
|
+
|
90
|
+
_filterFindBricks: function( $elems ) {
|
91
|
+
var selector = this.options.itemSelector;
|
92
|
+
// if there is a selector
|
93
|
+
// filter/find appropriate item elements
|
94
|
+
return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) );
|
95
|
+
},
|
96
|
+
|
97
|
+
_getBricks: function( $elems ) {
|
98
|
+
var $bricks = this._filterFindBricks( $elems )
|
99
|
+
.css({ position: 'absolute' })
|
100
|
+
.addClass('masonry-brick');
|
101
|
+
return $bricks;
|
102
|
+
},
|
103
|
+
|
104
|
+
// sets up widget
|
105
|
+
_create : function( options ) {
|
106
|
+
|
107
|
+
this.options = $.extend( true, {}, $.Mason.settings, options );
|
108
|
+
this.styleQueue = [];
|
109
|
+
|
110
|
+
// get original styles in case we re-apply them in .destroy()
|
111
|
+
var elemStyle = this.element[0].style;
|
112
|
+
this.originalStyle = {
|
113
|
+
// get height
|
114
|
+
height: elemStyle.height || ''
|
115
|
+
};
|
116
|
+
// get other styles that will be overwritten
|
117
|
+
var containerStyle = this.options.containerStyle;
|
118
|
+
for ( var prop in containerStyle ) {
|
119
|
+
this.originalStyle[ prop ] = elemStyle[ prop ] || '';
|
120
|
+
}
|
121
|
+
|
122
|
+
this.element.css( containerStyle );
|
123
|
+
|
124
|
+
this.horizontalDirection = this.options.isRTL ? 'right' : 'left';
|
125
|
+
|
126
|
+
this.offset = {
|
127
|
+
x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ),
|
128
|
+
y: parseInt( this.element.css( 'padding-top' ), 10 )
|
129
|
+
};
|
130
|
+
|
131
|
+
this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';
|
132
|
+
|
133
|
+
// add masonry class first time around
|
134
|
+
var instance = this;
|
135
|
+
setTimeout( function() {
|
136
|
+
instance.element.addClass('masonry');
|
137
|
+
}, 0 );
|
138
|
+
|
139
|
+
// bind resize method
|
140
|
+
if ( this.options.isResizable ) {
|
141
|
+
$(window).bind( 'smartresize.masonry', function() {
|
142
|
+
instance.resize();
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
|
147
|
+
// need to get bricks
|
148
|
+
this.reloadItems();
|
149
|
+
|
150
|
+
// set up dragging
|
151
|
+
if (this.options.isDraggable ){
|
152
|
+
this._initDrag(this.$bricks);
|
153
|
+
}
|
154
|
+
|
155
|
+
},
|
156
|
+
|
157
|
+
|
158
|
+
// _init fires when instance is first created
|
159
|
+
// and when instance is triggered again -> $el.masonry();
|
160
|
+
_init : function( callback ) {
|
161
|
+
this._getColumns();
|
162
|
+
this._reLayout( callback );
|
163
|
+
},
|
164
|
+
|
165
|
+
option: function( key, value ){
|
166
|
+
// set options AFTER initialization:
|
167
|
+
// signature: $('#foo').bar({ cool:false });
|
168
|
+
if ( $.isPlainObject( key ) ){
|
169
|
+
this.options = $.extend(true, this.options, key);
|
170
|
+
}
|
171
|
+
},
|
172
|
+
|
173
|
+
// ====================== General Layout ======================
|
174
|
+
|
175
|
+
// used on collection of atoms (should be filtered, and sorted before )
|
176
|
+
// accepts atoms-to-be-laid-out to start with
|
177
|
+
layout : function( $bricks, callback ) {
|
178
|
+
|
179
|
+
// place each brick
|
180
|
+
for (var i=0, len = $bricks.length; i < len; i++) {
|
181
|
+
this._placeBrick( $bricks[i] );
|
182
|
+
}
|
183
|
+
|
184
|
+
// set the size of the container
|
185
|
+
var containerSize = {};
|
186
|
+
containerSize.height = Math.max.apply( Math, this.colYs );
|
187
|
+
if ( this.options.isFitWidth ) {
|
188
|
+
var unusedCols = 0;
|
189
|
+
i = this.cols;
|
190
|
+
// count unused columns
|
191
|
+
while ( --i ) {
|
192
|
+
if ( this.colYs[i] !== 0 ) {
|
193
|
+
break;
|
194
|
+
}
|
195
|
+
unusedCols++;
|
196
|
+
}
|
197
|
+
// fit container to columns that have been used;
|
198
|
+
containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth;
|
199
|
+
}
|
200
|
+
this.styleQueue.push({ $el: this.element, style: containerSize });
|
201
|
+
|
202
|
+
// are we animating the layout arrangement?
|
203
|
+
// use plugin-ish syntax for css or animate
|
204
|
+
var styleFn = !this.isLaidOut ? 'css' : (
|
205
|
+
this.options.isAnimated ? 'animate' : 'css'
|
206
|
+
),
|
207
|
+
animOpts = this.options.animationOptions;
|
208
|
+
|
209
|
+
// process styleQueue
|
210
|
+
var obj;
|
211
|
+
for (i=0, len = this.styleQueue.length; i < len; i++) {
|
212
|
+
obj = this.styleQueue[i];
|
213
|
+
obj.$el[ styleFn ]( obj.style, animOpts );
|
214
|
+
}
|
215
|
+
|
216
|
+
// clear out queue for next time
|
217
|
+
this.styleQueue = [];
|
218
|
+
|
219
|
+
// provide $elems as context for the callback
|
220
|
+
if ( callback ) {
|
221
|
+
callback.call( $bricks );
|
222
|
+
}
|
223
|
+
|
224
|
+
this.isLaidOut = true;
|
225
|
+
},
|
226
|
+
|
227
|
+
// calculates number of columns
|
228
|
+
// i.e. this.columnWidth = 200
|
229
|
+
_getColumns : function() {
|
230
|
+
var container = this.options.isFitWidth ? this.element.parent() : this.element,
|
231
|
+
containerWidth = container.width();
|
232
|
+
|
233
|
+
// use fluid columnWidth function if there
|
234
|
+
this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) :
|
235
|
+
// if not, how about the explicitly set option?
|
236
|
+
this.options.columnWidth ||
|
237
|
+
// or use the size of the first item
|
238
|
+
this.$bricks.outerWidth(true) ||
|
239
|
+
// if there's no items, use size of container
|
240
|
+
containerWidth;
|
241
|
+
|
242
|
+
this.columnWidth += this.options.gutterWidth;
|
243
|
+
|
244
|
+
this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth );
|
245
|
+
this.cols = Math.max( this.cols, 1 );
|
246
|
+
|
247
|
+
},
|
248
|
+
|
249
|
+
// layout logic
|
250
|
+
_placeBrick: function( brick ) {
|
251
|
+
var $brick = $(brick),
|
252
|
+
colSpan, groupCount, groupY, groupColY, j;
|
253
|
+
|
254
|
+
//how many columns does this brick span
|
255
|
+
colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth );
|
256
|
+
colSpan = Math.min( colSpan, this.cols );
|
257
|
+
|
258
|
+
if ( colSpan === 1 ) {
|
259
|
+
// if brick spans only one column, just like singleMode
|
260
|
+
groupY = this.colYs;
|
261
|
+
} else {
|
262
|
+
// brick spans more than one column
|
263
|
+
// how many different places could this brick fit horizontally
|
264
|
+
groupCount = this.cols + 1 - colSpan;
|
265
|
+
groupY = [];
|
266
|
+
|
267
|
+
// for each group potential horizontal position
|
268
|
+
for ( j=0; j < groupCount; j++ ) {
|
269
|
+
// make an array of colY values for that one group
|
270
|
+
groupColY = this.colYs.slice( j, j+colSpan );
|
271
|
+
// and get the max value of the array
|
272
|
+
groupY[j] = Math.max.apply( Math, groupColY );
|
273
|
+
}
|
274
|
+
|
275
|
+
}
|
276
|
+
|
277
|
+
// get the minimum Y value from the columns
|
278
|
+
var minimumY = Math.min.apply( Math, groupY ),
|
279
|
+
shortCol = 0;
|
280
|
+
|
281
|
+
// Find index of short column, the first from the left
|
282
|
+
for (var i=0, len = groupY.length; i < len; i++) {
|
283
|
+
if ( groupY[i] === minimumY ) {
|
284
|
+
shortCol = i;
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
// position the brick
|
290
|
+
var position = {
|
291
|
+
top: minimumY + this.offset.y
|
292
|
+
};
|
293
|
+
// position.left or position.right
|
294
|
+
position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x;
|
295
|
+
this.styleQueue.push({ $el: $brick, style: position });
|
296
|
+
|
297
|
+
// apply setHeight to necessary columns
|
298
|
+
var setHeight = minimumY + $brick.outerHeight(true),
|
299
|
+
setSpan = this.cols + 1 - len;
|
300
|
+
for ( i=0; i < setSpan; i++ ) {
|
301
|
+
this.colYs[ shortCol + i ] = setHeight;
|
302
|
+
}
|
303
|
+
|
304
|
+
},
|
305
|
+
|
306
|
+
|
307
|
+
resize: function() {
|
308
|
+
var prevColCount = this.cols;
|
309
|
+
// get updated colCount
|
310
|
+
this._getColumns();
|
311
|
+
if ( this.isFluid || this.cols !== prevColCount ) {
|
312
|
+
// if column count has changed, trigger new layout
|
313
|
+
this._reLayout();
|
314
|
+
}
|
315
|
+
},
|
316
|
+
|
317
|
+
|
318
|
+
_reLayout : function( callback ) {
|
319
|
+
// reset columns
|
320
|
+
var i = this.cols;
|
321
|
+
this.colYs = [];
|
322
|
+
while (i--) {
|
323
|
+
this.colYs.push( 0 );
|
324
|
+
}
|
325
|
+
// apply layout logic to all bricks
|
326
|
+
this.layout( this.$bricks, callback );
|
327
|
+
},
|
328
|
+
|
329
|
+
// ====================== Convenience methods ======================
|
330
|
+
|
331
|
+
// goes through all children again and gets bricks in proper order
|
332
|
+
reloadItems : function() {
|
333
|
+
this.$bricks = this._getBricks( this.element.children() );
|
334
|
+
},
|
335
|
+
|
336
|
+
|
337
|
+
reload : function( callback ) {
|
338
|
+
this.reloadItems();
|
339
|
+
this._init( callback );
|
340
|
+
},
|
341
|
+
|
342
|
+
|
343
|
+
// convienence method for working with Infinite Scroll
|
344
|
+
appended : function( $content, isAnimatedFromBottom, callback ) {
|
345
|
+
if ( isAnimatedFromBottom ) {
|
346
|
+
// set new stuff to the bottom
|
347
|
+
this._filterFindBricks( $content ).css({ top: this.element.height() });
|
348
|
+
var instance = this;
|
349
|
+
setTimeout( function(){
|
350
|
+
instance._appended( $content, callback );
|
351
|
+
}, 1 );
|
352
|
+
} else {
|
353
|
+
this._appended( $content, callback );
|
354
|
+
}
|
355
|
+
},
|
356
|
+
|
357
|
+
_appended : function( $content, callback ) {
|
358
|
+
var $newBricks = this._getBricks( $content );
|
359
|
+
// add new bricks to brick pool
|
360
|
+
this.$bricks = this.$bricks.add( $newBricks );
|
361
|
+
this.layout( $newBricks, callback );
|
362
|
+
},
|
363
|
+
|
364
|
+
// removes elements from Masonry widget
|
365
|
+
remove : function( $content ) {
|
366
|
+
this.$bricks = this.$bricks.not( $content );
|
367
|
+
$content.remove();
|
368
|
+
},
|
369
|
+
|
370
|
+
// destroys widget, returns elements and container back (close) to original style
|
371
|
+
destroy : function() {
|
372
|
+
|
373
|
+
this.$bricks
|
374
|
+
.removeClass('masonry-brick')
|
375
|
+
.each(function(){
|
376
|
+
this.style.position = '';
|
377
|
+
this.style.top = '';
|
378
|
+
this.style.left = '';
|
379
|
+
});
|
380
|
+
|
381
|
+
// re-apply saved container styles
|
382
|
+
var elemStyle = this.element[0].style;
|
383
|
+
for ( var prop in this.originalStyle ) {
|
384
|
+
elemStyle[ prop ] = this.originalStyle[ prop ];
|
385
|
+
}
|
386
|
+
|
387
|
+
this.element
|
388
|
+
.unbind('.masonry')
|
389
|
+
.removeClass('masonry')
|
390
|
+
.removeData('masonry');
|
391
|
+
|
392
|
+
$(window).unbind('.masonry');
|
393
|
+
|
394
|
+
},
|
395
|
+
|
396
|
+
_getDistanceBetween : function(point1, point2){
|
397
|
+
var dx = point1.x - point2.x,
|
398
|
+
dy = point1.y - point2.y;
|
399
|
+
return Math.sqrt(dx*dx + dy*dy);
|
400
|
+
},
|
401
|
+
|
402
|
+
_getBrickPoint : function($brick){
|
403
|
+
var offset = $brick.offset();
|
404
|
+
return {
|
405
|
+
$brick: $brick,
|
406
|
+
x : offset.left + ($brick.outerWidth()/2),
|
407
|
+
y : offset.top + ($brick.outerHeight()/2)
|
408
|
+
};
|
409
|
+
},
|
410
|
+
|
411
|
+
_getClosestBrick : function(brick){
|
412
|
+
|
413
|
+
var $brick = $(brick),
|
414
|
+
_this = this,
|
415
|
+
dPoint = this._getBrickPoint($brick),
|
416
|
+
closest = null,
|
417
|
+
point, dist, $b, last, midpoint;
|
418
|
+
|
419
|
+
// find the index of the closest brick
|
420
|
+
this.$bricks.each(function(i, b){
|
421
|
+
|
422
|
+
$b = $(b);
|
423
|
+
point = _this._getBrickPoint($b);
|
424
|
+
|
425
|
+
// first check the distance from the brick center
|
426
|
+
dist = _this._getDistanceBetween(dPoint, point);
|
427
|
+
|
428
|
+
if(closest === null || dist < closest.dist){
|
429
|
+
closest = {
|
430
|
+
dist: dist,
|
431
|
+
$brick: $b,
|
432
|
+
index: (point.x > dPoint.x) ? i : i+1
|
433
|
+
};
|
434
|
+
}
|
435
|
+
|
436
|
+
// get the distance between the center of the line
|
437
|
+
// connecting the current and last block
|
438
|
+
if(typeof last !== 'undefined'){
|
439
|
+
midpoint = {
|
440
|
+
x : (last.x + point.x) / 2,
|
441
|
+
y : (last.y + point.y) / 2
|
442
|
+
};
|
443
|
+
dist = _this._getDistanceBetween(dPoint, midpoint);
|
444
|
+
if(dist < closest.dist){
|
445
|
+
closest = {
|
446
|
+
dist: dist,
|
447
|
+
$brick: $b,
|
448
|
+
index: (point.x > dPoint.x) ? i : i+1
|
449
|
+
};
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
last = point;
|
454
|
+
|
455
|
+
});
|
456
|
+
|
457
|
+
return closest;
|
458
|
+
},
|
459
|
+
|
460
|
+
_createGhostBrick : function($brick){
|
461
|
+
|
462
|
+
var closest = this._getClosestBrick($brick),
|
463
|
+
$ghost = $brick.clone().html('').addClass('drag-ghost'),
|
464
|
+
pos = $brick.position();
|
465
|
+
|
466
|
+
$ghost.css({
|
467
|
+
width : $brick.width(),
|
468
|
+
height : $brick.height()
|
469
|
+
});
|
470
|
+
|
471
|
+
this.$bricks.splice(closest.index, 0, $ghost[0]);
|
472
|
+
this.element.append($ghost);
|
473
|
+
this._reLayout();
|
474
|
+
|
475
|
+
return closest.index;
|
476
|
+
},
|
477
|
+
|
478
|
+
_initDrag : function($bricks){
|
479
|
+
|
480
|
+
var _this = this,
|
481
|
+
dragged = null,
|
482
|
+
pos, closest,
|
483
|
+
ghostInterval = null,
|
484
|
+
ghostIndex = -1,
|
485
|
+
ghostTime, ghostReset;
|
486
|
+
|
487
|
+
$bricks.bind('dragstart', function(e) {
|
488
|
+
|
489
|
+
// make sure we're dragging by the right thing
|
490
|
+
if(_this.options.dragHandleSelector !== null
|
491
|
+
&& !$(e.target).is(_this.options.dragHandleSelector)){
|
492
|
+
return false;
|
493
|
+
}
|
494
|
+
|
495
|
+
pos = $(this).position();
|
496
|
+
|
497
|
+
// add the dragClass
|
498
|
+
if(_this.options.dragClass !== null){
|
499
|
+
$(this).addClass(_this.options.dragClass);
|
500
|
+
}
|
501
|
+
|
502
|
+
// remove the brick being dragged from the array
|
503
|
+
dragged = this;
|
504
|
+
_this.$bricks = _this.$bricks.not(this);
|
505
|
+
_this._reLayout();
|
506
|
+
|
507
|
+
ghostTime = ghostReset = new Date().getTime();
|
508
|
+
|
509
|
+
ghostInterval = setInterval(function(){
|
510
|
+
if(ghostReset !== ghostTime){
|
511
|
+
if(ghostIndex !== -1){
|
512
|
+
|
513
|
+
// this kinda sucks because it doesn't use the original
|
514
|
+
// position of the bricks
|
515
|
+
if(_this._getClosestBrick($(dragged)).index === ghostIndex){
|
516
|
+
return;
|
517
|
+
}
|
518
|
+
|
519
|
+
$(_this.$bricks.splice(ghostIndex,1)).fadeOut(function(){
|
520
|
+
$(this).remove();
|
521
|
+
});
|
522
|
+
ghostIndex = -1;
|
523
|
+
_this._reLayout();
|
524
|
+
}
|
525
|
+
if(new Date().getTime() - ghostReset > 200){
|
526
|
+
ghostIndex = _this._createGhostBrick($(dragged));
|
527
|
+
ghostTime = ghostReset;
|
528
|
+
}
|
529
|
+
}
|
530
|
+
}, 100);
|
531
|
+
|
532
|
+
}).bind('drag', function(e, dd) {
|
533
|
+
|
534
|
+
$(this).css({
|
535
|
+
top : pos.top + dd.deltaY,
|
536
|
+
left : pos.left + dd.deltaX
|
537
|
+
});
|
538
|
+
|
539
|
+
ghostReset = new Date().getTime();
|
540
|
+
|
541
|
+
}).bind('dragend', function(e) {
|
542
|
+
|
543
|
+
// clear the ghosting interval
|
544
|
+
clearInterval(ghostInterval);
|
545
|
+
|
546
|
+
// remove the dragClass
|
547
|
+
if(_this.options.dragClass !== null){
|
548
|
+
$(this).removeClass(_this.options.dragClass);
|
549
|
+
}
|
550
|
+
|
551
|
+
// insert the brick back into the array
|
552
|
+
if(ghostIndex !== -1) {
|
553
|
+
var $ghost = $(_this.$bricks[ghostIndex]);
|
554
|
+
$ghost.fadeOut(function(){
|
555
|
+
$ghost.remove();
|
556
|
+
});
|
557
|
+
_this.$bricks[ghostIndex] = dragged;
|
558
|
+
ghostIndex = -1;
|
559
|
+
} else {
|
560
|
+
closest = _this._getClosestBrick(dragged);
|
561
|
+
_this.$bricks.splice(closest.index , 0, dragged);
|
562
|
+
}
|
563
|
+
dragged = null;
|
564
|
+
|
565
|
+
|
566
|
+
_this._reLayout();
|
567
|
+
});
|
568
|
+
}
|
569
|
+
|
570
|
+
};
|
571
|
+
|
572
|
+
|
573
|
+
// ======================= imagesLoaded Plugin ===============================
|
574
|
+
/*!
|
575
|
+
* jQuery imagesLoaded plugin v1.1.0
|
576
|
+
* http://github.com/desandro/imagesloaded
|
577
|
+
*
|
578
|
+
* MIT License. by Paul Irish et al.
|
579
|
+
*/
|
580
|
+
|
581
|
+
|
582
|
+
// $('#my-container').imagesLoaded(myFunction)
|
583
|
+
// or
|
584
|
+
// $('img').imagesLoaded(myFunction)
|
585
|
+
|
586
|
+
// execute a callback when all images have loaded.
|
587
|
+
// needed because .load() doesn't work on cached images
|
588
|
+
|
589
|
+
// callback function gets image collection as argument
|
590
|
+
// `this` is the container
|
591
|
+
|
592
|
+
$.fn.imagesLoaded = function( callback ) {
|
593
|
+
var $this = this,
|
594
|
+
$images = $this.find('img').add( $this.filter('img') ),
|
595
|
+
len = $images.length,
|
596
|
+
blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
|
597
|
+
loaded = [];
|
598
|
+
|
599
|
+
function triggerCallback() {
|
600
|
+
callback.call( $this, $images );
|
601
|
+
}
|
602
|
+
|
603
|
+
function imgLoaded( event ) {
|
604
|
+
var img = event.target;
|
605
|
+
if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
|
606
|
+
loaded.push( img );
|
607
|
+
if ( --len <= 0 ){
|
608
|
+
setTimeout( triggerCallback );
|
609
|
+
$images.unbind( '.imagesLoaded', imgLoaded );
|
610
|
+
}
|
611
|
+
}
|
612
|
+
}
|
613
|
+
|
614
|
+
// if no images, trigger immediately
|
615
|
+
if ( !len ) {
|
616
|
+
triggerCallback();
|
617
|
+
}
|
618
|
+
|
619
|
+
$images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() {
|
620
|
+
// cached images don't fire load sometimes, so we reset src.
|
621
|
+
var src = this.src;
|
622
|
+
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
623
|
+
// data uri bypasses webkit log warning (thx doug jones)
|
624
|
+
this.src = blank;
|
625
|
+
this.src = src;
|
626
|
+
});
|
627
|
+
|
628
|
+
return $this;
|
629
|
+
};
|
630
|
+
|
631
|
+
|
632
|
+
// helper function for logging errors
|
633
|
+
// $.error breaks jQuery chaining
|
634
|
+
var logError = function( message ) {
|
635
|
+
if ( window.console ) {
|
636
|
+
window.console.error( message );
|
637
|
+
}
|
638
|
+
};
|
639
|
+
|
640
|
+
// ======================= Plugin bridge ===============================
|
641
|
+
// leverages data method to either create or return $.Mason constructor
|
642
|
+
// A bit from jQuery UI
|
643
|
+
// https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
|
644
|
+
// A bit from jcarousel
|
645
|
+
// https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
|
646
|
+
|
647
|
+
$.fn.masonry = function( options ) {
|
648
|
+
if ( typeof options === 'string' ) {
|
649
|
+
// call method
|
650
|
+
var args = Array.prototype.slice.call( arguments, 1 );
|
651
|
+
|
652
|
+
this.each(function(){
|
653
|
+
var instance = $.data( this, 'masonry' );
|
654
|
+
if ( !instance ) {
|
655
|
+
logError( "cannot call methods on masonry prior to initialization; " +
|
656
|
+
"attempted to call method '" + options + "'" );
|
657
|
+
return;
|
658
|
+
}
|
659
|
+
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
|
660
|
+
logError( "no such method '" + options + "' for masonry instance" );
|
661
|
+
return;
|
662
|
+
}
|
663
|
+
// apply method
|
664
|
+
instance[ options ].apply( instance, args );
|
665
|
+
});
|
666
|
+
} else {
|
667
|
+
this.each(function() {
|
668
|
+
var instance = $.data( this, 'masonry' );
|
669
|
+
if ( instance ) {
|
670
|
+
// apply options & init
|
671
|
+
instance.option( options || {} );
|
672
|
+
instance._init();
|
673
|
+
} else {
|
674
|
+
// initialize new instance
|
675
|
+
$.data( this, 'masonry', new $.Mason( options, this ) );
|
676
|
+
}
|
677
|
+
});
|
678
|
+
}
|
679
|
+
return this;
|
680
|
+
};
|
681
|
+
|
682
|
+
})( window, jQuery );
|