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