1/** 2 * jquery.gridrotator.js v1.1.0 3 * http://www.codrops.com 4 * 5 * Licensed under the MIT license. 6 * http://www.opensource.org/licenses/mit-license.php 7 * 8 * Copyright 2012, Codrops 9 * http://www.codrops.com 10 */ 11;( function( $, window, undefined ) { 12 13 'use strict'; 14 15 /* 16 * debouncedresize: special jQuery event that happens once after a window resize 17 * 18 * latest version and complete README available on Github: 19 * https://github.com/louisremi/jquery-smartresize/blob/master/jquery.debouncedresize.js 20 * 21 * Copyright 2011 @louis_remi 22 * Licensed under the MIT license. 23 */ 24 var $event = $.event, 25 $special, 26 resizeTimeout; 27 28 $special = $event.special.debouncedresize = { 29 setup: function() { 30 $( this ).on( "resize", $special.handler ); 31 }, 32 teardown: function() { 33 $( this ).off( "resize", $special.handler ); 34 }, 35 handler: function( event, execAsap ) { 36 // Save the context 37 var context = this, 38 args = arguments, 39 dispatch = function() { 40 // set correct event type 41 event.type = "debouncedresize"; 42 $event.dispatch.apply( context, args ); 43 }; 44 45 if ( resizeTimeout ) { 46 clearTimeout( resizeTimeout ); 47 } 48 49 execAsap ? 50 dispatch() : 51 resizeTimeout = setTimeout( dispatch, $special.threshold ); 52 }, 53 threshold: 100 54 }; 55 56 // http://www.hardcode.nl/subcategory_1/article_317-array-shuffle-function 57 Array.prototype.shuffle = function() { 58 var i=this.length,p,t; 59 while (i--) { 60 p = Math.floor(Math.random()*i); 61 t = this[i]; 62 this[i]=this[p]; 63 this[p]=t; 64 } 65 return this; 66 }; 67 68 // HTML5 PageVisibility API 69 // http://www.html5rocks.com/en/tutorials/pagevisibility/intro/ 70 // by Joe Marini (@joemarini) 71 function getHiddenProp(){ 72 var prefixes = ['webkit','moz','ms','o']; 73 74 // if 'hidden' is natively supported just return it 75 if ('hidden' in document) return 'hidden'; 76 77 // otherwise loop over all the known prefixes until we find one 78 for (var i = 0; i < prefixes.length; i++){ 79 if ((prefixes[i] + 'Hidden') in document) 80 return prefixes[i] + 'Hidden'; 81 } 82 83 // otherwise it's not supported 84 return null; 85 } 86 function isHidden() { 87 var prop = getHiddenProp(); 88 if (!prop) return false; 89 90 return document[prop]; 91 } 92 93 function isEmpty( obj ) { 94 return Object.keys(obj).length === 0; 95 } 96 97 // global 98 var $window = $( window ), 99 Modernizr = window.Modernizr; 100 101 $.GridRotator = function( options, element ) { 102 103 this.$el = $( element ); 104 if( Modernizr.backgroundsize ) { 105 106 var self = this; 107 this.$el.addClass( 'ri-grid-loading' ); 108 this._init( options ); 109 110 } 111 112 }; 113 114 // the options 115 $.GridRotator.defaults = { 116 // number of rows 117 rows : 4, 118 // number of columns 119 columns : 10, 120 w1024 : { rows : 3, columns : 8 }, 121 w768 : {rows : 3,columns : 7 }, 122 w480 : {rows : 3,columns : 5 }, 123 w320 : {rows : 2,columns : 4 }, 124 w240 : {rows : 2,columns : 3 }, 125 // step: number of items that are replaced at the same time 126 // random || [some number] 127 // note: for performance issues, the number "can't" be > options.maxStep 128 step : 'random', 129 // change it as you wish.. 130 maxStep : 3, 131 // prevent user to click the items 132 preventClick : true, 133 // animation type 134 // showHide || fadeInOut || 135 // slideLeft || slideRight || slideTop || slideBottom || 136 // rotateBottom || rotateLeft || rotateRight || rotateTop || 137 // scale || 138 // rotate3d || 139 // rotateLeftScale || rotateRightScale || rotateTopScale || rotateBottomScale || 140 // random 141 animType : 'random', 142 // animation speed 143 animSpeed : 800, 144 // animation easings 145 animEasingOut : 'linear', 146 animEasingIn: 'linear', 147 // the item(s) will be replaced every 3 seconds 148 // note: for performance issues, the time "can't" be < 300 ms 149 interval : 3000, 150 // if false the animations will not start 151 // use false if onhover is true for example 152 slideshow : true, 153 // if true the items will switch when hovered 154 onhover : false, 155 // ids of elements that shouldn't change 156 nochange : [] 157 }; 158 159 $.GridRotator.prototype = { 160 161 _init : function( options ) { 162 163 // options 164 this.options = $.extend( true, {}, $.GridRotator.defaults, options ); 165 // cache some elements + variables 166 this._config(); 167 168 }, 169 _config : function() { 170 171 var self = this, 172 transEndEventNames = { 173 'WebkitTransition' : 'webkitTransitionEnd', 174 'MozTransition' : 'transitionend', 175 'OTransition' : 'oTransitionEnd', 176 'msTransition' : 'MSTransitionEnd', 177 'transition' : 'transitionend' 178 }; 179 180 // support CSS transitions and 3d transforms 181 this.supportTransitions = Modernizr.csstransitions; 182 this.supportTransforms3D = Modernizr.csstransforms3d; 183 184 this.transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ] + '.gridrotator'; 185 186 // all animation types for the random option 187 this.animTypes = this.supportTransforms3D ? [ 188 'fadeInOut', 189 'slideLeft', 190 'slideRight', 191 'slideTop', 192 'slideBottom', 193 'rotateLeft', 194 'rotateRight', 195 'rotateTop', 196 'rotateBottom', 197 'scale', 198 'rotate3d', 199 'rotateLeftScale', 200 'rotateRightScale', 201 'rotateTopScale', 202 'rotateBottomScale' ] : 203 [ 'fadeInOut', 'slideLeft', 'slideRight', 'slideTop', 'slideBottom' ]; 204 205 this.animType = this.options.animType; 206 207 if( this.animType !== 'random' && !this.supportTransforms3D && $.inArray( this.animType, this.animTypes ) === -1 && this.animType !== 'showHide' ) { 208 209 // fallback to 'fadeInOut' if user sets a type which is not supported 210 this.animType = 'fadeInOut'; 211 212 } 213 214 this.animTypesTotal = this.animTypes.length; 215 216 // the <ul> where the items are placed 217 this.$list = this.$el.children( 'ul' ); 218 // remove images and add background-image to anchors 219 // preload the images before 220 var loaded = 0,
221 $imgs = this.$list.find( 'img' ), 222 count = $imgs.length; 223 224 $imgs.each( function() { 225 226 var $img = $( this ), src = $img.attr( 'src' ); 227 228 $( '<img/>' ).load( function() { 229 230 ++loaded; 231 $img.parent().css( 'background-image', 'url(' + src + ')' ); 232 233 if( loaded === count ) { 234 235 $imgs.remove(); 236 self.$el.removeClass( 'ri-grid-loading' ); 237 // the items 238 self.$items = self.$list.children( 'li' ); 239 // make a copy of the items 240 self.$itemsCache = self.$items.clone(); 241 // total number of items 242 self.itemsTotal = self.$items.length; 243 // the items that will be out of the grid 244 // actually the item's child (anchor element) 245 self.outItems= []; 246 self._layout( function() { 247 self._initEvents(); 248 } ); 249 // replace [options.step] items after [options.interval] time 250 // the items that go out are randomly chosen, while the ones that get in 251 // follow a "First In First Out" logic 252 self._start(); 253 254 } 255 256 } ).attr( 'src', src ) 257 258 } ); 259 260 }, 261 _layout : function( callback ) { 262 263 var self = this; 264 265 // sets the grid dimentions based on the container's width 266 this._setGridDim(); 267 268 // reset 269 this.$list.empty(); 270 this.$items = this.$itemsCache.clone().appendTo( this.$list ); 271 272 var $outItems = this.$items.filter( ':gt(' + ( this.showTotal - 1 ) + ')' ), 273 $outAItems = $outItems.children( 'a' ); 274 275 this.outItems.length = 0; 276 277 $outAItems.each( function( i ) { 278 self.outItems.push( $( this ) ); 279 } ); 280 281 $outItems.remove(); 282 283 // container's width 284 var containerWidth = ( document.defaultView ) ? parseInt( document.defaultView.getComputedStyle( this.$el.get( 0 ), null ).width ) : this.$el.width(), 285 // item's width 286 itemWidth = Math.floor( containerWidth / this.columns ), 287 // item's hieght 288 itemHeight = $(".ri-grid-envelope").height() / this.rows, 289 // calculate gap 290 gapWidth = containerWidth - ( this.columns * Math.floor( itemWidth ) ); 291 292 for( var i = 0; i < this.rows; ++i ) { 293 294 for( var j = 0; j < this.columns; ++j ) { 295 296 var idx = this.columns * i + j, 297 $item = this.$items.eq( idx ); 298 299 $item.css( { 300 width : j < Math.floor( gapWidth ) ? itemWidth + 1 : itemWidth, 301 height : itemHeight 302 } ); 303 304 if( $.inArray( idx, this.options.nochange ) !== -1 ) { 305 $item.addClass( 'ri-nochange' ).data( 'nochange', true ); 306 } 307 308 } 309 310 } 311 312 if( this.options.preventClick ) { 313 314 this.$items.children().css( 'cursor', 'default' ).on( 'click.gridrotator', false ); 315 316 } 317 318 if( callback ) { 319 callback.call(); 320 } 321 322 }, 323 // set the grid rows and columns 324 _setGridDim : function() { 325 326 // container's width 327 var c_w = this.$el.width(); 328 329 // we will choose the number of rows/columns according to the container's width and the values set in the plugin options 330 switch( true ) { 331 case ( c_w < 240 ) : this.rows = this.options.w240.rows; this.columns = this.options.w240.columns; break; 332 case ( c_w < 320 ) : this.rows = this.options.w320.rows; this.columns = this.options.w320.columns; break; 333 case ( c_w < 480 ) : this.rows = this.options.w480.rows; this.columns = this.options.w480.columns; break; 334 case ( c_w < 768 ) : this.rows = this.options.w768.rows; this.columns = this.options.w768.columns; break; 335 case ( c_w < 1024 ) : this.rows = this.options.w1024.rows; this.columns = this.options.w1024.columns; break; 336 default : this.rows = this.options.rows; this.columns = this.options.columns; break; 337 } 338 339 this.showTotal = this.rows * this.columns; 340 341 }, 342 // init window resize event 343 _initEvents : function() { 344 345 var self = this; 346 347 $window.on( 'debouncedresize.gridrotator', function() { 348 self._layout(); 349 } ); 350 351 // use the property name to generate the prefixed event name 352 var visProp = getHiddenProp(); 353 354 // HTML5 PageVisibility API 355 // http://www.html5rocks.com/en/tutorials/pagevisibility/intro/ 356 // by Joe Marini (@joemarini) 357 if (visProp) { 358 359 var evtname = visProp.replace(/[H|h]idden/,'') + 'visibilitychange'; 360 document.addEventListener(evtname, function() { self._visChange(); } ); 361 362 } 363 364 if( !Modernizr.touch && this.options.onhover ) { 365 366 self.$items.on( 'mouseenter.gridrotator', function() { 367 368 var $item = $( this ); 369 if( !$item.data( 'active' ) && !$item.data( 'hovered' ) && !$item.data( 'nochange' ) ) { 370 $item.data( 'hovered', true ); 371 self._replace( $item ); 372 } 373 374 } ).on( 'mouseleave.gridrotator', function() { 375 376 $( this ).data( 'hovered', false ); 377 378 } ); 379 380 } 381 382 }, 383 _visChange : function() { 384 385 isHidden() ? clearTimeout( this.playtimeout ) : this._start(); 386 387 }, 388 // start rotating elements 389 _start : function() { 390 391 if( this.showTotal < this.itemsTotal && this.options.slideshow ) { 392 this._showNext(); 393 } 394 395 }, 396 // get which type of animation 397 _getAnimType : function() { 398 399 return this.animType === 'random' ? this.animTypes[ Math.floor( Math.random() * this.animTypesTotal ) ] : this.animType; 400 401 }, 402 // get css properties for the transition effect 403 _getAnimProperties : function( $out ) { 404 405 var startInProp = {}, startOutProp = {}, endInProp = {}, endOutProp = {},
406 animType = this._getAnimType(), speed, delay = 0; 407 408 switch( animType ) { 409 410 case 'showHide' : 411 412 speed = 0; 413 endOutProp.opacity = 0; 414 break; 415 416 case 'fadeInOut' : 417 418 endOutProp.opacity = 0; 419 break; 420 421 case 'slideLeft' : 422 423 startInProp.left = $out.width(); 424 endInProp.left = 0; 425 endOutProp.left = -$out.width(); 426 break; 427 428 case 'slideRight' : 429 430 startInProp.left = -$out.width(); 431 endInProp.left = 0; 432 endOutProp.left = $out.width(); 433 break; 434 435 case 'slideTop' : 436 437 startInProp.top = $out.height(); 438 endInProp.top = 0; 439 endOutProp.top = -$out.height(); 440 break; 441 442 case 'slideBottom' : 443 444 startInProp.top = -$out.height(); 445 endInProp.top = 0; 446 endOutProp.top = $out.height(); 447 break; 448 449 case 'rotateLeft' : 450 451 speed = this.options.animSpeed / 2; 452 startInProp.transform = 'rotateY(90deg)'; 453 endInProp.transform = 'rotateY(0deg)'; 454 delay = speed; 455 endOutProp.transform = 'rotateY(-90deg)'; 456 break; 457 458 case 'rotateRight' : 459 460 speed = this.options.animSpeed / 2; 461 startInProp.transform = 'rotateY(-90deg)'; 462 endInProp.transform = 'rotateY(0deg)';
463 delay = speed; 464 endOutProp.transform = 'rotateY(90deg)'; 465 break; 466 467 case 'rotateTop' : 468 469 speed = this.options.animSpeed / 2; 470 startInProp.transform= 'rotateX(90deg)'; 471 endInProp.transform = 'rotateX(0deg)'; 472 delay = speed; 473 endOutProp.transform = 'rotateX(-90deg)'; 474 break; 475 476 case 'rotateBottom' : 477 478 speed = this.options.animSpeed / 2; 479 startInProp.transform = 'rotateX(-90deg)'; 480 endInProp.transform = 'rotateX(0deg)'; 481 delay = speed; 482 endOutProp.transform = 'rotateX(90deg)'; 483 break; 484 485 case 'scale' : 486 487 speed = this.options.animSpeed / 2; 488 startInProp.transform = 'scale(0)'; 489 startOutProp.transform = 'scale(1)'; 490 endInProp.transform = 'scale(1)'; 491 delay = speed; 492 endOutProp.transform = 'scale(0)'; 493 break; 494 495 case 'rotateLeftScale' : 496 497 startOutProp.transform = 'scale(1)'; 498 speed = this.options.animSpeed / 2; 499 startInProp.transform = 'scale(0.3) rotateY(90deg)'; 500 endInProp.transform = 'scale(1) rotateY(0deg)';
501 delay = speed; 502 endOutProp.transform = 'scale(0.3) rotateY(-90deg)'; 503 break; 504 505 case 'rotateRightScale' : 506 507 startOutProp.transform = 'scale(1)'; 508 speed = this.options.animSpeed / 2; 509 startInProp.transform = 'scale(0.3) rotateY(-90deg)'; 510 endInProp.transform = 'scale(1) rotateY(0deg)'; 511 delay = speed; 512 endOutProp.transform = 'scale(0.3) rotateY(90deg)'; 513 break; 514 515 case 'rotateTopScale' : 516 517 startOutProp.transform = 'scale(1)'; 518 speed = this.options.animSpeed / 2; 519 startInProp.transform = 'scale(0.3) rotateX(90deg)'; 520 endInProp.transform = 'scale(1) rotateX(0deg)'; 521 delay = speed; 522 endOutProp.transform = 'scale(0.3) rotateX(-90deg)'; 523 break; 524 525 case 'rotateBottomScale' : 526 527 startOutProp.transform = 'scale(1)'; 528 speed = this.options.animSpeed / 2; 529 startInProp.transform = 'scale(0.3) rotateX(-90deg)'; 530 endInProp.transform = 'scale(1) rotateX(0deg)'; 531 delay = speed; 532 endOutProp.transform = 'scale(0.3) rotateX(90deg)'; 533 break; 534 535 case 'rotate3d' : 536 537 speed = this.options.animSpeed / 2; 538 startInProp.transform = 'rotate3d( 1, 1, 0, 90deg )'; 539 endInProp.transform = 'rotate3d( 1, 1, 0, 0deg )';
540 delay = speed; 541 endOutProp.transform = 'rotate3d( 1, 1, 0, -90deg )'; 542 break; 543 544 } 545 546 return { 547 startInProp : startInProp, 548 startOutProp : startOutProp, 549 endInProp : endInProp, 550 endOutProp : endOutProp, 551 delay : delay, 552 animSpeed : speed != undefined ? speed : this.options.animSpeed 553 }; 554 555 }, 556 // show next [option.step] elements 557 _showNext : function( time ) { 558 559 var self = this; 560 561 clearTimeout( this.playtimeout ); 562 563 this.playtimeout = setTimeout( function() { 564 565 var step = self.options.step, max= self.options.maxStep, min = 1; 566 567 if( max > self.showTotal ) { 568 max = self.showTotal; 569 } 570 571 // number of items to swith at this point of time 572 var nmbOut = step === 'random' ? Math.floor( Math.random() * max + min ) : Math.min( Math.abs( step ) , max ) , 573 // array with random indexes. These will be the indexes of the items we will replace 574 randArr = self._getRandom( nmbOut, self.showTotal ); 575 576 for( var i = 0; i < nmbOut; ++i ) { 577 578 // element to go out 579 var $out = self.$items.eq( randArr[ i ] ); 580 581 // if element is active, which means it is currently animating, 582 // then we need to get different positions.. 583 if( $out.data( 'active' ) || $out.data( 'nochange' ) ) { 584 585 // one of the items is active, call again.. 586 self._showNext( 1 ); 587 return false; 588 589 } 590 591 self._replace( $out ); 592 593 } 594 595 // again and again.. 596 self._showNext(); 597 598 }, time || Math.max( Math.abs( this.options.interval ) , 300 ) ); 599 600 }, 601 _replace : function( $out ) { 602 603 $out.data( 'active', true ); 604 605 var self = this, 606 $outA = $out.children( 'a:last' ), 607 newElProp = { 608 width : $outA.width(), 609 height : $outA.height() 610 }; 611 612 // element stays active 613 $out.data( 'active', true ); 614 615 // get the element (anchor) that will go in (first one inserted in this.outItems) 616 var $inA = this.outItems.shift(); 617 618 // save element that went out 619 this.outItems.push( $outA.clone().css( 'transition', 'none' ) ); 620 621 // prepend in element 622 $inA.css( newElProp ).prependTo( $out ); 623 624 var animProp = this._getAnimProperties( $outA ); 625 626 $inA.css( animProp.startInProp ); 627 $outA.css( animProp.startOutProp ); 628 629 this._setTransition( $inA, 'all', animProp.animSpeed, animProp.delay, this.options.animEasingIn ); 630 this._setTransition( $outA, 'all', animProp.animSpeed, 0, this.options.animEasingOut ); 631 632 this._applyTransition( $inA, animProp.endInProp, animProp.animSpeed, function() { 633 634 var $el = $( this ), 635 t = animProp.animSpeed === self.options.animSpeed && isEmpty( animProp.endInProp ) ? animProp.animSpeed : 0; 636 637 setTimeout( function() { 638 639 if( self.supportTransitions ) { 640 $el.off( self.transEndEventName ); 641 } 642 643 $el.next().remove(); 644 $el.parent().data( 'active', false ); 645 646 }, t ); 647 648 }, animProp.animSpeed === 0 || isEmpty( animProp.endInProp ) ); 649 this._applyTransition( $outA, animProp.endOutProp, animProp.animSpeed ); 650 651 }, 652 _getRandom : function( cnt, limit ) { 653 654 var randArray = []; 655 656 for( var i = 0; i < limit; ++i ) { 657 randArray.push( i ) 658 } 659 660 return randArray.shuffle().slice( 0, cnt ); 661 662 }, 663 _setTransition : function( el, prop, speed, delay, easing ) { 664 665 setTimeout( function() { 666 el.css( 'transition', prop + ' ' + speed + 'ms ' + delay + 'ms ' + easing ); 667 }, 25 ); 668 669 }, 670 _applyTransition : function( el, styleCSS, speed, fncomplete, force ) { 671 672 var self = this; 673 setTimeout( function() { 674 $.fn.applyStyle = self.supportTransitions ? $.fn.css : $.fn.animate; 675 676 if( fncomplete && self.supportTransitions ) { 677 678 el.on( self.transEndEventName, fncomplete ); 679 680 if( force ) { 681 fncomplete.call( el ); 682 } 683 684 } 685 686 fncomplete = fncomplete || function() { return false; }; 687 688 el.stop().applyStyle( styleCSS, $.extend( true, [], { duration : speed + 'ms', complete : fncomplete } ) ); 689 }, 25 ); 690 691 } 692 693 }; 694 695 var logError = function( message ) { 696 697 if ( window.console ) { 698 699 window.console.error( message ); 700 701 } 702 703 }; 704 705 $.fn.gridrotator = function( options ) { 706 707 var instance = $.data( this, 'gridrotator' ); 708 709 if ( typeof options === 'string' ) { 710 711 var args = Array.prototype.slice.call( arguments, 1 ); 712 713 this.each(function() { 714 715 if ( !instance ) { 716 717 logError( "cannot call methods on gridrotator prior to initialization; " + 718 "attempted to call method '" + options + "'" ); 719 return; 720 721 } 722 723 if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { 724 725 logError( "no such method '" + options + "' for gridrotator instance" ); 726 return; 727 728 } 729 730 instance[ options ].apply( instance, args ); 731 732 }); 733 734 } 735 else { 736 737 this.each(function() { 738 739 if ( instance ) { 740 741 instance._init(); 742 743 } 744 else { 745 746 instance = $.data( this, 'gridrotator', new $.GridRotator( options, this ) ); 747 748 } 749 750 }); 751 752 } 753 754 return instance; 755 756 }; 757 758} )( jQuery, window );
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.