/* * jQuery Touch Optimized Sliders "R"Us 1.4.0 * * Copyright (c) 2013 Fred Heusschen * www.frebsite.nl * * Plugin website: * tosrus.frebsite.nl * * Dual licensed under the MIT and GPL licenses. * http://en.wikipedia.org/wiki/MIT_License * http://en.wikipedia.org/wiki/GNU_General_Public_License */ // Whishlist // - drag image around if zoomed (function( $ ) { if ( $.fn.TosRUs ) { return; } var $window, $html, $body; var _viewScale = false, _isTouch = ( 'ontouchstart' in window ), _duration = 400; $.fn.TosRUs = $.fn.tosrus = function( oDefault, oDesktop, oTouch ) { if ( this.length == 0 ) { return this; } /* GLOBAL VARS */ var $tos = this, _tos = this[ 0 ]; oDefault = complObject( oDefault, {} ); oDefault = ( _isTouch ) ? $.extend( true, {}, oDefault, oTouch ) : $.extend( true, {}, oDefault, oDesktop ); var opts = complementOptions( oDefault, $tos, _tos ), _fixed = ( opts.wrapper.target == 'window' ), _index = 0, _total = 0, _scrollPosition = 0; $window = $(window); $html = $('html'); $body = $('body'); if ( !_viewScale && _isTouch && typeof FlameViewportScale != 'undefined' ) { _viewScale = new FlameViewportScale(); } /* MARKUP */ var $wrpr = $('
'), $sldr = $('
').appendTo( $wrpr ), $clse = null, $capt = null, $prev = null, $next = null; $wrpr .css( opts.wrapper.css ) .addClass( cls( _fixed ? 'fixed' : 'inline' ) ) .addClass( cls( _isTouch ? 'touch' : 'desktop' ) ) .addClass( cls( opts.slides.scale ) ); if ( opts.caption ) { $capt = $('
').appendTo( $wrpr ); } if ( opts.prev.button ) { $prev = ( opts.prev.button instanceof $ ) ? opts.prev.button : $('').appendTo( $wrpr ); } if ( opts.next.button ) { $next = ( opts.next.button instanceof $ ) ? opts.next.button : $('').appendTo( $wrpr ); } if ( opts.close.button ) { $clse = ( opts.close.button instanceof $ ) ? opts.close.button : $('').appendTo( $wrpr ); } /* ADD SLIDES */ var $anchors = getValidAnchors( $tos, opts ), $slides = _initSlides( $tos, $anchors, $wrpr, $sldr, opts ); _total = $slides.length; _index = setIndex( _index, $wrpr ); /* EVENTS */ $wrpr .bind( evt( '' ), function( e ) { e.stopPropagation(); } ) // Open event, opens the gallery and slides to the designated slide .bind( evt( 'open' ), function( e, index, direct ) { if ( !_opened ) { if ( _fixed ) { _scrollPosition = $window.scrollTop(); } $wrpr .show() .css( 'opacity', 0 ) .addClass( cls( 'hover' ) ); animate( $wrpr, { 'opacity': 1 }, direct ); // scale buttons scaleButtons( [ $prev, $next, $clse ], $capt ); // callback event $wrpr.trigger( evt( 'opening' ), [ index, direct ] ); } loadContents( $sldr, _index, opts ); direct = ( direct || !_opened ); _opened = setOpened( true, $wrpr ); if ( $.isNumeric( index ) ) { $wrpr.trigger( evt( 'slideTo' ), [ index, direct ] ); } } ) // Close event, closes the gallery .bind( evt( 'close' ), function( e, direct ) { if ( _opened ) { $wrpr.removeClass( cls( 'hover' ) ); stopVideo( $wrpr.find( '.' + cls( 'video' ) ), 'stop' ); if ( typeof direct == 'undefined' ) { direct = _duration / 2; } animate( $wrpr, { 'opacity': 0 }, direct, function() { $wrpr.hide(); } ); // callback event $wrpr.trigger( evt( 'closing' ), [ direct ] ); } _opened = setOpened( false, $wrpr ); } ) // Prev + Prev events, slides to the previous / next set of slides .bind( evt( 'prev' ) + ' ' + evt( 'next' ), function( e, slides, duration, easing ) { if ( !$.isNumeric( slides ) ) { slides = opts[ e.type ].slides; } $wrpr.trigger( evt( 'slideTo' ), [ ( e.type == 'prev' ) ? _index - slides : _index + slides, duration, easing ] ); } ) // SlideTo event, slides to the designated slide .bind( evt( 'slideTo' ), function( e, slide, duration, easing ) { if ( !_opened ) { return false; } if ( !$.isNumeric( slide ) ) { return false; } var doSlide = true; // Less then first if ( slide < 0 ) { slide = 0; if ( _index == 0 ) { doSlide = false; } } // More then last else if ( slide + opts.slides.visible > _total ) { slide = _total - opts.slides.visible; if ( _index + opts.slides.visible >= _total ) { doSlide = false; } } var $curSlide = $sldr.children().eq( _index ); _index = setIndex( slide, $wrpr ); loadContents( $sldr, _index, opts ); setButtons( $prev, $next, _index, _total, opts ); if ( doSlide ) { // Slide var left = 0 - ( _index * opts.slides.width ) + opts.slides.offset; if ( opts.slidesWidthPercentage ) { left += '%'; } animate( $sldr, { 'left': left }, duration, null, easing ); // Get caption if ( opts.caption ) { var caption = $sldr.children().eq( _index ).data( dta( 'caption' ) ) + "toto"; $capt .text( caption ) [ ( caption.length > 0 ) ? 'show' : 'hide' ](); } stopVideo( $curSlide.filter( '.' + cls( 'video' ) ), 'pause' ); // callback event $wrpr.trigger( evt( 'sliding' ), [ _index, duration ] ); } } ) // Toggle UI .bind( evt( 'click' ), function( e ) { $wrpr.toggleClass( cls( 'hover' ) ); } ); /* BUTTONS */ setButtons( $prev, $next, _index, _total, opts ); _initButtons( $wrpr, { 'prev' : $prev, 'next' : $next, 'close' : $clse } ); $window.bind( evt( 'orientationchange' ), function() { if ( _opened ) { scaleButtons( [ $prev, $next, $clse ], $capt, 250 ); } } ); /* KEYPRESSES */ _initKeys( $wrpr, opts ); /* DRAGGING */ if ( opts.touch.drag ) { $.fn.TosRUs.dragSlide( $wrpr, opts.slides.visible, _total ); } /* PREVENT SCROLLING */ if ( _fixed ) { $window.bind( evt( 'scroll' ), function( e ) { if ( _opened ) { window.scrollTo( 0, _scrollPosition ); e.preventDefault(); e.stopImmediatePropagation(); } } ); } /* START */ var _opened = setOpened( true, $wrpr ); if ( _fixed ) { $wrpr .appendTo( $body ) .trigger( evt( 'close' ), [ true ] ); } else { $wrpr.appendTo( opts.wrapper.target ); if ( opts.show ) { _opened = setOpened( false, $wrpr ); $wrpr.trigger( evt( 'open' ), [ 0, true ] ); } else { $wrpr.trigger( evt( 'close' ), [ true ] ); } } return $wrpr; }; /* Public variables */ $.fn.TosRUs.isTouch = _isTouch; /* Public methods */ $.fn.TosRUs.dragSlide = function( $wrpr, _visible, _total ) { var $sldr = $wrpr.find( '> .' + cls( 'slider' ) ); var _distance = 0, _direction = false, _swiping = false; $wrpr .hammer() .on( evt( 'dragleft' ) + ' ' + evt( 'dragright' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); _distance = e.gesture.deltaX; _direction = e.gesture.direction; _swiping = false; if ( ( _direction == 'left' && $wrpr.data( dta( 'index' ) ) + _visible >= _total ) || ( _direction == 'right' && $wrpr.data( dta( 'index' ) ) == 0 ) ) { _distance /= 2.5; } $sldr.css( 'marginLeft', Math.round( _distance ) ); } ) .on( evt( 'swipeleft' ) + ' ' + evt( 'swiperight' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); _swiping = true; } ) .on( evt( 'dragend' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); var duration = _duration / 2; if ( _direction == 'left' || _direction == 'right' ) { var easing = null; if ( _swiping ) { var slides = _visible; easing = 'swipeOutTos'; } else { var slideWidth = $sldr.children().first().width(), slides = Math.floor( ( Math.abs( _distance ) + ( slideWidth / 2 ) ) / slideWidth ); } if ( slides > 0 ) { $wrpr.trigger( evt( _direction == 'left' ? 'next' : 'prev' ), [ slides, duration, easing ] ); } } animate( $sldr, { 'marginLeft': 0 }, duration, null, easing ); _direction = false; } ) .on( evt( 'pinch' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); } ); } $.fn.TosRUs.pinchZoom = function( $img ) { var _endScale = 1, _curScale = 1, _oldScale = 1, _newScale = 1; $img .hammer() .on( evt( 'pinch' ), function(e) { e.gesture.preventDefault(); e.stopPropagation(); _endScale = ( e.gesture.scale - _curScale ) * _oldScale; _curScale = e.gesture.scale; _newScale = restrictMinMax( _newScale + _endScale, 0.5, 4 ); $img.css({ 'transform': 'scale( ' + _newScale + ' )' }); } ) .on( evt( 'doubletap' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); _endScale = 0; _curScale = 1; _newScale = ( _oldScale > 1 ) ? 1 : 3; _oldScale = animateScale( $img, _oldScale, _newScale ); } ) .on( evt( 'release' ), function( e ) { e.gesture.preventDefault(); e.stopPropagation(); _endScale = 0; _curScale = 1; _oldScale = _newScale; // After scaled < 1, animate to 1 if ( _oldScale < 1 ) { _newScale = 1; _oldScale = animateScale( $img, _oldScale, _newScale ); } } ); } /* SWIPE EASING */ jQuery.extend( jQuery.easing, { swipeOutTos: function (x, t, b, c, d) { return c * Math.sin( t / d * ( Math.PI / 2 ) ) + b; } }); /* INIT FUNCTIONS */ function _initSlides( $tos, $anchors, $wrpr, $sldr, opts ) { if ( opts.slides.collect ) { _initSlidesFromAnchors( $tos, $anchors, $wrpr, $sldr, opts ); } else { _initSlidesFromContent( $tos, $anchors, $wrpr, $sldr, opts ); } var $slides = $sldr.children(); // CSS if ( opts.slides.css ) { $slides.css( opts.slides.css ); } var width = opts.slides.width; if ( opts.slidesWidthPercentage ) { width += '%'; } $slides.css( 'width', width ); return $slides; } function _initSlidesFromAnchors( $tos, $anchors, $wrpr, $sldr, opts ) { getZoomAnchors( $anchors, opts ).addClass( cls( 'zoom' ) ); $anchors .css( opts.anchors.css ) .each( function( index ) { var $anchor = $(this); // Create the slide var $slide = $('
') .data( dta( 'anchor' ), $anchor ) .data( dta( 'content' ), $anchor.attr( 'href' ) ) .appendTo( $sldr ); // Clicking an achor opens the slide $anchor .data( dta( 'slide' ), $slide ) .bind( evt( opts.anchors.event ), function( e ) { e.preventDefault(); $wrpr.trigger( evt( 'open' ), [ index ] ); } ); // Get caption if ( opts.caption ) { $slide.data( dta( 'caption' ), '' ); for ( var c = 0, l = opts.caption.length; c < l; c++ ) { var caption = $anchor.attr( opts.caption[ c ] ); if ( caption && caption.length ) { $slide.data( dta( 'caption' ), caption ); break; } } } } ); } function _initSlidesFromContent( $tos, $anchors, $wrpr, $sldr, opts ) { $tos.children().each( function( index ) { var $slide = $(this); $('
') .append( $slide ) .appendTo( $sldr ); $slide = $slide.parent(); $slide.data( dta( 'caption' ), '' ); var $content = $slide.children(), contenttype = 'html', videotype = 'youtube'; // IMAGE if ( $content.is( 'img' ) ) { contenttype = 'image'; } // (video) else if ( $content.is( 'iframe' ) && $content.attr( 'src' ) ) { var src = $content.attr( 'src' ).toLowerCase(); // YOUTUBE if ( src.indexOf( 'youtube.com/embed/' ) > -1 ) { contenttype = 'video'; } // VIMEO else if ( src.indexOf( 'vimeo.com/video/' ) > -1 ) { contenttype = 'video'; videotype = 'vimeo'; } } $slide.data( dta( 'contenttype' ), contenttype ); switch ( contenttype ) { // IMAGE case 'image': if ( opts.touch.zoom ) { $.fn.TosRUs.pinchZoom( $content ); } break; // HTML NODE case 'html': $content.wrap( '
' ); break; // VIDEO case 'video': $slide.data( dta( 'videotype' ), videotype ); _initResizeVideo( $slide, opts.video.ratio, opts.video.maxWidth, opts.video.maxHeight ); break; } } ); } function _initResizeVideo( $slide, ratio, maxWidth, maxHeight ) { var $video = $slide.children(); $slide .addClass( cls( 'video' ) ) .bind( evt( 'resizeVideo' ), function() { $video.removeAttr( 'style' ); var _w = $slide.width(), _h = $slide.height(); if ( maxWidth && _w > maxWidth ) { _w = maxWidth; } if ( maxHeight && _h > maxHeight ) { _h = maxHeight; } if ( _w / _h < ratio ) { _h = _w / ratio; } else { _w = _h * ratio; } $video.width( _w ); $video.height( _h ); } ); $window.bind( evt( 'resize' ), function( e ) { $slide.trigger( evt( 'resizeVideo' ) ); } ); } function _initButtons( $wrpr, btns ) { $.each( btns, function( index, value ) { if ( value ) { value.bind( evt( 'click' ), function( e ) { e.stopPropagation(); e.preventDefault(); $wrpr.trigger( evt( index ) ); } ); } } ); } function _initKeys( $wrpr, opts ) { if ( opts.prev.key || opts.next.key || opts.close.key ) { $(document).bind( evt( 'keyup' ), function( e ) { var k = e.keyCode; if ( k == 27 || k == 37 || k == 39 ) { if ( $wrpr.data( dta( 'opened' ) ) ) { if ( k == 27 && opts.close.key ) { e.preventDefault(); $wrpr.trigger( evt( 'close' ) ); } else if ( ( k == 37 && opts.prev.key ) || ( k == 39 && opts.next.key ) ) { e.preventDefault(); $wrpr.trigger( evt( k == 37 ? 'prev' : 'next' ) ); } } } } ); } } /* GENERAL FUNCTIONS */ function animate( $element, properties, direct, callback, easing ) { var duration = _duration; if ( $.isNumeric( direct ) ) { duration = direct; direct = false; } if ( direct ) { $element.css( properties ); } else { setTimeout( function() { $element.animate( properties, { duration: duration, complete: callback, easing: easing, queue: false } ); }, 1 ); } } function animateScale( $element, startScale, endScale ) { $('
').css('width', startScale).animate({ 'width': endScale }, { duration: _duration / 2, step: function( now ) { $element.css({ 'transform': 'scale(' + now + ')' }); } }); return endScale; } function restrictMinMax( val, min, max ) { if ( typeof min == 'number' && val < min ) { val = min; } if ( typeof max == 'number' && val > max ) { val = max; } return val; } function setIndex( _index, $wrpr ) { $wrpr.data( dta( 'index' ), _index ); return _index; } function setOpened( _opened, $wrpr ) { $wrpr.data( dta( 'opened' ), _opened ); return _opened; } function setButtons( $prev, $next, _index, _total, opts ) { if ( $prev ) { $prev[ ( ( _index < 1 ) ? 'add' : 'remove' ) + 'Class' ]( cls( 'disabled' ) ); } if ( $next ) { $next[ ( ( _index >= _total - opts.slides.visible ) ? 'add' : 'remove' ) + 'Class' ]( cls( 'disabled' ) ); } } function scaleButtons( $btns, $capt, timeout ) { if ( _viewScale ) { var scale = _viewScale.getScale(); if ( typeof scale != 'undefined' ) { scale = 1 / scale; var $buttons = $(); for ( var a = 0, l = $btns.length; a < l; a++ ) { if ( $btns[ a ] ) { $buttons = $buttons.add( $btns[ a ] ); } } setTimeout( function() { if ( $capt ) { $capt.css({ 'text-size-adjust': Math.ceil( 100 * restrictMinMax( scale, 1, 3 ) ) + '%' }); } $buttons.css({ 'transform': 'scale(' + restrictMinMax( scale, 1, 2 ) + ')' }); }, timeout || 1 ); } } } function loadContents( $sldr, _index, opts ) { // Preload current loadContent( $sldr, _index, _index + opts.slides.visible, opts ); // Preload prev + next setTimeout( function() { loadContent( $sldr, _index - opts.slides.visible, _index, opts ); // prev loadContent( $sldr, _index + opts.slides.visible, _index + ( opts.slides.visible * 2 ), opts ); // next }, 500 ); } function loadContent( $sldr, start, end, opts ) { $sldr.children().slice( start, end ).each( function() { var $slide = $(this); if ( $slide.children().length == 0 ) { var content = $slide.data( dta( 'content' ) ), orgContent = content, contenttype = false, videotype = 'youtube'; // IMAGES if ( $.inArray( content.toLowerCase().split( '.' ).pop().split( '?' )[ 0 ], [ 'jpg', 'jpe', 'jpeg', 'gif', 'png' ] ) > -1 ) { contenttype = 'image'; } // HTML NODE else if ( content.indexOf( '#' ) == 0 ) { content = $(content); contenttype = 'html'; } // YOUTUBE (video) else if ( content.toLowerCase().indexOf( 'youtube.com/watch' ) > -1 ) { content = content.split( '?v=' )[ 1 ].split( '&' )[ 0 ]; if ( opts.video.imageLink ) { content = 'http://img.youtube.com/vi/' + content + '/0.jpg'; contenttype = 'videolink'; } else { content = 'http://www.youtube.com/embed/' + content; contenttype = 'video'; } } // VIMEO (video) else if ( content.toLowerCase().indexOf( 'vimeo.com/' ) > -1 ) { content = 'http://player.vimeo.com/video/' + content.split( 'vimeo.com/' )[ 1 ].split( '?' )[ 0 ] + '?api=1'; contenttype = 'video'; videotype = 'vimeo'; } $slide.data( dta( 'contenttype' ), contenttype ); switch ( contenttype ) { // IMAGE case 'image': case 'videolink': $('') .bind( evt( 'load' ), function( e ) { e.stopPropagation(); $slide.removeClass( cls( 'loading' ) ); if ( contenttype == 'videolink' ) { $('') .appendTo( $slide ); } else { if ( opts.touch.zoom ) { $.fn.TosRUs.pinchZoom( $(this) ); } } } ) .appendTo( $slide ) .attr( 'src', content ); break; // HTML NODE case 'html': $slide.removeClass( cls( 'loading' ) ); $('
') .append( content ) .appendTo( $slide ); break; // VIDEO case 'video': $slide.data( dta( 'videotype' ), videotype ); var $anchor = $slide.data( dta( 'anchor' ) ); $('