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