/**
 * Unfold Content component.
 *
 * @author Htmlstream
 * @version 1.0
 */
;
(function ($) {
	'use strict';
	
	$.HSCore.components.HSUnfold = {
		
		/**
		 * Base configuration of the component.
		 *
		 * @private
		 */
		_baseConfig: {
			unfoldEvent: 'click',
			unfoldType: 'simple',
			unfoldDuration: 300,
			unfoldEasing: 'linear',
			unfoldAnimationIn: 'fadeIn',
			unfoldAnimationOut: 'fadeOut',
			unfoldHideOnScroll: true,
			unfoldHideOnBlur: false,
			unfoldDelay: 350,
			unfoldOpenedElement: 'init',
			unfoldOverlay: false,
			afterOpen: function (invoker) {
			},
			beforeClose: function (invoker) {
			},
			afterClose: function (invoker) {
			}
		},
		
		/**
		 * Collection of all initialized items on the page.
		 *
		 * @private
		 */
		_pageCollection: $(),
		
		/**
		 * Initialization.
		 *
		 * @param {jQuery} collection
		 * @param {Object} config
		 *
		 * @public
		 * @return {jQuery}
		 */
		init: function (collection, config) {
			
			var self;
			
			if (!collection || !collection.length) return;
			
			self = this;
			
			var fieldsQty;
			
			collection.each(function (i, el) {
				
				var $this = $(el), itemConfig;
				
				if ($this.data('HSUnfold')) return;
				
				itemConfig = config && $.isPlainObject(config) ?
					$.extend(true, {}, self._baseConfig, config, $this.data()) :
					$.extend(true, {}, self._baseConfig, $this.data());
				
				switch (itemConfig.unfoldType) {
					
					case 'css-animation' :
						
						$this.data('HSUnfold', new UnfoldCSSAnimation($this, itemConfig));
						
						break;
					
					case 'jquery-slide' :
						
						$this.data('HSUnfold', new UnfoldJSlide($this, itemConfig));
						
						break;
					
					default :
						
						$this.data('HSUnfold', new UnfoldSimple($this, itemConfig));
					
				}
				
				self._pageCollection = self._pageCollection.add($this);
				self._bindEvents($this, itemConfig.unfoldEvent, itemConfig.unfoldDelay);
				var UnFold = $(el).data('HSUnfold');
				
				fieldsQty = $(UnFold.target).find('input, textarea').length;
				
				if ($(UnFold.target).find('[data-unfold-target]').length) {
					
					$this.addClass('target-of-invoker-has-unfolds');
					
				}
				
			});
			
			$(document).on('click touchstart', 'body', function (e) {
				
				if (e.target.id === self._baseConfig.unfoldOpenedElement) return;
				
				if ($(e.target).closest('#' + self._baseConfig.unfoldOpenedElement).length) return;
				
				self._pageCollection.each(function (i, el) {
					
					var windW = window.innerWidth,
						optIsMobileOnly = Boolean($(el).data('is-mobile-only'));
					
					if (!optIsMobileOnly) {
						
						$(el).data('HSUnfold').hide();
						
					} else if (optIsMobileOnly && windW < 769) {
						
						$(el).data('HSUnfold').hide();
						
					}
					
					if ($(el).data('HSUnfold').config.unfoldOverlay) {
						
						$(el).data('HSUnfold').config.unfoldDelay = setTimeout(function () {
							
							$('.' + $(el).data('HSUnfold').config.unfoldOverlay.className).fadeOut($(el).data('HSUnfold').config.unfoldOverlay.animationSpeed ? $(el).data('HSUnfold').config.unfoldOverlay.animationSpeed : 200);
							
						}, $(el).data('HSUnfold').config.unfoldDelay)
						
					}
					
					$(el).data('HSUnfold').config.beforeClose.call(self.target, self.element);
					
				});
				
			});
			
			$(window).on('scroll.HSUnfold', function () {
				
				self._pageCollection.each(function (i, el) {
					
					var UnFold = $(el).data('HSUnfold');
					
					if (UnFold.getOption('unfoldHideOnScroll') && fieldsQty === 0) {
						
						UnFold.hide();
						
					} else if (UnFold.getOption('unfoldHideOnScroll') && !(/iPhone|iPad|iPod/i.test(navigator.userAgent))) {
						
						UnFold.hide();
						
					}
					
				});
				
			});
			
			$(window).on('resize.HSUnfold', function () {
				
				if (self._resizeTimeOutId) clearTimeout(self._resizeTimeOutId);
				
				self._resizeTimeOutId = setTimeout(function () {
					
					self._pageCollection.each(function (i, el) {
						
						var UnFold = $(el).data('HSUnfold');
						
						UnFold.smartPosition(UnFold.target);
						
					});
					
				}, 50);
				
			});
			
			$(document).on('keydown.HSUnfold', function (e) {
				
				if ($('body').hasClass('u-unfold-opened')) {
					
					if (e.keyCode && e.keyCode === 38 || e.keyCode && e.keyCode === 40) {
						
						e.preventDefault();
						
					}
					
					if (e.keyCode && e.keyCode === 27) {
						
						self._pageCollection.each(function (i, el) {
							
							$(el).data('HSUnfold').hide();
							
						});
						
						$('body').removeClass('u-unfold-opened');
						
					}
					
				}
				
			});
			
			return collection;
			
		},
		
		/**
		 * Binds necessary events.
		 *
		 * @param {jQuery} $invoker
		 * @param {String} eventType
		 * @param {Number} delay
		 * @private
		 */
		_bindEvents: function ($invoker, eventType, delay) {
			
			var self = this,
				$unfold = $($invoker.data('unfold-target'));
			
			if (eventType === 'hover' && !_isTouch()) {
				
				$invoker.on('mouseenter.HSUnfold', function () {
					
					var $invoker = $(this),
						HSUnfold = $invoker.data('HSUnfold');
					
					if (!HSUnfold) return;
					
					if (HSUnfold.unfoldTimeOut) clearTimeout(HSUnfold.unfoldTimeOut);
					HSUnfold.show();
					$('body').addClass('u-unfold-opened');
					
				})
					.on('mouseleave.HSUnfold', function () {
						
						var $invoker = $(this),
							HSUnfold = $invoker.data('HSUnfold');
						
						if (!HSUnfold) return;
						
						HSUnfold.unfoldTimeOut = setTimeout(function () {
							
							HSUnfold.hide();
							$('body').removeClass('u-unfold-opened');
							
						}, delay);
						
					});
				
				if ($unfold.length) {
					
					$unfold.on('mouseenter.HSUnfold', function () {
						
						var HSUnfold = $invoker.data('HSUnfold');
						
						if (HSUnfold.unfoldTimeOut) clearTimeout(HSUnfold.unfoldTimeOut);
						HSUnfold.show();
						
					})
						.on('mouseleave.HSUnfold', function () {
							
							var HSUnfold = $invoker.data('HSUnfold');
							
							HSUnfold.unfoldTimeOut = setTimeout(function () {
								HSUnfold.hide();
							}, delay);
							
						});
				}
				
			} else {
				
				$invoker.on('click.HSUnfold', function (e) {
					
					var $curInvoker = $(this),
						$unfoldNotHasInnerUnfolds = $('[data-unfold-target].active:not(.target-of-invoker-has-unfolds)'),
						$unfoldHasInnerUnfold = $('[data-unfold-target].active.target-of-invoker-has-unfolds');
					
					self._baseConfig.unfoldOpenedElement = $curInvoker.data('HSUnfold').target[0].id;
					
					if (!$curInvoker.data('HSUnfold')) return;
					
					if (!$curInvoker.hasClass('target-of-invoker-has-unfolds')) {
						
						if ($unfoldNotHasInnerUnfolds.length) {
							
							$unfoldNotHasInnerUnfolds.data('HSUnfold').toggle();
							
						}
						
					} else {
						
						if ($unfoldHasInnerUnfold.length) {
							
							$unfoldHasInnerUnfold.data('HSUnfold').toggle();
							
						}
						
					}
					
					$curInvoker.data('HSUnfold').toggle();
					$('body').toggleClass('u-unfold-opened');
					
					e.stopPropagation();
					
					e.preventDefault();
					
				});
				
				if (Boolean($invoker.data('unfold-target-is-menu'))) {
					
					var $target = $($invoker.data('unfold-target')),
						$targetItems = $target.children();
					
					$targetItems.on('click', function () {
						
						$invoker.data('HSUnfold').toggle();
						
					});
					
				}
				
			}
			
		}
	};
	
	function _isTouch() {
		return 'ontouchstart' in window;
	}
	
	/**
	 * Abstract Unfold class.
	 *
	 * @param {jQuery} element
	 * @param {Object} config
	 * @abstract
	 */
	function AbstractUnfold(element, config) {
		
		var $self = this;
		
		if (!element.length) return false;
		
		$self.element = element;
		$self.config = config;
		
		$self.target = $($self.element.data('unfold-target'));
		
		$self.allInvokers = $('[data-unfold-target="' + $self.element.data('unfold-target') + '"]');
		
		$self.toggle = function () {
			if (!$self.target.length) return $self;
			
			var dataset = $self.element[0].dataset,
				overlay = dataset.unfoldOverlay ? JSON.parse(dataset.unfoldOverlay) : false;
			
			if ($self.defaultState) {
				
				if (dataset.unfoldOverlay) {
					
					if (!$('.' + overlay.className).length) {
						
						$('<div class="' + overlay.className + '" style="background-color: ' + overlay.background + '"></div>').prependTo('body');
						
					}
					
					if ($('.' + overlay.className).length) {
						
						$('.' + overlay.className).fadeIn(overlay.animationSpeed ? overlay.animationSpeed : 200);
						
					}
					
				}
				
				$self.show();
				
			} else {
				
				if ($('.' + overlay.className).length) {
					
					$self.config.unfoldTimeOut = setTimeout(function () {
						
						$('.' + overlay.className).fadeOut(overlay.animationSpeed ? overlay.animationSpeed : 200);
						
					}, $self.config.unfoldDelay)
					
				}
				
				$self.hide();
				
			}
			
			return $self;
		};
		
		this.smartPosition = function (target) {
			
			if (target.data('baseDirection')) {
				target.css(
					target.data('baseDirection').direction,
					target.data('baseDirection').value
				);
			}
			
			target.removeClass('u-unfold--reverse-y');
			
			var $w = $(window),
				styles = getComputedStyle(target.get(0)),
				direction = Math.abs(parseInt(styles.left, 10)) < 40 ? 'left' : 'right',
				targetOuterGeometry = target.offset();
			
			// horizontal axis
			if (direction === 'right') {
				
				if (!target.data('baseDirection')) target.data('baseDirection', {
					direction: 'right',
					value: parseInt(styles.right, 10)
				});
				
				if (targetOuterGeometry.left < 0) {
					
					target.css(
						'right',
						(parseInt(target.css('right'), 10) - (targetOuterGeometry.left - 10)) * -1
					);
					
				}
				
			} else {
				
				if (!target.data('baseDirection')) target.data('baseDirection', {
					direction: 'left',
					value: parseInt(styles.left, 10)
				});
				
				if (targetOuterGeometry.left + target.outerWidth() > $w.width()) {
					
					target.css(
						'left',
						(parseInt(target.css('left'), 10) - (targetOuterGeometry.left + target.outerWidth() + 10 - $w.width()))
					);
					
				}
				
			}
			
			// vertical axis
			if (targetOuterGeometry.top + target.outerHeight() - $w.scrollTop() > $w.height()) {
				
				target.addClass('u-unfold--reverse-y');
				
			}
			
		};
		
		this.getOption = function (option) {
			return this.config[option] ? this.config[option] : null;
		};
		
		return true;
	}
	
	
	/**
	 * UnfoldSimple constructor.
	 *
	 * @param {jQuery} element
	 * @param {Object} config
	 * @constructor
	 */
	function UnfoldSimple(element, config) {
		if (!AbstractUnfold.call(this, element, config)) return;
		
		Object.defineProperty(this, 'defaultState', {
			get: function () {
				return this.target.hasClass('u-unfold--hidden');
			}
		});
		
		this.target.addClass('u-unfold--simple');
		
		this.hide();
	}
	
	/**
	 * UnfoldCSSAnimation constructor.
	 *
	 * @param {jQuery} element
	 * @param {Object} config
	 * @constructor
	 */
	function UnfoldCSSAnimation(element, config) {
		if (!AbstractUnfold.call(this, element, config)) return;
		
		var self = this;
		
		this.target
			.addClass('u-unfold--css-animation u-unfold--hidden')
			.css('animation-duration', self.config.unfoldDuration + 'ms');
		
		Object.defineProperty(this, 'defaultState', {
			get: function () {
				return this.target.hasClass('u-unfold--hidden');
			}
		});
		
		if (this.target.length) {
			
			this.target.on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function (e) {
				
				if (self.target.hasClass(self.config.unfoldAnimationOut)) {
					
					self.target.removeClass(self.config.unfoldAnimationOut)
						.addClass('u-unfold--hidden');
					
					
					if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'false');
					
					self.config.afterClose.call(self.target, self.element);
				}
				
				if (self.target.hasClass(self.config.unfoldAnimationIn)) {
					
					if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'true');
					
					self.config.afterOpen.call(self.target, self.element);
				}
				
				e.preventDefault();
				e.stopPropagation();
			});
			
		}
	}
	
	/**
	 * UnfoldSlide constructor.
	 *
	 * @param {jQuery} element
	 * @param {Object} config
	 * @constructor
	 */
	function UnfoldJSlide(element, config) {
		if (!AbstractUnfold.call(this, element, config)) return;
		
		this.target.addClass('u-unfold--jquery-slide u-unfold--hidden').hide();
		
		Object.defineProperty(this, 'defaultState', {
			get: function () {
				return this.target.hasClass('u-unfold--hidden');
			}
		});
	}
	
	/**
	 * Shows Unfold.
	 *
	 * @public
	 * @return {UnfoldSimple}
	 */
	UnfoldSimple.prototype.show = function () {
		
		var activeEls = $(this)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').addClass('active');
		
		this.smartPosition(this.target);
		
		this.target.removeClass('u-unfold--hidden');
		if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'true');
		this.config.afterOpen.call(this.target, this.element);
		
		return this;
	};
	
	/**
	 * Hides Unfold.
	 *
	 * @public
	 * @return {UnfoldSimple}
	 */
	UnfoldSimple.prototype.hide = function () {
		
		var activeEls = $(this)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').removeClass('active');
		
		this.target.addClass('u-unfold--hidden');
		if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'false');
		this.config.afterClose.call(this.target, this.element);
		
		return this;
	};
	
	/**
	 * Shows Unfold.
	 *
	 * @public
	 * @return {UnfoldCSSAnimation}
	 */
	UnfoldCSSAnimation.prototype.show = function () {
		
		var self = this,
			activeEls = $(self)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').addClass('active');
		
		self.smartPosition(self.target);
		
		self.target.removeClass('u-unfold--hidden')
			.removeClass(self.config.unfoldAnimationOut);
		
		if (!self.config.unfoldOverlay) {
			
			self.target.addClass(self.config.unfoldAnimationIn);
			
		} else {
			
			setTimeout(function () {
				
				self.target.addClass(self.config.unfoldAnimationIn);
				
			});
			
		}
		
		self.config.afterOpen.call(self.target, self.element);
		
	};
	
	/**
	 * Hides Unfold.
	 *
	 * @public
	 * @return {UnfoldCSSAnimation}
	 */
	UnfoldCSSAnimation.prototype.hide = function () {
		
		var activeEls = $(this)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').removeClass('active');
		
		this.target.removeClass(this.config.unfoldAnimationIn)
			.addClass(this.config.unfoldAnimationOut);
		
	};
	
	/**
	 * Shows Unfold.
	 *
	 * @public
	 * @return {UnfoldJSlide}
	 */
	UnfoldJSlide.prototype.show = function () {
		
		var self = this;
		
		var activeEls = $(this)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').addClass('active');
		
		this.smartPosition(this.target);
		
		this.target.removeClass('u-unfold--hidden').stop().slideDown({
			duration: self.config.unfoldDuration,
			easing: self.config.unfoldEasing,
			complete: function () {
				self.config.afterOpen.call(self.target, self.element);
			}
		});
		
	};
	
	/**
	 * Hides Unfold.
	 *
	 * @public
	 * @return {UnfoldJSlide}
	 */
	UnfoldJSlide.prototype.hide = function () {
		
		var self = this;
		
		var activeEls = $(this)[0].config.unfoldTarget;
		
		$('[data-unfold-target="' + activeEls + '"]').removeClass('active');
		
		this.target.slideUp({
			duration: self.config.unfoldDuration,
			easing: self.config.unfoldEasing,
			complete: function () {
				self.config.afterClose.call(self.target, self.element);
				self.target.addClass('u-unfold--hidden');
			}
		});
		
	}
	
})(jQuery);
