first
@ -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;
|
||||
}
|
||||
}
|
1071
wp-content/plugins/visual-portfolio/assets/admin/css/style.scss
Normal file
After Width: | Height: | Size: 303 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 235 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 170 KiB |
@ -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 |
@ -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 |
@ -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 |
After Width: | Height: | Size: 20 KiB |
@ -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 |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 107 KiB |
@ -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,
|
||||
},
|
||||
});
|
@ -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,
|
||||
});
|
||||
});
|
@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
@ -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
|
||||
);
|
||||
}
|
106
wp-content/plugins/visual-portfolio/assets/admin/js/script.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Visual Portfolio layout Justified.
|
||||
*/
|
||||
[data-vp-layout="justified"] {
|
||||
.vp-portfolio__item-wrap {
|
||||
top: 0;
|
||||
left: 0;
|
||||
float: left;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
59
wp-content/plugins/visual-portfolio/assets/css/lazyload.scss
Normal 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;
|
||||
}
|
||||
}
|
306
wp-content/plugins/visual-portfolio/assets/css/main.scss
Normal 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);
|
||||
}
|
||||
}
|
28
wp-content/plugins/visual-portfolio/assets/css/noscript.scss
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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 |
@ -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 |
After Width: | Height: | Size: 3.7 KiB |
@ -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);
|
||||
}
|
||||
});
|
@ -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();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
165
wp-content/plugins/visual-portfolio/assets/js/items-style-fly.js
Normal 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}`);
|
||||
});
|
183
wp-content/plugins/visual-portfolio/assets/js/layout-grid.js
Normal 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';
|
||||
});
|
@ -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;
|
||||
}
|
||||
});
|
@ -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;
|
||||
}
|
||||
});
|
152
wp-content/plugins/visual-portfolio/assets/js/layout-slider.js
Normal 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`,
|
||||
});
|
||||
}
|
||||
});
|
179
wp-content/plugins/visual-portfolio/assets/js/layout-tiles.js
Normal 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;
|
||||
}
|
||||
});
|
@ -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');
|
||||
}
|
||||
});
|
26
wp-content/plugins/visual-portfolio/assets/js/lazyload.js
Normal 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();
|
||||
}
|
||||
});
|
@ -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',
|
||||
};
|
@ -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);
|
||||
});
|
||||
});
|
@ -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);
|
||||
});
|
||||
});
|
995
wp-content/plugins/visual-portfolio/assets/js/main.js
Normal 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();
|
||||
});
|
||||
}
|
@ -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();
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
@ -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]);
|
||||
}
|
||||
});
|
209
wp-content/plugins/visual-portfolio/assets/js/plugin-fancybox.js
Normal 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);
|
||||
});
|
||||
}
|
@ -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();
|
||||
});
|
206
wp-content/plugins/visual-portfolio/assets/js/plugin-isotope.js
Normal 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');
|
||||
}
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
431
wp-content/plugins/visual-portfolio/assets/js/plugin-swiper.js
Normal 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();
|
||||
});
|
635
wp-content/plugins/visual-portfolio/assets/js/popup-gallery.js
Normal 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);
|
||||
}
|
||||
);
|
||||
}
|
93
wp-content/plugins/visual-portfolio/assets/js/preview.js
Normal 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
|
||||
}
|
||||
},
|
||||
};
|
7
wp-content/plugins/visual-portfolio/assets/vendor/conditionize/conditionize.min.js
vendored
Normal file
1
wp-content/plugins/visual-portfolio/assets/vendor/conditionize/conditionize.min.js.map
vendored
Normal file
1
wp-content/plugins/visual-portfolio/assets/vendor/fancybox/dist/jquery.fancybox.min.css
vendored
Normal file
13
wp-content/plugins/visual-portfolio/assets/vendor/fancybox/dist/jquery.fancybox.min.js
vendored
Normal file
19
wp-content/plugins/visual-portfolio/assets/vendor/flickr-justified-gallery/dist/fjGallery.css
vendored
Normal 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;
|
||||
}
|
13
wp-content/plugins/visual-portfolio/assets/vendor/flickr-justified-gallery/dist/fjGallery.min.js
vendored
Normal file
1
wp-content/plugins/visual-portfolio/assets/vendor/iframe-resizer/js/iframeResizer.map
vendored
Normal file
8
wp-content/plugins/visual-portfolio/assets/vendor/iframe-resizer/js/iframeResizer.min.js
vendored
Normal file
12
wp-content/plugins/visual-portfolio/assets/vendor/isotope-layout/dist/isotope.pkgd.min.js
vendored
Normal file
3
wp-content/plugins/visual-portfolio/assets/vendor/lazysizes/lazysizes.min.js
vendored
Normal file
482
wp-content/plugins/visual-portfolio/assets/vendor/photoswipe/dist/default-skin/default-skin.css
vendored
Normal 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; }
|
BIN
wp-content/plugins/visual-portfolio/assets/vendor/photoswipe/dist/default-skin/default-skin.png
vendored
Normal file
After Width: | Height: | Size: 547 B |
@ -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 |
BIN
wp-content/plugins/visual-portfolio/assets/vendor/photoswipe/dist/default-skin/preloader.gif
vendored
Normal file
After Width: | Height: | Size: 866 B |
179
wp-content/plugins/visual-portfolio/assets/vendor/photoswipe/dist/photoswipe.css
vendored
Normal 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; }
|
4
wp-content/plugins/visual-portfolio/assets/vendor/photoswipe/dist/photoswipe.min.js
vendored
Normal file
1
wp-content/plugins/visual-portfolio/assets/vendor/readme.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
# Vendor scripts will be loaded automatically after `build` task
|