diff --git a/compile-sass.sh b/compile-sass.sh index 11d00a92e..2f7ef2022 100644 --- a/compile-sass.sh +++ b/compile-sass.sh @@ -12,31 +12,33 @@ sass -E 'UTF-8' --cache-location .tmp/sass-cache-1 src/views/admin/scss/tainacan sass -E 'UTF-8' --cache-location .tmp/sass-cache-2 src/views/roles/tainacan-roles.scss:src/assets/css/tainacan-roles.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-3 src/views/gutenberg-blocks/tainacan-collections/collections-list/collections-list.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-3 src/views/media-component/media-component.scss:src/assets/css/media-component.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/views/gutenberg-blocks/tainacan-collections/carousel-collections-list/carousel-collections-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/views/gutenberg-blocks/tainacan-collections/collections-list/collections-list.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/views/gutenberg-blocks/tainacan-items/items-list/items-list.scss:src/assets/css/tainacan-gutenberg-block-items-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/views/gutenberg-blocks/tainacan-collections/carousel-collections-list/carousel-collections-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/views/gutenberg-blocks/tainacan-items/dynamic-items-list/dynamic-items-list.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/views/gutenberg-blocks/tainacan-items/items-list/items-list.scss:src/assets/css/tainacan-gutenberg-block-items-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/views/gutenberg-blocks/tainacan-items/search-bar/search-bar.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/views/gutenberg-blocks/tainacan-items/dynamic-items-list/dynamic-items-list.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/views/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/views/gutenberg-blocks/tainacan-items/search-bar/search-bar.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css sass -E 'UTF-8' --cache-location .tmp/sass-cache-9 src/views/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/views/gutenberg-blocks/tainacan-terms/terms-list/terms-list.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/views/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/views/gutenberg-blocks/tainacan-facets/facets-list/facets-list.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/views/gutenberg-blocks/tainacan-terms/terms-list/terms-list.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/views/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/views/gutenberg-blocks/tainacan-facets/facets-list/facets-list.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-13 src/views/gutenberg-blocks/tainacan-facets/faceted-search/faceted-search.scss:src/assets/css/tainacan-gutenberg-block-faceted-search.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-13 src/views/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-14 src/views/gutenberg-blocks/tainacan-items/item-submission-form/item-submission-form.scss:src/assets/css/tainacan-gutenberg-block-item-submission-form.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-14 src/views/gutenberg-blocks/tainacan-facets/faceted-search/faceted-search.scss:src/assets/css/tainacan-gutenberg-block-faceted-search.css -sass -E 'UTF-8' --cache-location .tmp/sass-cache-15 src/views/gutenberg-blocks/gutenberg-blocks-style.scss:src/assets/css/tainacan-gutenberg-block-common-styles.css +sass -E 'UTF-8' --cache-location .tmp/sass-cache-15 src/views/gutenberg-blocks/tainacan-items/item-submission-form/item-submission-form.scss:src/assets/css/tainacan-gutenberg-block-item-submission-form.css + +sass -E 'UTF-8' --cache-location .tmp/sass-cache-16 src/views/gutenberg-blocks/gutenberg-blocks-style.scss:src/assets/css/tainacan-gutenberg-block-common-styles.css echo "Compilação do Sass Concluído!" exit 0 diff --git a/package-lock.json b/package-lock.json index 75b370569..69deb8a65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3513,18 +3513,32 @@ "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } } }, "emoji-regex": { @@ -7539,6 +7553,11 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "photoswipe": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-4.1.3.tgz", + "integrity": "sha512-89Z43IRUyw7ycTolo+AaiDn3W1EEIfox54hERmm9bI12IB9cvRfHSHez3XhAyU8XW2EAFrC+2sKMhh7SJwn0bA==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -11925,9 +11944,9 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" }, "yallist": { "version": "2.1.2", diff --git a/package.json b/package.json index 0227f7253..4c8804d99 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,15 @@ "build-prod": "cross-env NODE_ENV=production webpack --config webpack.prod.js --display-error-details --progress --hide-modules" }, "dependencies": { - "blurhash": "^1.1.3", "axios": "^0.21.1", + "blurhash": "^1.1.3", "buefy": "^0.9.4", "bulma": "^0.9.1", "css-vars-ponyfill": "^2.3.1", "mdi": "^2.2.43", "moment": "^2.25.3", "node-sass": "^4.14.1", + "photoswipe": "^4.1.3", "qs": "^6.9.4", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/src/assets/css/media-component.css b/src/assets/css/media-component.css new file mode 100644 index 000000000..b77b24c43 --- /dev/null +++ b/src/assets/css/media-component.css @@ -0,0 +1,896 @@ +/*! 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: 9999999; + -webkit-text-size-adjust: 100%; + /* create separate layer, to avoid paint on window.onscroll in webkit/blink */ + -webkit-backface-visibility: hidden; + outline: none; } + .pswp * { + 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 */ + 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: rgba(0, 0, 0, 0.8); + opacity: 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; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-touch-callout: none; } + +.pswp__zoom-wrap { + position: absolute; + width: 100%; + -webkit-transform-origin: left top; + -moz-transform-origin: left top; + -ms-transform-origin: left top; + transform-origin: left top; + /* for open/close transition */ + transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); } + +.pswp__bg { + will-change: opacity; + /* for open/close transition */ + 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; } + +/*! 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 + + */ +/* + + + + + + +
+
+
+
+
+
+
+ + +
+
+
+ + + + + +
+
+
+ + + + +set_primitive_type('date'); - $this->set_component('tainacan-date'); - $this->set_name( __('Date', 'tainacan') ); - $this->set_description( __('Exact date type, with day, month and year.', 'tainacan') ); - $this->set_preview_template(' -
-
- -
-
- '); - } + function __construct() { + // call metadatum type constructor + parent::__construct(); + $this->set_primitive_type('date'); + $this->set_component('tainacan-date'); + $this->set_name( __('Date', 'tainacan') ); + $this->set_description( __('Exact date type, with day, month and year.', 'tainacan') ); + $this->set_preview_template(' +
+
+ +
+
+ '); + $this->output_date_format = get_option('date_format'); + } - public function validate( Item_Metadata_Entity $item_metadata) { - $value = $item_metadata->get_value(); - $format = 'Y-m-d'; + public function validate( Item_Metadata_Entity $item_metadata) { + $value = $item_metadata->get_value(); + $format = 'Y-m-d'; - if (is_array($value)) { - foreach ($value as $date_value) { - $d = \DateTime::createFromFormat($format, $date_value); - if (!$d || $d->format($format) !== $date_value) { - $this->add_error($this->format_error_msg($date_value)); - return false; - } - } - return true; - } - - $d = \DateTime::createFromFormat($format, $value); - - if (!$d || $d->format($format) !== $value) { - $this->add_error($this->format_error_msg($value)); + if (is_array($value)) { + foreach ($value as $date_value) { + $d = \DateTime::createFromFormat($format, $date_value); + if (!$d || $d->format($format) !== $date_value) { + $this->add_error($this->format_error_msg($date_value)); + return false; + } + } + return true; + } - return false; - } - return true; - } + $d = \DateTime::createFromFormat($format, $value); + if (!$d || $d->format($format) !== $value) { + $this->add_error($this->format_error_msg($value)); + return false; + } + return true; + } /** * Get the value as a HTML string with proper date format set in admin @@ -86,7 +85,7 @@ class Date extends Metadata_Type { private function format_date_value($value) { if (empty($value)) return ""; - return mysql2date(get_option('date_format'), ($value)); + return mysql2date($this->output_date_format, ($value)); } private function format_error_msg($value) { diff --git a/src/views/admin/js/utilities.js b/src/views/admin/js/utilities.js index 1e98d6444..91c4ab14b 100644 --- a/src/views/admin/js/utilities.js +++ b/src/views/admin/js/utilities.js @@ -39,6 +39,7 @@ export const ThumbnailHelperFunctions = () => { break; case 'audio/midi': case 'audio/mpeg': + case 'audio/mp3': case 'audio/webm': case 'audio/ogg': case 'audio/wav': @@ -55,6 +56,7 @@ export const ThumbnailHelperFunctions = () => { case 'video/webm': case 'video/ogg': case 'video/mpeg': + case 'video/mp4': imageSrc = 'placeholder_video'; break; case 'url': diff --git a/src/views/class-tainacan-admin.php b/src/views/class-tainacan-admin.php index e8467eb7c..bd95c6343 100644 --- a/src/views/class-tainacan-admin.php +++ b/src/views/class-tainacan-admin.php @@ -238,6 +238,7 @@ class Admin { 'wp_ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => is_user_logged_in() ? wp_create_nonce( 'wp_rest' ) : false, 'components' => $components, + 'classes' => array(), 'i18n' => $tainacan_admin_i18n, 'user_caps' => $user_caps, 'user_prefs' => $prefs, @@ -247,7 +248,7 @@ class Admin { 'theme_collection_list_url' => get_post_type_archive_link( 'tainacan-collection' ), 'custom_header_support' => get_theme_support('custom-header'), 'registered_view_modes' => \Tainacan\Theme_Helper::get_instance()->get_registered_view_modes(), - 'exposer_mapper_param' => \Tainacan\Mappers_Handler::MAPPER_PARAM, + 'exposer_mapper_param' => \Tainacan\Mappers_Handler::MAPPER_PARAM, 'exposer_type_param' => \Tainacan\Exposers_Handler::TYPE_PARAM, 'repository_name' => get_bloginfo('name'), 'api_max_items_per_page' => $TAINACAN_API_MAX_ITEMS_PER_PAGE, @@ -266,13 +267,13 @@ class Admin { $metadata_types = $Tainacan_Metadata->fetch_metadata_types(); foreach( $maps as $type => $map ){ - foreach ( $map as $metadatum => $details){ - $settings['i18n']['helpers_label'][$type][$metadatum] = [ 'title' => $details['title'], 'description' => $details['description'] ]; - } - } - foreach ( $metadata_types as $index => $metadata_type){ - $class = new $metadata_type; - $settings['i18n']['helpers_label'][$class->get_component()] = $class->get_form_labels(); + foreach ( $map as $metadatum => $details){ + $settings['i18n']['helpers_label'][$type][$metadatum] = [ 'title' => $details['title'], 'description' => $details['description'] ]; + } + } + foreach ( $metadata_types as $index => $metadata_type){ + $class = new $metadata_type; + $settings['i18n']['helpers_label'][$class->get_component()] = $class->get_form_labels(); } $filter_types = $Tainacan_Filters->fetch_filter_types(); diff --git a/src/views/media-component/media-component.js b/src/views/media-component/media-component.js new file mode 100644 index 000000000..d9c5ed007 --- /dev/null +++ b/src/views/media-component/media-component.js @@ -0,0 +1,421 @@ +// TAINACAN MEDIA COMPONENT -------------------------------------------------------- +// +// Counts on some HMTL markup to make a list of media links be displayed +// as a carousel with a lightbox. Check examples in the end of the file +import PhotoSwipe from 'photoswipe/dist/photoswipe.min.js'; +import PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default.min.js'; +const { __ } = wp.i18n; + +tainacan_plugin.classes.TainacanMediaGallery = class TainacanMediaGallery { + + /** + * Constructor initializes the instance. Options are Snake Case because they come from PHP side + * @param {String} thumbs_gallery_selector html element to be queried containing the thumbnails list + * @param {String} main_gallery_selector html element to be queried containing the main list + * @param {Object} options several options to be tweaked + * @param {Object} options.swiper_thumbs_options object with SwiperJS options for the thumbnails list (https://swiperjs.com/swiper-api) + * @param {Object} options.swiper_main_options object with SwiperJS options for the main list + * @param {Boolean} options.show_share_button show share button on lightbox + * + * @return {Object} TainacanMediaGallery instance + */ + constructor(thumbs_gallery_selector, main_gallery_selector, options) { + this.thumbs_gallery_selector = thumbs_gallery_selector; + this.main_gallery_selector = main_gallery_selector; + this.thumbsSwiper = null; + this.mainSwiper = null; + this.options = options; + + this.initializeSwiper(); + + if (this.main_gallery_selector) + this.initPhotoSwipeFromDOM(this.main_gallery_selector + " .swiper-wrapper"); + else if (this.thumbs_gallery_selector) + this.initPhotoSwipeFromDOM(this.thumbs_gallery_selector + " .swiper-wrapper"); + } + + /* Initializes Swiper JS instances of carousels */ + initializeSwiper() { + + if (this.thumbs_gallery_selector) { + let thumbsSwiperOptions = { + spaceBetween: 12, + slidesPerView: 'auto', + navigation: { + nextEl: '.swiper-navigation-next_' + this.thumbs_gallery_selector, + prevEl: '.swiper-navigation-prev_' + this.thumbs_gallery_selector, + }, + pagination: { + el: '.swiper-pagination_' + this.thumbs_gallery_selector + }, + centerInsufficientSlides: true, + watchOverflow: true, + a11y: { + prevSlideMessage: __( 'Previous slide', 'tainacan'), + nextSlideMessage: __( 'Next slide', 'tainacan'), + firstSlideMessage: __('This is the first slide', 'tainacan'), + lastSlideMessage: __('This is the last slide', 'tainacan') + }, + }; + thumbsSwiperOptions = {...thumbsSwiperOptions, ...this.options.swiper_thumbs_options }; + this.thumbsSwiper = new Swiper(this.thumbs_gallery_selector, thumbsSwiperOptions); + } + + if (this.main_gallery_selector) { + + let mainSwiperOptions = { + slidesPerView: 1, + slidesPerGroup: 1, + // navigation: { + // nextEl: '.swiper-navigation-next_' + this.main_gallery_selector, + // prevEl: '.swiper-navigation-prev_' + this.main_gallery_selector, + // }, + // pagination: { + // el: '.swiper-pagination_' + this.main_gallery_selector + // }, + watchOverflow: true, + a11y: { + prevSlideMessage: __( 'Previous slide', 'tainacan'), + nextSlideMessage: __( 'Next slide', 'tainacan'), + firstSlideMessage: __('This is the first slide', 'tainacan'), + lastSlideMessage: __('This is the last slide', 'tainacan') + }, + }; + if (this.thumbsSwiper) { + mainSwiperOptions.thumbs = { + swiper: this.thumbsSwiper, + autoScrollOffset: 3 + } + } + mainSwiperOptions = {...mainSwiperOptions, ...this.options.swiper_main_options }; + this.mainSwiper = new Swiper(this.main_gallery_selector, mainSwiperOptions); + } + + if (this.thumbsMain && this.mainSwiper) { + this.mainSwiper.controller = { + control: this.thumbsSwiper, + by: 'slide' + } + } + } + + initPhotoSwipeFromDOM (gallerySelector) { + // loop through all gallery elements and bind events + let galleryElement = document.querySelector(gallerySelector); + + galleryElement.setAttribute("data-pswp-uid", this.options.media_id); + galleryElement.onclick = (event) => this.onThumbnailsClick(event, this); + + // Parse URL and open gallery if it contains #&pid=3&gid=1 + let hashData = this.photoswipeParseHash(); + + if (hashData.pid && hashData.gid) + this.openPhotoSwipe(hashData.pid, galleryElement, true, true); + } + + // parse slide data (url, title, size ...) from DOM elements + // (children of gallerySelector) + parseThumbnailElements(el) { + let items = []; + const galleryElements = el.childNodes; + + // Crossbrowser safe way to traverse nodeList + Array.prototype.forEach.call(galleryElements, (liElement) => { + + // Include only element nodes + if (liElement.nodeType === 1) { + + let item = {}; + let fullContentElement = liElement.querySelectorAll('.media-full-content *'); + + if ( !fullContentElement.length ) { + item = { + html: fullContentElement.outerHTML ? fullContentElement.outerHTML : fullContentElement + } + } else { + if (fullContentElement[fullContentElement.length - 1].nodeName === 'IMG') { + fullContentElement = fullContentElement[fullContentElement.length - 1]; + item = { + src: fullContentElement.src, + w: parseInt(fullContentElement.width), + h: parseInt(fullContentElement.height) + }; + } else { + fullContentElement = fullContentElement[0]; + item = { + html: fullContentElement.outerHTML ? fullContentElement.outerHTML : fullContentElement + } + } + } + + let metadataElement = liElement.querySelector('.swiper-slide-metadata'); + if (metadataElement) { + const name = metadataElement.querySelector('.swiper-slide-metadata__name'); + const caption = metadataElement.querySelector('.swiper-slide-metadata__caption'); + const description = metadataElement.querySelector('.swiper-slide-metadata__description'); + + item.title = { + name, + caption, + description + } + } else { + item.title = false; + } + + item.el = liElement; // save link to element for getThumbBoundsFn + items.push(item); + } + }); + + return items; + }; + + openPhotoSwipe( + index, + galleryElement, + disableAnimation, + fromURL + ) { + let pswpElement = document.querySelectorAll(".pswp")[0], + gallery, + options, + items; + items = this.parseThumbnailElements(galleryElement); + + // Photoswipe options + // https://photoswipe.com/documentation/options.html // + options = { + showHideOpacity: true, + loop: false, + timeToIdle: 6000, + timeToIdleOutside: 3000, + closeEl: true, + captionEl: true, + fullscreenEl: true, + zoomEl: true, + counterEl: true, + arrowEl: true, + preloaderEl: true, + shareEl: this.options.show_share_button ? this.options.show_share_button : false, + bgOpacity: 1, + // define gallery index (for URL) + galleryUID: galleryElement.getAttribute("data-pswp-uid"), + getThumbBoundsFn: (index) => { + let thumbnail = items[index].el, + pageYScroll = window.pageYOffset || document.documentElement.scrollTop, + rect = thumbnail.getBoundingClientRect(); + + return { x: rect.left, y: rect.top + pageYScroll, w: rect.width }; + }, + // Function builds caption markup + addCaptionHTMLFn: (item, captionEl, isFake) => { + // item - slide object + // captionEl - caption DOM element + // isFake - true when content is added to fake caption container + // (used to get size of next or previous caption) + + captionEl.children[0].innerHTML = ''; + + if(!item.title) + return false; + + if (item.title.caption) + captionEl.children[0].innerHTML += '' + item.title.caption.innerHTML + ''; + + if (item.title.name && item.title.caption || (!item.title.name && item.title.caption && item.title.description) ) + captionEl.children[0].innerHTML += '
'; + + if (item.title.name) + captionEl.children[0].innerHTML += '' + item.title.name.innerHTML + ''; + + if (item.title.description && item.title.name) + captionEl.children[0].innerHTML += '
'; + + if (item.title.description) + captionEl.children[0].innerHTML += '' + item.title.description.innerHTML + ''; + + return true; + }, + + }; + + // PhotoSwipe opened from URL + if (fromURL) { + if (options.galleryPIDs) { + // parse real index when custom PIDs are used + // http://photoswipe.com/documentation/faq.html#custom-pid-in-url + for (let j = 0; j < items.length; j++) { + if (items[j].pid == index) { + options.index = j; + break; + } + } + } else { + // in URL indexes start from 1 + options.index = parseInt(index, 10) - 1; + } + } else { + options.index = parseInt(index, 10); + } + + // exit if index not found + if (isNaN(options.index)) + return; + + if (disableAnimation) + options.showAnimationDuration = 0; + + // Pass data to PhotoSwipe and initialize it + gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options); + gallery.init(); + + /* Updates PhotoSwiper instance from Swiper */ + let swiperInstance = this.mainSwiper ? this.mainSwiper : this.thumbsSwiper; + + gallery.listen("unbindEvents", () => { + // This is index of current photoswipe slide + let getCurrentIndex = gallery.getCurrentIndex(); + // Update position of the slider + swiperInstance.slideTo(getCurrentIndex, 0, false); + // Start swiper autoplay (on close - if swiper autoplay is true) + if (swiperInstance.params && swiperInstance.params.autoplay && swiperInstance.params.autoplay.enabled) + swiperInstance.autoplay.start(); + }); + + // Swiper autoplay stop when image zoom */ + gallery.listen('initialZoomIn', () => { + if (swiperInstance.params && swiperInstance.params.autoplay && swiperInstance.params.autoplay.enabled && swiperInstance.autoplay.running) + swiperInstance.autoplay.stop(); + }); + }; + + // triggers when user clicks on thumbnail + onThumbnailsClick(e, self) { + e = e || window.event; + e.preventDefault ? e.preventDefault() : (e.returnValue = false); + + let eTarget = e.target || e.srcElement; + + // find root element of slide + let closest = function closest(el, fn) { + return el && (fn(el) ? el : closest(el.parentNode, fn)); + }; + + let clickedListItem = closest(eTarget, function(el) { + return el.tagName && el.tagName.toUpperCase() === "LI"; + }); + + if (!clickedListItem) + return; + + // find index of clicked item by looping through all child nodes + // alternatively, you may define index via data- attribute + let clickedGallery = clickedListItem.parentNode, + childNodes = clickedListItem.parentNode.childNodes, + numChildNodes = childNodes.length, + nodeIndex = 0, + index; + + for (let i = 0; i < numChildNodes; i++) { + if (childNodes[i].nodeType !== 1) + continue; + + if (childNodes[i] === clickedListItem) { + index = nodeIndex; + break; + } + nodeIndex++; + } + + // open PhotoSwipe if valid index found + if (index >= 0) + self.openPhotoSwipe(index, clickedGallery); + + return false; + } + + // parse picture index and gallery index from URL (#&pid=1&gid=2) + photoswipeParseHash() { + const hash = window.location.hash.substring(1), + params = {}; + + if (hash.length < 5) + return params; + + const vars = hash.split("&"); + for (let i = 0; i < vars.length; i++) { + if (!vars[i]) + continue; + + const pair = vars[i].split("="); + if (pair.length < 2) + continue; + + params[pair[0]] = pair[1]; + } + + if (params.gid) + params.gid = parseInt(params.gid, 10); + + return params; + } +} + +/* Loads and instantiates media components passed to the global variable */ +document.addEventListener('DOMContentLoaded', function() { + if (tainacan_plugin?.classes?.TainacanMediaGallery && tainacan_plugin?.tainacan_media_components) { + (Object.values(tainacan_plugin.tainacan_media_components) || []).forEach((component) => { + new tainacan_plugin.classes.TainacanMediaGallery( + component.has_media_thumbs ? '#' + component.media_thumbs_id : null, + component.has_media_main ? '#' + component.media_main_id : null, + component + ); + }); + } +}); + + +/* + +---- Carousel of thumbnails only ---------------------------------------- + +
+