vendor: 10,374 bytes, lines 1-392
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 29function 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 // items from the last append operation and ColumnHeights before the append 44 this.lastAppend = []; 45 this.lastColumnHeights = []; 46 // kick things off 47 this.create(); 48 element.classList.add('cr-colcade-loaded'); 49} 50 51var proto = Colcade.prototype; 52 53proto.option = function( options ) { 54 this.options = extend( this.options, options ); 55}; 56 57// globally unique identifiers 58var GUID = 0; 59// internal store of all Colcade intances 60var instances = {}; 61 62proto.create = function() { 63 this.errorCheck(); 64 // add guid for Colcade.data 65 var guid = this.guid = ++GUID; 66 this.element.colcadeGUID = guid; 67 instances[ guid ] = this; // associate via id 68 // update initial properties & layout 69 this.reload(); 70 // events 71 this._windowResizeHandler = this.onWindowResize.bind( this ); 72 this._loadHandler = this.onLoad.bind( this ); 73 window.addEventListener( 'resize', this._windowResizeHandler ); 74 this.element.addEventListener( 'load', this._loadHandler, true ); 75}; 76 77proto.errorCheck = function() { 78 var errors = []; 79 if ( !this.element ) { 80 errors.push( 'Bad element: ' + this.element ); 81 } 82 if ( !this.options.columns ) { 83 errors.push( 'columns option required: ' + this.options.columns ); 84 } 85 if ( !this.options.items ) { 86 errors.push( 'items option required: ' + this.options.items ); 87 } 88 89 if ( errors.length ) { 90 throw new Error( '[Colcade error] ' + errors.join('. ') ); 91 } 92}; 93 94// update properties and do layout 95proto.reload = function() { 96 this.updateColumns(); 97 this.updateItems(); 98 this.layout(); 99}; 100 101proto.updateColumns = function() { 102 this.columns = querySelect( this.options.columns, this.element ); 103}; 104 105proto.updateItems = function() { 106 this.items = querySelect( this.options.items, this.element ); 107}; 108 109proto.getActiveColumns = function() { 110 return this.columns.filter( function( column ) { 111 var style = getComputedStyle( column ); 112 return style.display != 'none'; 113 }); 114}; 115 116// ----- layout ----- // 117 118// public, updates activeColumns 119proto.layout = function() { 120 this.activeColumns = this.getActiveColumns(); 121 this._layout(); 122}; 123 124// private, does not update activeColumns 125proto._layout = function() { 126 // reset column heights 127 this.columnHeights = this.activeColumns.map( function() { 128 return 0; 129 }); 130 // layout all items 131 this.layoutItems( this.items ); 132}; 133 134proto.layoutItems = function( items ) { 135 items.forEach( this.layoutItem, this ); 136}; 137 138proto.layoutItem = function( item ) { 139 // layout item by appending to column 140 var minHeight = Math.min.apply( Math, this.columnHeights ); 141 var index = this.columnHeights.indexOf( minHeight ); 142 this.activeColumns[ index ].appendChild( item ); 143 // at least 1px, if item hasn't loaded 144 // Not exactly accurate, but it's cool 145 this.columnHeights[ index ] += item.offsetHeight || 1; 146}; 147 148// ----- adding items ----- // 149 150proto.append = function( elems ) { 151 var items = this.getQueryItems( elems ); 152 // add items to collection 153 this.items = this.items.concat( items ); 154 // capture the items and columnHeights for future onLoad events triggered by images load 155 this.lastAppend.length = 0; 156 this.lastAppend = items.slice(); 157 this.lastColumnHeights.length = 0; 158 this.lastColumnHeights = this.columnHeights.slice(); 159 // lay them out 160 this.layoutItems( items ); 161}; 162 163proto.prepend = function( elems ) { 164 var items = this.getQueryItems( elems ); 165 // add items to collection 166 this.items = items.concat( this.items ); 167 // lay out everything 168 this._layout(); 169}; 170 171proto.getQueryItems = function( elems ) { 172 elems = makeArray( elems ); 173 var fragment = document.createDocumentFragment(); 174 elems.forEach( function( elem ) { 175 fragment.appendChild( elem ); 176 }); 177 return querySelect( this.options.items, fragment ); 178}; 179 180proto.empty = function() { 181 this.lastAppend.length = 0; 182 this.lastColumnHeights.length = 0; 183 // 184 this.items.forEach( function( item ) { 185 item.remove(); 186 }, this ); 187 this.items.length = 0; 188 // lay out everything 189 this._layout(); 190}; 191 192// ----- measure column height ----- // 193 194proto.measureColumnHeight = function( elem ) { 195 var boundingRect = this.element.getBoundingClientRect(); 196 this.activeColumns.forEach( function( column, i ) { 197 // if elem, measure only that column 198 // if no elem, measure all columns 199 if ( !elem || column.contains( elem ) ) { 200 var lastChildRect = column.lastElementChild.getBoundingClientRect(); 201 // not an exact calculation as it includes top border, and excludes item bottom margin 202 this.columnHeights[ i ] = lastChildRect.bottom - boundingRect.top; 203 } 204 }, this ); 205}; 206 207// ----- events ----- // 208 209proto.onWindowResize = function() { 210 clearTimeout( this.resizeTimeout ); 211 this.resizeTimeout = setTimeout( function() { 212 this.onDebouncedResize(); 213 }.bind( this ), 100 ); 214}; 215 216proto.onDebouncedResize = function() { 217 var activeColumns = this.getActiveColumns(); 218 // check if columns changed 219 var isSameLength = activeColumns.length == this.activeColumns.length; 220 var isSameColumns = true; 221 this.activeColumns.forEach( function( column, i ) { 222 isSameColumns = isSameColumns && column == activeColumns[i]; 223 }); 224 if ( isSameLength && isSameColumns ) { 225 return; 226 } 227 // activeColumns changed 228 this.activeColumns = activeColumns; 229 this._layout(); 230}; 231 232proto.onLoad = function( event ) { 233 this.measureColumnHeight( event.target ); 234 if( 0 < this.lastColumnHeights.length && 0 < this.lastAppend.length ) { 235 this.columnHeights.length = 0; 236 this.columnHeights = this.lastColumnHeights.slice(); 237 this.layoutItems( this.lastAppend ); 238 } 239}; 240 241// ----- destroy ----- // 242 243proto.destroy = function() { 244 // move items back to container 245 this.items.forEach( function( item ) { 246 this.element.appendChild( item ); 247 }, this ); 248 // remove events 249 window.removeEventListener( 'resize', this._windowResizeHandler ); 250 this.element.removeEventListener( 'load', this._loadHandler, true ); 251 // remove data 252 delete this.element.colcadeGUID; 253 delete instances[ this.guid ]; 254}; 255 256// -------------------------- HTML init -------------------------- // 257 258docReady( function() { 259 var dataElems = querySelect('[data-colcade]'); 260 dataElems.forEach( htmlInit ); 261}); 262 263function htmlInit( elem ) { 264 // convert attribute "foo: bar, qux: baz" into object 265 var attr = elem.getAttribute('data-colcade'); 266 var attrParts = attr.split(','); 267 var options = {}; 268 attrParts.forEach( function( part ) { 269 var pair = part.split(':'); 270 var key = pair[0].trim(); 271 var value = pair[1].trim(); 272 options[ key ] = value; 273 }); 274 275 new Colcade( elem, options ); 276} 277 278Colcade.data = function( elem ) { 279 elem = getQueryElement( elem ); 280 var id = elem && elem.colcadeGUID; 281 return id && instances[ id ]; 282}; 283 284// -------------------------- jQuery -------------------------- // 285 286Colcade.makeJQueryPlugin = function( $ ) { 287 $ = $ || window.jQuery; 288 if ( !$ ) { 289 return; 290 } 291 292 $.fn.colcade = function( arg0 /*, arg1 */) { 293 // method call $().colcade( 'method', { options } ) 294 if ( typeof arg0 == 'string' ) { 295 // shift arguments by 1 296 var args = Array.prototype.slice.call( arguments, 1 ); 297 return methodCall( this, arg0, args ); 298 } 299 // just $().colcade({ options }) 300 plainCall( this, arg0 ); 301 return this; 302 }; 303 304 function methodCall( $elems, methodName, args ) { 305 var returnValue; 306 $elems.each( function( i, elem ) { 307 // get instance 308 var colcade = $.data( elem, 'colcade' ); 309 if ( !colcade ) { 310 return; 311 } 312 // apply method, get return value 313 var value = colcade[ methodName ].apply( colcade, args ); 314 // set return value if value is returned, use only first value 315 returnValue = returnValue === undefined ? value : returnValue; 316 }); 317 return returnValue !== undefined ? returnValue : $elems; 318 } 319 320 function plainCall( $elems, options ) { 321 $elems.each( function( i, elem ) { 322 var colcade = $.data( elem, 'colcade' ); 323 if ( colcade ) { 324 // set options & init 325 colcade.option( options ); 326 colcade.layout(); 327 } else { 328 // initialize new instance 329 colcade = new Colcade( elem, options ); 330 $.data( elem, 'colcade', colcade ); 331 } 332 }); 333 } 334}; 335 336// try making plugin 337Colcade.makeJQueryPlugin(); 338 339// -------------------------- utils -------------------------- // 340 341function extend( a, b ) { 342 for ( var prop in b ) { 343 a[ prop ] = b[ prop ]; 344 } 345 return a; 346} 347 348// turn element or nodeList into an array 349function makeArray( obj ) { 350 var ary = []; 351 if ( Array.isArray( obj ) ) { 352 // use object if already an array 353 ary = obj; 354 } else if ( obj && typeof obj.length == 'number' ) { 355 // convert nodeList to array 356 for ( var i=0; i < obj.length; i++ ) { 357 ary.push( obj[i] ); 358 } 359 } else { 360 // array of single index 361 ary.push( obj ); 362 } 363 return ary; 364} 365 366// get array of elements 367function querySelect( selector, elem ) { 368 elem = elem || document; 369 var elems = elem.querySelectorAll( selector ); 370 return makeArray( elems ); 371} 372 373function getQueryElement( elem ) { 374 if ( typeof elem == 'string' ) { 375 elem = document.querySelector( elem ); 376 } 377 return elem; 378} 379 380function docReady( onReady ) { 381 if ( document.readyState == 'complete' ) { 382 onReady(); 383 return; 384 } 385 document.addEventListener( 'DOMContentLoaded', onReady ); 386} 387 388// -------------------------- end -------------------------- // 389 390return Colcade; 391 392}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.