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