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