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