/* ========================================================================
 * VC: carousel.js v0.4.5
 * Fork Bootstrap: carousel.js v3.0.0
 * http://twbs.github.com/bootstrap/javascript.html#carousel
 * ========================================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ======================================================================== */

;
(function ( $ ) {
	"use strict";

	// CAROUSEL CLASS DEFINITION
	// =========================

	function Carousel( element, options ) {
		this.$element = $( element )
		this.$indicators = this.$element.find( '.vc_carousel-indicators' )
		this.options = options
		this.paused =
			this.sliding =
				this.interval =
					this.$active =
						this.$items = null
		'hover' === this.options.pause && this.$element
			.on( 'mouseenter', $.proxy( this.pause, this ) )
			.on( 'mouseleave', $.proxy( this.cycle, this ) )
		this._build() // new
	}

	Carousel.DEFAULTS = {
		mode: 'horizontal'
		, partial: false
		, interval: 5000
		, pause: 'hover'
		, wrap: false
		, autoHeight: false
		, perView: 1
	}
	Carousel.prototype.cycle = function ( e ) {
		e || (this.paused = false)

		this.interval && clearInterval( this.interval )

		this.options.interval
		&& ! this.paused
		&& (this.interval = setInterval( $.proxy( this.next, this ), this.options.interval ))
		this.touch_start_position = 0;
		return this
	}
	Carousel.prototype.getActiveIndex = function () {
		this.$active = this.$element.find( '.vc_item.vc_active' )
		if ( ! this.$active.length ) {
			this.$active = this.$element.find( '.vc_item:first' ).addClass( 'vc_active' )
		}
		this.$items = this.$active.parent().children()
		return this.$items.index( this.$active )
	}
	Carousel.prototype.showHideControl = function ( index ) {
		if ( 'undefined' === typeof(index) ) {
			var index = this.getActiveIndex()
		}
		this.$left_control[ 0 === index ? 'hide' : 'show' ]()
		this.$right_control[ index === this.items_count - 1 ? 'hide' : 'show' ]()
	}
	Carousel.prototype.to = function ( pos ) {
		var that = this
		var activeIndex = this.getActiveIndex()

		if ( pos > (this.$items.length - 1) || 0 > pos ) {
			return
		}

		if ( this.sliding ) {
			return this.$element.one( 'slid', function () {
				that.to( pos )
			} )
		}
		if ( activeIndex == pos ) {
			return this.pause().cycle()
		}

		return this.slide( pos > activeIndex ? 'next' : 'prev', $( this.$items[ pos ] ) )
	}

	Carousel.prototype.pause = function ( e ) {
		e || (this.paused = true)

		if ( this.$element.find( '.vc_right.vc_carousel-control, .vc_left.vc_carousel-control' ).length && $.support.transition.end ) {
			this.$element.trigger( $.support.transition.end )
			this.cycle( true )
		}

		this.interval = clearInterval( this.interval )

		return this
	}

	Carousel.prototype.next = function () {
		if ( this.sliding ) {
			return
		}
		return this.slide( 'next' )
	}

	Carousel.prototype.prev = function () {
		if ( this.sliding ) {
			return
		}
		return this.slide( 'prev' )
	}

	Carousel.prototype.slide = function ( type, next ) {
		var $active = this.$element.find( '.vc_item.vc_active' )
		var $next = next || $active[ type ]()
		var isCycling = this.interval
		var direction = 'next' === type ? 'vc_left' : 'vc_right'
		var fallback = 'next' === type ? 'first' : 'last'
		var that = this
		if ( ! $next.length ) {
			if ( ! this.options.wrap ) {
				this.returnSwipedSlide()
				return
			}
			$next = this.$element.find( '.vc_item' )[ fallback ]()
		}

		this.sliding = true

		isCycling && this.pause()

		var e = $.Event( 'slide.vc.carousel', { relatedTarget: $next[ 0 ], direction: direction } )

		if ( $next.hasClass( 'vc_active' ) ) {
			return
		}

		if ( this.$indicators.length ) {
			this.$indicators.find( '.vc_active' ).removeClass( 'vc_active' )
			this.$indicators.find( '.vc_partial' ).removeClass( 'vc_partial' )
			this.$element.one( 'slid', function () {
				var index = that.getActiveIndex(),
					$nextIndicator = $( that.$indicators.children().slice( index,
						that.getActiveIndex() + that.options.perView ) )
				$nextIndicator && $nextIndicator.addClass( 'vc_active' )
				that.options.partial && $nextIndicator && (index + 1 < that.items_count ? $nextIndicator.last().next().addClass( 'vc_partial' ) : $nextIndicator.first().prev().addClass( 'vc_partial' ))
				! that.options.wrap && that.showHideControl( index )
			} )
		}
		this.current_index = $next.index()
		if ( this.current_index > this.items_count ) {
			this.current_index = 0
		} else if ( 0 > this.current_index ) {
			this.current_index = this.items_count - 1
		}
		if ( this.options.autoHeight ) {
			this.current_pos_value = - 1 * this._step * this.current_index
		} else {
			this.current_pos_value = - 1 * $next.position()[ this.animation_position ]
		}
		if ( this.options.partial && this.current_index >= this.items_count - 1 ) {
			this.current_pos_value += this._step * (1 - this.partial_part)
		}
		if ( $.support.transition && this.$element.hasClass( 'vc_slide' ) ) {
			this.$element.trigger( e )
			if ( e.isDefaultPrevented() ) {
				return
			}
			this.$slideline_inner
				.addClass( 'vc_transition' )
				.css( this.animation_position, this.current_pos_value + that.pos_units )
			if ( ! this.options.autoHeight ) {
				this.recalculateSlidelineHeight( $next.height(), true )
			}
			this.$slideline_inner.one( $.support.transition.end, function () {
				$next.addClass( 'vc_active' )
				$active.removeClass( 'vc_active' )
				that.$slideline_inner.removeClass( [
					type,
					'vc_transition'
				].join( ' ' ) )
				that.sliding = false
				that.removeSwipeAnimationSpeed()
				setTimeout( function () {
					that.$element.trigger( 'slid' )
				}, 0 )
			} ).emulateTransitionEnd( this.transition_speed )
		} else {
			this.$element.trigger( e )
			if ( e.isDefaultPrevented() ) {
				return
			}
			$active.removeClass( 'vc_active' )
			$next.addClass( 'vc_active' )
			this.sliding = false
			this.$slideline_inner.css( this.animation_position, this.current_pos_value + that.pos_units )
		}
		isCycling && this.cycle()
		return this
	}
	Carousel.prototype.setSwipeAnimationSpeed = function () {
		this.$slideline_inner.addClass( 'vc_swipe-transition' )
	}
	Carousel.prototype.removeSwipeAnimationSpeed = function () {
		this.$slideline_inner.removeClass( 'vc_swipe-transition' )

	}
	/**
	 * Velocity
	 * @param   {Number}    delta_time
	 * @param   {Number}    delta_x
	 * @param   {Number}    delta_y
	 * @returns {Object}    velocity
	 */
	Carousel.prototype.velocity = function ( time, x ) {
		return {
			x: Math.abs( x / time ) || 0
		}
	}
	Carousel.prototype.recalculateSlidelineHeight = function ( height, animate ) {
		if ( true === animate ) {
			this.$slideline.animate( { height: height } )
		} else {
			this.$slideline.height( height )
		}
	}
	/**
	 * Change layout size after resizing of window.
	 */
	Carousel.prototype.resizeAction = function () {
		var max_height = 0,
			new_slideline_height = 0
		if ( 'horizontal' === this.options.mode ) {
			this.el_effect_size = this.$element.width() * ( this.options.partial ? this.partial_part : 1 )
			this.$slideline.width( this.items_count * this.el_effect_size )
		}

		if ( this.options.autoHeight ) {
			this.$items.height( 'auto' )
			this.$items.each( function () {
				var item_height = $( this ).height()
				if ( item_height > max_height ) {
					max_height = item_height
				}
			} )
			this.$items.height( max_height )
		} else {
			this.recalculateSlidelineHeight( this.$active.height() )
		}
		if ( 'vertical' === this.options.mode ) {
			this._step = this.$active.height()
			new_slideline_height = this.$active.height() * this.options.perView * (this.options.partial ? (1 + 1 - this.partial_part) : 1)
			this.recalculateSlidelineHeight( new_slideline_height, false )
			this.$slideline_inner.css( { top: - 1 * this.$active.position().top } )
			this.el_effect_size = this._step
		}
	}
	Carousel.prototype.returnSwipedSlide = function () {
		var params = {}
		params[ this.animation_position ] = this.current_pos_value + this.pos_units
		this.$slideline_inner.animate( params )
	}
	Carousel.prototype._build = function () {
		var el = this.$element.get( 0 ),
			_touch_start_position = false,
			_touch_start_time = 0,
			_pos_before_touch = 0,
			_diff = 0,
			_moved = false,
			that = this,
			mode = this.options.mode
		this.getActiveIndex()

		this.el_width = 0
		this.items_count = this.$items.length

		this.$slideline = this.$element.find( '.vc_carousel-slideline' )
		this.$slideline_inner = this.$slideline.find( '> div' )
		this.slideline_inner = this.$slideline_inner.get( 0 )

		this.partial_part = 0.8
		this._slide_width = 0
		this.swipe_velocity = 0.7
		this.current_pos_value = 0
		this.current_index = 0 // TODO: default start position by options
		this.el_effect_size = 0
		this.transition_speed = 600

		this.$left_control = this.$element.find( '.vc_left.vc_carousel-control' )
		this.$right_control = this.$element.find( '.vc_right.vc_carousel-control' )

		// Enable autoHeight if partial
		if ( this.options.partial ) {
			this.options.autoHeight = true
		}
		// Add Css classes for perView > 1
		if ( 1 < this.options.perView ) {
			this.$element.addClass( 'vc_per-view-more vc_per-view-' + this.options.perView )
		}

		if ( 'horizontal' === mode ) {
			this.pos_units = '%'
			this._step = 100.00 / this.items_count / this.options.perView
			this.animation_position = 'left'
			this.$items.width( this._step + this.pos_units )
			this.touch_direction = 'pageX'
		} else {
			this.pos_units = 'px'
			this.animation_position = 'top'
			this.touch_direction = 'pageY'
			this.$element.addClass( 'vc_carousel_vertical' )
		}
		// Hide first control if this.current_index === 0
		! that.options.wrap && this.showHideControl()
		// Add partial css class if partial
		if ( this.options.partial ) {
			this.$element.addClass( 'vc_partial' )
		}
		// Set indicator
		if ( this.$indicators.length ) {
			var $active_indecators = that.$indicators.children()
				.slice( this.current_index, this.current_index + this.options.perView )
				.addClass( 'vc_active' )
			this.options.partial && $active_indecators.last().next().addClass( 'vc_partial' )
		}
		$( window ).resize( this.resizeAction.bind( this ) );
		this.resizeAction()

		el.addEventListener( "touchstart", function ( e ) {
			_touch_start_position = parseFloat( e[ that.touch_direction ] )
			_touch_start_time = e.timeStamp
			_pos_before_touch = that.$slideline_inner.position()[ that.animation_position ]
		}.bind( this ), false )
		el.addEventListener( 'touchmove', function ( e ) {
			_diff = parseFloat( e[ that.touch_direction ] ) - _touch_start_position
			_moved = 0 < Math.abs( _diff )
			if ( ! _moved ) {
				return true
			}
			e.preventDefault()
			that.slideline_inner.style[ that.animation_position ] = (_pos_before_touch + _diff) + 'px'
		}, false )
		el.addEventListener( 'touchend', function ( e ) {
			var time, part, velocity
			if ( _moved ) {
				time = (e.timeStamp - _touch_start_time) / 1000
				part = _diff / that.el_effect_size
				velocity = that.velocity( time, part )
				if ( (velocity.x > that.swipe_velocity && 0 > part) || part <= - 0.7 ) {
					that.setSwipeAnimationSpeed()
					that.next()
				} else if ( velocity.x > that.swipe_velocity || 0.7 <= part ) {
					that.setSwipeAnimationSpeed()
					that.prev()
				} else {
					that.returnSwipedSlide()
				}
				_moved = false
			}
		}, false )
		this.$element.addClass( 'vc_build' )
		return this
	}
	// CAROUSEL PLUGIN DEFINITION
	// ==========================

	var old = $.fn.carousel

	$.fn.carousel = function ( option, value ) {
		return this.each( function () {
			var $this = $( this )
			var data = $this.data( 'vc.carousel' )
			var options = $.extend( {}, Carousel.DEFAULTS, $this.data(), 'object' === typeof(option) && option )
			var action = 'string' === typeof(option) ? option : options.slide

			if ( ! data ) {
				$this.data( 'vc.carousel', (data = new Carousel( this, options )) )
			}
			if ( 'number' === typeof(option) ) {
				data.to( option )
			} else if ( action ) {
				data[ action ]( value )
			} else if ( options.interval ) {
				data.pause().cycle()
			}
		} )
	}

	$.fn.carousel.Constructor = Carousel

	// CAROUSEL NO CONFLICT
	// ====================

	$.fn.carousel.noConflict = function () {
		$.fn.carousel = old
		return this
	}

	// CAROUSEL DATA-API
	// =================

	$( document ).off( 'click.vc.carousel.data-api' ).on( 'click.vc.carousel.data-api',
		'[data-slide], [data-slide-to]',
		function ( e ) {
			var $this = $( this ), href
			var $target = $( $this.attr( 'data-target' ) || (href = $this.attr( 'href' )) && href.replace( /.*(?=#[^\s]+$)/,
				'' ) ) //strip for ie7
			var options = $.extend( {}, $target.data(), $this.data() )
			var slideIndex = $this.attr( 'data-slide-to' )
			if ( slideIndex ) {
				options.interval = false
			}
			$target.carousel( options )

			if ( slideIndex = $this.attr( 'data-slide-to' ) ) {
				$target.data( 'vc.carousel' ).to( slideIndex )
			}

			e.preventDefault()
		} )

	$( window ).on( 'load', function () {
		$( '[data-ride="vc_carousel"]' ).each( function () {
			var $carousel = $( this )
			$carousel.carousel( $carousel.data() )
		} )
	} )

})( window.jQuery );
