1/** 2 * jquery.elastislide.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: 150 55 }; 56 57 // ======================= imagesLoaded Plugin =============================== 58 // https://github.com/desandro/imagesloaded 59 60 // $('#my-container').imagesLoaded(myFunction) 61 // execute a callback when all images have loaded. 62 // needed because .load() doesn't work on cached images 63 64 // callback function gets image collection as argument 65 // this is the container 66 67 // original: mit license. paul irish. 2010. 68 // contributors: Oren Solomianik, David DeSandro, Yiannis Chatzikonstantinou 69 70 // blank image data-uri bypasses webkit log warning (thx doug jones) 71 var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; 72 73 $.fn.imagesLoaded = function( callback ) { 74 var $this = this, 75 deferred = $.isFunction($.Deferred) ? $.Deferred() : 0, 76 hasNotify = $.isFunction(deferred.notify), 77 $images = $this.find('img').add( $this.filter('img') ), 78 loaded = [], 79 proper = [], 80 broken = []; 81 82 // Register deferred callbacks 83 if ($.isPlainObject(callback)) { 84 $.each(callback, function (key, value) { 85 if (key === 'callback') { 86 callback = value; 87 } else if (deferred) { 88 deferred[key](value); 89 } 90 }); 91 } 92 93 function doneLoading() { 94 var $proper = $(proper), 95 $broken = $(broken); 96 97 if ( deferred ) { 98 if ( broken.length ) { 99 deferred.reject( $images, $proper, $broken ); 100 } else { 101 deferred.resolve( $images ); 102 } 103 } 104 105 if ( $.isFunction( callback ) ) { 106 callback.call( $this, $images, $proper, $broken ); 107 } 108 } 109 110 function imgLoaded( img, isBroken ) { 111 // don't proceed if BLANK image, or image is already loaded 112 if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) { 113 return; 114 } 115 116 // store element in loaded images array 117 loaded.push( img ); 118 119 // keep track of broken and properly loaded images 120 if ( isBroken ) { 121 broken.push( img ); 122 } else { 123 proper.push( img ); 124 } 125 126 // cache image and its state for future calls 127 $.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } ); 128 129 // trigger deferred progress method if present 130 if ( hasNotify ) { 131 deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] ); 132 } 133 134 // call doneLoading and clean listeners if all images are loaded 135 if ( $images.length === loaded.length ){ 136 setTimeout( doneLoading ); 137 $images.unbind( '.imagesLoaded' ); 138 } 139 } 140 141 // if no images, trigger immediately 142 if ( !$images.length ) { 143 doneLoading(); 144 } else { 145 $images.bind( 'load.imagesLoaded error.imagesLoaded', function( event ){ 146 // trigger imgLoaded 147 imgLoaded( event.target, event.type === 'error' ); 148 }).each( function( i, el ) { 149 var src = el.src; 150 151 // find out if this image has been already checked for status 152 // if it was, and src has not changed, call imgLoaded on it 153 var cached = $.data( el, 'imagesLoaded' ); 154 if ( cached && cached.src === src ) { 155 imgLoaded( el, cached.isBroken ); 156 return; 157 } 158 159 // if complete is true and browser supports natural sizes, try 160 // to check for image status manually
161 if ( el.complete && el.naturalWidth !== undefined ) { 162 imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 ); 163 return; 164 } 165 166 // cached images don't fire load sometimes, so we reset src, but only when 167 // dealing with IE, or image is complete (loaded) and failed manual check 168 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 169 if ( el.readyState || el.complete ) { 170 el.src = BLANK; 171 el.src = src; 172 } 173 }); 174 } 175 176 return deferred ? deferred.promise( $this ) : $this; 177 }; 178 179 // global 180 var $window = $( window ), 181 Modernizr = window.Modernizr; 182 183 $.Elastislide = function( options, element ) { 184 185 this.$el = $( element ); 186 this._init( options ); 187 188 }; 189 190 $.Elastislide.defaults = { 191 // orientation 'horizontal' || 'vertical' 192 orientation : 'horizontal', 193 // sliding speed 194 speed : 500, 195 // sliding easing 196 easing : 'ease-in-out', 197 // the minimum number of items to show. 198 // when we resize the window, this will make sure minItems are always shown 199 // (unless of course minItems is higher than the total number of elements) 200 minItems : 3, 201 // index of the current item (left most item of the carousel) 202 start : 0, 203 // click item callback 204 onClick : function( el, position, evt ) { return false; }, 205 onReady : function() { return false; }, 206 onBeforeSlide : function() { return false; }, 207 onAfterSlide : function() { return false; } 208 }; 209 210 $.Elastislide.prototype = { 211 212 _init : function( options ) { 213 214 // options 215 this.options = $.extend( true, {}, $.Elastislide.defaults, options ); 216 217 // https://github.com/twitter/bootstrap/issues/2870 218 var self = this, 219 transEndEventNames = { 220 'WebkitTransition' : 'webkitTransitionEnd', 221 'MozTransition' : 'transitionend', 222 'OTransition' : 'oTransitionEnd', 223 'msTransition' : 'MSTransitionEnd', 224 'transition' : 'transitionend' 225 }; 226 227 this.transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ]; 228 229 // suport for css transforms and css transitions 230 this.support = Modernizr.csstransitions && Modernizr.csstransforms; 231 232 // current item's index 233 this.current = this.options.start; 234 235 // control if it's sliding 236 this.isSliding = false; 237 238 this.$items = this.$el.children( 'li' ); 239 // total number of items 240 this.itemsCount = this.$items.length; 241 if( this.itemsCount === 0 ) { 242 243 return false; 244 245 } 246 this._validate(); 247 // remove white space 248 this.$items.detach(); 249 this.$el.empty(); 250 this.$el.append( this.$items ); 251 252 // main wrapper 253 this.$el.wrap( '<div class="elastislide-wrapper elastislide-loading elastislide-' + this.options.orientation + '"></div>' ); 254 255 // check if we applied a transition to the <ul> 256 this.hasTransition = false; 257 258 // add transition for the <ul> 259 this.hasTransitionTimeout = setTimeout( function() { 260 261 self._addTransition(); 262 263 }, 100 ); 264 265 // preload the images 266 267 this.$el.imagesLoaded( function() { 268 269 self.$el.show(); 270 271 self._layout(); 272 self._configure(); 273 274 if( self.hasTransition ) { 275 276 // slide to current's position 277 self._removeTransition(); 278 self._slideToItem( self.current ); 279 280 self.$el.on( self.transEndEventName, function() { 281 282 self.$el.off( self.transEndEventName ); 283 self._setWrapperSize(); 284 // add transition for the <ul> 285 self._addTransition(); 286 self._initEvents(); 287 288 } ); 289 290 } 291 else { 292 293 clearTimeout( self.hasTransitionTimeout ); 294 self._setWrapperSize(); 295 self._initEvents(); 296 // slide to current's position 297 self._slideToItem( self.current ); 298 setTimeout( function() { self._addTransition(); }, 25 ); 299 300 } 301 302 self.options.onReady(); 303 304 } ); 305 306 }, 307 _validate : function() { 308 309 if( this.options.speed < 0 ) { 310 311 this.options.speed = 500; 312 313 } 314 if( this.options.minItems < 1 || this.options.minItems > this.itemsCount ) { 315 316 this.options.minItems = 1; 317 318 } 319 if( this.options.start < 0 || this.options.start > this.itemsCount - 1 ) { 320 321 this.options.start = 0; 322 323 } 324 if( this.options.orientation != 'horizontal' && this.options.orientation != 'vertical' ) { 325 326 this.options.orientation = 'horizontal'; 327 328 } 329 330 }, 331 _layout : function() { 332 333 this.$el.wrap( '<div class="elastislide-carousel"></div>' ); 334 335 this.$carousel = this.$el.parent(); 336 this.$wrapper = this.$carousel.parent().removeClass( 'elastislide-loading' ); 337 338 // save original image sizes 339 var $img = this.$items.find( 'img:first' ); 340 this.imgSize = { width : $img.outerWidth( true ), height : $img.outerHeight( true ) }; 341 342 this._setItemsSize(); 343 this.options.orientation === 'horizontal' ? this.$el.css( 'max-height', this.imgSize.height ) : this.$el.css( 'height', this.options.minItems * this.imgSize.height ); 344 345 // add the controls
346 this._addControls(); 347 348 }, 349 _addTransition : function() { 350 351 if( this.support ) { 352 353 this.$el.css( 'transition', 'all ' + this.options.speed + 'ms ' + this.options.easing ); 354 355 } 356 this.hasTransition = true; 357 358 }, 359 _removeTransition : function() { 360 361 if( this.support ) { 362 363 this.$el.css( 'transition', 'all 0s' ); 364 365 } 366 this.hasTransition = false; 367 368 }, 369 _addControls : function() { 370 371 var self = this; 372 373 // add navigation elements 374 this.$navigation = $( '<nav><span class="elastislide-prev">Previous</span><span class="elastislide-next">Next</span></nav>' ) 375 .appendTo( this.$wrapper ); 376 377 378 this.$navPrev = this.$navigation.find( 'span.elastislide-prev' ).on( 'mousedown.elastislide', function( event ) { 379 380 self._slide( 'prev' ); 381 return false; 382 383 } ); 384 385 this.$navNext = this.$navigation.find( 'span.elastislide-next' ).on( 'mousedown.elastislide', function( event ) { 386 387 self._slide( 'next' ); 388 return false; 389 390 } ); 391 392 }, 393 _setItemsSize : function() { 394 395 // width for the items (%) 396 var w = this.options.orientation === 'horizontal' ? ( Math.floor( this.$carousel.width() / this.options.minItems ) * 100 ) / this.$carousel.width() : 100; 397 398 this.$items.css( { 399 'width' : w + '%', 400 'max-width' : this.imgSize.width, 401 'max-height' : this.imgSize.height 402 } ); 403 404 if( this.options.orientation === 'vertical' ) { 405 406 this.$wrapper.css( 'max-width', this.imgSize.width + parseInt( this.$wrapper.css( 'padding-left' ) ) + parseInt( this.$wrapper.css( 'padding-right' ) ) ); 407 408 } 409 410 }, 411 _setWrapperSize : function() { 412 413 if( this.options.orientation === 'vertical' ) { 414 415 this.$wrapper.css( { 416 'height' : this.options.minItems * this.imgSize.height + parseInt( this.$wrapper.css( 'padding-top' ) ) + parseInt( this.$wrapper.css( 'padding-bottom' ) ) 417 } ); 418 419 } 420 421 }, 422 _configure : function() { 423 424 // check how many items fit in the carousel (visible area -> this.$carousel.width() ) 425 this.fitCount = this.options.orientation === 'horizontal' ? 426 this.$carousel.width() < this.options.minItems * this.imgSize.width ? this.options.minItems : Math.floor( this.$carousel.width() / this.imgSize.width ) : 427 this.$carousel.height() < this.options.minItems * this.imgSize.height ? this.options.minItems : Math.floor( this.$carousel.height() / this.imgSize.height ); 428 429 }, 430 _initEvents : function() { 431 432 var self = this; 433 434 $window.on( 'debouncedresize.elastislide', function() { 435 436 self._setItemsSize(); 437 self._configure(); 438 self._slideToItem( self.current ); 439 440 } ); 441 442 this.$el.on( this.transEndEventName, function() { 443 444 self._onEndTransition(); 445 446 } ); 447 448 if( this.options.orientation === 'horizontal' ) { 449 450 this.$el.on( { 451 swipeleft : function() { 452 453 self._slide( 'next' ); 454 455 }, 456 swiperight : function() { 457 458 self._slide( 'prev' ); 459 460 } 461 } ); 462 463 } 464 else { 465 466 this.$el.on( { 467 swipeup : function() { 468 469 self._slide( 'next' ); 470 471 }, 472 swipedown : function() { 473 474 self._slide( 'prev' ); 475 476 } 477 } ); 478 479 } 480 481 // item click event 482 this.$el.on( 'click.elastislide', 'li', function( event ) { 483 484 var $item = $( this ); 485 486 self.options.onClick( $item, $item.index(), event ); 487 488 }); 489 490 }, 491 _destroy : function( callback ) { 492 493 this.$el.off( this.transEndEventName ).off( 'swipeleft swiperight swipeup swipedown .elastislide' ); 494 $window.off( '.elastislide' ); 495 496 this.$el.css( { 497 'max-height' : 'none', 498 'transition' : 'none' 499 } ).unwrap( this.$carousel ).unwrap( this.$wrapper ); 500 501 this.$items.css( { 502 'width' : 'auto', 503 'max-width' : 'none', 504 'max-height' : 'none' 505 } ); 506 507 this.$navigation.remove(); 508 this.$wrapper.remove(); 509 510 if( callback ) { 511 512 callback.call(); 513 514 } 515 516 }, 517 _toggleControls : function( dir, display ) { 518 519 if( display ) { 520 521 ( dir === 'next' ) ? this.$navNext.show() : this.$navPrev.show(); 522 523 } 524 else { 525 526 ( dir === 'next' ) ? this.$navNext.hide() : this.$navPrev.hide(); 527 528 } 529 530 }, 531 _slide : function( dir, tvalue ) { 532 533 if( this.isSliding ) { 534 535 return false;
536 537 } 538 539 this.options.onBeforeSlide(); 540 541 this.isSliding = true; 542 543 var self = this, 544 translation = this.translation || 0, 545 // width/height of an item ( <li> ) 546 itemSpace = this.options.orientation === 'horizontal' ? this.$items.outerWidth( true ) : this.$items.outerHeight( true ), 547 // total width/height of the <ul> 548 totalSpace = this.itemsCount * itemSpace, 549 // visible width/height 550 visibleSpace = this.options.orientation === 'horizontal' ? this.$carousel.width() : this.$carousel.height(); 551 552 if( tvalue === undefined ) { 553 554 var amount = this.fitCount * itemSpace; 555 556 if( amount < 0 ) { 557 558 return false; 559 560 } 561 562 if( dir === 'next' && totalSpace - ( Math.abs( translation ) + amount ) < visibleSpace ) { 563 564 amount = totalSpace - ( Math.abs( translation ) + visibleSpace ); 565 566 // show / hide navigation buttons 567 this._toggleControls( 'next', false ); 568 this._toggleControls( 'prev', true ); 569 570 } 571 else if( dir === 'prev' && Math.abs( translation ) - amount < 0 ) { 572 573 amount = Math.abs( translation ); 574 575 // show / hide navigation buttons 576 this._toggleControls( 'next', true ); 577 this._toggleControls( 'prev', false ); 578 579 } 580 else { 581 582 // future translation value 583 var ftv = dir === 'next' ? Math.abs( translation ) + Math.abs( amount ) : Math.abs( translation ) - Math.abs( amount ); 584 585 // show / hide navigation buttons 586 ftv > 0 ? this._toggleControls( 'prev', true ) : this._toggleControls( 'prev', false ); 587 ftv < totalSpace - visibleSpace ? this._toggleControls( 'next', true ) : this._toggleControls( 'next', false ); 588 589 } 590 591 tvalue = dir === 'next' ? translation - amount : translation + amount; 592 593 } 594 else { 595 596 var amount = Math.abs( tvalue ); 597 598 if( Math.max( totalSpace, visibleSpace ) - amount < visibleSpace ) { 599 600 tvalue = - ( Math.max( totalSpace, visibleSpace ) - visibleSpace ); 601 602 } 603 604 // show / hide navigation buttons 605 amount > 0 ? this._toggleControls( 'prev', true ) : this._toggleControls( 'prev', false ); 606 Math.max( totalSpace, visibleSpace ) - visibleSpace > amount ? this._toggleControls( 'next', true ) : this._toggleControls( 'next', false ); 607 608 } 609 610 this.translation = tvalue; 611 612 if( translation === tvalue ) { 613 614 this._onEndTransition(); 615 return false; 616 617 } 618 619 if( this.support ) { 620 621 this.options.orientation === 'horizontal' ? this.$el.css( 'transform', 'translateX(' + tvalue + 'px)' ) : this.$el.css( 'transform', 'translateY(' + tvalue + 'px)' ); 622 623 } 624 else { 625 626 $.fn.applyStyle = this.hasTransition ? $.fn.animate : $.fn.css; 627 var styleCSS = this.options.orientation === 'horizontal' ? { left : tvalue } : { top : tvalue }; 628 629 this.$el.stop().applyStyle( styleCSS, $.extend( true, [], { duration : this.options.speed, complete : function() { 630 631 self._onEndTransition(); 632 633 } } ) ); 634 635 } 636 637 if( !this.hasTransition ) { 638 639 this._onEndTransition(); 640 641 } 642 643 }, 644 _onEndTransition : function() { 645 646 this.isSliding = false; 647 this.options.onAfterSlide(); 648 649 }, 650 _slideTo : function( pos ) { 651 652 var pos = pos || this.current, 653 translation = Math.abs( this.translation ) || 0, 654 itemSpace = this.options.orientation === 'horizontal' ? this.$items.outerWidth( true ) : this.$items.outerHeight( true ), 655 posR = translation + this.$carousel.width(), 656 ftv = Math.abs( pos * itemSpace ); 657 658 if( ftv + itemSpace > posR || ftv < translation ) { 659 660 this._slideToItem( pos ); 661 662 } 663 664 }, 665 _slideToItem : function( pos ) { 666 667 // how much to slide? 668 var amount = this.options.orientation === 'horizontal' ? pos * this.$items.outerWidth( true ) : pos * this.$items.outerHeight( true ); 669 this._slide( '', -amount ); 670 671 }, 672 // public method: adds new items to the carousel 673 /* 674 675 how to use: 676 var carouselEl = $( '#carousel' ), 677 carousel = carouselEl.elastislide(); 678 ... 679 680 // append or prepend new items: 681 carouselEl.prepend('<li><a href="#"><img src="images/large/2.jpg" alt="image02" /></a></li>'); 682 683 // call the add method: 684 es.add(); 685 686 */ 687 add : function( callback ) { 688 689 var self = this, 690 oldcurrent = this.current, 691 $currentItem = this.$items.eq( this.current ); 692 693 // adds new items to the carousel
694 this.$items = this.$el.children( 'li' ); 695 this.itemsCount = this.$items.length; 696 this.current = $currentItem.index(); 697 this._setItemsSize(); 698 this._configure(); 699 this._removeTransition(); 700 oldcurrent < this.current ? this._slideToItem( this.current ) : this._slide( 'next', this.translation ); 701 setTimeout( function() { self._addTransition(); }, 25 ); 702 703 if ( callback ) { 704 705 callback.call(); 706 707 } 708 709 }, 710 // public method: sets a new element as the current. slides to that position 711 setCurrent : function( idx, callback ) { 712 713 this.current = idx; 714 715 this._slideTo(); 716 717 if ( callback ) { 718 719 callback.call(); 720 721 } 722 723 }, 724 // public method: slides to the next set of items 725 next : function() { 726 727 self._slide( 'next' ); 728 729 }, 730 // public method: slides to the previous set of items 731 previous : function() { 732 733 self._slide( 'prev' ); 734 735 }, 736 // public method: slides to the first item 737 slideStart : function() { 738 739 this._slideTo( 0 ); 740 741 }, 742 // public method: slides to the last item 743 slideEnd : function() { 744 745 this._slideTo( this.itemsCount - 1 ); 746 747 }, 748 // public method: destroys the elastislide instance 749 destroy : function( callback ) { 750 751 this._destroy( callback ); 752 753 } 754 755 }; 756 757 var logError = function( message ) { 758 759 if ( window.console ) { 760 761 window.console.error( message ); 762 763 } 764 765 }; 766 767 $.fn.elastislide = function( options ) { 768 769 var self = $.data( this, 'elastislide' ); 770 771 if ( typeof options === 'string' ) { 772 773 var args = Array.prototype.slice.call( arguments, 1 ); 774 775 this.each(function() { 776 777 if ( !self ) { 778 779 logError( "cannot call methods on elastislide prior to initialization; " + 780 "attempted to call method '" + options + "'" ); 781 return; 782 783 } 784 785 if ( !$.isFunction( self[options] ) || options.charAt(0) === "_" ) { 786 787 logError( "no such method '" + options + "' for elastislide self" ); 788 return; 789 790 } 791 792 self[ options ].apply( self, args ); 793 794 }); 795 796 } 797 else { 798 799 this.each(function() { 800 801 if ( self ) { 802 803 self._init(); 804 805 } 806 else { 807 808 self = $.data( this, 'elastislide', new $.Elastislide( options, this ) ); 809 810 } 811 812 }); 813 814 } 815 816 return self; 817 818 }; 819 820} )( 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.