1/** 2 * Isotope v1.5.20 3 */ 4 5 6(function( window, $, undefined ){ 7 8 'use strict'; 9 10 // get global vars 11 var document = window.document; 12 var Modernizr = window.Modernizr; 13 14 // helper function 15 var capitalize = function( str ) { 16 return str.charAt(0).toUpperCase() + str.slice(1); 17 }; 18 19 // ========================= getStyleProperty by kangax =============================== 20 // http://perfectionkills.com/feature-testing-css-properties/ 21 22 var prefixes = 'Moz Webkit O Ms'.split(' '); 23 24 var getStyleProperty = function( propName ) { 25 var style = document.documentElement.style, 26 prefixed; 27 28 // test standard property first 29 if ( typeof style[propName] === 'string' ) { 30 return propName; 31 } 32 33 // capitalize 34 propName = capitalize( propName ); 35 36 // test vendor specific properties 37 for ( var i=0, len = prefixes.length; i < len; i++ ) { 38 prefixed = prefixes[i] + propName; 39 if ( typeof style[ prefixed ] === 'string' ) { 40 return prefixed; 41 } 42 } 43 }; 44 45 var transformProp = getStyleProperty('transform'), 46 transitionProp = getStyleProperty('transitionProperty'); 47 48 49 50 if ( Modernizr.csstransforms ) { 51 52 // i.e. transformFnNotations.scale(0.5) >> 'scale3d( 0.5, 0.5, 1)' 53 var transformFnNotations = Modernizr.csstransforms3d ? 54 { // 3D transform functions 55 translate : function ( position ) { 56 return 'translate3d(' + position[0] + 'px, ' + position[1] + 'px, 0) '; 57 }, 58 scale : function ( scale ) { 59 return 'scale3d(' + scale + ', ' + scale + ', 1) '; 60 } 61 } : 62 { // 2D transform functions 63 translate : function ( position ) { 64 return 'translate(' + position[0] + 'px, ' + position[1] + 'px) '; 65 }, 66 scale : function ( scale ) { 67 return 'scale(' + scale + ') '; 68 } 69 } 70 ; 71 72 var setIsoTransform = function ( elem, name, value ) { 73 // unpack current transform data 74 var data = $.data( elem, 'isoTransform' ) || {}, 75 newData = {}, 76 fnName, 77 transformObj = {}, 78 transformValue; 79 80 // i.e. newData.scale = 0.5 81 newData[ name ] = value; 82 // extend new value over current data 83 $.extend( data, newData ); 84 85 for ( fnName in data ) { 86 transformValue = data[ fnName ]; 87 transformObj[ fnName ] = transformFnNotations[ fnName ]( transformValue ); 88 } 89 90 // get proper order 91 // ideally, we could loop through this give an array, but since we only have 92 // a couple transforms we're keeping track of, we'll do it like so 93 var translateFn = transformObj.translate || '', 94 scaleFn = transformObj.scale || '', 95 // sorting so translate always comes first 96 valueFns = translateFn + scaleFn; 97 98 // set data back in elem 99 $.data( elem, 'isoTransform', data ); 100 101 // set name to vendor specific property 102 elem.style[ transformProp ] = valueFns; 103 }; 104 105 // ==================== scale =================== 106 107 $.cssNumber.scale = true; 108 109 $.cssHooks.scale = { 110 set: function( elem, value ) { 111 // uncomment this bit if you want to properly parse strings 112 // if ( typeof value === 'string' ) { 113 // value = parseFloat( value ); 114 // } 115 setIsoTransform( elem, 'scale', value ); 116 }, 117 get: function( elem, computed ) { 118 var transform = $.data( elem, 'isoTransform' ); 119 return transform && transform.scale ? transform.scale : 1; 120 } 121 }; 122 123 $.fx.step.scale = function( fx ) { 124 $.cssHooks.scale.set( fx.elem, fx.now+fx.unit ); 125 }; 126 127 128 // ==================== translate =================== 129 130 $.cssNumber.translate = true; 131 132 $.cssHooks.translate = { 133 set: function( elem, value ) { 134 135 // uncomment this bit if you want to properly parse strings 136 // if ( typeof value === 'string' ) { 137 // value = value.split(' '); 138 // } 139 // 140 // var i, val; 141 // for ( i = 0; i < 2; i++ ) { 142 // val = value[i]; 143 // if ( typeof val === 'string' ) { 144 // val = parseInt( val ); 145 // } 146 // } 147 148 setIsoTransform( elem, 'translate', value ); 149 }, 150 151 get: function( elem, computed ) { 152 var transform = $.data( elem, 'isoTransform' ); 153 return transform && transform.translate ? transform.translate : [ 0, 0 ]; 154 } 155 }; 156 157 } 158 159 // ========================= get transition-end event =============================== 160 var transitionEndEvent, transitionDurProp; 161 162 if ( Modernizr.csstransitions ) { 163 transitionEndEvent = { 164 WebkitTransitionProperty: 'webkitTransitionEnd', // webkit 165 MozTransitionProperty: 'transitionend', 166 OTransitionProperty: 'oTransitionEnd otransitionend', 167 transitionProperty: 'transitionend' 168 }[ transitionProp ]; 169 170 transitionDurProp = getStyleProperty('transitionDuration'); 171 } 172 173 // ========================= smartresize =============================== 174 175 /* 176 * smartresize: debounced resize event for jQuery 177 * 178 * latest version and complete README available on Github: 179 * https://github.com/louisremi/jquery.smartresize.js 180 * 181 * Copyright 2011 @louis_remi 182 * Licensed under the MIT license. 183 */ 184 185 var $event = $.event, 186 resizeTimeout; 187 188 $event.special.smartresize = { 189 setup: function() { 190 $(this).bind( "resize", $event.special.smartresize.handler ); 191 }, 192 teardown: function() { 193 $(this).unbind( "resize", $event.special.smartresize.handler ); 194 }, 195 handler: function( event, execAsap ) { 196 // Save the context 197 var context = this, 198 args = arguments; 199 200 // set correct event type 201 event.type = "smartresize"; 202 203 if ( resizeTimeout ) { clearTimeout( resizeTimeout ); } 204 resizeTimeout = setTimeout(function() { 205 jQuery.event.handle.apply( context, args ); 206 }, execAsap === "execAsap"? 0 : 100 ); 207 } 208 }; 209 210 $.fn.smartresize = function( fn ) { 211 return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] ); 212 }; 213 214 215 216// ========================= Isotope =============================== 217 218
219 // our "Widget" object constructor 220 $.Isotope = function( options, element, callback ){ 221 this.element = $( element ); 222 223 this._create( options ); 224 this._init( callback ); 225 }; 226 227 // styles of container element we want to keep track of 228 var isoContainerStyles = [ 'width', 'height' ]; 229 230 var $window = $(window); 231 232 $.Isotope.settings = { 233 resizable: true, 234 layoutMode : 'masonry', 235 containerClass : 'isotope', 236 itemClass : 'isotope-item', 237 hiddenClass : 'isotope-hidden', 238 hiddenStyle: { opacity: 0, scale: 0.001 }, 239 visibleStyle: { opacity: 1, scale: 1 }, 240 containerStyle: { 241 position: 'relative', 242 overflow: 'hidden' 243 }, 244 animationEngine: 'best-available', 245 animationOptions: { 246 queue: false, 247 duration: 800 248 }, 249 sortBy : 'original-order', 250 sortAscending : true, 251 resizesContainer : true, 252 transformsEnabled: true, 253 itemPositionDataEnabled: false 254 }; 255 256 $.Isotope.prototype = { 257 258 // sets up widget 259 _create : function( options ) { 260 261 this.options = $.extend( {}, $.Isotope.settings, options ); 262 263 this.styleQueue = []; 264 this.elemCount = 0; 265 266 // get original styles in case we re-apply them in .destroy() 267 var elemStyle = this.element[0].style; 268 this.originalStyle = {}; 269 // keep track of container styles 270 var containerStyles = isoContainerStyles.slice(0); 271 for ( var prop in this.options.containerStyle ) { 272 containerStyles.push( prop ); 273 } 274 for ( var i=0, len = containerStyles.length; i < len; i++ ) { 275 prop = containerStyles[i]; 276 this.originalStyle[ prop ] = elemStyle[ prop ] || ''; 277 } 278 // apply container style from options 279 this.element.css( this.options.containerStyle ); 280 281 this._updateAnimationEngine(); 282 this._updateUsingTransforms(); 283 284 // sorting 285 var originalOrderSorter = { 286 'original-order' : function( $elem, instance ) { 287 instance.elemCount ++; 288 return instance.elemCount; 289 }, 290 random : function() { 291 return Math.random(); 292 } 293 }; 294 295 this.options.getSortData = $.extend( this.options.getSortData, originalOrderSorter ); 296 297 // need to get atoms 298 this.reloadItems(); 299 300 // get top left position of where the bricks should be 301 this.offset = { 302 left: parseInt( ( this.element.css('padding-left') || 0 ), 10 ), 303 top: parseInt( ( this.element.css('padding-top') || 0 ), 10 ) 304 }; 305 306 // add isotope class first time around 307 var instance = this; 308 setTimeout( function() { 309 instance.element.addClass( instance.options.containerClass ); 310 }, 0 ); 311 312 // bind resize method 313 if ( this.options.resizable ) { 314 $window.bind( 'smartresize.isotope', function() { 315 instance.resize(); 316 }); 317 } 318 319 // dismiss all click events from hidden events 320 this.element.delegate( '.' + this.options.hiddenClass, 'click', function(){ 321 return false; 322 }); 323 324 }, 325 326 _getAtoms : function( $elems ) { 327 var selector = this.options.itemSelector, 328 // filter & find 329 $atoms = selector ? $elems.filter( selector ).add( $elems.find( selector ) ) : $elems, 330 // base style for atoms 331 atomStyle = { position: 'absolute' }; 332 333 if ( this.usingTransforms ) { 334 atomStyle.left = 0; 335 atomStyle.top = 0; 336 } 337 338 $atoms.css( atomStyle ).addClass( this.options.itemClass ); 339 340 this.updateSortData( $atoms, true ); 341 342 return $atoms; 343 }, 344 345 // _init fires when your instance is first created 346 // (from the constructor above), and when you 347 // attempt to initialize the widget again (by the bridge) 348 // after it has already been initialized. 349 _init : function( callback ) { 350 351 this.$filteredAtoms = this._filter( this.$allAtoms ); 352 this._sort(); 353 this.reLayout( callback ); 354 355 }, 356 357 option : function( opts ){ 358 // change options AFTER initialization: 359 // signature: $('#foo').bar({ cool:false }); 360 if ( $.isPlainObject( opts ) ){ 361 this.options = $.extend( true, this.options, opts ); 362 363 // trigger _updateOptionName if it exists 364 var updateOptionFn; 365 for ( var optionName in opts ) { 366 updateOptionFn = '_update' + capitalize( optionName ); 367 if ( this[ updateOptionFn ] ) { 368 this[ updateOptionFn ](); 369 } 370 } 371 } 372 }, 373 374 // ====================== updaters ====================== // 375 // kind of like setters 376 377 _updateAnimationEngine : function() { 378 var animationEngine = this.options.animationEngine.toLowerCase().replace( /[ _\-]/g, ''); 379 var isUsingJQueryAnimation; 380 // set applyStyleFnName 381 switch ( animationEngine ) { 382 case 'css' : 383 case 'none' : 384 isUsingJQueryAnimation = false;
385 break; 386 case 'jquery' : 387 isUsingJQueryAnimation = true; 388 break; 389 default : // best available 390 isUsingJQueryAnimation = !Modernizr.csstransitions; 391 } 392 this.isUsingJQueryAnimation = isUsingJQueryAnimation; 393 this._updateUsingTransforms(); 394 }, 395 396 _updateTransformsEnabled : function() { 397 this._updateUsingTransforms(); 398 }, 399 400 _updateUsingTransforms : function() { 401 var usingTransforms = this.usingTransforms = this.options.transformsEnabled && 402 Modernizr.csstransforms && Modernizr.csstransitions && !this.isUsingJQueryAnimation; 403 404 // prevent scales when transforms are disabled 405 if ( !usingTransforms ) { 406 delete this.options.hiddenStyle.scale; 407 delete this.options.visibleStyle.scale; 408 } 409 410 this.getPositionStyles = usingTransforms ? this._translate : this._positionAbs; 411 }, 412 413 414 // ====================== Filtering ====================== 415 416 _filter : function( $atoms ) { 417 var filter = this.options.filter === '' ? '*' : this.options.filter; 418 419 if ( !filter ) { 420 return $atoms; 421 } 422 423 var hiddenClass = this.options.hiddenClass, 424 hiddenSelector = '.' + hiddenClass, 425 $hiddenAtoms = $atoms.filter( hiddenSelector ), 426 $atomsToShow = $hiddenAtoms; 427 428 if ( filter !== '*' ) { 429 $atomsToShow = $hiddenAtoms.filter( filter ); 430 var $atomsToHide = $atoms.not( hiddenSelector ).not( filter ).addClass( hiddenClass ); 431 this.styleQueue.push({ $el: $atomsToHide, style: this.options.hiddenStyle }); 432 } 433 434 this.styleQueue.push({ $el: $atomsToShow, style: this.options.visibleStyle }); 435 $atomsToShow.removeClass( hiddenClass ); 436 437 return $atoms.filter( filter ); 438 }, 439 440 // ====================== Sorting ====================== 441 442 updateSortData : function( $atoms, isIncrementingElemCount ) { 443 var instance = this, 444 getSortData = this.options.getSortData, 445 $this, sortData; 446 $atoms.each(function(){ 447 $this = $(this); 448 sortData = {}; 449 // get value for sort data based on fn( $elem ) passed in 450 for ( var key in getSortData ) { 451 if ( !isIncrementingElemCount && key === 'original-order' ) { 452 // keep original order original 453 sortData[ key ] = $.data( this, 'isotope-sort-data' )[ key ]; 454 } else { 455 sortData[ key ] = getSortData[ key ]( $this, instance ); 456 } 457 } 458 // apply sort data to element 459 $.data( this, 'isotope-sort-data', sortData ); 460 }); 461 }, 462 463 // used on all the filtered atoms 464 _sort : function() { 465 466 var sortBy = this.options.sortBy, 467 getSorter = this._getSorter, 468 sortDir = this.options.sortAscending ? 1 : -1, 469 sortFn = function( alpha, beta ) { 470 var a = getSorter( alpha, sortBy ), 471 b = getSorter( beta, sortBy ); 472 // fall back to original order if data matches 473 if ( a === b && sortBy !== 'original-order') { 474 a = getSorter( alpha, 'original-order' ); 475 b = getSorter( beta, 'original-order' ); 476 } 477 return ( ( a > b ) ? 1 : ( a < b ) ? -1 : 0 ) * sortDir; 478 }; 479 480 this.$filteredAtoms.sort( sortFn ); 481 }, 482 483 _getSorter : function( elem, sortBy ) { 484 return $.data( elem, 'isotope-sort-data' )[ sortBy ]; 485 }, 486 487 // ====================== Layout Helpers ====================== 488 489 _translate : function( x, y ) { 490 return { translate : [ x, y ] }; 491 }, 492 493 _positionAbs : function( x, y ) { 494 return { left: x, top: y }; 495 }, 496 497 _pushPosition : function( $elem, x, y ) { 498 x = Math.round( x + this.offset.left ); 499 y = Math.round( y + this.offset.top ); 500 var position = this.getPositionStyles( x, y ); 501 this.styleQueue.push({ $el: $elem, style: position }); 502 if ( this.options.itemPositionDataEnabled ) { 503 $elem.data('isotope-item-position', {x: x, y: y} ); 504 } 505 }, 506 507 508 // ====================== General Layout ====================== 509 510 // used on collection of atoms (should be filtered, and sorted before ) 511 // accepts atoms-to-be-laid-out to start with 512 layout : function( $elems, callback ) { 513 514 var layoutMode = this.options.layoutMode; 515 516 // layout logic 517 this[ '_' + layoutMode + 'Layout' ]( $elems ); 518 519 // set the size of the container 520 if ( this.options.resizesContainer ) { 521 var containerStyle = this[ '_' + layoutMode + 'GetContainerSize' ](); 522 this.styleQueue.push({ $el: this.element, style: containerStyle }); 523 } 524 525 this._processStyleQueue( $elems, callback ); 526 527 this.isLaidOut = true; 528 }, 529 530 _processStyleQueue : function( $elems, callback ) { 531 // are we animating the layout arrangement? 532 // use plugin-ish syntax for css or animate 533 var styleFn = !this.isLaidOut ? 'css' : ( 534 this.isUsingJQueryAnimation ? 'animate' : 'css' 535 ), 536 animOpts = this.options.animationOptions, 537 onLayout = this.options.onLayout, 538 objStyleFn, processor, 539 triggerCallbackNow, callbackFn; 540
541 // default styleQueue processor, may be overwritten down below 542 processor = function( i, obj ) { 543 obj.$el[ styleFn ]( obj.style, animOpts ); 544 }; 545 546 if ( this._isInserting && this.isUsingJQueryAnimation ) { 547 // if using styleQueue to insert items 548 processor = function( i, obj ) { 549 // only animate if it not being inserted 550 objStyleFn = obj.$el.hasClass('no-transition') ? 'css' : styleFn; 551 obj.$el[ objStyleFn ]( obj.style, animOpts ); 552 }; 553 554 } else if ( callback || onLayout || animOpts.complete ) { 555 // has callback 556 var isCallbackTriggered = false, 557 // array of possible callbacks to trigger 558 callbacks = [ callback, onLayout, animOpts.complete ], 559 instance = this; 560 triggerCallbackNow = true; 561 // trigger callback only once 562 callbackFn = function() { 563 if ( isCallbackTriggered ) { 564 return; 565 } 566 var hollaback; 567 for (var i=0, len = callbacks.length; i < len; i++) { 568 hollaback = callbacks[i]; 569 if ( typeof hollaback === 'function' ) { 570 hollaback.call( instance.element, $elems, instance ); 571 } 572 } 573 isCallbackTriggered = true; 574 }; 575 576 if ( this.isUsingJQueryAnimation && styleFn === 'animate' ) { 577 // add callback to animation options 578 animOpts.complete = callbackFn; 579 triggerCallbackNow = false; 580 581 } else if ( Modernizr.csstransitions ) { 582 // detect if first item has transition 583 var i = 0, 584 firstItem = this.styleQueue[0], 585 testElem = firstItem && firstItem.$el, 586 styleObj; 587 // get first non-empty jQ object 588 while ( !testElem || !testElem.length ) { 589 styleObj = this.styleQueue[ i++ ]; 590 // HACK: sometimes styleQueue[i] is undefined 591 if ( !styleObj ) { 592 return; 593 } 594 testElem = styleObj.$el; 595 } 596 // get transition duration of the first element in that object 597 // yeah, this is inexact 598 var duration = parseFloat( getComputedStyle( testElem[0] )[ transitionDurProp ] ); 599 if ( duration > 0 ) { 600 processor = function( i, obj ) { 601 obj.$el[ styleFn ]( obj.style, animOpts ) 602 // trigger callback at transition end 603 .one( transitionEndEvent, callbackFn ); 604 }; 605 triggerCallbackNow = false; 606 } 607 } 608 } 609 610 // process styleQueue 611 $.each( this.styleQueue, processor ); 612 613 if ( triggerCallbackNow ) { 614 callbackFn(); 615 } 616 617 // clear out queue for next time 618 this.styleQueue = []; 619 }, 620 621 622 resize : function() { 623 if ( this[ '_' + this.options.layoutMode + 'ResizeChanged' ]() ) { 624 this.reLayout(); 625 } 626 }, 627 628 629 reLayout : function( callback ) { 630 631 this[ '_' + this.options.layoutMode + 'Reset' ](); 632 this.layout( this.$filteredAtoms, callback ); 633 634 }, 635 636 // ====================== Convenience methods ====================== 637 638 // ====================== Adding items ====================== 639 640 // adds a jQuery object of items to a isotope container 641 addItems : function( $content, callback ) { 642 var $newAtoms = this._getAtoms( $content ); 643 // add new atoms to atoms pools 644 this.$allAtoms = this.$allAtoms.add( $newAtoms ); 645 646 if ( callback ) { 647 callback( $newAtoms ); 648 } 649 }, 650 651 // convienence method for adding elements properly to any layout 652 // positions items, hides them, then animates them back in <--- very sezzy 653 insert : function( $content, callback ) { 654 // position items 655 this.element.append( $content ); 656 657 var instance = this; 658 this.addItems( $content, function( $newAtoms ) { 659 var $newFilteredAtoms = instance._filter( $newAtoms ); 660 instance._addHideAppended( $newFilteredAtoms ); 661 instance._sort(); 662 instance.reLayout(); 663 instance._revealAppended( $newFilteredAtoms, callback ); 664 }); 665 666 }, 667 668 // convienence method for working with Infinite Scroll 669 appended : function( $content, callback ) { 670 var instance = this; 671 this.addItems( $content, function( $newAtoms ) { 672 instance._addHideAppended( $newAtoms ); 673 instance.layout( $newAtoms ); 674 instance._revealAppended( $newAtoms, callback ); 675 }); 676 }, 677 678 // adds new atoms, then hides them before positioning 679 _addHideAppended : function( $newAtoms ) { 680 this.$filteredAtoms = this.$filteredAtoms.add( $newAtoms ); 681 $newAtoms.addClass('no-transition'); 682 683 this._isInserting = true; 684 685 // apply hidden styles 686 this.styleQueue.push({ $el: $newAtoms, style: this.options.hiddenStyle }); 687 }, 688 689 // sets visible style on new atoms 690 _revealAppended : function( $newAtoms, callback ) { 691 var instance = this; 692 // apply visible style after a sec 693 setTimeout( function() { 694 // enable animation 695 $newAtoms.removeClass('no-transition'); 696 // reveal newly inserted filtered elements 697 instance.styleQueue.push({ $el: $newAtoms, style: instance.options.visibleStyle }); 698 instance._isInserting = false;
699 instance._processStyleQueue( $newAtoms, callback ); 700 }, 10 ); 701 }, 702 703 // gathers all atoms 704 reloadItems : function() { 705 this.$allAtoms = this._getAtoms( this.element.children() ); 706 }, 707 708 // removes elements from Isotope widget 709 remove: function( $content, callback ) { 710 // remove elements from Isotope instance in callback 711 var instance = this; 712 // remove() as a callback, for after transition / animation 713 var removeContent = function() { 714 instance.$allAtoms = instance.$allAtoms.not( $content ); 715 $content.remove(); 716 if ( callback ) { 717 callback.call( instance.element ); 718 } 719 }; 720 721 if ( $content.filter( ':not(.' + this.options.hiddenClass + ')' ).length ) { 722 // if any non-hidden content needs to be removed 723 this.styleQueue.push({ $el: $content, style: this.options.hiddenStyle }); 724 this.$filteredAtoms = this.$filteredAtoms.not( $content ); 725 this._sort(); 726 this.reLayout( removeContent ); 727 } else { 728 // remove it now 729 removeContent(); 730 } 731 732 }, 733 734 shuffle : function( callback ) { 735 this.updateSortData( this.$allAtoms ); 736 this.options.sortBy = 'random'; 737 this._sort(); 738 this.reLayout( callback ); 739 }, 740 741 // destroys widget, returns elements and container back (close) to original style 742 destroy : function() { 743 744 var usingTransforms = this.usingTransforms; 745 var options = this.options; 746 747 this.$allAtoms 748 .removeClass( options.hiddenClass + ' ' + options.itemClass ) 749 .each(function(){ 750 var style = this.style; 751 style.position = ''; 752 style.top = ''; 753 style.left = ''; 754 style.opacity = ''; 755 if ( usingTransforms ) { 756 style[ transformProp ] = ''; 757 } 758 }); 759 760 // re-apply saved container styles 761 var elemStyle = this.element[0].style; 762 for ( var prop in this.originalStyle ) { 763 elemStyle[ prop ] = this.originalStyle[ prop ]; 764 } 765 766 this.element 767 .unbind('.isotope') 768 .undelegate( '.' + options.hiddenClass, 'click' ) 769 .removeClass( options.containerClass ) 770 .removeData('isotope'); 771 772 $window.unbind('.isotope'); 773 774 }, 775 776 777 // ====================== LAYOUTS ====================== 778 779 // calculates number of rows or columns 780 // requires columnWidth or rowHeight to be set on namespaced object 781 // i.e. this.masonry.columnWidth = 200 782 _getSegments : function( isRows ) { 783 var namespace = this.options.layoutMode, 784 measure = isRows ? 'rowHeight' : 'columnWidth', 785 size = isRows ? 'height' : 'width', 786 segmentsName = isRows ? 'rows' : 'cols', 787 containerSize = this.element[ size ](), 788 segments, 789 // i.e. options.masonry && options.masonry.columnWidth 790 segmentSize = this.options[ namespace ] && this.options[ namespace ][ measure ] || 791 // or use the size of the first item, i.e. outerWidth 792 this.$filteredAtoms[ 'outer' + capitalize(size) ](true) || 793 // if there's no items, use size of container 794 containerSize; 795 796 segments = Math.floor( containerSize / segmentSize ); 797 segments = Math.max( segments, 1 ); 798 799 // i.e. this.masonry.cols = .... 800 this[ namespace ][ segmentsName ] = segments; 801 // i.e. this.masonry.columnWidth = ... 802 this[ namespace ][ measure ] = segmentSize; 803 804 }, 805 806 _checkIfSegmentsChanged : function( isRows ) { 807 var namespace = this.options.layoutMode, 808 segmentsName = isRows ? 'rows' : 'cols', 809 prevSegments = this[ namespace ][ segmentsName ]; 810 // update cols/rows 811 this._getSegments( isRows ); 812 // return if updated cols/rows is not equal to previous 813 return ( this[ namespace ][ segmentsName ] !== prevSegments ); 814 }, 815 816 // ====================== Masonry ====================== 817 818 _masonryReset : function() { 819 // layout-specific props 820 this.masonry = {}; 821 // FIXME shouldn't have to call this again 822 this._getSegments(); 823 var i = this.masonry.cols; 824 this.masonry.colYs = []; 825 while (i--) { 826 this.masonry.colYs.push( 0 ); 827 } 828 }, 829 830 _masonryLayout : function( $elems ) { 831 var instance = this, 832 props = instance.masonry; 833 $elems.each(function(){ 834 var $this = $(this), 835 //how many columns does this brick span 836 colSpan = Math.ceil( $this.outerWidth(true) / props.columnWidth ); 837 colSpan = Math.min( colSpan, props.cols ); 838 839 if ( colSpan === 1 ) { 840 // if brick spans only one column, just like singleMode 841 instance._masonryPlaceBrick( $this, props.colYs ); 842 } else { 843 // brick spans more than one column 844 // how many different places could this brick fit horizontally 845 var groupCount = props.cols + 1 - colSpan, 846 groupY = [], 847 groupColY, 848 i; 849 850 // for each group potential horizontal position 851 for ( i=0; i < groupCount; i++ ) { 852 // make an array of colY values for that one group 853 groupColY = props.colYs.slice( i, i+colSpan ); 854 // and get the max value of the array 855 groupY[i] = Math.max.apply( Math, groupColY ); 856 } 857 858 instance._masonryPlaceBrick( $this, groupY ); 859 } 860 }); 861 }, 862 863 // worker method that places brick in the columnSet 864 // with the the minY 865 _masonryPlaceBrick : function( $brick, setY ) { 866 // get the minimum Y value from the columns 867 var minimumY = Math.min.apply( Math, setY ), 868 shortCol = 0; 869 870 // Find index of short column, the first from the left 871 for (var i=0, len = setY.length; i < len; i++) { 872 if ( setY[i] === minimumY ) { 873 shortCol = i; 874 break; 875 } 876 } 877 878 // position the brick 879 var x = this.masonry.columnWidth * shortCol, 880 y = minimumY; 881 this._pushPosition( $brick, x, y ); 882 883 // apply setHeight to necessary columns 884 var setHeight = minimumY + $brick.outerHeight(true), 885 setSpan = this.masonry.cols + 1 - len; 886 for ( i=0; i < setSpan; i++ ) { 887 this.masonry.colYs[ shortCol + i ] = setHeight; 888 } 889 890 }, 891 892 _masonryGetContainerSize : function() { 893 var containerHeight = Math.max.apply( Math, this.masonry.colYs ); 894 return { height: containerHeight }; 895 }, 896 897 _masonryResizeChanged : function() { 898 return this._checkIfSegmentsChanged(); 899 }, 900 901 // ====================== fitRows ====================== 902 903 _fitRowsReset : function() { 904 this.fitRows = { 905 x : 0, 906 y : 0, 907 height : 0 908 }; 909 }, 910 911 _fitRowsLayout : function( $elems ) { 912 var instance = this, 913 containerWidth = this.element.width(), 914 props = this.fitRows; 915 916 $elems.each( function() { 917 var $this = $(this), 918 atomW = $this.outerWidth(true), 919 atomH = $this.outerHeight(true); 920 921 if ( props.x !== 0 && atomW + props.x > containerWidth ) { 922 // if this element cannot fit in the current row 923 props.x = 0; 924 props.y = props.height; 925 } 926 927 // position the atom 928 instance._pushPosition( $this, props.x, props.y ); 929 930 props.height = Math.max( props.y + atomH, props.height ); 931 props.x += atomW; 932 933 }); 934 }, 935 936 _fitRowsGetContainerSize : function () { 937 return { height : this.fitRows.height }; 938 }, 939 940 _fitRowsResizeChanged : function() { 941 return true; 942 }, 943 944 945 // ====================== cellsByRow ====================== 946 947 _cellsByRowReset : function() { 948 this.cellsByRow = { 949 index : 0 950 }; 951 // get this.cellsByRow.columnWidth 952 this._getSegments(); 953 // get this.cellsByRow.rowHeight 954 this._getSegments(true); 955 }, 956 957 _cellsByRowLayout : function( $elems ) { 958 var instance = this, 959 props = this.cellsByRow; 960 $elems.each( function(){ 961 var $this = $(this), 962 col = props.index % props.cols, 963 row = Math.floor( props.index / props.cols ), 964 x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2, 965 y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2; 966 instance._pushPosition( $this, x, y ); 967 props.index ++; 968 }); 969 }, 970 971 _cellsByRowGetContainerSize : function() {
972 return { height : Math.ceil( this.$filteredAtoms.length / this.cellsByRow.cols ) * this.cellsByRow.rowHeight + this.offset.top }; 973 }, 974 975 _cellsByRowResizeChanged : function() { 976 return this._checkIfSegmentsChanged(); 977 }, 978 979 980 // ====================== straightDown ====================== 981 982 _straightDownReset : function() { 983 this.straightDown = { 984 y : 0 985 }; 986 }, 987 988 _straightDownLayout : function( $elems ) { 989 var instance = this; 990 $elems.each( function( i ){ 991 var $this = $(this); 992 instance._pushPosition( $this, 0, instance.straightDown.y ); 993 instance.straightDown.y += $this.outerHeight(true); 994 }); 995 }, 996 997 _straightDownGetContainerSize : function() { 998 return { height : this.straightDown.y }; 999 }, 1000 1001 _straightDownResizeChanged : function() { 1002 return true; 1003 }, 1004 1005 1006 // ====================== masonryHorizontal ====================== 1007 1008 _masonryHorizontalReset : function() { 1009 // layout-specific props 1010 this.masonryHorizontal = {}; 1011 // FIXME shouldn't have to call this again 1012 this._getSegments( true ); 1013 var i = this.masonryHorizontal.rows; 1014 this.masonryHorizontal.rowXs = []; 1015 while (i--) { 1016 this.masonryHorizontal.rowXs.push( 0 ); 1017 } 1018 }, 1019 1020 _masonryHorizontalLayout : function( $elems ) { 1021 var instance = this, 1022 props = instance.masonryHorizontal; 1023 $elems.each(function(){ 1024 var $this = $(this), 1025 //how many rows does this brick span 1026 rowSpan = Math.ceil( $this.outerHeight(true) / props.rowHeight ); 1027 rowSpan = Math.min( rowSpan, props.rows ); 1028 1029 if ( rowSpan === 1 ) { 1030 // if brick spans only one column, just like singleMode 1031 instance._masonryHorizontalPlaceBrick( $this, props.rowXs ); 1032 } else { 1033 // brick spans more than one row 1034 // how many different places could this brick fit horizontally 1035 var groupCount = props.rows + 1 - rowSpan, 1036 groupX = [], 1037 groupRowX, i; 1038 1039 // for each group potential horizontal position 1040 for ( i=0; i < groupCount; i++ ) { 1041 // make an array of colY values for that one group 1042 groupRowX = props.rowXs.slice( i, i+rowSpan ); 1043 // and get the max value of the array 1044 groupX[i] = Math.max.apply( Math, groupRowX ); 1045 } 1046 1047 instance._masonryHorizontalPlaceBrick( $this, groupX ); 1048 } 1049 }); 1050 }, 1051 1052 _masonryHorizontalPlaceBrick : function( $brick, setX ) { 1053 // get the minimum Y value from the columns 1054 var minimumX = Math.min.apply( Math, setX ), 1055 smallRow = 0; 1056 // Find index of smallest row, the first from the top 1057 for (var i=0, len = setX.length; i < len; i++) { 1058 if ( setX[i] === minimumX ) { 1059 smallRow = i; 1060 break; 1061 } 1062 } 1063 1064 // position the brick 1065 var x = minimumX, 1066 y = this.masonryHorizontal.rowHeight * smallRow; 1067 this._pushPosition( $brick, x, y ); 1068 1069 // apply setHeight to necessary columns 1070 var setWidth = minimumX + $brick.outerWidth(true), 1071 setSpan = this.masonryHorizontal.rows + 1 - len; 1072 for ( i=0; i < setSpan; i++ ) { 1073 this.masonryHorizontal.rowXs[ smallRow + i ] = setWidth; 1074 } 1075 }, 1076 1077 _masonryHorizontalGetContainerSize : function() { 1078 var containerWidth = Math.max.apply( Math, this.masonryHorizontal.rowXs ); 1079 return { width: containerWidth }; 1080 }, 1081 1082 _masonryHorizontalResizeChanged : function() { 1083 return this._checkIfSegmentsChanged(true); 1084 }, 1085 1086 1087 // ====================== fitColumns ====================== 1088 1089 _fitColumnsReset : function() { 1090 this.fitColumns = { 1091 x : 0, 1092 y : 0, 1093 width : 0 1094 }; 1095 }, 1096 1097 _fitColumnsLayout : function( $elems ) { 1098 var instance = this, 1099 containerHeight = this.element.height(), 1100 props = this.fitColumns; 1101 $elems.each( function() { 1102 var $this = $(this), 1103 atomW = $this.outerWidth(true), 1104 atomH = $this.outerHeight(true); 1105 1106 if ( props.y !== 0 && atomH + props.y > containerHeight ) { 1107 // if this element cannot fit in the current column 1108 props.x = props.width; 1109 props.y = 0; 1110 } 1111 1112 // position the atom 1113 instance._pushPosition( $this, props.x, props.y ); 1114 1115 props.width = Math.max( props.x + atomW, props.width ); 1116 props.y += atomH; 1117 1118 }); 1119 }, 1120 1121 _fitColumnsGetContainerSize : function () { 1122 return { width : this.fitColumns.width }; 1123 }, 1124 1125 _fitColumnsResizeChanged : function() { 1126 return true; 1127 }, 1128 1129 1130 1131 // ====================== cellsByColumn ====================== 1132 1133 _cellsByColumnReset : function() { 1134 this.cellsByColumn = { 1135 index : 0 1136 }; 1137 // get this.cellsByColumn.columnWidth 1138 this._getSegments(); 1139 // get this.cellsByColumn.rowHeight 1140 this._getSegments(true); 1141 }, 1142 1143 _cellsByColumnLayout : function( $elems ) { 1144 var instance = this, 1145 props = this.cellsByColumn; 1146 $elems.each( function(){ 1147 var $this = $(this), 1148 col = Math.floor( props.index / props.rows ), 1149 row = props.index % props.rows, 1150 x = ( col + 0.5 ) * props.columnWidth - $this.outerWidth(true) / 2, 1151 y = ( row + 0.5 ) * props.rowHeight - $this.outerHeight(true) / 2; 1152 instance._pushPosition( $this, x, y ); 1153 props.index ++; 1154 }); 1155 }, 1156 1157 _cellsByColumnGetContainerSize : function() { 1158 return { width : Math.ceil( this.$filteredAtoms.length / this.cellsByColumn.rows ) * this.cellsByColumn.columnWidth }; 1159 }, 1160 1161 _cellsByColumnResizeChanged : function() { 1162 return this._checkIfSegmentsChanged(true); 1163 }, 1164 1165 // ====================== straightAcross ====================== 1166 1167 _straightAcrossReset : function() { 1168 this.straightAcross = { 1169 x : 0 1170 }; 1171 }, 1172 1173 _straightAcrossLayout : function( $elems ) { 1174 var instance = this; 1175 $elems.each( function( i ){ 1176 var $this = $(this); 1177 instance._pushPosition( $this, instance.straightAcross.x, 0 ); 1178 instance.straightAcross.x += $this.outerWidth(true); 1179 }); 1180 }, 1181 1182 _straightAcrossGetContainerSize : function() { 1183 return { width : this.straightAcross.x }; 1184 }, 1185 1186 _straightAcrossResizeChanged : function() { 1187 return true; 1188 } 1189 1190 }; 1191 1192 1193 // ======================= imagesLoaded Plugin =============================== 1194 /*! 1195 * jQuery imagesLoaded plugin v1.1.0 1196 * http://github.com/desandro/imagesloaded 1197 * 1198 * MIT License. by Paul Irish et al. 1199 */ 1200 1201 1202 // $('#my-container').imagesLoaded(myFunction) 1203 // or 1204 // $('img').imagesLoaded(myFunction) 1205 1206 // execute a callback when all images have loaded. 1207 // needed because .load() doesn't work on cached images 1208 1209 // callback function gets image collection as argument 1210 // `this` is the container 1211 1212 $.fn.imagesLoaded = function( callback ) { 1213 var $this = this, 1214 $images = $this.find('img').add( $this.filter('img') ), 1215 len = $images.length, 1216 blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', 1217 loaded = []; 1218 1219 function triggerCallback() { 1220 callback.call( $this, $images ); 1221 } 1222 1223 function imgLoaded( event ) { 1224 var img = event.target; 1225 if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){ 1226 loaded.push( img ); 1227 if ( --len <= 0 ){ 1228 setTimeout( triggerCallback ); 1229 $images.unbind( '.imagesLoaded', imgLoaded ); 1230 } 1231 } 1232 } 1233 1234 // if no images, trigger immediately 1235 if ( !len ) { 1236 triggerCallback(); 1237 } 1238 1239 $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() { 1240 // cached images don't fire load sometimes, so we reset src. 1241 var src = this.src; 1242 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 1243 // data uri bypasses webkit log warning (thx doug jones) 1244 this.src = blank; 1245 this.src = src; 1246 }); 1247 1248 return $this; 1249 }; 1250 1251 1252 // helper function for logging errors 1253 // $.error breaks jQuery chaining 1254 var logError = function( message ) { 1255 if ( window.console ) { 1256 window.console.error( message ); 1257 } 1258 }; 1259 1260 // ======================= Plugin bridge =============================== 1261 // leverages data method to either create or return $.Isotope constructor 1262 // A bit from jQuery UI 1263 // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 1264 // A bit from jcarousel 1265 // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarou
1265sel.js 1266 1267 $.fn.isotope = function( options, callback ) { 1268 if ( typeof options === 'string' ) { 1269 // call method 1270 var args = Array.prototype.slice.call( arguments, 1 ); 1271 1272 this.each(function(){ 1273 var instance = $.data( this, 'isotope' ); 1274 if ( !instance ) { 1275 logError( "cannot call methods on isotope prior to initialization; " + 1276 "attempted to call method '" + options + "'" ); 1277 return; 1278 } 1279 if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { 1280 logError( "no such method '" + options + "' for isotope instance" ); 1281 return; 1282 } 1283 // apply method 1284 instance[ options ].apply( instance, args ); 1285 }); 1286 } else { 1287 this.each(function() { 1288 var instance = $.data( this, 'isotope' ); 1289 if ( instance ) { 1290 // apply options & init 1291 instance.option( options ); 1292 instance._init( callback ); 1293 } else { 1294 // initialize new instance 1295 $.data( this, 'isotope', new $.Isotope( options, this, callback ) ); 1296 } 1297 }); 1298 } 1299 // return jQuery object 1300 // so plugin methods do not have to 1301 return this; 1302 }; 1303 1304})( window, jQuery );
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.