diff --git a/themes/finna2/js/finna-cookie-consent-element.js b/themes/finna2/js/finna-cookie-consent-element.js
deleted file mode 100644
index ea8e0fe907a..00000000000
--- a/themes/finna2/js/finna-cookie-consent-element.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/* global VuFind, CookieConsent */
-class FinnaCookieConsentElement extends HTMLElement {
-
- /**
- * Get consent categories
- * @returns {string} Consent categories
- */
- get consentCategories() {
- return this.getAttribute('consent-categories') || '';
- }
-
- /**
- * Set consent categories
- * @param {string} newValue Value to set
- */
- set consentCategories(newValue) {
- this.setAttribute('consent-categories', newValue);
- }
-
- /**
- * Get service base URL
- * @returns {string} Service base URL
- */
- get serviceBaseUrl() {
- const url = this.getAttribute('service-url');
- if (url) {
- try {
- return new URL(url).host;
- } catch (_) {
- return url;
- }
- }
- return '';
- }
-
- /**
- * Get service URL
- * @returns {string} Service URL
- */
- get serviceUrl() {
- return this.getAttribute('service-url') || '';
- }
-
- /**
- * Set service URL
- * @param {string} newValue Value to set
- */
- set serviceUrl(newValue) {
- this.setAttribute('service-url', newValue);
- }
-
- /**
- * Constructor
- */
- constructor() {
- super();
- }
-
- /**
- * When the element is added to the dom
- */
- connectedCallback() {
- // Create the element
- const divInfo = document.createElement('div');
- divInfo.classList.add('embedded-content-cookie-info');
-
- const divHeading = document.createElement('div');
- divHeading.classList.add('embedded-content-heading');
- divHeading.append(VuFind.translate('embedded_content_heading'));
- divInfo.append(divHeading);
-
- const divDescription = document.createElement('div');
- divDescription.classList.add('embedded-content-description');
- const replacements = {
- '%%serviceBaseUrl%%': this.serviceBaseUrl,
- '%%consentCategories%%': this.consentCategories
- };
- divDescription.append(VuFind.translate('embedded_content_description', replacements));
- divInfo.append(divDescription);
-
- const divActions = document.createElement('div');
- divActions.classList.add('embedded-content-actions');
-
- const aOuterLink = document.createElement('a');
- aOuterLink.classList.add('btn', 'btn-primary');
- aOuterLink.href = this.serviceUrl || '';
- aOuterLink.target = '_blank';
- aOuterLink.append(VuFind.translate('embedded_content_external_link'));
-
- const linkIcon = document.createElement('i');
- linkIcon.classList.add('fa', 'fa-new-window');
- linkIcon.setAttribute('aria-hidden', true);
- aOuterLink.append(' ', linkIcon);
-
- const linkSpan = document.createElement('span');
- linkSpan.classList.add('visually-hidden');
- linkSpan.append(VuFind.translate('Open in a new window'));
- aOuterLink.append(linkSpan);
- divActions.append(aOuterLink);
-
- const aShowModal = document.createElement('a');
- aShowModal.classList.add('btn', 'btn-default');
- aShowModal.href = '#';
- aShowModal.setAttribute('aria-haspopup', 'dialog');
- aShowModal.append(VuFind.translate('Cookie Settings'));
- aShowModal.addEventListener('click', () => {
- $.fn.finnaPopup.closeOpen();
- CookieConsent.showPreferences();
- });
- divActions.append(aShowModal);
- divInfo.append(divActions);
- this.append(divInfo);
- }
-}
-
-customElements.define('finna-consent', FinnaCookieConsentElement);
diff --git a/themes/finna2/js/finna-image-paginator.js b/themes/finna2/js/finna-image-paginator.js
index 8c6aad35860..1e09e12f2f7 100644
--- a/themes/finna2/js/finna-image-paginator.js
+++ b/themes/finna2/js/finna-image-paginator.js
@@ -726,9 +726,6 @@ FinnaPaginator.prototype.loadImageInformation = function loadImageInformation()
if (typeof $('.open-link a').attr('href') !== 'undefined') {
_.setDimensions();
}
- _.popup.collapseArea.find('finna-video').on('click', () => {
- _.setCanvasElement('video');
- });
if ($('.imagepopup-holder .feedback-record')[0] || $('.imagepopup-holder .save-record')[0]) {
$('.imagepopup-holder .feedback-record, .imagepopup-holder .save-record').on('click', function onClickActionLink(/*e*/) {
$.fn.finnaPopup.closeOpen();
@@ -883,10 +880,8 @@ FinnaPaginator.prototype.createPopupObject = function createPopupObject(popup) {
_.canvasElements = {
leaflet: popup.find('.leaflet-map-image'),
noZoom: popup.find('.popup-nonzoom'),
- video: popup.find('.popup-video')
};
_.canvasElements.leaflet.attr('id', 'leaflet-map-image');
- _.canvasElements.video.attr('id', 'video-player');
if (_.images.length < 2) {
_.popup.covers.parent().hide();
_.popup.leftBrowseBtn.hide();
diff --git a/themes/finna2/js/finna-video-element.js b/themes/finna2/js/finna-video-element.js
deleted file mode 100644
index 8fcf9da0213..00000000000
--- a/themes/finna2/js/finna-video-element.js
+++ /dev/null
@@ -1,347 +0,0 @@
-/* global VuFind, finna */
-
-class VideoElement extends HTMLElement {
-
- /**
- * Get the type of the video, iFrame | video
- * @returns {string} Video type
- */
- get type() {
- return (this.getAttribute('type') || '').toLowerCase();
- }
-
- /**
- * Set the type of the video, iFrame | video
- * @param {string} value iFrame | video
- */
- set type(value) {
- this.setAttribute('type', value);
- }
-
- /**
- * Get the parent element to which the video player is being embedded into.
- * @returns {string|undefined} Parent element id or undefined
- */
- get embedParent() {
- return this.getAttribute('embed-parent') || undefined;
- }
-
- /**
- * Set the parent element to which the video player is being embedded into.
- * Omit to display in a new popup.
- * @param {string|undefined} value Parent element id or undefined.
- */
- set embedParent(value) {
- this.setAttribute('embed-parent', value);
- }
-
- /**
- * Get the source of the video.
- * @returns {string} Source of the video
- */
- get source() {
- return this.getAttribute('source') || '';
- }
-
- /**
- * Set the source of the video.
- * @param {string} value The video source
- */
- set source(value) {
- this.setAttribute('source', value);
- }
-
- /**
- * Get the video sources as an object.
- * @returns {object} Object containing video sources
- */
- get videoSources() {
- return this.getAttribute('video-sources') ? JSON.parse(this.getAttribute('video-sources')) : {};
- }
-
- /**
- * Set the video sources as an object.
- * @param {object} value Video sources object
- */
- set videoSources(value) {
- this.setAttribute('video-sources', JSON.stringify(value || {}));
- }
-
- /**
- * Get the poster url to display in viewer.
- * @returns {string} Poster url
- */
- get posterUrl() {
- return this.getAttribute('poster-url') || '';
- }
-
- /**
- * Set the poster url to display in viewer.
- * @param {string} value Poster url
- */
- set posterUrl(value) {
- this.setAttribute('poster-url', value);
- }
-
- /**
- * Get the identity for the popup group.
- * @returns {string} Id of the popup group
- */
- get popupId() {
- return this.getAttribute('popup-id') || '';
- }
-
- /**
- * Set the identity for the popup group.
- * @param {string} value Id of the popup group
- */
- set popupId(value) {
- this.setAttribute('popup-id', value);
- }
-
- /**
- * Get consent service required for the video.
- * @returns {string} Consent service accepted to display this video
- */
- get consentService() {
- return this.getAttribute('consent-service') || '';
- }
-
- /**
- * Get consent service required for the video.
- * @param {string} value Consent service accepted to display this video
- */
- set consentService(value) {
- this.setAttribute('consent-service', value);
- }
-
- /**
- * Get consent categories required for the video.
- * @returns {string} Consent categories
- */
- get consentCategories() {
- return this.getAttribute('consent-categories') || '';
- }
-
- /**
- * Set consent categories required for the video.
- * @param {string} value Consent categories
- */
- set consentCategories(value) {
- this.setAttribute('consent-categories', value);
- }
-
- /**
- * Get index.
- * @returns {string} Index of this video element
- */
- get index() {
- return this.getAttribute('index') || '';
- }
-
- /**
- * Set index.
- * @param {number} value Value
- */
- set index(value) {
- this.setAttribute('index', value);
- }
-
- /**
- * Get if the video should be activated on load.
- * return true if the value is 'true' as a string.
- * @returns {boolean} Is active?
- */
- get active() {
- return this.getAttribute('active') === 'true';
- }
-
- /**
- * Set if the video should be activated on load.
- * @param {boolean} value Is active?
- */
- set active(value) {
- this.setAttribute('active', value);
- }
-
- /**
- * Constructor
- */
- constructor() {
- super();
- this.modals = {
- video: ``,
- iframe: `
`,
- audio: ``
- };
-
- this.translations = {
- close: VuFind.translate('close'),
- next: VuFind.translate('Next Record'),
- previous: VuFind.translate('Previous Record'),
- };
- this.scripts = {
- 'videojs': 'vendor/video.min.js',
- 'video-popup': 'finna-video-popup.js'
- };
- this.subScripts = {
- 'videojs-hotkeys': 'vendor/videojs.hotkeys.min.js',
- 'videojs-quality': 'vendor/videojs-contrib-quality-levels.js',
- 'videojs-airplay': 'vendor/silvermine-videojs-airplay.min.js',
- };
- }
-
- /**
- * Called after consent settings have been initialized.
- */
- onConsentInitialized() {
- // Check if this video is inside a record
- const record = this.closest('div.record');
- const self = this;
- let classes = 'video-popup';
- let modal = this.modals.video;
- switch (this.type) {
- case 'iframe':
- classes = 'finna-iframe';
- modal = this.modals.iframe;
- break;
- case 'audio':
- classes = 'finna-audio';
- modal = this.modals.audio;
- break;
- }
- const popupSettings = {
- id: this.popupId,
- modal: modal,
- cycle: typeof this.embedParent !== 'undefined',
- classes: classes,
- parent: this.embedParent,
- translations: this.translations,
- onPopupInit: (t) => {
- if (this.embedParent) {
- t.removeClass('active-video');
- }
- },
- onPopupOpen: function onPopupOpen() {
- if (!self.hasConsent) {
- return;
- }
- if (record) {
- const warnings
- = record.querySelector(`.video-warning[data-index="${self.index}"]`);
- if (this.parent) {
- record.querySelectorAll('.active-video').forEach(v => {
- v.classList.remove('active-video');
- });
- record.querySelectorAll('.video-warning').forEach(v => {
- if (v.dataset.index !== self.index) {
- v.classList.add('hidden');
- } else {
- v.classList.remove('hidden');
- VuFind.observerManager.observe(
- 'LazyImages',
- v.querySelectorAll('img[data-src]')
- );
- }
- });
- this.currentTrigger().addClass('active-video');
- } else {
- this.content.css('height', '100%');
- if (warnings) {
- const clone = warnings.cloneNode(true);
- clone.classList.remove('hidden');
- this.modalHolder.append(clone);
- VuFind.observerManager.observe(
- 'LazyImages',
- clone.querySelectorAll('img[data-src]')
- );
- setTimeout(function startFade() {
- $(clone).fadeOut(2000);
- }, 3000);
- }
- }
- }
- switch (self.type) {
- case 'video':
- finna.scriptLoader.load(
- self.scripts,
- () => {
- finna.scriptLoader.load(
- self.subScripts,
- () => {
- finna.videoPopup.initVideoJs('.video-popup', self.videoSources, self.posterUrl);
- }
- );
- }
- );
- break;
- case 'iframe':
- // If using Chrome + VoiceOver, Chrome crashes if vimeo player video settings button has aria-haspopup=true
- document.querySelectorAll('.vp-prefs .js-prefs').forEach(e => {
- e.setAttribute('aria-haspopup', false);
- });
- this.content.find('iframe').attr('src', this.adjustEmbedLink(self.source));
- break;
- case 'audio':
- this.content.css('height', '100%');
- this.content.find('audio').attr('src', self.source);
- break;
- default:
- console.warn(`Unknown video type in video element: ${self.type}`);
- break;
- }
- }
- };
- this.hasConsent = this.type === 'iframe'
- ? VuFind.cookie.isServiceAllowed(this.consentService)
- : true;
- if (!this.hasConsent) {
- finna.scriptLoader.load(
- {'cookie-consent': 'finna-cookie-consent-element.js'},
- () => {
- const consentModal = document.createElement('finna-consent');
- consentModal.consentCategories = this.consentCategories;
- consentModal.serviceUrl = this.source;
-
- popupSettings.modal = consentModal;
- $(this).finnaPopup(popupSettings);
- if (this.active) {
- this.click();
- }
- }
- );
- } else {
- $(this).finnaPopup(popupSettings);
- if (this.active) {
- this.click();
- }
- }
- }
-
- /**
- * When the element is added to the dom
- */
- connectedCallback() {
- // Wait for the cookie consent to be initialized
- if (VuFind.cookie.getConsentConfig() === null) {
- VuFind.listen('cookie-consent-initialized', () => this.onConsentInitialized(), {once: true});
- } else {
- this.onConsentInitialized();
- }
- }
-
-
- /**
- * When the element is removed from the dom
- */
- disconnectedCallback() {
- $(this).trigger('removeclick.finna');
- }
-}
-
-customElements.define('finna-video', VideoElement);
diff --git a/themes/finna2/js/finna-video-player.js b/themes/finna2/js/finna-video-player.js
new file mode 100644
index 00000000000..c8cd004917f
--- /dev/null
+++ b/themes/finna2/js/finna-video-player.js
@@ -0,0 +1,238 @@
+/* global VuFind, finna, CookieConsent */
+finna.videoPlayer = (() => {
+
+ /**
+ * Scripts to load in order
+ * @member {object} requiredVideoScripts
+ */
+ const requiredVideoScripts = {
+ 'videojs': 'vendor/video.min.js',
+ };
+
+ /**
+ * Scripts that depend on the videojs script
+ * @member {object} dependentVideoScripts
+ */
+ const dependentVideoScripts = {
+ 'video-popup': 'finna-video-popup.js',
+ 'videojs-hotkeys': 'vendor/videojs.hotkeys.min.js',
+ 'videojs-quality': 'vendor/videojs-contrib-quality-levels.js',
+ 'videojs-airplay': 'vendor/silvermine-videojs-airplay.min.js',
+ };
+
+ /**
+ * Adds a specific class name to VuFind modal and a listener to listen when
+ * the modal is closed to remove the class name.
+ * @param {string} className Classname to set for opened modal.
+ */
+ function overrideModalClass(className)
+ {
+ const container = document.getElementById('modal');
+ if (container) {
+ const dialog = container.querySelector('.modal-dialog');
+ if (dialog) {
+ container.classList.add(className);
+ dialog.classList.add('modal-dialog-centered');
+ VuFind.listen('lightbox.closed', () => {
+ dialog.classList.remove('modal-dialog-centered');
+ container.classList.remove(className);
+ }, {once: true});
+ }
+ }
+ }
+
+ /**
+ * Display warning icons when inline video setting is enabled.
+ * @param {HTMLElement} element Element clicked
+ */
+ function showWarningIcons(element)
+ {
+ // Open video warnings in inline videos
+ document.querySelectorAll('.warnings-wrapper .video-warning').forEach(warning => {
+ finna.getPromise('lazyImages').then(() => {
+ VuFind.observerManager.observe(
+ 'LazyImages',
+ warning.querySelectorAll('img[data-src]')
+ );
+ });
+ warning.classList.toggle('hidden', element.dataset.index !== warning.dataset.index);
+ });
+ }
+
+ /**
+ * When a video button which uses videojs has been requested.
+ * @param {HTMLElement} element Element clicked
+ */
+ function onVideoOpen(element)
+ {
+ const videoPlayer = document.createElement('video');
+ videoPlayer.className = 'video-js vjs-big-play-centered video-popup';
+ videoPlayer.controls = '';
+
+ // Is the video inline video or popup video
+ let container;
+ if (element.dataset.inline) {
+ container = document.getElementById('inline-video');
+ container.replaceChildren(videoPlayer);
+ showWarningIcons(element);
+ } else {
+ // Try to close any open finna popups so the video can be shown properly
+ $.fn.finnaPopup.closeOpen();
+ VuFind.lightbox.render(videoPlayer.outerHTML);
+ overrideModalClass('finna-video-modal');
+ container = document.getElementById('modal');
+ }
+ const videoSources = JSON.parse(element.dataset.videoSources);
+ finna.videoPopup.initVideoJs(container, videoSources, element.dataset.posterUrl);
+ }
+
+ /**
+ * When a video which uses iframe has been requested.
+ * @param {HTMLElement} element Element clicked
+ */
+ function onIFrameOpen(element)
+ {
+ const iFrame = document.createElement('iframe');
+ iFrame.className = 'player';
+ iFrame.frameborder = 0;
+ iFrame.allowFullscreen = 'true';
+ iFrame.src = element.dataset.url;
+ if (element.dataset.inline) {
+ const container = document.getElementById('inline-video');
+ container.replaceChildren(iFrame);
+ showWarningIcons(element);
+ } else {
+ // Try to close any open finna popups so the video can be shown properly
+ $.fn.finnaPopup.closeOpen();
+ VuFind.lightbox.render(iFrame.outerHTML);
+ overrideModalClass('finna-iframe-modal');
+ }
+ }
+
+ /**
+ * Display a cookie consent window warning for the user.
+ * @param {HTMLElement} element The element which was clicked
+ */
+ function displayConsentWindow(element)
+ {
+ const consentModal = document.getElementById('finna-consent-modal-template');
+ if (consentModal) {
+ // Append the cloned element, as templates return a DocumentFragment instead of node, which does not work
+ // if outerHTML is called.
+ const cloned = consentModal.content.cloneNode(true);
+ const wrapper = document.createElement('div');
+ wrapper.className = 'embedded-content-placeholder';
+ wrapper.append(cloned);
+ // Replace %%consentCategories%% and %%serviceBaseUrl%% with proper values
+ const externalLink = wrapper.querySelector('.embedded-content-actions a[href="%%HREF%%"]');
+ if (externalLink) {
+ externalLink.setAttribute('href', element.dataset.url);
+ }
+ const description = wrapper.querySelector('.embedded-content-description');
+ if (description) {
+ const serviceBase = new URL(element.dataset.url);
+ description.innerText = description.innerText
+ .replace('%%consentCategories%%', element.dataset.consentTitle)
+ .replace('%%serviceBaseUrl%%', serviceBase.hostname);
+ }
+ let consentHolder;
+ if (element.dataset.inline) {
+ consentHolder = document.getElementById('inline-video');
+ consentHolder.replaceChildren(wrapper);
+ } else {
+ VuFind.lightbox.render(wrapper.outerHTML);
+ overrideModalClass('finna-consent-modal');
+ consentHolder = document.getElementById('modal');
+ }
+ const ccPreferences = consentHolder.querySelector('.embedded-content-actions button');
+ if (ccPreferences) {
+ // Set cookie consent preferences event after the modal has been initialized as the lightbox handles elements
+ // as a string, so it loses all the events applied before rendering
+ ccPreferences.addEventListener('click', () => {
+ VuFind.modal('hide');
+ CookieConsent.showPreferences();
+ });
+ }
+ }
+ }
+
+ /**
+ * Sets the video elements click event.
+ * @param {HTMLElement} element Element which displays the video popup
+ */
+ function setIFrameStateFromConsent(element)
+ {
+ if (VuFind.cookie.isServiceAllowed(element.dataset.consent)) {
+ element.addEventListener('click', () => {
+ document.querySelectorAll('.vc-finna-video-button').forEach(b => b.classList.remove('active-video'));
+ element.classList.add('active-video');
+ onIFrameOpen(element);
+ });
+ if (element.classList.contains('active-video')) {
+ onIFrameOpen(element);
+ }
+ return;
+ } else {
+ // We should display a consent information instead of the video
+ element.addEventListener('click', () => { displayConsentWindow(element); });
+ if (element.dataset.inline && element.classList.contains('active-video')) {
+ displayConsentWindow(element);
+ }
+ }
+ }
+
+ /**
+ * Provide a selector or HTMLButtonElement to initialize a button for embedded videos.
+ * @param {HTMLButtonElement|string} elementOrSelector Element or selector
+ */
+ function initIFrameButton(elementOrSelector)
+ {
+ const element = typeof elementOrSelector === 'string'
+ ? document.querySelector(elementOrSelector)
+ : elementOrSelector;
+ if (!element || element.classList.contains('initialized')) {
+ return;
+ }
+
+ const consentInitialized = VuFind.cookie.getConsentConfig();
+ // If consent configuration has not been initialized, wait for it
+ if (!consentInitialized) {
+ VuFind.listen('cookie-consent-initialized', () => {
+ setIFrameStateFromConsent(element);
+ });
+ } else {
+ setIFrameStateFromConsent(element);
+ }
+ }
+
+ /**
+ * Provide a selector or HTMLButtonElement to initialize a button for videos.
+ * Handles loading the proper scripts.
+ * @param {HTMLButtonElement|string} elementOrSelector Element or selector
+ */
+ function initVideoButton(elementOrSelector)
+ {
+ const element = typeof elementOrSelector === 'string'
+ ? document.querySelector(elementOrSelector)
+ : elementOrSelector;
+ if (!element || element.classList.contains('initialized')) {
+ return;
+ }
+ finna.scriptLoader.load(requiredVideoScripts, () => {
+ finna.scriptLoader.load(dependentVideoScripts, () => {
+ element.addEventListener('click', () => {
+ document.querySelectorAll('.vc-finna-video-button').forEach(b => b.classList.remove('active-video'));
+ onVideoOpen(element);
+ });
+ if (element.classList.contains('active-video')) {
+ element.click();
+ }
+ });
+ });
+ }
+
+ return {
+ initVideoButton,
+ initIFrameButton
+ };
+})();
diff --git a/themes/finna2/scss/finna/embedded-content.scss b/themes/finna2/scss/finna/embedded-content.scss
index 0419be2c96f..a238df79fde 100644
--- a/themes/finna2/scss/finna/embedded-content.scss
+++ b/themes/finna2/scss/finna/embedded-content.scss
@@ -1,4 +1,4 @@
-finna-consent, .embedded-content-placeholder {
+.embedded-content-placeholder {
background: black;
color: white;
display: flex;
@@ -19,6 +19,10 @@ finna-consent, .embedded-content-placeholder {
}
}
-finna-consent {
- height: 100%;
+#modal.finna-consent-modal {
+ .modal-content {
+ box-shadow: none;
+ background-color: transparent;
+ border: none;
+ }
}
diff --git a/themes/finna2/scss/finna/search.scss b/themes/finna2/scss/finna/search.scss
index 2a67596a793..de67927ad3b 100644
--- a/themes/finna2/scss/finna/search.scss
+++ b/themes/finna2/scss/finna/search.scss
@@ -145,6 +145,7 @@ mark, .highlight {
font-weight: 400;
clear: both;
word-break: break-all;
+ text-align: start;
.online-source {
color: $gray-light;
font-size: .9em;
diff --git a/themes/finna2/scss/finna/video-player.scss b/themes/finna2/scss/finna/video-player.scss
index 8ccd9a0b5eb..748dec50007 100644
--- a/themes/finna2/scss/finna/video-player.scss
+++ b/themes/finna2/scss/finna/video-player.scss
@@ -28,28 +28,30 @@
height: 100%;
width: 100%;
}
-.inline-video, .video-popup, .finna-iframe {
- &.finna-popup {
- display: flex;
- align-content: center;
- justify-content: center;
- .popup-iframe-wrapper {
- // These values are from examples in https://icareus.fi/videosoittimet/
- position: relative;
- width: 100%;
- // This padding-top value prevents the player from overflowing
- padding-top: 56.25%;
- margin: auto;
- iframe {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- }
- }
+
+// Template for using proper styles, when appending video viewer inside an element.
+%iframe-centering-template {
+ max-height: $inline-video-player-desktop-height;
+ @media screen {
+ @include media-breakpoint-down(md) {
+ max-height: $inline-video-player-tablet-height;
+ }
+ @include media-breakpoint-down(sm) {
+ max-height: $inline-video-player-mobile-height;
}
+ }
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ > iframe, > .video-js {
+ // This aspect ratio is result of division from 560/315, which is default aspect-ratio notified in icareus website
+ // It works miracles and using position absolute with padding is nightmare.
+ aspect-ratio: 1.7778/1;
+ height: 100%;
+ }
}
+
.inline-video-container {
width: 100vw;
position: relative;
@@ -58,13 +60,7 @@
background-color: black;
margin-bottom: 10px;
.inline-video {
- .embed-responsive-item {
- height: 100%;
- width: 100%;
- }
- .finna-iframe.modal-holder {
- max-width: 800px;
- }
+ @extend %iframe-centering-template;
}
.video-accordion {
padding-top: 15px;
@@ -126,14 +122,6 @@
}
}
-finna-video {
- display: inline-block;
- cursor: pointer;
- &.btn.btn-link {
- padding: 0;
- }
-}
-
.vjs-airplay-button {
.vjs-icon-placeholder {
background: $icon-airplay--default center center no-repeat;
@@ -150,3 +138,29 @@ finna-video {
}
}
}
+
+// Search results specific buttons without any padding to make things look smoother
+.btn.btn-link.video-link-container {
+ padding: 0;
+}
+
+#modal.finna-video-modal,
+#modal.finna-iframe-modal {
+ .modal-dialog {
+ max-width: 1200px;
+ .modal-content {
+ width: 100%;
+ height: 100%;
+ background-color: transparent;
+ box-shadow: none;
+ border: none;
+ > button.close {
+ right: -20px;
+ top: -20px;
+ }
+ .modal-body {
+ @extend %iframe-centering-template;
+ }
+ }
+ }
+}
diff --git a/themes/finna2/scss/global/variables.scss b/themes/finna2/scss/global/variables.scss
index 9b01c0a3e94..275d7367287 100644
--- a/themes/finna2/scss/global/variables.scss
+++ b/themes/finna2/scss/global/variables.scss
@@ -346,3 +346,7 @@ $item-status-color-available: $success !default;
$item-status-color-unavailable: $danger !default;
$item-status-color-unknown: $warning !default;
$item-status-color-uncertain: $warning !default;
+
+$inline-video-player-desktop-height: 600px;
+$inline-video-player-tablet-height: 500px;
+$inline-video-player-mobile-height: 200px;
diff --git a/themes/finna2/templates/RecordDriver/DefaultRecord/record-video-player.phtml b/themes/finna2/templates/RecordDriver/DefaultRecord/record-video-player.phtml
index a15ee9a2aa7..dc3168dd815 100644
--- a/themes/finna2/templates/RecordDriver/DefaultRecord/record-video-player.phtml
+++ b/themes/finna2/templates/RecordDriver/DefaultRecord/record-video-player.phtml
@@ -58,32 +58,17 @@
$desc = $embeddedVideo === 'data-embed-iframe' ? $this->translate('format_Video') : $text;
break;
}
-
- $popupId = ($embeddedVideo ?? '') === 'data-embed-iframe' ? 'finna-iframe' : 'finna-video';
- $type = ($embeddedVideo ?? '') === 'data-embed-iframe' ? 'iFrame' : 'video';
?>
-
- embed-parent="inline-video"
- active ="= $i === 0 ? 'true' : 'false'?>"
-
-
- video-sources="=htmlspecialchars(json_encode($url['videoSources']), ENT_QUOTES, 'UTF-8');?>"
-
- >
- =$this->icon('video-play', 'video-play-icon') ?>
- =$this->escapeHtml($this->truncate(ucfirst($desc), 30))?>
-
+ =
+ $this->component('finna-video-button', [
+ 'url' => $url['url'],
+ 'embed' => $embeddedVideo === 'data-embed-iframe',
+ 'index' => $i,
+ 'videoSources' => $url['videoSources'] ?? [],
+ 'description' => $desc,
+ 'inline' => $inlineVideo,
+ ]);
+ ?>
$url): ?>
$maxLinkCount)) {
continue;
}
-
- if ('popup' === $context && ++$linkCount > $maxLinkCount) {
- break;
- }
?>
=$i > 0 ? '
' : ''?>
$this->proxyUrl($url['url']),
'title' => $url['url'],
];
- switch ($url['embed'] ?? '') {
- case 'video':
- $currentUrl['data-embed-video'] = '';
- break;
- case 'iframe':
- $currentUrl['data-embed-iframe'] = '';
- break;
- default:
- if ($this->recordLinker()->getEmbeddedVideo($url['url']) == 'data-embed-iframe') {
- $url['embed'] = 'iframe';
- $currentUrl['data-embed-iframe'] = '';
- }
- break;
- }
- $desc = $url['desc'] ?? $url['description'] ?? $url['url'];
- if ($desc === $url['url']) {
- $desc = $this->truncateUrl($desc);
- }
+ $desc = !empty($url['desc']) ? $url['desc'] : $this->truncateUrl($url['url']);
?>
- component('finna-video-button', [
+ 'url' => $url['url'],
+ 'embed' => $this->recordLinker()->getEmbeddedVideo($url['url']) === 'data-embed-iframe',
+ 'index' => $i,
+ 'videoSources' => $url['videoSources'] ?? [],
+ 'description' => $desc,
+ 'class' => "btn btn-link video-link-container $context",
+ ]);
?>
-
htmlElement()->getAttributes($currentUrl, 'url-base') ?>>=$this->icon($icon) ?>
diff --git a/themes/finna2/templates/_ui/components/finna-video-button.phtml b/themes/finna2/templates/_ui/components/finna-video-button.phtml
new file mode 100644
index 00000000000..95fa0a360c1
--- /dev/null
+++ b/themes/finna2/templates/_ui/components/finna-video-button.phtml
@@ -0,0 +1,60 @@
+inline ?? false;
+ $index = $this->index ?? 0;
+ $description = $this->description ?? $this->translate('format_Video');
+ $videoSources = $this->videoSources ?? [];
+ $consent = $this->consent ?? 'recordvideo';
+ $embeddedVideo = $this->embed ?? false;
+ $buttonAttributes = [
+ 'class' => trim(($this->class ?? 'video-link-container') . " vc-invocation-$this->_invocation $this->_componentClass"),
+ 'aria-label' => $this->ariaLabel ?? $this->translate('Link to video') . ' ' . $description,
+ 'data-url' => $this->url ?? '',
+ 'data-index' => $index,
+ 'data-inline' => $inlineVideoPlayer,
+ 'data-embed' => $embeddedVideo,
+ 'data-consent' => $consent,
+ 'data-consent-title' => $this->translate($this->cookieConsent()->getCategoryTitleForService($consent)),
+ 'aria-haspopup' => true,
+ ];
+
+ // If inline video player is user and current video index is 0, this video button will be clicked to initialize the player.
+ // This will not cause the video to start playing.
+ if ($inlineVideoPlayer && $index === 0) {
+ $buttonAttributes['class'] .= ' active-video';
+ }
+ // Generate unique selector for the button
+ $jsSelector = '.' . str_replace(' ', '.', $buttonAttributes['class']);
+?>
+
+ {
+ const embedded = '{$embeddedVideo}';
+ if (embedded) {
+ finna.videoPlayer.initIFrameButton('{$jsSelector}');
+ } else {
+ finna.videoPlayer.initVideoButton('{$jsSelector}');
+ }
+ }
+ );
+ JS;
+?>
+=$this->assetManager()->outputInlineScriptString($script);?>
diff --git a/themes/finna2/templates/search/modals.phtml b/themes/finna2/templates/search/modals.phtml
index e2e169f2360..98da9a3c51e 100644
--- a/themes/finna2/templates/search/modals.phtml
+++ b/themes/finna2/templates/search/modals.phtml
@@ -30,7 +30,6 @@
-
+
+
+
+
+ =$this->transEsc('embedded_content_heading')?>
+
+
+ =$this->translate('embedded_content_description')?>
+
+
+
+
diff --git a/themes/finna2/theme.config.php b/themes/finna2/theme.config.php
index c009e8abc8e..36f46e6d51b 100644
--- a/themes/finna2/theme.config.php
+++ b/themes/finna2/theme.config.php
@@ -260,7 +260,6 @@
[ 'file' => 'vendor/cally.iife.js' ],
[ 'file' => 'finna-multiselect.js' ],
[ 'file' => 'finna-model-viewer.js' ],
- [ 'file' => 'finna-video-element.js' ],
[ 'file' => 'finna-feed-element.js' ],
[ 'file' => 'finna-carousel-manager.js' ],
[ 'file' => 'finna-select-a11y.js' ],