/* JS Document */

/******************************

JhumkaGali custom.js

PART 1 - the theme script exactly as it shipped (sticky header, mobile menu,
         home slider, gallery slider, lightbox). Do not delete it: without it the
         homepage hero slider and the mobile menu stop working.
PART 2 - JhumkaGali additions (product card polish, filtered sections, product
         sliders, product detail gallery). Put any future custom JS in PART 2.

******************************/

/* ===== PART 1: theme script (unchanged) ===== */

/* JS Document */

/******************************

[Table of Contents]

1. Vars and Inits
2. Set Header
3. Init Menu
4. Init Home Slider
5. Init Gallery
6. Init Testimonials Slider
7. Init Lightbox


******************************/

$(document).ready(function()
{
	"use strict";

	/* 

	1. Vars and Inits

	*/

	var header = $('.header');
	var menuActive = false;
	var menu = $('.menu');
	var burger = $('.burger_container');

	setHeader();

	$(window).on('resize', function()
	{
		setHeader();
	});

	$(document).on('scroll', function()
	{
		setHeader();
	});

	initHomeSlider();
	initMenu();
	initGallery();
	initTestSlider();
	initLightbox();

	/* 

	2. Set Header

	*/

	function setHeader()
	{
		if($(window).scrollTop() > 100)
		{
			header.addClass('scrolled');
		}
		else
		{
			header.removeClass('scrolled');
		}
	}

	/* 

	3. Init Menu

	*/

	function initMenu()
	{
		if($('.menu').length)
		{
			var menu = $('.menu');
			if($('.burger_container').length)
			{
				burger.on('click', function()
				{
					if(menuActive)
					{
						closeMenu();
					}
					else
					{
						openMenu();

						$(document).one('click', function cls(e)
						{
							if($(e.target).hasClass('menu_mm'))
							{
								$(document).one('click', cls);
							}
							else
							{
								closeMenu();
							}
						});
					}
				});
			}
		}
	}

	function openMenu()
	{
		menu.addClass('active');
		menuActive = true;
	}

	function closeMenu()
	{
		menu.removeClass('active');
		menuActive = false;
	}

	/* 

	4. Init Home Slider

	*/

	function initHomeSlider()
	{
		if($('.home_slider').length)
		{
			var homeSlider = $('.home_slider');

			homeSlider.owlCarousel(
			{
				items:1,
				autoplay:true,
				loop:true,
				nav:false,
				smartSpeed:1200
			});

			// Custom Navigation
			if($('.home_slider_next').length)
			{
				var next = $('.home_slider_next');
				next.on('click', function()
				{
					homeSlider.trigger('next.owl.carousel');
				});
			}

			/* Custom dots events */
			if($('.home_slider_custom_dot').length)
			{
				$('.home_slider_custom_dot').on('click', function(ev)
				{	
					var dot = $(ev.target);
					$('.home_slider_custom_dot').removeClass('active');
					dot.addClass('active');
					homeSlider.trigger('to.owl.carousel', [$(this).index(), 300]);
				});
			}

			/* Change active class for dots when slide changes by nav or touch */
			homeSlider.on('changed.owl.carousel', function(event)
			{
				$('.home_slider_custom_dot').removeClass('active');
				$('.home_slider_custom_dots li').eq(event.page.index).addClass('active');
			});	
		}
	}

	/* 

	5. Init Gallery

	*/

	function initGallery()
	{
		if($('.gallery_slider').length)
		{
			var gallerySlider = $('.gallery_slider');
			gallerySlider.owlCarousel(
			{
				items:6,
				loop:true,
				margin:22,
				autoplay:true,
				nav:false,
				dots:false,
				responsive:
				{
					0:
					{
						margin:7,
						items:3
					},
					575:
					{
						margin:7,
						items:6
					},
					991:
					{
						margin:22,
						items:6
					}
				}
			});
		}
	}

	/* 

	6. Init Testimonials Slider

	*/

	function initTestSlider()
	{
		if($('.test_slider').length)
		{
			var testSlider = $('.test_slider');
			testSlider.owlCarousel(
			{
				items:1,
				loop:true,
				autoplay:true,
				smartSpeed:1200,
				nav:false,
				dots:false
			});
		}
	}

	/*

	7. Init Lightbox

	*/

	function initLightbox()
	{
		if($('.gallery_item').length)
		{
			$('.colorbox').colorbox(
			{
				rel:'colorbox',
				photo: true,
				maxWidth:'95%',
				maxHeight:'95%'
			});
		}
	}
});


/******************************

PART 2 - JhumkaGali additions

1. Helpers
2. Product card polish (alt text, lazy images, stray token text)
3. Filtered product sections
4. Product sliders
5. Product detail gallery
6. Init

Product sections are driven by data attributes on the Shopivana card container:
  data-jg-filter="jhumka"   keep only products whose name contains this
  data-jg-max-price="499"   keep only products up to this price
  data-jg-limit="12"        show at most this many
  data-jg-slider="4"        turn into a slider, this many per view on desktop
A wrapper marked data-jg-section stays hidden until this script has filtered it.

******************************/
/******************************

JhumkaGali - custom scripts

Single place for all custom JavaScript. The theme's own custom.js (header, menu,
home slider, gallery, lightbox) stays untouched; this file only adds to it.

[Table of Contents]

1. Helpers
2. Product card polish (alt text, lazy images, stray token text)
3. Filtered product sections
4. Product sliders
5. Product detail gallery
6. Init

How to use it from the HTML (Shopivana fills these containers itself):

  <div class="... jg_section" data-jg-section>
    <div class="row product-card products_container"
         data-product-card="product-card" data-category="*" data-count="250" data-sort="new-first"
         data-jg-filter="jhumka"      only keep products whose name contains this
         data-jg-max-price="499"      only keep products up to this price
         data-jg-limit="12"           show at most this many
         data-jg-slider="4">          turn into a slider, this many per view on desktop
    </div>
  </div>

A section marked data-jg-section stays hidden until this file has filtered it, so a
section never shows the wrong products if the script fails to load. A section whose
filter matches nothing hides itself.

******************************/

(function () {
	"use strict";

	/*

	1. Helpers

	*/

	var SLIDER_DEFAULTS = {
		loop: false,           // no clones: cloned cards would lose their add-to-cart handlers
		margin: 28,
		nav: true,
		dots: true,
		autoplay: false,
		navText: ['<span aria-hidden="true">&#8249;</span>', '<span aria-hidden="true">&#8250;</span>']
	};

	function whenReady(callback) {
		var waited = 0;
		(function poll() {
			if (window.jQuery && document.readyState !== 'loading') {
				callback(window.jQuery);
			} else if (waited < 20000) {
				waited += 100;
				window.setTimeout(poll, 100);
			}
		})();
	}

	function priceOf($item) {
		var text = $item.find('.product_price').first().text().replace(/[^0-9.]/g, '');
		var value = parseFloat(text);
		return isNaN(value) ? null : value;
	}

	function nameOf($item) {
		return $item.find('.product_name').first().text().trim();
	}

	/*

	2. Product card polish

	Shopivana's card template leaves the image alt empty; the product name is a better
	description for screen readers and search engines. Images also get lazy loading so
	long grids don't download everything at once.

	*/

	function polishCards($, $container) {
		$container.find('.product_image img').not('.product_image_large img').each(function () {
			var name = nameOf($(this).closest('.product'));
			if (name && !this.getAttribute('alt')) {
				this.setAttribute('alt', name);
			}
			if (!this.hasAttribute('loading')) {
				this.setAttribute('loading', 'lazy');
				this.setAttribute('decoding', 'async');
			}
		});
	}

	// A product with no value for a token can leave the token's bare name behind
	// ("product-category"), and the compare-at price repeats the selling price when there
	// is no discount. Both are cleared so cards never show stray text or a fake discount.
	function cleanCardText($, $container) {
		var unresolved = function (text) {
			return !text || /^\{{0,2}\s*(product|class|function)-[\w-]+\s*\}{0,2}$/.test(text);
		};
		var amount = function (text) {
			return String(text).replace(/[^0-9.]/g, '');
		};

		$container.find('.product_category, .product_sku, .product_short_description').each(function () {
			if (unresolved($(this).text().trim())) $(this).empty();
		});

		$container.find('.product_price_old').each(function () {
			var $old = $(this);
			var oldText = $old.text().trim();
			var selling = $old.parent().contents().filter(function () { return this.nodeType === 3; }).text();
			if (unresolved(oldText) || !amount(oldText) || amount(oldText) === amount(selling)) {
				$old.empty();
			}
		});
	}

	// Product descriptions are written a line per measurement ("Weight - 23 gm"), which HTML
	// collapses into one long line. Those lines are turned into a two-column table. Anything
	// that isn't a clean label/value list is left exactly as it is.
	function specTable($, $container) {
		var escape = function (text) {
			return $('<span>').text(text).html();
		};

		// "Weight - 30 gm / Length - 5 cm / Width - 3 cm", one measurement per line. Every
		// line has to read as a label and a value, or the text is ordinary prose and is left
		// exactly as it is. Values keep their units because everything after the separator is
		// taken verbatim. Returns 'empty', true (made a table) or false (left alone).
		var toTable = function ($paragraph) {
			if (!$paragraph.length) return 'empty';

			var text = $.trim($paragraph.text());
			// a product with no description can leave the token's bare name behind
			if (!text || text === 'product-description') return 'empty';

			var lines = text.split(/\r?\n/).map(function (line) { return $.trim(line); }).filter(Boolean);
			if (lines.length < 2) return false;

			var rows = [];
			for (var i = 0; i < lines.length; i++) {
				var pair = lines[i].match(/^([^-–:]{1,28}?)\s*[-–:]\s*(.+)$/);
				if (!pair) return false;
				rows.push('<tr><th scope="row">' + escape($.trim(pair[1])) + '</th><td>' + escape($.trim(pair[2])) + '</td></tr>');
			}
			// th scope="row" rather than a Specification/Value header row: for a label-and-value
			// list that is the structure a screen reader wants, and a header row would add the
			// visual weight this block is meant to avoid.
			$paragraph.replaceWith('<table class="product_specs"><tbody>' + rows.join('') + '</tbody></table>');
			return true;
		};

		var built = false;
		$container.find('.product_specs_block').each(function () {
			var $block = $(this);
			var result = toTable($block.find('p').first());
			if (result === 'empty') { $block.remove(); return; }   // no label over an empty block
			if (result === true) built = true;
		});

		// Short descriptions are prose on every product today, but if one is ever written as a
		// measurement list it becomes a table too - unless the block above already made one,
		// so the same measurements can never appear twice.
		if (!built) {
			$container.find('.product_text p').each(function () { toTable($(this)); });
		}
	}

	/*

	3. Filtered product sections

	Shopivana can only ask for a category, a count and its own sort order, so "Jhumkas"
	or "Under 499" are worked out here from the cards it rendered.

	*/

	/*
	   Shopivana's products page can only search by text, so a price link like "Under 499"
	   has nowhere to point. ?jg_max=499 (or ?jg_min=) on any page applies the same filter
	   to the cards that page renders. A container's own data-jg-* always wins, and with
	   this file missing the link simply opens the full Shop page.
	*/

	function urlPriceFilter() {
		var params;
		try {
			params = new window.URLSearchParams(window.location.search);
		} catch (e) {
			return null;
		}
		var max = parseFloat(params.get('jg_max'));
		var min = parseFloat(params.get('jg_min'));
		if (isNaN(max) && isNaN(min)) {
			return null;
		}
		return { max: max, min: min };
	}

	function filterCards($, $container) {
		var filter = String($container.data('jg-filter') || '').toLowerCase();
		var maxPrice = parseFloat($container.data('jg-max-price'));
		var minPrice = parseFloat($container.data('jg-min-price'));
		var limit = parseInt($container.data('jg-limit'), 10);
		var fromUrl = urlPriceFilter();
		var kept = 0;

		if (fromUrl) {
			if (isNaN(maxPrice)) { maxPrice = fromUrl.max; }
			if (isNaN(minPrice)) { minPrice = fromUrl.min; }
		}

		$container.children().each(function () {
			var $item = $(this);
			var price = priceOf($item);
			var keep = true;

			if (filter && nameOf($item).toLowerCase().indexOf(filter) === -1) {
				keep = false;
			}
			if (keep && !isNaN(maxPrice) && price !== null && price > maxPrice) {
				keep = false;
			}
			if (keep && !isNaN(minPrice) && price !== null && price < minPrice) {
				keep = false;
			}
			if (keep && !isNaN(limit) && kept >= limit) {
				keep = false;
			}

			if (keep) {
				kept++;
			} else {
				$item.remove();
			}
		});

		return kept;
	}

	/*

	4. Product sliders

	*/

	function buildSlider($, $container) {
		if (!$.fn.owlCarousel) {
			return false;
		}

		var perView = parseInt($container.data('jg-slider'), 10) || 4;
		var options = $.extend({}, SLIDER_DEFAULTS, {
			items: perView,
			responsive: {
				0: { items: 2, margin: 14 },
				768: { items: Math.min(3, perView), margin: 20 },
				992: { items: perView, margin: 28 }
			}
		});

		// "owl-carousel" is what owl.carousel.css hooks onto (it clips the track); the
		// theme's own sliders carry it in the markup, this one is built at runtime.
		$container.addClass('owl-carousel products_slider').owlCarousel(options);
		makeSliderAccessible($, $container);
		return true;
	}

	// Owl 2.2 builds its arrows and dots as plain <div>: give them button semantics
	// and keyboard support.
	function makeSliderAccessible($, $container) {
		$container.find('.owl-nav .owl-prev').attr({ role: 'button', tabindex: 0, 'aria-label': 'Previous products' });
		$container.find('.owl-nav .owl-next').attr({ role: 'button', tabindex: 0, 'aria-label': 'Next products' });
		$container.find('.owl-dots .owl-dot').each(function (index) {
			$(this).attr({ role: 'button', tabindex: 0, 'aria-label': 'Go to slide ' + (index + 1) });
		});
		$container.on('keydown', '.owl-prev, .owl-next, .owl-dot', function (event) {
			if (event.which === 13 || event.which === 32) {
				event.preventDefault();
				$(this).trigger('click');
			}
		});
	}

	/*

	5. Product detail gallery

	The theme's product_custom.js binds the thumbnails on page load, but Shopivana renders
	the card afterwards, so those clicks never worked. This is delegated, so it does.

	*/

	function initDetailGallery($) {
		$(document).on('click', '.product_image_thumbnails .product_image_thumbnail', function (event) {
			event.preventDefault();
			var $thumb = $(this);
			var source = $thumb.find('img').attr('src') || $thumb.attr('href');
			if (!source) return;
			$thumb.closest('.product_image_thumbnails').find('.product_image_thumbnail').removeClass('active');
			$thumb.addClass('active');
			$thumb.closest('.product_image').find('.product_image_large img').attr('src', source);
		});
	}

	/*

	6. Init

	Shopivana renders the cards over AJAX, so each container is watched and handled as
	soon as its cards appear (and again if Shopivana ever re-renders them).

	*/

	function handleContainer($, container) {
		var $container = $(container);

		if (!$container.children().length || $container.data('jg-done')) {
			return;
		}
		$container.data('jg-done', true);

		polishCards($, $container);
		cleanCardText($, $container);
		specTable($, $container);

		var $section = $container.closest('[data-jg-section]');
		var kept = filterCards($, $container);

		if ($section.length && !kept) {
			$section.remove();       // nothing matched - drop the section instead of showing an empty one
			return;
		}

		if ($container.data('jg-slider')) {
			buildSlider($, $container);
		}

		$section.addClass('jg_ready');
	}

	function watch($, container) {
		handleContainer($, container);

		if (!window.MutationObserver) {
			window.setTimeout(function () { handleContainer($, container); }, 3000);
			return;
		}

		var timer = null;
		new MutationObserver(function () {
			window.clearTimeout(timer);
			timer = window.setTimeout(function () { handleContainer($, container); }, 60);
		}).observe(container, { childList: true });
	}

	whenReady(function ($) {
		initDetailGallery($);
		$('.product-card[data-product-card], .products_container[data-product-card]').each(function () {
			watch($, this);
		});
	});

})();
