;(function($) {

	var PPWooMiniCart = function($scope) {
		this.node      = $scope;
		this.wrap      = $scope.find('.pp-woo-mini-cart-container');
		this.behaviour = this.wrap.data('target');

		this._init();
	};

	PPWooMiniCart.prototype = {
		node: '',
		wrap: '',
		element: '',
		cart: '',
		hideTimer: null,
		behaviour: '',
		isPreview: false,

		_init: function() {
			this.element = this.node.find('.pp-woo-cart-contents');
			this.cart    = this.node.find('.pp-woo-mini-cart-wrap');

			this._bindEvents();
		},

		_bindEvents: function() {
			var self = this;

			if ( 'on-click' === this.behaviour ) {
				this.element.on('click', $.proxy( this._toggleCart, this ) );
			}

			if ( 'on-hover' === this.behaviour ) {
				// mouseenter/mouseleave on the container, not mouseover/mouseout on
				// the button. The bubbling pair fired every time the pointer crossed
				// onto one of the button's child spans, which made the panel flicker.
				// Binding to the container also covers the travel from button to panel.
				this.wrap.on('mouseenter', $.proxy( this._showCart, this ));
				this.wrap.on('mouseleave', $.proxy( this._hideCartLater, this ));

				// Keyboard equivalent. Without these the cart is unreachable in hover
				// mode for anyone not using a pointer.
				this.wrap.on('focusin', $.proxy( this._showCart, this ));
				this.wrap.on('focusout', $.proxy( this._hideCartLater, this ));
			}

			this.wrap.on('keydown', function(e) {
				if ( 'Escape' !== e.key && 27 !== e.keyCode ) {
					return;
				}

				if ( self.cart.hasClass('show-mini-cart') ) {
					self._hideCart();
					self.element.trigger('focus');
				}
			});

			if ( this.wrap.hasClass( 'pp-woo-mini-cart-preview' ) ) {
				setTimeout( function() {
					self._togglePreview();
				}, 200 );
			}
		},

		_showCart: function() {
			clearTimeout( this.hideTimer );
			this.cart.addClass('show-mini-cart');
			this.element.attr('aria-expanded', 'true');
		},

		_hideCart: function() {
			clearTimeout( this.hideTimer );
			this.cart.removeClass('show-mini-cart');
			this.element.attr('aria-expanded', 'false');
		},

		// Short grace period so crossing the gap between the button and the panel
		// does not close it mid-travel.
		_hideCartLater: function() {
			var self = this;

			clearTimeout( this.hideTimer );

			this.hideTimer = setTimeout( function() {
				self._hideCart();
			}, 200 );
		},

		_toggleCart: function(e) {
			e.preventDefault();

			if ( this.cart.hasClass('show-mini-cart') ) {
				this._hideCart();
			} else {
				this._showCart();
			}
		},

		_togglePreview: function() {
			if ( ! this.isPreview ) {
				this.isPreview = true;
				this._showCart();
			} else {
				this.isPreview = false;
				this._hideCart();
			}
		},

	};

	/**
	 * Close open mini carts when the click lands outside them.
	 *
	 * Bound once for the document rather than once per widget instance. The
	 * element_ready hook fires again on every editor re-render and there is no
	 * teardown hook here, so a per-instance handler would accumulate for the
	 * whole editing session and keep detached scopes alive.
	 */
	$(document).on('click.ppWooMiniCart', function(e) {
		if ( ! e.which ) {
			return;
		}

		var $open = $('.pp-woo-mini-cart-wrap.show-mini-cart');

		if ( ! $open.length ) {
			return;
		}

		$open.each(function() {
			var $cart      = $(this),
				$container = $cart.closest('.pp-woo-mini-cart-container');

			if ( $container.hasClass('pp-woo-mini-cart-preview') ) {
				return;
			}

			if ( $container.is(e.target) || $container.has(e.target).length ) {
				return;
			}

			$cart.removeClass('show-mini-cart');
			$container.find('.pp-woo-cart-contents').attr('aria-expanded', 'false');
		});
	});

	var WooMiniCartHandler = function ($scope) {
		new PPWooMiniCart( $scope );
	};

	$(window).on('elementor/frontend/init', function () {
		elementorFrontend.hooks.addAction( 'frontend/element_ready/pp-woo-mini-cart.default', WooMiniCartHandler );
	});

})( jQuery );
