This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,13 @@
/**
* Elementor Preview Styles
*/
.visual-portfolio-elementor-preview {
position: relative;
overflow: hidden;
pointer-events: none;
iframe {
max-width: none;
min-height: 20px;
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 15 15">
<defs>
<style>
.cls-1 {
fill: #bbbbbb;
}
</style>
</defs>
<rect id="BR" class="cls-1" y="6" width="7" height="9"/>
<rect id="BL" class="cls-1" x="8" y="10" width="7" height="5"/>
<rect id="TR" class="cls-1" width="7" height="5"/>
<rect id="TL" class="cls-1" x="8" width="7" height="9"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

View File

@ -0,0 +1,11 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="50" fill="currentColor"/>
<path d="M57.5311 69.5L70.2439 33H58.4473L47 69.5H57.5311Z" fill="url(#paint0_linear)"/>
<path d="M42.7128 69.5L30 33H41.7966L53.2439 69.5H42.7128Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="64.5" y1="33" x2="33" y2="77" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 548 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 15 15">
<defs>
<style>
.cls-1 {
fill: #555d66;
}
</style>
</defs>
<rect id="BR" class="cls-1" y="6" width="7" height="9"/>
<rect id="BL" class="cls-1" x="8" y="10" width="7" height="5"/>
<rect id="TR" class="cls-1" width="7" height="5"/>
<rect id="TL" class="cls-1" x="8" width="7" height="9"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,11 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="50" fill="white"/>
<path d="M57.5311 69.5L70.2439 33H58.4473L47 69.5H57.5311Z" fill="url(#paint0_linear)"/>
<path d="M42.7128 69.5L30 33H41.7966L53.2439 69.5H42.7128Z" fill="black"/>
<defs>
<linearGradient id="paint0_linear" x1="64.5" y1="33" x2="33" y2="77" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -0,0 +1,52 @@
import $ from 'jquery';
const { ajaxurl, VPAdminVariables } = window;
// multiple select with AJAX search
$('select[name="vp_general[portfolio_archive_page]"]').select2({
ajax: {
url: ajaxurl, // AJAX URL is predefined in WordPress admin
dataType: 'json',
delay: 250, // delay in ms while typing when to perform a AJAX search
data(params) {
return {
q: params.term, // search query
selected: this[0].value,
nonce: VPAdminVariables.nonce,
action: 'vp_get_pages_list', // AJAX action for admin-ajax.php
};
},
processResults(ajaxData) {
const options = [];
const data = this.$element.select2('data');
let alreadyAddedID = false;
// add selected value.
if (data && data[0] && data[0].selected) {
alreadyAddedID = Number(data[0].id);
options.push({
id: alreadyAddedID,
text: data[0].text,
});
}
// parse new options.
if (ajaxData) {
// ajaxData is the array of arrays, and each of them contains ID and the Label of the option
$.each(ajaxData, (index, itemData) => {
if (!alreadyAddedID || alreadyAddedID !== itemData[0]) {
options.push({
id: itemData[0],
text: itemData[1],
});
}
});
}
return {
results: options,
};
},
cache: true,
},
});

View File

@ -0,0 +1,24 @@
import $ from 'jquery';
const { ajaxurl, VPAskReviewNotice } = window;
const $body = $('body');
$body.on('click', '.vpf-review-plugin-notice-dismiss', function (e) {
const $this = $(this);
const type = $this.attr('data-vpf-review-action');
// Don't prevent click on Yes link, as it is URL for rate.
if (type !== 'yes') {
e.preventDefault();
}
// Hide notice.
$this.closest('.notice').slideUp('slow');
// Save user answer in DB.
$.post(ajaxurl, {
action: 'vpf_dismiss_ask_review_notice',
type,
nonce: VPAskReviewNotice.nonce,
});
});

View File

@ -0,0 +1,84 @@
import $ from 'jquery';
import rafSchd from 'raf-schd';
import { throttle } from 'throttle-debounce';
const { elementorFrontend, VPAdminElementorVariables: variables } = window;
const $wnd = $(window);
$wnd.on('elementor/frontend/init', ($data) => {
if (!variables) {
return;
}
const { target: elementorWindow } = $data;
// add fake iframe width, so @media styles will work fine.
function maybeResizePreviews() {
const elementorWidth = elementorWindow
.jQuery(elementorWindow.document)
.width();
elementorWindow.jQuery
.find('.visual-portfolio-elementor-preview iframe')
.forEach((item) => {
const $this = $(item);
const parentWidth = $this.parent().width();
$this.css({
width: elementorWidth,
});
if (item.iFrameResizer) {
item.iFrameResizer.sendMessage({
name: 'resize',
width: parentWidth,
});
item.iFrameResizer.resize();
}
});
}
// window resize.
$wnd.on('resize', throttle(300, rafSchd(maybeResizePreviews)));
// added/changed widget.
elementorFrontend.hooks.addAction(
'frontend/element_ready/visual-portfolio.default',
($scope) => {
const $block = $($scope).find(
'.visual-portfolio-elementor-preview'
);
const $frame = $block.find('iframe');
const id = $block.attr('data-id');
const iframeURL = `${
variables.preview_url +
(variables.preview_url.split('?')[1] ? '&' : '?')
}vp_preview_frame=true&vp_preview_type=elementor&vp_preview_frame_id=${id}&vp_preview_nonce=${
variables.nonce
}`;
$frame.attr('src', iframeURL);
// resize iframe
if ($.fn.iFrameResize) {
$frame.iFrameResize({
onInit() {
maybeResizePreviews();
},
onMessage({ message }) {
// select current block on click message.
if (message === 'clicked') {
// Select current widget to display settings.
$frame
.closest('.elementor-element')
.find('.elementor-editor-element-edit')
.click();
window.focus();
}
},
});
}
}
);
});

View File

@ -0,0 +1,48 @@
const { tinymce, VPTinyMCEData } = window;
if (typeof VPTinyMCEData !== 'undefined' && VPTinyMCEData.layouts.length) {
const options = [
{
text: '',
value: '',
},
];
Object.keys(VPTinyMCEData.layouts).forEach((k) => {
options.push({
text: VPTinyMCEData.layouts[k].title,
value: VPTinyMCEData.layouts[k].id,
});
});
tinymce.create('tinymce.plugins.visual_portfolio', {
init(editor) {
editor.addButton('visual_portfolio', {
type: 'listbox',
title: VPTinyMCEData.plugin_name,
icon: 'visual-portfolio',
classes: 'visual-portfolio-btn',
onclick() {
if (this.menu) {
this.menu.$el.find('.mce-first').hide();
}
},
onselect() {
if (this.value()) {
editor.insertContent(
`[visual_portfolio id="${this.value()}"]`
);
}
this.value('');
},
values: options,
value: '',
});
},
});
tinymce.PluginManager.add(
'visual_portfolio',
tinymce.plugins.visual_portfolio
);
}

View File

@ -0,0 +1,106 @@
import $ from 'jquery';
import rafSchd from 'raf-schd';
import { debounce } from '@wordpress/compose';
const { ajaxurl, VPAdminVariables } = window;
const $body = $('body');
// select shortcode text in input
$body.on(
'focus',
'[name="vp_list_shortcode"], [name="vp_filter_shortcode"], [name="vp_sort_shortcode"]',
function () {
this.select();
}
);
$body.on('click', '.vp-onclick-selection', function () {
// eslint-disable-next-line @wordpress/no-global-get-selection
window.getSelection().selectAllChildren(this);
});
// fix the problem with Gutenberg shortcode transform (allowed only plain text pasted).
$body.on('copy cut', '.vp-onclick-selection', (e) => {
// eslint-disable-next-line @wordpress/no-global-get-selection
const copyText = window
.getSelection()
.toString()
.replace(/[\n\r]+/g, '');
e.originalEvent.clipboardData.setData('text/plain', copyText);
e.originalEvent.preventDefault();
});
// Post format metabox show/hide
const $videoMetabox = $('#vp_format_video');
const $videoFormatCheckbox = $('#post-format-video');
let isVideoFormat = null;
function toggleVideoMetabox(show) {
if (isVideoFormat === null || isVideoFormat !== show) {
isVideoFormat = show;
$videoMetabox[show ? 'show' : 'hide']();
}
}
if ($videoMetabox.length) {
if ($videoFormatCheckbox.length) {
toggleVideoMetabox($videoFormatCheckbox.is(':checked'));
$body.on('change', '[name=post_format]', () => {
toggleVideoMetabox($videoFormatCheckbox.is(':checked'));
});
}
}
let oembedAjax = null;
let runAjaxVideoOembed = function ($this) {
oembedAjax = $.ajax({
url: ajaxurl,
method: 'POST',
dataType: 'json',
data: {
action: 'vp_find_oembed',
q: $this.val(),
nonce: VPAdminVariables.nonce,
},
complete(data) {
const json = data.responseJSON;
if (json && typeof json.html !== 'undefined') {
$this.next('.vp-oembed-preview').html(json.html);
}
},
});
};
runAjaxVideoOembed = debounce(300, rafSchd(runAjaxVideoOembed));
$body.on('change input', '.vp-input[name="_vp_format_video_url"]', function () {
if (oembedAjax !== null) {
oembedAjax.abort();
}
const $this = $(this);
$this.next('.vp-oembed-preview').html('');
runAjaxVideoOembed($this);
});
/**
* When attempting to disable registration of portfolio post type,
* We inform the user of the possible consequences and provide them with the opportunity to cancel this operation.
*/
$body.on(
'change',
"input[name='vp_general[register_portfolio_post_type]']",
function () {
// Does some stuff and logs the event to the console
if (!$(this).is(':checked')) {
// eslint-disable-next-line no-restricted-globals, no-alert, no-undef
const confirmation = confirm(
"Are you sure you want to turn off the Portfolio custom post type and related taxonomies? Make sure you don't use this post type on your site, otherwise you might see errors on the frontend."
);
if (!confirmation) {
$(this).prop('checked', true);
}
}
}
);

View File

@ -0,0 +1,25 @@
import $ from 'jquery';
const { vc } = window;
$(() => {
// shortcode frontend editor
if (typeof vc !== 'undefined') {
// on shortcode add and update events
vc.events.on('shortcodes:add shortcodeView:updated', (e) => {
if (e.settings.base !== 'visual_portfolio') {
return;
}
const wnd = vc.$frame[0].contentWindow;
const jQframe = wnd ? wnd.jQuery : false;
if (jQframe) {
const $vp = jQframe(e.view.el).children('.vp-portfolio');
if ($vp.length && typeof $vp.vpf !== 'undefined') {
$vp.vpf();
}
}
});
}
});

View File

@ -0,0 +1,14 @@
/**
* Lazyload CSS Variables
*/
:root {
// Lazy.
--vp-lazyload-images__background: linear-gradient(270deg, rgba(140, 140, 140, 15%), rgba(140, 140, 140, 5%), rgba(140, 140, 140, 5%), rgba(140, 140, 140, 15%));
--vp-lazyload-images__background-size: 400% 100%;
--vp-lazyload-images__animation-duration: 7s;
// Transitions.
--vp-lazyload-transition-duration: 0.3s;
--vp-lazyload-transition-easing: ease-in-out;
}

View File

@ -0,0 +1,47 @@
/**
* Global CSS Variables
*/
:root {
// Main colors.
--vp-color-brand: #2540cc;
--vp-color-gray: #6c7781;
--vp-color-gray-darken: #4b4b4b;
--vp-color-gray-light: #e8e8e8;
--vp-color-gray-lighten: #f7f7f7;
--vp-color-red: #b71515;
// Radius.
--vp-border-radius: 5px;
// Gap.
--vp-items__gap: 0;
// Transitions.
--vp-transition-duration: 0.3s;
--vp-transition-easing: ease-in-out;
--vp-interactive__transition-duration: 0.2s;
--vp-interactive__transition-easing: ease-in-out;
}
// Vertical gap.
.vp-portfolio__items {
--vp-items__gap-vertical: var(--vp-items__gap);
}
.vp-portfolio {
--vp-wrap__min-height: 114px;
--vp-elements__gap: 20px;
// Images.
--vp-images__object-fit: cover;
--vp-images__object-position: 50% 50%;
}
.vp-spinner {
--vp-spinner__color: currentcolor;
--vp-spinner__size: 20px;
--vp-spinner__border-size: 2px;
--vp-spinner__speed: 0.3s;
--vp-spinner--background__color: var(--vp-spinner__color);
--vp-spinner--background__opacity: 0.3;
}

View File

@ -0,0 +1,17 @@
/**
* Popup Galleries CSS Variables
*/
.vp-pswp,
.vp-fancybox {
--vp-popup__z-index: 1500;
// Thumbnails.
--vp-popup--thumbnails__size: 160px;
--vp-popup--thumbnails__aspect-ratio: 10 / 7;
--vp-popup--thumbnails__background-color: #1e1e1e;
--vp-popup--thumbnails--items__border-color: var(--vp-color-brand);
--vp-popup--thumbnails--scrollbar__size: 7px;
--vp-popup--thumbnails--scrollbar-track__background-color: #1f1f1f;
--vp-popup--thumbnails--scrollbar-thumb__background-color: #424242;
--vp-popup--thumbnails--scrollbar-thumb__border-radius: 10px;
}

View File

@ -0,0 +1,38 @@
/**
* Slider CSS Variables
*/
.vp-portfolio {
// Arrows.
--vp-layout-slider--arrows__width: 2em;
--vp-layout-slider--arrows__height: 2em;
--vp-layout-slider--arrows__offset: 10px;
--vp-layout-slider--arrows__compensation: 10px;
--vp-layout-slider--arrows__color: var(--vp-color-gray);
--vp-layout-slider--arrows__background-color: #fff;
--vp-layout-slider--arrows__border-radius: 50%;
--vp-layout-slider--arrows__box-shadow: 0 0 7px 2px rgba(0, 0, 0, 4%);
--vp-layout-slider--arrows__opacity: 0.5;
--vp-layout-slider--arrows-hover__box-shadow: 0 2px 15px 2px rgba(0, 0, 0, 4%);
--vp-layout-slider--arrows-hover__opacity: 1;
// Bullets.
--vp-layout-slider--bullets__margin-top: 2em;
--vp-layout-slider--bullets__width: 0.4em;
--vp-layout-slider--bullets__height: 0.4em;
--vp-layout-slider--bullets__gap: 0.4em;
--vp-layout-slider--bullets__compensation: 4px;
--vp-layout-slider--bullets__background-color: currentcolor;
--vp-layout-slider--bullets__border-radius: 50%;
--vp-layout-slider--bullets__opacity: 0.2;
--vp-layout-slider--bullets-hover__opacity: 0.5;
--vp-layout-slider--bullets-active__opacity: 1;
// Thumbnails.
--vp-layout-slider--thumbnails__opacity: 0.5;
--vp-layout-slider--thumbnails-hover__opacity: 1;
--vp-layout-slider--thumbnails-active__opacity: 1;
// Transitions.
--vp-layout-slider__transition-duration: var(--vp-interactive__transition-duration);
--vp-layout-slider__transition-easing: var(--vp-interactive__transition-easing);
}

View File

@ -0,0 +1,10 @@
/*
* Visual Portfolio custom scrollbar.
*/
.vp-portfolio__custom-scrollbar {
--vp-custom-scrollbar__background-color: #888;
.simplebar-scrollbar::before {
background-color: var(--vp-custom-scrollbar__background-color);
}
}

View File

@ -0,0 +1,13 @@
/*
* Additional styles to fix Elementor page builder conflicts.
*/
// Gaps.
[data-vp-layout="grid"],
[data-vp-layout="masonry"],
[data-vp-layout="tiles"] {
.elementor & .vp-portfolio__items .vp-portfolio__item-wrap .vp-portfolio__item {
margin-top: var(--vp-items__gap-vertical);
margin-left: var(--vp-items__gap);
}
}

View File

@ -0,0 +1,47 @@
/*
* Visual Portfolio layout Grid.
*/
// Gaps.
[data-vp-layout="grid"] {
.vp-portfolio__items {
margin-top: calc(-1 * var(--vp-items__gap-vertical));
margin-left: calc(-1 * var(--vp-items__gap));
}
.vp-portfolio__item-wrap .vp-portfolio__item {
margin-top: var(--vp-items__gap-vertical);
margin-left: var(--vp-items__gap);
}
}
// Images.
[data-vp-grid-images-aspect-ratio*=":"] {
.vp-portfolio__item-img img,
.vp-portfolio__item-img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.vp-portfolio__item-img-wrap {
position: relative;
display: block;
overflow: hidden;
&::before {
display: block;
padding-top: 56%;
content: "";
}
}
.vp-portfolio__item-img img {
width: 100%;
height: 100%;
object-fit: var(--vp-images__object-fit);
object-position: var(--vp-images__object-position);
}
}

View File

@ -0,0 +1,10 @@
/*
* Visual Portfolio layout Justified.
*/
[data-vp-layout="justified"] {
.vp-portfolio__item-wrap {
top: 0;
left: 0;
float: left;
}
}

View File

@ -0,0 +1,47 @@
/*
* Visual Portfolio layout Masonry.
*/
// Gaps.
[data-vp-layout="masonry"] {
.vp-portfolio__items {
margin-top: calc(-1 * var(--vp-items__gap-vertical));
margin-left: calc(-1 * var(--vp-items__gap));
}
.vp-portfolio__item-wrap .vp-portfolio__item {
margin-top: var(--vp-items__gap-vertical);
margin-left: var(--vp-items__gap);
}
}
// Images.
[data-vp-masonry-images-aspect-ratio*=":"] {
.vp-portfolio__item-img img,
.vp-portfolio__item-img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.vp-portfolio__item-img-wrap {
position: relative;
display: block;
overflow: hidden;
&::before {
display: block;
padding-top: 56%;
content: "";
}
}
.vp-portfolio__item-img img {
width: 100%;
height: 100%;
object-fit: var(--vp-images__object-fit);
object-position: var(--vp-images__object-position);
}
}

View File

@ -0,0 +1,167 @@
@import "./variables-slider";
/*
* Visual Portfolio layout Slider.
*/
[data-vp-layout="slider"] {
// Initial styles before Swiper init.
.vp-portfolio__items-wrap:not(.swiper) .vp-portfolio__items,
.vp-portfolio__thumbnails-wrap:not(.swiper) .vp-portfolio__thumbnails {
position: relative;
box-sizing: content-box;
display: flex;
}
.vp-portfolio__items-wrap:not(.swiper) .vp-portfolio__items .vp-portfolio__item-wrap,
.vp-portfolio__thumbnails-wrap:not(.swiper) .vp-portfolio__thumbnails .vp-portfolio__thumbnail-wrap {
width: calc(100% / var(--vp-layout-slider__initial-slides-per-view, 8));
min-width: calc(100% / var(--vp-layout-slider__initial-slides-per-view, 8));
}
@for $i from 1 through 8 {
&[data-vp-slider-slides-per-view="#{$i}"] .vp-portfolio__items-wrap:not(.swiper),
&[data-vp-slider-thumbnails-per-view="#{$i}"] .vp-portfolio__thumbnails-wrap:not(.swiper) {
--vp-layout-slider__initial-slides-per-view: #{$i};
}
}
.vp-portfolio__thumbnail-img-wrap {
position: relative;
display: block;
overflow: hidden;
}
.vp-portfolio__item-img-wrap::before,
.vp-portfolio__thumbnail-img-wrap::before {
display: block;
content: "";
}
.vp-portfolio__item-img img,
.vp-portfolio__thumbnail-img img {
object-fit: var(--vp-images__object-fit);
object-position: var(--vp-images__object-position);
}
// arrows
.vp-portfolio__items-arrow {
position: absolute;
top: 50%;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
width: var(--vp-layout-slider--arrows__width);
height: var(--vp-layout-slider--arrows__height);
margin-top: calc(-1 * var(--vp-layout-slider--arrows__height) / 2);
color: var(--vp-layout-slider--arrows__color);
cursor: pointer;
background-color: var(--vp-layout-slider--arrows__background-color);
border-radius: var(--vp-layout-slider--arrows__border-radius);
box-shadow: var(--vp-layout-slider--arrows__box-shadow);
opacity: var(--vp-layout-slider--arrows__opacity);
transition: var(--vp-layout-slider__transition-duration) opacity var(--vp-layout-slider__transition-easing), var(--vp-layout-slider__transition-duration) box-shadow var(--vp-layout-slider__transition-easing);
// additional element to make the buttons clickable also in outside.
&::after {
position: absolute;
top: calc(-1 * var(--vp-layout-slider--arrows__compensation));
right: calc(-1 * var(--vp-layout-slider--arrows__compensation));
bottom: calc(-1 * var(--vp-layout-slider--arrows__compensation));
left: calc(-1 * var(--vp-layout-slider--arrows__compensation));
display: block;
content: "";
}
&:hover {
box-shadow: var(--vp-layout-slider--arrows-hover__box-shadow);
opacity: var(--vp-layout-slider--arrows-hover__opacity);
}
// RTL.
@if variable-exists(rtl) and $rtl {
svg {
transform: scaleX(-1);
}
}
}
.vp-portfolio__items-arrow-prev {
left: var(--vp-layout-slider--arrows__offset);
}
.vp-portfolio__items-arrow-next {
right: var(--vp-layout-slider--arrows__offset);
}
// bullets
&[data-vp-slider-bullets="true"] .vp-portfolio__items-wrap {
padding-bottom: var(--vp-layout-slider--bullets__margin-top);
.vp-portfolio__items-arrow {
transform: translateY(calc(var(--vp-layout-slider--bullets__margin-top) / -2));
}
}
.vp-portfolio__items-bullets {
position: absolute;
bottom: 0;
z-index: 1;
text-align: center;
// Fixes Swiper default font-size
&.swiper-pagination-bullets-dynamic {
font-size: 1em;
}
> .swiper-pagination-bullet {
position: relative;
width: var(--vp-layout-slider--bullets__width);
height: var(--vp-layout-slider--bullets__height);
margin: 0 calc(var(--vp-layout-slider--bullets__gap) / 2);
cursor: pointer;
background-color: var(--vp-layout-slider--bullets__background-color);
border-radius: var(--vp-layout-slider--bullets__border-radius);
opacity: var(--vp-layout-slider--bullets__opacity);
transition: var(--vp-layout-slider__transition-duration) opacity var(--vp-layout-slider__transition-easing);
&:hover,
&:focus {
opacity: var(--vp-layout-slider--bullets-hover__opacity);
}
&.swiper-pagination-bullet-active {
opacity: var(--vp-layout-slider--bullets-active__opacity);
}
// additional element to make the buttons clickable also in outside.
&::after {
position: absolute;
top: calc(-1 * var(--vp-layout-slider--bullets__compensation));
right: calc(-1 * var(--vp-layout-slider--bullets__compensation));
bottom: calc(-1 * var(--vp-layout-slider--bullets__compensation));
left: calc(-1 * var(--vp-layout-slider--bullets__compensation));
display: block;
content: "";
}
}
}
// thumbnails
.vp-portfolio__thumbnails-wrap {
.vp-portfolio__thumbnail-wrap {
cursor: pointer;
opacity: var(--vp-layout-slider--thumbnails__opacity);
transition: var(--vp-layout-slider__transition-duration) opacity;
&:hover,
&:focus {
opacity: var(--vp-layout-slider--thumbnails-hover__opacity);
}
&.swiper-slide-thumb-active {
opacity: var(--vp-layout-slider--thumbnails-active__opacity);
}
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Visual Portfolio layout Tiles.
*/
[data-vp-layout="tiles"] {
// Gaps.
.vp-portfolio__items,
.vp-portfolio__item-wrap .vp-portfolio__item-img-wrap {
margin-top: calc(-1 * var(--vp-items__gap-vertical));
margin-left: calc(-1 * var(--vp-items__gap));
}
.vp-portfolio__item-wrap .vp-portfolio__item {
margin-top: var(--vp-items__gap-vertical);
margin-left: var(--vp-items__gap);
}
// Images
.vp-portfolio__item-img img,
.vp-portfolio__item-img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.vp-portfolio__item-img {
top: var(--vp-items__gap-vertical);
left: var(--vp-items__gap);
}
.vp-portfolio__item-img-wrap {
position: relative;
display: block;
overflow: hidden;
&::before {
display: block;
padding-top: 56%;
content: "";
}
}
.vp-portfolio__item-img img {
width: 100%;
height: 100%;
object-fit: var(--vp-images__object-fit);
object-position: var(--vp-images__object-position);
}
}

View File

@ -0,0 +1,32 @@
/*
* Visual Portfolio Lazyload Images fallback for old browsers
* which does not support CSS :has()
*/
:is(.vp-portfolio__item-img, .vp-portfolio__thumbnail-img) {
// Extra check for lazy loading class on the inner image
// to make sure image will be lazy loaded by Visual Portfolio.
&:is(.vp-has-lazyload, .vp-has-lazyloading, .vp-has-lazyloaded)::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
// Fixed possible bug with inaccessible links on images in the Classic style.
pointer-events: none;
visibility: visible;
content: "";
background-image: var(--vp-lazyload-images__background);
background-size: var(--vp-lazyload-images__background-size);
opacity: 1;
transition: var(--vp-lazyload-transition-duration) var(--vp-lazyload-transition-duration) opacity, var(--vp-lazyload-transition-duration) var(--vp-lazyload-transition-duration) visibility;
}
&:is(.vp-has-lazyloading)::before {
animation: vp-lazyload-placeholder var(--vp-lazyload-images__animation-duration) linear infinite;
}
&:is(.vp-has-lazyloaded)::before {
visibility: hidden;
opacity: 0;
}
}

View File

@ -0,0 +1,59 @@
@import "./variables-lazyload";
/*
* Visual Portfolio Lazyload Images
*
* - :first-of-type is used to prevent conflicts with hover image (Pro feature)
*/
// LazyLoad image
img.vp-lazyload,
img.vp-lazyloaded,
img.vp-lazypreload,
img.vp-lazyloading {
opacity: 0;
transition: var(--vp-lazyload-transition-duration) opacity;
}
img.vp-lazyloaded {
opacity: 1;
}
:is(.vp-portfolio__item-img, .vp-portfolio__thumbnail-img) {
// Extra check for lazy loading class on the inner image
// to make sure image will be lazy loaded by Visual Portfolio.
&:has(img:first-of-type:is(.vp-lazyload, .vp-lazyloading, .vp-lazyloaded))::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
// Fixed possible bug with inaccessible links on images in the Classic style.
pointer-events: none;
visibility: visible;
content: "";
background-image: var(--vp-lazyload-images__background);
background-size: var(--vp-lazyload-images__background-size);
opacity: 1;
transition: var(--vp-lazyload-transition-duration) var(--vp-lazyload-transition-duration) opacity, var(--vp-lazyload-transition-duration) var(--vp-lazyload-transition-duration) visibility;
}
&:has(img:first-of-type.vp-lazyloading)::before {
animation: vp-lazyload-placeholder var(--vp-lazyload-images__animation-duration) ease-in-out infinite;
}
&:has(img:first-of-type.vp-lazyloaded)::before {
visibility: hidden;
opacity: 0;
}
}
@keyframes vp-lazyload-placeholder {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}

View File

@ -0,0 +1,306 @@
@import "./variables-main";
/*
* Visual Portfolio main style.
*/
.vp-portfolio {
position: relative;
box-sizing: border-box;
min-height: var(--vp-wrap__min-height);
overflow-wrap: break-word;
*,
*::before,
*::after {
box-sizing: inherit;
}
// Fixes Swiper box-sizing conflict.
// https://github.com/nk-crew/visual-portfolio/issues/147
.swiper-wrapper {
box-sizing: inherit;
}
}
.vp-portfolio__items {
transition: var(--vp-transition-duration) height var(--vp-transition-easing), var(--vp-transition-duration) transform var(--vp-transition-easing);
}
.vp-portfolio::after,
.vp-portfolio__items::after {
display: block;
clear: both;
content: "";
}
.vp-portfolio__items-wrap,
.vp-portfolio__thumbnails-wrap,
.vp-portfolio__filter-wrap,
.vp-portfolio__sort-wrap,
.vp-portfolio__pagination-wrap,
.vp-portfolio__item {
position: relative;
overflow: hidden;
}
.vp-portfolio__items-wrap,
.vp-portfolio__thumbnails-wrap,
.vp-portfolio__layout-elements {
margin-bottom: var(--vp-elements__gap);
visibility: hidden;
opacity: 0;
transition: var(--vp-transition-duration) opacity, var(--vp-transition-duration) visibility;
}
.vp-portfolio > :last-child {
margin-bottom: 0;
}
.vp-portfolio__item-wrap {
position: relative;
float: left;
width: 33.333%;
}
// icons.
.vp-svg-icon {
display: inline-block;
width: 1em;
height: 1em;
overflow: visible;
font-size: inherit;
vertical-align: -0.125em;
}
// screen readers only.
.vp-screen-reader-text {
position: absolute !important;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
word-break: normal;
word-wrap: normal !important;
border: 0;
&:focus {
top: 5px;
right: 5px;
z-index: 100000;
display: block;
width: auto;
height: auto;
padding: 15px 23px 14px;
clip: auto !important;
clip-path: none;
font-size: 14px;
font-size: 0.875rem;
font-weight: 700;
line-height: normal;
color: var(--vp-color-brand);
text-decoration: none;
background-color: #f1f1f1;
border-radius: 3px;
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 60%);
}
}
// fix for default themes styles.
[data-vp-layout]:not([data-vp-layout="slider"]) .vp-portfolio__item-wrap {
padding: 0 !important;
margin: 0 !important;
}
[data-vp-layout="slider"] .vp-portfolio__item-wrap {
// In some plugins there is a style with `display: flex`, which breaks our items.
display: block;
float: none;
padding: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
margin-left: 0 !important;
}
.vp-portfolio__item .vp-portfolio__item-img img,
.vp-portfolio__item .vp-portfolio__item-img a,
.vp-portfolio__item .vp-portfolio__thumbnail-img img {
display: block;
width: 100%;
height: auto;
}
.vp-portfolio__item-img,
.vp-portfolio__thumbnail-img {
position: relative;
}
// Fix conflict with such theme styles:
// a { position: relative; }
// https://wordpress.org/support/topic/gallery-images-grey/
.vp-portfolio__item .vp-portfolio__item-img a {
position: unset;
}
// layout elements
.vp-portfolio__layout-elements {
display: flex;
flex-wrap: wrap;
gap: var(--vp-elements__gap);
&-align-left {
justify-content: flex-start;
}
&-align-center {
justify-content: center;
}
&-align-right {
justify-content: flex-end;
}
&-align-between {
justify-content: space-between;
}
}
// Fix custom theme styles for figures
.vp-portfolio figure.vp-portfolio__item {
display: block;
margin: 0;
}
[class^="wp-block-"]:not(.wp-block-gallery) figcaption.vp-portfolio__item-meta,
.vp-portfolio figcaption.vp-portfolio__item-meta {
margin-bottom: 0;
font-style: inherit;
}
// Preloader
.vp-portfolio__preloader-wrap {
visibility: visible;
opacity: 1;
transition: var(--vp-transition-duration) opacity, var(--vp-transition-duration) visibility;
}
.vp-portfolio__preloader {
position: absolute;
left: 50%;
width: 20px;
height: 20px;
margin-top: 45px;
margin-left: -10px;
svg,
img {
display: block;
width: 100%;
height: 100%;
border-radius: 20px;
}
&::after {
position: absolute;
top: -2px;
left: -2px;
display: block;
width: 24px;
height: 24px;
text-indent: -9999em;
content: "";
border: 1px solid rgba(#000, 0.2);
border-left: 1px solid #000;
border-radius: 50%;
animation: vp-preloader-spinner 0.3s infinite linear;
}
}
@keyframes vp-preloader-spinner {
100% {
transform: rotate(360deg);
}
}
// On loaded portfolio
.vp-portfolio.vp-portfolio__ready {
min-height: initial;
.vp-portfolio__items-wrap,
.vp-portfolio__thumbnails-wrap,
.vp-portfolio__layout-elements {
visibility: visible;
opacity: 1;
}
.vp-portfolio__preloader-wrap {
visibility: hidden;
opacity: 0;
.vp-portfolio__preloader {
animation: none;
}
}
}
.vp-portfolio__layout-elements__ready {
visibility: visible;
opacity: 1;
}
.vp-single-filter.vp-single-filter__ready {
.vp-portfolio__filter-wrap {
visibility: visible;
opacity: 1;
}
}
.vp-single-sort.vp-single-sort__ready {
.vp-portfolio__sort-wrap {
visibility: visible;
opacity: 1;
}
}
// Loading
.vp-portfolio.vp-portfolio__loading .vp-portfolio__layout-elements {
opacity: 0.5;
}
// Popup Galleries
.vp-portfolio__item-popup {
display: none;
}
// loading spinner.
.vp-spinner {
position: relative;
display: block;
width: var(--vp-spinner__size);
height: var(--vp-spinner__size);
text-indent: -9999em;
border: var(--vp-spinner__border-size) solid transparent;
border-left: var(--vp-spinner__border-size) solid var(--vp-spinner__color);
border-radius: 50%;
animation: vp-spinner var(--vp-spinner__speed) infinite linear;
&::after {
position: absolute;
top: calc(-1 * var(--vp-spinner__border-size));
right: calc(-1 * var(--vp-spinner__border-size));
bottom: calc(-1 * var(--vp-spinner__border-size));
left: calc(-1 * var(--vp-spinner__border-size));
display: block;
content: "";
border: var(--vp-spinner__border-size) solid var(--vp-spinner--background__color);
border-radius: 50%;
opacity: var(--vp-spinner--background__opacity);
}
}
@keyframes vp-spinner {
100% {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,28 @@
/*
* Visual Portfolio Noscript
* Loaded inline in class-assets.php -> add_noscript_styles
*/
// preloader
.vp-portfolio__preloader-wrap {
display: none;
}
// hidden items before load.
.vp-portfolio__items-wrap,
.vp-portfolio__filter-wrap,
.vp-portfolio__sort-wrap,
.vp-portfolio__pagination-wrap {
visibility: visible;
opacity: 1;
}
// image tag without script
.vp-portfolio__item .vp-portfolio__item-img noscript + img {
display: none;
}
// Slider thumbnails
.vp-portfolio__thumbnails-wrap {
display: none;
}

View File

@ -0,0 +1,88 @@
@import "./variables-popup";
/*
* Visual Portfolio styles for Fancybox.
*/
.vp-fancybox {
top: var(--wp-admin--admin-bar--height, 0);
z-index: var(--vp-popup__z-index);
height: calc(100% - var(--wp-admin--admin-bar--height, 0px));
// removed white background to improve vertical videos displaying.
.fancybox-slide--iframe .fancybox-content {
background: none;
}
// Caption.
.fancybox-caption__body {
font-size: 12px;
color: #fff;
h3 {
color: inherit;
}
a {
color: inherit;
&:hover {
opacity: 0.8;
}
}
.vp-portfolio__item-meta-title {
margin-top: 0;
margin-bottom: 3px;
font-size: 14px;
color: inherit;
a {
text-decoration: none;
}
}
}
// Thumbnails.
.fancybox-thumbs {
width: var(--vp-popup--thumbnails__size);
background: var(--vp-popup--thumbnails__background-color);
}
&.fancybox-show-thumbs .fancybox-inner {
right: var(--vp-popup--thumbnails__size);
}
.fancybox-thumbs__list a {
width: var(--vp-popup--thumbnails__size);
max-width: calc(100% - 4px);
height: calc(var(--vp-popup--thumbnails__size) / calc(var(--vp-popup--thumbnails__aspect-ratio)));
&::before {
border: 2px solid var(--vp-popup--thumbnails--items__border-color);
}
@supports (aspect-ratio: 16 / 9) {
width: calc(100% - 4px);
max-width: none;
height: auto;
max-height: none;
aspect-ratio: var(--vp-popup--thumbnails__aspect-ratio);
}
}
.fancybox-thumbs-y .fancybox-thumbs__list {
&::-webkit-scrollbar {
width: var(--vp-popup--thumbnails--scrollbar__size);
}
&::-webkit-scrollbar-track {
background: var(--vp-popup--thumbnails--scrollbar-track__background-color);
box-shadow: none;
}
&::-webkit-scrollbar-thumb {
background: var(--vp-popup--thumbnails--scrollbar-thumb__background-color);
border-radius: var(--vp-popup--thumbnails--scrollbar-thumb__border-radius);
}
}
}

View File

@ -0,0 +1,97 @@
@import "./variables-popup";
/*
* Visual Portfolio styles for PhotoSwipe.
*/
.vp-pswp {
top: var(--wp-admin--admin-bar--height, 0);
z-index: var(--vp-popup__z-index);
height: calc(100% - var(--wp-admin--admin-bar--height, 0px));
.pswp__caption {
background-color: rgba(0, 0, 0, 75%);
> div {
max-width: 600px;
font-size: 12px;
color: #fff;
}
h3 {
color: inherit;
}
a {
color: inherit;
&:hover {
opacity: 0.8;
}
}
.vp-portfolio__item-meta-title {
margin-top: 0;
margin-bottom: 3px;
font-size: 14px;
color: inherit;
a {
text-decoration: none;
}
}
}
.pswp__preloader {
position: absolute;
right: 0;
bottom: 0;
z-index: 1;
}
.vp-pswp-video {
position: relative;
z-index: 1045;
display: flex;
align-items: center;
width: 100%;
max-width: 1920px;
height: 100%;
margin: 0 auto;
line-height: 0;
text-align: left;
vertical-align: middle;
> div {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
}
video,
audio {
position: absolute;
width: 100% !important;
height: 100% !important;
margin: 0;
}
audio {
padding: 20px;
}
}
}
// Default cursor when click to zoom option disabled.
&.vp-pswp-no-zoom .pswp__img {
cursor: default;
}
}

View File

@ -0,0 +1,5 @@
// Fix images <a> tags.
// https://wordpress.org/support/topic/portfolio-page-doesnt-display-images/
.vp-portfolio__item .vp-portfolio__item-img a {
position: unset !important;
}

View File

@ -0,0 +1,33 @@
// Fix popup buttons styles.
// https://wordpress.org/support/topic/your-plugin-does-not-load-the-css-correctly/
.vp-pswp {
.pswp__button,
.pswp__button--arrow--left::before,
.pswp__button--arrow--right::before,
.pswp__button:hover,
.pswp__button--arrow--left:hover::before,
.pswp__button--arrow--right:hover::before {
padding: 0;
background-color: transparent;
border: none;
border-radius: 0;
}
}
.vp-fancybox {
.fancybox-button,
.fancybox-button:hover {
background-color: rgba(30, 30, 30, 60%);
border: none;
border-radius: 0;
box-shadow: none;
&::after {
content: none;
}
}
.fancybox-button:not(:hover) {
color: #ccc !important;
}
}

View File

@ -0,0 +1,36 @@
// Fix headings margin.
.entry-content .vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix preview background color.
.vp-preview-body {
background: none;
}
// Fix font family.
.vp-filter__item,
.vp-sort__item,
.vp-sort select,
.vp-portfolio__item-meta-category,
.vp-pagination__item {
font-family: "Noto Sans", sans-serif;
}
// Fix links border.
.vp-portfolio__item-meta-title,
.vp-filter__item,
.vp-sort__item,
.vp-portfolio__item-meta-category,
.vp-pagination__item,
.vp-portfolio__item-img {
.entry-content & a {
border: none;
}
}
// Fix figcaption padding.
.wp-block-visual-portfolio[class^="wp-block-"].vp-portfolio__items-style-fade figcaption,
.wp-block-visual-portfolio[class^="wp-block-"].vp-portfolio__items-style-fly figcaption {
padding: 0;
}

View File

@ -0,0 +1,23 @@
// Fix links decoration.
.entry .entry-content .vp-portfolio a {
text-decoration: none;
}
// Fix headings margin.
.entry-content .vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix headings decorators.
.vp-portfolio__item-meta-title::before {
content: none;
}
// Fix font family.
.vp-filter__item,
.vp-sort__item,
.vp-sort select,
.vp-portfolio__item-meta-category,
.vp-pagination__item {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

View File

@ -0,0 +1,30 @@
// Fix links border.
.entry-content .vp-portfolio {
a,
a:hover,
a:focus {
box-shadow: none;
}
}
// Fix headings padding and font.
.entry-content .vp-portfolio__item-meta-title {
padding-top: 0;
font-family: "Libre Franklin", "Helvetica Neue", helvetica, arial, sans-serif;
font-weight: 600;
}
// Fix for figcaption in block.
.wp-block-visual-portfolio[class^="wp-block-"]:not(.wp-block-gallery) figcaption {
margin-bottom: 0;
font-style: inherit;
text-align: center;
}
// Fix color on hover.
.vp-portfolio__items-style-fade .vp-portfolio__item-meta:hover,
.vp-portfolio__items-style-fade .vp-portfolio__item-meta:focus,
.vp-portfolio__items-style-fly .vp-portfolio__item-meta:hover,
.vp-portfolio__items-style-fly .vp-portfolio__item-meta:focus {
color: inherit;
}

View File

@ -0,0 +1,19 @@
// Fix preview background color.
.vp-preview-body {
background: none;
}
// Fix headings margin.
.entry-content .vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix font family.
.vp-portfolio__item-meta-title,
.vp-filter__item,
.vp-sort__item,
.vp-sort select,
.vp-portfolio__item-meta-category,
.vp-pagination__item {
font-family: Montserrat, "Helvetica Neue", sans-serif;
}

View File

@ -0,0 +1,56 @@
// Fix headings margin.
.entry-content .vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix caption margin.
.vp-portfolio__item figcaption {
margin-top: 0;
}
// Fix preview background color.
.vp-preview-body {
background: none;
}
// Fix font family.
.vp-filter__item,
.vp-sort__item,
.vp-sort select,
.vp-portfolio__item-meta-category,
.vp-pagination__item {
font-family: "Inter var", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, sans-serif;
}
// Filters, Sort and Pagination.
.vp-filter__style-default {
--vp-filter-default--items__font-size: 0.7em;
}
.vp-filter__style-minimal {
--vp-filter-minimal--items__font-size: 0.7em;
}
.vp-filter__style-dropdown {
--vp-filter-dropdown--items__font-size: 0.7em;
}
.vp-sort__style-default {
--vp-sort-default--items__font-size: 0.7em;
}
.vp-sort__style-minimal {
--vp-sort-minimal--items__font-size: 0.7em;
}
.vp-sort__style-dropdown {
--vp-sort-dropdown--items__font-size: 0.7em;
}
.vp-pagination__style-default {
--vp-pagination-default--items__font-size: 0.7em;
}
.vp-pagination__style-minimal {
--vp-pagination-minimal--items__font-size: 0.7em;
}

View File

@ -0,0 +1,25 @@
// Fix headings margin.
.entry-content .vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix caption margin.
.vp-portfolio__item figcaption {
margin-top: 0;
margin-bottom: 0;
}
// Fix preview background color.
.vp-preview-body {
background: none;
}
// Fix images size.
// https://github.com/WordPress/twentytwentyone/blob/trunk/inc/template-functions.php#L405-L447.
[data-vp-layout="tiles"] .vp-portfolio__item-img img,
[data-vp-masonry-images-aspect-ratio*=":"] .vp-portfolio__item-img img,
[data-vp-grid-images-aspect-ratio*=":"] .vp-portfolio__item-img img {
width: 100% !important;
max-width: none !important;
height: 100% !important;
}

View File

@ -0,0 +1,11 @@
// Fix headings margin.
.vp-portfolio__item-meta-title {
margin-top: 0;
}
// Fix top and bottom padding of main content in the popup iframe.
.vp-popup-iframe .wp-site-blocks > main {
padding-top: 80px;
padding-bottom: 80px;
margin-top: 0;
}

View File

@ -0,0 +1,11 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="20" rx="10" fill="black" />
<path d="M11.5062 13.9L14.0488 6.59998H11.6894L9.39999 13.9H11.5062Z" fill="url(#vpf_logo_paint0_linear)" />
<path d="M8.54255 13.9L5.99999 6.59998H8.35932L10.6488 13.9H8.54255Z" fill="white" />
<defs>
<linearGradient id="vpf_logo_paint0_linear" x1="12.9" y1="6.59998" x2="6.59999" y2="15.4" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="white" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 661 B

View File

@ -0,0 +1,11 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="20" rx="10" fill="white" />
<path d="M11.5062 13.9L14.0488 6.59998H11.6894L9.39999 13.9H11.5062Z" fill="url(#vpf_logo_paint0_linear)" />
<path d="M8.54255 13.9L5.99999 6.59998H8.35932L10.6488 13.9H8.54255Z" fill="black" />
<defs>
<linearGradient id="vpf_logo_paint0_linear" x1="12.9" y1="6.59998" x2="6.59999" y2="15.4" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="black" />
<stop offset="1" stop-color="black" stop-opacity="0" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,42 @@
import $ from 'jquery';
import { debounce } from 'throttle-debounce';
let jetpackLazyImagesLoadEvent;
try {
jetpackLazyImagesLoadEvent = new Event('jetpack-lazy-images-load', {
bubbles: true,
cancelable: true,
});
} catch (e) {
jetpackLazyImagesLoadEvent = document.createEvent('Event');
jetpackLazyImagesLoadEvent.initEvent(
'jetpack-lazy-images-load',
true,
true
);
}
// Fix AJAX loaded images.
$(document).on('loadedNewItems.vpf', function (event) {
if (event.namespace !== 'vpf') {
return;
}
$('body').get(0).dispatchEvent(jetpackLazyImagesLoadEvent);
});
// Fix masonry reloading when Jetpack images lazy loaded.
// https://github.com/Automattic/jetpack/issues/9595
//
// p.s. it looks like this fix is not working at all in Safari browser.
const runReLayout = debounce(200, ($gallery) => {
$gallery.vpf('imagesLoaded');
});
$(document.body).on('jetpack-lazy-loaded-image', '.vp-portfolio', function () {
const $this = $(this).closest('.vp-portfolio');
if ($this && $this.length) {
runReLayout($this);
}
});

View File

@ -0,0 +1,115 @@
import $ from 'jquery';
const { SimpleBar, navigator } = window;
const $doc = $(document);
// Don't run on Mac and mobile devices.
const allowScrollbar =
!/Mac|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
if (allowScrollbar && typeof SimpleBar !== 'undefined') {
// Extend VP class.
$doc.on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') {
return;
}
/**
* Init Simplebar plugin
*/
VP.prototype.initCustomScrollbar = function () {
const self = this;
self.emitEvent('beforeInitCustomScrollbar');
self.$items_wrap
.find('.vp-portfolio__custom-scrollbar')
.each(function () {
const instance = SimpleBar.instances.get(this);
if (!instance) {
new SimpleBar(this);
}
});
self.emitEvent('initCustomScrollbar');
};
/**
* Destroy Simplebar plugin
*/
VP.prototype.destroyCustomScrollbar = function () {
const self = this;
self.$items_wrap
.find('[data-simplebar="init"].vp-portfolio__custom-scrollbar')
.each(function () {
const instance = SimpleBar.instances.get(this);
if (instance) {
instance.unMount();
}
});
self.emitEvent('destroyCustomScrollbar');
};
});
// Add Items.
$doc.on('addItems.vpf', (event, self, $items, removeExisting) => {
if (event.namespace !== 'vpf') {
return;
}
if (removeExisting) {
self.destroyCustomScrollbar();
}
self.initCustomScrollbar();
});
// Init.
$doc.on('init.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.initCustomScrollbar();
});
// Destroy.
$doc.on('destroy.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.destroyCustomScrollbar();
});
// Init Swiper duplicated slides scrollbars.
$doc.on('initSwiper.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.sliderLoop === 'true') {
self.initCustomScrollbar();
}
});
// Fix Simplebar content size in some themes.
// For example, in Astra theme in content with enabled sidebar, Simplebar calculate wrong height automatically.
$(() => {
$('[data-simplebar="init"].vp-portfolio__custom-scrollbar').each(
function () {
const instance = SimpleBar.instances.get(this);
if (instance) {
instance.recalculate();
}
}
);
});
}

View File

@ -0,0 +1,165 @@
import $ from 'jquery';
const $wnd = $(window);
/**
* Check if lines cross
*
* @param {Object} a - first point of the first line
* @param {Object} b - second point of the first line
* @param {Object} c - first point of the second line
* @param {Object} d - second point of the second line
*
* @return {boolean} cross lines
*/
function isCrossLine(a, b, c, d) {
// Working code #1:
//
// var common = (b.x - a.x)*(d.y - c.y) - (b.y - a.y)*(d.x - c.x);
// if (common === 0) {
// return false;
// }
//
// var rH = (a.y - c.y)*(d.x - c.x) - (a.x - c.x)*(d.y - c.y);
// var sH = (a.y - c.y)*(b.x - a.x) - (a.x - c.x)*(b.y - a.y);
//
// var r = rH / common;
// var s = sH / common;
//
// return r >= 0 && r <= 1 && s >= 0 && s <= 1;
// Working code #2:
const v1 = (d.x - c.x) * (a.y - c.y) - (d.y - c.y) * (a.x - c.x);
const v2 = (d.x - c.x) * (b.y - c.y) - (d.y - c.y) * (b.x - c.x);
const v3 = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
const v4 = (b.x - a.x) * (d.y - a.y) - (b.y - a.y) * (d.x - a.x);
return v1 * v2 <= 0 && v3 * v4 <= 0;
}
// Init Events.
$(document).on('initEvents.vpf', (event, self) => {
if (event.namespace !== 'vpf' || self.options.itemsStyle !== 'fly') {
return;
}
const evp = `.vpf-uid-${self.uid}`;
// determine cursor position
let lastCursorPos = {};
$wnd.on(`mousemove${evp}`, (e) => {
lastCursorPos = {
x: e.clientX,
y: e.clientY,
};
});
self.$item.on(
`mouseenter${evp} mouseleave${evp}`,
'.vp-portfolio__item',
function (e) {
const $this = $(this);
const itemRect = $this[0].getBoundingClientRect();
const $overlay = $this.find('.vp-portfolio__item-overlay');
const enter = e.type === 'mouseenter';
let endX = '0%';
let endY = '0%';
const curCursorPos = {
x: e.clientX,
y: e.clientY,
};
// find the corner that placed on cursor path.
let isUp = isCrossLine(
{ x: itemRect.left, y: itemRect.top },
{ x: itemRect.left + itemRect.width, y: itemRect.top },
curCursorPos,
lastCursorPos
);
let isDown = isCrossLine(
{ x: itemRect.left, y: itemRect.top + itemRect.height },
{
x: itemRect.left + itemRect.width,
y: itemRect.top + itemRect.height,
},
curCursorPos,
lastCursorPos
);
let isLeft = isCrossLine(
{ x: itemRect.left, y: itemRect.top },
{ x: itemRect.left, y: itemRect.top + itemRect.height },
curCursorPos,
lastCursorPos
);
let isRight = isCrossLine(
{ x: itemRect.left + itemRect.width, y: itemRect.top },
{
x: itemRect.left + itemRect.width,
y: itemRect.top + itemRect.height,
},
curCursorPos,
lastCursorPos
);
// Sometimes isCrossLine returned false, so we need to check direction manually (less accurate, but it is not a big problem).
if (!isUp && !isDown && !isLeft && !isRight) {
const x =
(itemRect.width / 2 - curCursorPos.x + itemRect.left) /
(itemRect.width / 2);
const y =
(itemRect.height / 2 - curCursorPos.y + itemRect.top) /
(itemRect.height / 2);
if (Math.abs(x) > Math.abs(y)) {
if (x > 0) {
isLeft = true;
} else {
isRight = true;
}
} else if (y > 0) {
isUp = true;
} else {
isDown = true;
}
}
if (isUp) {
endY = '-100.1%';
} else if (isDown) {
endY = '100.1%';
} else if (isLeft) {
endX = '-100.1%';
} else if (isRight) {
endX = '100.1%';
}
if (enter) {
$overlay.css({
transition: 'none',
transform: `translateX(${endX}) translateY(${endY}) translateZ(0)`,
});
// Trigger a reflow, flushing the CSS changes. This need to fix some glithes in Safari and Firefox.
// Info here - https://stackoverflow.com/questions/11131875/what-is-the-cleanest-way-to-disable-css-transition-effects-temporarily
// eslint-disable-next-line no-unused-expressions
$overlay[0].offsetHeight;
}
$overlay.css({
transition: '.2s transform ease-in-out',
transform: `translateX(${enter ? '0%' : endX}) translateY(${
enter ? '0%' : endY
}) translateZ(0)`,
});
}
);
});
// Destroy Events.
$(document).on('destroyEvents.vpf', (event, self) => {
if (event.namespace !== 'vpf' || self.options.itemsStyle !== 'fly') {
return;
}
const evp = `.vpf-uid-${self.uid}`;
$wnd.off(`mousemove${evp}`);
self.$item.off(`mouseenter${evp} mouseleave${evp}`);
});

View File

@ -0,0 +1,183 @@
import $ from 'jquery';
const { screenSizes } = window.VPData;
//
// Our custom Grid layout for Isotope.
//
// * fixes grid items position in FireFox - https://wordpress.org/support/topic/gallery-difference-between-firefox-and-all-other-browsers/
//
if (
typeof window.Isotope !== 'undefined' &&
typeof window.Isotope.LayoutMode !== 'undefined'
) {
const VPRows = window.Isotope.LayoutMode.create('vpRows');
const proto = VPRows.prototype;
proto.measureColumns = function () {
// set items, used if measuring first item
this.items = this.isotope.filteredItems;
this.getContainerWidth();
// if columnWidth is 0, default to outerWidth of first item
if (!this.columnWidth) {
const firstItem = this.items[0];
const firstItemElem = firstItem && firstItem.element;
// columnWidth fall back to item of first element
this.columnWidth =
(firstItemElem && window.getSize(firstItemElem).outerWidth) ||
// if first elem has no width, default to size of container
this.containerWidth;
}
this.columnWidth += this.gutter;
// calculate columns
const containerWidth = this.containerWidth + this.gutter;
let cols = containerWidth / this.columnWidth;
// fix rounding errors, typically with gutters
const excess = this.columnWidth - (containerWidth % this.columnWidth);
// if overshoot is less than a pixel, round up, otherwise floor it
const mathMethod = excess && excess < 1 ? 'round' : 'floor';
cols = Math[mathMethod](cols);
this.cols = Math.max(cols, 1);
};
proto.getContainerWidth = function () {
// container is parent if fit width
const isFitWidth = this._getOption
? this._getOption('fitWidth')
: false;
const container = isFitWidth ? this.element.parentNode : this.element;
// check that this.size and size are there
// IE8 triggers resize on body size change, so they might not be
const size = window.getSize(container);
this.containerWidth = size && size.innerWidth;
};
proto._resetLayout = function () {
this.x = 0;
this.y = 0;
this.maxY = 0;
this.horizontalColIndex = 0;
this._getMeasurement('columnWidth', 'outerWidth');
this._getMeasurement('gutter', 'outerWidth');
this.measureColumns();
};
proto._getItemLayoutPosition = function (item) {
item.getSize();
// how many columns does this brick span
const remainder = item.size.outerWidth % this.columnWidth;
const mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
// round if off by 1 pixel, otherwise use ceil
let colSpan = Math[mathMethod](item.size.outerWidth / this.columnWidth);
colSpan = Math.min(colSpan, this.cols);
let col = this.horizontalColIndex % this.cols;
const isOver = colSpan > 1 && col + colSpan > this.cols;
// shift to next row if item can't fit on current row
col = isOver ? 0 : col;
// don't let zero-size items take up space
const hasSize = item.size.outerWidth && item.size.outerHeight;
this.horizontalColIndex = hasSize
? col + colSpan
: this.horizontalColIndex;
const itemWidth = item.size.outerWidth + this.gutter;
// if this element cannot fit in the current row
if (this.x !== 0 && this.horizontalColIndex === 1) {
this.x = 0;
this.y = this.maxY;
}
const position = {
x: this.x,
y: this.y,
};
this.maxY = Math.max(this.maxY, this.y + item.size.outerHeight);
this.x += itemWidth;
return position;
};
proto._getContainerSize = function () {
return { height: this.maxY };
};
}
// Init Options.
$(document).on('initOptions.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.defaults.gridColumns = 3;
if (!self.options.gridColumns) {
self.options.gridColumns = self.defaults.gridColumns;
}
if (!self.options.gridImagesAspectRatio) {
self.options.gridImagesAspectRatio =
self.defaults.gridImagesAspectRatio;
}
});
// Init Layout.
$(document).on('initLayout.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.layout !== 'grid') {
return;
}
// columns.
self.addStyle('.vp-portfolio__item-wrap', {
width: `${100 / self.options.gridColumns}%`,
});
// calculate responsive.
let count = self.options.gridColumns - 1;
let currentPoint = Math.min(screenSizes.length - 1, count);
for (; currentPoint >= 0; currentPoint -= 1) {
if (count > 0 && typeof screenSizes[currentPoint] !== 'undefined') {
self.addStyle(
'.vp-portfolio__item-wrap',
{
width: `${100 / count}%`,
},
`screen and (max-width: ${screenSizes[currentPoint]}px)`
);
}
count -= 1;
}
});
// Change Isotope Layout Mode.
$(document).on('beforeInitIsotope.vpf', (event, self, initOptions) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.layout !== 'grid' || typeof initOptions !== 'object') {
return;
}
initOptions.layoutMode = 'vpRows';
});

View File

@ -0,0 +1,19 @@
import $ from 'jquery';
// Init Options.
$(document).on('initOptions.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.defaults.justifiedRowHeight = 250;
self.defaults.justifiedRowHeightTolerance = 0.25;
if (!self.options.justifiedRowHeight) {
self.options.justifiedRowHeight = self.defaults.justifiedRowHeight;
}
if (!self.options.justifiedRowHeightTolerance) {
self.options.justifiedRowHeightTolerance =
self.defaults.justifiedRowHeightTolerance;
}
});

View File

@ -0,0 +1,53 @@
import $ from 'jquery';
const { screenSizes } = window.VPData;
// Init Options.
$(document).on('initOptions.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.defaults.masonryColumns = 3;
if (!self.options.masonryColumns) {
self.options.masonryColumns = self.defaults.masonryColumns;
}
if (!self.options.masonryImagesAspectRatio) {
self.options.masonryImagesAspectRatio =
self.defaults.masonryImagesAspectRatio;
}
});
// Init Layout.
$(document).on('initLayout.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.layout !== 'masonry') {
return;
}
// columns.
self.addStyle('.vp-portfolio__item-wrap', {
width: `${100 / self.options.masonryColumns}%`,
});
// calculate responsive.
let count = self.options.masonryColumns - 1;
let currentPoint = Math.min(screenSizes.length - 1, count);
for (; currentPoint >= 0; currentPoint -= 1) {
if (count > 0 && typeof screenSizes[currentPoint] !== 'undefined') {
self.addStyle(
'.vp-portfolio__item-wrap',
{
width: `${100 / count}%`,
},
`screen and (max-width: ${screenSizes[currentPoint]}px)`
);
}
count -= 1;
}
});

View File

@ -0,0 +1,152 @@
import isNumber from 'is-number';
import $ from 'jquery';
import { throttle } from 'throttle-debounce';
const { ResizeObserver } = window;
// Listen for slider width change to calculate dynamic height of images.
const dynamicHeightObserver = new ResizeObserver(
throttle(100, (entries) => {
entries.forEach(({ target }) => {
if (target && target.vpf) {
const self = target.vpf;
const calculatedHeight =
(self.$item.width() *
parseFloat(self.options.sliderItemsHeight)) /
100;
target
.querySelector('.vp-portfolio__items-wrap')
.style.setProperty(
'--vp-layout-slider--auto-items__height',
`${calculatedHeight}px`
);
}
});
})
);
// Init Layout.
$(document).on('initLayout.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.layout !== 'slider') {
return;
}
['items', 'thumbnails'].forEach((type) => {
let itemsHeight =
type === 'items'
? self.options.sliderItemsHeight
: self.options.sliderThumbnailsHeight;
if (itemsHeight === 'auto') {
return;
}
const typeSingle = type.replace(/s$/g, '');
let itemsMinHeight =
type === 'items' ? self.options.sliderItemsMinHeight : 0;
itemsHeight = isNumber(itemsHeight) ? `${itemsHeight}px` : itemsHeight;
// prevent minHeight option in preview, when used 'vh' units.
if (itemsMinHeight && self.isPreview() && /vh/.test(itemsMinHeight)) {
itemsMinHeight = 0;
}
const itemsPerView =
type === 'items'
? self.options.sliderSlidesPerView
: self.options.sliderThumbnailsPerView;
if (itemsPerView === 'auto') {
// fix fade slider items width.
// https://github.com/nk-crew/visual-portfolio/issues/95.
let itemsWidth = 'auto';
if (type === 'items' && self.options.sliderEffect === 'fade') {
itemsWidth = '100%';
}
// Calculate dynamic height.
// Previously we tried the pure CSS solution, but there was couple bugs like:
// - Classic styles items wrong height
// - FireFox wrong images width render
if (itemsHeight.indexOf('%') === itemsHeight.length - 1) {
dynamicHeightObserver.observe(self.$item[0]);
// Static height.
} else {
self.addStyle(`.vp-portfolio__${type}-wrap`, {
'--vp-layout-slider--auto-items__height': itemsHeight,
});
}
self.addStyle(`.vp-portfolio__${typeSingle}-wrap`, {
width: 'auto',
});
self.addStyle(
`.vp-portfolio__${typeSingle} .vp-portfolio__${typeSingle}-img img`,
{
width: itemsWidth,
height: 'var(--vp-layout-slider--auto-items__height)',
}
);
// min height.
if (itemsMinHeight) {
self.addStyle(
`.vp-portfolio__${typeSingle} .vp-portfolio__${typeSingle}-img img`,
{
'min-height': itemsMinHeight,
}
);
}
} else {
// We have to use this hack with Before to support Dynamic height.
// Also, previously we used the `margin-top`,
// but it is not working correctly with Items Mininmal Height option.
self.addStyle(`.vp-portfolio__${typeSingle}-img-wrap::before`, {
'padding-top': itemsHeight,
});
self.addStyle(`.vp-portfolio__${typeSingle}-img img`, {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
});
self.addStyle(`.vp-portfolio__${typeSingle}-img`, {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
});
self.addStyle(
`.vp-portfolio__${typeSingle} .vp-portfolio__${typeSingle}-img img`,
{
width: '100%',
height: '100%',
}
);
// min height.
if (itemsMinHeight) {
self.addStyle(`.vp-portfolio__${typeSingle}-img-wrap::before`, {
'min-height': itemsMinHeight,
});
}
}
});
// thumbnails top gap.
if (self.options.sliderThumbnailsGap) {
self.addStyle('.vp-portfolio__thumbnails-wrap', {
'margin-top': `${self.options.sliderThumbnailsGap}px`,
});
}
});

View File

@ -0,0 +1,179 @@
import $ from 'jquery';
const { screenSizes } = window.VPData;
// fix masonry items position for Tiles layout.
// https://github.com/nk-crew/visual-portfolio/issues/111
if (
typeof window.Isotope !== 'undefined' &&
typeof window.Isotope.LayoutMode !== 'undefined'
) {
const MasonryMode = window.Isotope.LayoutMode.modes.masonry;
if (MasonryMode) {
const defaultMeasureColumns = MasonryMode.prototype.measureColumns;
MasonryMode.prototype.measureColumns = function () {
let runDefault = true;
// if columnWidth is 0, default to columns count size.
if (!this.columnWidth) {
const $vp = $(this.element).closest(
'.vp-portfolio[data-vp-layout="tiles"]'
);
// change column size for Tiles type only.
if ($vp.length && $vp[0].vpf) {
this.getContainerWidth();
const { vpf } = $vp[0];
const settings = vpf.getTilesSettings();
// get columns number
let columns = parseInt(settings[0], 10) || 1;
// calculate responsive.
let count = columns - 1;
let currentPoint = Math.min(screenSizes.length - 1, count);
for (; currentPoint >= 0; currentPoint -= 1) {
if (
count > 0 &&
typeof screenSizes[currentPoint] !== 'undefined'
) {
if (
window.innerWidth <= screenSizes[currentPoint]
) {
columns = count;
}
}
count -= 1;
}
if (columns) {
this.columnWidth = this.containerWidth / columns;
this.columnWidth += this.gutter;
this.cols = columns;
runDefault = false;
}
}
}
if (runDefault) {
defaultMeasureColumns.call(this);
}
};
}
}
// Extend VP class.
$(document).on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') {
return;
}
/**
* Get Tiles Layout Settings
*
* @return {string} tiles layout
*/
VP.prototype.getTilesSettings = function () {
const self = this;
const layoutArr = self.options.tilesType.split(/[:|]/);
// remove last empty item
if (
typeof layoutArr[layoutArr.length - 1] !== 'undefined' &&
!layoutArr[layoutArr.length - 1]
) {
layoutArr.pop();
}
return layoutArr;
};
});
// Init Options.
$(document).on('initOptions.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.defaults.tilesType = '3|1,1|';
if (!self.options.tilesType) {
self.options.tilesType = self.defaults.tilesType;
}
});
// Init Layout.
$(document).on('initLayout.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (self.options.layout !== 'tiles') {
return;
}
const settings = self.getTilesSettings();
// get columns number
const columns = parseInt(settings[0], 10) || 1;
settings.shift();
// set columns
self.addStyle('.vp-portfolio__item-wrap', {
width: `${100 / columns}%`,
});
// set items sizes
if (settings && settings.length) {
for (let k = 0; k < settings.length; k += 1) {
const size = settings[k].split(',');
const w = parseFloat(size[0]) || 1;
const h = parseFloat(size[1]) || 1;
let itemSelector = '.vp-portfolio__item-wrap';
if (settings.length > 1) {
itemSelector += `:nth-of-type(${settings.length}n+${k + 1})`;
}
if (w && w !== 1) {
self.addStyle(itemSelector, {
width: `${(w * 100) / columns}%`,
});
}
self.addStyle(
`${itemSelector} .vp-portfolio__item-img-wrap::before`,
{
'padding-top': `${h * 100}%`,
}
);
}
}
// calculate responsive.
let count = columns - 1;
let currentPoint = Math.min(screenSizes.length - 1, count);
for (; currentPoint >= 0; currentPoint -= 1) {
if (count > 0 && typeof screenSizes[currentPoint] !== 'undefined') {
self.addStyle(
'.vp-portfolio__item-wrap',
{
width: `${100 / count}%`,
},
`screen and (max-width: ${screenSizes[currentPoint]}px)`
);
self.addStyle(
'.vp-portfolio__item-wrap:nth-of-type(n)',
{
width: `${100 / count}%`,
},
`screen and (max-width: ${screenSizes[currentPoint]}px)`
);
}
count -= 1;
}
});

View File

@ -0,0 +1,26 @@
/*
* Visual Portfolio images lazy load fallback for browsers
* which does not support CSS :has()
*/
// Lazyloaded - remove preloader images placeholder effect.
document.addEventListener('lazybeforeunveil', (e) => {
const vpfImgWrapper = e.target.closest(
'.vp-portfolio__item-img, .vp-portfolio__thumbnail-img'
);
if (vpfImgWrapper) {
vpfImgWrapper.classList.add('vp-has-lazyloading');
}
});
document.addEventListener('lazyloaded', (e) => {
const vpfImgWrapper = e.target.closest(
'.vp-portfolio__item-img, .vp-portfolio__thumbnail-img'
);
if (vpfImgWrapper) {
vpfImgWrapper.classList.add('vp-has-lazyloaded');
vpfImgWrapper.classList.add('vp-has-lazyloading');
}
});

View File

@ -0,0 +1,26 @@
// Recalculate image size if parent is <picture>
document.addEventListener('lazybeforesizes', (e) => {
// for some reason sometimes e.detail is undefined, so we need to check it.
if (!e.detail || !e.detail.width || !e.target) {
return;
}
const parent = e.target.closest(':not(picture)');
if (parent) {
e.detail.width = parent.clientWidth || e.detail.width;
}
});
/**
* Remove <noscript> tag.
* Some of optimization plugin make something, that killed our styles with noscript tag.
* Related topic: https://wordpress.org/support/topic/visual-portfolio-and-sg-optimizer-dont-play-well/
*/
document.addEventListener('lazybeforeunveil', (e) => {
const prevEl = e.target.previousElementSibling;
if (prevEl && prevEl.matches('noscript')) {
prevEl.remove();
}
});

View File

@ -0,0 +1,12 @@
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig = {
...window.lazySizesConfig,
lazyClass: 'vp-lazyload',
loadedClass: 'vp-lazyloaded',
preloadClass: 'vp-lazypreload',
loadingClass: 'vp-lazyloading',
srcAttr: 'data-src',
srcsetAttr: 'data-srcset',
sizesAttr: 'data-sizes',
};

View File

@ -0,0 +1,67 @@
(function (window, factory) {
const globalInstall = function () {
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if (window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
})(window, (window, document, lazySizes) => {
if (!window.addEventListener) {
return;
}
const getCSS = function (elem) {
return window.getComputedStyle(elem, null) || {};
};
const objectFitCover = {
calculateSize(element, width) {
const CSS = getCSS(element);
if (CSS && CSS.objectFit && CSS.objectFit === 'cover') {
const blockHeight = parseInt(
element.getAttribute('height'),
10
);
const blockWidth = parseInt(element.getAttribute('width'), 10);
if (blockHeight) {
if (
blockWidth / blockHeight >
element.clientWidth / element.clientHeight
) {
width = parseInt(
(element.clientHeight * blockWidth) / blockHeight,
10
);
}
}
}
return width;
},
};
lazySizes.objectFitCover = objectFitCover;
document.addEventListener('lazybeforesizes', (e) => {
// for some reason sometimes e.detail is undefined, so we need to check it.
if (
e.defaultPrevented ||
!e.detail ||
!e.detail.width ||
!e.target ||
e.detail.instance !== lazySizes
) {
return;
}
const element = e.target;
e.detail.width = objectFitCover.calculateSize(element, e.detail.width);
});
});

View File

@ -0,0 +1,91 @@
/**
* Load duplicated Swiper slides to prevent images "blink" effect after swipe.
*
* @param window
* @param factory
*/
(function (window, factory) {
const globalInstall = function () {
factory(window.lazySizes);
window.removeEventListener('lazyunveilread', globalInstall, true);
};
factory = factory.bind(null, window, window.document);
if (window.lazySizes) {
globalInstall();
} else {
window.addEventListener('lazyunveilread', globalInstall, true);
}
})(window, (window, document, lazySizes) => {
if (!window.addEventListener) {
return;
}
const { unveil } = lazySizes.loader;
const getSiblings = (el, filter) =>
[...el.parentNode.children].filter(
(child) =>
child.nodeType === 1 &&
child !== el &&
(!filter || child.matches(filter))
);
const swiperDuplicatesLoad = {
getSlideData(element) {
const $el = element.closest('.swiper-slide');
const slideIndex = $el
? $el.getAttribute('data-swiper-slide-index')
: false;
return {
$el,
slideIndex,
};
},
run(element) {
const slideData = this.getSlideData(element);
if (slideData.slideIndex) {
const $siblingDuplicates = getSiblings(
slideData.$el,
`[data-swiper-slide-index="${slideData.slideIndex}"]`
);
$siblingDuplicates.forEach((el) => {
// We should also get images in `loading` state, because in some rare situations
// duplicated images by default has this class and not displaying correctly.
const $images = el.querySelectorAll(
'img.vp-lazyload, img.vp-lazyloading'
);
if ($images) {
$images.forEach(($img) => {
unveil($img);
});
}
});
}
return true;
},
};
lazySizes.swiperDuplicatesLoad = swiperDuplicatesLoad;
document.addEventListener('lazyloaded', (e) => {
// for some reason sometimes e.detail is undefined, so we need to check it.
if (
e.defaultPrevented ||
!e.detail ||
e.detail.swiperDuplicatesChecked ||
!e.target ||
e.detail.instance !== lazySizes
) {
return;
}
const element = e.target;
e.detail.swiperDuplicatesChecked = swiperDuplicatesLoad.run(element);
});
});

View File

@ -0,0 +1,995 @@
import $ from 'jquery';
import rafSchd from 'raf-schd';
import { throttle } from 'throttle-debounce';
const { VPData } = window;
const { __ } = VPData;
const $wnd = $(window);
/**
* Emit Resize Event.
*/
function windowResizeEmit() {
if (typeof window.Event === 'function') {
// modern browsers
window.dispatchEvent(new window.Event('resize'));
} else {
// for IE and other old browsers
// causes deprecation warning on modern browsers
const evt = window.document.createEvent('UIEvents');
evt.initUIEvent('resize', true, false, window, 0);
window.dispatchEvent(evt);
}
}
const visibilityData = {};
let shouldCheckVisibility = false;
let checkVisibilityTimeout = false;
let isFocusVisible = false;
// fix portfolio inside Tabs and Accordions
// check visibility by timer https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom/33456469
//
// https://github.com/nk-crew/visual-portfolio/issues/11
// https://github.com/nk-crew/visual-portfolio/issues/113
function checkVisibility() {
clearTimeout(checkVisibilityTimeout);
if (!shouldCheckVisibility) {
return;
}
const $items = $('.vp-portfolio__ready');
if ($items.length) {
let isVisibilityChanged = false;
$items.each(function () {
const { vpf } = this;
if (!vpf) {
return;
}
const currentState = visibilityData[vpf.uid] || 'none';
visibilityData[vpf.uid] =
this.offsetParent === null ? 'hidden' : 'visible';
// changed from hidden to visible.
if (
currentState === 'hidden' &&
visibilityData[vpf.uid] === 'visible'
) {
isVisibilityChanged = true;
}
});
// resize, if visibility changed.
if (isVisibilityChanged) {
windowResizeEmit();
}
} else {
shouldCheckVisibility = false;
}
// run again.
checkVisibilityTimeout = setTimeout(checkVisibility, 500);
}
// run check function only after portfolio inited.
$(document).on('inited.vpf', (event) => {
if (event.namespace !== 'vpf') {
return;
}
shouldCheckVisibility = true;
checkVisibility();
});
/**
* If the most recent user interaction was via the keyboard;
* and the key press did not include a meta, alt/option, or control key;
* then the modality is keyboard. Otherwise, the modality is not keyboard.
*/
document.addEventListener(
'keydown',
function (e) {
if (e.metaKey || e.altKey || e.ctrlKey) {
return;
}
isFocusVisible = true;
},
true
);
/**
* If at any point a user clicks with a pointing device, ensure that we change
* the modality away from keyboard.
* This avoids the situation where a user presses a key on an already focused
* element, and then clicks on a different element, focusing it with a
* pointing device, while we still think we're in keyboard modality.
*/
document.addEventListener(
'mousedown',
() => {
isFocusVisible = false;
},
true
);
document.addEventListener(
'pointerdown',
() => {
isFocusVisible = false;
},
true
);
document.addEventListener(
'touchstart',
() => {
isFocusVisible = false;
},
true
);
/**
* Main VP class
*/
class VP {
constructor($item, userOptions) {
const self = this;
self.$item = $item;
// get id from class
const classes = $item[0].className.split(/\s+/);
for (let k = 0; k < classes.length; k += 1) {
if (classes[k] && /^vp-uid-/.test(classes[k])) {
self.uid = classes[k].replace(/^vp-uid-/, '');
}
if (classes[k] && /^vp-id-/.test(classes[k])) {
self.id = classes[k].replace(/^vp-id-/, '');
}
}
if (!self.uid) {
// eslint-disable-next-line no-console
console.error(__.couldnt_retrieve_vp);
return;
}
self.href = window.location.href;
self.$items_wrap = $item.find('.vp-portfolio__items');
self.$slider_thumbnails_wrap = $item.find('.vp-portfolio__thumbnails');
self.$pagination = $item.find('.vp-portfolio__pagination-wrap');
self.$filter = $item.find('.vp-portfolio__filter-wrap');
self.$sort = $item.find('.vp-portfolio__sort-wrap');
// find single filter block.
if (self.id) {
self.$filter = self.$filter.add(
`.vp-single-filter.vp-id-${self.id} .vp-portfolio__filter-wrap`
);
}
// find single sort block.
if (self.id) {
self.$sort = self.$sort.add(
`.vp-single-sort.vp-id-${self.id} .vp-portfolio__sort-wrap`
);
}
// user options
self.userOptions = userOptions;
self.firstRun = true;
self.init();
}
// emit event
// Example:
// $(document).on('init.vpf', function (event, infiniteObject) {
// console.log(infiniteObject);
// });
emitEvent(event, data) {
data = data ? [this].concat(data) : [this];
this.$item.trigger(`${event}.vpf`, data);
this.$item.trigger(`${event}.vpf-uid-${this.uid}`, data);
}
/**
* Init
*/
init() {
const self = this;
// destroy if already inited
if (!self.firstRun) {
self.destroy();
}
self.destroyed = false;
self.$item.addClass('vp-portfolio__ready');
// init options
self.initOptions();
// init events
self.initEvents();
// init layout
self.initLayout();
// init custom colors
self.initCustomColors();
self.emitEvent('init');
if (self.id) {
$(`.vp-single-filter.vp-id-${self.id}`)
.addClass('vp-single-filter__ready')
.parent('.vp-portfolio__layout-elements')
.addClass('vp-portfolio__layout-elements__ready');
$(`.vp-single-sort.vp-id-${self.id}`)
.addClass('vp-single-sort__ready')
.parent('.vp-portfolio__layout-elements')
.addClass('vp-portfolio__layout-elements__ready');
}
// resized
self.resized();
// images loaded
self.imagesLoaded();
self.emitEvent('inited');
self.firstRun = false;
}
/**
* Check if script loaded in preview.
*
* @return {boolean} is in preview.
*/
isPreview() {
const self = this;
return !!self.$item.closest('#vp_preview').length;
}
/**
* Called after resized container.
*/
resized() {
windowResizeEmit();
this.emitEvent('resized');
}
/**
* Images loaded.
*/
imagesLoaded() {
const self = this;
if (!self.$items_wrap.imagesLoaded) {
return;
}
self.$items_wrap.imagesLoaded().progress(() => {
this.emitEvent('imagesLoaded');
});
}
/**
* Destroy
*/
destroy() {
const self = this;
// remove loaded class
self.$item.removeClass('vp-portfolio__ready');
if (self.id) {
$(`.vp-single-filter.vp-id-${self.id}`)
.removeClass('vp-single-filter__ready')
.parent('.vp-portfolio__layout-elements')
.removeClass('vp-portfolio__layout-elements__ready');
$(`.vp-single-sort.vp-id-${self.id}`)
.removeClass('vp-single-sort__ready')
.parent('.vp-portfolio__layout-elements')
.removeClass('vp-portfolio__layout-elements__ready');
}
// destroy events
self.destroyEvents();
// remove all generated styles
self.removeStyle();
self.renderStyle();
self.emitEvent('destroy');
self.destroyed = true;
}
/**
* Add style to the current portfolio list
*
* @param {string} selector css selector
* @param {string} styles object with styles
* @param {string} media string with media query
*/
addStyle(selector, styles, media) {
media = media || '';
const self = this;
const { uid } = self;
if (!self.stylesList) {
self.stylesList = {};
}
if (typeof self.stylesList[uid] === 'undefined') {
self.stylesList[uid] = {};
}
if (typeof self.stylesList[uid][media] === 'undefined') {
self.stylesList[uid][media] = {};
}
if (typeof self.stylesList[uid][media][selector] === 'undefined') {
self.stylesList[uid][media][selector] = {};
}
self.stylesList[uid][media][selector] = $.extend(
self.stylesList[uid][media][selector],
styles
);
self.emitEvent('addStyle', [selector, styles, media, self.stylesList]);
}
/**
* Remove style from the current portfolio list
*
* @param {string} selector css selector (if not set - removed all styles)
* @param {string} styles object with styles
* @param {string} media string with media query
*/
removeStyle(selector, styles, media) {
media = media || '';
const self = this;
const { uid } = self;
if (!self.stylesList) {
self.stylesList = {};
}
if (typeof self.stylesList[uid] !== 'undefined' && !selector) {
self.stylesList[uid] = {};
}
if (
typeof self.stylesList[uid] !== 'undefined' &&
typeof self.stylesList[uid][media] !== 'undefined' &&
typeof self.stylesList[uid][media][selector] !== 'undefined' &&
selector
) {
delete self.stylesList[uid][media][selector];
}
self.emitEvent('removeStyle', [selector, styles, self.stylesList]);
}
/**
* Render style for the current portfolio list
*/
renderStyle() {
const self = this;
// timeout for the case, when styles added one by one
const { uid } = self;
let stylesString = '';
if (!self.stylesList) {
self.stylesList = {};
}
// create string with styles
if (typeof self.stylesList[uid] !== 'undefined') {
Object.keys(self.stylesList[uid]).forEach((m) => {
// media
if (m) {
stylesString += `@media ${m} {`;
}
Object.keys(self.stylesList[uid][m]).forEach((s) => {
// selector
const selectorParent = `.vp-uid-${uid}`;
let selector = `${selectorParent} ${s}`;
// add parent selector after `,`.
selector = selector.replace(
/, |,/g,
`, ${selectorParent} `
);
stylesString += `${selector} {`;
Object.keys(self.stylesList[uid][m][s]).forEach((p) => {
// property and value
stylesString += `${p}:${self.stylesList[uid][m][s][p]};`;
});
stylesString += '}';
});
// media
if (m) {
stylesString += '}';
}
});
}
// add in style tag
let $style = $(`#vp-style-${uid}`);
if (!$style.length) {
$style = $('<style>')
.attr('id', `vp-style-${uid}`)
.appendTo('head');
}
$style.html(stylesString);
self.emitEvent('renderStyle', [stylesString, self.stylesList, $style]);
}
/**
* First char to lower case
*
* @param {string} str string to transform
* @return {string} result string
*/
firstToLowerCase(str) {
return str.substr(0, 1).toLowerCase() + str.substr(1);
}
/**
* Init options
*
* @param {Object} userOptions user options
*/
initOptions(userOptions) {
const self = this;
// default options
self.defaults = {
layout: 'tile',
itemsGap: 0,
pagination: 'load-more',
};
// new user options
if (userOptions) {
self.userOptions = userOptions;
}
// prepare data options
const dataOptions = self.$item[0].dataset;
const pureDataOptions = {};
Object.keys(dataOptions).forEach((k) => {
if (k && k.substring(0, 2) === 'vp') {
pureDataOptions[self.firstToLowerCase(k.substring(2))] =
dataOptions[k];
}
});
self.options = $.extend(
{},
self.defaults,
pureDataOptions,
self.userOptions
);
self.emitEvent('initOptions');
}
/**
* Init events
*/
initEvents() {
const self = this;
const evp = `.vpf-uid-${self.uid}`;
// Stretch
function stretch() {
const rect = self.$item[0].getBoundingClientRect();
const { left } = rect;
const right = window.innerWidth - rect.right;
const ml = parseFloat(self.$item.css('margin-left') || 0);
const mr = parseFloat(self.$item.css('margin-right') || 0);
self.$item.css({
marginLeft: ml - left,
marginRight: mr - right,
maxWidth: 'none',
width: 'auto',
});
}
if (self.$item.hasClass('vp-portfolio__stretch') && !self.isPreview()) {
$wnd.on(`load${evp} resize${evp} orientationchange${evp}`, () => {
stretch();
});
stretch();
}
// add helper focus class
// TODO: change to CSS :has() when will be widely available
// @link https://caniuse.com/?search=%3Ahas
self.$item.on(`focus${evp}`, '.vp-portfolio__item a', function () {
const $item = $(this).closest('.vp-portfolio__item');
$item.addClass('vp-portfolio__item-focus');
if (isFocusVisible) {
$item.addClass('vp-portfolio__item-focus-visible');
}
});
self.$item.on(`blur${evp}`, '.vp-portfolio__item a', function () {
$(this)
.closest('.vp-portfolio__item')
.removeClass(
'vp-portfolio__item-focus vp-portfolio__item-focus-visible'
);
});
// on filter click
self.$filter.on(
`click${evp}`,
'.vp-filter .vp-filter__item a',
function (e) {
e.preventDefault();
const $this = $(this);
if (!self.loading) {
$this
.closest('.vp-filter__item')
.addClass('vp-filter__item-active')
.siblings()
.removeClass('vp-filter__item-active');
}
self.loadNewItems($this.attr('href'), true);
}
);
// on sort click
self.$sort.on(`click${evp}`, '.vp-sort .vp-sort__item a', function (e) {
e.preventDefault();
const $this = $(this);
if (!self.loading) {
$this
.closest('.vp-sort__item')
.addClass('vp-sort__item-active')
.siblings()
.removeClass('vp-sort__item-active');
}
self.loadNewItems($this.attr('href'), true);
});
// on filter/sort select change
self.$filter
.add(self.$sort)
.on(
`change${evp}`,
'.vp-filter select, .vp-sort select',
function () {
const $this = $(this);
const value = $this.val();
const $option = $this.find(`[value="${value}"]`);
if ($option.length) {
self.loadNewItems($option.attr('data-vp-url'), true);
}
}
);
// on pagination click
self.$item.on(
`click${evp}`,
'.vp-pagination .vp-pagination__item a',
function (e) {
e.preventDefault();
const $this = $(this);
const $pagination = $this.closest('.vp-pagination');
if (
$pagination.hasClass('vp-pagination__no-more') &&
self.options.pagination !== 'paged'
) {
return;
}
self.loadNewItems(
$this.attr('href'),
self.options.pagination === 'paged'
);
// Scroll to top
if (
self.options.pagination === 'paged' &&
$pagination.hasClass('vp-pagination__scroll-top')
) {
const $adminBar = $('#wpadminbar');
const currentTop =
window.pageYOffset ||
document.documentElement.scrollTop;
let { top } = self.$item.offset();
// Custom user offset.
if ($pagination.attr('data-vp-pagination-scroll-top')) {
top -=
parseInt(
$pagination.attr(
'data-vp-pagination-scroll-top'
),
10
) || 0;
}
// Admin bar offset.
if (
$adminBar.length &&
$adminBar.css('position') === 'fixed'
) {
top -= $adminBar.outerHeight();
}
// Limit max offset.
top = Math.max(0, top);
if (currentTop > top) {
window.scrollTo({
top,
behavior: 'smooth',
});
}
}
}
);
// on categories of item click
self.$item.on(
`click${evp}`,
'.vp-portfolio__items .vp-portfolio__item-meta-category a',
function (e) {
e.preventDefault();
e.stopPropagation();
self.loadNewItems($(this).attr('href'), true);
}
);
// resized container
self.$item.on(`transitionend${evp}`, '.vp-portfolio__items', (e) => {
if (e.currentTarget === e.target) {
self.resized();
}
});
self.emitEvent('initEvents');
}
/**
* Destroy events
*/
destroyEvents() {
const self = this;
const evp = `.vpf-uid-${self.uid}`;
// destroy click events
self.$item.off(evp);
self.$filter.off(evp);
self.$sort.off(evp);
// destroy window events
$wnd.off(evp);
self.emitEvent('destroyEvents');
}
/**
* Init layout
*/
initLayout() {
const self = this;
self.emitEvent('initLayout');
self.renderStyle();
}
/**
* Init custom color by data attributes:
* data-vp-bg-color
* data-vp-text-color
*/
initCustomColors() {
const self = this;
self.$item.find('[data-vp-bg-color]').each(function () {
const val = $(this).attr('data-vp-bg-color');
self.addStyle(`[data-vp-bg-color="${val}"]`, {
'background-color': `${val} !important`,
});
});
self.$item.find('[data-vp-text-color]').each(function () {
const val = $(this).attr('data-vp-text-color');
self.addStyle(`[data-vp-text-color="${val}"]`, {
color: `${val} !important`,
});
});
self.renderStyle();
self.emitEvent('initCustomColors');
}
/**
* Add New Items
*
* @param {object|dom|jQuery} $items - elements.
* @param {bool} removeExisting - remove existing elements.
* @param {Object} $newVP - new visual portfolio jQuery.
*/
addItems($items, removeExisting, $newVP) {
const self = this;
self.emitEvent('addItems', [$items, removeExisting, $newVP]);
}
/**
* Remove Items
*
* @param {object|dom|jQuery} $items - elements.
*/
removeItems($items) {
const self = this;
self.emitEvent('removeItems', [$items]);
}
/**
* AJAX Load New Items
*
* @param {string} url - url to request.
* @param {bool} removeExisting - remove existing elements.
* @param {Function} cb - callback.
*/
loadNewItems(url, removeExisting, cb) {
const self = this;
const { randomSeed } = self.options;
if (
(self.loading && typeof self.loading.readyState === 'undefined') ||
!url ||
self.href === url
) {
return;
}
// Abort previous AJAX loader to prevent conflict.
// We need it mostly for Search feature, because users can type also when already in loading state.
if (self.loading && self.loading.readyState && self.loading.abort) {
self.loading.abort();
}
const ajaxData = {
method: 'POST',
url,
data: {
vpf_ajax_call: true,
vpf_random_seed:
typeof randomSeed !== 'undefined' ? randomSeed : false,
},
complete({ responseText }) {
self.href = url;
self.replaceItems(responseText, removeExisting, cb);
},
};
self.loading = true;
self.$item.addClass('vp-portfolio__loading');
self.emitEvent('startLoadingNewItems', [url, ajaxData]);
self.loading = $.ajax(ajaxData);
}
/**
* Replace items to the new loaded using AJAX
*
* @param {string} content - new page content.
* @param {bool} removeExisting - remove existing elements.
* @param {Function} cb - callback.
*/
replaceItems(content, removeExisting, cb) {
const self = this;
if (!content) {
return;
}
// load to invisible container, then append to posts container
content = content
.replace('<body', '<body><div id="vp-ajax-load-body"')
.replace('</body>', '</div></body>');
const $body = $(content).filter('#vp-ajax-load-body');
// find current block on new page
const $newVP = $body.find(`.vp-portfolio.vp-uid-${self.uid}`);
// insert new items
if ($newVP.length) {
const newItems = $newVP.find('.vp-portfolio__items').html();
const nothingFound = $newVP.hasClass('vp-portfolio-not-found');
// We should clean up notices here, as they may be cloned over and over.
self.$item.find('.vp-notice').remove();
if (nothingFound) {
self.$item
.find('.vp-portfolio__items-wrap')
.before($newVP.find('.vp-notice').clone());
self.$item.addClass('vp-portfolio-not-found');
} else {
self.$item.removeClass('vp-portfolio-not-found');
}
// update filter
if (self.$filter.length) {
self.$filter.each(function () {
const $filter = $(this);
let newFilterContent = '';
if ($filter.parent().hasClass('vp-single-filter')) {
newFilterContent = $body
.find(
`[class="${$filter
.parent()
.attr('class')
.replace(
' vp-single-filter__ready',
''
)}"] .vp-portfolio__filter-wrap`
)
.html();
} else {
newFilterContent = $newVP
.find('.vp-portfolio__filter-wrap')
.html();
}
$filter.html(newFilterContent);
});
}
// update sort
if (self.$sort.length) {
self.$sort.each(function () {
const $sort = $(this);
let newFilterContent = '';
if ($sort.parent().hasClass('vp-single-sort')) {
newFilterContent = $body
.find(
`[class="${$sort
.parent()
.attr('class')
.replace(
' vp-single-sort__ready',
''
)}"] .vp-portfolio__sort-wrap`
)
.html();
} else {
newFilterContent = $newVP
.find('.vp-portfolio__sort-wrap')
.html();
}
$sort.html(newFilterContent);
});
}
// update pagination
if (self.$pagination.length) {
self.$pagination.html(
$newVP.find('.vp-portfolio__pagination-wrap').html()
);
}
self.addItems($(newItems), removeExisting, $newVP);
self.emitEvent('loadedNewItems', [$newVP, removeExisting, content]);
if (cb) {
cb();
}
}
// update next page data
const nextPageUrl = $newVP.attr('data-vp-next-page-url');
self.options.nextPageUrl = nextPageUrl;
self.$item.attr('data-vp-next-page-url', nextPageUrl);
self.$item.removeClass('vp-portfolio__loading');
self.loading = false;
self.emitEvent('endLoadingNewItems');
// images loaded
self.imagesLoaded();
// init custom colors
self.initCustomColors();
}
}
// extend VP object.
$(document).trigger('extendClass.vpf', [VP]);
// global definition
const plugin = function (options, ...args) {
let ret;
this.each(function () {
if (typeof ret !== 'undefined') {
return;
}
if (typeof options === 'object' || typeof options === 'undefined') {
if (!this.vpf) {
this.vpf = new VP($(this), options);
}
} else if (this.vpf) {
ret = this.vpf[options](...args);
}
});
return typeof ret !== 'undefined' ? ret : this;
};
plugin.constructor = VP;
// no conflict
const oldPlugin = $.fn.vpf;
$.fn.vpf = plugin;
$.fn.vpf.noConflict = function () {
$.fn.vpf = oldPlugin;
return this;
};
// initialization
$(() => {
$('.vp-portfolio').vpf();
});
const throttledInit = throttle(
200,
rafSchd(() => {
$('.vp-portfolio:not(.vp-portfolio__ready)').vpf();
})
);
if (window.MutationObserver) {
new window.MutationObserver(throttledInit).observe(
document.documentElement,
{
childList: true,
subtree: true,
}
);
} else {
$(document).on('DOMContentLoaded DOMNodeInserted load', () => {
throttledInit();
});
}

View File

@ -0,0 +1,51 @@
import $ from 'jquery';
import rafSchd from 'raf-schd';
import { throttle } from 'throttle-debounce';
const $wnd = $(window);
// Init infinite scroll pagination.
$(document).on('initEvents.vpf', (event, self) => {
if (event.namespace !== 'vpf' || self.options.pagination !== 'infinite') {
return;
}
const evp = `.vpf-uid-${self.uid}`;
const scrollThreshold = 400;
let visibilityCheckBusy = false;
function checkVisibilityAndLoad() {
if (visibilityCheckBusy || !self.options.nextPageUrl) {
return;
}
visibilityCheckBusy = true;
const rect = self.$item[0].getBoundingClientRect();
if (
rect.bottom > 0 &&
rect.bottom - scrollThreshold <= window.innerHeight
) {
self.loadNewItems(self.options.nextPageUrl, false, () => {
setTimeout(() => {
visibilityCheckBusy = false;
checkVisibilityAndLoad();
}, 300);
});
} else {
visibilityCheckBusy = false;
}
}
checkVisibilityAndLoad();
$wnd.on(
`load${evp} scroll${evp} resize${evp} orientationchange${evp}`,
throttle(
150,
rafSchd(() => {
checkVisibilityAndLoad();
})
)
);
});

View File

@ -0,0 +1,46 @@
import $ from 'jquery';
const { ResizeObserver } = window;
function setImgWidth($el) {
if ($el && $el.height > 1) {
$el.style.width = `${$el.height}px`;
}
}
// We need to use resize observer because for some reason in the Preview
// and on some mobile devices image height is 1px.
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach(({ target }) => {
if (target) {
setImgWidth(target);
}
});
});
// Init minimal paged pagination.
$(document).on('init.vpf loadedNewItems.vpf', (event, self) => {
if (
event.namespace !== 'vpf' ||
self.options.pagination !== 'paged' ||
!self.$pagination.children('.vp-pagination__style-minimal').length
) {
return;
}
// Hack used in Paged active item to make circle using hidden <img>.
// See styles for <img> tag in /templates/pagination/style.scss
const $activeItem = self.$pagination.find('.vp-pagination__item-active');
let $img = $activeItem.find('img');
if (!$img.length) {
$img = $(
'<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="">'
);
resizeObserver.observe($img[0]);
$activeItem.prepend($img);
setImgWidth($img[0]);
}
});

View File

@ -0,0 +1,209 @@
import $ from 'jquery';
const { VPData, VPPopupAPI } = window;
const { __, settingsPopupGallery } = VPData;
const $doc = $(document);
const $window = $(window);
if (typeof $.fancybox !== 'undefined' && VPPopupAPI) {
let fancyboxInstance;
// Extend Popup API.
VPPopupAPI.vendor = 'fancybox';
VPPopupAPI.open = function (items, index, self) {
const finalItems = [];
// prepare items for fancybox api.
items.forEach((item) => {
if (item.type === 'embed' && item.src) {
finalItems.push({
type: 'iframe',
src: item.src,
opts: {
width: item.width,
height: item.height,
caption: item.caption,
},
});
} else if (item.type === 'embed' && item.embed) {
finalItems.push({
type: 'html',
src: item.embed,
opts: {
width: item.width,
height: item.height,
caption: item.caption,
},
});
} else {
finalItems.push({
type: 'image',
src: item.src,
el: item.el,
opts: {
width: item.width,
height: item.height,
srcset: item.srcset,
caption: item.caption,
thumb: item.srcSmall,
},
});
}
});
const buttons = [];
if (settingsPopupGallery.show_zoom_button) {
buttons.push('zoom');
}
if (settingsPopupGallery.show_fullscreen_button) {
buttons.push('fullScreen');
}
if (settingsPopupGallery.show_slideshow) {
buttons.push('slideShow');
}
if (settingsPopupGallery.show_thumbs) {
buttons.push('thumbs');
}
if (settingsPopupGallery.show_share_button) {
buttons.push('share');
}
if (settingsPopupGallery.show_download_button) {
buttons.push('download');
}
if (settingsPopupGallery.show_close_button) {
buttons.push('close');
}
// define options
const options = {
// Close existing modals
// Set this to false if you do not need to stack multiple instances
closeExisting: true,
// Enable infinite gallery navigation
loop: true,
// Should display navigation arrows at the screen edges
arrows: settingsPopupGallery.show_arrows,
// Should display counter at the top left corner
infobar: settingsPopupGallery.show_counter,
// Should display close button (using `btnTpl.smallBtn` template) over the content
// Can be true, false, "auto"
// If "auto" - will be automatically enabled for "html", "inline" or "ajax" items
smallBtn: false,
// Should display toolbar (buttons at the top)
// Can be true, false, "auto"
// If "auto" - will be automatically hidden if "smallBtn" is enabled
toolbar: 'auto',
// What buttons should appear in the top right corner.
// Buttons will be created using templates from `btnTpl` option
// and they will be placed into toolbar (class="fancybox-toolbar"` element)
buttons,
// Custom CSS class for layout
baseClass: 'vp-fancybox',
// Hide browser vertical scrollbars; use at your own risk
hideScrollbar: true,
// Use mousewheel to navigate gallery
// If 'auto' - enabled for images only
wheel: false,
// Clicked on the content
clickContent(current) {
return current.type === 'image' &&
settingsPopupGallery.click_to_zoom
? 'zoom'
: false;
},
lang: 'wordpress',
i18n: {
wordpress: {
CLOSE: __.fancybox_close,
NEXT: __.fancybox_next,
PREV: __.fancybox_prev,
ERROR: __.fancybox_error,
PLAY_START: __.fancybox_play_start,
PLAY_STOP: __.fancybox_play_stop,
FULL_SCREEN: __.fancybox_full_screen,
THUMBS: __.fancybox_thumbs,
DOWNLOAD: __.fancybox_download,
SHARE: __.fancybox_share,
ZOOM: __.fancybox_zoom,
},
},
beforeClose() {
const currentItemData = items[fancyboxInstance.currIndex];
if (currentItemData) {
VPPopupAPI.maybeFocusGalleryItem(currentItemData);
}
if (self) {
self.emitEvent('beforeCloseFancybox', [
options,
items,
fancyboxInstance,
]);
}
fancyboxInstance = false;
},
beforeShow(e, instance) {
if (self) {
self.emitEvent('beforeShowFancybox', [e, instance]);
}
},
afterShow(e, instance) {
if (self) {
self.emitEvent('afterShowFancybox', [e, instance]);
}
},
};
if (self) {
self.emitEvent('beforeInitFancybox', [options, finalItems, index]);
}
// Disable Loop if only 1 item in gallery.
// We need this because Fancybox still let us scroll gallery using keyboard.
if (items.length === 1) {
options.loop = false;
}
// Start new fancybox instance
fancyboxInstance = $.fancybox.open(finalItems, options, index);
if (self) {
self.emitEvent('initFancybox', [
options,
finalItems,
index,
fancyboxInstance,
]);
}
};
VPPopupAPI.close = function () {
if (fancyboxInstance) {
fancyboxInstance.close();
fancyboxInstance = false;
}
};
// Fix zoom image sizes attribute.
// https://wordpress.org/support/topic/blurry-zoom-images/
$doc.on('transitionend', '.fancybox-content', function () {
const $img = $(this).find('.fancybox-image[sizes]');
const sizes = `${Math.round(100 * ($img.width() / $window.width()))}vw`;
$img.attr('sizes', sizes);
});
}

View File

@ -0,0 +1,130 @@
import $ from 'jquery';
// Extend VP class.
$(document).on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') {
return;
}
/**
* Init fjGallery plugin
*
* @param {mixed} options - gallery options.
* @param {mixed} additional - additional args.
*/
VP.prototype.initFjGallery = function (options = false, additional = null) {
const self = this;
if (self.$items_wrap.fjGallery && self.options.layout === 'justified') {
const initOptions =
options !== false
? options
: {
gutter: {
horizontal:
parseFloat(self.options.itemsGap) || 0,
vertical:
self.options.itemsGapVertical !== ''
? parseFloat(
self.options.itemsGapVertical
) || 0
: parseFloat(self.options.itemsGap) ||
0,
},
rowHeight:
parseFloat(self.options.justifiedRowHeight) ||
200,
maxRowsCount:
parseInt(
self.options.justifiedMaxRowsCount,
10
) || 0,
lastRow: self.options.justifiedLastRow || 'left',
rowHeightTolerance:
parseFloat(
self.options.justifiedRowHeightTolerance
) || 0,
calculateItemsHeight: true,
itemSelector: '.vp-portfolio__item-wrap',
imageSelector: '.vp-portfolio__item-img img',
transitionDuration: '0.3s',
};
if (initOptions.maxRowsCount === 0) {
initOptions.maxRowsCount = Number.POSITIVE_INFINITY;
}
self.emitEvent('beforeInitFjGallery', [initOptions, additional]);
self.$items_wrap.fjGallery(initOptions, additional);
self.emitEvent('initFjGallery', [initOptions, additional]);
}
};
/**
* Destroy fjGallery plugin
*/
VP.prototype.destroyFjGallery = function () {
const self = this;
const fjGallery = self.$items_wrap.data('fjGallery');
if (fjGallery) {
self.$items_wrap.fjGallery('destroy');
self.emitEvent('destroyFjGallery');
}
};
});
// Add Items.
$(document).on('addItems.vpf', (event, self, $items, removeExisting) => {
if (event.namespace !== 'vpf') {
return;
}
const fjGallery = self.$items_wrap.data('fjGallery');
if (!fjGallery) {
return;
}
if (removeExisting) {
self.destroyFjGallery();
self.$items_wrap.find('.vp-portfolio__item-wrap').remove();
self.$items_wrap.prepend($items);
self.initFjGallery();
} else {
self.$items_wrap.append($items);
self.initFjGallery('appendImages', $items);
}
});
// Init.
$(document).on('init.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.initFjGallery();
});
// Images Loaded.
$(document).on('imagesLoaded.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
// sometimes on iOs images failed to calculate positions, so we need this imagesLoaded event.
// related issue: https://github.com/nk-crew/visual-portfolio/issues/55
self.initFjGallery();
});
// Destroy.
$(document).on('destroy.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.destroyFjGallery();
});

View File

@ -0,0 +1,206 @@
import $ from 'jquery';
import rafSchd from 'raf-schd';
import { debounce, throttle } from 'throttle-debounce';
const { getComputedStyle } = window;
const $wnd = $(window);
const $doc = $(document);
const SUPPORTED_LAYOUTS = ['tiles', 'masonry', 'grid'];
// Extend VP class.
$doc.on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') {
return;
}
/**
* Init Isotope
* TODO: Check one of these scripts as alternative
* - https://github.com/haltu/muuri
* - https://github.com/Vestride/Shuffle
* - https://github.com/patrickkunka/mixitup
*
* @param {Object} options isotope options
*/
VP.prototype.initIsotope = function (options) {
const self = this;
if (
self.$items_wrap.isotope &&
SUPPORTED_LAYOUTS.includes(self.options.layout)
) {
const isRtl =
getComputedStyle(self.$items_wrap[0]).direction === 'rtl';
const initOptions = options || {
itemSelector: '.vp-portfolio__item-wrap',
layoutMode: 'masonry',
// masonry: {
// horizontalOrder: true
// },
transitionDuration: '0.3s',
percentPosition: true,
originLeft: !isRtl,
// See `initEvents.vpf` event why we need this option disabled.
resize: false,
};
self.emitEvent('beforeInitIsotope', [initOptions]);
self.$items_wrap.isotope(initOptions);
self.emitEvent('initIsotope', [initOptions]);
}
};
/**
* Destroy Isotope
*/
VP.prototype.destroyIsotope = function () {
const self = this;
const isotope = self.$items_wrap.data('isotope');
if (isotope) {
self.$items_wrap.isotope('destroy');
self.emitEvent('destroyIsotope');
}
};
});
// Add Items.
$doc.on('addItems.vpf', (event, self, $items, removeExisting) => {
if (event.namespace !== 'vpf') {
return;
}
const isotope = self.$items_wrap.data('isotope');
if (!isotope) {
return;
}
if (removeExisting) {
const $existing = self.$items_wrap.find('.vp-portfolio__item-wrap');
self.$items_wrap.isotope('remove', $existing);
// we need to prepend items when remove existing just because Tiles layout have troubles with appending and removing items
self.$items_wrap.prepend($items).isotope('prepended', $items);
} else {
self.$items_wrap.append($items).isotope('appended', $items);
}
// idk why, but with timeout isotope recalculate all items fine.
setTimeout(() => {
self.initIsotope('layout');
}, 0);
});
// Remove Items.
$doc.on('removeItems.vpf', (event, self, $items) => {
if (event.namespace !== 'vpf') {
return;
}
const isotope = self.$items_wrap.data('isotope');
if (!isotope) {
return;
}
self.$items_wrap.isotope('remove', $items);
});
// Init.
$doc.on('init.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.initIsotope();
});
// Images Loaded.
$doc.on('imagesLoaded.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
// sometimes on iOs images failed to calculate positions, so we need this imagesLoaded event.
// related issue: https://github.com/nk-crew/visual-portfolio/issues/55
self.initIsotope('layout');
});
// Destroy.
$doc.on('destroy.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.destroyIsotope();
});
// Init events.
$doc.on('initEvents.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
// We need to resize isotope manually, since the native relayout
// is not working properly, when container size is not changed
// but items sizes are changed in CSS. For some reason Isotope don't relayout it.
if (
self.$items_wrap.isotope &&
SUPPORTED_LAYOUTS.includes(self.options.layout)
) {
const evp = `.vpf-uid-${self.uid}`;
$wnd.on(
`resize${evp}`,
throttle(
100,
rafSchd(() => {
self.initIsotope('layout');
})
)
);
}
});
// Destroy events.
$doc.on('destroyEvents.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
if (SUPPORTED_LAYOUTS.includes(self.options.layout)) {
const evp = `.vpf-uid-${self.uid}`;
$wnd.off(`resize${evp}`);
}
});
// WPBakery Page Builder fullwidth row fix.
$doc.on(
'vc-full-width-row',
debounce(
150,
rafSchd((event, el) => {
$(el)
.find('.vp-portfolio')
.each(function () {
if (!this.vpf || !this.vpf.initIsotope) {
return;
}
const isotope = this.vpf.$items_wrap.data('isotope');
if (isotope) {
this.vpf.initIsotope('layout');
}
});
})
)
);

View File

@ -0,0 +1,515 @@
import isNumber from 'is-number';
import $ from 'jquery';
const {
Image,
VPData,
VPPopupAPI,
PhotoSwipe,
PhotoSwipeUI_Default: PhotoSwipeUIDefault,
} = window;
const { __, settingsPopupGallery } = VPData;
function resizeVideo(data, curItem) {
if (typeof curItem === 'undefined') {
if (data && data.itemHolders.length) {
data.itemHolders.forEach((val) => {
if (val.item && val.item.html) {
resizeVideo(data, val.item);
}
});
}
return;
}
// calculate real viewport in pixels
const vpW = data.viewportSize.x;
let vpH = data.viewportSize.y;
const ratio = curItem.vw / curItem.vh;
let resultW;
const $container = $(curItem.container);
const bars = data.options.barsSize;
let barTop = 0;
let barBot = 0;
if (bars) {
barTop = bars.top && bars.top !== 'auto' ? bars.top : 0;
barBot = bars.bottom && bars.bottom !== 'auto' ? bars.bottom : 0;
}
vpH -= barTop + barBot;
if (ratio > vpW / vpH) {
resultW = vpW;
} else {
resultW = vpH * ratio;
}
const $videoCont = $container.find('.vp-pswp-video');
$videoCont.css('max-width', resultW);
$videoCont.children().css({
paddingBottom: `${100 * (curItem.vh / curItem.vw)}%`,
});
$container.css({
top: barTop,
bottom: barBot,
});
}
if (PhotoSwipe && VPPopupAPI) {
let pswpInstance;
// prepare photoswipe markup
if (!$('.vp-pswp').length) {
const markup = `
<div class="pswp vp-pswp${
settingsPopupGallery.click_to_zoom ? '' : ' vp-pswp-no-zoom'
}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="pswp__bg"></div>
<div class="pswp__scroll-wrap">
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="${
__.pswp_close
}"></button>
<button class="pswp__button pswp__button--share" title="${
__.pswp_share
}"></button>
<button class="pswp__button pswp__button--fs" title="${
__.pswp_fs
}"></button>
<button class="pswp__button pswp__button--zoom" title="${
__.pswp_zoom
}"></button>
</div>
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="${
__.pswp_prev
}"></button>
<button class="pswp__button pswp__button--arrow--right" title="${
__.pswp_next
}"></button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
`;
$('body').append(markup);
}
// Extend Popup API.
VPPopupAPI.vendor = 'photoswipe';
VPPopupAPI.open = function (items, index, self) {
const finalItems = [];
// prepare items for fancybox api.
items.forEach((item) => {
if (item.type === 'embed') {
finalItems.push({
html: `<div class="vp-pswp-video"><div>${item.embed}</div></div>`,
vw: item.width || 0,
vh: item.height || 0,
title: item.caption,
});
} else {
finalItems.push({
src: item.src,
el: item.el,
w: item.width || 0,
h: item.height || 0,
title: item.caption,
o: {
src: item.src,
w: item.width || 0,
h: item.height || 0,
},
...(item.srcMedium
? {
m: {
src: item.srcMedium,
w: item.srcMediumWidth || 0,
h: item.srcMediumHeight || 0,
},
msrc: item.srcMedium,
}
: {}),
});
}
});
const $pswpElement = $('.vp-pswp');
const pswpElement = $pswpElement[0];
// define options (if needed)
const options = {
captionAndToolbarShowEmptyCaptions: false,
closeEl: settingsPopupGallery.show_close_button,
captionEl: true,
fullscreenEl: settingsPopupGallery.show_fullscreen_button,
zoomEl: settingsPopupGallery.show_zoom_button,
shareEl: settingsPopupGallery.show_share_button,
counterEl: settingsPopupGallery.show_counter,
arrowEl: settingsPopupGallery.show_arrows,
shareButtons: [
{
id: 'facebook',
label: __.pswp_share_fb,
url: 'https://www.facebook.com/sharer/sharer.php?u={{url}}',
},
{
id: 'twitter',
label: __.pswp_share_tw,
url: 'https://twitter.com/intent/tweet?text={{text}}&url={{url}}',
},
{
id: 'pinterest',
label: __.pswp_share_pin,
url: 'https://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{text}}',
},
],
getImageURLForShare() {
const currentItem = items[pswpInstance.getCurrentIndex()];
if (currentItem.type === 'image' && currentItem.src) {
return currentItem.src;
}
return pswpInstance.currItem.src || '';
},
getPageURLForShare() {
const currentItem = items[pswpInstance.getCurrentIndex()];
if (currentItem.type === 'image' && currentItem.src) {
return currentItem.src;
}
return window.location.href;
},
getTextForShare() {
const currentItem = items[pswpInstance.getCurrentIndex()];
if (currentItem.caption) {
const $caption = $(currentItem.caption);
if (
$caption.filter('.vp-portfolio__item-popup-title')
.length
) {
return $caption
.filter('.vp-portfolio__item-popup-title')
.text();
}
if (
$caption.filter('.vp-portfolio__item-popup-description')
.length
) {
return $caption
.filter('.vp-portfolio__item-popup-description')
.text();
}
}
return '';
},
bgOpacity: 1,
tapToClose: false,
tapToToggleControls: true,
showHideOpacity: true,
history: false,
getThumbBoundsFn(thumbIndex) {
if (!finalItems[thumbIndex] || !finalItems[thumbIndex].el) {
return false;
}
const $el = $(finalItems[thumbIndex].el).find('img')[0];
if (!$el) {
return false;
}
const rect = $el.getBoundingClientRect();
const pageYScroll =
window.pageYOffset || document.documentElement.scrollTop;
const pswpTop = parseFloat($pswpElement.css('top')) || 0;
return {
x: rect.left,
y: rect.top + pageYScroll - pswpTop,
w: rect.width,
h: rect.height,
};
},
getDoubleTapZoom(isMouseClick, item) {
// isMouseClick - true if mouse, false if double-tap
// item - slide object that is zoomed, usually current
// item.initialZoomLevel - initial scale ratio of image
// e.g. if viewport is 700px and image is 1400px,
// initialZoomLevel will be 0.5
if (isMouseClick) {
// is mouse click on image or zoom icon
// Click to zoom disabled.
if (!settingsPopupGallery.click_to_zoom) {
return item.initialZoomLevel;
}
// In case the image is vertically wide, zoom it to fit screen width only.
// - check if original image size is wider than screen
// - check if zoomed out image in less than 25% of the screen width
if (
item.w > window.innerWidth &&
(item.w * item.initialZoomLevel) / window.innerWidth <
0.25
) {
return window.innerWidth / item.w;
}
// zoom to original
return 1;
// e.g. for 1400px image:
// 0.5 - zooms to 700px
// 2 - zooms to 2800px
}
// zoom to original if initial zoom is less than 0.7x,
// otherwise to 1.5x, to make sure that double-tap gesture always zooms image.
return item.initialZoomLevel < 0.7 ? 1 : 1.5;
},
};
options.index = parseInt(index, 10);
// exit if index not found
if (!isNumber(options.index)) {
return;
}
// Pass data to PhotoSwipe and initialize it
pswpInstance = new PhotoSwipe(
pswpElement,
PhotoSwipeUIDefault,
finalItems,
options
);
// see: http://photoswipe.com/documentation/responsive-images.html
let realViewportWidth;
let useLargeImages = false;
let firstResize = true;
let imageSrcWillChange;
pswpInstance.listen('beforeResize', () => {
// pswpInstance.viewportSize.x - width of PhotoSwipe viewport
// pswpInstance.viewportSize.y - height of PhotoSwipe viewport
// window.devicePixelRatio - ratio between physical pixels and device independent pixels (Number)
// 1 (regular display), 2 (@2x, retina) ...
// calculate real pixels when size changes
realViewportWidth =
pswpInstance.viewportSize.x * window.devicePixelRatio;
// Code below is needed if you want image to switch dynamically on window.resize
// Find out if current images need to be changed
if (useLargeImages && realViewportWidth < 1000) {
useLargeImages = false;
imageSrcWillChange = true;
} else if (!useLargeImages && realViewportWidth >= 1000) {
useLargeImages = true;
imageSrcWillChange = true;
}
// Invalidate items only when source is changed and when it's not the first update
if (imageSrcWillChange && !firstResize) {
// invalidateCurrItems sets a flag on slides that are in DOM,
// which will force update of content (image) on window.resize.
pswpInstance.invalidateCurrItems();
}
if (firstResize) {
firstResize = false;
}
imageSrcWillChange = false;
});
pswpInstance.listen('gettingData', (idx, item) => {
// Prepare iframes.
if (item.html) {
// -- Iframe Autoplay - Part 1 --
// Disable autoplay parameter in iframes on inactive slides.
// Mostly for Youtube and Vimeo to prevent video playing in background.
// Later we add autoplay only to active slides.
item.html = item.html.replace(/autoplay=1/, 'autoplay=0');
return;
}
// Prepare image sizes.
if (useLargeImages && item.o) {
if (item.o.src) {
item.src = item.o.src;
}
if (item.o.w) {
item.w = item.o.w;
}
if (item.o.h) {
item.h = item.o.h;
}
} else if (item.m) {
if (item.m.src) {
item.src = item.m.src;
}
if (item.m.w) {
item.w = item.m.w;
}
if (item.m.h) {
item.h = item.m.h;
}
}
});
pswpInstance.listen('imageLoadComplete', (idx, item) => {
if (item.h < 1 || item.w < 1) {
const img = new Image();
img.onload = () => {
item.w = img.width;
item.h = img.height;
pswpInstance.invalidateCurrItems();
pswpInstance.updateSize(true);
};
img.src = item.src;
}
});
pswpInstance.listen('resize', function () {
resizeVideo(this);
});
pswpInstance.listen('afterChange', function () {
resizeVideo(this);
if (self) {
self.emitEvent('afterChangePhotoSwipe', [this, pswpInstance]);
}
});
// disable video play if no active.
pswpInstance.listen('beforeChange', function () {
const data = this;
// -- Iframe Autoplay - Part 2 --
// Set autoplay to 1 on active slides and to 0 on inactive.
if (data && data.itemHolders.length) {
const currentIndex = data.getCurrentIndex();
data.itemHolders.forEach((val) => {
const $iframe = val.el
? $(val.el).find('.vp-pswp-video iframe')
: false;
if ($iframe && $iframe.length) {
if (val.index === currentIndex) {
$iframe.attr(
'src',
$iframe
.attr('src')
.replace(/autoplay=0/, 'autoplay=1')
);
} else {
$iframe.attr(
'src',
$iframe
.attr('src')
.replace(/autoplay=1/, 'autoplay=0')
);
}
}
});
}
if (self) {
self.emitEvent('beforeChangePhotoSwipe', [data, pswpInstance]);
}
});
// destroy event.
pswpInstance.listen('destroy', function () {
const data = this;
if (data) {
// Remove video block.
if (data.itemHolders.length) {
data.itemHolders.forEach((val) => {
if (val.el) {
$(val.el).find('.vp-pswp-video').remove();
}
});
}
const currentItemData = items[data.getCurrentIndex()];
if (currentItemData) {
VPPopupAPI.maybeFocusGalleryItem(currentItemData);
}
if (self) {
self.emitEvent('beforeClosePhotoSwipe', [
options,
items,
pswpInstance,
]);
}
}
pswpInstance = false;
});
if (self) {
self.emitEvent('beforeInitPhotoSwipe', [
options,
finalItems,
index,
pswpInstance,
]);
}
pswpInstance.init();
if (self) {
self.emitEvent('initPhotoSwipe', [
options,
finalItems,
index,
pswpInstance,
]);
}
};
VPPopupAPI.close = function () {
if (pswpInstance) {
pswpInstance.close();
pswpInstance = false;
}
};
}

View File

@ -0,0 +1,431 @@
import isNumber from 'is-number';
import $ from 'jquery';
const $doc = $(document);
const { screenSizes } = window.VPData;
function getSwiperVersion(Swiper) {
let ver = 8;
// in version 8 added new parameter `maxBackfaceHiddenSlides`.
if (typeof Swiper.defaults.maxBackfaceHiddenSlides === 'undefined') {
ver = 7;
}
// in version 7 added new parameter `rewind`.
if (typeof Swiper.defaults.rewind === 'undefined') {
ver = 6;
}
// in version 6 added new parameter `loopPreventsSlide`.
if (typeof Swiper.defaults.loopPreventsSlide === 'undefined') {
ver = 5;
}
return ver;
}
// Extend VP class.
$doc.on('extendClass.vpf', (event, VP) => {
if (event.namespace !== 'vpf') {
return;
}
/**
* Init Swiper plugin
*
* @param {mixed} options - slider options.
*/
VP.prototype.initSwiper = function (options = false) {
const self = this;
if (
self.options.layout === 'slider' &&
typeof window.Swiper !== 'undefined'
) {
const $parent = self.$items_wrap.parent();
$parent.addClass('swiper');
self.$items_wrap.addClass('swiper-wrapper');
self.$items_wrap.children().addClass('swiper-slide');
// calculate responsive.
let slidesPerView = self.options.sliderSlidesPerView || 3;
const breakPoints = {};
if (self.options.sliderEffect === 'fade') {
slidesPerView = 1;
}
if (isNumber(slidesPerView)) {
let count = slidesPerView;
let currentPoint = Math.min(screenSizes.length - 1, count - 1);
for (; currentPoint >= 0; currentPoint -= 1) {
if (
count > 0 &&
typeof screenSizes[currentPoint] !== 'undefined'
) {
breakPoints[screenSizes[currentPoint] + 1] = {
slidesPerView: count,
};
}
count -= 1;
}
slidesPerView = count || 1;
}
let optionsThumbs = false;
let $thumbsParent = false;
options = options || {
speed: (parseFloat(self.options.sliderSpeed) || 0) * 1000,
autoHeight: self.options.sliderItemsHeight === 'auto',
effect: self.options.sliderEffect || 'slide',
// fix fade items collapse (mostly in Default items style).
fadeEffect: {
crossFade: true,
},
spaceBetween: parseFloat(self.options.itemsGap) || 0,
centeredSlides: self.options.sliderCenteredSlides === 'true',
freeMode: {
enabled: self.options.sliderFreeMode === 'true',
sticky: self.options.sliderFreeModeSticky === 'true',
},
loop: self.options.sliderLoop === 'true',
// This feature is cool, but not working properly when loop enabled
// and fast clicking on previous button is not working properly
// https://github.com/nolimits4web/swiper/issues/5945
// loopPreventsSlide: false,
autoplay: parseFloat(self.options.sliderAutoplay) > 0 && {
delay: parseFloat(self.options.sliderAutoplay) * 1000,
disableOnInteraction: false,
},
navigation: self.options.sliderArrows === 'true' && {
nextEl: '.vp-portfolio__items-arrow-next',
prevEl: '.vp-portfolio__items-arrow-prev',
},
pagination: self.options.sliderBullets === 'true' && {
el: '.vp-portfolio__items-bullets',
clickable: true,
dynamicBullets:
self.options.sliderBulletsDynamic === 'true',
renderBullet(index, className) {
return `<span class="${className}" data-bullet-index="${index}" data-bullet-number="${
index + 1
}"></span>`;
},
},
mousewheel: self.options.sliderMousewheel === 'true',
slidesPerView,
breakpoints: breakPoints,
keyboard: true,
grabCursor: true,
preloadImages: false,
// fixes text selection when swipe in the items gap.
touchEventsTarget: 'container',
};
// fix first load slide position (seems like a conflict with lazySizes)
// issue: https://github.com/nk-crew/visual-portfolio/issues/54
if (options.speed === 0) {
options.speed = 1;
}
let positionFix = 0;
options.on = {
transitionEnd() {
if (positionFix === 0) {
positionFix = 1;
this.setTransition(1);
this.setTranslate(this.translate + 0.1);
} else if (positionFix === 1) {
positionFix = 2;
this.slideReset();
}
},
// These events used to add fixes for
// conflict with custom cursor movement.
touchStart(swiper, e) {
self.emitEvent('swiperTouchStart', [swiper, e]);
},
touchMove(swiper, e) {
self.emitEvent('swiperTouchMove', [swiper, e]);
},
touchEnd(swiper, e) {
self.emitEvent('swiperTouchEnd', [swiper, e]);
},
};
self.emitEvent('beforeInitSwiper', [options]);
// thumbnails.
if (self.$slider_thumbnails_wrap.length) {
$thumbsParent = self.$slider_thumbnails_wrap.parent();
$thumbsParent.addClass('swiper');
self.$slider_thumbnails_wrap.addClass('swiper-wrapper');
self.$slider_thumbnails_wrap
.children()
.addClass('swiper-slide');
// calculate responsive.
let thumbnailsPerView =
self.options.sliderThumbnailsPerView || 8;
const thumbnailsBreakPoints = {};
if (isNumber(thumbnailsPerView)) {
let count = thumbnailsPerView;
let currentPoint = Math.min(
screenSizes.length - 1,
count - 1
);
for (; currentPoint >= 0; currentPoint -= 1) {
if (
count > 0 &&
typeof screenSizes[currentPoint] !== 'undefined'
) {
thumbnailsBreakPoints[
screenSizes[currentPoint] + 1
] = {
slidesPerView: count,
};
}
count -= 1;
}
thumbnailsPerView = count || 1;
}
optionsThumbs = {
autoHeight: self.options.sliderThumbnailsHeight === 'auto',
effect: 'slide',
spaceBetween:
parseFloat(self.options.sliderThumbnailsGap) || 0,
loop: false,
// This feature is cool, but not working properly when loop enabled
// and fast clicking on previous button is not working properly
// https://github.com/nolimits4web/swiper/issues/5945
// loopPreventsSlide: false,
freeMode: {
enabled: true,
sticky: true,
},
loopedSlides: 5,
slidesPerView: thumbnailsPerView,
breakpoints: thumbnailsBreakPoints,
keyboard: true,
grabCursor: true,
watchSlidesVisibility: true,
watchSlidesProgress: true,
preloadImages: false,
// fixed text selection when swipe in the items gap.
touchEventsTarget: 'container',
on: {
// These events used to add fixes for
// conflict with custom cursor movement.
touchStart(swiper, e) {
self.emitEvent('swiperTouchStart', [swiper, e]);
},
touchMove(swiper, e) {
self.emitEvent('swiperTouchMove', [swiper, e]);
},
touchEnd(swiper, e) {
self.emitEvent('swiperTouchEnd', [swiper, e]);
},
},
};
}
// Fallbacks for old Swiper versions.
(() => {
const swiperVersion = getSwiperVersion(window.Swiper);
const isThumbsEnabled =
optionsThumbs && $thumbsParent && $thumbsParent[0];
// Since v7 used container class `swiper`, we should also add old `swiper-container` class.
if (swiperVersion < 7) {
$parent.addClass('swiper-container');
if (isThumbsEnabled) {
$thumbsParent.addClass('swiper-container');
}
}
// Since v7 freeMode options moved under `freeMode` object.
if (swiperVersion < 7) {
options.freeModeSticky = options.freeMode.sticky;
options.freeMode = options.freeMode.enabled;
if (isThumbsEnabled) {
optionsThumbs.freeModeSticky =
optionsThumbs.freeMode.sticky;
optionsThumbs.freeMode = optionsThumbs.freeMode.enabled;
}
}
// Since v5 `breakpointsInverse` option is removed and it is now `true` by default, but in older versions it was `false`.
if (swiperVersion >= 5) {
options.breakpointsInverse = true;
if (isThumbsEnabled) {
optionsThumbs.breakpointsInverse = true;
}
}
})();
// Init Swiper.
if (optionsThumbs && $thumbsParent && $thumbsParent[0]) {
const swiperThumbs = new window.Swiper(
$thumbsParent[0],
optionsThumbs
);
options.thumbs = {
swiper: swiperThumbs,
};
}
const instance = new window.Swiper($parent[0], options);
// Autoplay Hover Pause.
if (
self.options.sliderAutoplayHoverPause === 'true' &&
parseFloat(self.options.sliderAutoplay) > 0
) {
self.$item.on(
`mouseenter.vpf-uid-${self.uid}`,
'.swiper',
() => {
$parent[0].swiper.autoplay.stop();
}
);
self.$item.on(
`mouseleave.vpf-uid-${self.uid}`,
'.swiper',
() => {
$parent[0].swiper.autoplay.start();
}
);
}
self.emitEvent('initSwiper', [options, instance]);
}
};
/**
* Destroy Swiper plugin
*/
VP.prototype.destroySwiper = function () {
const self = this;
const $parent = self.$items_wrap.parent();
const $thumbsParent = self.$slider_thumbnails_wrap.length
? self.$slider_thumbnails_wrap.parent()
: false;
const SliderSwiper = $parent[0].swiper;
const ThumbsSwiper = $thumbsParent ? $thumbsParent[0].swiper : false;
let isDestroyed = false;
// Thumbnails.
if (ThumbsSwiper) {
ThumbsSwiper.destroy();
$thumbsParent.removeClass('swiper');
self.$slider_thumbnails_wrap.removeClass('swiper-wrapper');
self.$slider_thumbnails_wrap.children().removeClass('swiper-slide');
isDestroyed = true;
}
// Slider.
if (SliderSwiper) {
SliderSwiper.destroy();
$parent.removeClass('swiper');
self.$items_wrap.removeClass('swiper-wrapper');
self.$items_wrap.children().removeClass('swiper-slide');
$parent
.find('.vp-portfolio__items-bullets')
.removeClass(
'swiper-pagination-clickable swiper-pagination-bullets-dynamic'
)
.removeAttr('style')
.html('');
isDestroyed = true;
}
if (isDestroyed) {
self.emitEvent('destroySwiper');
}
};
});
// Add Items.
$doc.on('addItems.vpf', (event, self, $items, removeExisting, $newVP) => {
if (event.namespace !== 'vpf') {
return;
}
const Swiper = self.$items_wrap.parent()[0].swiper;
if (!Swiper) {
return;
}
// Slider.
{
if (removeExisting) {
Swiper.removeAllSlides();
}
const appendArr = [];
$items.addClass('swiper-slide').each(function () {
appendArr.push(this);
});
Swiper.appendSlide(appendArr);
}
// Thumbnails.
const ThumbsSwiper = self.$slider_thumbnails_wrap.length
? self.$slider_thumbnails_wrap.parent()[0].swiper
: false;
if (ThumbsSwiper) {
if (removeExisting) {
ThumbsSwiper.removeAllSlides();
}
const appendArr = [];
$newVP
.find('.vp-portfolio__thumbnails > .vp-portfolio__thumbnail-wrap')
.clone()
.addClass('swiper-slide')
.each(function () {
appendArr.push(this);
});
ThumbsSwiper.appendSlide(appendArr);
}
});
// Init.
$doc.on('init.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.initSwiper();
});
// Destroy.
$doc.on('destroy.vpf', (event, self) => {
if (event.namespace !== 'vpf') {
return;
}
self.destroySwiper();
});

View File

@ -0,0 +1,635 @@
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);
}
);
}

View File

@ -0,0 +1,93 @@
import $ from 'jquery';
const $body = $('body');
const $doc = $(document);
const $preview = $('#vp_preview');
// prevent click on links.
document.addEventListener(
'click',
(e) => {
e.stopPropagation();
e.preventDefault();
if (window.parentIFrame) {
window.parentIFrame.sendMessage('clicked');
}
},
true
);
// prevent click on <select> and similar elements.
document.addEventListener(
'mousedown',
(e) => {
e.stopPropagation();
e.preventDefault();
e.target.blur();
window.focus();
},
true
);
// add dynamic data to AJAX calls.
$doc.on('startLoadingNewItems.vpf', (event, vpObject, url, ajaxData) => {
if (event.namespace !== 'vpf') {
return;
}
ajaxData.data = Object.assign(
ajaxData.data || {},
window.vp_preview_post_data
);
});
// Dynamic CSS cache.
const dynamicCSScache = {};
// configure iFrame resizer script.
window.iFrameResizer = {
log: false,
heightCalculationMethod() {
return $preview.outerHeight(true);
},
onMessage(data) {
if (!data || !data.name) {
return;
}
switch (data.name) {
case 'resize':
// This random number needed for proper resize Isotope and other plugins.
$body.css('max-width', data.width + Math.random());
break;
case 'dynamic-css': {
// Insert dynamic styles.
const styleId = `vp-dynamic-styles-${data.blockId}-inline-css`;
// Skip if styles haven't changed.
if (
dynamicCSScache[styleId] &&
data.styles === dynamicCSScache[styleId]
) {
break;
}
let $style = $(`#${styleId}`);
if (!$style.length) {
$style = $(`<style id="${styleId}"></style>`).appendTo(
'head'
);
}
dynamicCSScache[styleId] = data.styles;
$style.text(data.styles);
break;
}
// no default
}
},
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,19 @@
.fj-gallery {
position: relative;
overflow: hidden;
}
.fj-gallery::after {
content: "";
display: block;
clear: both;
}
.fj-gallery .fj-gallery-item {
float: left;
top: 0;
left: 0;
}
.fj-gallery .fj-gallery-item > img {
display: block;
width: 100%;
height: auto;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,482 @@
/*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */
/*
Contents:
1. Buttons
2. Share modal and links
3. Index indicator ("1 of X" counter)
4. Caption
5. Loading indicator
6. Additional styles (root element, top bar, idle state, hidden state, etc.)
*/
/*
1. Buttons
*/
/* <button> css reset */
.pswp__button {
width: 44px;
height: 44px;
position: relative;
background: none;
cursor: pointer;
overflow: visible;
-webkit-appearance: none;
display: block;
border: 0;
padding: 0;
margin: 0;
float: right;
opacity: 0.75;
-webkit-transition: opacity 0.2s;
transition: opacity 0.2s;
-webkit-box-shadow: none;
box-shadow: none; }
.pswp__button:focus, .pswp__button:hover {
opacity: 1; }
.pswp__button:active {
outline: none;
opacity: 0.9; }
.pswp__button::-moz-focus-inner {
padding: 0;
border: 0; }
/* pswp__ui--over-close class it added when mouse is over element that should close gallery */
.pswp__ui--over-close .pswp__button--close {
opacity: 1; }
.pswp__button,
.pswp__button--arrow--left:before,
.pswp__button--arrow--right:before {
background: url(default-skin.png) 0 0 no-repeat;
background-size: 264px 88px;
width: 44px;
height: 44px; }
@media (-webkit-min-device-pixel-ratio: 1.1), (-webkit-min-device-pixel-ratio: 1.09375), (min-resolution: 105dpi), (min-resolution: 1.1dppx) {
/* Serve SVG sprite if browser supports SVG and resolution is more than 105dpi */
.pswp--svg .pswp__button,
.pswp--svg .pswp__button--arrow--left:before,
.pswp--svg .pswp__button--arrow--right:before {
background-image: url(default-skin.svg); }
.pswp--svg .pswp__button--arrow--left,
.pswp--svg .pswp__button--arrow--right {
background: none; } }
.pswp__button--close {
background-position: 0 -44px; }
.pswp__button--share {
background-position: -44px -44px; }
.pswp__button--fs {
display: none; }
.pswp--supports-fs .pswp__button--fs {
display: block; }
.pswp--fs .pswp__button--fs {
background-position: -44px 0; }
.pswp__button--zoom {
display: none;
background-position: -88px 0; }
.pswp--zoom-allowed .pswp__button--zoom {
display: block; }
.pswp--zoomed-in .pswp__button--zoom {
background-position: -132px 0; }
/* no arrows on touch screens */
.pswp--touch .pswp__button--arrow--left,
.pswp--touch .pswp__button--arrow--right {
visibility: hidden; }
/*
Arrow buttons hit area
(icon is added to :before pseudo-element)
*/
.pswp__button--arrow--left,
.pswp__button--arrow--right {
background: none;
top: 50%;
margin-top: -50px;
width: 70px;
height: 100px;
position: absolute; }
.pswp__button--arrow--left {
left: 0; }
.pswp__button--arrow--right {
right: 0; }
.pswp__button--arrow--left:before,
.pswp__button--arrow--right:before {
content: '';
top: 35px;
background-color: rgba(0, 0, 0, 0.3);
height: 30px;
width: 32px;
position: absolute; }
.pswp__button--arrow--left:before {
left: 6px;
background-position: -138px -44px; }
.pswp__button--arrow--right:before {
right: 6px;
background-position: -94px -44px; }
/*
2. Share modal/popup and links
*/
.pswp__counter,
.pswp__share-modal {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; }
.pswp__share-modal {
display: block;
background: rgba(0, 0, 0, 0.5);
width: 100%;
height: 100%;
top: 0;
left: 0;
padding: 10px;
position: absolute;
z-index: 1600;
opacity: 0;
-webkit-transition: opacity 0.25s ease-out;
transition: opacity 0.25s ease-out;
-webkit-backface-visibility: hidden;
will-change: opacity; }
.pswp__share-modal--hidden {
display: none; }
.pswp__share-tooltip {
z-index: 1620;
position: absolute;
background: #FFF;
top: 56px;
border-radius: 2px;
display: block;
width: auto;
right: 44px;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
-webkit-transform: translateY(6px);
-ms-transform: translateY(6px);
transform: translateY(6px);
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
-webkit-backface-visibility: hidden;
will-change: transform; }
.pswp__share-tooltip a {
display: block;
padding: 8px 12px;
color: #000;
text-decoration: none;
font-size: 14px;
line-height: 18px; }
.pswp__share-tooltip a:hover {
text-decoration: none;
color: #000; }
.pswp__share-tooltip a:first-child {
/* round corners on the first/last list item */
border-radius: 2px 2px 0 0; }
.pswp__share-tooltip a:last-child {
border-radius: 0 0 2px 2px; }
.pswp__share-modal--fade-in {
opacity: 1; }
.pswp__share-modal--fade-in .pswp__share-tooltip {
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0); }
/* increase size of share links on touch devices */
.pswp--touch .pswp__share-tooltip a {
padding: 16px 12px; }
a.pswp__share--facebook:before {
content: '';
display: block;
width: 0;
height: 0;
position: absolute;
top: -12px;
right: 15px;
border: 6px solid transparent;
border-bottom-color: #FFF;
-webkit-pointer-events: none;
-moz-pointer-events: none;
pointer-events: none; }
a.pswp__share--facebook:hover {
background: #3E5C9A;
color: #FFF; }
a.pswp__share--facebook:hover:before {
border-bottom-color: #3E5C9A; }
a.pswp__share--twitter:hover {
background: #55ACEE;
color: #FFF; }
a.pswp__share--pinterest:hover {
background: #CCC;
color: #CE272D; }
a.pswp__share--download:hover {
background: #DDD; }
/*
3. Index indicator ("1 of X" counter)
*/
.pswp__counter {
position: absolute;
left: 0;
top: 0;
height: 44px;
font-size: 13px;
line-height: 44px;
color: #FFF;
opacity: 0.75;
padding: 0 10px; }
/*
4. Caption
*/
.pswp__caption {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
min-height: 44px; }
.pswp__caption small {
font-size: 11px;
color: #BBB; }
.pswp__caption__center {
text-align: left;
max-width: 420px;
margin: 0 auto;
font-size: 13px;
padding: 10px;
line-height: 20px;
color: #CCC; }
.pswp__caption--empty {
display: none; }
/* Fake caption element, used to calculate height of next/prev image */
.pswp__caption--fake {
visibility: hidden; }
/*
5. Loading indicator (preloader)
You can play with it here - http://codepen.io/dimsemenov/pen/yyBWoR
*/
.pswp__preloader {
width: 44px;
height: 44px;
position: absolute;
top: 0;
left: 50%;
margin-left: -22px;
opacity: 0;
-webkit-transition: opacity 0.25s ease-out;
transition: opacity 0.25s ease-out;
will-change: opacity;
direction: ltr; }
.pswp__preloader__icn {
width: 20px;
height: 20px;
margin: 12px; }
.pswp__preloader--active {
opacity: 1; }
.pswp__preloader--active .pswp__preloader__icn {
/* We use .gif in browsers that don't support CSS animation */
background: url(preloader.gif) 0 0 no-repeat; }
.pswp--css_animation .pswp__preloader--active {
opacity: 1; }
.pswp--css_animation .pswp__preloader--active .pswp__preloader__icn {
-webkit-animation: clockwise 500ms linear infinite;
animation: clockwise 500ms linear infinite; }
.pswp--css_animation .pswp__preloader--active .pswp__preloader__donut {
-webkit-animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite;
animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite; }
.pswp--css_animation .pswp__preloader__icn {
background: none;
opacity: 0.75;
width: 14px;
height: 14px;
position: absolute;
left: 15px;
top: 15px;
margin: 0; }
.pswp--css_animation .pswp__preloader__cut {
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
*/
position: relative;
width: 7px;
height: 14px;
overflow: hidden; }
.pswp--css_animation .pswp__preloader__donut {
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 14px;
height: 14px;
border: 2px solid #FFF;
border-radius: 50%;
border-left-color: transparent;
border-bottom-color: transparent;
position: absolute;
top: 0;
left: 0;
background: none;
margin: 0; }
@media screen and (max-width: 1024px) {
.pswp__preloader {
position: relative;
left: auto;
top: auto;
margin: 0;
float: right; } }
@-webkit-keyframes clockwise {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes clockwise {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@-webkit-keyframes donut-rotate {
0% {
-webkit-transform: rotate(0);
transform: rotate(0); }
50% {
-webkit-transform: rotate(-140deg);
transform: rotate(-140deg); }
100% {
-webkit-transform: rotate(0);
transform: rotate(0); } }
@keyframes donut-rotate {
0% {
-webkit-transform: rotate(0);
transform: rotate(0); }
50% {
-webkit-transform: rotate(-140deg);
transform: rotate(-140deg); }
100% {
-webkit-transform: rotate(0);
transform: rotate(0); } }
/*
6. Additional styles
*/
/* root element of UI */
.pswp__ui {
-webkit-font-smoothing: auto;
visibility: visible;
opacity: 1;
z-index: 1550; }
/* top black bar with buttons and "1 of X" indicator */
.pswp__top-bar {
position: absolute;
left: 0;
top: 0;
height: 44px;
width: 100%; }
.pswp__caption,
.pswp__top-bar,
.pswp--has_mouse .pswp__button--arrow--left,
.pswp--has_mouse .pswp__button--arrow--right {
-webkit-backface-visibility: hidden;
will-change: opacity;
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
/* pswp--has_mouse class is added only when two subsequent mousemove events occur */
.pswp--has_mouse .pswp__button--arrow--left,
.pswp--has_mouse .pswp__button--arrow--right {
visibility: visible; }
.pswp__top-bar,
.pswp__caption {
background-color: rgba(0, 0, 0, 0.5); }
/* pswp__ui--fit class is added when main image "fits" between top bar and bottom bar (caption) */
.pswp__ui--fit .pswp__top-bar,
.pswp__ui--fit .pswp__caption {
background-color: rgba(0, 0, 0, 0.3); }
/* pswp__ui--idle class is added when mouse isn't moving for several seconds (JS option timeToIdle) */
.pswp__ui--idle .pswp__top-bar {
opacity: 0; }
.pswp__ui--idle .pswp__button--arrow--left,
.pswp__ui--idle .pswp__button--arrow--right {
opacity: 0; }
/*
pswp__ui--hidden class is added when controls are hidden
e.g. when user taps to toggle visibility of controls
*/
.pswp__ui--hidden .pswp__top-bar,
.pswp__ui--hidden .pswp__caption,
.pswp__ui--hidden .pswp__button--arrow--left,
.pswp__ui--hidden .pswp__button--arrow--right {
/* Force paint & create composition layer for controls. */
opacity: 0.001; }
/* pswp__ui--one-slide class is added when there is just one item in gallery */
.pswp__ui--one-slide .pswp__button--arrow--left,
.pswp__ui--one-slide .pswp__button--arrow--right,
.pswp__ui--one-slide .pswp__counter {
display: none; }
.pswp__element--disabled {
display: none !important; }
.pswp--minimal--dark .pswp__top-bar {
background: none; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

View File

@ -0,0 +1 @@
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,179 @@
/*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */
/*
Styles for basic PhotoSwipe functionality (sliding area, open/close transitions)
*/
/* pswp = photoswipe */
.pswp {
display: none;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
overflow: hidden;
-ms-touch-action: none;
touch-action: none;
z-index: 1500;
-webkit-text-size-adjust: 100%;
/* create separate layer, to avoid paint on window.onscroll in webkit/blink */
-webkit-backface-visibility: hidden;
outline: none; }
.pswp * {
-webkit-box-sizing: border-box;
box-sizing: border-box; }
.pswp img {
max-width: none; }
/* style is added when JS option showHideOpacity is set to true */
.pswp--animate_opacity {
/* 0.001, because opacity:0 doesn't trigger Paint action, which causes lag at start of transition */
opacity: 0.001;
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--open {
display: block; }
.pswp--zoom-allowed .pswp__img {
/* autoprefixer: off */
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in; }
.pswp--zoomed-in .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab; }
.pswp--dragging .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing; }
/*
Background is added as a separate element.
As animating opacity is much faster than animating rgba() background-color.
*/
.pswp__bg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #000;
opacity: 0;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
will-change: opacity; }
.pswp__scroll-wrap {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden; }
.pswp__container,
.pswp__zoom-wrap {
-ms-touch-action: none;
touch-action: none;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0; }
/* Prevent selection and tap highlights */
.pswp__container,
.pswp__img {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none; }
.pswp__zoom-wrap {
position: absolute;
width: 100%;
-webkit-transform-origin: left top;
-ms-transform-origin: left top;
transform-origin: left top;
/* for open/close transition */
-webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp__bg {
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--animated-in .pswp__bg,
.pswp--animated-in .pswp__zoom-wrap {
-webkit-transition: none;
transition: none; }
.pswp__container,
.pswp__zoom-wrap {
-webkit-backface-visibility: hidden; }
.pswp__item {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: hidden; }
.pswp__img {
position: absolute;
width: auto;
height: auto;
top: 0;
left: 0; }
/*
stretched thumbnail or div placeholder element (see below)
style is added to avoid flickering in webkit/blink when layers overlap
*/
.pswp__img--placeholder {
-webkit-backface-visibility: hidden; }
/*
div element that matches size of large image
large image loads on top of it
*/
.pswp__img--placeholder--blank {
background: #222; }
.pswp--ie .pswp__img {
width: 100% !important;
height: auto !important;
left: 0;
top: 0; }
/*
Error message appears when image is not loaded
(JS option errorMsg controls markup)
*/
.pswp__error-msg {
position: absolute;
left: 0;
top: 50%;
width: 100%;
text-align: center;
font-size: 14px;
line-height: 16px;
margin-top: -8px;
color: #CCC; }
.pswp__error-msg a {
color: #CCC;
text-decoration: underline; }

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
# Vendor scripts will be loaded automatically after `build` task

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More