[Back]
import $ from 'jquery';

const { VPData } = window;
const { settingsPopupGallery } = VPData;
const templatesSupport = 'content' in document.createElement('template');

/*
 * Global Popup Gallery API.
 */
const VPPopupAPI = {
	vendor: false,

	vendors: [
		{
			vendor: 'youtube',
			embedUrl: 'https://www.youtube.com/embed/{{video_id}}?{{params}}',
			pattern:
				/(https?:\/\/)?(www.)?(youtube\.com|youtu\.be|youtube-nocookie\.com)\/(?:embed\/|shorts\/|v\/|watch\?v=|watch\?list=(.*)&v=|watch\?(.*[^&]&)v=)?((\w|-){11})(&list=(\w+)&?)?(.*)/,
			patternIndex: 6,
			params: {
				autoplay: 1,
				autohide: 1,
				fs: 1,
				rel: 0,
				hd: 1,
				wmode: 'transparent',
				enablejsapi: 1,
				html5: 1,
			},
			paramsIndex: 10,
			embedCallback(url, match) {
				let result = false;
				const vendorData = this;
				const videoId =
					match && match[vendorData.patternIndex]
						? match[vendorData.patternIndex]
						: false;

				if (videoId) {
					const isShorts = /\/shorts\//.test(url);

					const width = isShorts ? 476 : 1920;
					const height = isShorts ? 847 : 1080;

					result = VPPopupAPI.embedCallback(
						{
							...vendorData,
							width,
							height,
						},
						videoId,
						url,
						match
					);
				}

				return result;
			},
		},
		{
			vendor: 'vimeo',
			embedUrl: 'https://player.vimeo.com/video/{{video_id}}?{{params}}',
			pattern:
				/https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:$|\/|\?)(.*)/,
			patternIndex: 3,
			params: {
				autoplay: 1,
				hd: 1,
				show_title: 1,
				show_byline: 1,
				show_portrait: 0,
				fullscreen: 1,
			},
			paramsIndex: 4,
		},
	],

	init() {},
	open() {},
	close() {},

	/**
	 * Parse query parameters.
	 * Thanks to https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
	 *
	 * @param {string} query - query string.
	 *
	 * @return {string}
	 */
	getQueryStringParams(query) {
		return query
			? (/^[?#]/.test(query) ? query.slice(1) : query)
					.split('&')
					.reduce((params, param) => {
						const [key, value] = param.split('=');
						params[key] = value
							? decodeURIComponent(value.replace(/\+/g, ' '))
							: '';
						return params;
					}, {})
			: {};
	},

	/**
	 * Prepare params from parsed URL.
	 *
	 * @param {Object} match      - url match data.
	 * @param {Object} vendorData - vendor data.
	 *
	 * @return {string}
	 */
	prepareParams(match, vendorData) {
		let result = '';

		// Prepare default params.
		const params = vendorData.params || {};

		// Parse params from URL.
		if (vendorData.paramsIndex && match && match[vendorData.paramsIndex]) {
			const newParams = VPPopupAPI.getQueryStringParams(
				match[vendorData.paramsIndex]
			);

			if (newParams && typeof newParams === 'object') {
				Object.keys(newParams).forEach((key) => {
					if (key && newParams[key]) {
						params[key] = newParams[key];
					}
				});
			}
		}

		if (params && Object.keys(params).length) {
			Object.keys(params).forEach((key) => {
				if (key && params[key]) {
					if (result) {
						result += '&';
					}
					result += `${key}=${params[key]}`;
				}
			});
		}

		return result;
	},

	/**
	 * Prepare data for embed.
	 *
	 * @param {Object}           vendorData current video vendor data.
	 * @param {string}           videoId    parsed video ID.
	 * @param {string}           url        video URL provided.
	 * @param {Object | boolean} match      URL match data.
	 *
	 * @return {Object}
	 */
	embedCallback(vendorData, videoId, url, match = false) {
		let { embedUrl } = vendorData;
		embedUrl = embedUrl.replace(/{{video_id}}/g, videoId);
		embedUrl = embedUrl.replace(/{{video_url}}/g, url);
		embedUrl = embedUrl.replace(
			/{{video_url_encoded}}/g,
			encodeURIComponent(url)
		);
		embedUrl = embedUrl.replace(
			/{{params}}/g,
			match ? VPPopupAPI.prepareParams(match, vendorData) : ''
		);

		const width = vendorData.width || 1920;
		const height = vendorData.height || 1080;

		return {
			vendor: vendorData.vendor,
			id: videoId,
			embed: `<iframe width="${width}" height="${height}" src="${embedUrl}" scrolling="no" frameborder="0" allowTransparency="true" allow="accelerometer; autoplay; clipboard-write; fullscreen; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
			embedUrl,
			url,
			width,
			height,
		};
	},

	/**
	 * Parse video URL and return object with data
	 *
	 * @param {string} url    - video url.
	 * @param {string} url    - optional poster url.
	 *
	 * @param          poster
	 * @return {object|boolean} video data
	 */
	parseVideo(url, poster) {
		let result = false;

		VPPopupAPI.vendors.forEach((vendorData) => {
			if (!result) {
				const match = url.match(vendorData.pattern);
				const videoId =
					match && match[vendorData.patternIndex]
						? match[vendorData.patternIndex]
						: false;

				if (videoId) {
					// Custom embed callback.
					if (vendorData.embedCallback) {
						result = vendorData.embedCallback(url, match, poster);

						// Predefined embed callback.
					} else {
						result = VPPopupAPI.embedCallback(
							vendorData,
							videoId,
							url,
							match
						);
					}
				}
			}
		});

		// Unknown vendor.
		if (!result) {
			result = VPPopupAPI.embedCallback(
				{
					vendor: 'unknown',
					embedUrl: url,
				},
				url,
				url,
				false
			);
		}

		return result;
	},

	/**
	 * Parse gallery item popup data.
	 *
	 * @param {element} itemElement - gallery item
	 */
	parseItem(itemElement) {
		let result = false;

		const $dataElement =
			itemElement &&
			itemElement.querySelector('.vp-portfolio__item-popup');

		if ($dataElement) {
			result = {
				$dataElement,
				$content: $dataElement,
				data: $dataElement.dataset,
			};

			// Support for <template> tag.
			if (
				templatesSupport &&
				$dataElement.nodeName === 'TEMPLATE' &&
				$dataElement.content
			) {
				result.$content = $dataElement.content;
			}

			result.$title = result?.$content?.querySelector(
				'.vp-portfolio__item-popup-title'
			);
			result.$description = result?.$content?.querySelector(
				'.vp-portfolio__item-popup-description'
			);
		}

		return result;
	},

	/**
	 * Parse gallery
	 *
	 * @param {jQuery} $gallery - gallery element.
	 *
	 * @return {Array} gallery data
	 */
	parseGallery($gallery) {
		const items = [];
		let size;
		let item;
		let video;
		let videoData;

		// Find all gallery items
		// Skip Swiper slider duplicates.
		// Previously we also used the `:not(.swiper-slide-duplicate-active)`, but it contains a valid first slide.
		$gallery
			.find('.vp-portfolio__item-wrap:not(.swiper-slide-duplicate)')
			.each(function () {
				const itemData = VPPopupAPI.parseItem(this);

				if (itemData) {
					size = (
						itemData?.data?.vpPopupImgSize || '1920x1080'
					).split('x');
					video = itemData?.data?.vpPopupVideo;
					videoData = false;

					if (video) {
						videoData = VPPopupAPI.parseVideo(
							video,
							itemData?.data?.vpPopupPoster
						);
					}

					if (videoData) {
						item = {
							type: 'embed',
							el: this,
							poster: videoData.poster,
							src: videoData.embedUrl,
							embed: videoData.embed,
							width: videoData.width || 1920,
							height: videoData.height || 1080,
						};
					} else {
						// create slide object
						item = {
							type: 'image',
							el: this,
							src: itemData?.data?.vpPopupImg,
							srcset: itemData?.data?.vpPopupImgSrcset,
							width: parseInt(size[0], 10),
							height: parseInt(size[1], 10),
						};

						const srcSmall =
							itemData?.data?.vpPopupSmImg || item.src;
						if (srcSmall) {
							const smallSize = (
								itemData?.data?.vpPopupSmImgSize ||
								itemData?.data?.vpPopupImgSize ||
								'1920x1080'
							).split('x');

							item.srcSmall = srcSmall;
							item.srcSmallWidth = parseInt(smallSize[0], 10);
							item.srcSmallHeight = parseInt(smallSize[1], 10);
						}

						const srcMedium =
							itemData?.data?.vpPopupMdImg || item.src;
						if (srcMedium) {
							const mediumSize = (
								itemData?.data?.vpPopupMdImgSize ||
								itemData?.data?.vpPopupImgSize ||
								'1920x1080'
							).split('x');

							item.srcMedium = srcMedium;
							item.srcMediumWidth = parseInt(mediumSize[0], 10);
							item.srcMediumHeight = parseInt(mediumSize[1], 10);
						}
					}

					if (itemData?.$title || itemData?.$description) {
						item.caption =
							(itemData?.$title?.outerHTML || '') +
							(itemData?.$description?.outerHTML || '');
					}

					items.push(item);
				}
			});

		return items;
	},

	/**
	 * Try to focus gallery item link.
	 * Used when popup gallery is closed.
	 *
	 * @param {Object} data - data of the current item
	 */
	maybeFocusGalleryItem(data) {
		if (!settingsPopupGallery.restore_focus) {
			return;
		}

		// Focus native gallery item.
		if (data.linkEl) {
			$(data.linkEl).focus();

			// Focus Visual Portfolio gallery item.
		} else if (data.el) {
			$(data.el).find('.vp-portfolio__item-img > a').focus();
		}
	},
};

window.VPPopupAPI = VPPopupAPI;

// Extend VP class.
$(document).on('extendClass.vpf', (event, VP) => {
	if (event.namespace !== 'vpf') {
		return;
	}

	/**
	 * Init popup gallery
	 */
	VP.prototype.initPopupGallery = function () {
		const self = this;
		if (
			!self.options.itemsClickAction ||
			self.options.itemsClickAction === 'url'
		) {
			return;
		}

		// prevent on preview page
		if (self.isPreview()) {
			return;
		}

		// click action
		// `a.vp-portfolio__item-overlay` added as fallback for old templates, used in themes.
		self.$item.on(
			`click.vpf-uid-${self.uid}`,
			`
        .vp-portfolio__item a.vp-portfolio__item-meta,
        .vp-portfolio__item .vp-portfolio__item-img > a,
        .vp-portfolio__item .vp-portfolio__item-meta-title > a,
        .vp-portfolio__item a.vp-portfolio__item-overlay
      `,
			function (e) {
				if (e.isDefaultPrevented()) {
					return;
				}

				const $this = $(this);
				let $itemWrap = $this.closest('.vp-portfolio__item-wrap');

				// Use Swiper data-attribute to support slide duplicates.
				if (
					$itemWrap.hasClass('swiper-slide-duplicate') &&
					$itemWrap.attr('data-swiper-slide-index')
				) {
					$itemWrap = self.$item.find(
						`[data-swiper-slide-index="${$itemWrap.attr(
							'data-swiper-slide-index'
						)}"].swiper-slide:not(.swiper-slide-duplicate)`
					);
				}

				if (!$itemWrap.find('.vp-portfolio__item-popup').length) {
					return;
				}

				const items = VPPopupAPI.parseGallery(self.$item);
				let index = -1;

				// Get gallery item index.
				// We should check all items with gallery data to prevent
				// issue with items and custom URL used.
				items.forEach((item, idx) => {
					if (item.el === $itemWrap[0]) {
						index = idx;
					}
				});

				// Let's open popup once item index found.
				if (index !== -1) {
					e.preventDefault();
					VPPopupAPI.open(items, index, self);
				}
			}
		);
	};

	/**
	 * Destroy popup gallery
	 */
	VP.prototype.destroyPopupGallery = function () {
		const self = this;

		if (
			!self.options.itemsClickAction ||
			self.options.itemsClickAction === 'url'
		) {
			return;
		}

		self.$item.off(`click.vpf-uid-${self.uid}`);

		self.emitEvent('destroyPopupGallery');
	};
});

// Init.
$(document).on('init.vpf', (event, self) => {
	if (event.namespace !== 'vpf') {
		return;
	}

	self.initPopupGallery();
});

// Destroy.
$(document).on('destroy.vpf', (event, self) => {
	if (event.namespace !== 'vpf') {
		return;
	}

	self.destroyPopupGallery();
});

// Check if link is image.
function isLinkImage(link) {
	return /(.png|.jpg|.jpeg|.gif|.tiff|.tif|.jfif|.jpe|.svg|.bmp|.webp)$/.test(
		link.href.toLowerCase().split('?')[0].split('#')[0]
	);
}

// Parse image data from link.
function parseImgData(link) {
	const $link = $(link);
	let img = link.childNodes[0];
	let caption = $link.next('figcaption');

	// <noscript> tag used in plugins, that adds lazy loading
	if (img.nodeName === 'NOSCRIPT' && link.childNodes[1]) {
		img = link.childNodes[1];
	}

	if (!caption.length && $link.parent('.gallery-icon').length) {
		caption = $link.parent('.gallery-icon').next('figcaption');
	}

	caption = caption.html();

	if (caption) {
		caption = `<div class="vp-portfolio__item-popup-description">${caption}</div>`;
	}

	return {
		type: 'image',
		el: img,
		linkEl: link,
		src: link.href,
		caption,
	};
}

/* Popup for default WordPress images */
if (settingsPopupGallery.enable_on_wordpress_images) {
	$(document).on(
		'click',
		`
      .wp-block-image > a,
      .wp-block-image > figure > a,
      .wp-block-gallery .blocks-gallery-item > figure > a,
      .wp-block-gallery .wp-block-image > a,
      .wp-block-media-text > figure > a,
      .gallery .gallery-icon > a,
      figure.wp-caption > a,
      figure.tiled-gallery__item > a,
      p > a
    `,
		function (e) {
			if (e.isDefaultPrevented()) {
				return;
			}

			if (!this.childNodes.length) {
				return;
			}

			let imageNode = this.childNodes[0];

			// <noscript> tag used in plugins, that adds lazy loading
			if (imageNode.nodeName === 'NOSCRIPT' && this.childNodes[1]) {
				imageNode = this.childNodes[1];
			}

			// check if child node is <img> or <picture> tag.
			// <picture> tag used in plugins, that adds WebP support
			if (
				imageNode.nodeName !== 'IMG' &&
				imageNode.nodeName !== 'PICTURE'
			) {
				return;
			}

			// check if link is image.
			if (!isLinkImage(this)) {
				return;
			}

			e.preventDefault();

			const $this = $(this);
			const items = [];
			const currentImage = parseImgData(this);
			const $gallery = $this.closest(
				'.wp-block-gallery, .gallery, .tiled-gallery__gallery'
			);
			let activeIndex = 0;

			// Block gallery, WordPress default gallery, Jetpack gallery.
			if ($gallery.length) {
				const $galleryItems = $gallery.find(
					'.blocks-gallery-item > figure > a, .wp-block-image > a, .gallery-icon > a, figure.tiled-gallery__item > a'
				);
				let i = 0;

				$galleryItems.each(function () {
					// check if link is image.
					if (isLinkImage(this)) {
						if (this === currentImage.linkEl) {
							activeIndex = i;
						}

						items.push(parseImgData(this));

						i += 1;
					}
				});

				// WordPress gallery.
			} else {
				items.push(currentImage);
			}

			VPPopupAPI.open(items, activeIndex);
		}
	);
}