vendor: 8,775 bytes, lines 1-363
1/*! 2 * Isotope PACKAGED 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/** 12 * Bridget makes jQuery widgets 13 * v2.0.1 14 * MIT license 15 */ 16 17/* jshint browser: true, strict: true, undef: true, unused: true */ 18 19( function( window, factory ) { 20 // universal module definition 21 /*jshint strict: false */ /* globals define, module, require */ 22 if ( typeof define == 'function' && define.amd ) { 23 // AMD 24 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) { 25 return factory( window, jQuery ); 26 }); 27 } else if ( typeof module == 'object' && module.exports ) { 28 // CommonJS 29 module.exports = factory( 30 window, 31 require('jquery') 32 ); 33 } else { 34 // browser global 35 window.jQueryBridget = factory( 36 window, 37 window.jQuery 38 ); 39 } 40 41}( window, function factory( window, jQuery ) { 42'use strict'; 43 44// ----- utils ----- // 45 46var arraySlice = Array.prototype.slice; 47 48// helper function for logging errors 49// $.error breaks jQuery chaining 50var console = window.console; 51var logError = typeof console == 'undefined' ? function() {} : 52 function( message ) { 53 console.error( message ); 54 }; 55 56// ----- jQueryBridget ----- // 57 58function jQueryBridget( namespace, PluginClass, $ ) { 59 $ = $ || jQuery || window.jQuery; 60 if ( !$ ) { 61 return; 62 } 63 64 // add option method -> $().plugin('option', {...}) 65 if ( !PluginClass.prototype.option ) { 66 // option setter 67 PluginClass.prototype.option = function( opts ) { 68 // bail out if not an object 69 if ( !$.isPlainObject( opts ) ){ 70 return; 71 } 72 this.options = $.extend( true, this.options, opts ); 73 }; 74 } 75 76 // make jQuery plugin 77 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) { 78 if ( typeof arg0 == 'string' ) { 79 // method call $().plugin( 'methodName', { options } ) 80 // shift arguments by 1 81 var args = arraySlice.call( arguments, 1 ); 82 return methodCall( this, arg0, args ); 83 } 84 // just $().plugin({ options }) 85 plainCall( this, arg0 ); 86 return this; 87 }; 88 89 // $().plugin('methodName') 90 function methodCall( $elems, methodName, args ) { 91 var returnValue; 92 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")'; 93 94 $elems.each( function( i, elem ) { 95 // get instance 96 var instance = $.data( elem, namespace ); 97 if ( !instance ) { 98 logError( namespace + ' not initialized. Cannot call methods, i.e. ' + 99 pluginMethodStr ); 100 return; 101 } 102 103 var method = instance[ methodName ]; 104 if ( !method || methodName.charAt(0) == '_' ) { 105 logError( pluginMethodStr + ' is not a valid method' ); 106 return; 107 } 108 109 // apply method, get return value 110 var value = method.apply( instance, args ); 111 // set return value if value is returned, use only first value 112 returnValue = returnValue === undefined ? value : returnValue; 113 }); 114 115 return returnValue !== undefined ? returnValue : $elems; 116 } 117 118 function plainCall( $elems, options ) { 119 $elems.each( function( i, elem ) { 120 var instance = $.data( elem, namespace ); 121 if ( instance ) { 122 // set options & init 123 instance.option( options ); 124 instance._init(); 125 } else { 126 // initialize new instance 127 instance = new PluginClass( elem, options ); 128 $.data( elem, namespace, instance ); 129 } 130 }); 131 } 132 133 updateJQuery( $ ); 134 135} 136 137// ----- updateJQuery ----- // 138 139// set $.bridget for v1 backwards compatibility 140function updateJQuery( $ ) { 141 if ( !$ || ( $ && $.bridget ) ) { 142 return; 143 } 144 $.bridget = jQueryBridget; 145} 146 147updateJQuery( jQuery || window.jQuery ); 148 149// ----- ----- // 150 151return jQueryBridget; 152 153})); 154 155/** 156 * EvEmitter v1.1.0 157 * Lil' event emitter 158 * MIT License 159 */ 160 161/* jshint unused: true, undef: true, strict: true */ 162 163( function( global, factory ) { 164 // universal module definition 165 /* jshint strict: false */ /* globals define, module, window */ 166 if ( typeof define == 'function' && define.amd ) { 167 // AMD - RequireJS 168 define( 'ev-emitter/ev-emitter',factory ); 169 } else if ( typeof module == 'object' && module.exports ) { 170 // CommonJS - Browserify, Webpack 171 module.exports = factory(); 172 } else { 173 // Browser globals 174 global.EvEmitter = factory(); 175 } 176 177}( typeof window != 'undefined' ? window : this, function() { 178 179 180 181function EvEmitter() {} 182 183var proto = EvEmitter.prototype; 184 185proto.on = function( eventName, listener ) { 186 if ( !eventName || !listener ) { 187 return; 188 } 189 // set events hash 190 var events = this._events = this._events || {}; 191 // set listeners array 192 var listeners = events[ eventName ] = events[ eventName ] || []; 193 // only add once 194 if ( listeners.indexOf( listener ) == -1 ) { 195 listeners.push( listener ); 196 } 197 198 return this; 199}; 200 201proto.once = function( eventName, listener ) { 202 if ( !eventName || !listener ) { 203 return; 204 } 205 // add event 206 this.on( eventName, listener ); 207 // set once flag 208 // set onceEvents hash 209 var onceEvents = this._onceEvents = this._onceEvents || {}; 210 // set onceListeners object 211 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 212 // set flag 213 onceListeners[ listener ] = true; 214 215 return this; 216}; 217 218proto.off = function( eventName, listener ) { 219 var listeners = this._events && this._events[ eventName ]; 220 if ( !listeners || !listeners.length ) { 221 return; 222 } 223 var index = listeners.indexOf( listener ); 224 if ( index != -1 ) { 225 listeners.splice( index, 1 ); 226 } 227 228 return this; 229}; 230 231proto.emitEvent = function( eventName, args ) { 232 var listeners = this._events && this._events[ eventName ]; 233 if ( !listeners || !listeners.length ) { 234 return; 235 } 236 // copy over to avoid interference if .off() in listener 237 listeners = listeners.slice(0); 238 args = args || []; 239 // once stuff 240 var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 241 242 for ( var i=0; i < listeners.length; i++ ) { 243 var listener = listeners[i] 244 var isOnce = onceListeners && onceListeners[ listener ]; 245 if ( isOnce ) { 246 // remove listener 247 // remove before trigger to prevent recursion 248 this.off( eventName, listener ); 249 // unset once flag 250 delete onceListeners[ listener ]; 251 } 252 // trigger listener 253 listener.apply( this, args ); 254 } 255 256 return this; 257}; 258 259proto.allOff = function() { 260 delete this._events; 261 delete this._onceEvents; 262}; 263 264return EvEmitter; 265 266})); 267 268/*! 269 * getSize v2.0.3 270 * measure size of elements 271 * MIT license 272 */ 273 274/* jshint browser: true, strict: true, undef: true, unused: true */ 275/* globals console: false */ 276 277( function( window, factory ) { 278 /* jshint strict: false */ /* globals define, module */ 279 if ( typeof define == 'function' && define.amd ) { 280 // AMD 281 define( 'get-size/get-size',factory ); 282 } else if ( typeof module == 'object' && module.exports ) { 283 // CommonJS 284 module.exports = factory(); 285 } else { 286 // browser global 287 window.getSize = factory(); 288 } 289 290})( window, function factory() { 291'use strict'; 292 293// -------------------------- helpers -------------------------- // 294 295// get a number from a string, not a percentage 296function getStyleSize( value ) { 297 var num = parseFloat( value ); 298 // not a percent like '100%', and a number 299 var isValid = value.indexOf('%') == -1 && !isNaN( num ); 300 return isValid && num; 301} 302 303function noop() {} 304 305var logError = typeof console == 'undefined' ? noop : 306 function( message ) { 307 console.error( message ); 308 }; 309 310// -------------------------- measurements -------------------------- // 311 312var measurements = [ 313 'paddingLeft', 314 'paddingRight', 315 'paddingTop', 316 'paddingBottom', 317 'marginLeft', 318 'marginRight', 319 'marginTop', 320 'marginBottom', 321 'borderLeftWidth', 322 'borderRightWidth', 323 'borderTopWidth', 324 'borderBottomWidth' 325]; 326 327var measurementsLength = measurements.length; 328 329function getZeroSize() { 330 var size = { 331 width: 0, 332 height: 0, 333 innerWidth: 0, 334 innerHeight: 0, 335 outerWidth: 0, 336 outerHeight: 0 337 }; 338 for ( var i=0; i < measurementsLength; i++ ) { 339 var measurement = measurements[i]; 340 size[ measurement ] = 0; 341 } 342 return size; 343} 344 345// -------------------------- getStyle -------------------------- // 346 347/** 348 * getStyle, get style of element, check for Firefox bug 349 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 350 */ 351function getStyle( elem ) { 352 var style = getComputedStyle( elem ); 353 if ( !style ) { 354 logError( 'Style returned ' + style + 355 '. Are you running this code in a hidden iframe on Firefox? ' + 356 'See https://bit.ly/getsizebug1' ); 357 } 358 return style; 359} 360 361// -------------------------- setup -------------------------- // 362 363var isSetup = false;
vendor: 44,338 bytes, lines 364-2139
364 365var isBoxSizeOuter; 366 367/** 368 * setup 369 * check isBoxSizerOuter 370 * do on first getSize() rather than on page load for Firefox bug 371 */ 372function setup() { 373 // setup once 374 if ( isSetup ) { 375 return; 376 } 377 isSetup = true; 378 379 // -------------------------- box sizing -------------------------- // 380 381 /** 382 * Chrome & Safari measure the outer-width on style.width on border-box elems 383 * IE11 & Firefox<29 measures the inner-width 384 */ 385 var div = document.createElement('div'); 386 div.style.width = '200px'; 387 div.style.padding = '1px 2px 3px 4px'; 388 div.style.borderStyle = 'solid'; 389 div.style.borderWidth = '1px 2px 3px 4px'; 390 div.style.boxSizing = 'border-box'; 391 392 var body = document.body || document.documentElement; 393 body.appendChild( div ); 394 var style = getStyle( div ); 395 // round value for browser zoom. desandro/masonry#928 396 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200; 397 getSize.isBoxSizeOuter = isBoxSizeOuter; 398 399 body.removeChild( div ); 400} 401 402// -------------------------- getSize -------------------------- // 403 404function getSize( elem ) { 405 setup(); 406 407 // use querySeletor if elem is string 408 if ( typeof elem == 'string' ) { 409 elem = document.querySelector( elem ); 410 } 411 412 // do not proceed on non-objects 413 if ( !elem || typeof elem != 'object' || !elem.nodeType ) { 414 return; 415 } 416 417 var style = getStyle( elem ); 418 419 // if hidden, everything is 0 420 if ( style.display == 'none' ) { 421 return getZeroSize(); 422 } 423 424 var size = {}; 425 size.width = elem.offsetWidth; 426 size.height = elem.offsetHeight; 427 428 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; 429 430 // get all measurements 431 for ( var i=0; i < measurementsLength; i++ ) { 432 var measurement = measurements[i]; 433 var value = style[ measurement ]; 434 var num = parseFloat( value ); 435 // any 'auto', 'medium' value will be 0 436 size[ measurement ] = !isNaN( num ) ? num : 0; 437 } 438 439 var paddingWidth = size.paddingLeft + size.paddingRight; 440 var paddingHeight = size.paddingTop + size.paddingBottom; 441 var marginWidth = size.marginLeft + size.marginRight; 442 var marginHeight = size.marginTop + size.marginBottom; 443 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 444 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 445 446 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 447 448 // overwrite width and height if we can get it from style 449 var styleWidth = getStyleSize( style.width ); 450 if ( styleWidth !== false ) { 451 size.width = styleWidth + 452 // add padding and border unless it's already including it 453 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 454 } 455 456 var styleHeight = getStyleSize( style.height ); 457 if ( styleHeight !== false ) { 458 size.height = styleHeight + 459 // add padding and border unless it's already including it 460 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 461 } 462 463 size.innerWidth = size.width - ( paddingWidth + borderWidth ); 464 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 465 466 size.outerWidth = size.width + marginWidth; 467 size.outerHeight = size.height + marginHeight; 468 469 return size; 470} 471 472return getSize; 473 474}); 475 476/** 477 * matchesSelector v2.0.2 478 * matchesSelector( element, '.selector' ) 479 * MIT license 480 */ 481 482/*jshint browser: true, strict: true, undef: true, unused: true */ 483 484( function( window, factory ) { 485 /*global define: false, module: false */ 486 'use strict'; 487 // universal module definition 488 if ( typeof define == 'function' && define.amd ) { 489 // AMD 490 define( 'desandro-matches-selector/matches-selector',factory ); 491 } else if ( typeof module == 'object' && module.exports ) { 492 // CommonJS 493 module.exports = factory(); 494 } else { 495 // browser global 496 window.matchesSelector = factory(); 497 } 498 499}( window, function factory() { 500 'use strict'; 501 502 var matchesMethod = ( function() { 503 var ElemProto = window.Element.prototype; 504 // check for the standard method name first 505 if ( ElemProto.matches ) { 506 return 'matches'; 507 } 508 // check un-prefixed 509 if ( ElemProto.matchesSelector ) { 510 return 'matchesSelector'; 511 } 512 // check vendor prefixes 513 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 514 515 for ( var i=0; i < prefixes.length; i++ ) { 516 var prefix = prefixes[i]; 517 var method = prefix + 'MatchesSelector'; 518 if ( ElemProto[ method ] ) { 519 return method; 520 } 521 } 522 })(); 523 524 return function matchesSelector( elem, selector ) { 525 return elem[ matchesMethod ]( selector ); 526 }; 527 528})); 529 530/** 531 * Fizzy UI utils v2.0.7 532 * MIT license 533 */ 534 535/*jshint browser: true, undef: true, unused: true, strict: true */ 536 537( function( window, factory ) { 538 // universal module definition 539 /*jshint strict: false */ /*globals define, module, require */ 540 541 if ( typeof define == 'function' && define.amd ) { 542 // AMD 543 define( 'fizzy-ui-utils/utils',[ 544 'desandro-matches-selector/matches-selector' 545 ], function( matchesSelector ) { 546 return factory( window, matchesSelector ); 547 }); 548 } else if ( typeof module == 'object' && module.exports ) { 549 // CommonJS 550 module.exports = factory( 551 window, 552 require('desandro-matches-selector') 553 ); 554 } else { 555 // browser global 556 window.fizzyUIUtils = factory( 557 window, 558 window.matchesSelector 559 ); 560 } 561 562}( window, function factory( window, matchesSelector ) { 563 564 565 566var utils = {}; 567 568// ----- extend ----- // 569 570// extends objects 571utils.extend = function( a, b ) { 572 for ( var prop in b ) { 573 a[ prop ] = b[ prop ]; 574 } 575 return a; 576}; 577 578// ----- modulo ----- // 579 580utils.modulo = function( num, div ) { 581 return ( ( num % div ) + div ) % div; 582}; 583 584// ----- makeArray ----- // 585 586var arraySlice = Array.prototype.slice; 587 588// turn element or nodeList into an array 589utils.makeArray = function( obj ) { 590 if ( Array.isArray( obj ) ) { 591 // use object if already an array 592 return obj; 593 } 594 // return empty array if undefined or null. #6 595 if ( obj === null || obj === undefined ) { 596 return []; 597 } 598 599 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; 600 if ( isArrayLike ) { 601 // convert nodeList to array 602 return arraySlice.call( obj ); 603 } 604 605 // array of single index 606 return [ obj ]; 607}; 608 609// ----- removeFrom ----- // 610 611utils.removeFrom = function( ary, obj ) { 612 var index = ary.indexOf( obj ); 613 if ( index != -1 ) { 614 ary.splice( index, 1 ); 615 } 616}; 617 618// ----- getParent ----- // 619 620utils.getParent = function( elem, selector ) { 621 while ( elem.parentNode && elem != document.body ) { 622 elem = elem.parentNode; 623 if ( matchesSelector( elem, selector ) ) { 624 return elem; 625 } 626 } 627}; 628 629// ----- getQueryElement ----- // 630 631// use element as selector string 632utils.getQueryElement = function( elem ) { 633 if ( typeof elem == 'string' ) { 634 return document.querySelector( elem ); 635 } 636 return elem; 637}; 638 639// ----- handleEvent ----- // 640 641// enable .ontype to trigger from .addEventListener( elem, 'type' ) 642utils.handleEvent = function( event ) { 643 var method = 'on' + event.type; 644 if ( this[ method ] ) { 645 this[ method ]( event ); 646 } 647}; 648 649// ----- filterFindElements ----- // 650 651utils.filterFindElements = function( elems, selector ) { 652 // make array of elems 653 elems = utils.makeArray( elems ); 654 var ffElems = []; 655 656 elems.forEach( function( elem ) { 657 // check that elem is an actual element 658 if ( !( elem instanceof HTMLElement ) ) { 659 return; 660 } 661 // add elem if no selector 662 if ( !selector ) { 663 ffElems.push( elem ); 664 return; 665 } 666 // filter & find items if we have a selector 667 // filter 668 if ( matchesSelector( elem, selector ) ) { 669 ffElems.push( elem ); 670 } 671 // find children 672 var childElems = elem.querySelectorAll( selector ); 673 // concat childElems to filterFound array 674 for ( var i=0; i < childElems.length; i++ ) { 675 ffElems.push( childElems[i] ); 676 } 677 }); 678 679 return ffElems; 680}; 681 682// ----- debounceMethod ----- // 683 684utils.debounceMethod = function( _class, methodName, threshold ) { 685 threshold = threshold || 100; 686 // original method 687 var method = _class.prototype[ methodName ]; 688 var timeoutName = methodName + 'Timeout'; 689 690 _class.prototype[ methodName ] = function() { 691 var timeout = this[ timeoutName ]; 692 clearTimeout( timeout ); 693 694 var args = arguments; 695 var _this = this; 696 this[ timeoutName ] = setTimeout( function() { 697 method.apply( _this, args ); 698 delete _this[ timeoutName ]; 699 }, threshold ); 700 }; 701}; 702 703// ----- docReady ----- // 704 705utils.docReady = function( callback ) { 706 var readyState = document.readyState; 707 if ( readyState == 'complete' || readyState == 'interactive' ) { 708 // do async to allow for other scripts to run. metafizzy/flickity#441 709 setTimeout( callback ); 710 } else { 711 document.addEventListener( 'DOMContentLoaded', callback ); 712 } 713}; 714 715// ----- htmlInit ----- // 716 717// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 718utils.toDashed = function( str ) { 719 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 720 return $1 + '-' + $2; 721 }).toLowerCase(); 722}; 723 724var console = window.console; 725/** 726 * allow user to initialize classes via [data-namespace] or .js-namespace class 727 * htmlInit( Widget, 'widgetName' ) 728 * options are parsed from data-namespace-options 729 */ 730utils.htmlInit = function( WidgetClass, namespace ) { 731 utils.docReady( function() { 732 var dashedNamespace = utils.toDashed( namespace ); 733 var dataAttr = 'data-' + dashedNamespace; 734 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' ); 735 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace ); 736 var elems = utils.makeArray( dataAttrElems ) 737 .concat( utils.makeArray( jsDashElems ) ); 738 var dataOptionsAttr = dataAttr + '-options'; 739 var jQuery = window.jQuery; 740 741 elems.forEach( function( elem ) { 742 var attr = elem.getAttribute( dataAttr ) || 743 elem.getAttribute( dataOptionsAttr ); 744 var options; 745 try { 746 options = attr && JSON.parse( attr ); 747 } catch ( error ) { 748 // log error, do not initialize 749 if ( console ) { 750 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className + 751 ': ' + error ); 752 } 753 return; 754 } 755 // initialize 756 var instance = new WidgetClass( elem, options ); 757 // make available via $().data('namespace') 758 if ( jQuery ) { 759 jQuery.data( elem, namespace, instance ); 760 } 761 }); 762 763 }); 764}; 765 766// ----- ----- // 767 768return utils; 769 770})); 771 772/** 773 * Outlayer Item 774 */ 775 776( function( window, factory ) { 777 // universal module definition 778 /* jshint strict: false */ /* globals define, module, require */ 779 if ( typeof define == 'function' && define.amd ) { 780 // AMD - RequireJS 781 define( 'outlayer/item',[ 782 'ev-emitter/ev-emitter', 783 'get-size/get-size' 784 ], 785 factory 786 ); 787 } else if ( typeof module == 'object' && module.exports ) { 788 // CommonJS - Browserify, Webpack 789 module.exports = factory( 790 require('ev-emitter'), 791 require('get-size') 792 ); 793 } else { 794 // browser global 795 window.Outlayer = {}; 796 window.Outlayer.Item = factory( 797 window.EvEmitter, 798 window.getSize 799 ); 800 } 801 802}( window, function factory( EvEmitter, getSize ) { 803'use strict'; 804 805// ----- helpers ----- // 806 807function isEmptyObj( obj ) { 808 for ( var prop in obj ) { 809 return false; 810 } 811 prop = null; 812 return true; 813} 814 815// -------------------------- CSS3 support -------------------------- // 816 817 818var docElemStyle = document.documentElement.style; 819 820var transitionProperty = typeof docElemStyle.transition == 'string' ? 821 'transition' : 'WebkitTransition'; 822var transformProperty = typeof docElemStyle.transform == 'string' ? 823 'transform' : 'WebkitTransform'; 824 825var transitionEndEvent = { 826 WebkitTransition: 'webkitTransitionEnd', 827 transition: 'transitionend' 828}[ transitionProperty ]; 829 830// cache all vendor properties that could have vendor prefix 831var vendorProperties = { 832 transform: transformProperty, 833 transition: transitionProperty, 834 transitionDuration: transitionProperty + 'Duration', 835 transitionProperty: transitionProperty + 'Property', 836 transitionDelay: transitionProperty + 'Delay' 837}; 838 839// -------------------------- Item -------------------------- // 840 841function Item( element, layout ) { 842 if ( !element ) { 843 return; 844 } 845 846 this.element = element; 847 // parent layout class, i.e. Masonry, Isotope, or Packery 848 this.layout = layout; 849 this.position = { 850 x: 0, 851 y: 0 852 }; 853 854 this._create(); 855} 856 857// inherit EvEmitter 858var proto = Item.prototype = Object.create( EvEmitter.prototype ); 859proto.constructor = Item; 860 861proto._create = function() { 862 // transition objects 863 this._transn = { 864 ingProperties: {}, 865 clean: {}, 866 onEnd: {} 867 }; 868 869 this.css({ 870 position: 'absolute' 871 }); 872}; 873 874// trigger specified handler for event type 875proto.handleEvent = function( event ) { 876 var method = 'on' + event.type; 877 if ( this[ method ] ) { 878 this[ method ]( event ); 879 } 880}; 881 882proto.getSize = function() { 883 this.size = getSize( this.element ); 884}; 885 886/** 887 * apply CSS styles to element 888 * @param {Object} style 889 */ 890proto.css = function( style ) { 891 var elemStyle = this.element.style; 892 893 for ( var prop in style ) { 894 // use vendor property if available 895 var supportedProp = vendorProperties[ prop ] || prop; 896 elemStyle[ supportedProp ] = style[ prop ]; 897 } 898}; 899 900 // measure position, and sets it 901proto.getPosition = function() { 902 var style = getComputedStyle( this.element ); 903 var isOriginLeft = this.layout._getOption('originLeft'); 904 var isOriginTop = this.layout._getOption('originTop'); 905 var xValue = style[ isOriginLeft ? 'left' : 'right' ]; 906 var yValue = style[ isOriginTop ? 'top' : 'bottom' ]; 907 var x = parseFloat( xValue ); 908 var y = parseFloat( yValue ); 909 // convert percent to pixels 910 var layoutSize = this.layout.size; 911 if ( xValue.indexOf('%') != -1 ) { 912 x = ( x / 100 ) * layoutSize.width; 913 } 914 if ( yValue.indexOf('%') != -1 ) { 915 y = ( y / 100 ) * layoutSize.height; 916 } 917 // clean up 'auto' or other non-integer values 918 x = isNaN( x ) ? 0 : x; 919 y = isNaN( y ) ? 0 : y; 920 // remove padding from measurement 921 x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight; 922 y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom; 923 924 this.position.x = x; 925 this.position.y = y; 926}; 927 928// set settled position, apply padding 929proto.layoutPosition = function() { 930 var layoutSize = this.layout.size; 931 var style = {}; 932 var isOriginLeft = this.layout._getOption('originLeft'); 933 var isOriginTop = this.layout._getOption('originTop'); 934 935 // x 936 var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight'; 937 var xProperty = isOriginLeft ? 'left' : 'right'; 938 var xResetProperty = isOriginLeft ? 'right' : 'left'; 939 940 var x = this.position.x + layoutSize[ xPadding ]; 941 // set in percentage or pixels 942 style[ xProperty ] = this.getXValue( x ); 943 // reset other property 944 style[ xResetProperty ] = ''; 945 946 // y 947 var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom'; 948 var yProperty = isOriginTop ? 'top' : 'bottom'; 949 var yResetProperty = isOriginTop ? 'bottom' : 'top'; 950 951 var y = this.position.y + layoutSize[ yPadding ]; 952 // set in percentage or pixels 953 style[ yProperty ] = this.getYValue( y ); 954 // reset other property 955 style[ yResetProperty ] = ''; 956 957 this.css( style ); 958 this.emitEvent( 'layout', [ this ] ); 959}; 960 961proto.getXValue = function( x ) { 962 var isHorizontal = this.layout._getOption('horizontal'); 963 return this.layout.options.percentPosition && !isHorizontal ? 964 ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px'; 965}; 966 967proto.getYValue = function( y ) { 968 var isHorizontal = this.layout._getOption('horizontal'); 969 return this.layout.options.percentPosition && isHorizontal ? 970 ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px'; 971}; 972 973proto._transitionTo = function( x, y ) { 974 this.getPosition(); 975 // get current x & y from top/left 976 var curX = this.position.x; 977 var curY = this.position.y; 978 979 var didNotMove = x == this.position.x && y == this.position.y; 980 981 // save end position 982 this.setPosition( x, y ); 983 984 // if did not move and not transitioning, just go to layout 985 if ( didNotMove && !this.isTransitioning ) { 986 this.layoutPosition(); 987 return; 988 } 989 990 var transX = x - curX; 991 var transY = y - curY; 992 var transitionStyle = {}; 993 transitionStyle.transform = this.getTranslate( transX, transY ); 994 995 this.transition({ 996 to: transitionStyle, 997 onTransitionEnd: { 998 transform: this.layoutPosition 999 }, 1000 isCleaning: true 1001 }); 1002}; 1003 1004proto.getTranslate = function( x, y ) { 1005 // flip cooridinates if origin on right or bottom 1006 var isOriginLeft = this.layout._getOption('originLeft'); 1007 var isOriginTop = this.layout._getOption('originTop'); 1008 x = isOriginLeft ? x : -x; 1009 y = isOriginTop ? y : -y; 1010 return 'translate3d(' + x + 'px, ' + y + 'px, 0)'; 1011}; 1012 1013// non transition + transform support 1014proto.goTo = function( x, y ) { 1015 this.setPosition( x, y ); 1016 this.layoutPosition(); 1017}; 1018 1019proto.moveTo = proto._transitionTo; 1020 1021proto.setPosition = function( x, y ) { 1022 this.position.x = parseFloat( x ); 1023 this.position.y = parseFloat( y ); 1024}; 1025 1026// ----- transition ----- // 1027 1028/** 1029 * @param {Object} style - CSS 1030 * @param {Function} onTransitionEnd 1031 */ 1032 1033// non transition, just trigger callback 1034proto._nonTransition = function( args ) { 1035 this.css( args.to ); 1036 if ( args.isCleaning ) { 1037 this._removeStyles( args.to ); 1038 } 1039 for ( var prop in args.onTransitionEnd ) { 1040 args.onTransitionEnd[ prop ].call( this ); 1041 } 1042}; 1043 1044/** 1045 * proper transition 1046 * @param {Object} args - arguments 1047 * @param {Object} to - style to transition to 1048 * @param {Object} from - style to start transition from 1049 * @param {Boolean} isCleaning - removes transition styles after transition 1050 * @param {Function} onTransitionEnd - callback 1051 */ 1052proto.transition = function( args ) { 1053 // redirect to nonTransition if no transition duration 1054 if ( !parseFloat( this.layout.options.transitionDuration ) ) { 1055 this._nonTransition( args ); 1056 return; 1057 } 1058 1059 var _transition = this._transn; 1060 // keep track of onTransitionEnd callback by css property 1061 for ( var prop in args.onTransitionEnd ) { 1062 _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ]; 1063 } 1064 // keep track of properties that are transitioning 1065 for ( prop in args.to ) { 1066 _transition.ingProperties[ prop ] = true; 1067 // keep track of properties to clean up when transition is done 1068 if ( args.isCleaning ) { 1069 _transition.clean[ prop ] = true; 1070 } 1071 } 1072 1073 // set from styles 1074 if ( args.from ) { 1075 this.css( args.from ); 1076 // force redraw. http://blog.alexmaccaw.com/css-transitions 1077 var h = this.element.offsetHeight; 1078 // hack for JSHint to hush about unused var 1079 h = null; 1080 } 1081 // enable transition 1082 this.enableTransition( args.to ); 1083 // set styles that are transitioning 1084 this.css( args.to ); 1085 1086 this.isTransitioning = true; 1087 1088}; 1089 1090// dash before all cap letters, including first for 1091// WebkitTransform => -webkit-transform 1092function toDashedAll( str ) { 1093 return str.replace( /([A-Z])/g, function( $1 ) { 1094 return '-' + $1.toLowerCase(); 1095 }); 1096} 1097 1098var transitionProps = 'opacity,' + toDashedAll( transformProperty ); 1099 1100proto.enableTransition = function(/* style */) { 1101 // HACK changing transitionProperty during a transition 1102 // will cause transition to jump 1103 if ( this.isTransitioning ) { 1104 return; 1105 } 1106 1107 // make `transition: foo, bar, baz` from style object 1108 // HACK un-comment this when enableTransition can work 1109 // while a transition is happening 1110 // var transitionValues = []; 1111 // for ( var prop in style ) { 1112 // // dash-ify camelCased properties like WebkitTransition 1113 // prop = vendorProperties[ prop ] || prop; 1114 // transitionValues.push( toDashedAll( prop ) ); 1115 // } 1116 // munge number to millisecond, to match stagger 1117 var duration = this.layout.options.transitionDuration; 1118 duration = typeof duration == 'number' ? duration + 'ms' : duration; 1119 // enable transition styles 1120 this.css({ 1121 transitionProperty: transitionProps, 1122 transitionDuration: duration, 1123 transitionDelay: this.staggerDelay || 0 1124 }); 1125 // listen for transition end event 1126 this.element.addEventListener( transitionEndEvent, this, false ); 1127}; 1128 1129// ----- events ----- // 1130 1131proto.onwebkitTransitionEnd = function( event ) { 1132 this.ontransitionend( event ); 1133}; 1134 1135proto.onotransitionend = function( event ) { 1136 this.ontransitionend( event ); 1137}; 1138 1139// properties that I munge to make my life easier 1140var dashedVendorProperties = { 1141 '-webkit-transform': 'transform' 1142}; 1143 1144proto.ontransitionend = function( event ) { 1145 // disregard bubbled events from children 1146 if ( event.target !== this.element ) { 1147 return; 1148 } 1149 var _transition = this._transn; 1150 // get property name of transitioned property, convert to prefix-free 1151 var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName; 1152 1153 // remove property that has completed transitioning 1154 delete _transition.ingProperties[ propertyName ]; 1155 // check if any properties are still transitioning 1156 if ( isEmptyObj( _transition.ingProperties ) ) { 1157 // all properties have completed transitioning 1158 this.disableTransition(); 1159 } 1160 // clean style 1161 if ( propertyName in _transition.clean ) { 1162 // clean up style 1163 this.element.style[ event.propertyName ] = ''; 1164 delete _transition.clean[ propertyName ]; 1165 } 1166 // trigger onTransitionEnd callback 1167 if ( propertyName in _transition.onEnd ) { 1168 var onTransitionEnd = _transition.onEnd[ propertyName ]; 1169 onTransitionEnd.call( this ); 1170 delete _transition.onEnd[ propertyName ]; 1171 } 1172 1173 this.emitEvent( 'transitionEnd', [ this ] ); 1174}; 1175 1176proto.disableTransition = function() { 1177 this.removeTransitionStyles(); 1178 this.element.removeEventListener( transitionEndEvent, this, false ); 1179 this.isTransitioning = false; 1180}; 1181 1182/** 1183 * removes style property from element 1184 * @param {Object} style 1185**/ 1186proto._removeStyles = function( style ) { 1187 // clean up transition styles 1188 var cleanStyle = {}; 1189 for ( var prop in style ) { 1190 cleanStyle[ prop ] = ''; 1191 } 1192 this.css( cleanStyle ); 1193}; 1194 1195var cleanTransitionStyle = { 1196 transitionProperty: '', 1197 transitionDuration: '', 1198 transitionDelay: '' 1199}; 1200 1201proto.removeTransitionStyles = function() { 1202 // remove transition 1203 this.css( cleanTransitionStyle ); 1204}; 1205 1206// ----- stagger ----- // 1207 1208proto.stagger = function( delay ) { 1209 delay = isNaN( delay ) ? 0 : delay; 1210 this.staggerDelay = delay + 'ms'; 1211}; 1212 1213// ----- show/hide/remove ----- // 1214 1215// remove element from DOM 1216proto.removeElem = function() { 1217 this.element.parentNode.removeChild( this.element ); 1218 // remove display: none 1219 this.css({ display: '' }); 1220 this.emitEvent( 'remove', [ this ] ); 1221}; 1222 1223proto.remove = function() { 1224 // just remove element if no transition support or no transition 1225 if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) { 1226 this.removeElem(); 1227 return; 1228 } 1229 1230 // start transition 1231 this.once( 'transitionEnd', function() { 1232 this.removeElem(); 1233 }); 1234 this.hide(); 1235}; 1236 1237proto.reveal = function() { 1238 delete this.isHidden; 1239 // remove display: none 1240 this.css({ display: '' }); 1241 1242 var options = this.layout.options; 1243 1244 var onTransitionEnd = {}; 1245 var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle'); 1246 onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd; 1247 1248 this.transition({ 1249 from: options.hiddenStyle, 1250 to: options.visibleStyle, 1251 isCleaning: true, 1252 onTransitionEnd: onTransitionEnd 1253 }); 1254}; 1255 1256proto.onRevealTransitionEnd = function() { 1257 // check if still visible 1258 // during transition, item may have been hidden 1259 if ( !this.isHidden ) { 1260 this.emitEvent('reveal'); 1261 } 1262}; 1263 1264/** 1265 * get style property use for hide/reveal transition end 1266 * @param {String} styleProperty - hiddenStyle/visibleStyle 1267 * @returns {String} 1268 */ 1269proto.getHideRevealTransitionEndProperty = function( styleProperty ) { 1270 var optionStyle = this.layout.options[ styleProperty ]; 1271 // use opacity 1272 if ( optionStyle.opacity ) { 1273 return 'opacity'; 1274 } 1275 // get first property 1276 for ( var prop in optionStyle ) { 1277 return prop; 1278 } 1279}; 1280 1281proto.hide = function() { 1282 // set flag 1283 this.isHidden = true; 1284 // remove display: none 1285 this.css({ display: '' }); 1286 1287 var options = this.layout.options; 1288 1289 var onTransitionEnd = {}; 1290 var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle'); 1291 onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd; 1292 1293 this.transition({ 1294 from: options.visibleStyle, 1295 to: options.hiddenStyle, 1296 // keep hidden stuff hidden 1297 isCleaning: true, 1298 onTransitionEnd: onTransitionEnd 1299 }); 1300}; 1301 1302proto.onHideTransitionEnd = function() { 1303 // check if still hidden 1304 // during transition, item may have been un-hidden 1305 if ( this.isHidden ) { 1306 this.css({ display: 'none' }); 1307 this.emitEvent('hide'); 1308 } 1309}; 1310 1311proto.destroy = function() { 1312 this.css({ 1313 position: '', 1314 left: '', 1315 right: '', 1316 top: '', 1317 bottom: '', 1318 transition: '', 1319 transform: '' 1320 }); 1321}; 1322 1323return Item; 1324 1325})); 1326 1327/*! 1328 * Outlayer v2.1.1 1329 * the brains and guts of a layout library 1330 * MIT license 1331 */ 1332 1333( function( window, factory ) { 1334 'use strict'; 1335 // universal module definition 1336 /* jshint strict: false */ /* globals define, module, require */ 1337 if ( typeof define == 'function' && define.amd ) { 1338 // AMD - RequireJS 1339 define( 'outlayer/outlayer',[ 1340 'ev-emitter/ev-emitter', 1341 'get-size/get-size', 1342 'fizzy-ui-utils/utils', 1343 './item' 1344 ], 1345 function( EvEmitter, getSize, utils, Item ) { 1346 return factory( window, EvEmitter, getSize, utils, Item); 1347 } 1348 ); 1349 } else if ( typeof module == 'object' && module.exports ) { 1350 // CommonJS - Browserify, Webpack 1351 module.exports = factory( 1352 window, 1353 require('ev-emitter'), 1354 require('get-size'), 1355 require('fizzy-ui-utils'), 1356 require('./item') 1357 ); 1358 } else { 1359 // browser global 1360 window.Outlayer = factory( 1361 window, 1362 window.EvEmitter, 1363 window.getSize, 1364 window.fizzyUIUtils, 1365 window.Outlayer.Item 1366 ); 1367 } 1368 1369}( window, function factory( window, EvEmitter, getSize, utils, Item ) { 1370'use strict'; 1371 1372// ----- vars ----- // 1373 1374var console = window.console; 1375var jQuery = window.jQuery; 1376var noop = function() {}; 1377 1378// -------------------------- Outlayer -------------------------- // 1379 1380// globally unique identifiers 1381var GUID = 0; 1382// internal store of all Outlayer intances 1383var instances = {}; 1384 1385 1386/** 1387 * @param {Element, String} element 1388 * @param {Object} options 1389 * @constructor 1390 */ 1391function Outlayer( element, options ) { 1392 var queryElement = utils.getQueryElement( element ); 1393 if ( !queryElement ) { 1394 if ( console ) { 1395 console.error( 'Bad element for ' + this.constructor.namespace + 1396 ': ' + ( queryElement || element ) ); 1397 } 1398 return; 1399 } 1400 this.element = queryElement; 1401 // add jQuery 1402 if ( jQuery ) { 1403 this.$element = jQuery( this.element ); 1404 } 1405 1406 // options 1407 this.options = utils.extend( {}, this.constructor.defaults ); 1408 this.option( options ); 1409 1410 // add id for Outlayer.getFromElement 1411 var id = ++GUID; 1412 this.element.outlayerGUID = id; // expando 1413 instances[ id ] = this; // associate via id 1414 1415 // kick it off 1416 this._create(); 1417 1418 var isInitLayout = this._getOption('initLayout'); 1419 if ( isInitLayout ) { 1420 this.layout(); 1421 } 1422} 1423 1424// settings are for internal use only 1425Outlayer.namespace = 'outlayer'; 1426Outlayer.Item = Item; 1427 1428// default options 1429Outlayer.defaults = { 1430 containerStyle: { 1431 position: 'relative' 1432 }, 1433 initLayout: true, 1434 originLeft: true, 1435 originTop: true, 1436 resize: true, 1437 resizeContainer: true, 1438 // item options 1439 transitionDuration: '0.4s', 1440 hiddenStyle: { 1441 opacity: 0, 1442 transform: 'scale(0.001)' 1443 }, 1444 visibleStyle: { 1445 opacity: 1, 1446 transform: 'scale(1)' 1447 } 1448}; 1449 1450var proto = Outlayer.prototype; 1451// inherit EvEmitter 1452utils.extend( proto, EvEmitter.prototype ); 1453 1454/** 1455 * set options 1456 * @param {Object} opts 1457 */ 1458proto.option = function( opts ) { 1459 utils.extend( this.options, opts ); 1460}; 1461 1462/** 1463 * get backwards compatible option value, check old name 1464 */ 1465proto._getOption = function( option ) { 1466 var oldOption = this.constructor.compatOptions[ option ]; 1467 return oldOption && this.options[ oldOption ] !== undefined ? 1468 this.options[ oldOption ] : this.options[ option ]; 1469}; 1470 1471Outlayer.compatOptions = { 1472 // currentName: oldName 1473 initLayout: 'isInitLayout', 1474 horizontal: 'isHorizontal', 1475 layoutInstant: 'isLayoutInstant', 1476 originLeft: 'isOriginLeft', 1477 originTop: 'isOriginTop', 1478 resize: 'isResizeBound', 1479 resizeContainer: 'isResizingContainer' 1480}; 1481 1482proto._create = function() { 1483 // get items from children 1484 this.reloadItems(); 1485 // elements that affect layout, but are not laid out 1486 this.stamps = []; 1487 this.stamp( this.options.stamp ); 1488 // set container style 1489 utils.extend( this.element.style, this.options.containerStyle ); 1490 1491 // bind resize method 1492 var canBindResize = this._getOption('resize'); 1493 if ( canBindResize ) { 1494 this.bindResize(); 1495 } 1496}; 1497 1498// goes through all children again and gets bricks in proper order 1499proto.reloadItems = function() { 1500 // collection of item elements 1501 this.items = this._itemize( this.element.children ); 1502}; 1503 1504 1505/** 1506 * turn elements into Outlayer.Items to be used in layout 1507 * @param {Array or NodeList or HTMLElement} elems 1508 * @returns {Array} items - collection of new Outlayer Items 1509 */ 1510proto._itemize = function( elems ) { 1511 1512 var itemElems = this._filterFindItemElements( elems ); 1513 var Item = this.constructor.Item; 1514 1515 // create new Outlayer Items for collection 1516 var items = []; 1517 for ( var i=0; i < itemElems.length; i++ ) { 1518 var elem = itemElems[i]; 1519 var item = new Item( elem, this ); 1520 items.push( item ); 1521 } 1522 1523 return items; 1524}; 1525 1526/** 1527 * get item elements to be used in layout 1528 * @param {Array or NodeList or HTMLElement} elems 1529 * @returns {Array} items - item elements 1530 */ 1531proto._filterFindItemElements = function( elems ) { 1532 return utils.filterFindElements( elems, this.options.itemSelector ); 1533}; 1534 1535/** 1536 * getter method for getting item elements 1537 * @returns {Array} elems - collection of item elements 1538 */ 1539proto.getItemElements = function() { 1540 return this.items.map( function( item ) { 1541 return item.element; 1542 }); 1543}; 1544 1545// ----- init & layout ----- // 1546 1547/** 1548 * lays out all items 1549 */ 1550proto.layout = function() { 1551 this._resetLayout(); 1552 this._manageStamps(); 1553 1554 // don't animate first layout 1555 var layoutInstant = this._getOption('layoutInstant'); 1556 var isInstant = layoutInstant !== undefined ? 1557 layoutInstant : !this._isLayoutInited; 1558 this.layoutItems( this.items, isInstant ); 1559 1560 // flag for initalized 1561 this._isLayoutInited = true; 1562}; 1563 1564// _init is alias for layout 1565proto._init = proto.layout; 1566 1567/** 1568 * logic before any new layout 1569 */ 1570proto._resetLayout = function() { 1571 this.getSize(); 1572}; 1573 1574 1575proto.getSize = function() { 1576 this.size = getSize( this.element ); 1577}; 1578 1579/** 1580 * get measurement from option, for columnWidth, rowHeight, gutter 1581 * if option is String -> get element from selector string, & get size of element 1582 * if option is Element -> get size of element 1583 * else use option as a number 1584 * 1585 * @param {String} measurement 1586 * @param {String} size - width or height 1587 * @private 1588 */ 1589proto._getMeasurement = function( measurement, size ) { 1590 var option = this.options[ measurement ]; 1591 var elem; 1592 if ( !option ) { 1593 // default to 0 1594 this[ measurement ] = 0; 1595 } else { 1596 // use option as an element 1597 if ( typeof option == 'string' ) { 1598 elem = this.element.querySelector( option ); 1599 } else if ( option instanceof HTMLElement ) { 1600 elem = option; 1601 } 1602 // use size of element, if element 1603 this[ measurement ] = elem ? getSize( elem )[ size ] : option; 1604 } 1605}; 1606 1607/** 1608 * layout a collection of item elements 1609 * @api public 1610 */ 1611proto.layoutItems = function( items, isInstant ) { 1612 items = this._getItemsForLayout( items ); 1613 1614 this._layoutItems( items, isInstant ); 1615 1616 this._postLayout(); 1617}; 1618 1619/** 1620 * get the items to be laid out 1621 * you may want to skip over some items 1622 * @param {Array} items 1623 * @returns {Array} items 1624 */ 1625proto._getItemsForLayout = function( items ) { 1626 return items.filter( function( item ) { 1627 return !item.isIgnored; 1628 }); 1629}; 1630 1631/** 1632 * layout items 1633 * @param {Array} items 1634 * @param {Boolean} isInstant 1635 */ 1636proto._layoutItems = function( items, isInstant ) { 1637 this._emitCompleteOnItems( 'layout', items ); 1638 1639 if ( !items || !items.length ) { 1640 // no items, emit event with empty array 1641 return; 1642 } 1643 1644 var queue = []; 1645 1646 items.forEach( function( item ) { 1647 // get x/y object from method 1648 var position = this._getItemLayoutPosition( item ); 1649 // enqueue 1650 position.item = item; 1651 position.isInstant = isInstant || item.isLayoutInstant; 1652 queue.push( position ); 1653 }, this ); 1654 1655 this._processLayoutQueue( queue ); 1656}; 1657 1658/** 1659 * get item layout position 1660 * @param {Outlayer.Item} item 1661 * @returns {Object} x and y position 1662 */ 1663proto._getItemLayoutPosition = function( /* item */ ) { 1664 return { 1665 x: 0, 1666 y: 0 1667 }; 1668}; 1669 1670/** 1671 * iterate over array and position each item 1672 * Reason being - separating this logic prevents 'layout invalidation' 1673 * thx @paul_irish 1674 * @param {Array} queue 1675 */ 1676proto._processLayoutQueue = function( queue ) { 1677 this.updateStagger(); 1678 queue.forEach( function( obj, i ) { 1679 this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i ); 1680 }, this ); 1681}; 1682 1683// set stagger from option in milliseconds number 1684proto.updateStagger = function() { 1685 var stagger = this.options.stagger; 1686 if ( stagger === null || stagger === undefined ) { 1687 this.stagger = 0; 1688 return; 1689 } 1690 this.stagger = getMilliseconds( stagger ); 1691 return this.stagger; 1692}; 1693 1694/** 1695 * Sets position of item in DOM 1696 * @param {Outlayer.Item} item 1697 * @param {Number} x - horizontal position 1698 * @param {Number} y - vertical position 1699 * @param {Boolean} isInstant - disables transitions 1700 */ 1701proto._positionItem = function( item, x, y, isInstant, i ) { 1702 if ( isInstant ) { 1703 // if not transition, just set CSS 1704 item.goTo( x, y ); 1705 } else { 1706 item.stagger( i * this.stagger ); 1707 item.moveTo( x, y ); 1708 } 1709}; 1710 1711/** 1712 * Any logic you want to do after each layout, 1713 * i.e. size the container 1714 */ 1715proto._postLayout = function() { 1716 this.resizeContainer(); 1717}; 1718 1719proto.resizeContainer = function() { 1720 var isResizingContainer = this._getOption('resizeContainer'); 1721 if ( !isResizingContainer ) { 1722 return; 1723 } 1724 var size = this._getContainerSize(); 1725 if ( size ) { 1726 this._setContainerMeasure( size.width, true ); 1727 this._setContainerMeasure( size.height, false ); 1728 } 1729}; 1730 1731/** 1732 * Sets width or height of container if returned 1733 * @returns {Object} size 1734 * @param {Number} width 1735 * @param {Number} height 1736 */ 1737proto._getContainerSize = noop; 1738 1739/** 1740 * @param {Number} measure - size of width or height 1741 * @param {Boolean} isWidth 1742 */ 1743proto._setContainerMeasure = function( measure, isWidth ) { 1744 if ( measure === undefined ) { 1745 return; 1746 } 1747 1748 var elemSize = this.size; 1749 // add padding and border width if border box 1750 if ( elemSize.isBorderBox ) { 1751 measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight + 1752 elemSize.borderLeftWidth + elemSize.borderRightWidth : 1753 elemSize.paddingBottom + elemSize.paddingTop + 1754 elemSize.borderTopWidth + elemSize.borderBottomWidth; 1755 } 1756 1757 measure = Math.max( measure, 0 ); 1758 this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px'; 1759}; 1760 1761/** 1762 * emit eventComplete on a collection of items events 1763 * @param {String} eventName 1764 * @param {Array} items - Outlayer.Items 1765 */ 1766proto._emitCompleteOnItems = function( eventName, items ) { 1767 var _this = this; 1768 function onComplete() { 1769 _this.dispatchEvent( eventName + 'Complete', null, [ items ] ); 1770 } 1771 1772 var count = items.length; 1773 if ( !items || !count ) { 1774 onComplete(); 1775 return; 1776 } 1777 1778 var doneCount = 0; 1779 function tick() { 1780 doneCount++; 1781 if ( doneCount == count ) { 1782 onComplete(); 1783 } 1784 } 1785 1786 // bind callback 1787 items.forEach( function( item ) { 1788 item.once( eventName, tick ); 1789 }); 1790}; 1791 1792/** 1793 * emits events via EvEmitter and jQuery events 1794 * @param {String} type - name of event 1795 * @param {Event} event - original event 1796 * @param {Array} args - extra arguments 1797 */ 1798proto.dispatchEvent = function( type, event, args ) { 1799 // add original event to arguments 1800 var emitArgs = event ? [ event ].concat( args ) : args; 1801 this.emitEvent( type, emitArgs ); 1802 1803 if ( jQuery ) { 1804 // set this.$element 1805 this.$element = this.$element || jQuery( this.element ); 1806 if ( event ) { 1807 // create jQuery event 1808 var $event = jQuery.Event( event ); 1809 $event.type = type; 1810 this.$element.trigger( $event, args ); 1811 } else { 1812 // just trigger with type if no event available 1813 this.$element.trigger( type, args ); 1814 } 1815 } 1816}; 1817 1818// -------------------------- ignore & stamps -------------------------- // 1819 1820 1821/** 1822 * keep item in collection, but do not lay it out 1823 * ignored items do not get skipped in layout 1824 * @param {Element} elem 1825 */ 1826proto.ignore = function( elem ) { 1827 var item = this.getItem( elem ); 1828 if ( item ) { 1829 item.isIgnored = true; 1830 } 1831}; 1832 1833/** 1834 * return item to layout collection 1835 * @param {Element} elem 1836 */ 1837proto.unignore = function( elem ) { 1838 var item = this.getItem( elem ); 1839 if ( item ) { 1840 delete item.isIgnored; 1841 } 1842}; 1843 1844/** 1845 * adds elements to stamps 1846 * @param {NodeList, Array, Element, or String} elems 1847 */ 1848proto.stamp = function( elems ) { 1849 elems = this._find( elems ); 1850 if ( !elems ) { 1851 return; 1852 } 1853 1854 this.stamps = this.stamps.concat( elems ); 1855 // ignore 1856 elems.forEach( this.ignore, this ); 1857}; 1858 1859/** 1860 * removes elements to stamps 1861 * @param {NodeList, Array, or Element} elems 1862 */ 1863proto.unstamp = function( elems ) { 1864 elems = this._find( elems ); 1865 if ( !elems ){ 1866 return; 1867 } 1868 1869 elems.forEach( function( elem ) { 1870 // filter out removed stamp elements 1871 utils.removeFrom( this.stamps, elem ); 1872 this.unignore( elem ); 1873 }, this ); 1874}; 1875 1876/** 1877 * finds child elements 1878 * @param {NodeList, Array, Element, or String} elems 1879 * @returns {Array} elems 1880 */ 1881proto._find = function( elems ) { 1882 if ( !elems ) { 1883 return; 1884 } 1885 // if string, use argument as selector string 1886 if ( typeof elems == 'string' ) { 1887 elems = this.element.querySelectorAll( elems ); 1888 } 1889 elems = utils.makeArray( elems ); 1890 return elems; 1891}; 1892 1893proto._manageStamps = function() { 1894 if ( !this.stamps || !this.stamps.length ) { 1895 return; 1896 } 1897 1898 this._getBoundingRect(); 1899 1900 this.stamps.forEach( this._manageStamp, this ); 1901}; 1902 1903// update boundingLeft / Top 1904proto._getBoundingRect = function() { 1905 // get bounding rect for container element 1906 var boundingRect = this.element.getBoundingClientRect(); 1907 var size = this.size; 1908 this._boundingRect = { 1909 left: boundingRect.left + size.paddingLeft + size.borderLeftWidth, 1910 top: boundingRect.top + size.paddingTop + size.borderTopWidth, 1911 right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ), 1912 bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth ) 1913 }; 1914}; 1915 1916/** 1917 * @param {Element} stamp 1918**/ 1919proto._manageStamp = noop; 1920 1921/** 1922 * get x/y position of element relative to container element 1923 * @param {Element} elem 1924 * @returns {Object} offset - has left, top, right, bottom 1925 */ 1926proto._getElementOffset = function( elem ) { 1927 var boundingRect = elem.getBoundingClientRect(); 1928 var thisRect = this._boundingRect; 1929 var size = getSize( elem ); 1930 var offset = { 1931 left: boundingRect.left - thisRect.left - size.marginLeft, 1932 top: boundingRect.top - thisRect.top - size.marginTop, 1933 right: thisRect.right - boundingRect.right - size.marginRight, 1934 bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom 1935 }; 1936 return offset; 1937}; 1938 1939// -------------------------- resize -------------------------- // 1940 1941// enable event handlers for listeners 1942// i.e. resize -> onresize 1943proto.handleEvent = utils.handleEvent; 1944 1945/** 1946 * Bind layout to window resizing 1947 */ 1948proto.bindResize = function() { 1949 window.addEventListener( 'resize', this ); 1950 this.isResizeBound = true; 1951}; 1952 1953/** 1954 * Unbind layout to window resizing 1955 */ 1956proto.unbindResize = function() { 1957 window.removeEventListener( 'resize', this ); 1958 this.isResizeBound = false; 1959}; 1960 1961proto.onresize = function() { 1962 this.resize(); 1963}; 1964 1965utils.debounceMethod( Outlayer, 'onresize', 100 ); 1966 1967proto.resize = function() { 1968 // don't trigger if size did not change 1969 // or if resize was unbound. See #9 1970 if ( !this.isResizeBound || !this.needsResizeLayout() ) { 1971 return; 1972 } 1973 1974 this.layout(); 1975}; 1976 1977/** 1978 * check if layout is needed post layout 1979 * @returns Boolean 1980 */ 1981proto.needsResizeLayout = function() { 1982 var size = getSize( this.element ); 1983 // check that this.size and size are there 1984 // IE8 triggers resize on body size change, so they might not be 1985 var hasSizes = this.size && size; 1986 return hasSizes && size.innerWidth !== this.size.innerWidth; 1987}; 1988 1989// -------------------------- methods -------------------------- // 1990 1991/** 1992 * add items to Outlayer instance 1993 * @param {Array or NodeList or Element} elems 1994 * @returns {Array} items - Outlayer.Items 1995**/ 1996proto.addItems = function( elems ) { 1997 var items = this._itemize( elems ); 1998 // add items to collection 1999 if ( items.length ) { 2000 this.items = this.items.concat( items ); 2001 } 2002 return items; 2003}; 2004 2005/** 2006 * Layout newly-appended item elements 2007 * @param {Array or NodeList or Element} elems 2008 */ 2009proto.appended = function( elems ) { 2010 var items = this.addItems( elems ); 2011 if ( !items.length ) { 2012 return; 2013 } 2014 // layout and reveal just the new items 2015 this.layoutItems( items, true ); 2016 this.reveal( items ); 2017}; 2018 2019/** 2020 * Layout prepended elements 2021 * @param {Array or NodeList or Element} elems 2022 */ 2023proto.prepended = function( elems ) { 2024 var items = this._itemize( elems ); 2025 if ( !items.length ) { 2026 return; 2027 } 2028 // add items to beginning of collection 2029 var previousItems = this.items.slice(0); 2030 this.items = items.concat( previousItems ); 2031 // start new layout 2032 this._resetLayout(); 2033 this._manageStamps(); 2034 // layout new stuff without transition 2035 this.layoutItems( items, true ); 2036 this.reveal( items ); 2037 // layout previous items 2038 this.layoutItems( previousItems ); 2039}; 2040 2041/** 2042 * reveal a collection of items 2043 * @param {Array of Outlayer.Items} items 2044 */ 2045proto.reveal = function( items ) { 2046 this._emitCompleteOnItems( 'reveal', items ); 2047 if ( !items || !items.length ) { 2048 return; 2049 } 2050 var stagger = this.updateStagger(); 2051 items.forEach( function( item, i ) { 2052 item.stagger( i * stagger ); 2053 item.reveal(); 2054 }); 2055}; 2056 2057/** 2058 * hide a collection of items 2059 * @param {Array of Outlayer.Items} items 2060 */ 2061proto.hide = function( items ) { 2062 this._emitCompleteOnItems( 'hide', items ); 2063 if ( !items || !items.length ) { 2064 return; 2065 } 2066 var stagger = this.updateStagger(); 2067 items.forEach( function( item, i ) { 2068 item.stagger( i * stagger ); 2069 item.hide(); 2070 }); 2071}; 2072 2073/** 2074 * reveal item elements 2075 * @param {Array}, {Element}, {NodeList} items 2076 */ 2077proto.revealItemElements = function( elems ) { 2078 var items = this.getItems( elems ); 2079 this.reveal( items ); 2080}; 2081 2082/** 2083 * hide item elements 2084 * @param {Array}, {Element}, {NodeList} items 2085 */ 2086proto.hideItemElements = function( elems ) { 2087 var items = this.getItems( elems ); 2088 this.hide( items ); 2089}; 2090 2091/** 2092 * get Outlayer.Item, given an Element 2093 * @param {Element} elem 2094 * @param {Function} callback 2095 * @returns {Outlayer.Item} item 2096 */ 2097proto.getItem = function( elem ) { 2098 // loop through items to get the one that matches 2099 for ( var i=0; i < this.items.length; i++ ) { 2100 var item = this.items[i]; 2101 if ( item.element == elem ) { 2102 // return item 2103 return item; 2104 } 2105 } 2106}; 2107 2108/** 2109 * get collection of Outlayer.Items, given Elements 2110 * @param {Array} elems 2111 * @returns {Array} items - Outlayer.Items 2112 */ 2113proto.getItems = function( elems ) { 2114 elems = utils.makeArray( elems ); 2115 var items = []; 2116 elems.forEach( function( elem ) { 2117 var item = this.getItem( elem ); 2118 if ( item ) { 2119 items.push( item ); 2120 } 2121 }, this ); 2122 2123 return items; 2124}; 2125 2126/** 2127 * remove element(s) from instance and DOM 2128 * @param {Array or NodeList or Element} elems 2129 */ 2130proto.remove = function( elems ) { 2131 var removeItems = this.getItems( elems ); 2132 2133 this._emitCompleteOnItems( 'remove', removeItems ); 2134 2135 // bail if no items to remove 2136 if ( !removeItems || !removeItems.length ) { 2137 return; 2138 } 2139
vendor: 38,284 bytes, lines 2140-3562
2140 removeItems.forEach( function( item ) { 2141 item.remove(); 2142 // remove item from collection 2143 utils.removeFrom( this.items, item ); 2144 }, this ); 2145}; 2146 2147// ----- destroy ----- // 2148 2149// remove and disable Outlayer instance 2150proto.destroy = function() { 2151 // clean up dynamic styles 2152 var style = this.element.style; 2153 style.height = ''; 2154 style.position = ''; 2155 style.width = ''; 2156 // destroy items 2157 this.items.forEach( function( item ) { 2158 item.destroy(); 2159 }); 2160 2161 this.unbindResize(); 2162 2163 var id = this.element.outlayerGUID; 2164 delete instances[ id ]; // remove reference to instance by id 2165 delete this.element.outlayerGUID; 2166 // remove data for jQuery 2167 if ( jQuery ) { 2168 jQuery.removeData( this.element, this.constructor.namespace ); 2169 } 2170 2171}; 2172 2173// -------------------------- data -------------------------- // 2174 2175/** 2176 * get Outlayer instance from element 2177 * @param {Element} elem 2178 * @returns {Outlayer} 2179 */ 2180Outlayer.data = function( elem ) { 2181 elem = utils.getQueryElement( elem ); 2182 var id = elem && elem.outlayerGUID; 2183 return id && instances[ id ]; 2184}; 2185 2186 2187// -------------------------- create Outlayer class -------------------------- // 2188 2189/** 2190 * create a layout class 2191 * @param {String} namespace 2192 */ 2193Outlayer.create = function( namespace, options ) { 2194 // sub-class Outlayer 2195 var Layout = subclass( Outlayer ); 2196 // apply new options and compatOptions 2197 Layout.defaults = utils.extend( {}, Outlayer.defaults ); 2198 utils.extend( Layout.defaults, options ); 2199 Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions ); 2200 2201 Layout.namespace = namespace; 2202 2203 Layout.data = Outlayer.data; 2204 2205 // sub-class Item 2206 Layout.Item = subclass( Item ); 2207 2208 // -------------------------- declarative -------------------------- // 2209 2210 utils.htmlInit( Layout, namespace ); 2211 2212 // -------------------------- jQuery bridge -------------------------- // 2213 2214 // make into jQuery plugin 2215 if ( jQuery && jQuery.bridget ) { 2216 jQuery.bridget( namespace, Layout ); 2217 } 2218 2219 return Layout; 2220}; 2221 2222function subclass( Parent ) { 2223 function SubClass() { 2224 Parent.apply( this, arguments ); 2225 } 2226 2227 SubClass.prototype = Object.create( Parent.prototype ); 2228 SubClass.prototype.constructor = SubClass; 2229 2230 return SubClass; 2231} 2232 2233// ----- helpers ----- // 2234 2235// how many milliseconds are in each unit 2236var msUnits = { 2237 ms: 1, 2238 s: 1000 2239}; 2240 2241// munge time-like parameter into millisecond number 2242// '0.4s' -> 40 2243function getMilliseconds( time ) { 2244 if ( typeof time == 'number' ) { 2245 return time; 2246 } 2247 var matches = time.match( /(^\d*\.?\d*)(\w*)/ ); 2248 var num = matches && matches[1]; 2249 var unit = matches && matches[2]; 2250 if ( !num.length ) { 2251 return 0; 2252 } 2253 num = parseFloat( num ); 2254 var mult = msUnits[ unit ] || 1; 2255 return num * mult; 2256} 2257 2258// ----- fin ----- // 2259 2260// back in global 2261Outlayer.Item = Item; 2262 2263return Outlayer; 2264 2265})); 2266 2267/** 2268 * Isotope Item 2269**/ 2270 2271( function( window, factory ) { 2272 // universal module definition 2273 /* jshint strict: false */ /*globals define, module, require */ 2274 if ( typeof define == 'function' && define.amd ) { 2275 // AMD 2276 define( 'isotope-layout/js/item',[ 2277 'outlayer/outlayer' 2278 ], 2279 factory ); 2280 } else if ( typeof module == 'object' && module.exports ) { 2281 // CommonJS 2282 module.exports = factory( 2283 require('outlayer') 2284 ); 2285 } else { 2286 // browser global 2287 window.Isotope = window.Isotope || {}; 2288 window.Isotope.Item = factory( 2289 window.Outlayer 2290 ); 2291 } 2292 2293}( window, function factory( Outlayer ) { 2294'use strict'; 2295 2296// -------------------------- Item -------------------------- // 2297 2298// sub-class Outlayer Item 2299function Item() { 2300 Outlayer.Item.apply( this, arguments ); 2301} 2302 2303var proto = Item.prototype = Object.create( Outlayer.Item.prototype ); 2304 2305var _create = proto._create; 2306proto._create = function() { 2307 // assign id, used for original-order sorting 2308 this.id = this.layout.itemGUID++; 2309 _create.call( this ); 2310 this.sortData = {}; 2311}; 2312 2313proto.updateSortData = function() { 2314 if ( this.isIgnored ) { 2315 return; 2316 } 2317 // default sorters 2318 this.sortData.id = this.id; 2319 // for backward compatibility 2320 this.sortData['original-order'] = this.id; 2321 this.sortData.random = Math.random(); 2322 // go thru getSortData obj and apply the sorters 2323 var getSortData = this.layout.options.getSortData; 2324 var sorters = this.layout._sorters; 2325 for ( var key in getSortData ) { 2326 var sorter = sorters[ key ]; 2327 this.sortData[ key ] = sorter( this.element, this ); 2328 } 2329}; 2330 2331var _destroy = proto.destroy; 2332proto.destroy = function() { 2333 // call super 2334 _destroy.apply( this, arguments ); 2335 // reset display, #741 2336 this.css({ 2337 display: '' 2338 }); 2339}; 2340 2341return Item; 2342 2343})); 2344 2345/** 2346 * Isotope LayoutMode 2347 */ 2348 2349( function( window, factory ) { 2350 // universal module definition 2351 /* jshint strict: false */ /*globals define, module, require */ 2352 if ( typeof define == 'function' && define.amd ) { 2353 // AMD 2354 define( 'isotope-layout/js/layout-mode',[ 2355 'get-size/get-size', 2356 'outlayer/outlayer' 2357 ], 2358 factory ); 2359 } else if ( typeof module == 'object' && module.exports ) { 2360 // CommonJS 2361 module.exports = factory( 2362 require('get-size'), 2363 require('outlayer') 2364 ); 2365 } else { 2366 // browser global 2367 window.Isotope = window.Isotope || {}; 2368 window.Isotope.LayoutMode = factory( 2369 window.getSize, 2370 window.Outlayer 2371 ); 2372 } 2373 2374}( window, function factory( getSize, Outlayer ) { 2375 'use strict'; 2376 2377 // layout mode class 2378 function LayoutMode( isotope ) { 2379 this.isotope = isotope; 2380 // link properties 2381 if ( isotope ) { 2382 this.options = isotope.options[ this.namespace ]; 2383 this.element = isotope.element; 2384 this.items = isotope.filteredItems; 2385 this.size = isotope.size; 2386 } 2387 } 2388 2389 var proto = LayoutMode.prototype; 2390 2391 /** 2392 * some methods should just defer to default Outlayer method 2393 * and reference the Isotope instance as `this` 2394 **/ 2395 var facadeMethods = [ 2396 '_resetLayout', 2397 '_getItemLayoutPosition', 2398 '_manageStamp', 2399 '_getContainerSize', 2400 '_getElementOffset', 2401 'needsResizeLayout', 2402 '_getOption' 2403 ]; 2404 2405 facadeMethods.forEach( function( methodName ) { 2406 proto[ methodName ] = function() { 2407 return Outlayer.prototype[ methodName ].apply( this.isotope, arguments ); 2408 }; 2409 }); 2410 2411 // ----- ----- // 2412 2413 // for horizontal layout modes, check vertical size 2414 proto.needsVerticalResizeLayout = function() { 2415 // don't trigger if size did not change 2416 var size = getSize( this.isotope.element ); 2417 // check that this.size and size are there 2418 // IE8 triggers resize on body size change, so they might not be 2419 var hasSizes = this.isotope.size && size; 2420 return hasSizes && size.innerHeight != this.isotope.size.innerHeight; 2421 }; 2422 2423 // ----- measurements ----- // 2424 2425 proto._getMeasurement = function() { 2426 this.isotope._getMeasurement.apply( this, arguments ); 2427 }; 2428 2429 proto.getColumnWidth = function() { 2430 this.getSegmentSize( 'column', 'Width' ); 2431 }; 2432 2433 proto.getRowHeight = function() { 2434 this.getSegmentSize( 'row', 'Height' ); 2435 }; 2436 2437 /** 2438 * get columnWidth or rowHeight 2439 * segment: 'column' or 'row' 2440 * size 'Width' or 'Height' 2441 **/ 2442 proto.getSegmentSize = function( segment, size ) { 2443 var segmentName = segment + size; 2444 var outerSize = 'outer' + size; 2445 // columnWidth / outerWidth // rowHeight / outerHeight 2446 this._getMeasurement( segmentName, outerSize ); 2447 // got rowHeight or columnWidth, we can chill 2448 if ( this[ segmentName ] ) { 2449 return; 2450 } 2451 // fall back to item of first element 2452 var firstItemSize = this.getFirstItemSize(); 2453 this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] || 2454 // or size of container 2455 this.isotope.size[ 'inner' + size ]; 2456 }; 2457 2458 proto.getFirstItemSize = function() { 2459 var firstItem = this.isotope.filteredItems[0]; 2460 return firstItem && firstItem.element && getSize( firstItem.element ); 2461 }; 2462 2463 // ----- methods that should reference isotope ----- // 2464 2465 proto.layout = function() { 2466 this.isotope.layout.apply( this.isotope, arguments ); 2467 }; 2468 2469 proto.getSize = function() { 2470 this.isotope.getSize(); 2471 this.size = this.isotope.size; 2472 }; 2473 2474 // -------------------------- create -------------------------- // 2475 2476 LayoutMode.modes = {}; 2477 2478 LayoutMode.create = function( namespace, options ) { 2479 2480 function Mode() { 2481 LayoutMode.apply( this, arguments ); 2482 } 2483 2484 Mode.prototype = Object.create( proto ); 2485 Mode.prototype.constructor = Mode; 2486 2487 // default options 2488 if ( options ) { 2489 Mode.options = options; 2490 } 2491 2492 Mode.prototype.namespace = namespace; 2493 // register in Isotope 2494 LayoutMode.modes[ namespace ] = Mode; 2495 2496 return Mode; 2497 }; 2498 2499 return LayoutMode; 2500 2501})); 2502 2503/*! 2504 * Masonry v4.2.1 2505 * Cascading grid layout library 2506 * https://masonry.desandro.com 2507 * MIT License 2508 * by David DeSandro 2509 */ 2510 2511( function( window, factory ) { 2512 // universal module definition 2513 /* jshint strict: false */ /*globals define, module, require */ 2514 if ( typeof define == 'function' && define.amd ) { 2515 // AMD 2516 define( 'masonry-layout/masonry',[ 2517 'outlayer/outlayer', 2518 'get-size/get-size' 2519 ], 2520 factory ); 2521 } else if ( typeof module == 'object' && module.exports ) { 2522 // CommonJS 2523 module.exports = factory( 2524 require('outlayer'), 2525 require('get-size') 2526 ); 2527 } else { 2528 // browser global 2529 window.Masonry = factory( 2530 window.Outlayer, 2531 window.getSize 2532 ); 2533 } 2534 2535}( window, function factory( Outlayer, getSize ) { 2536 2537 2538 2539// -------------------------- masonryDefinition -------------------------- // 2540 2541 // create an Outlayer layout class 2542 var Masonry = Outlayer.create('masonry'); 2543 // isFitWidth -> fitWidth 2544 Masonry.compatOptions.fitWidth = 'isFitWidth'; 2545 2546 var proto = Masonry.prototype; 2547 2548 proto._resetLayout = function() { 2549 this.getSize(); 2550 this._getMeasurement( 'columnWidth', 'outerWidth' ); 2551 this._getMeasurement( 'gutter', 'outerWidth' ); 2552 this.measureColumns(); 2553 2554 // reset column Y 2555 this.colYs = []; 2556 for ( var i=0; i < this.cols; i++ ) { 2557 this.colYs.push( 0 ); 2558 } 2559 2560 this.maxY = 0; 2561 this.horizontalColIndex = 0; 2562 }; 2563 2564 proto.measureColumns = function() { 2565 this.getContainerWidth(); 2566 // if columnWidth is 0, default to outerWidth of first item 2567 if ( !this.columnWidth ) { 2568 var firstItem = this.items[0]; 2569 var firstItemElem = firstItem && firstItem.element; 2570 // columnWidth fall back to item of first element 2571 this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth || 2572 // if first elem has no width, default to size of container 2573 this.containerWidth; 2574 } 2575 2576 var columnWidth = this.columnWidth += this.gutter; 2577 2578 // calculate columns 2579 var containerWidth = this.containerWidth + this.gutter; 2580 var cols = containerWidth / columnWidth; 2581 // fix rounding errors, typically with gutters 2582 var excess = columnWidth - containerWidth % columnWidth; 2583 // if overshoot is less than a pixel, round up, otherwise floor it 2584 var mathMethod = excess && excess < 1 ? 'round' : 'floor'; 2585 cols = Math[ mathMethod ]( cols ); 2586 this.cols = Math.max( cols, 1 ); 2587 }; 2588 2589 proto.getContainerWidth = function() { 2590 // container is parent if fit width 2591 var isFitWidth = this._getOption('fitWidth'); 2592 var container = isFitWidth ? this.element.parentNode : this.element; 2593 // check that this.size and size are there 2594 // IE8 triggers resize on body size change, so they might not be 2595 var size = getSize( container ); 2596 this.containerWidth = size && size.innerWidth; 2597 }; 2598 2599 proto._getItemLayoutPosition = function( item ) { 2600 item.getSize(); 2601 // how many columns does this brick span 2602 var remainder = item.size.outerWidth % this.columnWidth; 2603 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil'; 2604 // round if off by 1 pixel, otherwise use ceil 2605 var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth ); 2606 colSpan = Math.min( colSpan, this.cols ); 2607 // use horizontal or top column position 2608 var colPosMethod = this.options.horizontalOrder ? 2609 '_getHorizontalColPosition' : '_getTopColPosition'; 2610 var colPosition = this[ colPosMethod ]( colSpan, item ); 2611 // position the brick 2612 var position = { 2613 x: this.columnWidth * colPosition.col, 2614 y: colPosition.y 2615 }; 2616 // apply setHeight to necessary columns 2617 var setHeight = colPosition.y + item.size.outerHeight; 2618 var setMax = colSpan + colPosition.col; 2619 for ( var i = colPosition.col; i < setMax; i++ ) { 2620 this.colYs[i] = setHeight; 2621 } 2622 2623 return position; 2624 }; 2625 2626 proto._getTopColPosition = function( colSpan ) { 2627 var colGroup = this._getTopColGroup( colSpan ); 2628 // get the minimum Y value from the columns 2629 var minimumY = Math.min.apply( Math, colGroup ); 2630 2631 return { 2632 col: colGroup.indexOf( minimumY ), 2633 y: minimumY, 2634 }; 2635 }; 2636 2637 /** 2638 * @param {Number} colSpan - number of columns the element spans 2639 * @returns {Array} colGroup 2640 */ 2641 proto._getTopColGroup = function( colSpan ) { 2642 if ( colSpan < 2 ) { 2643 // if brick spans only one column, use all the column Ys 2644 return this.colYs; 2645 } 2646 2647 var colGroup = []; 2648 // how many different places could this brick fit horizontally 2649 var groupCount = this.cols + 1 - colSpan; 2650 // for each group potential horizontal position 2651 for ( var i = 0; i < groupCount; i++ ) { 2652 colGroup[i] = this._getColGroupY( i, colSpan ); 2653 } 2654 return colGroup; 2655 }; 2656 2657 proto._getColGroupY = function( col, colSpan ) { 2658 if ( colSpan < 2 ) { 2659 return this.colYs[ col ]; 2660 } 2661 // make an array of colY values for that one group 2662 var groupColYs = this.colYs.slice( col, col + colSpan ); 2663 // and get the max value of the array 2664 return Math.max.apply( Math, groupColYs ); 2665 }; 2666 2667 // get column position based on horizontal index. #873 2668 proto._getHorizontalColPosition = function( colSpan, item ) { 2669 var col = this.horizontalColIndex % this.cols; 2670 var isOver = colSpan > 1 && col + colSpan > this.cols; 2671 // shift to next row if item can't fit on current row 2672 col = isOver ? 0 : col; 2673 // don't let zero-size items take up space 2674 var hasSize = item.size.outerWidth && item.size.outerHeight; 2675 this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex; 2676 2677 return { 2678 col: col, 2679 y: this._getColGroupY( col, colSpan ), 2680 }; 2681 }; 2682 2683 proto._manageStamp = function( stamp ) { 2684 var stampSize = getSize( stamp ); 2685 var offset = this._getElementOffset( stamp ); 2686 // get the columns that this stamp affects 2687 var isOriginLeft = this._getOption('originLeft'); 2688 var firstX = isOriginLeft ? offset.left : offset.right; 2689 var lastX = firstX + stampSize.outerWidth; 2690 var firstCol = Math.floor( firstX / this.columnWidth ); 2691 firstCol = Math.max( 0, firstCol ); 2692 var lastCol = Math.floor( lastX / this.columnWidth ); 2693 // lastCol should not go over if multiple of columnWidth #425 2694 lastCol -= lastX % this.columnWidth ? 0 : 1; 2695 lastCol = Math.min( this.cols - 1, lastCol ); 2696 // set colYs to bottom of the stamp 2697 2698 var isOriginTop = this._getOption('originTop'); 2699 var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) + 2700 stampSize.outerHeight; 2701 for ( var i = firstCol; i <= lastCol; i++ ) { 2702 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] ); 2703 } 2704 }; 2705 2706 proto._getContainerSize = function() { 2707 this.maxY = Math.max.apply( Math, this.colYs ); 2708 var size = { 2709 height: this.maxY 2710 }; 2711 2712 if ( this._getOption('fitWidth') ) { 2713 size.width = this._getContainerFitWidth(); 2714 } 2715 2716 return size; 2717 }; 2718 2719 proto._getContainerFitWidth = function() { 2720 var unusedCols = 0; 2721 // count unused columns 2722 var i = this.cols; 2723 while ( --i ) { 2724 if ( this.colYs[i] !== 0 ) { 2725 break; 2726 } 2727 unusedCols++; 2728 } 2729 // fit container to columns that have been used 2730 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter; 2731 }; 2732 2733 proto.needsResizeLayout = function() { 2734 var previousWidth = this.containerWidth; 2735 this.getContainerWidth(); 2736 return previousWidth != this.containerWidth; 2737 }; 2738 2739 return Masonry; 2740 2741})); 2742 2743/*! 2744 * Masonry layout mode 2745 * sub-classes Masonry 2746 * https://masonry.desandro.com 2747 */ 2748 2749( function( window, factory ) { 2750 // universal module definition 2751 /* jshint strict: false */ /*globals define, module, require */ 2752 if ( typeof define == 'function' && define.amd ) { 2753 // AMD 2754 define( 'isotope-layout/js/layout-modes/masonry',[ 2755 '../layout-mode', 2756 'masonry-layout/masonry' 2757 ], 2758 factory ); 2759 } else if ( typeof module == 'object' && module.exports ) { 2760 // CommonJS 2761 module.exports = factory( 2762 require('../layout-mode'), 2763 require('masonry-layout') 2764 ); 2765 } else { 2766 // browser global 2767 factory( 2768 window.Isotope.LayoutMode, 2769 window.Masonry 2770 ); 2771 } 2772 2773}( window, function factory( LayoutMode, Masonry ) { 2774'use strict'; 2775 2776// -------------------------- masonryDefinition -------------------------- // 2777 2778 // create an Outlayer layout class 2779 var MasonryMode = LayoutMode.create('masonry'); 2780 2781 var proto = MasonryMode.prototype; 2782 2783 var keepModeMethods = { 2784 _getElementOffset: true, 2785 layout: true, 2786 _getMeasurement: true 2787 }; 2788 2789 // inherit Masonry prototype 2790 for ( var method in Masonry.prototype ) { 2791 // do not inherit mode methods 2792 if ( !keepModeMethods[ method ] ) { 2793 proto[ method ] = Masonry.prototype[ method ]; 2794 } 2795 } 2796 2797 var measureColumns = proto.measureColumns; 2798 proto.measureColumns = function() { 2799 // set items, used if measuring first item 2800 this.items = this.isotope.filteredItems; 2801 measureColumns.call( this ); 2802 }; 2803 2804 // point to mode options for fitWidth 2805 var _getOption = proto._getOption; 2806 proto._getOption = function( option ) { 2807 if ( option == 'fitWidth' ) { 2808 return this.options.isFitWidth !== undefined ? 2809 this.options.isFitWidth : this.options.fitWidth; 2810 } 2811 return _getOption.apply( this.isotope, arguments ); 2812 }; 2813 2814 return MasonryMode; 2815 2816})); 2817 2818/** 2819 * fitRows layout mode 2820 */ 2821 2822( function( window, factory ) { 2823 // universal module definition 2824 /* jshint strict: false */ /*globals define, module, require */ 2825 if ( typeof define == 'function' && define.amd ) { 2826 // AMD 2827 define( 'isotope-layout/js/layout-modes/fit-rows',[ 2828 '../layout-mode' 2829 ], 2830 factory ); 2831 } else if ( typeof exports == 'object' ) { 2832 // CommonJS 2833 module.exports = factory( 2834 require('../layout-mode') 2835 ); 2836 } else { 2837 // browser global 2838 factory( 2839 window.Isotope.LayoutMode 2840 ); 2841 } 2842 2843}( window, function factory( LayoutMode ) { 2844'use strict'; 2845 2846var FitRows = LayoutMode.create('fitRows'); 2847 2848var proto = FitRows.prototype; 2849 2850proto._resetLayout = function() { 2851 this.x = 0; 2852 this.y = 0; 2853 this.maxY = 0; 2854 this._getMeasurement( 'gutter', 'outerWidth' ); 2855}; 2856 2857proto._getItemLayoutPosition = function( item ) { 2858 item.getSize(); 2859 2860 var itemWidth = item.size.outerWidth + this.gutter; 2861 // if this element cannot fit in the current row 2862 var containerWidth = this.isotope.size.innerWidth + this.gutter; 2863 if ( this.x !== 0 && itemWidth + this.x > containerWidth ) { 2864 this.x = 0; 2865 this.y = this.maxY; 2866 } 2867 2868 var position = { 2869 x: this.x, 2870 y: this.y 2871 }; 2872 2873 this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight ); 2874 this.x += itemWidth; 2875 2876 return position; 2877}; 2878 2879proto._getContainerSize = function() { 2880 return { height: this.maxY }; 2881}; 2882 2883return FitRows; 2884 2885})); 2886 2887/** 2888 * vertical layout mode 2889 */ 2890 2891( function( window, factory ) { 2892 // universal module definition 2893 /* jshint strict: false */ /*globals define, module, require */ 2894 if ( typeof define == 'function' && define.amd ) { 2895 // AMD 2896 define( 'isotope-layout/js/layout-modes/vertical',[ 2897 '../layout-mode' 2898 ], 2899 factory ); 2900 } else if ( typeof module == 'object' && module.exports ) { 2901 // CommonJS 2902 module.exports = factory( 2903 require('../layout-mode') 2904 ); 2905 } else { 2906 // browser global 2907 factory( 2908 window.Isotope.LayoutMode 2909 ); 2910 } 2911 2912}( window, function factory( LayoutMode ) { 2913'use strict'; 2914 2915var Vertical = LayoutMode.create( 'vertical', { 2916 horizontalAlignment: 0 2917}); 2918 2919var proto = Vertical.prototype; 2920 2921proto._resetLayout = function() { 2922 this.y = 0; 2923}; 2924 2925proto._getItemLayoutPosition = function( item ) { 2926 item.getSize(); 2927 var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) * 2928 this.options.horizontalAlignment; 2929 var y = this.y; 2930 this.y += item.size.outerHeight; 2931 return { x: x, y: y }; 2932}; 2933 2934proto._getContainerSize = function() { 2935 return { height: this.y }; 2936}; 2937 2938return Vertical; 2939 2940})); 2941 2942/*! 2943 * Isotope v3.0.6 2944 * 2945 * Licensed GPLv3 for open source use 2946 * or Isotope Commercial License for commercial use 2947 * 2948 * https://isotope.metafizzy.co 2949 * Copyright 2010-2018 Metafizzy 2950 */ 2951 2952( function( window, factory ) { 2953 // universal module definition 2954 /* jshint strict: false */ /*globals define, module, require */ 2955 if ( typeof define == 'function' && define.amd ) { 2956 // AMD 2957 define( [ 2958 'outlayer/outlayer', 2959 'get-size/get-size', 2960 'desandro-matches-selector/matches-selector', 2961 'fizzy-ui-utils/utils', 2962 'isotope-layout/js/item', 2963 'isotope-layout/js/layout-mode', 2964 // include default layout modes 2965 'isotope-layout/js/layout-modes/masonry', 2966 'isotope-layout/js/layout-modes/fit-rows', 2967 'isotope-layout/js/layout-modes/vertical' 2968 ], 2969 function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) { 2970 return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ); 2971 }); 2972 } else if ( typeof module == 'object' && module.exports ) { 2973 // CommonJS 2974 module.exports = factory( 2975 window, 2976 require('outlayer'), 2977 require('get-size'), 2978 require('desandro-matches-selector'), 2979 require('fizzy-ui-utils'), 2980 require('isotope-layout/js/item'), 2981 require('isotope-layout/js/layout-mode'), 2982 // include default layout modes 2983 require('isotope-layout/js/layout-modes/masonry'), 2984 require('isotope-layout/js/layout-modes/fit-rows'), 2985 require('isotope-layout/js/layout-modes/vertical') 2986 ); 2987 } else { 2988 // browser global 2989 window.Isotope = factory( 2990 window, 2991 window.Outlayer, 2992 window.getSize, 2993 window.matchesSelector, 2994 window.fizzyUIUtils, 2995 window.Isotope.Item, 2996 window.Isotope.LayoutMode 2997 ); 2998 } 2999 3000}( window, function factory( window, Outlayer, getSize, matchesSelector, utils, 3001 Item, LayoutMode ) { 3002 3003 3004 3005// -------------------------- vars -------------------------- // 3006 3007var jQuery = window.jQuery; 3008 3009// -------------------------- helpers -------------------------- // 3010 3011var trim = String.prototype.trim ? 3012 function( str ) { 3013 return str.trim(); 3014 } : 3015 function( str ) { 3016 return str.replace( /^\s+|\s+$/g, '' ); 3017 }; 3018 3019// -------------------------- isotopeDefinition -------------------------- // 3020 3021 // create an Outlayer layout class 3022 var Isotope = Outlayer.create( 'isotope', { 3023 layoutMode: 'masonry', 3024 isJQueryFiltering: true, 3025 sortAscending: true 3026 }); 3027 3028 Isotope.Item = Item; 3029 Isotope.LayoutMode = LayoutMode; 3030 3031 var proto = Isotope.prototype; 3032 3033 proto._create = function() { 3034 this.itemGUID = 0; 3035 // functions that sort items 3036 this._sorters = {}; 3037 this._getSorters(); 3038 // call super 3039 Outlayer.prototype._create.call( this ); 3040 3041 // create layout modes 3042 this.modes = {}; 3043 // start filteredItems with all items 3044 this.filteredItems = this.items; 3045 // keep of track of sortBys 3046 this.sortHistory = [ 'original-order' ]; 3047 // create from registered layout modes 3048 for ( var name in LayoutMode.modes ) { 3049 this._initLayoutMode( name ); 3050 } 3051 }; 3052 3053 proto.reloadItems = function() { 3054 // reset item ID counter 3055 this.itemGUID = 0; 3056 // call super 3057 Outlayer.prototype.reloadItems.call( this ); 3058 }; 3059 3060 proto._itemize = function() { 3061 var items = Outlayer.prototype._itemize.apply( this, arguments ); 3062 // assign ID for original-order 3063 for ( var i=0; i < items.length; i++ ) { 3064 var item = items[i]; 3065 item.id = this.itemGUID++; 3066 } 3067 this._updateItemsSortData( items ); 3068 return items; 3069 }; 3070 3071 3072 // -------------------------- layout -------------------------- // 3073 3074 proto._initLayoutMode = function( name ) { 3075 var Mode = LayoutMode.modes[ name ]; 3076 // set mode options 3077 // HACK extend initial options, back-fill in default options 3078 var initialOpts = this.options[ name ] || {}; 3079 this.options[ name ] = Mode.options ? 3080 utils.extend( Mode.options, initialOpts ) : initialOpts; 3081 // init layout mode instance 3082 this.modes[ name ] = new Mode( this ); 3083 }; 3084 3085 3086 proto.layout = function() { 3087 // if first time doing layout, do all magic 3088 if ( !this._isLayoutInited && this._getOption('initLayout') ) { 3089 this.arrange(); 3090 return; 3091 } 3092 this._layout(); 3093 }; 3094 3095 // private method to be used in layout() & magic() 3096 proto._layout = function() { 3097 // don't animate first layout 3098 var isInstant = this._getIsInstant(); 3099 // layout flow 3100 this._resetLayout(); 3101 this._manageStamps(); 3102 this.layoutItems( this.filteredItems, isInstant ); 3103 3104 // flag for initalized 3105 this._isLayoutInited = true; 3106 }; 3107 3108 // filter + sort + layout 3109 proto.arrange = function( opts ) { 3110 // set any options pass 3111 this.option( opts ); 3112 this._getIsInstant(); 3113 // filter, sort, and layout 3114 3115 // filter 3116 var filtered = this._filter( this.items ); 3117 this.filteredItems = filtered.matches; 3118 3119 this._bindArrangeComplete(); 3120 3121 if ( this._isInstant ) { 3122 this._noTransition( this._hideReveal, [ filtered ] ); 3123 } else { 3124 this._hideReveal( filtered ); 3125 } 3126 3127 this._sort(); 3128 this._layout(); 3129 }; 3130 // alias to _init for main plugin method 3131 proto._init = proto.arrange; 3132 3133 proto._hideReveal = function( filtered ) { 3134 this.reveal( filtered.needReveal ); 3135 this.hide( filtered.needHide ); 3136 }; 3137 3138 // HACK 3139 // Don't animate/transition first layout 3140 // Or don't animate/transition other layouts 3141 proto._getIsInstant = function() { 3142 var isLayoutInstant = this._getOption('layoutInstant'); 3143 var isInstant = isLayoutInstant !== undefined ? isLayoutInstant : 3144 !this._isLayoutInited; 3145 this._isInstant = isInstant; 3146 return isInstant; 3147 }; 3148 3149 // listen for layoutComplete, hideComplete and revealComplete 3150 // to trigger arrangeComplete 3151 proto._bindArrangeComplete = function() { 3152 // listen for 3 events to trigger arrangeComplete 3153 var isLayoutComplete, isHideComplete, isRevealComplete; 3154 var _this = this; 3155 function arrangeParallelCallback() { 3156 if ( isLayoutComplete && isHideComplete && isRevealComplete ) { 3157 _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] ); 3158 } 3159 } 3160 this.once( 'layoutComplete', function() { 3161 isLayoutComplete = true; 3162 arrangeParallelCallback(); 3163 }); 3164 this.once( 'hideComplete', function() { 3165 isHideComplete = true; 3166 arrangeParallelCallback(); 3167 }); 3168 this.once( 'revealComplete', function() { 3169 isRevealComplete = true; 3170 arrangeParallelCallback(); 3171 }); 3172 }; 3173 3174 // -------------------------- filter -------------------------- // 3175 3176 proto._filter = function( items ) { 3177 var filter = this.options.filter; 3178 filter = filter || '*'; 3179 var matches = []; 3180 var hiddenMatched = []; 3181 var visibleUnmatched = []; 3182 3183 var test = this._getFilterTest( filter ); 3184 3185 // test each item 3186 for ( var i=0; i < items.length; i++ ) { 3187 var item = items[i]; 3188 if ( item.isIgnored ) { 3189 continue; 3190 } 3191 // add item to either matched or unmatched group 3192 var isMatched = test( item ); 3193 // item.isFilterMatched = isMatched; 3194 // add to matches if its a match 3195 if ( isMatched ) { 3196 matches.push( item ); 3197 } 3198 // add to additional group if item needs to be hidden or revealed 3199 if ( isMatched && item.isHidden ) { 3200 hiddenMatched.push( item ); 3201 } else if ( !isMatched && !item.isHidden ) { 3202 visibleUnmatched.push( item ); 3203 } 3204 } 3205 3206 // return collections of items to be manipulated 3207 return { 3208 matches: matches, 3209 needReveal: hiddenMatched, 3210 needHide: visibleUnmatched 3211 }; 3212 }; 3213 3214 // get a jQuery, function, or a matchesSelector test given the filter 3215 proto._getFilterTest = function( filter ) { 3216 if ( jQuery && this.options.isJQueryFiltering ) { 3217 // use jQuery 3218 return function( item ) { 3219 return jQuery( item.element ).is( filter ); 3220 }; 3221 } 3222 if ( typeof filter == 'function' ) { 3223 // use filter as function 3224 return function( item ) { 3225 return filter( item.element ); 3226 }; 3227 } 3228 // default, use filter as selector string 3229 return function( item ) { 3230 return matchesSelector( item.element, filter ); 3231 }; 3232 }; 3233 3234 // -------------------------- sorting -------------------------- // 3235 3236 /** 3237 * @params {Array} elems 3238 * @public 3239 */ 3240 proto.updateSortData = function( elems ) { 3241 // get items 3242 var items; 3243 if ( elems ) { 3244 elems = utils.makeArray( elems ); 3245 items = this.getItems( elems ); 3246 } else { 3247 // update all items if no elems provided 3248 items = this.items; 3249 } 3250 3251 this._getSorters(); 3252 this._updateItemsSortData( items ); 3253 }; 3254 3255 proto._getSorters = function() { 3256 var getSortData = this.options.getSortData; 3257 for ( var key in getSortData ) { 3258 var sorter = getSortData[ key ]; 3259 this._sorters[ key ] = mungeSorter( sorter ); 3260 } 3261 }; 3262 3263 /** 3264 * @params {Array} items - of Isotope.Items 3265 * @private 3266 */ 3267 proto._updateItemsSortData = function( items ) { 3268 // do not update if no items 3269 var len = items && items.length; 3270 3271 for ( var i=0; len && i < len; i++ ) { 3272 var item = items[i]; 3273 item.updateSortData(); 3274 } 3275 }; 3276 3277 // ----- munge sorter ----- // 3278 3279 // encapsulate this, as we just need mungeSorter 3280 // other functions in here are just for munging 3281 var mungeSorter = ( function() { 3282 // add a magic layer to sorters for convienent shorthands 3283 // `.foo-bar` will use the text of .foo-bar querySelector 3284 // `[foo-bar]` will use attribute 3285 // you can also add parser 3286 // `.foo-bar parseInt` will parse that as a number 3287 function mungeSorter( sorter ) { 3288 // if not a string, return function or whatever it is 3289 if ( typeof sorter != 'string' ) { 3290 return sorter; 3291 } 3292 // parse the sorter string 3293 var args = trim( sorter ).split(' '); 3294 var query = args[0]; 3295 // check if query looks like [an-attribute] 3296 var attrMatch = query.match( /^\[(.+)\]$/ ); 3297 var attr = attrMatch && attrMatch[1]; 3298 var getValue = getValueGetter( attr, query ); 3299 // use second argument as a parser 3300 var parser = Isotope.sortDataParsers[ args[1] ]; 3301 // parse the value, if there was a parser 3302 sorter = parser ? function( elem ) { 3303 return elem && parser( getValue( elem ) ); 3304 } : 3305 // otherwise just return value 3306 function( elem ) { 3307 return elem && getValue( elem ); 3308 }; 3309 3310 return sorter; 3311 } 3312 3313 // get an attribute getter, or get text of the querySelector 3314 function getValueGetter( attr, query ) { 3315 // if query looks like [foo-bar], get attribute 3316 if ( attr ) { 3317 return function getAttribute( elem ) { 3318 return elem.getAttribute( attr ); 3319 }; 3320 } 3321 3322 // otherwise, assume its a querySelector, and get its text 3323 return function getChildText( elem ) { 3324 var child = elem.querySelector( query ); 3325 return child && child.textContent; 3326 }; 3327 } 3328 3329 return mungeSorter; 3330 })(); 3331 3332 // parsers used in getSortData shortcut strings 3333 Isotope.sortDataParsers = { 3334 'parseInt': function( val ) { 3335 return parseInt( val, 10 ); 3336 }, 3337 'parseFloat': function( val ) { 3338 return parseFloat( val ); 3339 } 3340 }; 3341 3342 // ----- sort method ----- // 3343 3344 // sort filteredItem order 3345 proto._sort = function() { 3346 if ( !this.options.sortBy ) { 3347 return; 3348 } 3349 // keep track of sortBy History 3350 var sortBys = utils.makeArray( this.options.sortBy ); 3351 if ( !this._getIsSameSortBy( sortBys ) ) { 3352 // concat all sortBy and sortHistory, add to front, oldest goes in last 3353 this.sortHistory = sortBys.concat( this.sortHistory ); 3354 } 3355 // sort magic 3356 var itemSorter = getItemSorter( this.sortHistory, this.options.sortAscending ); 3357 this.filteredItems.sort( itemSorter ); 3358 }; 3359 3360 // check if sortBys is same as start of sortHistory 3361 proto._getIsSameSortBy = function( sortBys ) { 3362 for ( var i=0; i < sortBys.length; i++ ) { 3363 if ( sortBys[i] != this.sortHistory[i] ) { 3364 return false; 3365 } 3366 } 3367 return true; 3368 }; 3369 3370 // returns a function used for sorting 3371 function getItemSorter( sortBys, sortAsc ) { 3372 return function sorter( itemA, itemB ) { 3373 // cycle through all sortKeys 3374 for ( var i = 0; i < sortBys.length; i++ ) { 3375 var sortBy = sortBys[i]; 3376 var a = itemA.sortData[ sortBy ]; 3377 var b = itemB.sortData[ sortBy ]; 3378 if ( a > b || a < b ) { 3379 // if sortAsc is an object, use the value given the sortBy key 3380 var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc; 3381 var direction = isAscending ? 1 : -1; 3382 return ( a > b ? 1 : -1 ) * direction; 3383 } 3384 } 3385 return 0; 3386 }; 3387 } 3388 3389 // -------------------------- methods -------------------------- // 3390 3391 // get layout mode 3392 proto._mode = function() { 3393 var layoutMode = this.options.layoutMode; 3394 var mode = this.modes[ layoutMode ]; 3395 if ( !mode ) { 3396 // TODO console.error 3397 throw new Error( 'No layout mode: ' + layoutMode ); 3398 } 3399 // HACK sync mode's options 3400 // any options set after init for layout mode need to be synced 3401 mode.options = this.options[ layoutMode ]; 3402 return mode; 3403 }; 3404 3405 proto._resetLayout = function() { 3406 // trigger original reset layout 3407 Outlayer.prototype._resetLayout.call( this ); 3408 this._mode()._resetLayout(); 3409 }; 3410 3411 proto._getItemLayoutPosition = function( item ) { 3412 return this._mode()._getItemLayoutPosition( item ); 3413 }; 3414 3415 proto._manageStamp = function( stamp ) { 3416 this._mode()._manageStamp( stamp ); 3417 }; 3418 3419 proto._getContainerSize = function() { 3420 return this._mode()._getContainerSize(); 3421 }; 3422 3423 proto.needsResizeLayout = function() { 3424 return this._mode().needsResizeLayout(); 3425 }; 3426 3427 // -------------------------- adding & removing -------------------------- // 3428 3429 // HEADS UP overwrites default Outlayer appended 3430 proto.appended = function( elems ) { 3431 var items = this.addItems( elems ); 3432 if ( !items.length ) { 3433 return; 3434 } 3435 // filter, layout, reveal new items 3436 var filteredItems = this._filterRevealAdded( items ); 3437 // add to filteredItems 3438 this.filteredItems = this.filteredItems.concat( filteredItems ); 3439 }; 3440 3441 // HEADS UP overwrites default Outlayer prepended 3442 proto.prepended = function( elems ) { 3443 var items = this._itemize( elems ); 3444 if ( !items.length ) { 3445 return; 3446 } 3447 // start new layout 3448 this._resetLayout(); 3449 this._manageStamps(); 3450 // filter, layout, reveal new items 3451 var filteredItems = this._filterRevealAdded( items ); 3452 // layout previous items 3453 this.layoutItems( this.filteredItems ); 3454 // add to items and filteredItems 3455 this.filteredItems = filteredItems.concat( this.filteredItems ); 3456 this.items = items.concat( this.items ); 3457 }; 3458 3459 proto._filterRevealAdded = function( items ) { 3460 var filtered = this._filter( items ); 3461 this.hide( filtered.needHide ); 3462 // reveal all new items 3463 this.reveal( filtered.matches ); 3464 // layout new items, no transition 3465 this.layoutItems( filtered.matches, true ); 3466 return filtered.matches; 3467 }; 3468 3469 /** 3470 * Filter, sort, and layout newly-appended item elements 3471 * @param {Array or NodeList or Element} elems 3472 */ 3473 proto.insert = function( elems ) { 3474 var items = this.addItems( elems ); 3475 if ( !items.length ) { 3476 return; 3477 } 3478 // append item elements 3479 var i, item; 3480 var len = items.length; 3481 for ( i=0; i < len; i++ ) { 3482 item = items[i]; 3483 this.element.appendChild( item.element ); 3484 } 3485 // filter new stuff 3486 var filteredInsertItems = this._filter( items ).matches; 3487 // set flag 3488 for ( i=0; i < len; i++ ) { 3489 items[i].isLayoutInstant = true; 3490 } 3491 this.arrange(); 3492 // reset flag 3493 for ( i=0; i < len; i++ ) { 3494 delete items[i].isLayoutInstant; 3495 } 3496 this.reveal( filteredInsertItems ); 3497 }; 3498 3499 var _remove = proto.remove; 3500 proto.remove = function( elems ) { 3501 elems = utils.makeArray( elems ); 3502 var removeItems = this.getItems( elems ); 3503 // do regular thing 3504 _remove.call( this, elems ); 3505 // bail if no items to remove 3506 var len = removeItems && removeItems.length; 3507 // remove elems from filteredItems 3508 for ( var i=0; len && i < len; i++ ) { 3509 var item = removeItems[i]; 3510 // remove item from collection 3511 utils.removeFrom( this.filteredItems, item ); 3512 } 3513 }; 3514 3515 proto.shuffle = function() { 3516 // update random sortData 3517 for ( var i=0; i < this.items.length; i++ ) { 3518 var item = this.items[i]; 3519 item.sortData.random = Math.random(); 3520 } 3521 this.options.sortBy = 'random'; 3522 this._sort(); 3523 this._layout(); 3524 }; 3525 3526 /** 3527 * trigger fn without transition 3528 * kind of hacky to have this in the first place 3529 * @param {Function} fn 3530 * @param {Array} args 3531 * @returns ret 3532 * @private 3533 */ 3534 proto._noTransition = function( fn, args ) { 3535 // save transitionDuration before disabling 3536 var transitionDuration = this.options.transitionDuration; 3537 // disable transition 3538 this.options.transitionDuration = 0; 3539 // do it 3540 var returnValue = fn.apply( this, args ); 3541 // re-enable transition for reveal 3542 this.options.transitionDuration = transitionDuration; 3543 return returnValue; 3544 }; 3545 3546 // ----- helper methods ----- // 3547 3548 /** 3549 * getter method for getting filtered item elements 3550 * @returns {Array} elems - collection of item elements 3551 */ 3552 proto.getFilteredItemElements = function() { 3553 return this.filteredItems.map( function( item ) { 3554 return item.element; 3555 }); 3556 }; 3557 3558 // ----- ----- // 3559 3560 return Isotope; 3561 3562}));
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.