wp_back/wp-content/plugins/visual-portfolio/assets/js/popup-gallery.js
2024-05-20 15:37:46 +03:00

636 lines
14 KiB
JavaScript

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);
}
);
}