vendor: 17,537 bytes, lines 1-621
1/*! 2 * Isotope v3.0.6 3 * 4 * Licensed GPLv3 for open source use 5 * or Isotope Commercial License for commercial use 6 * 7 * https://isotope.metafizzy.co 8 * Copyright 2010-2018 Metafizzy 9 */ 10 11( function( window, factory ) { 12 // universal module definition 13 /* jshint strict: false */ /*globals define, module, require */ 14 if ( typeof define == 'function' && define.amd ) { 15 // AMD 16 define( [ 17 'outlayer/outlayer', 18 'get-size/get-size', 19 'desandro-matches-selector/matches-selector', 20 'fizzy-ui-utils/utils', 21 './item', 22 './layout-mode', 23 // include default layout modes 24 './layout-modes/masonry', 25 './layout-modes/fit-rows', 26 './layout-modes/vertical' 27 ], 28 function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) { 29 return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ); 30 }); 31 } else if ( typeof module == 'object' && module.exports ) { 32 // CommonJS 33 module.exports = factory( 34 window, 35 require('outlayer'), 36 require('get-size'), 37 require('desandro-matches-selector'), 38 require('fizzy-ui-utils'), 39 require('./item'), 40 require('./layout-mode'), 41 // include default layout modes 42 require('./layout-modes/masonry'), 43 require('./layout-modes/fit-rows'), 44 require('./layout-modes/vertical') 45 ); 46 } else { 47 // browser global 48 window.Isotope = factory( 49 window, 50 window.Outlayer, 51 window.getSize, 52 window.matchesSelector, 53 window.fizzyUIUtils, 54 window.Isotope.Item, 55 window.Isotope.LayoutMode 56 ); 57 } 58 59}( window, function factory( window, Outlayer, getSize, matchesSelector, utils, 60 Item, LayoutMode ) { 61 62'use strict'; 63 64// -------------------------- vars -------------------------- // 65 66var jQuery = window.jQuery; 67 68// -------------------------- helpers -------------------------- // 69 70var trim = String.prototype.trim ? 71 function( str ) { 72 return str.trim(); 73 } : 74 function( str ) { 75 return str.replace( /^\s+|\s+$/g, '' ); 76 }; 77 78// -------------------------- isotopeDefinition -------------------------- // 79 80 // create an Outlayer layout class 81 var Isotope = Outlayer.create( 'isotope', { 82 layoutMode: 'masonry', 83 isJQueryFiltering: true, 84 sortAscending: true 85 }); 86 87 Isotope.Item = Item; 88 Isotope.LayoutMode = LayoutMode; 89 90 var proto = Isotope.prototype; 91 92 proto._create = function() { 93 this.itemGUID = 0; 94 // functions that sort items 95 this._sorters = {}; 96 this._getSorters(); 97 // call super 98 Outlayer.prototype._create.call( this ); 99 100 // create layout modes 101 this.modes = {}; 102 // start filteredItems with all items 103 this.filteredItems = this.items; 104 // keep of track of sortBys 105 this.sortHistory = [ 'original-order' ]; 106 // create from registered layout modes 107 for ( var name in LayoutMode.modes ) { 108 this._initLayoutMode( name ); 109 } 110 }; 111 112 proto.reloadItems = function() { 113 // reset item ID counter 114 this.itemGUID = 0; 115 // call super 116 Outlayer.prototype.reloadItems.call( this ); 117 }; 118 119 proto._itemize = function() { 120 var items = Outlayer.prototype._itemize.apply( this, arguments ); 121 // assign ID for original-order 122 for ( var i=0; i < items.length; i++ ) { 123 var item = items[i]; 124 item.id = this.itemGUID++; 125 } 126 this._updateItemsSortData( items ); 127 return items; 128 }; 129 130 131 // -------------------------- layout -------------------------- // 132 133 proto._initLayoutMode = function( name ) { 134 var Mode = LayoutMode.modes[ name ]; 135 // set mode options 136 // HACK extend initial options, back-fill in default options 137 var initialOpts = this.options[ name ] || {}; 138 this.options[ name ] = Mode.options ? 139 utils.extend( Mode.options, initialOpts ) : initialOpts; 140 // init layout mode instance 141 this.modes[ name ] = new Mode( this ); 142 }; 143 144 145 proto.layout = function() { 146 // if first time doing layout, do all magic 147 if ( !this._isLayoutInited && this._getOption('initLayout') ) { 148 this.arrange(); 149 return; 150 } 151 this._layout(); 152 }; 153 154 // private method to be used in layout() & magic() 155 proto._layout = function() { 156 // don't animate first layout 157 var isInstant = this._getIsInstant(); 158 // layout flow 159 this._resetLayout(); 160 this._manageStamps(); 161 this.layoutItems( this.filteredItems, isInstant ); 162 163 // flag for initalized 164 this._isLayoutInited = true; 165 }; 166 167 // filter + sort + layout 168 proto.arrange = function( opts ) { 169 // set any options pass 170 this.option( opts ); 171 this._getIsInstant(); 172 // filter, sort, and layout 173 174 // filter 175 var filtered = this._filter( this.items ); 176 this.filteredItems = filtered.matches; 177 178 this._bindArrangeComplete(); 179 180 if ( this._isInstant ) { 181 this._noTransition( this._hideReveal, [ filtered ] ); 182 } else { 183 this._hideReveal( filtered ); 184 } 185 186 this._sort(); 187 this._layout(); 188 }; 189 // alias to _init for main plugin method 190 proto._init = proto.arrange; 191 192 proto._hideReveal = function( filtered ) { 193 this.reveal( filtered.needReveal ); 194 this.hide( filtered.needHide ); 195 }; 196 197 // HACK 198 // Don't animate/transition first layout 199 // Or don't animate/transition other layouts 200 proto._getIsInstant = function() { 201 var isLayoutInstant = this._getOption('layoutInstant'); 202 var isInstant = isLayoutInstant !== undefined ? isLayoutInstant : 203 !this._isLayoutInited; 204 this._isInstant = isInstant; 205 return isInstant; 206 }; 207 208 // listen for layoutComplete, hideComplete and revealComplete 209 // to trigger arrangeComplete 210 proto._bindArrangeComplete = function() { 211 // listen for 3 events to trigger arrangeComplete 212 var isLayoutComplete, isHideComplete, isRevealComplete; 213 var _this = this; 214 function arrangeParallelCallback() { 215 if ( isLayoutComplete && isHideComplete && isRevealComplete ) { 216 _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] ); 217 } 218 } 219 this.once( 'layoutComplete', function() { 220 isLayoutComplete = true; 221 arrangeParallelCallback(); 222 }); 223 this.once( 'hideComplete', function() { 224 isHideComplete = true; 225 arrangeParallelCallback(); 226 }); 227 this.once( 'revealComplete', function() { 228 isRevealComplete = true; 229 arrangeParallelCallback(); 230 }); 231 }; 232 233 // -------------------------- filter -------------------------- // 234 235 proto._filter = function( items ) { 236 var filter = this.options.filter; 237 filter = filter || '*'; 238 var matches = []; 239 var hiddenMatched = []; 240 var visibleUnmatched = []; 241 242 var test = this._getFilterTest( filter ); 243 244 // test each item 245 for ( var i=0; i < items.length; i++ ) { 246 var item = items[i]; 247 if ( item.isIgnored ) { 248 continue; 249 } 250 // add item to either matched or unmatched group 251 var isMatched = test( item ); 252 // item.isFilterMatched = isMatched; 253 // add to matches if its a match 254 if ( isMatched ) { 255 matches.push( item ); 256 } 257 // add to additional group if item needs to be hidden or revealed 258 if ( isMatched && item.isHidden ) { 259 hiddenMatched.push( item ); 260 } else if ( !isMatched && !item.isHidden ) { 261 visibleUnmatched.push( item ); 262 } 263 } 264 265 // return collections of items to be manipulated 266 return { 267 matches: matches, 268 needReveal: hiddenMatched, 269 needHide: visibleUnmatched 270 }; 271 }; 272 273 // get a jQuery, function, or a matchesSelector test given the filter 274 proto._getFilterTest = function( filter ) { 275 if ( jQuery && this.options.isJQueryFiltering ) { 276 // use jQuery 277 return function( item ) { 278 return jQuery( item.element ).is( filter ); 279 }; 280 } 281 if ( typeof filter == 'function' ) { 282 // use filter as function 283 return function( item ) { 284 return filter( item.element ); 285 }; 286 } 287 // default, use filter as selector string 288 return function( item ) { 289 return matchesSelector( item.element, filter ); 290 }; 291 }; 292 293 // -------------------------- sorting -------------------------- // 294 295 /** 296 * @params {Array} elems 297 * @public 298 */ 299 proto.updateSortData = function( elems ) { 300 // get items 301 var items; 302 if ( elems ) { 303 elems = utils.makeArray( elems ); 304 items = this.getItems( elems ); 305 } else { 306 // update all items if no elems provided 307 items = this.items; 308 } 309 310 this._getSorters(); 311 this._updateItemsSortData( items ); 312 }; 313 314 proto._getSorters = function() { 315 var getSortData = this.options.getSortData; 316 for ( var key in getSortData ) { 317 var sorter = getSortData[ key ]; 318 this._sorters[ key ] = mungeSorter( sorter ); 319 } 320 }; 321 322 /** 323 * @params {Array} items - of Isotope.Items 324 * @private 325 */ 326 proto._updateItemsSortData = function( items ) { 327 // do not update if no items 328 var len = items && items.length; 329 330 for ( var i=0; len && i < len; i++ ) { 331 var item = items[i]; 332 item.updateSortData(); 333 } 334 }; 335 336 // ----- munge sorter ----- // 337 338 // encapsulate this, as we just need mungeSorter 339 // other functions in here are just for munging 340 var mungeSorter = ( function() { 341 // add a magic layer to sorters for convienent shorthands 342 // `.foo-bar` will use the text of .foo-bar querySelector 343 // `[foo-bar]` will use attribute 344 // you can also add parser 345 // `.foo-bar parseInt` will parse that as a number 346 function mungeSorter( sorter ) { 347 // if not a string, return function or whatever it is 348 if ( typeof sorter != 'string' ) { 349 return sorter; 350 } 351 // parse the sorter string 352 var args = trim( sorter ).split(' '); 353 var query = args[0]; 354 // check if query looks like [an-attribute] 355 var attrMatch = query.match( /^\[(.+)\]$/ ); 356 var attr = attrMatch && attrMatch[1]; 357 var getValue = getValueGetter( attr, query ); 358 // use second argument as a parser 359 var parser = Isotope.sortDataParsers[ args[1] ]; 360 // parse the value, if there was a parser 361 sorter = parser ? function( elem ) { 362 return elem && parser( getValue( elem ) ); 363 } : 364 // otherwise just return value 365 function( elem ) { 366 return elem && getValue( elem ); 367 }; 368 369 return sorter; 370 } 371 372 // get an attribute getter, or get text of the querySelector 373 function getValueGetter( attr, query ) { 374 // if query looks like [foo-bar], get attribute 375 if ( attr ) { 376 return function getAttribute( elem ) { 377 return elem.getAttribute( attr ); 378 }; 379 } 380 381 // otherwise, assume its a querySelector, and get its text 382 return function getChildText( elem ) { 383 var child = elem.querySelector( query ); 384 return child && child.textContent; 385 }; 386 } 387 388 return mungeSorter; 389 })(); 390 391 // parsers used in getSortData shortcut strings 392 Isotope.sortDataParsers = { 393 'parseInt': function( val ) { 394 return parseInt( val, 10 ); 395 }, 396 'parseFloat': function( val ) { 397 return parseFloat( val ); 398 } 399 }; 400 401 // ----- sort method ----- // 402 403 // sort filteredItem order 404 proto._sort = function() { 405 if ( !this.options.sortBy ) { 406 return; 407 } 408 // keep track of sortBy History 409 var sortBys = utils.makeArray( this.options.sortBy ); 410 if ( !this._getIsSameSortBy( sortBys ) ) { 411 // concat all sortBy and sortHistory, add to front, oldest goes in last 412 this.sortHistory = sortBys.concat( this.sortHistory ); 413 } 414 // sort magic 415 var itemSorter = getItemSorter( this.sortHistory, this.options.sortAscending ); 416 this.filteredItems.sort( itemSorter ); 417 }; 418 419 // check if sortBys is same as start of sortHistory 420 proto._getIsSameSortBy = function( sortBys ) { 421 for ( var i=0; i < sortBys.length; i++ ) { 422 if ( sortBys[i] != this.sortHistory[i] ) { 423 return false; 424 } 425 } 426 return true; 427 }; 428 429 // returns a function used for sorting 430 function getItemSorter( sortBys, sortAsc ) { 431 return function sorter( itemA, itemB ) { 432 // cycle through all sortKeys 433 for ( var i = 0; i < sortBys.length; i++ ) { 434 var sortBy = sortBys[i]; 435 var a = itemA.sortData[ sortBy ]; 436 var b = itemB.sortData[ sortBy ]; 437 if ( a > b || a < b ) { 438 // if sortAsc is an object, use the value given the sortBy key 439 var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc; 440 var direction = isAscending ? 1 : -1; 441 return ( a > b ? 1 : -1 ) * direction; 442 } 443 } 444 return 0; 445 }; 446 } 447 448 // -------------------------- methods -------------------------- // 449 450 // get layout mode 451 proto._mode = function() { 452 var layoutMode = this.options.layoutMode; 453 var mode = this.modes[ layoutMode ]; 454 if ( !mode ) { 455 // TODO console.error 456 throw new Error( 'No layout mode: ' + layoutMode ); 457 } 458 // HACK sync mode's options 459 // any options set after init for layout mode need to be synced 460 mode.options = this.options[ layoutMode ]; 461 return mode; 462 }; 463 464 proto._resetLayout = function() { 465 // trigger original reset layout 466 Outlayer.prototype._resetLayout.call( this ); 467 this._mode()._resetLayout(); 468 }; 469 470 proto._getItemLayoutPosition = function( item ) { 471 return this._mode()._getItemLayoutPosition( item ); 472 }; 473 474 proto._manageStamp = function( stamp ) { 475 this._mode()._manageStamp( stamp ); 476 }; 477 478 proto._getContainerSize = function() { 479 return this._mode()._getContainerSize(); 480 }; 481 482 proto.needsResizeLayout = function() { 483 return this._mode().needsResizeLayout(); 484 }; 485 486 // -------------------------- adding & removing -------------------------- // 487 488 // HEADS UP overwrites default Outlayer appended 489 proto.appended = function( elems ) { 490 var items = this.addItems( elems ); 491 if ( !items.length ) { 492 return; 493 } 494 // filter, layout, reveal new items 495 var filteredItems = this._filterRevealAdded( items ); 496 // add to filteredItems 497 this.filteredItems = this.filteredItems.concat( filteredItems ); 498 }; 499 500 // HEADS UP overwrites default Outlayer prepended 501 proto.prepended = function( elems ) { 502 var items = this._itemize( elems ); 503 if ( !items.length ) { 504 return; 505 } 506 // start new layout 507 this._resetLayout(); 508 this._manageStamps(); 509 // filter, layout, reveal new items 510 var filteredItems = this._filterRevealAdded( items ); 511 // layout previous items 512 this.layoutItems( this.filteredItems ); 513 // add to items and filteredItems 514 this.filteredItems = filteredItems.concat( this.filteredItems ); 515 this.items = items.concat( this.items ); 516 }; 517 518 proto._filterRevealAdded = function( items ) { 519 var filtered = this._filter( items ); 520 this.hide( filtered.needHide ); 521 // reveal all new items 522 this.reveal( filtered.matches ); 523 // layout new items, no transition 524 this.layoutItems( filtered.matches, true ); 525 return filtered.matches; 526 }; 527 528 /** 529 * Filter, sort, and layout newly-appended item elements 530 * @param {Array or NodeList or Element} elems 531 */ 532 proto.insert = function( elems ) { 533 var items = this.addItems( elems ); 534 if ( !items.length ) { 535 return; 536 } 537 // append item elements 538 var i, item; 539 var len = items.length; 540 for ( i=0; i < len; i++ ) { 541 item = items[i]; 542 this.element.appendChild( item.element ); 543 } 544 // filter new stuff 545 var filteredInsertItems = this._filter( items ).matches; 546 // set flag 547 for ( i=0; i < len; i++ ) { 548 items[i].isLayoutInstant = true; 549 } 550 this.arrange(); 551 // reset flag 552 for ( i=0; i < len; i++ ) { 553 delete items[i].isLayoutInstant; 554 } 555 this.reveal( filteredInsertItems ); 556 }; 557 558 var _remove = proto.remove; 559 proto.remove = function( elems ) { 560 elems = utils.makeArray( elems ); 561 var removeItems = this.getItems( elems ); 562 // do regular thing 563 _remove.call( this, elems ); 564 // bail if no items to remove 565 var len = removeItems && removeItems.length; 566 // remove elems from filteredItems 567 for ( var i=0; len && i < len; i++ ) { 568 var item = removeItems[i]; 569 // remove item from collection 570 utils.removeFrom( this.filteredItems, item ); 571 } 572 }; 573 574 proto.shuffle = function() { 575 // update random sortData 576 for ( var i=0; i < this.items.length; i++ ) { 577 var item = this.items[i]; 578 item.sortData.random = Math.random(); 579 } 580 this.options.sortBy = 'random'; 581 this._sort(); 582 this._layout(); 583 }; 584 585 /** 586 * trigger fn without transition 587 * kind of hacky to have this in the first place 588 * @param {Function} fn 589 * @param {Array} args 590 * @returns ret 591 * @private 592 */ 593 proto._noTransition = function( fn, args ) { 594 // save transitionDuration before disabling 595 var transitionDuration = this.options.transitionDuration; 596 // disable transition 597 this.options.transitionDuration = 0; 598 // do it 599 var returnValue = fn.apply( this, args ); 600 // re-enable transition for reveal 601 this.options.transitionDuration = transitionDuration; 602 return returnValue; 603 }; 604 605 // ----- helper methods ----- // 606 607 /** 608 * getter method for getting filtered item elements 609 * @returns {Array} elems - collection of item elements 610 */ 611 proto.getFilteredItemElements = function() { 612 return this.filteredItems.map( function( item ) { 613 return item.element; 614 }); 615 }; 616 617 // ----- ----- // 618 619 return Isotope; 620 621}));
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.