PluginProbe
Powerkit – Supercharge your WordPress Site / 2.3.7
Powerkit – Supercharge your WordPress Site v2.3.7
3.1.1 3.1.0 3.0.9 3.0.8 trunk 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 All 87 releases
powerkit / modules / posts / public / js / colcade.js

colcade.js in Powerkit – Supercharge your WordPress Site 2.3.7, at modules/posts/public/js/colcade.js

367 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * Colcade v0.2.0
3 * Lightweight masonry layout
4 * by David DeSandro
5 * MIT license
6 */
7
8 /*jshint browser: true, undef: true, unused: true */
9
10 ( function( window, factory ) {
11 // universal module definition
12 /*jshint strict: false */
13 /*global define: false, module: false */
14 if ( typeof define == 'function' && define.amd ) {
15 // AMD
16 define( factory );
17 } else if ( typeof module == 'object' && module.exports ) {
18 // CommonJS
19 module.exports = factory();
20 } else {
21 // browser global
22 window.Colcade = factory();
23 }
24
25 }( window, function factory() {
26
27 // -------------------------- Colcade -------------------------- //
28
29 function Colcade( element, options ) {
30 element = getQueryElement( element );
31
32 // do not initialize twice on same element
33 if ( element && element.colcadeGUID ) {
34 var instance = instances[ element.colcadeGUID ];
35 instance.option( options );
36 return instance;
37 }
38
39 this.element = element;
40 // options
41 this.options = {};
42 this.option( options );
43 // kick things off
44 this.create();
45 }
46
47 var proto = Colcade.prototype;
48
49 proto.option = function( options ) {
50 this.options = extend( this.options, options );
51 };
52
53 // globally unique identifiers
54 var GUID = 0;
55 // internal store of all Colcade intances
56 var instances = {};
57
58 proto.create = function() {
59 this.errorCheck();
60 // add guid for Colcade.data
61 var guid = this.guid = ++GUID;
62 this.element.colcadeGUID = guid;
63 instances[ guid ] = this; // associate via id
64 // update initial properties & layout
65 this.reload();
66 // events
67 this._windowResizeHandler = this.onWindowResize.bind( this );
68 this._loadHandler = this.onLoad.bind( this );
69 window.addEventListener( 'resize', this._windowResizeHandler );
70 this.element.addEventListener( 'load', this._loadHandler, true );
71 };
72
73 proto.errorCheck = function() {
74 var errors = [];
75 if ( !this.element ) {
76 errors.push( 'Bad element: ' + this.element );
77 }
78 if ( !this.options.columns ) {
79 errors.push( 'columns option required: ' + this.options.columns );
80 }
81 if ( !this.options.items ) {
82 errors.push( 'items option required: ' + this.options.items );
83 }
84
85 if ( errors.length ) {
86 throw new Error( '[Colcade error] ' + errors.join('. ') );
87 }
88 };
89
90 // update properties and do layout
91 proto.reload = function() {
92 this.updateColumns();
93 this.updateItems();
94 this.layout();
95 };
96
97 proto.updateColumns = function() {
98 this.columns = querySelect( this.options.columns, this.element );
99 };
100
101 proto.updateItems = function() {
102 this.items = querySelect( this.options.items, this.element );
103 };
104
105 proto.getActiveColumns = function() {
106 return this.columns.filter( function( column ) {
107 var style = getComputedStyle( column );
108 return style.display != 'none';
109 });
110 };
111
112 // ----- layout ----- //
113
114 // public, updates activeColumns
115 proto.layout = function() {
116 this.activeColumns = this.getActiveColumns();
117 this._layout();
118 };
119
120 // private, does not update activeColumns
121 proto._layout = function() {
122 // reset column heights
123 this.columnHeights = this.activeColumns.map( function() {
124 return 0;
125 });
126 // layout all items
127 this.layoutItems( this.items );
128 };
129
130 proto.layoutItems = function( items ) {
131 items.forEach( this.layoutItem, this );
132 };
133
134 proto.layoutItem = function( item ) {
135 // layout item by appending to column
136 var minHeight = Math.min.apply( Math, this.columnHeights );
137 var index = this.columnHeights.indexOf( minHeight );
138 this.activeColumns[ index ].appendChild( item );
139 // at least 1px, if item hasn't loaded
140 // Not exactly accurate, but it's cool
141 this.columnHeights[ index ] += item.offsetHeight || 1;
142 };
143
144 // ----- adding items ----- //
145
146 proto.append = function( elems ) {
147 var items = this.getQueryItems( elems );
148 // add items to collection
149 this.items = this.items.concat( items );
150 // lay them out
151 this.layoutItems( items );
152 };
153
154 proto.prepend = function( elems ) {
155 var items = this.getQueryItems( elems );
156 // add items to collection
157 this.items = items.concat( this.items );
158 // lay out everything
159 this._layout();
160 };
161
162 proto.getQueryItems = function( elems ) {
163 elems = makeArray( elems );
164 var fragment = document.createDocumentFragment();
165 elems.forEach( function( elem ) {
166 fragment.appendChild( elem );
167 });
168 return querySelect( this.options.items, fragment );
169 };
170
171 // ----- measure column height ----- //
172
173 proto.measureColumnHeight = function( elem ) {
174 var boundingRect = this.element.getBoundingClientRect();
175 this.activeColumns.forEach( function( column, i ) {
176 // if elem, measure only that column
177 // if no elem, measure all columns
178 if ( !elem || column.contains( elem ) ) {
179 var lastChildRect = column.lastElementChild.getBoundingClientRect();
180 // not an exact calculation as it includes top border, and excludes item bottom margin
181 this.columnHeights[ i ] = lastChildRect.bottom - boundingRect.top;
182 }
183 }, this );
184 };
185
186 // ----- events ----- //
187
188 proto.onWindowResize = function() {
189 clearTimeout( this.resizeTimeout );
190 this.resizeTimeout = setTimeout( function() {
191 this.onDebouncedResize();
192 }.bind( this ), 100 );
193 };
194
195 proto.onDebouncedResize = function() {
196 var activeColumns = this.getActiveColumns();
197 // check if columns changed
198 var isSameLength = activeColumns.length == this.activeColumns.length;
199 var isSameColumns = true;
200 this.activeColumns.forEach( function( column, i ) {
201 isSameColumns = isSameColumns && column == activeColumns[i];
202 });
203 if ( isSameLength && isSameColumns ) {
204 return;
205 }
206 // activeColumns changed
207 this.activeColumns = activeColumns;
208 this._layout();
209 };
210
211 proto.onLoad = function( event ) {
212 this.measureColumnHeight( event.target );
213 };
214
215 // ----- destroy ----- //
216
217 proto.destroy = function() {
218 // move items back to container
219 this.items.forEach( function( item ) {
220 this.element.appendChild( item );
221 }, this );
222 // remove events
223 window.removeEventListener( 'resize', this._windowResizeHandler );
224 this.element.removeEventListener( 'load', this._loadHandler, true );
225 // remove data
226 delete this.element.colcadeGUID;
227 delete instances[ this.guid ];
228 };
229
230 // -------------------------- HTML init -------------------------- //
231
232 docReady( function() {
233 var dataElems = querySelect('[data-colcade]');
234 dataElems.forEach( htmlInit );
235 });
236
237 function htmlInit( elem ) {
238 // convert attribute "foo: bar, qux: baz" into object
239 var attr = elem.getAttribute('data-colcade');
240 var attrParts = attr.split(',');
241 var options = {};
242 attrParts.forEach( function( part ) {
243 var pair = part.split(':');
244 var key = pair[0].trim();
245 var value = pair[1].trim();
246 options[ key ] = value;
247 });
248
249 new Colcade( elem, options );
250 }
251
252 Colcade.data = function( elem ) {
253 elem = getQueryElement( elem );
254 var id = elem && elem.colcadeGUID;
255 return id && instances[ id ];
256 };
257
258 // -------------------------- jQuery -------------------------- //
259
260 Colcade.makeJQueryPlugin = function( $ ) {
261 $ = $ || window.jQuery;
262 if ( !$ ) {
263 return;
264 }
265
266 $.fn.colcade = function( arg0 /*, arg1 */) {
267 // method call $().colcade( 'method', { options } )
268 if ( typeof arg0 == 'string' ) {
269 // shift arguments by 1
270 var args = Array.prototype.slice.call( arguments, 1 );
271 return methodCall( this, arg0, args );
272 }
273 // just $().colcade({ options })
274 plainCall( this, arg0 );
275 return this;
276 };
277
278 function methodCall( $elems, methodName, args ) {
279 var returnValue;
280 $elems.each( function( i, elem ) {
281 // get instance
282 var colcade = $.data( elem, 'colcade' );
283 if ( !colcade ) {
284 return;
285 }
286 // apply method, get return value
287 var value = colcade[ methodName ].apply( colcade, args );
288 // set return value if value is returned, use only first value
289 returnValue = returnValue === undefined ? value : returnValue;
290 });
291 return returnValue !== undefined ? returnValue : $elems;
292 }
293
294 function plainCall( $elems, options ) {
295 $elems.each( function( i, elem ) {
296 var colcade = $.data( elem, 'colcade' );
297 if ( colcade ) {
298 // set options & init
299 colcade.option( options );
300 colcade.layout();
301 } else {
302 // initialize new instance
303 colcade = new Colcade( elem, options );
304 $.data( elem, 'colcade', colcade );
305 }
306 });
307 }
308 };
309
310 // try making plugin
311 Colcade.makeJQueryPlugin();
312
313 // -------------------------- utils -------------------------- //
314
315 function extend( a, b ) {
316 for ( var prop in b ) {
317 a[ prop ] = b[ prop ];
318 }
319 return a;
320 }
321
322 // turn element or nodeList into an array
323 function makeArray( obj ) {
324 var ary = [];
325 if ( Array.isArray( obj ) ) {
326 // use object if already an array
327 ary = obj;
328 } else if ( obj && typeof obj.length == 'number' ) {
329 // convert nodeList to array
330 for ( var i=0; i < obj.length; i++ ) {
331 ary.push( obj[i] );
332 }
333 } else {
334 // array of single index
335 ary.push( obj );
336 }
337 return ary;
338 }
339
340 // get array of elements
341 function querySelect( selector, elem ) {
342 elem = elem || document;
343 var elems = elem.querySelectorAll( selector );
344 return makeArray( elems );
345 }
346
347 function getQueryElement( elem ) {
348 if ( typeof elem == 'string' ) {
349 elem = document.querySelector( elem );
350 }
351 return elem;
352 }
353
354 function docReady( onReady ) {
355 if ( document.readyState == 'complete' ) {
356 onReady();
357 return;
358 }
359 document.addEventListener( 'DOMContentLoaded', onReady );
360 }
361
362 // -------------------------- end -------------------------- //
363
364 return Colcade;
365
366 }));
367