masonry-rails 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +103 -3
- data/VERSION +1 -1
- data/masonry-rails.gemspec +9 -2
- data/vendor/assets/javascripts/jquery.isotope.js +1401 -0
- data/vendor/assets/javascripts/jquery.isotope.min.js +11 -0
- data/vendor/assets/stylesheets/isotope.css +65 -0
- data/vendor/assets/stylesheets/isotope/base.css +14 -0
- data/vendor/assets/stylesheets/isotope/colorshapes.css +37 -0
- data/vendor/assets/stylesheets/isotope/elements.css +97 -0
- data/vendor/assets/stylesheets/isotope/options.css +96 -0
- metadata +10 -3
data/README.md
CHANGED
@@ -13,11 +13,28 @@ This gem includes:
|
|
13
13
|
* jquery infinitescroll
|
14
14
|
* jquery event drag
|
15
15
|
|
16
|
+
Now [Isotope](http://isotope.metafizzy.co/index.html) is also included. Masonry and Isotope can even be used in combination!
|
17
|
+
|
16
18
|
For random content generation
|
17
19
|
|
18
20
|
* box maker
|
19
21
|
* loremimages
|
20
22
|
|
23
|
+
## Isotope assets
|
24
|
+
|
25
|
+
Javascript
|
26
|
+
|
27
|
+
* jquery.isotope.js
|
28
|
+
* jquery.isotope.min.js
|
29
|
+
|
30
|
+
Stylesheets
|
31
|
+
|
32
|
+
* isotope.css
|
33
|
+
|
34
|
+
More stylesheets in `isotope` folder in `vendor/assets
|
35
|
+
|
36
|
+
Please see Isotope [License](http://isotope.metafizzy.co/docs/license.html) for commercial use.
|
37
|
+
|
21
38
|
## Usage
|
22
39
|
|
23
40
|
In Gemfile
|
@@ -300,7 +317,7 @@ Put a `<nav>` under your main container. This will trigger loading the next page
|
|
300
317
|
</nav>
|
301
318
|
```
|
302
319
|
|
303
|
-
The link should load in a page containing elements that match `.
|
320
|
+
The link should load in a page containing elements that match `.box`
|
304
321
|
|
305
322
|
```html
|
306
323
|
<div class="box col3">
|
@@ -381,9 +398,28 @@ Or whichever animated loader icon you want to use ;)
|
|
381
398
|
|
382
399
|
You can combine masonry infinite scroll with Kaminari using suggestions here:
|
383
400
|
|
384
|
-
|
401
|
+
In the above example, to enable paging simply have the `pages/2.html` link respond with the next page and infinite scroll will automatically ajax-retrieve and render the elements indicated by `itemSelector` into the container.
|
402
|
+
|
403
|
+
`$container.masonry( 'appended', $newElems, true );`
|
385
404
|
|
386
|
-
|
405
|
+
In the page returned, ensure it contains a `#pagenav` element with a link to the next page.
|
406
|
+
|
407
|
+
```javascript
|
408
|
+
function( newElements ) {
|
409
|
+
// `this` matches the element you called the plugin on (fx. #container)
|
410
|
+
|
411
|
+
// get new #page-nav
|
412
|
+
var nexPageNav = $(this).find('#page-nav');
|
413
|
+
|
414
|
+
// substitute current #page-nav with new #page-nav from page loaded
|
415
|
+
$('#page-nav').replaceWith(nexPageNav);
|
416
|
+
|
417
|
+
# ...
|
418
|
+
```
|
419
|
+
|
420
|
+
See [Create-Infinite-Scrolling-with-jQuery](https://github.com/amatsuda/kaminari/wiki/How-To:-Create-Infinite-Scrolling-with-jQuery) for another example.
|
421
|
+
|
422
|
+
[Sausage](https://github.com/christophercliff/sausage) which used in this example is also included with this gem for convenience ;)
|
387
423
|
|
388
424
|
Stylesheets: `sausage/sausage.css`and `sausage/sausage.reset.css`
|
389
425
|
|
@@ -391,6 +427,70 @@ Javascript: `masonry/jquery.sausagemin.min.js`
|
|
391
427
|
|
392
428
|
For more see [sausage info](http://christophercliff.github.com/sausage)
|
393
429
|
|
430
|
+
The div with a class of 'page' is important for sausage. It is used to determine the different pages for the navigation on the right.
|
431
|
+
|
432
|
+
```html
|
433
|
+
<div id="container" class="transitions-enabled infinite-scroll clearfix">
|
434
|
+
<div class='page'>
|
435
|
+
<%= render @articles %>
|
436
|
+
</div>
|
437
|
+
</div>
|
438
|
+
```
|
439
|
+
|
440
|
+
```html
|
441
|
+
<div class='article box col<%= article.size %>'>
|
442
|
+
<h3><%= article.title %></h3>
|
443
|
+
<div class='author'>by <%= article.author %></div>
|
444
|
+
<div class='date'>by <%= article.created_at.to_s(:long) %></div>
|
445
|
+
<p>
|
446
|
+
<%= article.body %>
|
447
|
+
</p>
|
448
|
+
</div>
|
449
|
+
```
|
450
|
+
|
451
|
+
Where `article.size` returns a valid col size, fx 1 to 5.
|
452
|
+
|
453
|
+
```javascript
|
454
|
+
(function() {
|
455
|
+
var page = 1,
|
456
|
+
loading = false;
|
457
|
+
|
458
|
+
function nearBottomOfPage() {
|
459
|
+
return $(window).scrollTop() > $(document).height() - $(window).height() - 200;
|
460
|
+
}
|
461
|
+
|
462
|
+
$(window).scroll(function(){
|
463
|
+
if (loading) {
|
464
|
+
return;
|
465
|
+
}
|
466
|
+
|
467
|
+
if(nearBottomOfPage()) {
|
468
|
+
loading=true;
|
469
|
+
page++;
|
470
|
+
$.ajax({
|
471
|
+
url: '/articles?page=' + page,
|
472
|
+
type: 'get',
|
473
|
+
dataType: 'script',
|
474
|
+
success: function() {
|
475
|
+
$(window).sausage('draw');
|
476
|
+
loading=false;
|
477
|
+
}
|
478
|
+
});
|
479
|
+
}
|
480
|
+
});
|
481
|
+
|
482
|
+
$(window).sausage();
|
483
|
+
}());
|
484
|
+
```
|
485
|
+
|
486
|
+
It checks if the user scrolled to the bottom of the page. If that is the case, an ajax-request is sent to the `ArticlesController requesting the next page. After the ajax-request is completed successfully the sausage-navigation is redrawn.
|
487
|
+
|
488
|
+
When the ajax-request is sent to the ArticlesController we need to append the next page of articles. We need to create a file named `index.js.erb` to achieve this goal.
|
489
|
+
|
490
|
+
```javascript
|
491
|
+
$("#container").append("<div class='page'><%= escape_javascript(render(@articles)) %></div>");
|
492
|
+
```
|
493
|
+
|
394
494
|
Note: You need to configure Jquery UI to use sausage.
|
395
495
|
|
396
496
|
See: [railscast-endless-page](http://railscasts.com/episodes/114-endless-page-revised) for how to use endless pages with Rails using *will_paginate* gem.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.8
|
data/masonry-rails.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "masonry-rails"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kristian Mandrup"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-09-17"
|
13
13
|
s.description = "Masonry will rock your world!"
|
14
14
|
s.email = "kmandrup@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -64,6 +64,8 @@ Gem::Specification.new do |s|
|
|
64
64
|
"spec/index.html",
|
65
65
|
"spec/spec_helper.rb",
|
66
66
|
"vendor/assets/images/masonry/loader.gif",
|
67
|
+
"vendor/assets/javascripts/jquery.isotope.js",
|
68
|
+
"vendor/assets/javascripts/jquery.isotope.min.js",
|
67
69
|
"vendor/assets/javascripts/masonry/box-maker.js",
|
68
70
|
"vendor/assets/javascripts/masonry/jquery.event-drag.js",
|
69
71
|
"vendor/assets/javascripts/masonry/jquery.imagesloaded.js",
|
@@ -77,6 +79,11 @@ Gem::Specification.new do |s|
|
|
77
79
|
"vendor/assets/javascripts/masonry/jquery.sausage.js",
|
78
80
|
"vendor/assets/javascripts/masonry/jquery.sausage.min.js",
|
79
81
|
"vendor/assets/javascripts/masonry/modernizr-transitions.js",
|
82
|
+
"vendor/assets/stylesheets/isotope.css",
|
83
|
+
"vendor/assets/stylesheets/isotope/base.css",
|
84
|
+
"vendor/assets/stylesheets/isotope/colorshapes.css",
|
85
|
+
"vendor/assets/stylesheets/isotope/elements.css",
|
86
|
+
"vendor/assets/stylesheets/isotope/options.css",
|
80
87
|
"vendor/assets/stylesheets/masonry/basic.css",
|
81
88
|
"vendor/assets/stylesheets/masonry/centered.css",
|
82
89
|
"vendor/assets/stylesheets/masonry/fluid.css",
|
@@ -0,0 +1,1401 @@
|
|
1
|
+
/**
|
2
|
+
* Isotope v1.5.19
|
3
|
+
* An exquisite jQuery plugin for magical layouts
|
4
|
+
* http://isotope.metafizzy.co
|
5
|
+
*
|
6
|
+
* Commercial use requires one-time license fee
|
7
|
+
* http://metafizzy.co/#licenses
|
8
|
+
*
|
9
|
+
* Copyright 2012 David DeSandro / Metafizzy
|
10
|
+
*/
|
11
|
+
|
12
|
+
/*jshint asi: true, browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */
|
13
|
+
/*global jQuery: false */
|
14
|
+
|
15
|
+
(function( window, $, undefined ){
|
16
|
+
|
17
|
+
'use strict';
|
18
|
+
|
19
|
+
// get global vars
|
20
|
+
var document = window.document;
|
21
|
+
var Modernizr = window.Modernizr;
|
22
|
+
|
23
|
+
// helper function
|
24
|
+
var capitalize = function( str ) {
|
25
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
26
|
+
};
|
27
|
+
|
28
|
+
// ========================= getStyleProperty by kangax ===============================
|
29
|
+
// http://perfectionkills.com/feature-testing-css-properties/
|
30
|
+
|
31
|
+
var prefixes = 'Moz Webkit O Ms'.split(' ');
|
32
|
+
|
33
|
+
var getStyleProperty = function( propName ) {
|
34
|
+
var style = document.documentElement.style,
|
35
|
+
prefixed;
|
36
|
+
|
37
|
+
// test standard property first
|
38
|
+
if ( typeof style[propName] === 'string' ) {
|
39
|
+
return propName;
|
40
|
+
}
|
41
|
+
|
42
|
+
// capitalize
|
43
|
+
propName = capitalize( propName );
|
44
|
+
|
45
|
+
// test vendor specific properties
|
46
|
+
for ( var i=0, len = prefixes.length; i < len; i++ ) {
|
47
|
+
prefixed = prefixes[i] + propName;
|
48
|
+
if ( typeof style[ prefixed ] === 'string' ) {
|
49
|
+
return prefixed;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
};
|
53
|
+
|
54
|
+
var transformProp = getStyleProperty('transform'),
|
55
|
+
transitionProp = getStyleProperty('transitionProperty');
|
56
|
+
|
57
|
+
|
58
|
+
// ========================= miniModernizr ===============================
|
59
|
+
// <3<3<3 and thanks to Faruk and Paul for doing the heavy lifting
|
60
|
+
|
61
|
+
/*!
|
62
|
+
* Modernizr v1.6ish: miniModernizr for Isotope
|
63
|
+
* http://www.modernizr.com
|
64
|
+
*
|
65
|
+
* Developed by:
|
66
|
+
* - Faruk Ates http://farukat.es/
|
67
|
+
* - Paul Irish http://paulirish.com/
|
68
|
+
*
|
69
|
+
* Copyright (c) 2009-2010
|
70
|
+
* Dual-licensed under the BSD or MIT licenses.
|
71
|
+
* http://www.modernizr.com/license/
|
72
|
+
*/
|
73
|
+
|
74
|
+
/*
|
75
|
+
* This version whittles down the script just to check support for
|
76
|
+
* CSS transitions, transforms, and 3D transforms.
|
77
|
+
*/
|
78
|
+
|
79
|
+
var tests = {
|
80
|
+
csstransforms: function() {
|
81
|
+
return !!transformProp;
|
82
|
+
},
|
83
|
+
|
84
|
+
csstransforms3d: function() {
|
85
|
+
var test = !!getStyleProperty('perspective');
|
86
|
+
// double check for Chrome's false positive
|
87
|
+
if ( test ) {
|
88
|
+
var vendorCSSPrefixes = ' -o- -moz- -ms- -webkit- -khtml- '.split(' '),
|
89
|
+
mediaQuery = '@media (' + vendorCSSPrefixes.join('transform-3d),(') + 'modernizr)',
|
90
|
+
$style = $('<style>' + mediaQuery + '{#modernizr{height:3px}}' + '</style>')
|
91
|
+
.appendTo('head'),
|
92
|
+
$div = $('<div id="modernizr" />').appendTo('html');
|
93
|
+
|
94
|
+
test = $div.height() === 3;
|
95
|
+
|
96
|
+
$div.remove();
|
97
|
+
$style.remove();
|
98
|
+
}
|
99
|
+
return test;
|
100
|
+
},
|
101
|
+
|
102
|
+
csstransitions: function() {
|
103
|
+
return !!transitionProp;
|
104
|
+
}
|
105
|
+
};
|
106
|
+
|
107
|
+
var testName;
|
108
|
+
|
109
|
+
if ( Modernizr ) {
|
110
|
+
// if there's a previous Modernzir, check if there are necessary tests
|
111
|
+
for ( testName in tests) {
|
112
|
+
if ( !Modernizr.hasOwnProperty( testName ) ) {
|
113
|
+
// if test hasn't been run, use addTest to run it
|
114
|
+
Modernizr.addTest( testName, tests[ testName ] );
|
115
|
+
}
|
116
|
+
}
|
117
|
+
} else {
|
118
|
+
// or create new mini Modernizr that just has the 3 tests
|
119
|
+
Modernizr = window.Modernizr = {
|
120
|
+
_version : '1.6ish: miniModernizr for Isotope'
|
121
|
+
};
|
122
|
+
|
123
|
+
var classes = ' ';
|
124
|
+
var result;
|
125
|
+
|
126
|
+
// Run through tests
|
127
|
+
for ( testName in tests) {
|
128
|
+
result = tests[ testName ]();
|
129
|
+
Modernizr[ testName ] = result;
|
130
|
+
classes += ' ' + ( result ? '' : 'no-' ) + testName;
|
131
|
+
}
|
132
|
+
|
133
|
+
// Add the new classes to the <html> element.
|
134
|
+
$('html').addClass( classes );
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
// ========================= isoTransform ===============================
|
139
|
+
|
140
|
+
/**
|
141
|
+
* provides hooks for .css({ scale: value, translate: [x, y] })
|
142
|
+
* Progressively enhanced CSS transforms
|
143
|
+
* Uses hardware accelerated 3D transforms for Safari
|
144
|
+
* or falls back to 2D transforms.
|
145
|
+
*/
|
146
|
+
|
147
|
+
if ( Modernizr.csstransforms ) {
|
148
|
+
|
149
|
+
// i.e. transformFnNotations.scale(0.5) >> 'scale3d( 0.5, 0.5, 1)'
|
150
|
+
var transformFnNotations = Modernizr.csstransforms3d ?
|
151
|
+
{ // 3D transform functions
|
152
|
+
translate : function ( position ) {
|
153
|
+
return 'translate3d(' + position[0] + 'px, ' + position[1] + 'px, 0) ';
|
154
|
+
},
|
155
|
+
scale : function ( scale ) {
|
156
|
+
return 'scale3d(' + scale + ', ' + scale + ', 1) ';
|
157
|
+
}
|
158
|
+
} :
|
159
|
+
{ // 2D transform functions
|
160
|
+
translate : function ( position ) {
|
161
|
+
return 'translate(' + position[0] + 'px, ' + position[1] + 'px) ';
|
162
|
+
},
|
163
|
+
scale : function ( scale ) {
|
164
|
+
return 'scale(' + scale + ') ';
|
165
|
+
}
|
166
|
+
}
|
167
|
+
;
|
168
|
+
|
169
|
+
var setIsoTransform = function ( elem, name, value ) {
|
170
|
+
// unpack current transform data
|
171
|
+
var data = $.data( elem, 'isoTransform' ) || {},
|
172
|
+
newData = {},
|
173
|
+
fnName,
|
174
|
+
transformObj = {},
|
175
|
+
transformValue;
|
176
|
+
|
177
|
+
// i.e. newData.scale = 0.5
|
178
|
+
newData[ name ] = value;
|
179
|
+
// extend new value over current data
|
180
|
+
$.extend( data, newData );
|
181
|
+
|
182
|
+
for ( fnName in data ) {
|
183
|
+
transformValue = data[ fnName ];
|
184
|
+
transformObj[ fnName ] = transformFnNotations[ fnName ]( transformValue );
|
185
|
+
}
|
186
|
+
|
187
|
+
// get proper order
|
188
|
+
// ideally, we could loop through this give an array, but since we only have
|
189
|
+
// a couple transforms we're keeping track of, we'll do it like so
|
190
|
+
var translateFn = transformObj.translate || '',
|
191
|
+
scaleFn = transformObj.scale || '',
|
192
|
+
// sorting so translate always comes first
|
193
|
+
valueFns = translateFn + scaleFn;
|
194
|
+
|
195
|
+
// set data back in elem
|
196
|
+
$.data( elem, 'isoTransform', data );
|
197
|
+
|
198
|
+
// set name to vendor specific property
|
199
|
+
elem.style[ transformProp ] = valueFns;
|
200
|
+
};
|
201
|
+
|
202
|
+
// ==================== scale ===================
|
203
|
+
|
204
|
+
$.cssNumber.scale = true;
|
205
|
+
|
206
|
+
$.cssHooks.scale = {
|
207
|
+
set: function( elem, value ) {
|
208
|
+
// uncomment this bit if you want to properly parse strings
|
209
|
+
// if ( typeof value === 'string' ) {
|
210
|
+
// value = parseFloat( value );
|
211
|
+
// }
|
212
|
+
setIsoTransform( elem, 'scale', value );
|
213
|
+
},
|
214
|
+
get: function( elem, computed ) {
|
215
|
+
var transform = $.data( elem, 'isoTransform' );
|
216
|
+
return transform && transform.scale ? transform.scale : 1;
|
217
|
+
}
|
218
|
+
};
|
219
|
+
|
220
|
+
$.fx.step.scale = function( fx ) {
|
221
|
+
$.cssHooks.scale.set( fx.elem, fx.now+fx.unit );
|
222
|
+
};
|
223
|
+
|
224
|
+
|
225
|
+
// ==================== translate ===================
|
226
|
+
|
227
|
+
$.cssNumber.translate = true;
|
228
|
+
|
229
|
+
$.cssHooks.translate = {
|
230
|
+
set: function( elem, value ) {
|
231
|
+
|
232
|
+
// uncomment this bit if you want to properly parse strings
|
233
|
+
// if ( typeof value === 'string' ) {
|
234
|
+
// value = value.split(' ');
|
235
|
+
// }
|
236
|
+
//
|
237
|
+
// var i, val;
|
238
|
+
// for ( i = 0; i < 2; i++ ) {
|
239
|
+
// val = value[i];
|
240
|
+
// if ( typeof val === 'string' ) {
|
241
|
+
// val = parseInt( val );
|
242
|
+
// }
|
243
|
+
// }
|
244
|
+
|
245
|
+
setIsoTransform( elem, 'translate', value );
|
246
|
+
},
|
247
|
+
|
248
|
+
get: function( elem, computed ) {
|
249
|
+
var transform = $.data( elem, 'isoTransform' );
|
250
|
+
return transform && transform.translate ? transform.translate : [ 0, 0 ];
|
251
|
+
}
|
252
|
+
};
|
253
|
+
|
254
|
+
}
|
255
|
+
|
256
|
+
// ========================= get transition-end event ===============================
|
257
|
+
var transitionEndEvent, transitionDurProp;
|
258
|
+
|
259
|
+
if ( Modernizr.csstransitions ) {
|
260
|
+
transitionEndEvent = {
|
261
|
+
WebkitTransitionProperty: 'webkitTransitionEnd', // webkit
|
262
|
+
MozTransitionProperty: 'transitionend',
|
263
|
+
OTransitionProperty: 'oTransitionEnd',
|
264
|
+
transitionProperty: 'transitionEnd'
|
265
|
+
}[ transitionProp ];
|
266
|
+
|
267
|
+
transitionDurProp = getStyleProperty('transitionDuration');
|
268
|
+
}
|
269
|
+
|
270
|
+
// ========================= smartresize ===============================
|
271
|
+
|
272
|
+
/*
|
273
|
+
* smartresize: debounced resize event for jQuery
|
274
|
+
*
|
275
|
+
* latest version and complete README available on Github:
|
276
|
+
* https://github.com/louisremi/jquery.smartresize.js
|
277
|
+
*
|
278
|
+
* Copyright 2011 @louis_remi
|
279
|
+
* Licensed under the MIT license.
|
280
|
+
*/
|
281
|
+
|
282
|
+
var $event = $.event,
|
283
|
+
resizeTimeout;
|
284
|
+
|
285
|
+
$event.special.smartresize = {
|
286
|
+
setup: function() {
|
287
|
+
$(this).bind( "resize", $event.special.smartresize.handler );
|
288
|
+
},
|
289
|
+
teardown: function() {
|
290
|
+
$(this).unbind( "resize", $event.special.smartresize.handler );
|
291
|
+
},
|
292
|
+
handler: function( event, execAsap ) {
|
293
|
+
// Save the context
|
294
|
+
var context = this,
|
295
|
+
args = arguments;
|
296
|
+
|
297
|
+
// set correct event type
|
298
|
+
event.type = "smartresize";
|
299
|
+
|
300
|
+
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
|
301
|
+
resizeTimeout = setTimeout(function() {
|
302
|
+
jQuery.event.handle.apply( context, args );
|
303
|
+
}, execAsap === "execAsap"? 0 : 100 );
|
304
|
+
}
|
305
|
+
};
|
306
|
+
|
307
|
+
$.fn.smartresize = function( fn ) {
|
308
|
+
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
|
309
|
+
};
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
// ========================= Isotope ===============================
|
314
|
+
|
315
|
+
|
316
|
+
// our "Widget" object constructor
|
317
|
+
$.Isotope = function( options, element, callback ){
|
318
|
+
this.element = $( element );
|
319
|
+
|
320
|
+
this._create( options );
|
321
|
+
this._init( callback );
|
322
|
+
};
|
323
|
+
|
324
|
+
// styles of container element we want to keep track of
|
325
|
+
var isoContainerStyles = [ 'width', 'height' ];
|
326
|
+
|
327
|
+
var $window = $(window);
|
328
|
+
|
329
|
+
$.Isotope.settings = {
|
330
|
+
resizable: true,
|
331
|
+
layoutMode : 'masonry',
|
332
|
+
containerClass : 'isotope',
|
333
|
+
itemClass : 'isotope-item',
|
334
|
+
hiddenClass : 'isotope-hidden',
|
335
|
+
hiddenStyle: { opacity: 0, scale: 0.001 },
|
336
|
+
visibleStyle: { opacity: 1, scale: 1 },
|
337
|
+
containerStyle: {
|
338
|
+
position: 'relative',
|
339
|
+
overflow: 'hidden'
|
340
|
+
},
|
341
|
+
animationEngine: 'best-available',
|
342
|
+
animationOptions: {
|
343
|
+
queue: false,
|
344
|
+
duration: 800
|
345
|
+
},
|
346
|
+
sortBy : 'original-order',
|
347
|
+
sortAscending : true,
|
348
|
+
resizesContainer : true,
|
349
|
+
transformsEnabled: !$.browser.opera, // disable transforms in Opera
|
350
|
+
itemPositionDataEnabled: false
|
351
|
+
};
|
352
|
+
|
353
|
+
$.Isotope.prototype = {
|
354
|
+
|
355
|
+
// sets up widget
|
356
|
+
_create : function( options ) {
|
357
|
+
|
358
|
+
this.options = $.extend( {}, $.Isotope.settings, options );
|
359
|
+
|
360
|
+
this.styleQueue = [];
|
361
|
+
this.elemCount = 0;
|
362
|
+
|
363
|
+
// get original styles in case we re-apply them in .destroy()
|
364
|
+
var elemStyle = this.element[0].style;
|
365
|
+
this.originalStyle = {};
|
366
|
+
// keep track of container styles
|
367
|
+
var containerStyles = isoContainerStyles.slice(0);
|
368
|
+
for ( var prop in this.options.containerStyle ) {
|
369
|
+
containerStyles.push( prop );
|
370
|
+
}
|
371
|
+
for ( var i=0, len = containerStyles.length; i < len; i++ ) {
|
372
|
+
prop = containerStyles[i];
|
373
|
+
this.originalStyle[ prop ] = elemStyle[ prop ] || '';
|
374
|
+
}
|
375
|
+
// apply container style from options
|
376
|
+
this.element.css( this.options.containerStyle );
|
377
|
+
|
378
|
+
this._updateAnimationEngine();
|
379
|
+
this._updateUsingTransforms();
|
380
|
+
|
381
|
+
// sorting
|
382
|
+
var originalOrderSorter = {
|
383
|
+
'original-order' : function( $elem, instance ) {
|
384
|
+
instance.elemCount ++;
|
385
|
+
return instance.elemCount;
|
386
|
+
},
|
387
|
+
random : function() {
|
388
|
+
return Math.random();
|
389
|
+
}
|
390
|
+
};
|
391
|
+
|
392
|
+
this.options.getSortData = $.extend( this.options.getSortData, originalOrderSorter );
|
393
|
+
|
394
|
+
// need to get atoms
|
395
|
+
this.reloadItems();
|
396
|
+
|
397
|
+
// get top left position of where the bricks should be
|
398
|
+
this.offset = {
|
399
|
+
left: parseInt( ( this.element.css('padding-left') || 0 ), 10 ),
|
400
|
+
top: parseInt( ( this.element.css('padding-top') || 0 ), 10 )
|
401
|
+
};
|
402
|
+
|
403
|
+
// add isotope class first time around
|
404
|
+
var instance = this;
|
405
|
+
setTimeout( function() {
|
406
|
+
instance.element.addClass( instance.options.containerClass );
|
407
|
+
}, 0 );
|
408
|
+
|
409
|
+
// bind resize method
|
410
|
+
if ( this.options.resizable ) {
|
411
|
+
$window.bind( 'smartresize.isotope', function() {
|
412
|
+
instance.resize();
|
413
|
+
});
|
414
|
+
}
|
415
|
+
|
416
|
+
// dismiss all click events from hidden events
|
417
|
+
this.element.delegate( '.' + this.options.hiddenClass, 'click', function(){
|
418
|
+
return false;
|
419
|
+
});
|
420
|
+
|
421
|
+
},
|
422
|
+
|
423
|
+
_getAtoms : function( $elems ) {
|
424
|
+
var selector = this.options.itemSelector,
|
425
|
+
// filter & find
|
426
|
+
$atoms = selector ? $elems.filter( selector ).add( $elems.find( selector ) ) : $elems,
|
427
|
+
// base style for atoms
|
428
|
+
atomStyle = { position: 'absolute' };
|
429
|
+
|
430
|
+
if ( this.usingTransforms ) {
|
431
|
+
atomStyle.left = 0;
|
432
|
+
atomStyle.top = 0;
|
433
|
+
}
|
434
|
+
|
435
|
+
$atoms.css( atomStyle ).addClass( this.options.itemClass );
|
436
|
+
|
437
|
+
this.updateSortData( $atoms, true );
|
438
|
+
|
439
|
+
return $atoms;
|
440
|
+
},
|
441
|
+
|
442
|
+
// _init fires when your instance is first created
|
443
|
+
// (from the constructor above), and when you
|
444
|
+
// attempt to initialize the widget again (by the bridge)
|
445
|
+
// after it has already been initialized.
|
446
|
+
_init : function( callback ) {
|
447
|
+
|
448
|
+
this.$filteredAtoms = this._filter( this.$allAtoms );
|
449
|
+
this._sort();
|
450
|
+
this.reLayout( callback );
|
451
|
+
|
452
|
+
},
|
453
|
+
|
454
|
+
option : function( opts ){
|
455
|
+
// change options AFTER initialization:
|
456
|
+
// signature: $('#foo').bar({ cool:false });
|
457
|
+
if ( $.isPlainObject( opts ) ){
|
458
|
+
this.options = $.extend( true, this.options, opts );
|
459
|
+
|
460
|
+
// trigger _updateOptionName if it exists
|
461
|
+
var updateOptionFn;
|
462
|
+
for ( var optionName in opts ) {
|
463
|
+
updateOptionFn = '_update' + capitalize( optionName );
|
464
|
+
if ( this[ updateOptionFn ] ) {
|
465
|
+
this[ updateOptionFn ]();
|
466
|
+
}
|
467
|
+
}
|
468
|
+
}
|
469
|
+
},
|
470
|
+
|
471
|
+
// ====================== updaters ====================== //
|
472
|
+
// kind of like setters
|
473
|
+
|
474
|
+
_updateAnimationEngine : function() {
|
475
|
+
var animationEngine = this.options.animationEngine.toLowerCase().replace( /[ _\-]/g, '');
|
476
|
+
var isUsingJQueryAnimation;
|
477
|
+
// set applyStyleFnName
|
478
|
+
switch ( animationEngine ) {
|
479
|
+
case 'css' :
|
480
|
+
case 'none' :
|
481
|
+
isUsingJQueryAnimation = false;
|
482
|
+
break;
|
483
|
+
case 'jquery' :
|
484
|
+
isUsingJQueryAnimation = true;
|
485
|
+
break;
|
486
|
+
default : // best available
|
487
|
+
isUsingJQueryAnimation = !Modernizr.csstransitions;
|
488
|
+
}
|
489
|
+
this.isUsingJQueryAnimation = isUsingJQueryAnimation;
|
490
|
+
this._updateUsingTransforms();
|
491
|
+
},
|
492
|
+
|
493
|
+
_updateTransformsEnabled : function() {
|
494
|
+
this._updateUsingTransforms();
|
495
|
+
},
|
496
|
+
|
497
|
+
_updateUsingTransforms : function() {
|
498
|
+
var usingTransforms = this.usingTransforms = this.options.transformsEnabled &&
|
499
|
+
Modernizr.csstransforms && Modernizr.csstransitions && !this.isUsingJQueryAnimation;
|
500
|
+
|
501
|
+
// prevent scales when transforms are disabled
|
502
|
+
if ( !usingTransforms ) {
|
503
|
+
delete this.options.hiddenStyle.scale;
|
504
|
+
delete this.options.visibleStyle.scale;
|
505
|
+
}
|
506
|
+
|
507
|
+
this.getPositionStyles = usingTransforms ? this._translate : this._positionAbs;
|
508
|
+
},
|
509
|
+
|
510
|
+
|
511
|
+
// ====================== Filtering ======================
|
512
|
+
|
513
|
+
_filter : function( $atoms ) {
|
514
|
+
var filter = this.options.filter === '' ? '*' : this.options.filter;
|
515
|
+
|
516
|
+
if ( !filter ) {
|
517
|
+
return $atoms;
|
518
|
+
}
|
519
|
+
|
520
|
+
var hiddenClass = this.options.hiddenClass,
|
521
|
+
hiddenSelector = '.' + hiddenClass,
|
522
|
+
$hiddenAtoms = $atoms.filter( hiddenSelector ),
|
523
|
+
$atomsToShow = $hiddenAtoms;
|
524
|
+
|
525
|
+
if ( filter !== '*' ) {
|
526
|
+
$atomsToShow = $hiddenAtoms.filter( filter );
|
527
|
+
var $atomsToHide = $atoms.not( hiddenSelector ).not( filter ).addClass( hiddenClass );
|
528
|
+
this.styleQueue.push({ $el: $atomsToHide, style: this.options.hiddenStyle });
|
529
|
+
}
|
530
|
+
|
531
|
+
this.styleQueue.push({ $el: $atomsToShow, style: this.options.visibleStyle });
|
532
|
+
$atomsToShow.removeClass( hiddenClass );
|
533
|
+
|
534
|
+
return $atoms.filter( filter );
|
535
|
+
},
|
536
|
+
|
537
|
+
// ====================== Sorting ======================
|
538
|
+
|
539
|
+
updateSortData : function( $atoms, isIncrementingElemCount ) {
|
540
|
+
var instance = this,
|
541
|
+
getSortData = this.options.getSortData,
|
542
|
+
$this, sortData;
|
543
|
+
$atoms.each(function(){
|
544
|
+
$this = $(this);
|
545
|
+
sortData = {};
|
546
|
+
// get value for sort data based on fn( $elem ) passed in
|
547
|
+
for ( var key in getSortData ) {
|
548
|
+
if ( !isIncrementingElemCount && key === 'original-order' ) {
|
549
|
+
// keep original order original
|
550
|
+
sortData[ key ] = $.data( this, 'isotope-sort-data' )[ key ];
|
551
|
+
} else {
|
552
|
+
sortData[ key ] = getSortData[ key ]( $this, instance );
|
553
|
+
}
|
554
|
+
}
|
555
|
+
// apply sort data to element
|
556
|
+
$.data( this, 'isotope-sort-data', sortData );
|
557
|
+
});
|
558
|
+
},
|
559
|
+
|
560
|
+
// used on all the filtered atoms
|
561
|
+
_sort : function() {
|
562
|
+
|
563
|
+
var sortBy = this.options.sortBy,
|
564
|
+
getSorter = this._getSorter,
|
565
|
+
sortDir = this.options.sortAscending ? 1 : -1,
|
566
|
+
sortFn = function( alpha, beta ) {
|
567
|
+
var a = getSorter( alpha, sortBy ),
|
568
|
+
b = getSorter( beta, sortBy );
|
569
|
+
// fall back to original order if data matches
|
570
|
+
if ( a === b && sortBy !== 'original-order') {
|
571
|
+
a = getSorter( alpha, 'original-order' );
|
572
|
+
b = getSorter( beta, 'original-order' );
|
573
|
+
}
|
574
|
+
return ( ( a > b ) ? 1 : ( a < b ) ? -1 : 0 ) * sortDir;
|
575
|
+
};
|
576
|
+
|
577
|
+
this.$filteredAtoms.sort( sortFn );
|
578
|
+
},
|
579
|
+
|
580
|
+
_getSorter : function( elem, sortBy ) {
|
581
|
+
return $.data( elem, 'isotope-sort-data' )[ sortBy ];
|
582
|
+
},
|
583
|
+
|
584
|
+
// ====================== Layout Helpers ======================
|
585
|
+
|
586
|
+
_translate : function( x, y ) {
|
587
|
+
return { translate : [ x, y ] };
|
588
|
+
},
|
589
|
+
|
590
|
+
_positionAbs : function( x, y ) {
|
591
|
+
return { left: x, top: y };
|
592
|
+
},
|
593
|
+
|
594
|
+
_pushPosition : function( $elem, x, y ) {
|
595
|
+
x = Math.round( x + this.offset.left );
|
596
|
+
y = Math.round( y + this.offset.top );
|
597
|
+
var position = this.getPositionStyles( x, y );
|
598
|
+
this.styleQueue.push({ $el: $elem, style: position });
|
599
|
+
if ( this.options.itemPositionDataEnabled ) {
|
600
|
+
$elem.data('isotope-item-position', {x: x, y: y} );
|
601
|
+
}
|
602
|
+
},
|
603
|
+
|
604
|
+
|
605
|
+
// ====================== General Layout ======================
|
606
|
+
|
607
|
+
// used on collection of atoms (should be filtered, and sorted before )
|
608
|
+
// accepts atoms-to-be-laid-out to start with
|
609
|
+
layout : function( $elems, callback ) {
|
610
|
+
|
611
|
+
var layoutMode = this.options.layoutMode;
|
612
|
+
|
613
|
+
// layout logic
|
614
|
+
this[ '_' + layoutMode + 'Layout' ]( $elems );
|
615
|
+
|
616
|
+
// set the size of the container
|
617
|
+
if ( this.options.resizesContainer ) {
|
618
|
+
var containerStyle = this[ '_' + layoutMode + 'GetContainerSize' ]();
|
619
|
+
this.styleQueue.push({ $el: this.element, style: containerStyle });
|
620
|
+
}
|
621
|
+
|
622
|
+
this._processStyleQueue( $elems, callback );
|
623
|
+
|
624
|
+
this.isLaidOut = true;
|
625
|
+
},
|
626
|
+
|
627
|
+
_processStyleQueue : function( $elems, callback ) {
|
628
|
+
// are we animating the layout arrangement?
|
629
|
+
// use plugin-ish syntax for css or animate
|
630
|
+
var styleFn = !this.isLaidOut ? 'css' : (
|
631
|
+
this.isUsingJQueryAnimation ? 'animate' : 'css'
|
632
|
+
),
|
633
|
+
animOpts = this.options.animationOptions,
|
634
|
+
onLayout = this.options.onLayout,
|
635
|
+
objStyleFn, processor,
|
636
|
+
triggerCallbackNow, callbackFn;
|
637
|
+
|
638
|
+
// default styleQueue processor, may be overwritten down below
|
639
|
+
processor = function( i, obj ) {
|
640
|
+
obj.$el[ styleFn ]( obj.style, animOpts );
|
641
|
+
};
|
642
|
+
|
643
|
+
if ( this._isInserting && this.isUsingJQueryAnimation ) {
|
644
|
+
// if using styleQueue to insert items
|
645
|
+
processor = function( i, obj ) {
|
646
|
+
// only animate if it not being inserted
|
647
|
+
objStyleFn = obj.$el.hasClass('no-transition') ? 'css' : styleFn;
|
648
|
+
obj.$el[ objStyleFn ]( obj.style, animOpts );
|
649
|
+
};
|
650
|
+
|
651
|
+
} else if ( callback || onLayout || animOpts.complete ) {
|
652
|
+
// has callback
|
653
|
+
var isCallbackTriggered = false,
|
654
|
+
// array of possible callbacks to trigger
|
655
|
+
callbacks = [ callback, onLayout, animOpts.complete ],
|
656
|
+
instance = this;
|
657
|
+
triggerCallbackNow = true;
|
658
|
+
// trigger callback only once
|
659
|
+
callbackFn = function() {
|
660
|
+
if ( isCallbackTriggered ) {
|
661
|
+
return;
|
662
|
+
}
|
663
|
+
var hollaback;
|
664
|
+
for (var i=0, len = callbacks.length; i < len; i++) {
|
665
|
+
hollaback = callbacks[i];
|
666
|
+
if ( typeof hollaback === 'function' ) {
|
667
|
+
hollaback.call( instance.element, $elems, instance );
|
668
|
+
}
|
669
|
+
}
|
670
|
+
isCallbackTriggered = true;
|
671
|
+
};
|
672
|
+
|
673
|
+
if ( this.isUsingJQueryAnimation && styleFn === 'animate' ) {
|
674
|
+
// add callback to animation options
|
675
|
+
animOpts.complete = callbackFn;
|
676
|
+
triggerCallbackNow = false;
|
677
|
+
|
678
|
+
} else if ( Modernizr.csstransitions ) {
|
679
|
+
// detect if first item has transition
|
680
|
+
var i = 0,
|
681
|
+
firstItem = this.styleQueue[0],
|
682
|
+
testElem = firstItem && firstItem.$el,
|
683
|
+
styleObj;
|
684
|
+
// get first non-empty jQ object
|
685
|
+
while ( !testElem || !testElem.length ) {
|
686
|
+
styleObj = this.styleQueue[ i++ ];
|
687
|
+
// HACK: sometimes styleQueue[i] is undefined
|
688
|
+
if ( !styleObj ) {
|
689
|
+
return;
|
690
|
+
}
|
691
|
+
testElem = styleObj.$el;
|
692
|
+
}
|
693
|
+
// get transition duration of the first element in that object
|
694
|
+
// yeah, this is inexact
|
695
|
+
var duration = parseFloat( getComputedStyle( testElem[0] )[ transitionDurProp ] );
|
696
|
+
if ( duration > 0 ) {
|
697
|
+
processor = function( i, obj ) {
|
698
|
+
obj.$el[ styleFn ]( obj.style, animOpts )
|
699
|
+
// trigger callback at transition end
|
700
|
+
.one( transitionEndEvent, callbackFn );
|
701
|
+
};
|
702
|
+
triggerCallbackNow = false;
|
703
|
+
}
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
// process styleQueue
|
708
|
+
$.each( this.styleQueue, processor );
|
709
|
+
|
710
|
+
if ( triggerCallbackNow ) {
|
711
|
+
callbackFn();
|
712
|
+
}
|
713
|
+
|
714
|
+
// clear out queue for next time
|
715
|
+
this.styleQueue = [];
|
716
|
+
},
|
717
|
+
|
718
|
+
|
719
|
+
resize : function() {
|
720
|
+
if ( this[ '_' + this.options.layoutMode + 'ResizeChanged' ]() ) {
|
721
|
+
this.reLayout();
|
722
|
+
}
|
723
|
+
},
|
724
|
+
|
725
|
+
|
726
|
+
reLayout : function( callback ) {
|
727
|
+
|
728
|
+
this[ '_' + this.options.layoutMode + 'Reset' ]();
|
729
|
+
this.layout( this.$filteredAtoms, callback );
|
730
|
+
|
731
|
+
},
|
732
|
+
|
733
|
+
// ====================== Convenience methods ======================
|
734
|
+
|
735
|
+
// ====================== Adding items ======================
|
736
|
+
|
737
|
+
// adds a jQuery object of items to a isotope container
|
738
|
+
addItems : function( $content, callback ) {
|
739
|
+
var $newAtoms = this._getAtoms( $content );
|
740
|
+
// add new atoms to atoms pools
|
741
|
+
this.$allAtoms = this.$allAtoms.add( $newAtoms );
|
742
|
+
|
743
|
+
if ( callback ) {
|
744
|
+
callback( $newAtoms );
|
745
|
+
}
|
746
|
+
},
|
747
|
+
|
748
|
+
// convienence method for adding elements properly to any layout
|
749
|
+
// positions items, hides them, then animates them back in <--- very sezzy
|
750
|
+
insert : function( $content, callback ) {
|
751
|
+
// position items
|
752
|
+
this.element.append( $content );
|
753
|
+
|
754
|
+
var instance = this;
|
755
|
+
this.addItems( $content, function( $newAtoms ) {
|
756
|
+
var $newFilteredAtoms = instance._filter( $newAtoms );
|
757
|
+
instance._addHideAppended( $newFilteredAtoms );
|
758
|
+
instance._sort();
|
759
|
+
instance.reLayout();
|
760
|
+
instance._revealAppended( $newFilteredAtoms, callback );
|
761
|
+
});
|
762
|
+
|
763
|
+
},
|
764
|
+
|
765
|
+
// convienence method for working with Infinite Scroll
|
766
|
+
appended : function( $content, callback ) {
|
767
|
+
var instance = this;
|
768
|
+
this.addItems( $content, function( $newAtoms ) {
|
769
|
+
instance._addHideAppended( $newAtoms );
|
770
|
+
instance.layout( $newAtoms );
|
771
|
+
instance._revealAppended( $newAtoms, callback );
|
772
|
+
});
|
773
|
+
},
|
774
|
+
|
775
|
+
// adds new atoms, then hides them before positioning
|
776
|
+
_addHideAppended : function( $newAtoms ) {
|
777
|
+
this.$filteredAtoms = this.$filteredAtoms.add( $newAtoms );
|
778
|
+
$newAtoms.addClass('no-transition');
|
779
|
+
|
780
|
+
this._isInserting = true;
|
781
|
+
|
782
|
+
// apply hidden styles
|
783
|
+
this.styleQueue.push({ $el: $newAtoms, style: this.options.hiddenStyle });
|
784
|
+
},
|
785
|
+
|
786
|
+
// sets visible style on new atoms
|
787
|
+
_revealAppended : function( $newAtoms, callback ) {
|
788
|
+
var instance = this;
|
789
|
+
// apply visible style after a sec
|
790
|
+
setTimeout( function() {
|
791
|
+
// enable animation
|
792
|
+
$newAtoms.removeClass('no-transition');
|
793
|
+
// reveal newly inserted filtered elements
|
794
|
+
instance.styleQueue.push({ $el: $newAtoms, style: instance.options.visibleStyle });
|
795
|
+
instance._isInserting = false;
|
796
|
+
instance._processStyleQueue( $newAtoms, callback );
|
797
|
+
}, 10 );
|
798
|
+
},
|
799
|
+
|
800
|
+
// gathers all atoms
|
801
|
+
reloadItems : function() {
|
802
|
+
this.$allAtoms = this._getAtoms( this.element.children() );
|
803
|
+
},
|
804
|
+
|
805
|
+
// removes elements from Isotope widget
|
806
|
+
remove: function( $content, callback ) {
|
807
|
+
// remove elements from Isotope instance in callback
|
808
|
+
var instance = this;
|
809
|
+
// remove() as a callback, for after transition / animation
|
810
|
+
var removeContent = function() {
|
811
|
+
instance.$allAtoms = instance.$allAtoms.not( $content );
|
812
|
+
$content.remove();
|
813
|
+
if ( callback ) {
|
814
|
+
callback.call( instance.element );
|
815
|
+
}
|
816
|
+
};
|
817
|
+
|
818
|
+
if ( $content.filter( ':not(.' + this.options.hiddenClass + ')' ).length ) {
|
819
|
+
// if any non-hidden content needs to be removed
|
820
|
+
this.styleQueue.push({ $el: $content, style: this.options.hiddenStyle });
|
821
|
+
this.$filteredAtoms = this.$filteredAtoms.not( $content );
|
822
|
+
this._sort();
|
823
|
+
this.reLayout( removeContent );
|
824
|
+
} else {
|
825
|
+
// remove it now
|
826
|
+
removeContent();
|
827
|
+
}
|
828
|
+
|
829
|
+
},
|
830
|
+
|
831
|
+
shuffle : function( callback ) {
|
832
|
+
this.updateSortData( this.$allAtoms );
|
833
|
+
this.options.sortBy = 'random';
|
834
|
+
this._sort();
|
835
|
+
this.reLayout( callback );
|
836
|
+
},
|
837
|
+
|
838
|
+
// destroys widget, returns elements and container back (close) to original style
|
839
|
+
destroy : function() {
|
840
|
+
|
841
|
+
var usingTransforms = this.usingTransforms;
|
842
|
+
var options = this.options;
|
843
|
+
|
844
|
+
this.$allAtoms
|
845
|
+
.removeClass( options.hiddenClass + ' ' + options.itemClass )
|
846
|
+
.each(function(){
|
847
|
+
var style = this.style;
|
848
|
+
style.position = '';
|
849
|
+
style.top = '';
|
850
|
+
style.left = '';
|
851
|
+
style.opacity = '';
|
852
|
+
if ( usingTransforms ) {
|
853
|
+
style[ transformProp ] = '';
|
854
|
+
}
|
855
|
+
});
|
856
|
+
|
857
|
+
// re-apply saved container styles
|
858
|
+
var elemStyle = this.element[0].style;
|
859
|
+
for ( var prop in this.originalStyle ) {
|
860
|
+
elemStyle[ prop ] = this.originalStyle[ prop ];
|
861
|
+
}
|
862
|
+
|
863
|
+
this.element
|
864
|
+
.unbind('.isotope')
|
865
|
+
.undelegate( '.' + options.hiddenClass, 'click' )
|
866
|
+
.removeClass( options.containerClass )
|
867
|
+
.removeData('isotope');
|
868
|
+
|
869
|
+
$window.unbind('.isotope');
|
870
|
+
|
871
|
+
},
|
872
|
+
|
873
|
+
|
874
|
+
// ====================== LAYOUTS ======================
|
875
|
+
|
876
|
+
// calculates number of rows or columns
|
877
|
+
// requires columnWidth or rowHeight to be set on namespaced object
|
878
|
+
// i.e. this.masonry.columnWidth = 200
|
879
|
+
_getSegments : function( isRows ) {
|
880
|
+
var namespace = this.options.layoutMode,
|
881
|
+
measure = isRows ? 'rowHeight' : 'columnWidth',
|
882
|
+
size = isRows ? 'height' : 'width',
|
883
|
+
segmentsName = isRows ? 'rows' : 'cols',
|
884
|
+
containerSize = this.element[ size ](),
|
885
|
+
segments,
|
886
|
+
// i.e. options.masonry && options.masonry.columnWidth
|
887
|
+
segmentSize = this.options[ namespace ] && this.options[ namespace ][ measure ] ||
|
888
|
+
// or use the size of the first item, i.e. outerWidth
|
889
|
+
this.$filteredAtoms[ 'outer' + capitalize(size) ](true) ||
|
890
|
+
// if there's no items, use size of container
|
891
|
+
containerSize;
|
892
|
+
|
893
|
+
segments = Math.floor( containerSize / segmentSize );
|
894
|
+
segments = Math.max( segments, 1 );
|
895
|
+
|
896
|
+
// i.e. this.masonry.cols = ....
|
897
|
+
this[ namespace ][ segmentsName ] = segments;
|
898
|
+
// i.e. this.masonry.columnWidth = ...
|
899
|
+
this[ namespace ][ measure ] = segmentSize;
|
900
|
+
|
901
|
+
},
|
902
|
+
|
903
|
+
_checkIfSegmentsChanged : function( isRows ) {
|
904
|
+
var namespace = this.options.layoutMode,
|
905
|
+
segmentsName = isRows ? 'rows' : 'cols',
|
906
|
+
prevSegments = this[ namespace ][ segmentsName ];
|
907
|
+
// update cols/rows
|
908
|
+
this._getSegments( isRows );
|
909
|
+
// return if updated cols/rows is not equal to previous
|
910
|
+
return ( this[ namespace ][ segmentsName ] !== prevSegments );
|
911
|
+
},
|
912
|
+
|
913
|
+
// ====================== Masonry ======================
|
914
|
+
|
915
|
+
_masonryReset : function() {
|
916
|
+
// layout-specific props
|
917
|
+
this.masonry = {};
|
918
|
+
// FIXME shouldn't have to call this again
|
919
|
+
this._getSegments();
|
920
|
+
var i = this.masonry.cols;
|
921
|
+
this.masonry.colYs = [];
|
922
|
+
while (i--) {
|
923
|
+
this.masonry.colYs.push( 0 );
|
924
|
+
}
|
925
|
+
},
|
926
|
+
|
927
|
+
_masonryLayout : function( $elems ) {
|
928
|
+
var instance = this,
|
929
|
+
props = instance.masonry;
|
930
|
+
$elems.each(function(){
|
931
|
+
var $this = $(this),
|
932
|
+
//how many columns does this brick span
|
933
|
+
colSpan = Math.ceil( $this.outerWidth(true) / props.columnWidth );
|
934
|
+
colSpan = Math.min( colSpan, props.cols );
|
935
|
+
|
936
|
+
if ( colSpan === 1 ) {
|
937
|
+
// if brick spans only one column, just like singleMode
|
938
|
+
instance._masonryPlaceBrick( $this, props.colYs );
|
939
|
+
} else {
|
940
|
+
// brick spans more than one column
|
941
|
+
// how many different places could this brick fit horizontally
|
942
|
+
var groupCount = props.cols + 1 - colSpan,
|
943
|
+
groupY = [],
|
944
|
+
groupColY,
|
945
|
+
i;
|
946
|
+
|
947
|
+
// for each group potential horizontal position
|
948
|
+
for ( i=0; i < groupCount; i++ ) {
|
949
|
+
// make an array of colY values for that one group
|
950
|
+
groupColY = props.colYs.slice( i, i+colSpan );
|
951
|
+
// and get the max value of the array
|
952
|
+
groupY[i] = Math.max.apply( Math, groupColY );
|
953
|
+
}
|
954
|
+
|
955
|
+
instance._masonryPlaceBrick( $this, groupY );
|
956
|
+
}
|
957
|
+
});
|
958
|
+
},
|
959
|
+
|
960
|
+
// worker method that places brick in the columnSet
|
961
|
+
// with the the minY
|
962
|
+
_masonryPlaceBrick : function( $brick, setY ) {
|
963
|
+
// get the minimum Y value from the columns
|
964
|
+
var minimumY = Math.min.apply( Math, setY ),
|
965
|
+
shortCol = 0;
|
966
|
+
|
967
|
+
// Find index of short column, the first from the left
|
968
|
+
for (var i=0, len = setY.length; i < len; i++) {
|
969
|
+
if ( setY[i] === minimumY ) {
|
970
|
+
shortCol = i;
|
971
|
+
break;
|
972
|
+
}
|
973
|
+
}
|
974
|
+
|
975
|
+
// position the brick
|
976
|
+
var x = this.masonry.columnWidth * shortCol,
|
977
|
+
y = minimumY;
|
978
|
+
this._pushPosition( $brick, x, y );
|
979
|
+
|
980
|
+
// apply setHeight to necessary columns
|
981
|
+
var setHeight = minimumY + $brick.outerHeight(true),
|
982
|
+
setSpan = this.masonry.cols + 1 - len;
|
983
|
+
for ( i=0; i < setSpan; i++ ) {
|
984
|
+
this.masonry.colYs[ shortCol + i ] = setHeight;
|
985
|
+
}
|
986
|
+
|
987
|
+
},
|
988
|
+
|
989
|
+
_masonryGetContainerSize : function() {
|
990
|
+
var containerHeight = Math.max.apply( Math, this.masonry.colYs );
|
991
|
+
return { height: containerHeight };
|
992
|
+
},
|
993
|
+
|
994
|
+
_masonryResizeChanged : function() {
|
995
|
+
return this._checkIfSegmentsChanged();
|
996
|
+
},
|
997
|
+
|
998
|
+
// ====================== fitRows ======================
|
999
|
+
|
1000
|
+
_fitRowsReset : function() {
|
1001
|
+
this.fitRows = {
|
1002
|
+
x : 0,
|
1003
|
+
y : 0,
|
1004
|
+
height : 0
|
1005
|
+
};
|
1006
|
+
},
|
1007
|
+
|
1008
|
+
_fitRowsLayout : function( $elems ) {
|
1009
|
+
var instance = this,
|
1010
|
+
containerWidth = this.element.width(),
|
1011
|
+
props = this.fitRows;
|
1012
|
+
|
1013
|
+
$elems.each( function() {
|
1014
|
+
var $this = $(this),
|
1015
|
+
atomW = $this.outerWidth(true),
|
1016
|
+
atomH = $this.outerHeight(true);
|
1017
|
+
|
1018
|
+
if ( props.x !== 0 && atomW + props.x > containerWidth ) {
|
1019
|
+
// if this element cannot fit in the current row
|
1020
|
+
props.x = 0;
|
1021
|
+
props.y = props.height;
|
1022
|
+
}
|
1023
|
+
|
1024
|
+
// position the atom
|
1025
|
+
instance._pushPosition( $this, props.x, props.y );
|
1026
|
+
|
1027
|
+
props.height = Math.max( props.y + atomH, props.height );
|
1028
|
+
props.x += atomW;
|
1029
|
+
|
1030
|
+
});
|
1031
|
+
},
|
1032
|
+
|
1033
|
+
_fitRowsGetContainerSize : function () {
|
1034
|
+
return { height : this.fitRows.height };
|
1035
|
+
},
|
1036
|
+
|
1037
|
+
_fitRowsResizeChanged : function() {
|
1038
|
+
return true;
|
1039
|
+
},
|
1040
|
+
|
1041
|
+
|
1042
|
+
// ====================== cellsByRow ======================
|
1043
|
+
|
1044
|
+
_cellsByRowReset : function() {
|
1045
|
+
this.cellsByRow = {
|
1046
|
+
index : 0
|
1047
|
+
};
|
1048
|
+
// get this.cellsByRow.columnWidth
|
1049
|
+
this._getSegments();
|
1050
|
+
// get this.cellsByRow.rowHeight
|
1051
|
+
this._getSegments(true);
|
1052
|
+
},
|
1053
|
+
|
1054
|
+
_cellsByRowLayout : function( $elems ) {
|
1055
|
+
var instance = this,
|
1056
|
+
props = this.cellsByRow;
|
1057
|
+
$elems.each( function(){
|
1058
|
+
var $this = $(this),
|
1059
|
+
col = props.index % props.cols,
|
1060
|
+
row = Math.floor( props.index / props.cols ),
|
1061
|
+
x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2,
|
1062
|
+
y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2;
|
1063
|
+
instance._pushPosition( $this, x, y );
|
1064
|
+
props.index ++;
|
1065
|
+
});
|
1066
|
+
},
|
1067
|
+
|
1068
|
+
_cellsByRowGetContainerSize : function() {
|
1069
|
+
return { height : Math.ceil( this.$filteredAtoms.length / this.cellsByRow.cols ) * this.cellsByRow.rowHeight + this.offset.top };
|
1070
|
+
},
|
1071
|
+
|
1072
|
+
_cellsByRowResizeChanged : function() {
|
1073
|
+
return this._checkIfSegmentsChanged();
|
1074
|
+
},
|
1075
|
+
|
1076
|
+
|
1077
|
+
// ====================== straightDown ======================
|
1078
|
+
|
1079
|
+
_straightDownReset : function() {
|
1080
|
+
this.straightDown = {
|
1081
|
+
y : 0
|
1082
|
+
};
|
1083
|
+
},
|
1084
|
+
|
1085
|
+
_straightDownLayout : function( $elems ) {
|
1086
|
+
var instance = this;
|
1087
|
+
$elems.each( function( i ){
|
1088
|
+
var $this = $(this);
|
1089
|
+
instance._pushPosition( $this, 0, instance.straightDown.y );
|
1090
|
+
instance.straightDown.y += $this.outerHeight(true);
|
1091
|
+
});
|
1092
|
+
},
|
1093
|
+
|
1094
|
+
_straightDownGetContainerSize : function() {
|
1095
|
+
return { height : this.straightDown.y };
|
1096
|
+
},
|
1097
|
+
|
1098
|
+
_straightDownResizeChanged : function() {
|
1099
|
+
return true;
|
1100
|
+
},
|
1101
|
+
|
1102
|
+
|
1103
|
+
// ====================== masonryHorizontal ======================
|
1104
|
+
|
1105
|
+
_masonryHorizontalReset : function() {
|
1106
|
+
// layout-specific props
|
1107
|
+
this.masonryHorizontal = {};
|
1108
|
+
// FIXME shouldn't have to call this again
|
1109
|
+
this._getSegments( true );
|
1110
|
+
var i = this.masonryHorizontal.rows;
|
1111
|
+
this.masonryHorizontal.rowXs = [];
|
1112
|
+
while (i--) {
|
1113
|
+
this.masonryHorizontal.rowXs.push( 0 );
|
1114
|
+
}
|
1115
|
+
},
|
1116
|
+
|
1117
|
+
_masonryHorizontalLayout : function( $elems ) {
|
1118
|
+
var instance = this,
|
1119
|
+
props = instance.masonryHorizontal;
|
1120
|
+
$elems.each(function(){
|
1121
|
+
var $this = $(this),
|
1122
|
+
//how many rows does this brick span
|
1123
|
+
rowSpan = Math.ceil( $this.outerHeight(true) / props.rowHeight );
|
1124
|
+
rowSpan = Math.min( rowSpan, props.rows );
|
1125
|
+
|
1126
|
+
if ( rowSpan === 1 ) {
|
1127
|
+
// if brick spans only one column, just like singleMode
|
1128
|
+
instance._masonryHorizontalPlaceBrick( $this, props.rowXs );
|
1129
|
+
} else {
|
1130
|
+
// brick spans more than one row
|
1131
|
+
// how many different places could this brick fit horizontally
|
1132
|
+
var groupCount = props.rows + 1 - rowSpan,
|
1133
|
+
groupX = [],
|
1134
|
+
groupRowX, i;
|
1135
|
+
|
1136
|
+
// for each group potential horizontal position
|
1137
|
+
for ( i=0; i < groupCount; i++ ) {
|
1138
|
+
// make an array of colY values for that one group
|
1139
|
+
groupRowX = props.rowXs.slice( i, i+rowSpan );
|
1140
|
+
// and get the max value of the array
|
1141
|
+
groupX[i] = Math.max.apply( Math, groupRowX );
|
1142
|
+
}
|
1143
|
+
|
1144
|
+
instance._masonryHorizontalPlaceBrick( $this, groupX );
|
1145
|
+
}
|
1146
|
+
});
|
1147
|
+
},
|
1148
|
+
|
1149
|
+
_masonryHorizontalPlaceBrick : function( $brick, setX ) {
|
1150
|
+
// get the minimum Y value from the columns
|
1151
|
+
var minimumX = Math.min.apply( Math, setX ),
|
1152
|
+
smallRow = 0;
|
1153
|
+
// Find index of smallest row, the first from the top
|
1154
|
+
for (var i=0, len = setX.length; i < len; i++) {
|
1155
|
+
if ( setX[i] === minimumX ) {
|
1156
|
+
smallRow = i;
|
1157
|
+
break;
|
1158
|
+
}
|
1159
|
+
}
|
1160
|
+
|
1161
|
+
// position the brick
|
1162
|
+
var x = minimumX,
|
1163
|
+
y = this.masonryHorizontal.rowHeight * smallRow;
|
1164
|
+
this._pushPosition( $brick, x, y );
|
1165
|
+
|
1166
|
+
// apply setHeight to necessary columns
|
1167
|
+
var setWidth = minimumX + $brick.outerWidth(true),
|
1168
|
+
setSpan = this.masonryHorizontal.rows + 1 - len;
|
1169
|
+
for ( i=0; i < setSpan; i++ ) {
|
1170
|
+
this.masonryHorizontal.rowXs[ smallRow + i ] = setWidth;
|
1171
|
+
}
|
1172
|
+
},
|
1173
|
+
|
1174
|
+
_masonryHorizontalGetContainerSize : function() {
|
1175
|
+
var containerWidth = Math.max.apply( Math, this.masonryHorizontal.rowXs );
|
1176
|
+
return { width: containerWidth };
|
1177
|
+
},
|
1178
|
+
|
1179
|
+
_masonryHorizontalResizeChanged : function() {
|
1180
|
+
return this._checkIfSegmentsChanged(true);
|
1181
|
+
},
|
1182
|
+
|
1183
|
+
|
1184
|
+
// ====================== fitColumns ======================
|
1185
|
+
|
1186
|
+
_fitColumnsReset : function() {
|
1187
|
+
this.fitColumns = {
|
1188
|
+
x : 0,
|
1189
|
+
y : 0,
|
1190
|
+
width : 0
|
1191
|
+
};
|
1192
|
+
},
|
1193
|
+
|
1194
|
+
_fitColumnsLayout : function( $elems ) {
|
1195
|
+
var instance = this,
|
1196
|
+
containerHeight = this.element.height(),
|
1197
|
+
props = this.fitColumns;
|
1198
|
+
$elems.each( function() {
|
1199
|
+
var $this = $(this),
|
1200
|
+
atomW = $this.outerWidth(true),
|
1201
|
+
atomH = $this.outerHeight(true);
|
1202
|
+
|
1203
|
+
if ( props.y !== 0 && atomH + props.y > containerHeight ) {
|
1204
|
+
// if this element cannot fit in the current column
|
1205
|
+
props.x = props.width;
|
1206
|
+
props.y = 0;
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
// position the atom
|
1210
|
+
instance._pushPosition( $this, props.x, props.y );
|
1211
|
+
|
1212
|
+
props.width = Math.max( props.x + atomW, props.width );
|
1213
|
+
props.y += atomH;
|
1214
|
+
|
1215
|
+
});
|
1216
|
+
},
|
1217
|
+
|
1218
|
+
_fitColumnsGetContainerSize : function () {
|
1219
|
+
return { width : this.fitColumns.width };
|
1220
|
+
},
|
1221
|
+
|
1222
|
+
_fitColumnsResizeChanged : function() {
|
1223
|
+
return true;
|
1224
|
+
},
|
1225
|
+
|
1226
|
+
|
1227
|
+
|
1228
|
+
// ====================== cellsByColumn ======================
|
1229
|
+
|
1230
|
+
_cellsByColumnReset : function() {
|
1231
|
+
this.cellsByColumn = {
|
1232
|
+
index : 0
|
1233
|
+
};
|
1234
|
+
// get this.cellsByColumn.columnWidth
|
1235
|
+
this._getSegments();
|
1236
|
+
// get this.cellsByColumn.rowHeight
|
1237
|
+
this._getSegments(true);
|
1238
|
+
},
|
1239
|
+
|
1240
|
+
_cellsByColumnLayout : function( $elems ) {
|
1241
|
+
var instance = this,
|
1242
|
+
props = this.cellsByColumn;
|
1243
|
+
$elems.each( function(){
|
1244
|
+
var $this = $(this),
|
1245
|
+
col = Math.floor( props.index / props.rows ),
|
1246
|
+
row = props.index % props.rows,
|
1247
|
+
x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2,
|
1248
|
+
y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2;
|
1249
|
+
instance._pushPosition( $this, x, y );
|
1250
|
+
props.index ++;
|
1251
|
+
});
|
1252
|
+
},
|
1253
|
+
|
1254
|
+
_cellsByColumnGetContainerSize : function() {
|
1255
|
+
return { width : Math.ceil( this.$filteredAtoms.length / this.cellsByColumn.rows ) * this.cellsByColumn.columnWidth };
|
1256
|
+
},
|
1257
|
+
|
1258
|
+
_cellsByColumnResizeChanged : function() {
|
1259
|
+
return this._checkIfSegmentsChanged(true);
|
1260
|
+
},
|
1261
|
+
|
1262
|
+
// ====================== straightAcross ======================
|
1263
|
+
|
1264
|
+
_straightAcrossReset : function() {
|
1265
|
+
this.straightAcross = {
|
1266
|
+
x : 0
|
1267
|
+
};
|
1268
|
+
},
|
1269
|
+
|
1270
|
+
_straightAcrossLayout : function( $elems ) {
|
1271
|
+
var instance = this;
|
1272
|
+
$elems.each( function( i ){
|
1273
|
+
var $this = $(this);
|
1274
|
+
instance._pushPosition( $this, instance.straightAcross.x, 0 );
|
1275
|
+
instance.straightAcross.x += $this.outerWidth(true);
|
1276
|
+
});
|
1277
|
+
},
|
1278
|
+
|
1279
|
+
_straightAcrossGetContainerSize : function() {
|
1280
|
+
return { width : this.straightAcross.x };
|
1281
|
+
},
|
1282
|
+
|
1283
|
+
_straightAcrossResizeChanged : function() {
|
1284
|
+
return true;
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
};
|
1288
|
+
|
1289
|
+
|
1290
|
+
// ======================= imagesLoaded Plugin ===============================
|
1291
|
+
/*!
|
1292
|
+
* jQuery imagesLoaded plugin v1.1.0
|
1293
|
+
* http://github.com/desandro/imagesloaded
|
1294
|
+
*
|
1295
|
+
* MIT License. by Paul Irish et al.
|
1296
|
+
*/
|
1297
|
+
|
1298
|
+
|
1299
|
+
// $('#my-container').imagesLoaded(myFunction)
|
1300
|
+
// or
|
1301
|
+
// $('img').imagesLoaded(myFunction)
|
1302
|
+
|
1303
|
+
// execute a callback when all images have loaded.
|
1304
|
+
// needed because .load() doesn't work on cached images
|
1305
|
+
|
1306
|
+
// callback function gets image collection as argument
|
1307
|
+
// `this` is the container
|
1308
|
+
|
1309
|
+
$.fn.imagesLoaded = function( callback ) {
|
1310
|
+
var $this = this,
|
1311
|
+
$images = $this.find('img').add( $this.filter('img') ),
|
1312
|
+
len = $images.length,
|
1313
|
+
blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
|
1314
|
+
loaded = [];
|
1315
|
+
|
1316
|
+
function triggerCallback() {
|
1317
|
+
callback.call( $this, $images );
|
1318
|
+
}
|
1319
|
+
|
1320
|
+
function imgLoaded( event ) {
|
1321
|
+
var img = event.target;
|
1322
|
+
if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
|
1323
|
+
loaded.push( img );
|
1324
|
+
if ( --len <= 0 ){
|
1325
|
+
setTimeout( triggerCallback );
|
1326
|
+
$images.unbind( '.imagesLoaded', imgLoaded );
|
1327
|
+
}
|
1328
|
+
}
|
1329
|
+
}
|
1330
|
+
|
1331
|
+
// if no images, trigger immediately
|
1332
|
+
if ( !len ) {
|
1333
|
+
triggerCallback();
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
$images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() {
|
1337
|
+
// cached images don't fire load sometimes, so we reset src.
|
1338
|
+
var src = this.src;
|
1339
|
+
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
1340
|
+
// data uri bypasses webkit log warning (thx doug jones)
|
1341
|
+
this.src = blank;
|
1342
|
+
this.src = src;
|
1343
|
+
});
|
1344
|
+
|
1345
|
+
return $this;
|
1346
|
+
};
|
1347
|
+
|
1348
|
+
|
1349
|
+
// helper function for logging errors
|
1350
|
+
// $.error breaks jQuery chaining
|
1351
|
+
var logError = function( message ) {
|
1352
|
+
if ( window.console ) {
|
1353
|
+
window.console.error( message );
|
1354
|
+
}
|
1355
|
+
};
|
1356
|
+
|
1357
|
+
// ======================= Plugin bridge ===============================
|
1358
|
+
// leverages data method to either create or return $.Isotope constructor
|
1359
|
+
// A bit from jQuery UI
|
1360
|
+
// https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
|
1361
|
+
// A bit from jcarousel
|
1362
|
+
// https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
|
1363
|
+
|
1364
|
+
$.fn.isotope = function( options, callback ) {
|
1365
|
+
if ( typeof options === 'string' ) {
|
1366
|
+
// call method
|
1367
|
+
var args = Array.prototype.slice.call( arguments, 1 );
|
1368
|
+
|
1369
|
+
this.each(function(){
|
1370
|
+
var instance = $.data( this, 'isotope' );
|
1371
|
+
if ( !instance ) {
|
1372
|
+
logError( "cannot call methods on isotope prior to initialization; " +
|
1373
|
+
"attempted to call method '" + options + "'" );
|
1374
|
+
return;
|
1375
|
+
}
|
1376
|
+
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
|
1377
|
+
logError( "no such method '" + options + "' for isotope instance" );
|
1378
|
+
return;
|
1379
|
+
}
|
1380
|
+
// apply method
|
1381
|
+
instance[ options ].apply( instance, args );
|
1382
|
+
});
|
1383
|
+
} else {
|
1384
|
+
this.each(function() {
|
1385
|
+
var instance = $.data( this, 'isotope' );
|
1386
|
+
if ( instance ) {
|
1387
|
+
// apply options & init
|
1388
|
+
instance.option( options );
|
1389
|
+
instance._init( callback );
|
1390
|
+
} else {
|
1391
|
+
// initialize new instance
|
1392
|
+
$.data( this, 'isotope', new $.Isotope( options, this, callback ) );
|
1393
|
+
}
|
1394
|
+
});
|
1395
|
+
}
|
1396
|
+
// return jQuery object
|
1397
|
+
// so plugin methods do not have to
|
1398
|
+
return this;
|
1399
|
+
};
|
1400
|
+
|
1401
|
+
})( window, jQuery );
|