From ac637db31dbb109e24f1e34b7ef1027451d01ee5 Mon Sep 17 00:00:00 2001 From: Kuldip Chaudhary <64731232+KMchaudhary@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:40:17 +0530 Subject: [PATCH] Revert "Develop to Main Sync for v1.5.0 (#1523)" This reverts commit f29261e50cd09b748a83e04b0f1c4aea724ec802. --- .github/ISSUE_TEMPLATE/bug_report.md | 31 - .github/ISSUE_TEMPLATE/feature_request.md | 22 - CHANGELOG.md | 19 - admin/class-rtgodam-retranscodemedia.php | 2 +- admin/class-rtgodam-transcoder-admin.php | 151 +- admin/class-rtgodam-transcoder-handler.php | 41 +- .../class-rtgodam-transcoder-rest-routes.php | 5 - admin/godam-transcoder-actions.php | 9 +- admin/godam-transcoder-functions.php | 13 - .../block-extensions/core-image-extension.js | 73 - assets/src/block-extensions/index.js | 10 - assets/src/blocks/godam-audio/index.js | 4 +- assets/src/blocks/godam-gallery/index.js | 4 +- assets/src/blocks/godam-pdf/index.js | 4 +- assets/src/blocks/godam-player/VideoJS.js | 66 +- assets/src/blocks/godam-player/block.json | 2 +- assets/src/blocks/godam-player/edit.js | 72 +- assets/src/blocks/godam-player/editor.scss | 68 +- assets/src/blocks/godam-player/index.js | 4 +- assets/src/blocks/godam-player/style.scss | 131 +- assets/src/css/_common.scss | 3 - assets/src/css/_video-engagement.scss | 16 - assets/src/css/bubble-skin.scss | 130 +- assets/src/css/classic-skin.scss | 10 +- assets/src/css/godam-gallery.scss | 205 +-- assets/src/css/godam-player.scss | 300 +--- assets/src/css/godam-video-embed.scss | 162 -- assets/src/css/minimal-skin.scss | 9 - assets/src/css/pills-skin.scss | 23 +- assets/src/js/admin.js | 23 - assets/src/js/godam-gallery.js | 610 ++++--- assets/src/js/godam-player/engagement.js | 79 +- .../managers/configurationManager.js | 19 - .../godam-player/managers/controlsManager.js | 159 +- .../js/godam-player/managers/eventsManager.js | 8 +- .../managers/layers/formLayerManager.js | 37 +- .../managers/layers/hotspotLayerManager.js | 233 +-- .../js/godam-player/managers/layersManager.js | 7 - .../godam-player/managers/menuButtonHover.js | 26 +- .../js/godam-player/managers/playerManager.js | 4 +- assets/src/js/godam-player/utils/constants.js | 12 - assets/src/js/godam-player/videoPlayer.js | 17 +- assets/src/js/godam-video-embed.js | 390 ---- .../media-library/views/attachment-details.js | 31 +- .../src/js/media-library/views/attachment.js | 7 +- .../views/filters/media-retranscode.js | 2 +- .../views/godam-media-frame-shared.js | 9 +- .../ninja-forms-submissions-list.js | 55 - godam.php | 4 +- inc/classes/assets/class-ima-assets.php | 4 - .../assets/class-jetpack-form-assets.php | 4 - inc/classes/class-assets.php | 36 - inc/classes/class-elementor-widgets.php | 4 - inc/classes/class-media-library-ajax.php | 182 +- inc/classes/class-pages.php | 89 - inc/classes/class-plugin.php | 2 - inc/classes/class-rewrite.php | 4 - inc/classes/class-video-embed.php | 115 -- inc/classes/class-video-engagement.php | 4 - inc/classes/class-video-metadata.php | 8 +- inc/classes/class-video-preview.php | 4 - ...rest-forms-field-godam-record-frontend.php | 4 - inc/classes/fluentforms/class-form-submit.php | 4 - .../fields/class-recorder-field.php | 2 +- inc/classes/lifter-lms/class-lifter-lms.php | 4 - .../class-ninja-forms-integration.php | 25 +- .../rest-api/class-dynamic-gallery.php | 60 +- inc/classes/rest-api/class-media-library.php | 366 +--- inc/classes/rest-api/class-settings.php | 13 +- inc/classes/rest-api/class-transcoding.php | 101 +- .../shortcodes/class-godam-video-gallery.php | 59 +- inc/classes/sureforms/class-assets.php | 61 +- .../class-wpforms-field-godam-video.php | 4 +- .../wpforms-field-godam-record-entry-edit.php | 4 - .../wpforms-field-godam-record-entry-view.php | 6 +- .../wpforms-field-godam-record-frontend.php | 4 - inc/helpers/custom-functions.php | 137 +- inc/templates/godam-player.php | 516 +++--- inc/templates/video-embed.php | 44 - languages/godam.pot | 1574 +++++++++-------- package-lock.json | 59 +- package.json | 6 +- pages/analytics/Analytics.js | 23 +- pages/analytics/charts.js | 2 +- pages/analytics/helper.js | 12 +- pages/analytics/index.js | 10 +- pages/analytics/index.scss | 70 +- pages/dashboard/index.js | 10 +- pages/dashboard/index.scss | 3 - pages/godam/components/GoDAMHeader.jsx | 4 +- .../tabs/VideoPlayer/VideoPlayer.jsx | 2 +- pages/godam/index.js | 10 +- pages/godam/redux/slice/media-settings.js | 2 - pages/help/App.js | 62 +- pages/help/index.js | 13 +- pages/help/index.scss | 10 +- pages/media-library/index.js | 10 +- .../tools/components/tabs/RetranscodeTab.jsx | 106 +- pages/tools/index.js | 7 +- pages/utils/posthog.js | 40 - .../video-editor/components/SidebarLayers.js | 25 +- pages/video-editor/components/cta/ImageCTA.js | 4 +- .../components/layers/CTALayer.js | 29 +- .../components/layers/HotspotLayer.js | 262 +-- .../components/media-grid/MediaGrid.jsx | 8 +- pages/video-editor/index.js | 10 +- pages/video-editor/style.scss | 1 - pages/video-editor/utils/index.js | 2 +- pages/video-editor/video-control.css | 4 +- pages/whats-new/index.js | 8 +- readme.txt | 31 +- webpack.config.js | 24 - 112 files changed, 2014 insertions(+), 5620 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 assets/src/block-extensions/core-image-extension.js delete mode 100644 assets/src/block-extensions/index.js delete mode 100644 assets/src/css/godam-video-embed.scss delete mode 100644 assets/src/js/godam-video-embed.js delete mode 100644 assets/src/js/ninja-forms/ninja-forms-submissions-list.js delete mode 100644 inc/classes/class-video-embed.php delete mode 100644 inc/templates/video-embed.php delete mode 100644 pages/utils/posthog.js diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 5dac65d07..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: "Bug Report" -about: "If something isn't working as expected" -title: '' -labels: '' -assignees: '' ---- - -## Bug Report - -**Current Behavior** -A clear and concise description of the buggy behavior. - -**Expected behavior/code** -A clear and concise description of what you expected to happen (or code). - -**Steps to reproduce the bug** -Steps to reproduce if any. - -**Testing Environment (if applicable)** - -- Browser used -- OS: [e.g. OSX 10.15.x, Ubuntu 18.04] -- Browser used -- ... - -**Possible Solution** - - -**Additional context/Screenshots** -Add any other context about the problem here. If applicable, add screenshots to help explain. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 19700d206..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: "\U0001F680 Feature Request" -about: "Suggest an idea for this project." -title: '' -labels: '' -assignees: '' ---- - -## Feature Request - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I have an issue when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. Add any considered drawbacks. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Documentation, Adoption, Migration Strategy (if applicable)** -If you can, explain how users will be able to use this and possibly write out a version of the docs. -Maybe a screenshot or design? \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9130b5828..8fb26c588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,5 @@ # Changelog # -## v1.5.0 (January 20, 2026) ## - -- Feat: Implemented GoDAM virtual attachment support for Image, Audio, and PDF media. This allows users to import a wide range of media directly from GoDAM Central. -- Feat: Added support for the video quality menu on the Desktop Safari browser. -- Tweak: Made the Dashboard and Analytics pages mobile-responsive. -- Tweak: Improved the video player mobile view for different player skins, including Default, Classic, Minimal, Pills, and Bubble. -- Tweak: Improved layer styles for mobile device sizes. -- Tweak: Improved keyboard forward and backward shortcuts for the video player. -- Fix: Resolved interactive form and poll layer submission issues in the GoDAM Gallery block. -- Fix: Constrained hotspot layer points within the video canvas to ensure consistency across devices. -- Fix: Skipped poll label updates on poll submission to improve user experience. -- Fix: Displayed media item metadata (such as transcoded and embed URLs) in the GoDAM tab. -- Fix: Improved real-time aspect ratio updates for the GoDAM Block. -- Fix: Resolved an issue where input was not possible when a form was used in fullscreen mode on Safari with GoDAM Video. -- Fix: Made the video preview page work correctly for all layers. -- Fix: Fixed overlap issues between the GoDAM Video block UI and the engagement modal on mobile screens. -- Fix: Resolved GoDAM block rendering issues when used as a child block of Row, Columns, or Grid blocks. - - ## v1.4.9 (December 19, 2025) ## - Feat: Added Free Plan for GoDAM diff --git a/admin/class-rtgodam-retranscodemedia.php b/admin/class-rtgodam-retranscodemedia.php index 899f407a1..a7e66d122 100644 --- a/admin/class-rtgodam-retranscodemedia.php +++ b/admin/class-rtgodam-retranscodemedia.php @@ -111,7 +111,7 @@ public function load_rest_endpoints() { * Add the Retranscode Media meta box to the EasyDam Tools page. */ public function render_tools_page() { - $this->add_easydam_meta_boxes(); + $this->add_easydam_meta_boxes() ?>

diff --git a/admin/class-rtgodam-transcoder-admin.php b/admin/class-rtgodam-transcoder-admin.php index 09b87097f..d3c280aaa 100644 --- a/admin/class-rtgodam-transcoder-admin.php +++ b/admin/class-rtgodam-transcoder-admin.php @@ -40,9 +40,6 @@ public function __construct() { add_action( 'admin_notices', array( $this, 'dashboard_offer_banner' ) ); add_action( 'admin_notices', array( $this, 'free_plan_admin_notice' ) ); add_action( 'admin_notices', array( $this, 'usage_limit_notices' ) ); - add_action( 'admin_notices', array( $this, 'posthog_tracking_notice' ) ); - add_action( 'admin_init', array( $this, 'handle_posthog_tracking_action' ) ); - add_action( 'admin_init', array( $this, 'handle_clear_godam_cache' ) ); add_action( 'wp_ajax_rtgodam_dismiss_free_plan_notice', array( $this, 'dismiss_free_plan_notice' ) ); } } @@ -391,41 +388,6 @@ public function is_dashboard_screen() { return isset( $current_screen->id ) && 'dashboard' === $current_screen->id; } - /** - * Handle the cache clearing request for GoDAM usage data. - * This runs on admin_init to ensure headers haven't been sent yet. - * - * @since 1.5.0 - */ - public function handle_clear_godam_cache() { - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is verified below. - if ( ! isset( $_GET['clear_godam_cache'] ) ) { - return; - } - - // Verify user capabilities. - if ( ! current_user_can( 'manage_options' ) ) { - return; - } - - // Verify nonce. - if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'clear_godam_cache' ) ) { - return; - } - - // Clear the cache. - delete_option( 'rtgodam_user_data' ); - - // Build redirect URL safely. - $current_url = remove_query_arg( array( 'clear_godam_cache', '_wpnonce' ) ); - - // Perform the redirect with error handling. - if ( ! headers_sent() ) { - wp_safe_redirect( $current_url ); - exit; - } - } - /** * Display usage limit notices when bandwidth/storage usage is high. */ @@ -446,6 +408,15 @@ public function usage_limit_notices() { return; } + // Handle cache clearing request. + if ( isset( $_GET['clear_godam_cache'] ) && current_user_can( 'manage_options' ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'clear_godam_cache' ) ) { + delete_option( 'rtgodam_user_data' ); + // Redirect back to the same page without the query parameter. + $current_url = remove_query_arg( array( 'clear_godam_cache', '_wpnonce' ) ); + wp_safe_redirect( $current_url ); + exit; + } + // Get user data with usage information. $user_data = rtgodam_get_user_data( true ); @@ -652,108 +623,4 @@ private function display_bandwidth_exceeded_notice( $bandwidth_percentage ) {
GoDAM even more awesome? Allow GoDAM to collect anonymous diagnostic data and usage information.', 'godam' ) - ); - - $policy_url = 'https://posthog.com/privacy'; - - echo '

'; - echo wp_kses_post( $notice ); - echo ' (' . esc_html__( 'what we collect', 'godam' ) . ')'; - echo '

'; - echo '

'; - echo ' ' . esc_html__( 'Allow', 'godam' ) . ''; - echo ' ' . esc_html__( 'No thanks', 'godam' ) . ''; - echo '

'; - } - - /** - * Handle PostHog tracking action. - * - * @since 1.4.9 - */ - public function handle_posthog_tracking_action() { - if ( ! isset( $_GET['godam_tracker_optin'] ) && ! isset( $_GET['godam_tracker_optout'] ) ) { - return; - } - - $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : ''; - - if ( ! wp_verify_nonce( $nonce, 'godam_tracker_action' ) ) { - return; - } - - if ( ! current_user_can( 'manage_options' ) ) { - return; - } - - $settings = get_option( 'rtgodam-settings', array() ); - - if ( ! isset( $settings['general'] ) ) { - $settings['general'] = array(); - } - - if ( isset( $_GET['godam_tracker_optin'] ) && 'true' === $_GET['godam_tracker_optin'] ) { - $settings['general']['enable_posthog_tracking'] = true; - $settings['general']['posthog_initialized'] = true; - } elseif ( isset( $_GET['godam_tracker_optout'] ) && 'true' === $_GET['godam_tracker_optout'] ) { - $settings['general']['enable_posthog_tracking'] = false; - $settings['general']['posthog_initialized'] = true; - } - - update_option( 'rtgodam-settings', $settings ); - - wp_safe_redirect( esc_url_raw( remove_query_arg( array( 'godam_tracker_optin', 'godam_tracker_optout', '_wpnonce' ) ) ) ); - exit; - } } diff --git a/admin/class-rtgodam-transcoder-handler.php b/admin/class-rtgodam-transcoder-handler.php index 31243a193..cd52d4002 100644 --- a/admin/class-rtgodam-transcoder-handler.php +++ b/admin/class-rtgodam-transcoder-handler.php @@ -202,34 +202,6 @@ public function wp_media_transcoding( $wp_metadata, $attachment_id, $autoformat return; } - /** - * Filter to allow external developers to disable automatic transcoding on upload. - * This allows users to have manual control over when videos get transcoded. - * - * Note: This filter only applies to automatic uploads. Manual retranscoding requests - * (via bulk actions, tools page, etc.) will always proceed regardless of this setting. - * Form integrations will also use this filter to disable transcoding for form uploads. - * - * Example usage: - * add_filter( 'godam_auto_transcode_on_upload', '__return_false' ); // Disable globally - * - * @since 1.5.0 - * - * @param bool $auto_transcode_on_upload Whether to automatically transcode on upload. Default true. - */ - if ( ! $retranscode ) { - $auto_transcode_on_upload = apply_filters( 'godam_auto_transcode_on_upload', true ); - - if ( ! $auto_transcode_on_upload ) { - return $wp_metadata; - } - } - - // Skip transcoding and re-transcoding for images. - if ( preg_match( '/image/i', $wp_metadata['mime_type'], $type_array ) ) { - return $wp_metadata; - } - if ( empty( $wp_metadata['mime_type'] ) ) { return $wp_metadata; } @@ -283,6 +255,7 @@ public function wp_media_transcoding( $wp_metadata, $attachment_id, $autoformat $type = strtolower( $type_arry[ count( $type_arry ) - 1 ] ); $extension = pathinfo( $path, PATHINFO_EXTENSION ); $not_allowed_type = array(); + preg_match( '/video|audio/i', $metadata['mime_type'], $type_array ); if ( ( preg_match( '/video|audio/i', $metadata['mime_type'], $type_array ) || @@ -348,16 +321,14 @@ public function wp_media_transcoding( $wp_metadata, $attachment_id, $autoformat // Get author name with fallback to username. $author_first_name = ''; $author_last_name = ''; - $author_email = ''; if ( $attachment_author ) { - $author_first_name = $attachment_author->first_name ?? ''; - $author_last_name = $attachment_author->last_name ?? ''; - $author_email = $attachment_author->user_email ?? ''; + $author_first_name = $attachment_author->first_name; + $author_last_name = $attachment_author->last_name; // If first and last names are empty, use username as fallback. if ( empty( $author_first_name ) && empty( $author_last_name ) ) { - $author_first_name = $attachment_author->user_login ?? ''; + $author_first_name = $attachment_author->user_login; } } @@ -381,7 +352,7 @@ public function wp_media_transcoding( $wp_metadata, $attachment_id, $autoformat 'watermark' => boolval( $rtgodam_watermark ), 'resolutions' => array( 'auto' ), 'video_quality' => $rtgodam_video_compress_quality, - 'wp_author_email' => apply_filters( 'godam_author_email_to_send', $author_email, $attachment_id ), + 'wp_author_email' => apply_filters( 'godam_author_email_to_send', $attachment_author ? $attachment_author->user_email : '', $attachment_id ), 'wp_site' => $site_url, 'wp_author_first_name' => apply_filters( 'godam_author_first_name_to_send', $author_first_name, $attachment_id ), 'wp_author_last_name' => apply_filters( 'godam_author_last_name_to_send', $author_last_name, $attachment_id ), @@ -794,7 +765,7 @@ public function get_post_id_by_meta_key_and_value( $key, $value ) { $meta = wp_cache_get( $cache_key, 'godam' ); if ( empty( $meta ) ) { $meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s", $key, $value ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery - wp_cache_set( $cache_key, $meta, 'godam', HOUR_IN_SECONDS ); + wp_cache_set( $cache_key, $meta, 'godam', 3600 ); } if ( is_array( $meta ) && ! empty( $meta ) && isset( $meta[0] ) ) { diff --git a/admin/class-rtgodam-transcoder-rest-routes.php b/admin/class-rtgodam-transcoder-rest-routes.php index bf0c29b50..2d9a1ca72 100644 --- a/admin/class-rtgodam-transcoder-rest-routes.php +++ b/admin/class-rtgodam-transcoder-rest-routes.php @@ -287,11 +287,6 @@ public function handle_callback( WP_REST_Request $request ) { // Setting the transcoded PDF URL. update_post_meta( $attachment_id, 'rtgodam_transcoded_url', esc_url_raw( $post_array['download_url'] ) ); } - - if ( 'image' === $job_type && isset( $post_array['download_url'] ) && ! empty( $post_array['download_url'] ) ) { - // Setting the transcoded Image URL. - update_post_meta( $attachment_id, 'rtgodam_transcoded_url', esc_url_raw( $post_array['download_url'] ) ); - } } else { $flag = __( 'Something went wrong. The required attachment id does not exists. It must have been deleted.', 'godam' ); } diff --git a/admin/godam-transcoder-actions.php b/admin/godam-transcoder-actions.php index 571101a07..87bb17d76 100644 --- a/admin/godam-transcoder-actions.php +++ b/admin/godam-transcoder-actions.php @@ -32,7 +32,6 @@ function rtgodam_add_transcoded_url_field( $form_fields, $post ) { $is_allowed = ( str_starts_with( $mime_type, 'video/' ) || str_starts_with( $mime_type, 'audio/' ) || - str_starts_with( $mime_type, 'image/' ) || 'application/pdf' === $mime_type ); @@ -45,13 +44,15 @@ function rtgodam_add_transcoded_url_field( $form_fields, $post ) { $easydam_settings = get_option( 'rtgodam-settings', array() ); + $adaptive_bitrate_enabled = ! empty( $easydam_settings['video']['adaptive_bitrate'] ); + // Determine if the site has a valid API key (i.e., Premium user). $api_key = get_option( 'rtgodam-api-key', '' ); if ( ! empty( $api_key ) ) { - // If $job_id is present then show the oEmbed URL field for video files. - if ( ! empty( $job_id ) && 0 === strpos( $mime_type, 'video/' ) ) { + // If $job_id is present then show the oEmbed URL. + if ( ! empty( $job_id ) ) { $oembed_url = RTGODAM_API_BASE . '/web/video/' . $job_id; $form_fields['oembed_url'] = array( @@ -69,7 +70,7 @@ function rtgodam_add_transcoded_url_field( $form_fields, $post ) { } $transcoded_url_label = __( 'Transcoded CDN URL', 'godam' ); - if ( 0 === strpos( $mime_type, 'video/' ) ) { + if ( strpos( $mime_type, 'video/' ) === 0 ) { $transcoded_url_label = __( 'Transcoded CDN URL (MPD)', 'godam' ); } diff --git a/admin/godam-transcoder-functions.php b/admin/godam-transcoder-functions.php index de2a6d83b..695f42a6e 100644 --- a/admin/godam-transcoder-functions.php +++ b/admin/godam-transcoder-functions.php @@ -439,19 +439,6 @@ function rtgodam_verify_api_key( $api_key, $save = false ) { if ( 200 === $status_code && isset( $body['message']['account_token'] ) ) { $account_token = $body['message']['account_token']; - - // Enable PostHog tracking once API key is activated or plugin is updated with active API key. - $settings = get_option( 'rtgodam-settings', array() ); - if ( $save || empty( $settings['general']['posthog_initialized'] ) ) { - $settings['general']['enable_posthog_tracking'] = true; - $settings['general']['posthog_initialized'] = true; - update_option( 'rtgodam-settings', $settings ); - } elseif ( ! empty( $settings['general']['posthog_initialized'] ) && ! $settings['general']['enable_posthog_tracking'] && $save ) { - // If user previously opted out but is now activating an API key, re-enable tracking. - $settings['general']['enable_posthog_tracking'] = true; - update_option( 'rtgodam-settings', $settings ); - } - if ( $save ) { // Save the API key in the site options only if it is verified. update_option( 'rtgodam-api-key', $api_key ); diff --git a/assets/src/block-extensions/core-image-extension.js b/assets/src/block-extensions/core-image-extension.js deleted file mode 100644 index d9ece3e2a..000000000 --- a/assets/src/block-extensions/core-image-extension.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * WordPress dependencies - */ -import { addFilter } from '@wordpress/hooks'; -import { createHigherOrderComponent } from '@wordpress/compose'; -import { useEffect } from '@wordpress/element'; - -/** - * Higher-order component that wraps the core/image block edit component - * to add the godam-virtual-attachment-created event listener. - * - * @param {Function} BlockEdit - Original block edit component. - * @return {Function} Enhanced block edit component. - */ -const withVirtualAttachmentHandler = createHigherOrderComponent( - ( BlockEdit ) => { - return ( props ) => { - // Only apply to core/image block - if ( props.name !== 'core/image' ) { - return ; - } - - const { attributes, setAttributes } = props; - const { id } = attributes; - - useEffect( () => { - /** - * Handle virtual attachment created event - * Updates the block's id attribute when a virtual attachment is created - * - * @param {CustomEvent} event - The custom event containing attachment details - */ - const handleVirtualAttachmentCreated = ( event ) => { - const { attachment, virtualMediaId } = event.detail || {}; - - // Update id attribute only if it's undefined, null or matches the virtualMediaId - if ( - attachment && - ( id === undefined || id === null || id === virtualMediaId ) - ) { - setAttributes( { id: attachment.id } ); - } - }; - - // Attach event listener - document.addEventListener( - 'godam-virtual-attachment-created', - handleVirtualAttachmentCreated, - ); - - // Cleanup function to remove event listener - return () => { - document.removeEventListener( - 'godam-virtual-attachment-created', - handleVirtualAttachmentCreated, - ); - }; - }, [ id, setAttributes ] ); - - return ; - }; - }, - 'withVirtualAttachmentHandler', -); - -/** - * Extend the core/image block edit component to handle virtual attachment events. - */ -addFilter( - 'editor.BlockEdit', - 'godam/core-image-virtual-attachment', - withVirtualAttachmentHandler, -); diff --git a/assets/src/block-extensions/index.js b/assets/src/block-extensions/index.js deleted file mode 100644 index f6224ede0..000000000 --- a/assets/src/block-extensions/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Block Extensions - * - * Import block extensions here to ensure they are loaded. - */ - -/** - * Internal dependencies - */ -import './core-image-extension'; diff --git a/assets/src/blocks/godam-audio/index.js b/assets/src/blocks/godam-audio/index.js index 1fffe002f..36a967418 100644 --- a/assets/src/blocks/godam-audio/index.js +++ b/assets/src/blocks/godam-audio/index.js @@ -6,7 +6,7 @@ import edit from './edit'; import metadata from './block.json'; import save from './save'; import './style.scss'; -import icon from '../../images/godam-audio-filled.svg'; +import { ReactComponent as icon } from '../../images/godam-audio-filled.svg'; const { name } = metadata; @@ -16,7 +16,7 @@ export { metadata, name }; * Block registration settings. */ export const settings = { - icon: GoDAM Audio Block icon, + icon, example: { attributes: { src: 'https://upload.wikimedia.org/wikipedia/commons/d/dd/Armstrong_Small_Step.ogg', diff --git a/assets/src/blocks/godam-gallery/index.js b/assets/src/blocks/godam-gallery/index.js index 482b8d2c3..48d196207 100644 --- a/assets/src/blocks/godam-gallery/index.js +++ b/assets/src/blocks/godam-gallery/index.js @@ -8,14 +8,14 @@ import { registerBlockType } from '@wordpress/blocks'; */ import edit from './edit'; import metadata from './block.json'; -import icon from '../../images/godam-gallery-filled.svg'; +import { ReactComponent as icon } from '../../images/godam-gallery-filled.svg'; /** * Register the block */ registerBlockType( metadata.name, { ...metadata, - icon: GoDAM Gallery Block icon, + icon, edit, save: () => null, // Dynamic block, so save returns null } ); diff --git a/assets/src/blocks/godam-pdf/index.js b/assets/src/blocks/godam-pdf/index.js index 63771bf1a..b4a1a7335 100644 --- a/assets/src/blocks/godam-pdf/index.js +++ b/assets/src/blocks/godam-pdf/index.js @@ -6,7 +6,7 @@ import edit from './edit'; import metadata from './block.json'; import save from './save'; import './style.scss'; -import icon from '../../images/godam-pdf.svg'; +import { ReactComponent as icon } from '../../images/godam-pdf.svg'; const { name } = metadata; @@ -16,7 +16,7 @@ export { metadata, name }; * Block registration settings. */ export const settings = { - icon: GoDAM PDF Block icon, + icon, edit, save, }; diff --git a/assets/src/blocks/godam-player/VideoJS.js b/assets/src/blocks/godam-player/VideoJS.js index 81212d6f8..91e27b0a1 100644 --- a/assets/src/blocks/godam-player/VideoJS.js +++ b/assets/src/blocks/godam-player/VideoJS.js @@ -15,21 +15,13 @@ import 'videojs-flvjs-es6'; /** * WordPress dependencies */ -import { useRef, useEffect, useMemo } from '@wordpress/element'; +import { useRef, useEffect } from '@wordpress/element'; export const VideoJS = ( props ) => { const videoRef = useRef( null ); const playerRef = useRef( null ); const { options, onReady, onPlayerReady } = props; - const paddingTopValue = useMemo( () => { - if ( options.aspectRatio ) { - const [ x, y ] = options.aspectRatio.split( ':' ); - return `${ ( y / x ) * 100 }%`; - } - return '56.25%'; - }, [ options.aspectRatio ] ); - useEffect( () => { // Make sure Video.js player is only initialized once if ( ! playerRef.current ) { @@ -40,39 +32,10 @@ export const VideoJS = ( props ) => { videoElement.classList.add( 'vjs-styles-dimensions' ); videoRef.current.appendChild( videoElement ); - const videojsOptions = { ...options }; - - if ( ! options.aspectRatio ) { - videojsOptions.aspectRatio = '16:9'; - } - - if ( options.aspectRatio && ! /^\d+:\d+$/.test( options.aspectRatio ) ) { - // Unset the aspectRatio from videojsOptions as we will set it later - delete videojsOptions.aspectRatio; - } - - playerRef.current = videojs( videoElement, videojsOptions, () => { + playerRef.current = videojs( videoElement, options, () => { if ( onReady ) { onReady( playerRef.current ); } - - // Video.js player initialize instantly and hides the video loading spinner, so add a slight delay to hide it smoothly - setTimeout( () => { - if ( videoRef.current ) { - // Hide the video player loading animation - const parentElement = videoRef.current.parentElement; - - if ( parentElement ) { - // Remove the child element with class name 'godam-video-loading' - const childElement = parentElement.querySelector( '.godam-video-loading' ); - if ( childElement ) { - childElement.classList.add( 'hide' ); - } - } - - videoRef.current.style.display = 'block'; - } - }, 500 ); } ); // Add quality menu @@ -91,17 +54,6 @@ export const VideoJS = ( props ) => { player.preload( options.preload || '' ); player.playsinline( options.playsinline ); player.src( options.sources ); - // Verify if aspectRatio is in valid format x:y - if ( /^\d+:\d+$/.test( options.aspectRatio ) ) { - player.aspectRatio( options.aspectRatio ); - - // Get x and y from aspectRatio - const [ x, y ] = options.aspectRatio.split( ':' ); - if ( playerRef.current && x && y ) { - const playerEl = playerRef.current.el_; - playerEl.style.paddingTop = `${ ( y / x ) * 100 }%`; - } - } } }, [ options, videoRef ] ); @@ -111,6 +63,15 @@ export const VideoJS = ( props ) => { onPlayerReady( player ); + if ( playerRef.current ) { + const playerEl = playerRef.current.el_; + const video = playerEl.querySelector( 'video' ); + + video.addEventListener( 'loadedmetadata', () => { + playerEl.style.paddingTop = `${ ( video.videoHeight / video.videoWidth ) * 100 }%`; + } ); + } + return () => { if ( player && ! player.isDisposed() ) { player.dispose(); @@ -121,10 +82,7 @@ export const VideoJS = ( props ) => { return (
-
-
-
-
+
); }; diff --git a/assets/src/blocks/godam-player/block.json b/assets/src/blocks/godam-player/block.json index 3ee0669aa..59371de26 100644 --- a/assets/src/blocks/godam-player/block.json +++ b/assets/src/blocks/godam-player/block.json @@ -154,7 +154,7 @@ }, "usesContext": [ "queryId" ], "editorStyle": [ "file:./index.css", "godam-player-frontend-style" ], - "style": [ "file:./style-index.css", "godam-player-style", "godam-player-frontend-style" ], + "style": [ "godam-player-style", "godam-player-frontend-style" ], "editorScript": [ "file:./index.js" ], "viewScript": [ "godam-player-frontend-script", "godam-player-analytics-script" ], "render": "file:./render.php" diff --git a/assets/src/blocks/godam-player/edit.js b/assets/src/blocks/godam-player/edit.js index 16f51be37..5c4ec77c0 100644 --- a/assets/src/blocks/godam-player/edit.js +++ b/assets/src/blocks/godam-player/edit.js @@ -121,9 +121,6 @@ function VideoEdit( { verticalAlignment, overlayTimeRange, showOverlay, - aspectRatio, - videoWidth, - videoHeight, } = attributes; const [ temporaryURL, setTemporaryURL ] = useState( attributes.blob ); const [ defaultPoster, setDefaultPoster ] = useState( '' ); @@ -135,52 +132,35 @@ function VideoEdit( { const dispatch = useDispatch(); - // Calculate aspect ratio in x:y format, matching frontend logic. - const calculatedAspectRatio = useMemo( () => { - if ( aspectRatio === 'responsive' && videoWidth && videoHeight ) { - return `${ videoWidth }:${ videoHeight }`; - } - // Return aspectRatio if it's in x:y format, otherwise return '' as default - if ( aspectRatio && /^\d+:\d+$/.test( aspectRatio ) ) { - return aspectRatio; - } - - return ''; - }, [ aspectRatio, videoWidth, videoHeight ] ); - // Memoize video options to prevent unnecessary rerenders. - const videoOptions = useMemo( () => { - const options = { - controls, - autoplay, - preload, - fluid: true, - playsinline: true, - flvjs: { - mediaDataSource: { - isLive: true, - cors: false, - withCredentials: false, - }, + const videoOptions = useMemo( () => ( { + controls, + autoplay, + preload, + fluid: true, + playsinline: true, + flvjs: { + mediaDataSource: { + isLive: true, + cors: false, + withCredentials: false, }, - loop, - muted, - poster: poster || defaultPoster, - sources, - aspectRatio: calculatedAspectRatio, - // VHS (HLS/DASH) initial configuration to prefer a ~14 Mbps start. - // This only affects the initial bandwidth guess; VHS will continue to measure actual throughput and adapt. - html5: { - vhs: { - bandwidth: 14_000_000, // Pretend network can do ~14 Mbps at startup - bandwidthVariance: 1.0, // allow renditions close to estimate - limitRenditionByPlayerDimensions: false, // don't cap by video element size - }, + }, + loop, + muted, + poster: poster || defaultPoster, + sources, + aspectRatio: '16:9', + // VHS (HLS/DASH) initial configuration to prefer a ~14 Mbps start. + // This only affects the initial bandwidth guess; VHS will continue to measure actual throughput and adapt. + html5: { + vhs: { + bandwidth: 14_000_000, // Pretend network can do ~14 Mbps at startup + bandwidthVariance: 1.0, // allow renditions close to estimate + limitRenditionByPlayerDimensions: false, // don't cap by video element size }, - }; - - return options; - }, [ controls, autoplay, preload, loop, muted, poster, defaultPoster, sources, calculatedAspectRatio ] ); + }, + } ), [ controls, autoplay, preload, loop, muted, poster, defaultPoster, sources ] ); // Memoize the video component to prevent rerenders. const videoComponent = useMemo( () => ( diff --git a/assets/src/blocks/godam-player/editor.scss b/assets/src/blocks/godam-player/editor.scss index 5c7ffc2ac..312fa8634 100644 --- a/assets/src/blocks/godam-player/editor.scss +++ b/assets/src/blocks/godam-player/editor.scss @@ -1,24 +1,4 @@ -/** - * GoDAM Video Block - Editor styles - * These styles ensure the block works correctly in the editor, - * including in flex layouts (Row/Stack). - */ -.wp-block-godam-video { - width: 100%; - min-width: 0; // Allow shrinking in flex layouts (Row/Stack) - - // Ensure the figure takes full width - > figure { - width: 100%; - margin: 0; - } - - // Ensure the video wrapper maintains proper dimensions in editor - .godam-video-wrapper { - position: relative; - width: 100%; - } -} +@import url(https://vjs.zencdn.net/8.3.0/video-js.css); .wp-block-godam-video .godam-editor-video-placeholder { width: 100%; @@ -31,49 +11,3 @@ position: relative; aspect-ratio: 16 / 9; } - -.wp-block-godam-video { - - .godam-video-container { - position: relative; - width: 100%; - } - - .godam-video-loading { - width: 100%; - background-color: #000; - display: flex; - align-items: center; - justify-content: center; - z-index: 10; - - .godam-video-loading-spinner:not(.hide) { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - - &::after { - content: ''; - display: block; - width: 40px; - height: 40px; - border: 4px solid #fff; - border-top-color: transparent; - border-radius: 50%; - animation: godam-spin 1s linear infinite; - } - } - } - - @keyframes godam-spin { - - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } - } -} \ No newline at end of file diff --git a/assets/src/blocks/godam-player/index.js b/assets/src/blocks/godam-player/index.js index 5d97023ae..9369a7aa7 100644 --- a/assets/src/blocks/godam-player/index.js +++ b/assets/src/blocks/godam-player/index.js @@ -13,7 +13,7 @@ import { registerBlockType } from '@wordpress/blocks'; */ import Edit from './edit'; import save from './save'; -import icon from '../../images/godam-video-filled.svg'; +import { ReactComponent as icon } from '../../images/godam-video-filled.svg'; /** * Every block starts by registering a new block type definition. @@ -21,7 +21,7 @@ import icon from '../../images/godam-video-filled.svg'; * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/ */ registerBlockType( 'godam/video', { - icon: GoDAM Video Block icon, + icon, usesContext: [ 'queryId' ], /** * @see ./edit.js diff --git a/assets/src/blocks/godam-player/style.scss b/assets/src/blocks/godam-player/style.scss index 2e7889d92..ed7be82b9 100644 --- a/assets/src/blocks/godam-player/style.scss +++ b/assets/src/blocks/godam-player/style.scss @@ -9,28 +9,6 @@ @use "./cf7-godam-theme"; @use './variables'; -/** - * GoDAM Video Block - Main wrapper styles - * These styles ensure the block works correctly in all layout contexts, - * including flex layouts (Row, Stack) and standard layouts (Group, Columns). - */ -.wp-block-godam-video { - width: 100%; - min-width: 0; // Allow shrinking in flex layouts (Row/Stack) - - // Ensure the figure takes full width - figure[id^="godam-player-container"] { - width: 100%; - margin: 0; - } - - // Ensure the video wrapper maintains proper dimensions - .godam-video-wrapper { - position: relative; - width: 100%; - } -} - .rtgodam-video-caption { margin-bottom: 1em; margin-top: .5em; @@ -117,77 +95,6 @@ aspect-ratio: var(--rtgodam-video-aspect-ratio, 16 / 9); background-color: rgba(149, 149, 149, 0.2); box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - container-type: inline-size; - container-name: godam-player-default-skin; -} - -/* Responsive CTA Image Layer using Container Queries */ -@container (max-width: 580px) { - - .image-cta-container { - gap: 15px; - padding: 15px; - width: 96%; - height: auto; - overflow-y: auto; - - img { - - /* Scale down image for mobile but keep aspect ratio */ - width: 120px; - height: auto; - object-fit: contain; - flex-shrink: 0; - } - - .image-cta-description { - justify-content: flex-start; - } - - h2 { - font-size: 20px; - margin-bottom: 5px; - } - - p { - font-size: 13px; - margin-bottom: 10px; - line-height: 1.4; - } - - .image-cta-btn { - font-size: 13px; - padding: 8px 12px; - } - } -} - -@container (max-width: 400px) { - - .image-cta-container { - padding: 10px; - gap: 10px; - - img { - width: 100px; - } - - h2 { - font-size: 16px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - p { - font-size: 12px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - } } .animate-play-btn { @@ -259,8 +166,8 @@ .easydam-layer--cta-text { max-height: 100%; width: 100%; + padding: 2rem; overflow: auto; - padding: 0 2rem; } .easydam-layer--cta-html { @@ -375,30 +282,20 @@ flex-direction: row; gap: 30px; max-width: 600px; - width: 100%; background-color: #fff; align-items: center; padding: 20px; - margin: auto; h2 { font-size: 32px; font-weight: 700; line-height: 1.125; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } + } p { color: #8c8c8c; font-size: 16px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } + } .image-cta-btn { width: fit-content; @@ -411,7 +308,7 @@ border-radius: 0 20px 20px 0; display: flex; flex-direction: column; - gap: 0; + gap: 20px; align-content: center; justify-content: center; padding-left: 0; @@ -423,7 +320,7 @@ .vertical-image-cta-container { flex-direction: column; - max-width: 250px; + width: 300px; border-radius: 36px; img { @@ -434,7 +331,6 @@ .image-cta-description { text-align: center; padding: 0; - gap: 0; } .image-cta-btn { @@ -447,7 +343,7 @@ display: flex; justify-content: center; align-items: center; - min-height: 100%; + height: 100%; width: 100%; } @@ -455,9 +351,8 @@ position: absolute; top: 0; bottom: 0; - left: 0; - right: 0; overflow: auto; + padding: 2rem 1rem; h2 { margin-block-start: 0; @@ -465,7 +360,7 @@ } p { - font-size: 16px; + font-size: 13px; } img { @@ -475,16 +370,15 @@ a { text-decoration: none; } + + .image-cta-description { + gap: 10px; + } } .easydam-layer.hotspot-layer { pointer-events: none; background: transparent; - z-index: 5; -} - -.video-js .vjs-control-bar { - z-index: 8; } @@ -932,7 +826,6 @@ display: block !important; padding: 0 !important; margin: 0 !important; - max-height: 300px; overflow: auto; font-size: 12px !important; } diff --git a/assets/src/css/_common.scss b/assets/src/css/_common.scss index 48beb89ba..63b634aee 100644 --- a/assets/src/css/_common.scss +++ b/assets/src/css/_common.scss @@ -98,11 +98,8 @@ } &.godam-input__api-key { - .components-text-control__input{ - /* Adjust width to account for padding */ - /* @see: https://github.com/rtCamp/godam/issues/749 */ width: calc(100% + 0.75rem); } diff --git a/assets/src/css/_video-engagement.scss b/assets/src/css/_video-engagement.scss index ab6128968..758b61192 100644 --- a/assets/src/css/_video-engagement.scss +++ b/assets/src/css/_video-engagement.scss @@ -507,17 +507,6 @@ height: calc( 100% - clamp( 72px, 24vw, 170px ) ); max-height: 826px; position: relative; - box-sizing: border-box; - - &.is-embed-page { - width: 100% !important; - max-width: none !important; - height: 100% !important; - - @media screen and ( max-width: 768px ) { - padding: 0; - } - } &.is-skip-engagements { @@ -926,7 +915,6 @@ } &.like { - &::before { background-image: url( ../images/unlike-mobile-icon.svg ); } @@ -940,7 +928,6 @@ } &.is-liked { - &::before { background-image: url( ../images/like-mobile-icon.svg ); } @@ -948,14 +935,12 @@ } &.comment { - &::before { background-image: url( ../images/comment-mobile-icon.svg ); } } &.view { - &::before { background-image: url( ../images/view-mobile-icon.svg ); } @@ -993,7 +978,6 @@ .rtgodam-video-engagement--comment-modal-title { font-size: 1.1rem; font-weight: 600; - color: #fff; } .rtgodam-video-engagement--comment-modal--video-info-title { diff --git a/assets/src/css/bubble-skin.scss b/assets/src/css/bubble-skin.scss index 70efe3966..d84738df4 100644 --- a/assets/src/css/bubble-skin.scss +++ b/assets/src/css/bubble-skin.scss @@ -125,7 +125,6 @@ bottom: 25px; right: 90px; z-index: 19; - pointer-events: none; } } @@ -139,7 +138,6 @@ left: 40px; bottom: 25px; z-index: 19; - pointer-events: none; } } @@ -166,8 +164,6 @@ gap: 8px; padding: 4px 0 !important; flex-direction: column; - max-height: 300px; - overflow: auto; li { padding: 0; @@ -183,13 +179,10 @@ padding-top: 8px; } - .vjs-menu-item:not(.vjs-selected):hover { - font-weight: 400; - background-color: rgba(255, 255, 255, 0.1); - } - - .vjs-menu-item.vjs-selected:hover { - background-color: #fff !important; + .vjs-menu-item:hover { + font-weight: 900; + background-color: unset !important; + color: #cdc5c5 !important; } span::before { @@ -259,10 +252,6 @@ @container godam-player-bubble-skin (max-width: 480px) { - .video-js .vjs-big-play-button { - left: 55%; - } - .video-js .vjs-control-bar { margin: -55px auto 0; height: 5em !important; @@ -271,16 +260,12 @@ height: 3em !important; } - .vjs-custom-fullscreen-exit-control { - width: 3em !important; - } - .vjs-fullscreen-control { - right: 10px; + right: 10px } .vjs-volume-control { - display: none !important; // Fallback for old browser versions not supporting interaction media features + display: none; //Fallback for old browser versions not supporting interaction media features } .vjs-settings-button{ @@ -357,7 +342,6 @@ &:has(.vjs-subs-caps-button.vjs-hidden) .vjs-volume-panel { right: 108px; - width: 4em; } .vjs-time-control.vjs-current-time { @@ -395,9 +379,6 @@ } } - .vjs-menu-content { - max-height: 120px !important; - } .vjs-button.godam-share-button { bottom: 136px !important; @@ -415,7 +396,6 @@ width: 8em; bottom: 0 !important; left: 0 !important; - z-index: 19; .vjs-menu-content { @@ -471,98 +451,14 @@ } @container godam-player-bubble-skin (min-width: 768px) { - - .video-js .vjs-control-bar { - - justify-content: center; - - .vjs-duration { - position: relative !important; - display: flex !important; - align-items: center; - order: 1 !important; - z-index: 19; - margin-left: -65px; - transform: translateY(-50%); - - .vjs-duration-display { - position: static !important; - padding-left: 0; - right: unset !important; - bottom: unset !important; - } - } - - .vjs-current-time { - position: relative !important; - display: flex !important; - align-items: center; - order: 3 !important; - z-index: 19; - margin-right: -65px; - transform: translateY(-50%); - left: unset !important; - - .vjs-current-time-display { - position: static !important; - padding-right: 0; - left: unset !important; - bottom: unset !important; - } - } - - .vjs-progress-control { - order: 2 !important; - margin: 0; - } + + .vjs-current-time { + left: 65px !important; } - } - - @media screen and (min-width: 768px) { - - .video-js.vjs-fullscreen .vjs-control-bar { - - justify-content: center; - - .vjs-duration { - position: relative !important; - display: flex !important; - align-items: center; - order: 1 !important; - z-index: 19; - margin-left: -65px; - transform: translateY(-50%); - - .vjs-duration-display { - position: static !important; - padding-left: 0; - right: unset !important; - bottom: unset !important; - } - } - - .vjs-current-time { - position: relative !important; - display: flex !important; - align-items: center; - order: 3 !important; - z-index: 19; - margin-right: -65px; - transform: translateY(-50%); - left: unset !important; - - .vjs-current-time-display { - position: static !important; - padding-right: 0; - left: unset !important; - bottom: unset !important; - } - } - - .vjs-progress-control { - order: 2 !important; - margin: 0; - } + + .vjs-duration-display { + right: 100px !important; } + } } \ No newline at end of file diff --git a/assets/src/css/classic-skin.scss b/assets/src/css/classic-skin.scss index c7e5bf32c..d17462778 100644 --- a/assets/src/css/classic-skin.scss +++ b/assets/src/css/classic-skin.scss @@ -8,7 +8,6 @@ color: #1c1b1b !important; height: 60px !important; width: 60px !important; - left: 53% !important; span::before { font-size: 38px !important; @@ -272,8 +271,6 @@ gap: 8px; align-items: center; padding: 2px 4px !important; - max-height: 300px; - overflow: auto; } .vjs-settings-button { @@ -417,7 +414,7 @@ .vjs-menu { background-color: #2b333f; - z-index: 19; + z-index: 10; } .vjs-time-control { @@ -545,7 +542,6 @@ .video-js .vjs-big-play-button { height: 50px; width: 50px; - left: 55% !important; span::before { font-size: 1.7rem; @@ -623,9 +619,5 @@ .video-js .godam-chevron-icon { display: block; } - - .vjs-menu-content { - max-height: 150px !important; - } } } \ No newline at end of file diff --git a/assets/src/css/godam-gallery.scss b/assets/src/css/godam-gallery.scss index 106d88e5e..6a5cbc78d 100644 --- a/assets/src/css/godam-gallery.scss +++ b/assets/src/css/godam-gallery.scss @@ -9,82 +9,22 @@ &.columns-2 { grid-template-columns: repeat(2, 1fr); - - @media screen and (max-width: 320px) { - grid-template-columns: 1fr; - } } &.columns-3 { grid-template-columns: repeat(3, 1fr); - - @media screen and (max-width: 500px) { - grid-template-columns: repeat(2, 1fr); - } - - @media screen and (max-width: 320px) { - grid-template-columns: 1fr; - } } &.columns-4 { grid-template-columns: repeat(4, 1fr); - - @media screen and (max-width: 768px) { - grid-template-columns: repeat(3, 1fr); - } - - @media screen and (max-width: 500px) { - grid-template-columns: repeat(2, 1fr); - } - - @media screen and (max-width: 320px) { - grid-template-columns: 1fr; - } } &.columns-5 { grid-template-columns: repeat(5, 1fr); - - @media screen and (max-width: 900px) { - grid-template-columns: repeat(4, 1fr); - } - - @media screen and (max-width: 768px) { - grid-template-columns: repeat(3, 1fr); - } - - @media screen and (max-width: 500px) { - grid-template-columns: repeat(2, 1fr); - } - - @media screen and (max-width: 320px) { - grid-template-columns: 1fr; - } } &.columns-6 { grid-template-columns: repeat(6, 1fr); - - @media screen and (max-width: 1024px) { - grid-template-columns: repeat(5, 1fr); - } - - @media screen and (max-width: 900px) { - grid-template-columns: repeat(4, 1fr); - } - - @media screen and (max-width: 768px) { - grid-template-columns: repeat(3, 1fr); - } - - @media screen and (max-width: 500px) { - grid-template-columns: repeat(2, 1fr); - } - - @media screen and (max-width: 320px) { - grid-template-columns: 1fr; - } } // Move general selectors FIRST (lowest specificity) @@ -463,18 +403,11 @@ @keyframes godam-spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } + to { transform: rotate(360deg); } } // Compitibility with Like, comments and Views .is-gallery { - &.godam-modal, .godam-modal-overlay, .godam-modal-content { @@ -504,139 +437,3 @@ animation: godam-spin 1s ease-in-out infinite; } } - -// Video ID Modal Styles -.godam-video-id-modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 999999; - display: flex; - align-items: center; - justify-content: center; - - .godam-modal-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(10px); - display: flex; - align-items: center; - justify-content: center; - - .godam-overlay-loading-spinner { - display: inline-block; - width: 50px; - height: 50px; - border: 5px solid rgba(255, 255, 255, 0.3); - border-radius: 50%; - border-top-color: #fff; - animation: godam-spin 1s ease-in-out infinite; - z-index: 10; - } - } - - .godam-modal-content { - position: relative; - background: #fff; - border-radius: 12px; - max-width: min(95vw, 1200px); - max-height: min(95vh, 800px); - width: 100%; - height: 100%; - text-align: center; - z-index: 1; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - transition: opacity 0.3s ease; - - &.godam-modal-content-hidden { - opacity: 0; - pointer-events: none; - } - - &.godam-modal-content-visible { - opacity: 1; - pointer-events: auto; - animation: modalFadeIn 0.3s ease; - } - } - - .godam-modal-close { - position: absolute; - top: 32px; - right: 32px; - font-size: 28px; - cursor: pointer; - color: #333; - line-height: 1; - transition: color 0.2s ease, transform 0.2s ease; - z-index: 1; - background-color: #fff; - border-radius: 100px; - border: 1px solid #333; - outline: none; - - &:hover { - color: #000; - border-color: #000; - transform: scale(1.1); - } - - @media screen and (max-width: 768px) { - color: rgba(255, 255, 255, 0.8); - background-color: rgba(0, 0, 0, 0.7); - border: 1px solid rgba(255, 255, 255, 0.8); - top: 14px; - right: 14px; - - &:hover { - background-color: rgba(0, 0, 0, 0.9); - color: #fff; - border-color: #fff; - } - } - } - - .godam-modal-body { - height: 100%; - position: relative; - transition: transform 0.3s ease, opacity 0.3s ease; - - iframe { - width: 100%; - height: 100%; - border: none; - border-radius: 8px; - } - } - - &.godam-modal-fullscreen { - - .godam-modal-body { - position: fixed; - inset: 0; - } - - .godam-modal-close { - z-index: 0; - } - } -} - -@keyframes modalFadeIn { - - from { - opacity: 0; - transform: scale(0.9); - } - - to { - opacity: 1; - transform: scale(1); - } -} diff --git a/assets/src/css/godam-player.scss b/assets/src/css/godam-player.scss index 377ce7919..063bf9afd 100644 --- a/assets/src/css/godam-player.scss +++ b/assets/src/css/godam-player.scss @@ -56,8 +56,6 @@ background: rgba(255, 255, 255, 0.7); z-index: 10; color: initial; - container-type: inline-size; - container-name: easydam-layer-container; } .hidden { @@ -151,8 +149,8 @@ } @container godam-player-default-skin (max-width: 480px) { - width: 40px; - height: 40px; + width: 50px; + height: 30px; svg { width: 20px; @@ -200,27 +198,8 @@ .easydam-layer--cta-text { max-height: 100%; width: 100%; + padding: 2rem; overflow: auto; - padding: 0 2rem; - - /** - * Quill Styles. - */ - .ql-align-center { - text-align: center; - } - - .ql-align-right { - text-align: right; - } - - .ql-align-justify { - text-align: justify; - } - - strong { - font-weight: 700; - } } .easydam-layer--cta-html { @@ -330,59 +309,25 @@ } } -.image-cta-no-image { - width: clamp(100px, 20vw, 250px); - height: clamp(100px, 20vw, 200px); - display: flex; - justify-content: center; - align-items: center; - font-size: 1rem; - text-align: center; - background: linear-gradient(135deg, #f0f0f0 0%, #a7a7a7 100%); -} - .image-cta-container, .vertical-image-cta-container { display: flex; flex-direction: row; - gap: 20px; + gap: 30px; max-width: 600px; - width: 100%; background-color: #fff; align-items: center; padding: 20px; - border-radius: 12px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); - box-sizing: border-box; - margin: auto; h2 { - font-size: clamp(20px, 4vw, 32px); + font-size: 32px; font-weight: 700; line-height: 1.125; - margin: 0 0 10px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; } p { color: #8c8c8c; font-size: 16px; - margin: 0 0 20px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - img { - max-width: 100%; - width: 200px; - height: auto; - border-radius: 8px; - object-fit: cover; } .image-cta-btn { @@ -392,9 +337,12 @@ } .image-cta-description { - flex: 1; + flex-basis: 50%; + border-radius: 0 20px 20px 0; display: flex; flex-direction: column; + gap: 20px; + align-content: center; justify-content: center; padding-left: 0; @@ -405,9 +353,8 @@ .vertical-image-cta-container { flex-direction: column; - max-width: 250px; + width: 300px; border-radius: 36px; - text-align: center; img { border-radius: 25px; @@ -415,135 +362,56 @@ } .image-cta-description { + text-align: center; padding: 0; - align-items: center; } .image-cta-btn { + text-decoration: none; margin: 0 auto; } - - @container easydam-layer-container (max-width: 445px) { - width: 200px; - gap: 15px; - } } .image-cta-parent-container { display: flex; justify-content: center; align-items: center; - min-height: 100%; + height: 100%; width: 100%; - padding: 20px; - box-sizing: border-box; } .image-cta-overlay-container { position: absolute; top: 0; bottom: 0; - left: 0; - right: 0; overflow: auto; - width: 100%; - height: 100%; - box-sizing: border-box; + padding: 2rem 1rem; h2 { margin-block-start: 0; - margin-block-end: 10px; + margin-block-end: 0; } p { - font-size: 16px; - margin-bottom: 15px; + font-size: 13px; } img { height: auto; - max-width: 100%; } a { text-decoration: none; } -} - -/* Responsive CTA Image Layer using Container Queries */ -@container (max-width: 580px) { - - .image-cta-container { - gap: 15px; - padding: 15px; - width: 96%; - height: auto; - overflow-y: auto; - img { - - /* Scale down image for mobile but keep aspect ratio */ - width: 120px; - height: auto; - object-fit: contain; - flex-shrink: 0; - } - - .image-cta-description { - gap: 0; - justify-content: flex-start; - } - - h2 { - font-size: 20px; - margin-bottom: 5px; - } - - p { - font-size: 13px; - margin-bottom: 10px; - line-height: 1.4; - } - - .image-cta-btn { - font-size: 13px; - padding: 8px 12px; - } - } -} - -@container (max-width: 400px) { - - .image-cta-container { - padding: 10px; + .image-cta-description { gap: 10px; - - img { - width: 100px; - } - - h2 { - font-size: 16px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - p { - font-size: 12px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } } } .easydam-layer.hotspot-layer { pointer-events: none; background: transparent; - z-index: 5; &.overlapped { z-index: 0; @@ -980,24 +848,6 @@ body.godam-share-modal-open { opacity: 0; transition: opacity 0.2s ease, visibility 0.2s ease; word-wrap: break-word; - - /* Create an invisible bridge between hotspot and tooltip for smooth hover */ - &::before { - content: ""; - position: absolute; - left: 0; - right: 0; - height: 10px; - - /* Default: tooltip is above hotspot, so bridge is below tooltip */ - top: 100%; - } - - /* When tooltip is below hotspot, bridge should be above tooltip */ - &.tooltip-bottom::before { - top: auto; - bottom: 100%; - } } .fullscreen-layer .hotspot-tooltip { @@ -1128,16 +978,11 @@ body.godam-share-modal-open { .vjs-menu { opacity: 0; - z-index: 19; - - /* Use visibility in addition to opacity for better Safari compatibility */ - visibility: hidden; - transition: opacity 0.3s ease, visibility 0.3s ease; + transition: opacity 0.3s ease; } .vjs-menu.vjs-lock-showing { opacity: 1; - visibility: visible; } .vjs-menu.vjs-closing { @@ -1152,7 +997,6 @@ body.godam-share-modal-open { display: block !important; padding: 0 !important; margin: 0 !important; - max-height: 300px; overflow: auto; font-size: 12px !important; } @@ -1160,7 +1004,6 @@ body.godam-share-modal-open { .vjs-control-bar { background-color: var(--rtgodam-control-bar-color, #2b333fb3) !important; display: flex; - z-index: 8; } .vjs-control:hover { @@ -1458,19 +1301,6 @@ body.godam-share-modal-open { /* Style for video player fullscreen buttons */ .video-js { - /* Remove blue tap highlight on mobile devices */ - -webkit-tap-highlight-color: transparent !important; - - * { - - /* Remove focus outlines and box shadows from all player controls */ - &:focus, - &:active, - &:focus-visible { - outline: none; - } - } - .vjs-custom-fullscreen-exit-control { position: fixed !important; top: 0.75rem !important; @@ -1484,7 +1314,7 @@ body.godam-share-modal-open { background: url(../images/close.svg); background-position: center; background-repeat: no-repeat; - background-size: auto; + background-size: contain; display: inline-block; } @@ -1495,15 +1325,6 @@ body.godam-share-modal-open { &.vjs-fullscreen { - // Fix for Forms confirmation message font size issue for fullscreen videos - .form-container { - font-size: initial; - } - - .easydam-layer { - font-size: initial; - } - .vjs-custom-fullscreen-exit-control { display: block !important; } @@ -1522,10 +1343,6 @@ body.godam-share-modal-open { @container godam-player-default-skin (max-width: 480px) { - .vjs-big-play-button { - left: 55% !important; - } - .vjs-modal-dialog-content { padding: 15px 15px 0 15px; @@ -1548,7 +1365,6 @@ body.godam-share-modal-open { .vjs-control { height: 3em !important; - z-index: 10; } .vjs-play-control { @@ -1647,7 +1463,7 @@ body.godam-share-modal-open { .vjs-menu { background-color: #2b333f; - z-index: 19; + z-index: 10; } .vjs-time-control { @@ -1693,7 +1509,6 @@ body.godam-share-modal-open { bottom: 0; right: calc(3 * 40px); height: 3em !important; - z-index: 19; } } @@ -1718,10 +1533,6 @@ body.godam-share-modal-open { bottom: initial !important; } } - - .vjs-menu-content { - max-height: 120px !important; - } } } @@ -1738,26 +1549,9 @@ body.godam-share-modal-open { white-space: nowrap; } -/** - * GoDAM Video Block - Main wrapper styles - * These styles ensure the block works correctly in all layout contexts, - * including flex layouts (Row, Stack) and standard layouts (Group, Columns). - */ -.wp-block-godam-video { - width: 100%; - min-width: 0; // Allow shrinking in flex layouts (Row/Stack) - - // Ensure the figure takes full width - figure[id^="godam-player-container"] { - width: 100%; - margin: 0; - } -} - /* GoDAM Video Heading Overlay Styles */ .godam-video-wrapper { position: relative; - width: 100%; } /* Ensure Elementor widget wrapper maintains proper dimensions */ @@ -1803,6 +1597,7 @@ body.godam-share-modal-open { position: absolute; opacity: 0; visibility: hidden; + pointer-events: none; top: 10px; right: 10px; z-index: 10; @@ -1827,42 +1622,6 @@ body.godam-share-modal-open { } } - // FULLSCREEN CONTAINER STYLES - Critical for iOS Safari compatibility - &.godam-video-fullscreen { - // Hide share button in custom fullscreen (consistency with other devices) - .vjs-button.godam-share-button { - display: none !important; - } - - // Position the player to cover the entire viewport - position: fixed; - inset: 0; - width: 100vw; - - // DYNAMIC HEIGHT HANDLING FOR iOS (Critical Fix) - // Problem: iOS Safari viewport height changes dynamically when address bar collapses/expands - // Solution: Use multiple height declarations for maximum compatibility - - // Method 1: JavaScript-controlled CSS custom property (--vh) - // - Calculated by setupIOSViewportHeight() function in controlsManager.js - // - Uses visualViewport API when available (iOS 13+) - // - Accounts for address bar, keyboard, and pinch-zoom changes - // - Fallback to 1vh if --vh is not set (older browsers) - height: calc(var(--vh, 1vh) * 100); - - // Method 2: Native CSS dvh unit (Dynamic Viewport Height) - // - Modern browsers (iOS 15.4+, Chrome 108+, etc.) - // - Automatically adjusts when address bar collapses/expands - // - Most performant solution when supported - @supports (height: 100dvh) { - height: 100dvh; - } - background: #000; - z-index: 99999999; - display: flex; - align-items: center; - } - &:hover { .vjs-big-play-button { @@ -1890,12 +1649,19 @@ body.godam-share-modal-open { } } - // Hide share button in standard fullscreen - .video-js.vjs-fullscreen .vjs-button.godam-share-button { - display: none !important; + &.godam-video-fullscreen { + position: fixed; + inset: 0; + width: 100vw; + // Dynamic height calculation for iOS using --vh variable set by JS. + // Fallback to 100vh if --vh is not set, for non-iOS devices. + height: calc(var(--vh, 1vh) * 100); + background: #000; + z-index: 99999999; + display: flex; + align-items: center; } - .video-js { .vjs-big-play-button { @@ -2285,4 +2051,4 @@ body.godam-share-modal-open { font-size: 35px; } } -} +} \ No newline at end of file diff --git a/assets/src/css/godam-video-embed.scss b/assets/src/css/godam-video-embed.scss deleted file mode 100644 index e16d01a19..000000000 --- a/assets/src/css/godam-video-embed.scss +++ /dev/null @@ -1,162 +0,0 @@ -// Styles for the GoDAM video embed page -// This page is designed to be embedded in iframes/modals - -body.godam-embed-page { - margin: 0; - padding: 0; - background-color: #000; - overflow: hidden; - height: 100%; - width: 100%; - - .rtgodam-video-engagement { - display: none; - } -} - -html { - height: 100%; - width: 100%; -} - -.video-not-found { - text-align: center; - padding: 3rem 0.5rem; - font-size: 1.5rem !important; - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - color: #fff; - background-color: #000; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.godam-video-embed { - width: 100%; - height: 100%; - min-height: 100%; - display: flex; - align-items: center; - justify-content: center; - background-color: #000; - position: relative; - - &--container { - width: 100%; - height: 100%; - min-height: 100%; - display: flex; - align-items: center; - justify-content: center; - background-color: #000; - position: relative; - } - - // Ensure video player takes full available space and adjusts to container - figure { - margin: 0; - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; - display: flex; - align-items: center; - justify-content: center; - position: relative; - } - - .godam-video-wrapper { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; - display: flex; - align-items: center; - justify-content: center; - position: relative; - } - - .easydam-video-container { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; - position: relative; - } - - .video-js { - width: 100% !important; - height: 100% !important; - max-width: 100%; - max-height: 100%; - object-fit: contain; - } - - // Ensure video element inside video-js maintains aspect ratio - .video-js video { - width: 100%; - height: 100%; - object-fit: contain; - } - - // Make sure the player container is responsive - .video-js.vjs-fluid { - padding-top: 0 !important; - } - - .video-js.vjs-fluid, - .video-js.vjs-16-9, - .video-js.vjs-4-3 { - height: 100%; - } - - .srfm-form-container { - height: auto; - - .srfm-form { - margin-block-end: 0; - - .srfm-submit-button .srfm-submit-wrap { - height: auto; - } - } - } -} - -// Loading overlay styles -.godam-video-embed-loading { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #fff; - z-index: 9999; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s ease-out; -} - -// Spinner styles -.godam-video-embed-spinner { - width: 40px; - height: 40px; - border: 4px solid #f3f3f3; - border-top: 4px solid #333; - border-radius: 50%; - animation: godam-spin 1s linear infinite; -} - -@keyframes godam-spin { - - 0% { - transform: rotate( 0deg ); - } - - 100% { - transform: rotate( 360deg ); - } -} - diff --git a/assets/src/css/minimal-skin.scss b/assets/src/css/minimal-skin.scss index f4e66fe42..bb5806c8b 100644 --- a/assets/src/css/minimal-skin.scss +++ b/assets/src/css/minimal-skin.scss @@ -37,8 +37,6 @@ background-color: #fff !important; color: #000; border-radius: 4px; - max-height: 300px; - overflow: auto; li { padding: 8px; @@ -88,10 +86,6 @@ @container godam-player-minimal-skin (max-width: 480px) { - .video-js .vjs-big-play-button { - left: 55%; - } - .video-js .vjs-control-bar { height: 6em !important; @@ -197,12 +191,9 @@ .video-js .vjs-menu { width: 8em; - z-index: 19; .vjs-menu-content { - max-height: 120px !important; - .godam-back-item-container::after { top: 24.5px; } diff --git a/assets/src/css/pills-skin.scss b/assets/src/css/pills-skin.scss index 3ef93f7dd..b97eb0ee9 100644 --- a/assets/src/css/pills-skin.scss +++ b/assets/src/css/pills-skin.scss @@ -9,9 +9,6 @@ background-color: var(--rtgodam-control-bar-color, #000) !important; bottom: 0.5rem !important; z-index: 19; - - /* Add horizontal padding to prevent controls from touching the edges of the pill shape */ - padding: 0 10px; } .video-js .vjs-big-play-button { @@ -28,17 +25,7 @@ display: none; } - .vjs-current-time { - display: flex; - - /* Add left padding to separate time from the volume control in Pills skin */ - padding: 0 0 0 15px; - align-items: center; - justify-content: center; - min-width: 1.2em; - } - - .vjs-time-divider, .vjs-duration { + .vjs-current-time, .vjs-time-divider, .vjs-duration { display: flex !important; padding: 0 !important; align-items: center; @@ -73,8 +60,6 @@ background-color: #fff !important; color: #000; border-radius: 4px; - max-height: 300px; - overflow: auto; li { padding: 8px; @@ -121,10 +106,6 @@ @container godam-player-pills-skin (max-width: 480px) { - .video-js .vjs-big-play-button { - left: 55%; - } - .video-js .godam-central-controls { display: flex; align-items: center; @@ -224,8 +205,6 @@ width: 8em; .vjs-menu-content { - - max-height: 120px !important; .godam-back-item-container::after { top: 24.5px; diff --git a/assets/src/js/admin.js b/assets/src/js/admin.js index c5f9ccda3..559d5cc3b 100644 --- a/assets/src/js/admin.js +++ b/assets/src/js/admin.js @@ -162,29 +162,6 @@ function initAdminUI() { initTogglePostboxes(); handleBannerClose(); new ParticleEffect(); - initHandleInsightsDataNotice(); -} - -/** - * Toggle insights data description in admin notice. - */ -function initHandleInsightsDataNotice() { - document.body.addEventListener( 'click', function( e ) { - if ( e.target && e.target.classList.contains( 'godam-insights-data-we-collect' ) ) { - e.preventDefault(); - const updatedpParent = e.target.closest( '.updated' ); - if ( updatedpParent ) { - const description = updatedpParent.querySelector( '.description' ); - if ( description ) { - if ( description.style.display === 'none' ) { - description.style.display = 'block'; - } else { - description.style.display = 'none'; - } - } - } - } - } ); } document.addEventListener( 'DOMContentLoaded', initAdminUI ); diff --git a/assets/src/js/godam-gallery.js b/assets/src/js/godam-gallery.js index 45e95a90c..1aec08fd7 100644 --- a/assets/src/js/godam-gallery.js +++ b/assets/src/js/godam-gallery.js @@ -1,8 +1,17 @@ +/* global GODAMPlayer */ + +/** + * WordPress dependencies + */ +import { select, dispatch } from '@wordpress/data'; + /** * External dependencies */ import DOMPurify from 'isomorphic-dompurify'; +const engagementStore = 'godam-video-engagement'; + // Common function to load more videos async function loadMoreVideos( gallery, offset, columns, orderby, order, totalVideos ) { const loadCount = 3 * columns; @@ -31,8 +40,6 @@ async function loadMoreVideos( gallery, offset, columns, orderby, order, totalVi date_range: gallery.dataset.dateRange || '', custom_date_start: gallery.dataset.customDateStart || '', custom_date_end: gallery.dataset.customDateEnd || '', - engagements: gallery.dataset.engagements === '1', - open_to_new_page: gallery.dataset.openToNewPage === '1', } ); const response = await fetch( `/wp-json/godam/v1/gallery-shortcode?${ params.toString() }` ); const data = await response.json(); @@ -124,13 +131,11 @@ document.addEventListener( 'click', async function( e ) { const orderby = btn.getAttribute( 'data-orderby' ); const order = btn.getAttribute( 'data-order' ); const totalVideos = parseInt( btn.getAttribute( 'data-total' ), 10 ); - const engagements = btn.getAttribute( 'data-engagements' ) === '1'; - const openToNewPage = btn.getAttribute( 'data-open-to-new-page' ) === '1'; // Hide button btn.style.display = 'none'; - const newOffset = await loadMoreVideos( gallery, offset, columns, orderby, order, totalVideos, engagements, openToNewPage ); + const newOffset = await loadMoreVideos( gallery, offset, columns, orderby, order, totalVideos ); if ( newOffset ) { btn.setAttribute( 'data-offset', newOffset ); @@ -141,13 +146,13 @@ document.addEventListener( 'click', async function( e ) { } } ); -// Handle gallery item clicks - show video ID modal -document.addEventListener( 'click', function( e ) { +// Use event delegation - attach listener to document, check if clicked element matches +document.addEventListener( 'click', async function( e ) { + // Check if the clicked element is a video thumbnail if ( e.target.closest( '.godam-video-thumbnail' ) ) { const thumbnail = e.target.closest( '.godam-video-thumbnail' ); const videoId = thumbnail.getAttribute( 'data-video-id' ); - const videoUrl = thumbnail.getAttribute( 'data-video-url' ); - + const videoCptUrl = thumbnail.getAttribute( 'data-video-url' ); if ( ! videoId ) { return; } @@ -158,96 +163,307 @@ document.addEventListener( 'click', function( e ) { return; } - // If the gallery is configured to open videos in a new page, do so and skip the modal - const openToNewPage = currentGallery.getAttribute( 'data-open-to-new-page' ) === '1'; - if ( openToNewPage && videoUrl ) { - window.open( DOMPurify.sanitize( videoUrl ), '_blank' ); - return; + // Check if modal exists or create it + let modal = document.getElementById( 'godam-video-modal' ); + if ( ! modal ) { + modal = document.createElement( 'div' ); + modal.id = 'godam-video-modal'; + modal.className = 'godam-modal'; + document.body.appendChild( modal ); } + modal.classList.add( 'is-gallery' ); + + // Store current video ID and loading state + modal.dataset.currentVideoId = videoId; + modal.dataset.isLoading = 'false'; // Get gallery pagination info + const galleryOffset = parseInt( currentGallery.getAttribute( 'data-offset' ) || 0, 10 ); const galleryColumns = parseInt( currentGallery.getAttribute( 'data-columns' ) || 3, 10 ); const galleryOrderby = currentGallery.getAttribute( 'data-orderby' ) || 'date'; const galleryOrder = currentGallery.getAttribute( 'data-order' ) || 'DESC'; const galleryTotal = parseInt( currentGallery.getAttribute( 'data-total' ) || 0, 10 ); - // Create brand new modal with inline styles - const modal = document.createElement( 'div' ); - modal.className = 'godam-video-id-modal'; - modal.dataset.currentVideoId = videoId; - modal.dataset.isLoading = 'true'; + // Check if engagements are enabled for the gallery + const galleryEngagements = currentGallery.getAttribute( 'data-engagements' ) === '1'; + + // If open to new page is enabled, open the video in a new tab instead of modal + const openToNewPage = currentGallery.getAttribute( 'data-open-to-new-page' ) === '1'; + + if ( openToNewPage ) { + window.open( videoCptUrl, '_blank' ); + return; + } + + // Show modal immediately with the player modal.innerHTML = ` -
-
-
-
- -
- +
+
+ × +
+
+ + + +
+
+
`; - document.body.appendChild( modal ); + modal.classList.remove( 'hidden' ); + + // Prevent background scrolling when modal is open document.body.style.overflow = 'hidden'; - // Get iframe and elements - const iframe = modal.querySelector( 'iframe' ); - const overlaySpinner = modal.querySelector( '.godam-overlay-loading-spinner' ); - const modalContent = modal.querySelector( '.godam-modal-content' ); - - // Show modal content when iframe loads - const showModalContent = () => { - overlaySpinner.style.display = 'none'; - modalContent.classList.remove( 'godam-modal-content-hidden' ); - modalContent.classList.add( 'godam-modal-content-visible' ); - modal.dataset.isLoading = 'false'; - }; + // Function to load a new video + const loadNewVideo = async ( newVideoId ) => { + // Check if already loading + if ( modal.dataset.isLoading === 'true' ) { + return; + } - iframe.addEventListener( 'load', showModalContent ); + window.galleryScroll = false; - // Function to update modal with new video - const updateModalVideo = ( newVideoId, newVideoUrl, direction = 'next' ) => { - modal.dataset.currentVideoId = newVideoId; + // Set loading state modal.dataset.isLoading = 'true'; + modal.dataset.currentVideoId = newVideoId; - const _iframe = modal.querySelector( 'iframe' ); - const _overlaySpinner = modal.querySelector( '.godam-overlay-loading-spinner' ); - const _modalContent = modal.querySelector( '.godam-modal-content' ); - const modalBody = modal.querySelector( '.godam-modal-body' ); - - if ( _iframe && _overlaySpinner && _modalContent && modalBody ) { - // Show loading on overlay and hide content - _overlaySpinner.style.display = 'block'; - _modalContent.classList.remove( 'godam-modal-content-visible' ); - _modalContent.classList.add( 'godam-modal-content-hidden' ); - - // Add animation class based on direction - const animationClass = direction === 'next' ? 'slide-out-up' : 'slide-out-down'; - modalBody.classList.add( animationClass ); - - // After animation, update iframe - setTimeout( () => { - _iframe.src = DOMPurify.sanitize( newVideoUrl ); - - modalBody.classList.remove( animationClass ); - const slideInClass = direction === 'next' ? 'slide-in-down' : 'slide-in-up'; - modalBody.classList.add( slideInClass ); - - // Remove animation class after transition - setTimeout( () => { - modalBody.classList.remove( slideInClass ); - }, 300 ); - }, 300 ); + // Show loading state + const videoContainer = modal.querySelector( '.easydam-video-container' ); + if ( videoContainer ) { + videoContainer.innerHTML = ` +
+
+ + + +
+
+ `; + videoContainer.classList.add( 'animate-video-loading' ); + } + + try { + let data; + const videoMarkUp = select( engagementStore ).getVideoMarkUp()[ newVideoId ]; + if ( videoMarkUp ) { + data = { + html: videoMarkUp, + status: 'success', + }; + } else { + const response = await fetch( `/wp-json/godam/v1/video-shortcode?id=${ newVideoId }` ); + data = await response.json(); + + if ( data.status === 'success' && data.html ) { + dispatch( engagementStore ).addVideoMarkUp( newVideoId, data.html ); + } + } + + if ( data.status === 'success' && data.html ) { + // Update the video element with the fetched data + if ( videoContainer ) { + videoContainer.innerHTML = data.html; + videoContainer.classList.remove( 'animate-video-loading' ); + + // Update video title in the modal header + const videoTitle = modal.querySelector( '.godam-video-title' ); + if ( videoTitle ) { + videoTitle.innerHTML = DOMPurify.sanitize( data.title || '' ); + } + + // Update the date + const videoDate = modal.querySelector( '.godam-video-date' ); + if ( videoDate ) { + videoDate.textContent = data.date || ''; + } + + const engagementContainer = videoContainer.querySelector( '.rtgodam-video-engagement' ); + let engagementId = engagementContainer?.getAttribute( 'data-engagement-id' ) || 0; + const siteUrl = engagementContainer?.getAttribute( 'data-engagement-site-url' ) || ''; + + if ( ! galleryEngagements ) { + engagementContainer.remove(); + } + + // Reinitialize the player with the new content + if ( typeof GODAMPlayer === 'function' ) { + const godamPlayer = GODAMPlayer( modal ); + const initEngagement = godamPlayer.initEngagement; + + // Helper function to setup player once it's ready + const setupGalleryPlayer = ( playerInstance ) => { + if ( galleryEngagements ) { + // If engagements are enabled, initiate the comment modal with Data + let skipEngagements = false; + if ( 0 === engagementId ) { + const vidFigure = videoContainer.querySelector( 'figure' ); + engagementId = vidFigure?.id.replace( 'godam-player-container', 'engagement' ); + skipEngagements = true; + } + const newVideoEngagementsData = select( engagementStore ).getTitles()[ newVideoId ]; + + const startPlayback = () => { + dispatch( engagementStore ).initiateCommentModal( newVideoId, siteUrl, engagementId, skipEngagements ).then( () => { + window.galleryScroll = true; + } ); + playerInstance.play().catch( ( error ) => { + // eslint-disable-next-line no-console + console.warn( 'Failed to start gallery playback:', error ); + } ); + }; + + if ( newVideoEngagementsData ) { + startPlayback(); + } else { + initEngagement.then( startPlayback ); + } + engagementContainer?.remove(); + } else { + dispatch( engagementStore ).initiateCommentModal( newVideoId, siteUrl, engagementId, true ).then( () => { + window.galleryScroll = true; + } ); + playerInstance.play().catch( ( error ) => { + // eslint-disable-next-line no-console + console.warn( 'Failed to start gallery playback:', error ); + } ); + } + }; + + // Get the video element for this modal + const videoPlayerElement = modal.querySelector( '.video-js' ); + const videojs = window.videojs; + + // Clean up any previous event listener for this modal + if ( modal._galleryPlayerReadyHandler ) { + document.removeEventListener( 'godamPlayerReady', modal._galleryPlayerReadyHandler ); + modal._galleryPlayerReadyHandler = null; + } + + if ( videojs ) { + // Check if player is already ready (fallback for synchronous initialization) + const existingPlayer = videoPlayerElement ? videojs.getPlayer( videoPlayerElement ) : null; + if ( existingPlayer ) { + setupGalleryPlayer( existingPlayer ); + } else { + // Listen for the godamPlayerReady event (async initialization) + const onPlayerReady = ( event ) => { + // Ensure this event is for our video element + if ( event.detail.videoElement === videoPlayerElement ) { + setupGalleryPlayer( event.detail.player ); + document.removeEventListener( 'godamPlayerReady', onPlayerReady ); + modal._galleryPlayerReadyHandler = null; + } + }; + modal._galleryPlayerReadyHandler = onPlayerReady; + document.addEventListener( 'godamPlayerReady', onPlayerReady ); + } + } else { + // eslint-disable-next-line no-console + console.error( 'Video.js is not loaded. Cannot initialize player.' ); + } + } + } + } else if ( videoContainer ) { + videoContainer.innerHTML = '
Video could not be loaded.
'; + videoContainer.classList.remove( 'animate-video-loading' ); + } + } catch ( error ) { + // Handle error case + if ( videoContainer ) { + videoContainer.innerHTML = '
Video could not be loaded.
'; + videoContainer.classList.remove( 'animate-video-loading' ); + } + } finally { + // Reset loading state + modal.dataset.isLoading = 'false'; } }; + // Initialize the video player + if ( typeof GODAMPlayer === 'function' ) { + GODAMPlayer( modal ); + // Wait for player to be ready before trying to play + const videoPlayerElement = modal.querySelector( '.video-js' ); + const videojs = window.videojs; + + // Clean up any previous event listener for this modal + if ( modal._initialPlayerReadyHandler ) { + document.removeEventListener( 'godamPlayerReady', modal._initialPlayerReadyHandler ); + modal._initialPlayerReadyHandler = null; + } + + if ( videojs && videoPlayerElement ) { + // Check if player is already ready + const existingPlayer = videojs.getPlayer( videoPlayerElement ); + if ( existingPlayer ) { + existingPlayer.play().catch( ( error ) => { + // eslint-disable-next-line no-console + console.warn( 'Failed to start initial playback:', error ); + } ); + } else { + // Listen for the godamPlayerReady event + const onInitialPlayerReady = ( event ) => { + if ( event.detail.videoElement === videoPlayerElement ) { + event.detail.player.play(); + document.removeEventListener( 'godamPlayerReady', onInitialPlayerReady ); + modal._initialPlayerReadyHandler = null; + } + }; + modal._initialPlayerReadyHandler = onInitialPlayerReady; + document.addEventListener( 'godamPlayerReady', onInitialPlayerReady ); + } + } + } + + // Close handlers + const close = () => { + // Clean up event listeners to prevent memory leaks + if ( modal._initialPlayerReadyHandler ) { + document.removeEventListener( 'godamPlayerReady', modal._initialPlayerReadyHandler ); + modal._initialPlayerReadyHandler = null; + } + if ( modal._galleryPlayerReadyHandler ) { + document.removeEventListener( 'godamPlayerReady', modal._galleryPlayerReadyHandler ); + modal._galleryPlayerReadyHandler = null; + } + + // Send heatmap (type 2) for the current video on close BEFORE disposing/removing + const currentId = parseInt( modal.dataset.currentVideoId || 0, 10 ); + if ( currentId ) { + window.analytics?.trackVideoEvent( { type: 2, videoId: currentId, root: modal } ); + } + + // Find and dispose any video players in the modal + const players = modal.querySelectorAll( '.video-js' ); + players.forEach( ( player ) => { + if ( player.player ) { + player.player.dispose(); + } + } ); + modal.remove(); + // Re-enable background scrolling + document.body.style.overflow = ''; + document.body.removeEventListener( 'wheel', handleScroll ); + document.body.removeEventListener( 'touchend', handleTouchend ); + }; + + modal.querySelector( '.godam-modal-close' )?.addEventListener( 'click', close ); + modal.addEventListener( 'click', ( err ) => { + if ( ! err.target.closest( '.godam-modal-content' ) ) { + close(); + } + } ); + + // Load initial video + await loadNewVideo( videoId ); + // Function to get current video items (updates after loading more) const getCurrentVideoItems = () => { return currentGallery.querySelectorAll( '.godam-video-item' ); @@ -261,10 +477,13 @@ document.addEventListener( 'click', function( e ) { // Check if there are more videos to load if ( currentVideoCount < galleryTotal ) { + // Calculate current offset + const currentOffset = galleryOffset + currentVideoCount; + // Try to load more videos const newOffset = await loadMoreVideos( currentGallery, - currentVideoCount, + currentOffset, galleryColumns, galleryOrderby, galleryOrder, @@ -272,106 +491,49 @@ document.addEventListener( 'click', function( e ) { ); // If successfully loaded more videos, return true - return !! newOffset; - } - } - return false; - }; - - // Function to navigate to next/previous video (used by postMessage handler) - const navigateVideo = async ( direction ) => { - // Check if modal is ready for navigation - if ( modal.dataset.isLoading === 'true' ) { - return; - } - - // Get current video items (refreshed) - let videoItems = getCurrentVideoItems(); - - // Get current video index - const currentId = modal.dataset.currentVideoId; - const currentIndex = Array.from( videoItems ).findIndex( ( item ) => - item.querySelector( '.godam-video-thumbnail' ).getAttribute( 'data-video-id' ) === currentId, - ); - - if ( currentIndex === -1 ) { - return; - } - - let newIndex; - - if ( direction === 'next' ) { - // Navigate to next video - if ( currentIndex === videoItems.length - 1 ) { - // At last video, try to load more - modal.dataset.isLoading = 'true'; - const moreLoaded = await loadMoreIfNeeded( currentIndex, videoItems ); - modal.dataset.isLoading = 'false'; - - if ( moreLoaded ) { - // Refresh video items after loading more - videoItems = getCurrentVideoItems(); - newIndex = currentIndex + 1; - } else { - // No more videos available, do nothing - return; + if ( newOffset ) { + // Update the load more button if it exists + const loadMoreBtn = currentGallery.nextElementSibling; + if ( loadMoreBtn && loadMoreBtn.classList.contains( 'godam-load-more' ) ) { + loadMoreBtn.setAttribute( 'data-offset', newOffset ); + + // Hide button if all videos are loaded + if ( newOffset >= galleryTotal ) { + loadMoreBtn.remove(); + } + } + return true; } - } else { - newIndex = currentIndex + 1; - } - } else { - // Navigate to previous video - if ( currentIndex === 0 ) { - // At first video, do nothing - return; - } - newIndex = currentIndex - 1; - } - - // Get the new video ID - const newThumbnail = videoItems[ newIndex ]?.querySelector( '.godam-video-thumbnail' ); - if ( newThumbnail ) { - const newVideoId = newThumbnail.getAttribute( 'data-video-id' ); - const newVideoUrl = newThumbnail.getAttribute( 'data-video-url' ); - if ( newVideoId && newVideoId !== currentId && newVideoUrl ) { - updateModalVideo( newVideoId, newVideoUrl, direction ); } } + return false; }; - // Scroll functionality with cooldown and threshold + // Scroll functionality for desktop (wheel events) const SCROLL_COOLDOWN = 1000; // milliseconds - const SCROLL_THRESHOLD = 200; // Minimum scroll distance to trigger let lastScrollTime = 0; let scrollTimeout; - let accumulatedDelta = 0; const handleScroll = async ( event ) => { event.preventDefault(); // Prevent background scroll event.stopPropagation(); // Prevent event bubbling - // Check if modal is ready for scrolling - if ( modal.dataset.isLoading === 'true' ) { + if ( ! window.galleryScroll ) { return; } - // Accumulate scroll delta - accumulatedDelta += event.deltaY; + const currentPopUp = document.querySelector( '#rtgodam-video-engagement--comment-modal' ); + if ( ! currentPopUp ) { + return; + } // Clear any existing timeout clearTimeout( scrollTimeout ); // Set a new timeout for debouncing scrollTimeout = setTimeout( async () => { - // Check if accumulated scroll meets threshold - if ( Math.abs( accumulatedDelta ) < SCROLL_THRESHOLD ) { - accumulatedDelta = 0; - return; - } - const currentTime = Date.now(); if ( currentTime - lastScrollTime < SCROLL_COOLDOWN ) { - accumulatedDelta = 0; return; } @@ -385,27 +547,22 @@ document.addEventListener( 'click', function( e ) { ); if ( currentIndex === -1 ) { - accumulatedDelta = 0; return; } let newIndex; - if ( accumulatedDelta > 0 ) { + if ( event.deltaY > 0 ) { // Scrolling down (next video) if ( currentIndex === videoItems.length - 1 ) { // At last video, try to load more - modal.dataset.isLoading = 'true'; const moreLoaded = await loadMoreIfNeeded( currentIndex, videoItems ); - modal.dataset.isLoading = 'false'; - if ( moreLoaded ) { // Refresh video items after loading more videoItems = getCurrentVideoItems(); newIndex = currentIndex + 1; } else { // No more videos available, do nothing - accumulatedDelta = 0; return; } } else { @@ -415,7 +572,6 @@ document.addEventListener( 'click', function( e ) { // Scrolling up (previous video) if ( currentIndex === 0 ) { // At first video, do nothing - accumulatedDelta = 0; return; } newIndex = currentIndex - 1; @@ -425,43 +581,36 @@ document.addEventListener( 'click', function( e ) { const newThumbnail = videoItems[ newIndex ]?.querySelector( '.godam-video-thumbnail' ); if ( newThumbnail ) { const newVideoId = newThumbnail.getAttribute( 'data-video-id' ); - const newVideoUrl = newThumbnail.getAttribute( 'data-video-url' ); - if ( newVideoId && newVideoId !== currentId && newVideoUrl ) { + if ( newVideoId && newVideoId !== currentId ) { lastScrollTime = currentTime; + const popUpElement = document.querySelector( '#rtgodam-video-engagement--comment-modal' ); + if ( popUpElement ) { + popUpElement.style.display = 'none'; + } + // Send type 2 for the old video + window.analytics?.trackVideoEvent( { type: 2, root: document.querySelector( '#rtgodam-video-engagement--comment-modal' ) } ); - const direction = accumulatedDelta > 0 ? 'next' : 'prev'; - updateModalVideo( newVideoId, newVideoUrl, direction ); + loadNewVideo( newVideoId ); } } - - accumulatedDelta = 0; }, 150 ); // Debounce delay }; - // Touch functionality for mobile - let touchStartY = 0; - let touchEndY = 0; - - const handleTouchStart = ( err ) => { - touchStartY = err.touches[ 0 ].clientY; - }; - - const handleTouchMove = ( err ) => { - err.preventDefault(); // Prevent background scroll - err.stopPropagation(); // Prevent event bubbling - }; - - const handleTouchEnd = async ( err ) => { + const handleTouchend = async ( err ) => { touchEndY = err.changedTouches[ 0 ].clientY; const touchDiff = touchStartY - touchEndY; - // Check if modal is ready - if ( modal.dataset.isLoading === 'true' ) { + if ( ! window.galleryScroll ) { + return; + } + + const currentPopUp = document.querySelector( '#rtgodam-video-engagement--comment-modal' ); + if ( ! currentPopUp ) { return; } // Minimum swipe distance - if ( Math.abs( touchDiff ) < SCROLL_THRESHOLD ) { + if ( Math.abs( touchDiff ) < 50 ) { return; } @@ -494,10 +643,7 @@ document.addEventListener( 'click', function( e ) { // Swiping up (next video) if ( currentIndex === videoItems.length - 1 ) { // At last video, try to load more - modal.dataset.isLoading = 'true'; const moreLoaded = await loadMoreIfNeeded( currentIndex, videoItems ); - modal.dataset.isLoading = 'false'; - if ( moreLoaded ) { // Refresh video items after loading more videoItems = getCurrentVideoItems(); @@ -522,85 +668,37 @@ document.addEventListener( 'click', function( e ) { const newThumbnail = videoItems[ newIndex ]?.querySelector( '.godam-video-thumbnail' ); if ( newThumbnail ) { const newVideoId = newThumbnail.getAttribute( 'data-video-id' ); - const newVideoUrl = newThumbnail.getAttribute( 'data-video-url' ); - if ( newVideoId && newVideoId !== currentId && newVideoUrl ) { + if ( newVideoId && newVideoId !== currentId ) { lastScrollTime = currentTime; + const popUpElement = document.querySelector( '#rtgodam-video-engagement--comment-modal' ); + if ( popUpElement ) { + popUpElement.style.display = 'none'; + } + + // Send type 2 for the old video + window.analytics?.trackVideoEvent( { type: 2, root: document.querySelector( '#rtgodam-video-engagement--comment-modal' ) } ); - const direction = touchDiff > 0 ? 'next' : 'prev'; - updateModalVideo( newVideoId, newVideoUrl, direction ); + loadNewVideo( newVideoId ); } } }, 150 ); // Debounce delay }; - // Add scroll listener - document.addEventListener( 'wheel', handleScroll, { passive: false } ); + document.body.addEventListener( 'wheel', handleScroll, { passive: false } ); - // Add touch listeners - document.body.addEventListener( 'touchstart', handleTouchStart, { passive: true } ); - document.body.addEventListener( 'touchmove', handleTouchMove, { passive: false } ); - document.body.addEventListener( 'touchend', handleTouchEnd, { passive: false } ); - - // Handle postMessage from iframe - const handlePostMessage = async ( event ) => { - // Only handle messages when modal exists and is open - if ( ! modal || ! document.body.contains( modal ) ) { - return; - } - - // Verify message is from the iframe (check origin if needed) - // For security, you might want to check event.origin is same as your site origin. - if ( event.origin !== window.location.origin ) { - return; - } - - if ( event.data && event.data.type ) { - if ( event.data.type === 'godamScrollNext' ) { - await navigateVideo( 'next' ); - } else if ( event.data.type === 'godamScrollPrevious' ) { - await navigateVideo( 'prev' ); - } else if ( event.data.type === 'godamFullscreenChange' ) { - // Custom logic for fullscreen change event - const { isFullscreen } = event.data; - - // Add or remove fullscreen class to modal - if ( isFullscreen ) { - modal.classList.add( 'godam-modal-fullscreen' ); - } else { - modal.classList.remove( 'godam-modal-fullscreen' ); - } - } - } - }; - - // Add message listener - window.addEventListener( 'message', handlePostMessage ); + // Touch functionality for mobile + let touchStartY = 0; + let touchEndY = 0; - // Close on ESC key - const handleEscape = ( event ) => { - if ( event.key === 'Escape' ) { - closeModal(); - } - }; - document.addEventListener( 'keydown', handleEscape ); - - // Close modal function - const closeModal = () => { - document.removeEventListener( 'wheel', handleScroll ); - document.body.removeEventListener( 'touchstart', handleTouchStart ); - document.body.removeEventListener( 'touchmove', handleTouchMove ); - document.body.removeEventListener( 'touchend', handleTouchEnd ); - window.removeEventListener( 'message', handlePostMessage ); - document.removeEventListener( 'keydown', handleEscape ); - iframe.removeEventListener( 'load', showModalContent ); - modal.remove(); - document.body.style.overflow = ''; - }; + document.body.addEventListener( 'touchstart', ( err ) => { + touchStartY = err.touches[ 0 ].clientY; + }, { passive: false } ); - // Close on X button click - modal.querySelector( '.godam-modal-close' ).addEventListener( 'click', closeModal ); + document.body.addEventListener( 'touchmove', ( err ) => { + err.preventDefault(); // Prevent background scroll + err.stopPropagation(); // Prevent event bubbling + }, { passive: false } ); - // Close on overlay click - modal.querySelector( '.godam-modal-overlay' ).addEventListener( 'click', closeModal ); + document.body.addEventListener( 'touchend', handleTouchend, { passive: false } ); } } ); diff --git a/assets/src/js/godam-player/engagement.js b/assets/src/js/godam-player/engagement.js index 1c57bb7ab..4c8c22ea5 100644 --- a/assets/src/js/godam-player/engagement.js +++ b/assets/src/js/godam-player/engagement.js @@ -216,15 +216,13 @@ const engagementStore = { * @param {string} siteUrl - The URL of the site where the video is hosted. * @param {string} videoId - The ID of the video. * @param {boolean} skipEngagements - Whether to skip engagements. - * @param {boolean} isEmbedPage - Whether the comment modal is for an embed page. * * @return {Object} An action object containing the type. */ - initiateCommentModal: ( videoAttachmentId, siteUrl, videoId, skipEngagements = false, isEmbedPage = false ) => { - engagementStore.generateCommentModal( videoAttachmentId, siteUrl, videoId, skipEngagements, isEmbedPage ); + initiateCommentModal: ( videoAttachmentId, siteUrl, videoId, skipEngagements = false ) => { + engagementStore.generateCommentModal( videoAttachmentId, siteUrl, videoId, skipEngagements ); return { type: ACTIONS.GENERATE_COMMENT_MODAL, - isEmbedPage, }; }, @@ -491,9 +489,8 @@ const engagementStore = { * @param {string} siteUrl The site URL. * @param {string} videoId The video attachment ID. * @param {boolean} skipEngagements Whether to skip engagements. - * @param {boolean} isEmbedPage Whether the comment modal is for an embed page. */ - generateCommentModal( videoAttachmentId, siteUrl, videoId, skipEngagements = false, isEmbedPage = false ) { + generateCommentModal( videoAttachmentId, siteUrl, videoId, skipEngagements = false ) { const modalId = 'rtgodam-video-engagement--comment-modal'; let commentModal = document.getElementById( modalId ); @@ -504,16 +501,7 @@ const engagementStore = { commentModal.setAttribute( 'id', modalId ); document.body.appendChild( commentModal ); this.root = createRoot( commentModal ); - this.root.render( - , - ); + this.root.render( ); }, }; @@ -767,9 +755,9 @@ function timeToSeconds( h, m, s ) { /** * Component to render a text with @HH:MM:SS or @MM:SS timestamps linked to video positions. * - * @param {Object} props - Component props. - * @param {string} props.text - Text to render. - * @param {Function} [props.onJump] - Callback to handle clicking a timestamp. + * @param {Object} props - Component props. + * @param {string} props.text - Text to render. + * @param {function(number, string)} [props.onJump] - Callback to handle clicking a timestamp. */ function TimeLinkedText( { text, onJump } ) { // Matches @HH:MM:SS or @MM:SS @@ -1133,24 +1121,14 @@ function GuestLoginForm( props ) { * @return {JSX.Element} A React element representing the comment box modal. */ function CommentBox( props ) { - const { videoAttachmentId, storeObj, siteUrl, videoId, skipEngagements, isEmbedPage } = props; + const { videoAttachmentId, storeObj, siteUrl, videoId, skipEngagements } = props; const baseClass = 'rtgodam-video-engagement--comment-modal'; const memoizedStoreObj = useMemo( () => storeObj, [ storeObj ] ); const commentsCount = memoizedStoreObj.select.getCommentsCount()[ videoAttachmentId ] || 0; const likesCount = memoizedStoreObj.select.getLikes()[ videoAttachmentId ] || 0; const viewsCount = memoizedStoreObj.select.getViews()[ videoAttachmentId ] || 0; const isUserLiked = memoizedStoreObj.select.getIsUserLiked()[ videoAttachmentId ] || false; - - // Get title from store or fallback to video element's data-video-title attribute - let titles = memoizedStoreObj.select.getTitles()[ videoAttachmentId ]; - if ( ! titles ) { - const videoKey = videoId.replace( 'engagement-', '' ); - const videoFigureId = `godam-player-container-${ videoKey }`; - const videoContainer = document.getElementById( videoFigureId ); - const videoElement = videoContainer?.querySelector( 'video[data-video-title]' ); - titles = videoElement?.getAttribute( 'data-video-title' ) || __( 'GoDAM Video', 'godam' ); - } - + const titles = memoizedStoreObj.select.getTitles()[ videoAttachmentId ] || __( 'GoDAM Video', 'godam' ); const comments = memoizedStoreObj.select.getComments()[ videoAttachmentId ] || []; const [ commentsData, setCommentsData ] = useState( comments ); const videoKey = videoId.replace( 'engagement-', '' ); @@ -1166,35 +1144,18 @@ function CommentBox( props ) { useEffect( () => { const currentVideoParent = document.getElementById( videoFigureId ); - - if ( ! currentVideoParent ) { - return; - } - const currentVideo = currentVideoParent.querySelector( '.godam-video-wrapper' ); - - if ( ! currentVideo ) { - return; - } - - const videoContainer = videoContainerRef.current; - - if ( ! videoContainer ) { - return; - } - const currentVideoClass = currentVideoParent.className; const currentVideoStyles = currentVideoParent.getAttribute( 'style' ); + const videoContainer = videoContainerRef.current; videoContainer.className = currentVideoClass; videoContainer.style = currentVideoStyles; videoContainer.appendChild( currentVideo ); document.body.classList.add( 'no-scroll' ); return () => { - if ( currentVideoParent && currentVideo ) { - currentVideoParent.insertBefore( currentVideo, currentVideoParent.firstChild ); - } + currentVideoParent.insertBefore( currentVideo, currentVideoParent.firstChild ); document.body.classList.remove( 'no-scroll' ); // Godam gallery cleanup if needed @@ -1226,17 +1187,10 @@ function CommentBox( props ) { return (
-
+

{ titles }

- { - ! isEmbedPage && ( - - ) - } +
@@ -1318,9 +1272,6 @@ function CommentBox( props ) { * This function should be called once to initialize the video engagement store. * It is called automatically on page load by the Godam plugin. */ -export async function engagement() { - const engagementStoreInstance = await engagementStore.init(); - // Dispatch custom event to notify that the engagement store is initialized. - document.dispatchEvent( new CustomEvent( 'godamEngagementStoreInitialized', { detail: { engagementStoreInstance } } ) ); - return engagementStoreInstance; +export function engagement() { + return engagementStore.init(); } diff --git a/assets/src/js/godam-player/managers/configurationManager.js b/assets/src/js/godam-player/managers/configurationManager.js index 0942ac4da..77d813ed6 100644 --- a/assets/src/js/godam-player/managers/configurationManager.js +++ b/assets/src/js/godam-player/managers/configurationManager.js @@ -77,13 +77,7 @@ export default class ConfigurationManager { this.videoSetupControls = { ...videoSetupControls, sources, - // Disable native text tracks to use Video.js custom UI (crucial for Safari/iOS hover menus) - nativeTextTracks: false, - // Ensure video plays inline on mobile devices instead of forcing native fullscreen - playsinline: true, html5: { - // Redundant but safe: ensure HTML5 tech also respects custom text tracks - nativeTextTracks: false, vhs: { bandwidth: 14_000_000, // Pretend network can do ~14 Mbps at startup bandwidthVariance: 1.0, // allow renditions close to estimate @@ -91,19 +85,6 @@ export default class ConfigurationManager { }, }, }; - - const isIOS = /iPad|iPhone|iPod/.test( navigator.userAgent ) && ! window.MSStream; - const isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent ); - - if ( isIOS || isSafari ) { - // forces VHS even on Safari and iOS devices - // This will override native HLS playback with VHS to support features like quality selection. - this.videoSetupControls.html5.vhs.overrideNative = true; - this.videoSetupControls.html5.nativeAudioTracks = false; - this.videoSetupControls.html5.nativeVideoTracks = false; - this.videoSetupControls.html5.nativeTextTracks = false; - } - this.isPreviewEnabled = this.videoSetupOptions?.preview; this.ensureControlBarDefaults(); diff --git a/assets/src/js/godam-player/managers/controlsManager.js b/assets/src/js/godam-player/managers/controlsManager.js index c01b3a4cc..d62f1da5e 100644 --- a/assets/src/js/godam-player/managers/controlsManager.js +++ b/assets/src/js/godam-player/managers/controlsManager.js @@ -95,57 +95,29 @@ export default class ControlsManager { const videoContainer = e.target.closest( '.video-js' ); const godamVideoContainer = e.target.closest( '.easydam-video-container' ); - // Determine current fullscreen state by checking for fullscreen classes + // Check both containers for fullscreen classes const isFullscreen = ( videoContainer && videoContainer.classList.contains( 'vjs-fullscreen' ) ); if ( isFullscreen ) { - // EXIT FULLSCREEN MODE - - // Remove fullscreen CSS classes from video containers if ( videoContainer ) { videoContainer.classList.remove( 'vjs-fullscreen' ); } if ( godamVideoContainer ) { godamVideoContainer.classList.remove( 'godam-video-fullscreen' ); } - - // RESTORE BACKGROUND SCROLLING (iOS-compatible method) - // Step 1: Get the saved scroll position from body's top style - const scrollY = document.body.style.top; - // Step 2: Reset all scroll-prevention styles + // Restore scrolling document.body.style.overflow = ''; - document.body.style.position = ''; - document.body.style.top = ''; - document.body.style.width = ''; - document.documentElement.style.overflow = ''; - // Step 3: Restore the original scroll position (multiply by -1 because it was stored as negative) - if ( scrollY ) { - window.scrollTo( 0, parseInt( scrollY || '0' ) * -1 ); - } } else { - // ENTER FULLSCREEN MODE - - // Add fullscreen CSS classes to video containers if ( videoContainer ) { videoContainer.classList.add( 'vjs-fullscreen' ); } if ( godamVideoContainer ) { godamVideoContainer.classList.add( 'godam-video-fullscreen' ); } - - // PREVENT BACKGROUND SCROLLING (robust iOS method) - // iOS Safari doesn't respect overflow: hidden alone, so we use position: fixed - // Step 1: Save current scroll position - const scrollY = window.scrollY; - // Step 2: Prevent scrolling by fixing body position and offsetting it + // Prevent scrolling on iOS document.body.style.overflow = 'hidden'; - document.documentElement.style.overflow = 'hidden'; - document.body.style.position = 'fixed'; - document.body.style.top = `-${ scrollY }px`; // Negative offset to keep content in place - document.body.style.width = '100%'; // Prevent horizontal scrollbar } - - // Notify other components about fullscreen state change + // Trigger customfullscreenchange event on the VideoJS player this.player().trigger( 'customfullscreenchange' ); } } @@ -173,13 +145,9 @@ export default class ControlsManager { } handleClick( e ) { - // Find the video containers that need fullscreen styling removed const videoContainer = e.target.closest( '.video-js' ); const godamVideoContainer = e.target.closest( '.easydam-video-container' ); - // EXIT FULLSCREEN MODE (always exit when this button is clicked) - - // Remove fullscreen CSS classes from video containers if ( videoContainer ) { videoContainer.classList.remove( 'vjs-fullscreen' ); } @@ -187,21 +155,9 @@ export default class ControlsManager { godamVideoContainer.classList.remove( 'godam-video-fullscreen' ); } - // RESTORE BACKGROUND SCROLLING (iOS-compatible method) - // Step 1: Get the saved scroll position from body's top style - const scrollY = document.body.style.top; - // Step 2: Reset all scroll-prevention styles + // Restore scrolling document.body.style.overflow = ''; - document.body.style.position = ''; - document.body.style.top = ''; - document.body.style.width = ''; - document.documentElement.style.overflow = ''; - // Step 3: Restore the original scroll position (multiply by -1 because it was stored as negative) - if ( scrollY ) { - window.scrollTo( 0, parseInt( scrollY || '0' ) * -1 ); - } - - // Notify other components about fullscreen state change + // Trigger customfullscreenchange event on the VideoJS player this.player().trigger( 'customfullscreenchange' ); } } @@ -246,81 +202,36 @@ export default class ControlsManager { * Setup iOS dynamic viewport height variable (--vh) * Ensures fullscreen container height matches the visible viewport */ - /** - * Setup dynamic viewport height tracking for iOS devices - * - * PROBLEM: On iOS Safari, the viewport height changes dynamically when: - * - Address bar collapses/expands during scrolling - * - On-screen keyboard appears/disappears - * - Device rotates between portrait/landscape - * - * SOLUTION: Use the visualViewport API (iOS 13+) which provides accurate - * viewport dimensions that account for dynamic UI elements. This ensures - * fullscreen containers always fill the available screen space. - */ setupIOSViewportHeight() { - /** - * Calculate and update the CSS custom property --vh with current viewport height - * - * This function is called whenever the viewport size changes to ensure - * the fullscreen player container always matches the visible viewport. - */ const setViewportHeight = () => { - // Get accurate viewport height accounting for dynamic UI elements - let vh; - - // PREFERRED: Use visualViewport API (iOS 13+, modern browsers) - // - Accounts for on-screen keyboard - // - Accounts for dynamic address bar (iOS Safari) - // - Accounts for pinch-to-zoom scaling - if ( window.visualViewport ) { - vh = window.visualViewport.height * 0.01; // Convert to vh units (1% of viewport height) - } else { - // FALLBACK: Use window.innerHeight (older browsers) - // Less accurate but better than nothing - vh = window.innerHeight * 0.01; - } - - // Set CSS custom property that can be used in stylesheets - // Example usage: height: calc(var(--vh, 1vh) * 100); + // Calculate and set the custom CSS property --vh to match viewport height. + // Multiply by 0.01 to convert to viewport height percentage. (1vh = 1% of viewport height). + const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty( '--vh', `${ vh }px` ); }; - // INITIALIZATION: Set viewport height immediately when called + // Initialize on load setViewportHeight(); - // EVENT LISTENER 1: visualViewport API (most accurate, iOS 13+) - if ( window.visualViewport ) { - // Listen for viewport resize events (address bar, keyboard, etc.) - window.visualViewport.addEventListener( 'resize', setViewportHeight ); - - // Also listen for scroll events as viewport can change during scrolling - window.visualViewport.addEventListener( 'scroll', setViewportHeight ); - } - - // EVENT LISTENER 2: Window resize (fallback for older browsers) - // Use requestAnimationFrame for smooth updates instead of setTimeout + // Debounced resize handler let resizeTimer = null; window.addEventListener( 'resize', () => { - // Cancel previous animation frame to debounce rapid events if ( resizeTimer ) { - cancelAnimationFrame( resizeTimer ); + clearTimeout( resizeTimer ); } - // Schedule update for next animation frame (smooth, performant) - resizeTimer = requestAnimationFrame( setViewportHeight ); - }, { passive: true } ); // passive: true for better scroll performance + resizeTimer = setTimeout( setViewportHeight, 100 ); + }, { passive: true } ); - // EVENT LISTENER 3: Orientation changes (device rotation) + // Handle orientation changes if ( screen.orientation ) { - // Modern Screen Orientation API (preferred) screen.orientation.addEventListener( 'change', () => { - // Delay slightly to allow iOS to finish recalculating viewport + // Give iOS a moment to recalc innerHeight setTimeout( setViewportHeight, 200 ); } ); } else { - // FALLBACK: Legacy orientationchange event + // Fallback for browsers that don't support ScreenOrientation API window.addEventListener( 'orientationchange', () => { - // Delay slightly to allow iOS to finish recalculating viewport + // Give iOS a moment to recalc innerHeight setTimeout( setViewportHeight, 200 ); } ); } @@ -537,33 +448,19 @@ export default class ControlsManager { * @param {number} newHeight - New height value */ setupStandardControlLayout( playButton, skipButtons, newWidth, newHeight ) { - const skin = this.config.videoSetupOptions?.playerSkin; - - if ( skin === PLAYER_SKINS.MINIMAL ) { - // For Minimal skin, center the play button and skip buttons vertically and horizontally - if ( playButton ) { - // Use -15px offset for play button and -20px for skip buttons to align their centers - // The play button is typically smaller than the skip button containers - playButton.style.setProperty( 'bottom', `${ ( newHeight / 2 ) - 15 }px` ); - playButton.style.setProperty( 'left', `${ ( newWidth / 2 ) - 20 }px` ); - } - + if ( this.config.videoSetupOptions?.playerSkin === PLAYER_SKINS.MINIMAL ) { + playButton.style.setProperty( 'bottom', `${ ( newHeight / 2 ) + 4 }px` ); skipButtons.forEach( ( button ) => { - // Align skip buttons to the same vertical center as the play button - button.style.setProperty( 'bottom', `${ ( newHeight / 2 ) - 20 }px` ); + button.style.setProperty( 'bottom', `${ ( newHeight / 2 ) - 5 }px` ); } ); - } else if ( skin === PLAYER_SKINS.DEFAULT ) { - // For Default skin, ONLY center the skip buttons. Play button stays at bottom left (CSS). - skipButtons.forEach( ( button ) => { - button.style.setProperty( 'bottom', `${ ( newHeight / 2 ) - 20 }px` ); - } ); - } else { - // For other non-default skins - if ( playButton ) { - playButton.style.setProperty( 'bottom', `${ newHeight / 2 }px` ); - playButton.style.setProperty( 'left', `${ ( newWidth / 2 ) - 20 }px` ); - } + } + + if ( this.config.videoSetupOptions?.playerSkin !== PLAYER_SKINS.DEFAULT ) { + playButton.style.setProperty( 'bottom', `${ newHeight / 2 }px` ); + playButton.style.setProperty( 'left', `${ ( newWidth / 2 ) - 20 }px` ); + } + if ( this.config.videoSetupOptions?.playerSkin !== PLAYER_SKINS.MINIMAL ) { skipButtons.forEach( ( button ) => { button.style.setProperty( 'bottom', `${ newHeight / 2 }px` ); } ); diff --git a/assets/src/js/godam-player/managers/eventsManager.js b/assets/src/js/godam-player/managers/eventsManager.js index 2d83c2c97..e7068a3bd 100644 --- a/assets/src/js/godam-player/managers/eventsManager.js +++ b/assets/src/js/godam-player/managers/eventsManager.js @@ -54,7 +54,6 @@ export default class EventsManager { this.onPlayerConfigurationSetup = callbacks.onPlayerConfigurationSetup; this.onTimeUpdate = callbacks.onTimeUpdate; this.onFullscreenChange = callbacks.onFullscreenChange; - this.onVideoResize = callbacks.onVideoResize; this.onPlay = callbacks.onPlay; this.onControlsMove = callbacks.onControlsMove; } @@ -104,17 +103,12 @@ export default class EventsManager { this.player.on( 'resize', () => this.handleVideoResize() ); window.addEventListener( 'resize', () => this.handleVideoResize() ); this.player.on( 'fullscreenchange', () => this.handleVideoResize() ); - this.player.on( 'customfullscreenchange', () => this.handleVideoResize() ); } /** * Handle video resize events */ handleVideoResize() { - if ( this.onVideoResize ) { - this.onVideoResize(); - } - // Skip if video is fullscreen or classic skin if ( ! this.player || typeof this.player.isFullscreen !== 'function' || @@ -125,7 +119,7 @@ export default class EventsManager { // Handle control bar positioning during fullscreen this.handleFullscreenControlBar(); - // Check container width constraint for other skins + // Check container width constraint const videoContainer = this.video.closest( '.easydam-video-container' ); if ( videoContainer?.offsetWidth > 480 ) { return; diff --git a/assets/src/js/godam-player/managers/layers/formLayerManager.js b/assets/src/js/godam-player/managers/layers/formLayerManager.js index 4fb170f4f..eb3251a74 100644 --- a/assets/src/js/godam-player/managers/layers/formLayerManager.js +++ b/assets/src/js/godam-player/managers/layers/formLayerManager.js @@ -93,17 +93,6 @@ export default class FormLayerManager { layerObj.layerElement.appendChild( skipButton ); } - /** - * Create arrow icon for skip button - * - * @return {HTMLElement} Created arrow icon element - */ - createArrowIcon() { - const arrowIcon = document.createElement( 'span' ); - arrowIcon.innerHTML = ``; - return arrowIcon; - } - /** * Create skip button * @@ -114,7 +103,10 @@ export default class FormLayerManager { const skipButton = document.createElement( 'button' ); skipButton.textContent = skipText; skipButton.classList.add( 'skip-button' ); - skipButton.appendChild( this.createArrowIcon() ); + + const arrowIcon = document.createElement( 'span' ); + arrowIcon.innerHTML = ``; + skipButton.appendChild( arrowIcon ); return skipButton; } @@ -128,22 +120,7 @@ export default class FormLayerManager { setupFormObserver( layerObj, skipButton ) { const observer = new MutationObserver( () => { if ( this.hasConfirmationMessage( layerObj.layerElement ) ) { - // Update button text while preserving the arrow icon - let textNode = null; - for ( let i = 0; i < skipButton.childNodes.length; i++ ) { - if ( skipButton.childNodes[ i ].nodeType === Node.TEXT_NODE ) { - textNode = skipButton.childNodes[ i ]; - break; - } - } - - if ( textNode ) { - textNode.textContent = __( 'Continue', 'godam' ); - } else { - // Fallback: if no text node exists, update all content - skipButton.textContent = __( 'Continue', 'godam' ); - skipButton.appendChild( this.createArrowIcon() ); - } + skipButton.textContent = __( 'Continue', 'godam' ); skipButton.classList.remove( 'hidden' ); observer.disconnect(); } @@ -182,8 +159,8 @@ export default class FormLayerManager { // Special cases const wpPollsForm = element.querySelector( '.wp-polls-form' ); - const wpPollsResult = element.querySelector( '.wp-polls-ans' ); - if ( ! wpPollsForm && wpPollsResult ) { + const wpPollsAnswer = element.querySelector( '.wp-polls-answer' ); + if ( ! wpPollsForm && wpPollsAnswer ) { return true; } diff --git a/assets/src/js/godam-player/managers/layers/hotspotLayerManager.js b/assets/src/js/godam-player/managers/layers/hotspotLayerManager.js index 7880de4df..446ddbb20 100644 --- a/assets/src/js/godam-player/managers/layers/hotspotLayerManager.js +++ b/assets/src/js/godam-player/managers/layers/hotspotLayerManager.js @@ -3,18 +3,13 @@ */ import { __, sprintf } from '@wordpress/i18n'; -/** - * Internal dependencies - */ -import { HOTSPOT_CONSTANTS } from '../../utils/constants'; - /** * Hotspot Layer Manager * Handles hotspot layer functionality including creation, positioning, and interaction */ export default class HotspotLayerManager { - static BASE_WIDTH = HOTSPOT_CONSTANTS.BASE_WIDTH; - static BASE_HEIGHT = HOTSPOT_CONSTANTS.BASE_HEIGHT; + static BASE_WIDTH = 800; + static BASE_HEIGHT = 600; constructor( player, isDisplayingLayers, currentPlayerVideoInstanceId ) { this.player = player; @@ -94,17 +89,7 @@ export default class HotspotLayerManager { } } ); - // Use requestAnimationFrame to wait for layout to stabilize after fullscreen resize - let framesToWait = 2; - const waitForResize = () => { - if ( framesToWait > 0 ) { - framesToWait--; - window.requestAnimationFrame( waitForResize ); - } else { - this.updateHotspotPositions(); - } - }; - window.requestAnimationFrame( waitForResize ); + this.updateHotspotPositions(); } /** @@ -138,64 +123,6 @@ export default class HotspotLayerManager { } ); } - /** - * Compute content rectangle - * - * @return {Object|null} Content rectangle {left, top, width, height} or null - */ - computeContentRect() { - const videoEl = this.player.tech( true )?.el() || this.player.el().querySelector( 'video' ); - const containerEl = this.player.el(); - - if ( ! videoEl || ! containerEl ) { - return null; - } - - const nativeW = videoEl.videoWidth || this.player.videoWidth() || 0; - const nativeH = videoEl.videoHeight || this.player.videoHeight() || 0; - - const elW = containerEl.offsetWidth; - const elH = containerEl.offsetHeight; - - // If video dimensions aren't loaded yet, use full container - if ( ! nativeW || ! nativeH ) { - return { - left: 0, - top: 0, - width: elW, - height: elH, - }; - } - - const videoAspectRatio = nativeW / nativeH; - const containerAspectRatio = elW / elH; - - let contentW, contentH, offsetX, offsetY; - - if ( containerAspectRatio > videoAspectRatio ) { - // Pillarboxed (black bars on left/right) - contentH = elH; - contentW = elH * videoAspectRatio; - offsetX = ( elW - contentW ) / 2; - offsetY = 0; - } else { - // Letterboxed (black bars on top/bottom) - contentW = elW; - contentH = elW / videoAspectRatio; - offsetX = 0; - offsetY = ( elH - contentH ) / 2; - } - - const result = { - left: Math.round( offsetX ), - top: Math.round( offsetY ), - width: Math.round( contentW ), - height: Math.round( contentH ), - }; - - return result; - } - /** * Create hotspot element * @@ -212,39 +139,18 @@ export default class HotspotLayerManager { hotspotDiv.classList.add( 'hotspot', 'circle' ); hotspotDiv.style.position = 'absolute'; - const contentRect = this.computeContentRect(); - // Positioning const fallbackPosX = hotspot.oPosition?.x ?? hotspot.position.x; const fallbackPosY = hotspot.oPosition?.y ?? hotspot.position.y; - - let fallbackDiameter = hotspot.oSize?.diameter ?? hotspot.size?.diameter; - if ( ! fallbackDiameter ) { - if ( hotspot.unit === 'percent' && contentRect ) { - fallbackDiameter = ( HOTSPOT_CONSTANTS.DEFAULT_DIAMETER_PX / contentRect.width ) * 100; - } else { - fallbackDiameter = hotspot.unit === 'percent' ? HOTSPOT_CONSTANTS.DEFAULT_DIAMETER_PERCENT : HOTSPOT_CONSTANTS.DEFAULT_DIAMETER_PX; - } - } - - let pixelX, pixelY, pixelDiameter; - - if ( hotspot.unit === 'percent' && contentRect ) { - // New percentage-based positioning - pixelX = contentRect.left + ( ( fallbackPosX / 100 ) * contentRect.width ); - pixelY = contentRect.top + ( ( fallbackPosY / 100 ) * contentRect.height ); - pixelDiameter = ( fallbackDiameter / 100 ) * contentRect.width; - } else { - // Legacy pixel-based positioning (relative to 800x600) - // We now map these to the contentRect instead of the full container to avoid black bars - const effectiveRect = contentRect || { left: 0, top: 0, width: containerWidth, height: containerHeight }; - pixelX = effectiveRect.left + ( ( fallbackPosX / baseWidth ) * effectiveRect.width ); - pixelY = effectiveRect.top + ( ( fallbackPosY / baseHeight ) * effectiveRect.height ); - pixelDiameter = ( fallbackDiameter / baseWidth ) * effectiveRect.width; - } + const pixelX = ( fallbackPosX / baseWidth ) * containerWidth; + const pixelY = ( fallbackPosY / baseHeight ) * containerHeight; hotspotDiv.style.left = `${ pixelX }px`; hotspotDiv.style.top = `${ pixelY }px`; + + // Sizing + const fallbackDiameter = hotspot.oSize?.diameter ?? hotspot.size?.diameter ?? 48; + const pixelDiameter = ( fallbackDiameter / baseWidth ) * containerWidth; hotspotDiv.style.width = `${ pixelDiameter }px`; hotspotDiv.style.height = `${ pixelDiameter }px`; @@ -351,100 +257,57 @@ export default class HotspotLayerManager { } /** - * Position tooltip relative to hotspot, constrained within the video container + * Position tooltip relative to hotspot * * @param {HTMLElement} hotspotDiv - The hotspot element for positioning reference * @param {HTMLElement} tooltipDiv - The tooltip element to position */ positionTooltip( hotspotDiv, tooltipDiv ) { - const videoContainer = this.player.el(); - const containerRect = videoContainer.getBoundingClientRect(); const hotspotRect = hotspotDiv.getBoundingClientRect(); - - // Temporarily make tooltip visible to measure it accurately - const originalVisibility = tooltipDiv.style.visibility; - const originalOpacity = tooltipDiv.style.opacity; - tooltipDiv.style.visibility = 'hidden'; - tooltipDiv.style.opacity = '0'; - tooltipDiv.style.display = 'block'; - const tooltipRect = tooltipDiv.getBoundingClientRect(); + const viewportWidth = window.innerWidth; - // Restore original styles - tooltipDiv.style.visibility = originalVisibility; - tooltipDiv.style.opacity = originalOpacity; - - // Calculate space relative to video container (not viewport) - const spaceAbove = hotspotRect.top - containerRect.top; - const spaceBelow = containerRect.bottom - hotspotRect.bottom; - - const tooltipHeight = tooltipRect.height; - const tooltipWidth = tooltipRect.width; - - // Minimum padding from container edges - const edgePadding = 8; - - // Reset all positioning classes and styles first - tooltipDiv.classList.remove( 'tooltip-top', 'tooltip-bottom', 'tooltip-left', 'tooltip-right', 'no-arrow' ); - tooltipDiv.style.top = ''; - tooltipDiv.style.bottom = ''; - tooltipDiv.style.left = ''; - tooltipDiv.style.right = ''; - tooltipDiv.style.transform = ''; - - // Vertical positioning - prefer above, fallback to below - if ( spaceAbove >= tooltipHeight + edgePadding ) { - // Place above - tooltipDiv.style.bottom = '100%'; - tooltipDiv.style.top = 'auto'; - tooltipDiv.classList.add( 'tooltip-top' ); - } else if ( spaceBelow >= tooltipHeight + edgePadding ) { + // Vertical positioning + const spaceAbove = hotspotRect.top; + if ( spaceAbove < tooltipRect.height + 10 ) { // Place below tooltipDiv.style.bottom = 'auto'; tooltipDiv.style.top = '100%'; tooltipDiv.classList.add( 'tooltip-bottom' ); - } else if ( spaceAbove >= spaceBelow ) { + tooltipDiv.classList.remove( 'tooltip-top' ); + } else { + // Place above tooltipDiv.style.bottom = '100%'; tooltipDiv.style.top = 'auto'; tooltipDiv.classList.add( 'tooltip-top' ); - } else { - tooltipDiv.style.bottom = 'auto'; - tooltipDiv.style.top = '100%'; - tooltipDiv.classList.add( 'tooltip-bottom' ); + tooltipDiv.classList.remove( 'tooltip-bottom' ); } - // Horizontal positioning - calculate where tooltip would overflow - const hotspotCenterInContainer = ( hotspotRect.left + ( hotspotRect.width / 2 ) ) - containerRect.left; - const tooltipHalfWidth = tooltipWidth / 2; + // Horizontal positioning + const spaceLeft = hotspotRect.left; + const spaceRight = viewportWidth - hotspotRect.right; - // Check if centered tooltip would overflow left or right of container - const wouldOverflowLeft = ( hotspotCenterInContainer - tooltipHalfWidth ) < edgePadding; - const wouldOverflowRight = ( hotspotCenterInContainer + tooltipHalfWidth ) > ( containerRect.width - edgePadding ); - - if ( wouldOverflowLeft && wouldOverflowRight ) { - // Tooltip is wider than available space, center it as best as possible - tooltipDiv.style.left = '50%'; - tooltipDiv.style.transform = 'translateX(-50%)'; - tooltipDiv.classList.add( 'no-arrow' ); - } else if ( wouldOverflowLeft ) { - // Align tooltip to the left edge of hotspot, but ensure it stays within container - const leftOffset = Math.max( edgePadding - ( hotspotRect.left - containerRect.left ), 0 ); - tooltipDiv.style.left = `${ leftOffset }px`; - tooltipDiv.style.right = 'auto'; + if ( spaceLeft < 10 ) { + // Adjust to the right + tooltipDiv.style.left = '0'; tooltipDiv.style.transform = 'translateX(0)'; - tooltipDiv.classList.add( 'tooltip-left', 'no-arrow' ); - } else if ( wouldOverflowRight ) { - // Align tooltip to the right edge of hotspot, but ensure it stays within container - const rightOffset = Math.max( edgePadding - ( containerRect.right - hotspotRect.right ), 0 ); + tooltipDiv.classList.add( 'tooltip-left' ); + tooltipDiv.classList.remove( 'tooltip-right' ); + tooltipDiv.classList.add( 'no-arrow' ); + } else if ( spaceRight < 10 ) { + // Adjust to the left tooltipDiv.style.left = 'auto'; - tooltipDiv.style.right = `${ rightOffset }px`; + tooltipDiv.style.right = '0'; tooltipDiv.style.transform = 'translateX(0)'; - tooltipDiv.classList.add( 'tooltip-right', 'no-arrow' ); + tooltipDiv.classList.add( 'tooltip-right' ); + tooltipDiv.classList.remove( 'tooltip-left' ); + tooltipDiv.classList.add( 'no-arrow' ); } else { - // Centered horizontally - tooltip fits within container + // Centered horizontally tooltipDiv.style.left = '50%'; tooltipDiv.style.right = 'auto'; tooltipDiv.style.transform = 'translateX(-50%)'; + tooltipDiv.classList.remove( 'tooltip-left', 'tooltip-right', 'no-arrow' ); } } @@ -459,8 +322,6 @@ export default class HotspotLayerManager { const baseWidth = HotspotLayerManager.BASE_WIDTH; const baseHeight = HotspotLayerManager.BASE_HEIGHT; - const contentRect = this.computeContentRect(); - this.hotspotLayers.forEach( ( layerObj ) => { const hotspotDivs = layerObj.layerElement.querySelectorAll( '.hotspot' ); hotspotDivs.forEach( ( hotspotDiv, index ) => { @@ -469,26 +330,14 @@ export default class HotspotLayerManager { // Recalc position const fallbackPosX = hotspot.oPosition?.x ?? hotspot.position.x; const fallbackPosY = hotspot.oPosition?.y ?? hotspot.position.y; - const fallbackDiameter = hotspot.oSize?.diameter ?? hotspot.size?.diameter ?? 48; - - let pixelX, pixelY, pixelDiameter; - - if ( hotspot.unit === 'percent' && contentRect ) { - // New percentage-based positioning - pixelX = contentRect.left + ( ( fallbackPosX / 100 ) * contentRect.width ); - pixelY = contentRect.top + ( ( fallbackPosY / 100 ) * contentRect.height ); - pixelDiameter = ( fallbackDiameter / 100 ) * contentRect.width; - } else { - // Legacy pixel-based positioning - // We now map these to the contentRect instead of the full container to avoid black bars - const effectiveRect = contentRect || { left: 0, top: 0, width: containerWidth, height: containerHeight }; - pixelX = effectiveRect.left + ( ( fallbackPosX / baseWidth ) * effectiveRect.width ); - pixelY = effectiveRect.top + ( ( fallbackPosY / baseHeight ) * effectiveRect.height ); - pixelDiameter = ( fallbackDiameter / baseWidth ) * effectiveRect.width; - } - + const pixelX = ( fallbackPosX / baseWidth ) * containerWidth; + const pixelY = ( fallbackPosY / baseHeight ) * containerHeight; hotspotDiv.style.left = `${ pixelX }px`; hotspotDiv.style.top = `${ pixelY }px`; + + // Recalc size + const fallbackDiameter = hotspot.oSize?.diameter ?? hotspot.size?.diameter ?? 48; + const pixelDiameter = ( fallbackDiameter / baseWidth ) * containerWidth; hotspotDiv.style.width = `${ pixelDiameter }px`; hotspotDiv.style.height = `${ pixelDiameter }px`; diff --git a/assets/src/js/godam-player/managers/layersManager.js b/assets/src/js/godam-player/managers/layersManager.js index e184e83c3..64a2dd432 100644 --- a/assets/src/js/godam-player/managers/layersManager.js +++ b/assets/src/js/godam-player/managers/layersManager.js @@ -122,13 +122,6 @@ export default class LayersManager { this.hotspotLayerManager.handleHotspotLayersTimeUpdate( currentTime ); } - /** - * Handle video resize events - */ - handleVideoResize() { - this.hotspotLayerManager.updateHotspotPositions(); - } - /** * Handle fullscreen changes for layers */ diff --git a/assets/src/js/godam-player/managers/menuButtonHover.js b/assets/src/js/godam-player/managers/menuButtonHover.js index 0cbd35f6c..bbc421da0 100644 --- a/assets/src/js/godam-player/managers/menuButtonHover.js +++ b/assets/src/js/godam-player/managers/menuButtonHover.js @@ -9,7 +9,7 @@ class MenuButtonHoverManager { this.player = player; // Add more menu button component names as needed - this.menuButtons = [ 'SubsCapsButton', 'SettingsButton', 'CaptionsButton', 'SubtitlesButton' ]; + this.menuButtons = [ 'SubsCapsButton', 'SettingsButton' ]; this.init(); } @@ -17,24 +17,12 @@ class MenuButtonHoverManager { init() { this.menuButtons.forEach( ( buttonName ) => { const button = this.player.controlBar?.getChild( buttonName ); - let btnEl = button?.el(); - // Fallback to DOM search if component not found via getChild (common in Safari/iOS) - if ( ! btnEl ) { - const classMap = { - SubsCapsButton: '.vjs-subs-caps-button', - SettingsButton: '.vjs-settings-button', - CaptionsButton: '.vjs-captions-button', - SubtitlesButton: '.vjs-subtitles-button', - }; - - const className = classMap[ buttonName ] || null; - - if ( className ) { - btnEl = this.player.el().querySelector( className ); - } + if ( ! button ) { + return; } + const btnEl = button.el(); if ( ! btnEl ) { return; } @@ -100,15 +88,12 @@ class MenuButtonHoverManager { if ( ! isClickedOpen() ) { menuEl.style.display = 'block'; menuEl.classList.add( 'vjs-lock-showing' ); - // Add vjs-hover class to button to maintain active state in Safari - btnEl.classList.add( 'vjs-hover' ); this.closeOtherMenus( menuEl ); } } ); btnEl.addEventListener( 'mouseleave', () => { overBtn = false; - btnEl.classList.remove( 'vjs-hover' ); setTimeout( update, 500 ); // small delay to allow moving between } ); @@ -117,15 +102,12 @@ class MenuButtonHoverManager { if ( ! isClickedOpen() ) { menuEl.style.display = 'block'; menuEl.classList.add( 'vjs-lock-showing' ); - // Keep button in hover state while interacting with menu - btnEl.classList.add( 'vjs-hover' ); this.closeOtherMenus( menuEl ); } } ); menuEl.addEventListener( 'mouseleave', () => { overMenu = false; - btnEl.classList.remove( 'vjs-hover' ); setTimeout( update, 500 ); } ); diff --git a/assets/src/js/godam-player/managers/playerManager.js b/assets/src/js/godam-player/managers/playerManager.js index e7761a653..92f97f723 100644 --- a/assets/src/js/godam-player/managers/playerManager.js +++ b/assets/src/js/godam-player/managers/playerManager.js @@ -385,10 +385,10 @@ export default class PlayerManager { togglePlayPause( player ) { if ( player.paused() ) { player.play(); - this.showIndicator( player.el(), 'play-indicator', '' ); + this.showIndicator( player.el(), 'play-indicator', '' ); } else { player.pause(); - this.showIndicator( player.el(), 'pause-indicator', '' ); + this.showIndicator( player.el(), 'pause-indicator', '' ); } } diff --git a/assets/src/js/godam-player/utils/constants.js b/assets/src/js/godam-player/utils/constants.js index dc380b80a..2d8a5b9f3 100644 --- a/assets/src/js/godam-player/utils/constants.js +++ b/assets/src/js/godam-player/utils/constants.js @@ -26,18 +26,6 @@ export const FORM_TYPES = { METFORM: 'metform', }; -/** - * Hotspot constants. - */ -export const HOTSPOT_CONSTANTS = { - DEFAULT_DIAMETER_PERCENT: 15, - DEFAULT_DIAMETER_PX: 48, - MIN_PX: 10, - MIN_PERCENT_FALLBACK: 5, - BASE_WIDTH: 800, - BASE_HEIGHT: 600, -}; - /** * Layer types. */ diff --git a/assets/src/js/godam-player/videoPlayer.js b/assets/src/js/godam-player/videoPlayer.js index 5f42e59db..1b848ca86 100644 --- a/assets/src/js/godam-player/videoPlayer.js +++ b/assets/src/js/godam-player/videoPlayer.js @@ -149,13 +149,7 @@ export default class GodamVideoPlayer { if ( isInModal ) { const aspectRatio = window.innerWidth < 420 ? '9:16' : '16:9'; - // Check if aspect ratio is valid x:y format - if ( ! /^\d+:\d+$/.test( aspectRatio ) ) { - // eslint-disable-next-line no-console - console.warn( `Invalid aspect ratio format: "${ aspectRatio }". Falling back to "16:9".` ); - } else { - this.player.aspectRatio( aspectRatio ); - } + this.player.aspectRatio( aspectRatio ); } } @@ -195,13 +189,7 @@ export default class GodamVideoPlayer { if ( ! isInModal ) { const aspectRatio = this.configManager.videoSetupOptions?.aspectRatio || '16:9'; - // Check if aspect ratio is valid x:y format - if ( ! /^\d+:\d+$/.test( aspectRatio ) ) { - // eslint-disable-next-line no-console - console.warn( `Invalid aspect ratio format: "${ aspectRatio }". Falling back to "16:9".` ); - } else { - this.player.aspectRatio( aspectRatio ); - } + this.player.aspectRatio( aspectRatio ); } } @@ -251,7 +239,6 @@ export default class GodamVideoPlayer { onPlayerConfigurationSetup: () => this.controlsManager.setupPlayerConfiguration(), onTimeUpdate: ( currentTime ) => this.handleTimeUpdate( currentTime ), onFullscreenChange: () => this.layersManager.handleFullscreenChange(), - onVideoResize: () => this.layersManager.handleVideoResize(), onPlay: () => this.layersManager.handlePlay(), onControlsMove: () => this.controlsManager.moveVideoControls(), } ); diff --git a/assets/src/js/godam-video-embed.js b/assets/src/js/godam-video-embed.js deleted file mode 100644 index a38b4aaf8..000000000 --- a/assets/src/js/godam-video-embed.js +++ /dev/null @@ -1,390 +0,0 @@ -/** - * GoDAM Video Embed Script - * - * Listens for scroll events on the video embed page and sends postMessage - * to the parent window to trigger video navigation. - * - * @since 1.5.0 - */ - -document.addEventListener( 'DOMContentLoaded', function() { - // Only run if we're in an iframe - if ( window.self === window.top ) { - return; - } - - // Scroll functionality with cooldown and threshold - const SCROLL_COOLDOWN = 1000; // milliseconds - const SCROLL_THRESHOLD = 280; // Minimum scroll distance to trigger - let lastScrollTime = 0; - let scrollTimeout; - let accumulatedDelta = 0; - - // Helper function to check if an element is scrollable - const isElementScrollable = ( element ) => { - if ( ! element || element === document.body || element === document.documentElement ) { - return false; - } - - const style = window.getComputedStyle( element ); - const overflowY = style.overflowY; - const overflowX = style.overflowX; - const isScrollable = ( overflowY === 'auto' || overflowY === 'scroll' ) || ( overflowX === 'auto' || overflowX === 'scroll' ); - - if ( ! isScrollable ) { - return false; - } - - // Check if element can actually scroll - const canScrollVertically = element.scrollHeight > element.clientHeight; - const canScrollHorizontally = element.scrollWidth > element.clientWidth; - - return canScrollVertically || canScrollHorizontally; - }; - - // Helper function to find the scrollable parent of an element - const findScrollableParent = ( element ) => { - let current = element; - while ( current && current !== document.body && current !== document.documentElement ) { - if ( isElementScrollable( current ) ) { - return current; - } - current = current.parentElement; - } - return null; - }; - - // Helper function to check if element can scroll in the given direction - const canScrollInDirection = ( element, deltaY ) => { - if ( ! element ) { - return false; - } - - if ( deltaY > 0 ) { - // Scrolling down - check if can scroll down - return element.scrollTop < element.scrollHeight - element.clientHeight; - } - // Scrolling up - check if can scroll up - return element.scrollTop > 0; - }; - - // Helper function to check if easydam-layer is visible (excluding hotspot-layer) - const isGoDAMLayerVisible = () => { - const layers = document.querySelectorAll( '.easydam-layer' ); - for ( const layer of layers ) { - // Skip hotspot-layer - if ( layer.classList.contains( 'hotspot-layer' ) ) { - continue; - } - // Check if layer is visible - const style = window.getComputedStyle( layer ); - if ( style.display !== 'none' && style.visibility !== 'hidden' ) { - return true; - } - } - return false; - }; - - // Helper function to check if any player is in fullscreen mode - const isAnyPlayerFullscreen = () => { - // Check if GoDAM API is available - if ( ! window.GoDAMAPI ) { - return false; - } - - try { - const players = window.GoDAMAPI.getAllPlayers(); - for ( const playerObj of players ) { - if ( playerObj.player && playerObj.player.isFullscreen && playerObj.player.isFullscreen() ) { - return true; - } - } - } catch ( error ) { - // If API call fails, assume not fullscreen - return false; - } - - return false; - }; - - // Handle wheel scroll events - const handleScroll = ( event ) => { - // Find the target element and check if it's within a scrollable container - const target = event.target; - const scrollableParent = findScrollableParent( target ); - - // If we're scrolling within a scrollable element, check if it can scroll further - if ( scrollableParent ) { - const canScroll = canScrollInDirection( scrollableParent, event.deltaY ); - // If the scrollable element can still scroll, don't trigger navigation - if ( canScroll ) { - return; - } - // If the scrollable element is at its limit, prevent default and allow navigation - event.preventDefault(); - event.stopPropagation(); - } else { - // Not in a scrollable container, prevent default and allow navigation - event.preventDefault(); - event.stopPropagation(); - } - - // Accumulate scroll delta - accumulatedDelta += event.deltaY; - - // Clear any existing timeout - clearTimeout( scrollTimeout ); - - // Set a new timeout for debouncing - scrollTimeout = setTimeout( () => { - // Check if accumulated scroll meets threshold - if ( Math.abs( accumulatedDelta ) < SCROLL_THRESHOLD ) { - accumulatedDelta = 0; - return; - } - - const currentTime = Date.now(); - if ( currentTime - lastScrollTime < SCROLL_COOLDOWN ) { - accumulatedDelta = 0; - return; - } - - // Don't send scroll event if easydam-layer is visible (excluding hotspot-layer) - if ( isGoDAMLayerVisible() ) { - accumulatedDelta = 0; - return; - } - - // Don't send scroll event if any player is in fullscreen mode - if ( isAnyPlayerFullscreen() ) { - accumulatedDelta = 0; - return; - } - - // Determine scroll direction - if ( accumulatedDelta > 0 ) { - // Scrolling down (next video) - window.parent.postMessage( { type: 'godamScrollNext' }, window.location.origin ); - } else { - // Scrolling up (previous video) - window.parent.postMessage( { type: 'godamScrollPrevious' }, window.location.origin ); - } - - lastScrollTime = currentTime; - accumulatedDelta = 0; - }, 150 ); // Debounce delay - }; - - // Touch functionality for mobile - let touchStartY = 0; - let touchEndY = 0; - let touchStartElement = null; - - const handleTouchStart = ( event ) => { - touchStartY = event.touches[ 0 ].clientY; - touchStartElement = event.target; - }; - - const handleTouchMove = ( event ) => { - // Find the target element and check if it's within a scrollable container - const target = event.target; - const scrollableParent = findScrollableParent( target ); - - // If we're touching within a scrollable element, allow normal scrolling - if ( scrollableParent ) { - // Check if the scrollable element can scroll - const canScrollVertically = scrollableParent.scrollHeight > scrollableParent.clientHeight; - if ( canScrollVertically ) { - // Allow the scrollable element to handle the scroll - return; - } - } - - // Not in a scrollable container or container can't scroll, prevent default - event.preventDefault(); // Prevent background scroll - event.stopPropagation(); // Prevent event bubbling - }; - - const handleTouchEnd = ( event ) => { - touchEndY = event.changedTouches[ 0 ].clientY; - const touchDiff = touchStartY - touchEndY; - - // Check if the touch started within a scrollable container - if ( touchStartElement ) { - const scrollableParent = findScrollableParent( touchStartElement ); - if ( scrollableParent ) { - // Check if the scrollable element can scroll in the swipe direction - const canScrollVertically = scrollableParent.scrollHeight > scrollableParent.clientHeight; - if ( canScrollVertically ) { - // Check if element can still scroll in the swipe direction - const canScroll = canScrollInDirection( scrollableParent, touchDiff ); - if ( canScroll ) { - // The scrollable element can still scroll, don't trigger navigation - return; - } - } - } - } - - // Minimum swipe distance - if ( Math.abs( touchDiff ) < SCROLL_THRESHOLD ) { - return; - } - - // Clear any existing timeout - clearTimeout( scrollTimeout ); - - // Set a new timeout for debouncing - scrollTimeout = setTimeout( () => { - const currentTime = Date.now(); - if ( currentTime - lastScrollTime < SCROLL_COOLDOWN ) { - return; - } - - // Don't send scroll event if easydam-layer is visible (excluding hotspot-layer) - if ( isGoDAMLayerVisible() ) { - return; - } - - // Don't send scroll event if any player is in fullscreen mode - if ( isAnyPlayerFullscreen() ) { - return; - } - - // Determine swipe direction - if ( touchDiff > 0 ) { - // Swiping up (next video) - window.parent.postMessage( { type: 'godamScrollNext' }, window.location.origin ); - } else { - // Swiping down (previous video) - window.parent.postMessage( { type: 'godamScrollPrevious' }, window.location.origin ); - } - - lastScrollTime = currentTime; - }, 150 ); // Debounce delay - }; - - // Add scroll listener - document.addEventListener( 'wheel', handleScroll, { passive: false } ); - - // Add touch listeners - document.body.addEventListener( 'touchstart', handleTouchStart, { passive: true } ); - document.body.addEventListener( 'touchmove', handleTouchMove, { passive: false } ); - document.body.addEventListener( 'touchend', handleTouchEnd, { passive: false } ); -} ); - -// Listen for GoDAM Player ready event to set up fullscreen change listener -document.addEventListener( 'godamAllPlayersReady', () => { - // Check if GoDAM API is available - if ( ! window.GoDAMAPI ) { - return; - } - - // Get all ready players - const players = window.GoDAMAPI.getAllPlayers(); - - // Listen for fullscreen changes on all players - players.forEach( ( playerObj ) => { - if ( playerObj.player ) { - // Listen for fullscreen change event - playerObj.player.on( 'fullscreenchange', ( data ) => { - // Send postMessage to parent window - window.parent.postMessage( { - type: 'godamFullscreenChange', - isFullscreen: data.isFullscreen, - attachmentId: playerObj.attachmentId, - }, window.location.origin ); - } ); - } - } ); -} ); - -/** - * Render CommentBox component on video embed page by default. - * - * @since 1.5.0 - */ -document.addEventListener( 'DOMContentLoaded', function() { - const urlParams = new URLSearchParams( window.location.search ); - const videoId = urlParams.get( 'id' ); - - if ( ! videoId ) { - return; - } - - const videoIdNum = parseInt( videoId, 10 ); - if ( ! videoIdNum || isNaN( videoIdNum ) ) { - return; - } - - // Create and show loading overlay - const loadingOverlay = document.createElement( 'div' ); - loadingOverlay.className = 'godam-video-embed-loading'; - loadingOverlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #fff; z-index: 9999; display: flex; align-items: center; justify-content: center;'; - - // Create spinner element - const spinner = document.createElement( 'div' ); - spinner.className = 'godam-video-embed-spinner'; - loadingOverlay.appendChild( spinner ); - - document.body.appendChild( loadingOverlay ); - - // Function to hide loading overlay - const hideLoadingOverlay = () => { - if ( loadingOverlay && loadingOverlay.parentNode ) { - loadingOverlay.style.opacity = '0'; - loadingOverlay.style.pointerEvents = 'none'; - loadingOverlay.style.transition = 'opacity 0.3s ease-out'; - setTimeout( () => { - loadingOverlay.remove(); - }, 300 ); - } - }; - - // Listen for the engagement store to be initialized. - document.addEventListener( 'godamEngagementStoreInitialized', renderCommentBox ); - - // Render the comment box. - function renderCommentBox() { - // Check WordPress dependencies - if ( typeof wp === 'undefined' || ! wp.data || ! wp.element?.createRoot ) { - return; - } - - const storeName = 'godam-video-engagement'; - const select = wp.data.select( storeName ); - const dispatch = wp.data.dispatch( storeName ); - - if ( ! select || ! dispatch ) { - return; - } - - // Find video element and get instance ID - const videoElement = document.querySelector( `.easydam-player.video-js[data-id="${ videoIdNum }"]` ); - const videoInstanceId = videoElement?.getAttribute( 'data-instance-id' ); - - if ( ! videoInstanceId ) { - return; - } - - // Get site URL - const siteUrl = window.location.origin; - - // Determine if engagements should be shown - const embedElement = document.querySelector( '.godam-video-embed' ); - const skipEngagements = embedElement?.getAttribute( 'data-show-engagements' ) !== 'true'; - - // Dispatch action to initiate comment modal - dispatch.initiateCommentModal( - videoIdNum.toString(), - siteUrl, - `engagement-${ videoInstanceId }`, - skipEngagements, - true, - ); - - // Hide loading overlay after modal is initiated - hideLoadingOverlay(); - } -} ); - diff --git a/assets/src/js/media-library/views/attachment-details.js b/assets/src/js/media-library/views/attachment-details.js index 969ad8cc4..0a124a9d1 100644 --- a/assets/src/js/media-library/views/attachment-details.js +++ b/assets/src/js/media-library/views/attachment-details.js @@ -112,23 +112,12 @@ export default AttachmentDetails?.extend( { render() { AttachmentDetails.prototype.render.apply( this, arguments ); - const mime = this.model.get( 'mime' ); - - if ( mime && ! mime.startsWith( 'video/' ) ) { - return this; - } const hlsUrl = this.model.get( 'hls_url' ); - const mpdUrl = this.model.get( 'mpd_url' ); - const id = this.model.get( 'id' ); - const godamAPIBase = window?.godamRestRoute?.apiBase; - let oEmbeddedVideoUrl = null; - if ( godamAPIBase && id ) { - oEmbeddedVideoUrl = godamAPIBase + '/web/video/' + id; - } + const attachmentUrl = this.model.get( 'url' ); // Skip the local Media Library attachments. - if ( ( ! mpdUrl || ! isMpd( mpdUrl ) ) && ( ! hlsUrl || ! isM3U8( hlsUrl ) ) ) { + if ( ( ! attachmentUrl || ! isMpd( attachmentUrl ) ) && ( ! hlsUrl || ! isM3U8( hlsUrl ) ) ) { return this; } @@ -137,25 +126,13 @@ export default AttachmentDetails?.extend( { // No need to check if table exists, as if it did we would have returned early on link checks. const tableBody = createTable( this.el ); - if ( oEmbeddedVideoUrl ) { - tableBody.appendChild( - createAttachmentField( { - id: attachmentId, - fieldName: 'oembed_video_url', - fieldLabel: __( 'oEmbed Video URL', 'godam' ), - url: oEmbeddedVideoUrl, - helpText: __( 'The oEmbed URL can be used to embed the video in other platforms that support oEmbed.', 'godam' ), - } ), - ); - } - - if ( mpdUrl && isMpd( mpdUrl ) ) { + if ( attachmentUrl && isMpd( attachmentUrl ) ) { tableBody.appendChild( createAttachmentField( { id: attachmentId, fieldName: 'transcoded_url', fieldLabel: __( 'Transcoded CDN URL (MPD)', 'godam' ), - url: mpdUrl, + url: attachmentUrl, helpText: __( 'The URL of the transcoded file is generated automatically and cannot be edited.', 'godam' ), } ), ); diff --git a/assets/src/js/media-library/views/attachment.js b/assets/src/js/media-library/views/attachment.js index 0956ab4ee..bff52fe74 100644 --- a/assets/src/js/media-library/views/attachment.js +++ b/assets/src/js/media-library/views/attachment.js @@ -162,12 +162,7 @@ const Attachment = wp?.media?.view?.Attachment?.extend( { return this; } - const modelTypes = [ 'video', 'audio', 'image' ]; - - if ( isAPIKeyValid() && ( - modelTypes.includes( this.model.get( 'type' ) ) || - ( this.model.get( 'type' ) === 'application' && this.model.get( 'subtype' ) === 'pdf' ) - ) ) { + if ( isAPIKeyValid() && ( this.model.get( 'type' ) === 'video' || this.model.get( 'type' ) === 'audio' || this.model.get( 'type' ) === 'application' ) ) { // Get the transcoding status from the model const transcodingStatus = this.model.get( 'transcoding_status' ); const virtual = this.model.get( 'virtual' ); diff --git a/assets/src/js/media-library/views/filters/media-retranscode.js b/assets/src/js/media-library/views/filters/media-retranscode.js index 3553c9ff3..edc5b5029 100644 --- a/assets/src/js/media-library/views/filters/media-retranscode.js +++ b/assets/src/js/media-library/views/filters/media-retranscode.js @@ -28,7 +28,7 @@ MediaRetranscode = MediaRetranscode?.extend( { this.controller.on( 'select:activate select:deactivate', this.toggleButtonSelectClass, this ); - this.model.set( 'text', 'Transcode Media' ); + this.model.set( 'text', 'Retranscode Media' ); }, toggleButtonSelectClass() { diff --git a/assets/src/js/media-library/views/godam-media-frame-shared.js b/assets/src/js/media-library/views/godam-media-frame-shared.js index 048d7cf34..08315f7ca 100644 --- a/assets/src/js/media-library/views/godam-media-frame-shared.js +++ b/assets/src/js/media-library/views/godam-media-frame-shared.js @@ -98,8 +98,10 @@ const GoDAMMediaFrameShared = { this.content.set( RenderedContent ); // Attaches callback to create attachment entry in WordPress for GoDAM Video. - state.off( 'select', this.onGoDAMSelect, this ); - state.on( 'select', this.onGoDAMSelect, this ); + if ( 'video' === mimeTypes ) { + state.off( 'select', this.onGoDAMSelect, this ); + state.on( 'select', this.onGoDAMSelect, this ); + } }, onGoDAMSelect() { @@ -129,7 +131,7 @@ const GoDAMMediaFrameShared = { url: data.url, hls_url: data.hls_url, mpd_url: data.mpd_url, - mime: data.mime, + mime: 'video/mp4', type: data.type, subtype: data.subtype, status: data.status, @@ -140,7 +142,6 @@ const GoDAMMediaFrameShared = { owner: data.owner, label: data.label, icon: data.icon, - thumbnail_url: data.thumbnail_url, caption: data.caption, description: data.description, } ), diff --git a/assets/src/js/ninja-forms/ninja-forms-submissions-list.js b/assets/src/js/ninja-forms/ninja-forms-submissions-list.js deleted file mode 100644 index 8a1cd2865..000000000 --- a/assets/src/js/ninja-forms/ninja-forms-submissions-list.js +++ /dev/null @@ -1,55 +0,0 @@ -document.addEventListener( 'DOMContentLoaded', () => { - const processCells = () => { - const list = document.querySelector( '#the-list' ); - - if ( ! list ) { - return; - } - - list.querySelectorAll( '.nf-submissions-cell' ).forEach( ( cell ) => { - // Prevent re-processing - if ( cell && cell.dataset && cell.dataset.godamProcessed === '1' ) { - return; - } - - const text = cell.textContent.trim(); - - if ( ( text.startsWith( 'http://' ) || text.startsWith( 'https://' ) ) && text.includes( '/wp-content/uploads/' ) ) { - let url; - - try { - url = new URL( text, window.location.origin ); - } catch ( e ) { - // If the URL is invalid, do not transform the cell into a link. - return; - } - - // Only allow same-origin http(s) URLs. - if ( ( url.protocol === 'http:' || url.protocol === 'https:' ) && url.origin === window.location.origin ) { - const link = document.createElement( 'a' ); - link.href = url.href; - link.target = '_blank'; - link.rel = 'noopener noreferrer'; - link.textContent = 'View Recording'; - - cell.textContent = ''; - cell.appendChild( link ); - - cell.dataset.godamProcessed = '1'; - } - } - } ); - }; - - // Initial run - processCells(); - - const ninjaFormEntriesObserver = new MutationObserver( () => { - processCells(); - } ); - - ninjaFormEntriesObserver.observe( document.getElementById( 'nf-submissions-element' ), { - childList: true, - subtree: true, - } ); -} ); diff --git a/godam.php b/godam.php index d592b2632..21b5436c6 100644 --- a/godam.php +++ b/godam.php @@ -3,7 +3,7 @@ * Plugin Name: GoDAM * Plugin URI: https://godam.io * Description: Seamlessly manage and deliver your media assets directly from the cloud-based media management. Store assets efficiently, stream them via a CDN, and enhance your website's performance and user experience. Featuring adaptive bit rate streaming, adding interactive layers in videos, and taking full advantage of a digital asset management solution within WordPress. - * Version: 1.5.0 + * Version: 1.4.9 * Requires at least: 6.5 * Requires PHP: 7.4 * Text Domain: godam @@ -43,7 +43,7 @@ /** * The version of the plugin */ - define( 'RTGODAM_VERSION', '1.5.0' ); + define( 'RTGODAM_VERSION', '1.4.9' ); } if ( ! defined( 'RTGODAM_API_BASE' ) ) { diff --git a/inc/classes/assets/class-ima-assets.php b/inc/classes/assets/class-ima-assets.php index 9ff82575d..8057fbf0b 100644 --- a/inc/classes/assets/class-ima-assets.php +++ b/inc/classes/assets/class-ima-assets.php @@ -7,10 +7,6 @@ namespace RTGODAM\Inc\Assets; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/assets/class-jetpack-form-assets.php b/inc/classes/assets/class-jetpack-form-assets.php index 6142f237c..97dc9cd84 100644 --- a/inc/classes/assets/class-jetpack-form-assets.php +++ b/inc/classes/assets/class-jetpack-form-assets.php @@ -7,10 +7,6 @@ namespace RTGODAM\Inc\Assets; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/class-assets.php b/inc/classes/class-assets.php index 07fd2f4bc..9568b9eb4 100644 --- a/inc/classes/class-assets.php +++ b/inc/classes/class-assets.php @@ -36,7 +36,6 @@ protected function setup_hooks() { */ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); - add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); } /** @@ -282,41 +281,6 @@ public function admin_enqueue_scripts() { wp_enqueue_style( 'daterangepicker-css', RTGODAM_URL . 'assets/src/libs/daterangepicker.css', array(), filemtime( RTGODAM_PATH . 'assets/src/libs/daterangepicker.css' ) ); } - /** - * Enqueue block editor assets. - * - * @since 1.5.0 - * - * @return void - */ - public function enqueue_block_editor_assets() { - $block_extensions_asset_file = RTGODAM_PATH . 'assets/build/js/block-extensions.min.asset.php'; - - // Default dependencies if asset file doesn't exist yet. - $block_extensions_asset = array( - 'dependencies' => array( - 'wp-blocks', - 'wp-element', - 'wp-hooks', - 'wp-compose', - ), - 'version' => RTGODAM_VERSION, - ); - - // Check if asset file exists (generated by @wordpress/scripts). - if ( file_exists( $block_extensions_asset_file ) ) { - $block_extensions_asset = include $block_extensions_asset_file; - } - - wp_enqueue_script( - 'godam-block-extensions', - RTGODAM_URL . 'assets/build/js/block-extensions.min.js', - $block_extensions_asset['dependencies'], - $block_extensions_asset['version'], - true - ); - } - /** * Enqueue GoDAM Settings JS localization. * diff --git a/inc/classes/class-elementor-widgets.php b/inc/classes/class-elementor-widgets.php index 411fb2693..549fa12ad 100644 --- a/inc/classes/class-elementor-widgets.php +++ b/inc/classes/class-elementor-widgets.php @@ -7,10 +7,6 @@ namespace RTGODAM\Inc; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Elementor_Controls\Godam_Media; use RTGODAM\Inc\Elementor_Widgets\Godam_Audio; use RTGODAM\Inc\Elementor_Widgets\Godam_Gallery; diff --git a/inc/classes/class-media-library-ajax.php b/inc/classes/class-media-library-ajax.php index aa786ae3d..9a51121e1 100644 --- a/inc/classes/class-media-library-ajax.php +++ b/inc/classes/class-media-library-ajax.php @@ -47,22 +47,6 @@ public function setup_hooks() { add_action( 'rtgodam_handle_callback_finished', array( $this, 'download_transcoded_mp4_source' ), 10, 4 ); add_filter( 'wp_get_attachment_url', array( $this, 'filter_attachment_url_for_virtual_media' ), 10, 2 ); - - // Add filters for virtual media srcset support. - add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_virtual_media_srcset' ), 10, 5 ); - } - - /** - * Validate if a URL is valid. - * Ref: https://cmljnelson.blog/2018/08/31/url-validation-in-wordpress - * - * @since 1.5.0 - * - * @param string $url The URL to validate. - * @return bool True if valid, false otherwise. - */ - public function is_valid_url( $url ) { - return esc_url_raw( $url ) === $url; } /** @@ -106,18 +90,8 @@ public function prepare_godam_media_item( $item ) { 'mpd_url' => $item['transcoded_file_path'] ?? '', ); - // Set icon with fallback to default mime type icon for audio and PDF. $result['icon'] = $item['thumbnail_url'] ?? ''; - // If no thumbnail URL, use WordPress default icons for audio and PDF. - if ( empty( $result['icon'] ) ) { - if ( 'audio' === $item['job_type'] ) { - $result['icon'] = includes_url( 'images/media/audio.png' ); - } elseif ( 'application/pdf' === $item['mime_type'] ) { - $result['icon'] = includes_url( 'images/media/document.png' ); - } - } - if ( 'stream' === $item['job_type'] ) { $result['type'] = 'video'; } @@ -148,51 +122,16 @@ private function get_mime_type_for_job_type( $job_type, $mime_type ) { /** * Upload media to the Frappe backend. * - * @param int $attachment_id Attachment ID. - * @param bool $retranscode Whether this is a retranscode request. + * @param int $attachment_id Attachment ID. * @return void */ - public function upload_media_to_frappe_backend( $attachment_id, $retranscode = false ) { + public function upload_media_to_frappe_backend( $attachment_id ) { // Check if local development environment. if ( rtgodam_is_local_environment() ) { return; } - /** - * Filter to allow external developers to disable automatic transcoding on upload. - * This allows users to have manual control over when media files get transcoded. - * - * Note: This filter only applies to automatic uploads. Manual retranscoding requests - * (via bulk actions, tools page, etc.) will always proceed regardless of this setting. - * Form integrations will also use this filter to disable transcoding for form uploads. - * - * Example usage: - * add_filter( 'godam_auto_transcode_on_upload', '__return_false' ); // Disable globally - * - * @since 1.5.0 - * - * @param bool $auto_transcode_on_upload Whether to automatically transcode on upload. Default true. - */ - if ( ! $retranscode ) { - $auto_transcode_on_upload = apply_filters( 'godam_auto_transcode_on_upload', true ); - - if ( ! $auto_transcode_on_upload ) { - return; - } - } - - $transcoding_job_id = get_post_meta( $attachment_id, 'rtgodam_transcoding_job_id', true ); - - // Check virtual media status for transcoding requests. - $godam_original_id = get_post_meta( $attachment_id, '_godam_original_id', true ); - $is_virtual_media = ! empty( $godam_original_id ); - - // Skip transcoding for virtual media. - if ( $is_virtual_media ) { - return; - } - - // Only if attachment type is image. + // Only if attachment type if image. if ( 'image' !== substr( get_post_mime_type( $attachment_id ), 0, 5 ) ) { return; } @@ -203,7 +142,7 @@ public function upload_media_to_frappe_backend( $attachment_id, $retranscode = f return; } - $api_url = RTGODAM_API_BASE . '/api/resource/Transcoder Job' . ( empty( $transcoding_job_id ) ? '' : '/' . $transcoding_job_id ); + $api_url = RTGODAM_API_BASE . '/api/resource/Transcoder Job'; $attachment_url = wp_get_attachment_url( $attachment_id ); @@ -218,52 +157,33 @@ public function upload_media_to_frappe_backend( $attachment_id, $retranscode = f // Get author name with fallback to username. $author_first_name = ''; $author_last_name = ''; - $author_email = ''; if ( $attachment_author ) { - $author_first_name = $attachment_author->first_name ?? ''; - $author_last_name = $attachment_author->last_name ?? ''; - $author_email = $attachment_author->user_email ?? ''; + $author_first_name = $attachment_author->first_name; + $author_last_name = $attachment_author->last_name; // If first and last names are empty, use username as fallback. if ( empty( $author_first_name ) && empty( $author_last_name ) ) { - $author_first_name = $attachment_author->user_login ?? ''; + $author_first_name = $attachment_author->user_login; } } - if ( ! defined( 'RTGODAM_TRANSCODER_CALLBACK_URL' ) ) { - include_once RTGODAM_PATH . 'admin/class-rtgodam-transcoder-rest-routes.php'; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingCustomConstant - define( 'RTGODAM_TRANSCODER_CALLBACK_URL', \RTGODAM_Transcoder_Rest_Routes::get_callback_url() ); - } - - $callback_url = RTGODAM_TRANSCODER_CALLBACK_URL; - - /** - * Manually setting the rest api endpoint, we can refactor that later to use similar functionality as callback_url. - */ - $status_callback_url = get_rest_url( get_current_blog_id(), '/godam/v1/transcoding/transcoding-status' ); - // Request params. $params = array( - 'retranscode' => empty( $transcoding_job_id ) ? 0 : 1, 'api_token' => $api_key, 'job_type' => 'image', - 'job_for' => 'wp-media', 'file_origin' => $attachment_url, 'orignal_file_name' => $file_name ?? $file_title, - 'callback_url' => rawurlencode( $callback_url ), - 'status_callback' => rawurlencode( $status_callback_url ), - 'wp_author_email' => apply_filters( 'godam_author_email_to_send', $author_email, $attachment_id ), + 'wp_author_email' => apply_filters( 'godam_author_email_to_send', $attachment_author ? $attachment_author->user_email : '', $attachment_id ), 'wp_site' => $site_url, 'wp_author_first_name' => apply_filters( 'godam_author_first_name_to_send', $author_first_name, $attachment_id ), 'wp_author_last_name' => apply_filters( 'godam_author_last_name_to_send', $author_last_name, $attachment_id ), 'public' => 1, ); - $upload_media = wp_remote_request( + $upload_media = wp_remote_post( $api_url, array( - 'method' => empty( $transcoding_job_id ) ? 'POST' : 'PUT', 'body' => wp_json_encode( $params ), 'headers' => array( 'Content-Type' => 'application/json', @@ -357,15 +277,8 @@ public function delete_child_media_folder( $term, $taxonomy ) { * @return array $response Attachment response. */ public function add_media_transcoding_status_js( $response, $attachment ) { - // Check if attachment type is video, audio, PDF, or image. - $mime_type = $attachment->post_mime_type; - $is_video = 'video' === substr( $mime_type, 0, 5 ); - $is_audio = 'audio' === substr( $mime_type, 0, 5 ); - $is_pdf = 'application/pdf' === $mime_type; - $is_image = 'image' === substr( $mime_type, 0, 5 ); - - // Only process supported attachment types. - if ( ! ( $is_video || $is_audio || $is_pdf || $is_image ) ) { + // Check if attachment type is video. + if ( 'video' !== substr( $attachment->post_mime_type, 0, 5 ) ) { return $response; } @@ -406,26 +319,11 @@ public function add_media_transcoding_status_js( $response, $attachment ) { if ( ! empty( $godam_original_id ) ) { // Indicate that this is a virtual attachment. $response['virtual'] = true; - // Set the icon to be used for the virtual media preview. + $response['icon'] = get_post_meta( $attachment->ID, 'icon', true ); // Populate the image field used by the media library to show previews. - $icon_url = wp_mime_type_icon( $attachment->ID ); - - // For audio and PDF, ensure we use the default icons. - if ( empty( $icon_url ) || strpos( $icon_url, '.svg' ) !== false ) { - if ( $is_audio ) { - $icon_url = includes_url( 'images/media/audio.png' ); - } elseif ( $is_pdf ) { - $icon_url = includes_url( 'images/media/document.png' ); - } - } - - $response['image'] = array(); - - if ( ! empty( $icon_url ) ) { - $response['icon'] = $icon_url; - $response['image']['src'] = $icon_url; - } + $response['image'] = array(); + $response['image']['src'] = $response['icon']; } return $response; @@ -761,7 +659,7 @@ class="annual-plan-offer-banner__dismiss" * @return int|WP_Error Attachment ID on success, WP_Error on failure. */ private function godam_replace_attachment_with_external_file( $attachment_id, $file_url = '' ) { - if ( ! $attachment_id || ! $this->is_valid_url( $file_url ) ) { + if ( ! $attachment_id || ! filter_var( $file_url, FILTER_VALIDATE_URL ) || ! wp_http_validate_url( $file_url ) ) { return new \WP_Error( 'invalid_input', __( 'Invalid attachment ID or URL.', 'godam' ) ); } @@ -889,54 +787,4 @@ public function filter_attachment_url_for_virtual_media( $url, $post_id ) { return $url; } - - /** - * Filter srcset calculation for virtual media to use full URLs. - * - * @since 1.5.0 - * - * @param array|false $sources Array of image sources for srcset or false. - * @param array $size_array Array of width and height values. - * @param string $image_src The 'src' of the image. - * @param array $image_meta The image meta data. - * @param int $attachment_id The image attachment ID. - * - * @return array|false Filtered sources array or false. - */ - public function filter_virtual_media_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) { - $godam_original_id = get_post_meta( $attachment_id, '_godam_original_id', true ); - - if ( empty( $godam_original_id ) ) { - return $sources; - } - - // Check if image attachment. - $attachment_mime_type = get_post_mime_type( $attachment_id ); - if ( 'image' !== substr( $attachment_mime_type, 0, 5 ) ) { - return $sources; - } - - // Rebuild sources array for virtual media. - if ( empty( $sources ) || ! is_array( $sources ) ) { - return $sources; - } - - // Use the current image URL as the base for all subsizes. - $base_url = trailingslashit( untrailingslashit( dirname( $image_src ) ) ); - - // Rebuild sources array for virtual media. - foreach ( $sources as &$source ) { - - // Get last string after the last slash in the file url. - $file_basename = basename( $source['url'] ); - - // Rebuild the full URL using the base URL and the file basename. - $url = $base_url . ltrim( $file_basename, '/' ); - - $source['url'] = esc_url( $url ); - } - unset( $source ); // Break the reference. - - return $sources; - } } diff --git a/inc/classes/class-pages.php b/inc/classes/class-pages.php index 1fed19a20..678c47d19 100644 --- a/inc/classes/class-pages.php +++ b/inc/classes/class-pages.php @@ -507,12 +507,6 @@ public function admin_enqueue_scripts() { ) ); - wp_localize_script( - 'transcoder-page-script-video-editor', - 'posthogConfig', - $this->get_posthog_config() - ); - // Enqueue Gravity Forms styles if the plugin is active. if ( $is_gf_active ) { $this->enqueue_gravity_forms_styles(); @@ -615,12 +609,6 @@ public function admin_enqueue_scripts() { $current_time = new \DateTime( 'now', $timezone ); $end_time = new \DateTime( '2026-01-20 23:59:59', $timezone ); - wp_localize_script( - 'godam-page-script-dashboard', - 'posthogConfig', - $this->get_posthog_config() - ); - wp_localize_script( 'godam-page-script-dashboard', 'videoData', @@ -680,12 +668,6 @@ public function admin_enqueue_scripts() { ) ); - wp_localize_script( - 'transcoder-page-script-analytics', - 'posthogConfig', - $this->get_posthog_config() - ); - $rtgodam_user_data = rtgodam_get_user_data( true ); wp_localize_script( @@ -721,12 +703,6 @@ public function admin_enqueue_scripts() { $rtgodam_user_data ); - wp_localize_script( - 'godam-page-script-help', - 'posthogConfig', - $this->get_posthog_config() - ); - // Footer URL data for internal redirection. wp_localize_script( 'godam-page-script-help', @@ -757,12 +733,6 @@ public function admin_enqueue_scripts() { ); } - wp_localize_script( - 'transcoder-page-script-godam', - 'posthogConfig', - $this->get_posthog_config() - ); - // Footer URL data for internal redirection. wp_localize_script( 'transcoder-page-script-godam', @@ -794,12 +764,6 @@ public function admin_enqueue_scripts() { $rtgodam_user_data ); - wp_localize_script( - 'godam-page-script-tools', - 'posthogConfig', - $this->get_posthog_config() - ); - // Footer URL data for internal redirection. wp_localize_script( 'godam-page-script-tools', @@ -830,12 +794,6 @@ public function admin_enqueue_scripts() { ) ); - wp_localize_script( - 'godam-page-script-whats-new', - 'posthogConfig', - $this->get_posthog_config() - ); - wp_enqueue_script( 'godam-page-script-whats-new' ); } @@ -868,53 +826,6 @@ public function admin_enqueue_scripts() { 'roles' => $roles, ) ); - - wp_localize_script( - 'media-library-react', - 'posthogConfig', - $this->get_posthog_config() - ); - } - - /** - * Get PostHog configuration for internal GoDAM analytics tracking. - * These are hardcoded public keys - clients don't need to configure anything. - * - * @return array PostHog configuration array with 'key', 'host', and 'enabled' settings. - */ - private function get_posthog_config() { - $settings = get_option( 'rtgodam-settings', array() ); - $enable_tracking = isset( $settings['general']['enable_posthog_tracking'] ) ? $settings['general']['enable_posthog_tracking'] : false; - - $config = array( - 'key' => 'phc_9P3X3py1SfwrF78SXXkIyL2cHjkRTpvWzqf8RZJDaSk', - 'host' => 'https://us.i.posthog.com', - 'enabled' => (int) $enable_tracking, // Convert boolean to int (0/1) for proper JS encoding. - ); - - if ( $enable_tracking ) { - global $wpdb, $wp_version; - - if ( ! function_exists( 'get_plugins' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - - $all_plugins = get_plugins(); - $active_plugins = get_option( 'active_plugins', array() ); - - $config['properties'] = array( - 'php_version' => phpversion(), - 'mysql_version' => $wpdb->db_version(), - 'server_software' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '', - 'wp_version' => $wp_version, - 'site_language' => get_locale(), - 'active_plugins_count' => count( $active_plugins ), - 'total_plugins_count' => count( $all_plugins ), - 'user_count' => count_users()['total_users'] ?? 0, - ); - } - - return $config; } /** diff --git a/inc/classes/class-plugin.php b/inc/classes/class-plugin.php index 47acdcd24..28b8f2a5e 100644 --- a/inc/classes/class-plugin.php +++ b/inc/classes/class-plugin.php @@ -17,7 +17,6 @@ use RTGODAM\Inc\Media_Tracker; use RTGODAM\Inc\Rewrite; use RTGODAM\Inc\Video_Preview; -use RTGODAM\Inc\Video_Embed; use RTGODAM\Inc\Video_Permalinks; use RTGODAM\Inc\Video_Engagement; use RTGODAM\Inc\Update; @@ -88,7 +87,6 @@ protected function __construct() { Seo::get_instance(); Rewrite::get_instance(); Video_Preview::get_instance(); - Video_Embed::get_instance(); Video_Permalinks::get_instance(); Embed::get_instance(); diff --git a/inc/classes/class-rewrite.php b/inc/classes/class-rewrite.php index 3cd28e763..65ad59740 100644 --- a/inc/classes/class-rewrite.php +++ b/inc/classes/class-rewrite.php @@ -8,10 +8,6 @@ namespace RTGODAM\Inc; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/class-video-embed.php b/inc/classes/class-video-embed.php deleted file mode 100644 index aadb32c47..000000000 --- a/inc/classes/class-video-embed.php +++ /dev/null @@ -1,115 +0,0 @@ -setup_hooks(); - } - - /** - * To setup action/filter. - * - * @since 1.5.0 - * - * @return void - */ - protected function setup_hooks() { - add_action( 'template_include', array( $this, 'load_video_embed_template' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_video_embed_assets' ) ); - add_filter( 'show_admin_bar', array( $this, 'hide_admin_bar_on_embed' ) ); // phpcs:ignore WordPressVIPMinimum.UserExperience.AdminBarRemoval.RemovalDetected -- This is a valid use case for the video embed page. - add_filter( 'qm/dispatch/html', array( $this, 'disable_query_monitor_on_embed' ) ); - } - - /** - * Load the video embed template. - * - * @since 1.5.0 - * - * @param string $template The current template. - * @return string The path to the video embed template. - */ - public function load_video_embed_template( $template ) { - if ( 'video-embed' === get_query_var( 'godam_page' ) ) { - return RTGODAM_PATH . 'inc/templates/video-embed.php'; - } - return $template; - } - - /** - * Enqueue assets for the video embed page. - * - * @since 1.5.0 - * - * This method registers the styles and scripts needed for the video embed page. - */ - public function enqueue_video_embed_assets() { - // Enqueue style for the video embed page. - wp_register_style( - 'godam-video-embed-style', - RTGODAM_URL . 'assets/build/css/godam-video-embed.css', - array(), - filemtime( RTGODAM_PATH . 'assets/build/css/godam-video-embed.css' ) - ); - - // Enqueue script for the video embed page. - wp_register_script( - 'godam-video-embed-script', - RTGODAM_URL . 'assets/build/js/godam-video-embed.min.js', - array(), - filemtime( RTGODAM_PATH . 'assets/build/js/godam-video-embed.min.js' ), - true - ); - } - - /** - * Hide the admin bar on the video embed page. - * - * @since 1.5.0 - * - * @param bool $show_admin_bar Whether to show the admin bar. - * @return bool False if on embed page, otherwise the original value. - */ - public function hide_admin_bar_on_embed( $show_admin_bar ) { - if ( 'video-embed' === get_query_var( 'godam_page' ) ) { - return false; - } - return $show_admin_bar; - } - - /** - * Disable Query Monitor on the video embed page. - * - * @since 1.5.0 - * - * @param bool $dispatch Whether to dispatch Query Monitor output. - * @return bool False if on embed page, otherwise the original value. - */ - public function disable_query_monitor_on_embed( $dispatch ) { - if ( 'video-embed' === get_query_var( 'godam_page' ) ) { - return false; - } - return $dispatch; - } -} diff --git a/inc/classes/class-video-engagement.php b/inc/classes/class-video-engagement.php index 59acba65b..935ab1f2c 100644 --- a/inc/classes/class-video-engagement.php +++ b/inc/classes/class-video-engagement.php @@ -7,10 +7,6 @@ namespace RTGODAM\Inc; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; use WP_Error; diff --git a/inc/classes/class-video-metadata.php b/inc/classes/class-video-metadata.php index 7fafb89d7..9cec66957 100644 --- a/inc/classes/class-video-metadata.php +++ b/inc/classes/class-video-metadata.php @@ -191,13 +191,7 @@ private function migrate_existing_video_metadata() { public function set_media_library_thumbnail( $response, $attachment, $meta ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $attachment and $meta are not modified. if ( 0 === strpos( $response['mime'], 'video/' ) || 'application/pdf' === $response['mime'] ) { - $thumbnail_url = get_post_meta( $response['id'], 'rtgodam_media_video_thumbnail', true ); - - // Check for icon if it is a virtual media (for PDFs imported from GoDAM). - if ( empty( $thumbnail_url ) ) { - $thumbnail_url = get_post_meta( $response['id'], 'rtgodam_media_pdf_thumbnail', true ); - } - + $thumbnail_url = get_post_meta( $response['id'], 'rtgodam_media_video_thumbnail', true ); $attachment_meta = get_post_meta( $response['id'], '_wp_attachment_metadata', true ); if ( ! empty( $thumbnail_url ) ) { diff --git a/inc/classes/class-video-preview.php b/inc/classes/class-video-preview.php index 697f7340d..84dcc2cfd 100644 --- a/inc/classes/class-video-preview.php +++ b/inc/classes/class-video-preview.php @@ -8,10 +8,6 @@ namespace RTGODAM\Inc; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/everest-forms/everest-forms-field-godam-record-frontend.php b/inc/classes/everest-forms/everest-forms-field-godam-record-frontend.php index 7d92c0452..1b551268a 100644 --- a/inc/classes/everest-forms/everest-forms-field-godam-record-frontend.php +++ b/inc/classes/everest-forms/everest-forms-field-godam-record-frontend.php @@ -7,10 +7,6 @@ * @since 1.4.0 */ -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - // Get the primary field data. $godam_primary = $field['properties']['inputs']['primary'] ?? array(); diff --git a/inc/classes/fluentforms/class-form-submit.php b/inc/classes/fluentforms/class-form-submit.php index 7cba617dc..56f75f0c5 100644 --- a/inc/classes/fluentforms/class-form-submit.php +++ b/inc/classes/fluentforms/class-form-submit.php @@ -7,10 +7,6 @@ namespace RTGODAM\Inc\FluentForms; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/fluentforms/fields/class-recorder-field.php b/inc/classes/fluentforms/fields/class-recorder-field.php index 81ba32284..29881584d 100644 --- a/inc/classes/fluentforms/fields/class-recorder-field.php +++ b/inc/classes/fluentforms/fields/class-recorder-field.php @@ -80,7 +80,7 @@ public function __construct() { /** * Initialize all values. */ - $this->editor_label = __( 'GoDAM Recorder', 'godam' ); + $this->editor_label = __( 'Godam Recorder', 'godam' ); $this->button_text = __( 'Record Video', 'godam' ); } diff --git a/inc/classes/lifter-lms/class-lifter-lms.php b/inc/classes/lifter-lms/class-lifter-lms.php index 2273ca27b..bf16667db 100644 --- a/inc/classes/lifter-lms/class-lifter-lms.php +++ b/inc/classes/lifter-lms/class-lifter-lms.php @@ -10,10 +10,6 @@ namespace RTGODAM\Inc\Lifter_LMS; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - use RTGODAM\Inc\Traits\Singleton; /** diff --git a/inc/classes/ninja-forms/class-ninja-forms-integration.php b/inc/classes/ninja-forms/class-ninja-forms-integration.php index 9b219a603..00ea61cfb 100644 --- a/inc/classes/ninja-forms/class-ninja-forms-integration.php +++ b/inc/classes/ninja-forms/class-ninja-forms-integration.php @@ -46,7 +46,7 @@ public function init() { * @return void */ public function setup_hooks() { - add_action( 'admin_enqueue_scripts', array( $this, 'add_additional_scripts' ), 11 ); + add_action( 'admin_enqueue_scripts', array( $this, 'add_additional_css_for_video_editor' ), 11 ); add_action( 'wp_enqueue_scripts', array( $this, 'add_additional_css_for_godam_player' ), 11 ); add_action( 'rtgodam_render_layer_for_video_editor_before', array( $this, 'add_css_for_the_layer_inside_iframe' ), 10, 2 ); @@ -57,13 +57,13 @@ public function setup_hooks() { } /** - * Add additional scripts. + * Add additional css for video editor. * * @since 1.4.0 * * @return void */ - public function add_additional_scripts() { + public function add_additional_css_for_video_editor() { $custom_css = ' .form-container.ninja-form { margin: unset; @@ -78,24 +78,8 @@ public function add_additional_scripts() { } '; - wp_add_inline_style( 'rtgodam-style', $custom_css ); - $screen = get_current_screen(); - if ( ! $screen ) { - return; - } - - if ( 'ninja-forms_page_nf-submissions' !== $screen->id ) { - return; - } - - wp_enqueue_script( - 'rtgodam-ninja-forms-submissions-list', - RTGODAM_URL . 'assets/build/js/ninja-forms-submissions-list.min.js', - array( 'jquery', 'rtgodam-script' ), - filemtime( RTGODAM_PATH . 'assets/build/js/ninja-forms-submissions-list.min.js' ), - true - ); + wp_add_inline_style( 'rtgodam-style', $custom_css ); } /** @@ -112,6 +96,7 @@ public function add_additional_css_for_godam_player() { } '; + wp_add_inline_style( 'godam-player-style', $custom_css ); } diff --git a/inc/classes/rest-api/class-dynamic-gallery.php b/inc/classes/rest-api/class-dynamic-gallery.php index 3728d0b43..2788446ba 100644 --- a/inc/classes/rest-api/class-dynamic-gallery.php +++ b/inc/classes/rest-api/class-dynamic-gallery.php @@ -101,14 +101,6 @@ public function get_rest_routes() { 'type' => 'string', 'default' => '', ), - 'engagements' => array( - 'type' => 'boolean', - 'default' => true, - ), - 'open_to_new_page' => array( - 'type' => 'boolean', - 'default' => false, - ), ), ), ), @@ -139,8 +131,6 @@ public function render_gallery( WP_REST_Request $request ) { 'date_range' => $request->get_param( 'date_range' ), 'custom_date_start' => $request->get_param( 'custom_date_start' ), 'custom_date_end' => $request->get_param( 'custom_date_end' ), - 'engagements' => $request->get_param( 'engagements' ), - 'open_to_new_page' => $request->get_param( 'open_to_new_page' ), ); // Add filter for dynamic gallery attributes. @@ -248,7 +238,7 @@ public function render_gallery( WP_REST_Request $request ) { $video_settings = get_option( 'rtgodam_video_post_settings', array() ); $cpt_url_slug = ! empty( $video_settings['video_slug'] ) ? sanitize_title( $video_settings['video_slug'] ) : 'videos'; - $cpt_base_url = home_url( '/' ); + $cpt_base_url = home_url( '/' . $cpt_url_slug ); $query = new \WP_Query( $args ); ob_start(); @@ -276,9 +266,7 @@ public function render_gallery( WP_REST_Request $request ) { data-search="' . esc_attr( $atts['search'] ?? '' ) . '" data-date-range="' . esc_attr( $atts['date_range'] ?? '' ) . '" data-custom-date-start="' . esc_attr( $atts['custom_date_start'] ?? '' ) . '" - data-custom-date-end="' . esc_attr( $atts['custom_date_end'] ?? '' ) . '" - data-engagements="' . ( $atts['engagements'] ? '1' : '0' ) . '" - data-open-to-new-page="' . ( $atts['open_to_new_page'] ? '1' : '0' ) . '">'; + data-custom-date-end="' . esc_attr( $atts['custom_date_end'] ?? '' ) . '">'; } do_action( 'rtgodam_dynamic_gallery_before_output', $query, $atts ); @@ -307,49 +295,9 @@ public function render_gallery( WP_REST_Request $request ) { $duration = $metadata['length_formatted']; } } - - // Check if engagements are enabled for the video. - $engagements_enabled = $atts['engagements']; - $item_engagements_enabled = false; - if ( $engagements_enabled ) { - // Check if engagements are enabled for the video is transcoded. - $transcoded_job_id = get_post_meta( $video_id, 'rtgodam_transcoding_job_id', true ); - $tanscoded_status = get_post_meta( $video_id, 'rtgodam_transcoding_status', true ); - $item_engagements_enabled = ! empty( $transcoded_job_id ) && 'transcoded' === strtolower( $tanscoded_status ); - } - - // Build the query arguments for the video embed page. - $query_args = array( - 'godam_page' => 'video-embed', - 'id' => $video_id, - ); - - // Add the engagements query argument if it is enabled. - if ( $item_engagements_enabled ) { - $query_args['engagements'] = 'show'; - } - - $video_url = add_query_arg( $query_args, $cpt_base_url ); - - if ( isset( $atts['open_to_new_page'] ) && $atts['open_to_new_page'] ) { - $video_slug = get_post_field( 'post_name', $video_id ); - $video_settings = get_option( 'rtgodam_video_post_settings', array() ); - $cpt_url_slug = ! empty( $video_settings['video_slug'] ) ? sanitize_title( $video_settings['video_slug'] ) : 'videos'; - $cpt_base_url = home_url( '/' . $cpt_url_slug ); - $video_url = $cpt_base_url . '/' . $video_slug; - - if ( $item_engagements_enabled ) { - $video_url = add_query_arg( - array( - 'engagements' => 'show', - ), - $video_url - ); - } - } - + echo '
'; - echo '
'; + echo '
'; echo '' . esc_attr( $video_title ) . ''; if ( $duration ) { echo '' . esc_html( $duration ) . ''; diff --git a/inc/classes/rest-api/class-media-library.php b/inc/classes/rest-api/class-media-library.php index d3d9701fb..4d320600f 100644 --- a/inc/classes/rest-api/class-media-library.php +++ b/inc/classes/rest-api/class-media-library.php @@ -303,207 +303,6 @@ public function get_rest_routes() { }, ), ), - array( - 'namespace' => $this->namespace, - 'route' => '/' . $this->rest_base . '/generate-image-subsizes-callback', - 'args' => array( - 'methods' => \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'generate_image_subsizes_callback' ), - 'permission_callback' => array( $this, 'verify_callback_permission' ), - ), - ), - ); - } - - /** - * Verify callback permission by checking API key. - * - * @since 1.5.0 - * - * @param WP_REST_Request $request The request object. - * @return bool|WP_Error True if permission granted, WP_Error otherwise. - */ - public function verify_callback_permission( $request ) { - $event = $request->get_param( 'event' ); - $data = $request->get_param( 'data' ); - - if ( empty( $data['api_key'] ) ) { - return new \WP_Error( 'api_key_required', __( 'API key is required.', 'godam' ), array( 'status' => 403 ) ); - } - - $provided_api_key = $data['api_key']; - $stored_api_key = get_option( 'rtgodam-api-key' ); - - if ( ! hash_equals( $stored_api_key, $provided_api_key ) ) { - return new \WP_Error( 'forbidden', __( 'Invalid API key.', 'godam' ), array( 'status' => 403 ) ); - } - - if ( 'image_resize' !== $event ) { - return new \WP_Error( 'invalid_event', __( 'Invalid event.', 'godam' ), array( 'status' => 403 ) ); - } - - return true; - } - - /** - * Update image attachment meta with subsizes. - * - * @since 1.5.0 - * - * @param array $sizes Array of sizes data. - * @param int $job_id Job ID. - * @param int $attachment_id Attachment ID. Default to 0. - * - * @return bool True if successful, false otherwise. - */ - private function update_image_attachment_meta( $sizes, $job_id, $attachment_id = 0 ) { - if ( empty( $sizes ) || ! is_array( $sizes ) ) { - return false; - } - - if ( empty( $attachment_id ) && empty( $job_id ) ) { - return false; - } - - if ( empty( $attachment_id ) || ! is_numeric( $attachment_id ) ) { - $attachment_id = rtgodam_get_post_id_by_meta_key_and_value( 'rtgodam_transcoding_job_id', $job_id ); - } - - $subsizes = array(); - foreach ( $sizes as $size ) { - $subsizes[] = array( - 'file' => $size['url'], - 'filesize' => $size['file_size'], - 'width' => $size['width'], - 'height' => $size['height'], - ); - } - - $attachment_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true ); - - // Normalize attachment meta to an array with a sizes key. - if ( ! is_array( $attachment_meta ) ) { - $attachment_meta = array(); - } - - if ( empty( $attachment_meta['sizes'] ) || ! is_array( $attachment_meta['sizes'] ) ) { - $attachment_meta['sizes'] = array(); - } - - // Get registered image sizes from WordPress Settings > Media. - $registered_sizes = wp_get_registered_image_subsizes(); - $additional_sizes = wp_get_additional_image_sizes(); - - // Remove additional sizes from registered sizes to avoid duplicates. - foreach ( $additional_sizes as $size_name => $size_data ) { - if ( isset( $registered_sizes[ $size_name ] ) ) { - unset( $registered_sizes[ $size_name ] ); - } - } - - // Map received subsizes to registered size names. - // Determine the closest matching registered size for each received size. - foreach ( $subsizes as $size ) { - $external_size_name = ''; - $min_diff = PHP_INT_MAX; - foreach ( $registered_sizes as $size_name => $registered_size_data ) { - if ( $registered_size_data['width'] === $size['width'] && $registered_size_data['height'] === $size['height'] ) { - $external_size_name = $size_name; - break; - } elseif ( $size['width'] <= $registered_size_data['width'] && $size['height'] <= $registered_size_data['height'] ) { - $diff = $registered_size_data['width'] - $size['width'] + $registered_size_data['height'] - $size['height']; - if ( $diff < $min_diff ) { - $min_diff = $diff; - $external_size_name = $size_name; - } - } - } - - if ( empty( $external_size_name ) ) { - continue; - } - - // Get last string after the last slash in the file url. - $file_basename = basename( $size['file'] ); - - $attachment_meta['sizes'][ $external_size_name ] = array( - 'file' => $file_basename, - 'filesize' => $size['filesize'], - 'width' => $size['width'], - 'height' => $size['height'], - ); - } - - // Ensure top-level width/height exist for srcset calculation, fall back to the largest generated size. - if ( ( empty( $attachment_meta['width'] ) || empty( $attachment_meta['height'] ) ) && ! empty( $subsizes ) ) { - $largest = array_reduce( - $subsizes, - function ( $carry, $item ) { - if ( null === $carry ) { - return $item; - } - return ( $item['width'] > $carry['width'] ) ? $item : $carry; - }, - null - ); - - if ( $largest ) { - $attachment_meta['width'] = (int) $largest['width']; - $attachment_meta['height'] = (int) $largest['height']; - } - } - - // Backfill the "file" key so WordPress does not bail early while building srcset. - if ( empty( $attachment_meta['file'] ) ) { - $full_url = wp_get_attachment_url( $attachment_id ); - if ( $full_url ) { - $path = wp_parse_url( $full_url, PHP_URL_PATH ); - $attachment_meta['file'] = ltrim( wp_basename( $path ), '/' ); - } - } - - update_post_meta( $attachment_id, '_wp_attachment_metadata', $attachment_meta ); - return true; - } - - /** - * Generate image subsizes callback. - * - * @param \WP_REST_Request $request REST API request. - * @return \WP_REST_Response|WP_Error Success response or WP_Error on invalid event. - * - * @since 1.5.0 - */ - public function generate_image_subsizes_callback( $request ) { - $data = $request->get_json_params(); - - if ( 'image_resize' !== $data['event'] ) { - return rest_ensure_response( - array( - 'success' => false, - 'message' => __( 'Invalid event.', 'godam' ), - ) - ); - } - - $job_id = isset( $data['data']['job_id'] ) ? $data['data']['job_id'] : ''; - $sizes = isset( $data['data']['resized_images'] ) && is_array( $data['data']['resized_images'] ) ? $data['data']['resized_images'] : array(); - - if ( empty( $job_id ) || empty( $sizes ) ) { - return new \WP_Error( 'invalid_data', __( 'job_id and resized_images are required.', 'godam' ), array( 'status' => 400 ) ); - } - - $result = $this->update_image_attachment_meta( $sizes, $job_id ); - - if ( ! $result ) { - return new \WP_Error( 'update_failed', __( 'Failed to update attachment metadata.', 'godam' ), array( 'status' => 500 ) ); - } - - return rest_ensure_response( - array( - 'success' => true, - 'message' => __( 'Image subsizes successfully generated.', 'godam' ), - ) ); } @@ -1508,63 +1307,15 @@ public function create_media_entry( $request ) { // Set custom metadata to track GoDAM-related properties. update_post_meta( $attach_id, '_godam_original_id', $godam_id ); + update_post_meta( $attach_id, '_godam_icon', esc_url_raw( $data['icon'] ?? '' ) ); + update_post_meta( $attach_id, '_filesize_human', sanitize_text_field( $data['filesizeHumanReadable'] ?? '' ) ); + update_post_meta( $attach_id, '_godam_label', sanitize_text_field( $data['label'] ?? '' ) ); update_post_meta( $attach_id, '_owner_email', sanitize_email( $data['owner'] ?? '' ) ); update_post_meta( $attach_id, 'rtgodam_transcoded_url', esc_url_raw( $data['mpd_url'] ?? '' ) ); update_post_meta( $attach_id, 'rtgodam_transcoding_status', 'transcoded' ); + update_post_meta( $attach_id, 'icon', $data['icon'] ); + update_post_meta( $attach_id, 'rtgodam_hls_transcoded_url', esc_url_raw( $data['hls_url'] ?? '' ) ); update_post_meta( $attach_id, 'rtgodam_transcoding_job_id', $godam_id ); - update_post_meta( $attach_id, '_wp_attached_file', sanitize_text_field( $data['filename'] ) ); // Virtual media path. - - - if ( 'video' === $data['type'] ) { - update_post_meta( $attach_id, 'rtgodam_hls_transcoded_url', esc_url_raw( $data['hls_url'] ?? '' ) ); - - $wp_attachment_metadata = array( - 'filesize' => isset( $data['filesizeInBytes'] ) ? (int) $data['filesizeInBytes'] : 0, - ); - - // Set Video thumbnail from icon URL if provided. - if ( ! empty( $data['icon'] ) ) { - update_post_meta( $attach_id, 'rtgodam_media_video_thumbnail', esc_url_raw( $data['icon'] ) ); - } - - update_post_meta( $attach_id, '_wp_attachment_metadata', $wp_attachment_metadata ); - } elseif ( 'image' === $data['type'] ) { - // Initialize metadata with basic info. - $wp_attachment_metadata = array( - 'filesize' => isset( $data['filesizeInBytes'] ) ? (int) $data['filesizeInBytes'] : 0, - 'sizes' => array(), - ); - - // Add width and height if available. - if ( ! empty( $data['width'] ) && ! empty( $data['height'] ) ) { - $wp_attachment_metadata['width'] = (int) $data['width']; - $wp_attachment_metadata['height'] = (int) $data['height']; - } - - update_post_meta( $attach_id, '_wp_attachment_metadata', $wp_attachment_metadata ); - - // Request image subsizes from GoDAM Central. - $this->request_image_subsizes_from_godam( $godam_id, $attach_id ); - } elseif ( 'audio' === $data['type'] ) { - $wp_attachment_metadata = array( - 'filesize' => isset( $data['filesizeInBytes'] ) ? (int) $data['filesizeInBytes'] : 0, - 'mime_type' => $data['mime'], - ); - - update_post_meta( $attach_id, '_wp_attachment_metadata', $wp_attachment_metadata ); - } elseif ( 'pdf' === $data['type'] ) { - $wp_attachment_metadata = array( - 'filesize' => isset( $data['filesizeInBytes'] ) ? (int) $data['filesizeInBytes'] : 0, - ); - - update_post_meta( $attach_id, '_wp_attachment_metadata', $wp_attachment_metadata ); - - // Set PDF thumbnail from icon URL if provided. - if ( ! empty( $data['icon'] ) ) { - update_post_meta( $attach_id, 'rtgodam_media_pdf_thumbnail', esc_url_raw( $data['icon'] ) ); - } - } - // Return the newly created media object. return new \WP_REST_Response( @@ -1881,111 +1632,4 @@ private function prepare_term_responses( $terms ) { return $prepared; } - - /** - * Request image subsizes generation from GoDAM Central. - * - * Sends a request to GoDAM Central API to generate image subsizes based on - * WordPress registered image sizes (from Settings > Media). The API endpoint - * should accept: - * - job_id: The GoDAM file ID - * - api_key: The GoDAM API key - * - sizes_data: Array of size requests with width, height, crop - * - events_callback_url: The URL to send events to. - * - * @since 1.5.0 - * - * @param string $job_id The GoDAM job ID. - * @param int $attachment_id The WordPress attachment ID. - * @return bool True if request was successful, false otherwise. - */ - private function request_image_subsizes_from_godam( $job_id, $attachment_id ) { - $api_key = get_option( 'rtgodam-api-key', '' ); - - if ( empty( $api_key ) ) { - return false; - } - - // Get registered image sizes from WordPress Settings > Media. - $registered_sizes = wp_get_registered_image_subsizes(); - $additional_sizes = wp_get_additional_image_sizes(); - - // Remove additional sizes from registered sizes to avoid duplicates. - foreach ( $additional_sizes as $size_name => $size_data ) { - if ( isset( $registered_sizes[ $size_name ] ) ) { - unset( $registered_sizes[ $size_name ] ); - } - } - - if ( empty( $registered_sizes ) ) { - return false; - } - - - // Prepare size requests for GoDAM Central. - $size_requests = array(); - foreach ( $registered_sizes as $size_name => $size_data ) { - $size_requests[] = array( - 'width' => $size_data['width'], - 'height' => $size_data['height'], - 'crop' => $size_data['crop'], - ); - } - - // Construct the GoDAM API endpoint URL. - $api_url = RTGODAM_API_BASE . '/api/method/godam_core.api.image.generate_resized_images'; - - $events_callback_url = rest_url( 'godam/v1/media-library/generate-image-subsizes-callback' ); - - // Prepare request body. - $request_body = array( - 'job_id' => $job_id, - 'api_key' => $api_key, - 'sizes_data' => $size_requests, - 'events_callback_url' => $events_callback_url, - ); - - $args = array( - 'body' => wp_json_encode( $request_body ), - 'headers' => array( - 'Content-Type' => 'application/json', - ), - ); - - // Use vip_safe_wp_remote_post as primary and wp_safe_remote_post as fallback. - if ( function_exists( 'vip_safe_wp_remote_post' ) ) { - $response = vip_safe_wp_remote_post( $api_url, $args, 3, 3 ); - } else { - $response = wp_safe_remote_post( $api_url, $args ); - } - - // Check for WP_Error or non-200 status codes. - if ( is_wp_error( $response ) ) { - return false; - } - - $response_code = wp_remote_retrieve_response_code( $response ); - - if ( 200 !== $response_code ) { - return false; - } - - $body = json_decode( wp_remote_retrieve_body( $response ), true ); - - if ( empty( $body ) || ! is_array( $body ) ) { - return false; - } - - $body = isset( $body['message'] ) && ! empty( $body['message'] ) ? $body['message'] : array(); - - if ( ! isset( $body['message'] ) ) { - return false; - } - - if ( 'sizes_exist' === $body['message'] && isset( $body['sizes'] ) && is_array( $body['sizes'] ) ) { - return $this->update_image_attachment_meta( $body['sizes'], $job_id, $attachment_id ); - } - - return true; - } } diff --git a/inc/classes/rest-api/class-settings.php b/inc/classes/rest-api/class-settings.php index 52500bea9..d7332d5a5 100644 --- a/inc/classes/rest-api/class-settings.php +++ b/inc/classes/rest-api/class-settings.php @@ -49,8 +49,6 @@ private function get_default_settings() { 'general' => array( 'enable_folder_organization' => true, 'enable_gtm_tracking' => false, - 'enable_posthog_tracking' => false, - 'posthog_initialized' => false, ), 'video_player' => array( 'brand_image' => '', @@ -178,7 +176,6 @@ public function verify_api_key( $request ) { $result['data']['api_key'] = rtgodam_mask_string( $result['data']['api_key'] ); } - return new \WP_REST_Response( array( 'status' => 'success', @@ -244,13 +241,9 @@ public function get_api_key() { */ public function get_easydam_settings() { // Retrieve settings from the database. - $easydam_settings = get_option( 'rtgodam-settings', array() ); - $default_settings = $this->get_default_settings(); - - // Merge defaults with saved settings. - $merged_settings = array_replace_recursive( $default_settings, $easydam_settings ); + $easydam_settings = get_option( 'rtgodam-settings', $this->get_default_settings() ); - return new \WP_REST_Response( $merged_settings, 200 ); + return new \WP_REST_Response( $easydam_settings, 200 ); } /** @@ -315,8 +308,6 @@ public function sanitize_settings( $settings ) { 'general' => array( 'enable_folder_organization' => rest_sanitize_boolean( $settings['general']['enable_folder_organization'] ?? $default['general']['enable_folder_organization'] ), 'enable_gtm_tracking' => rest_sanitize_boolean( $settings['general']['enable_gtm_tracking'] ?? $default['general']['enable_gtm_tracking'] ), - 'enable_posthog_tracking' => rest_sanitize_boolean( $settings['general']['enable_posthog_tracking'] ?? $default['general']['enable_posthog_tracking'] ), - 'posthog_initialized' => rest_sanitize_boolean( $settings['general']['posthog_initialized'] ?? $default['general']['posthog_initialized'] ), ), 'video_player' => array( 'brand_image' => sanitize_text_field( $settings['video_player']['brand_image'] ?? $default['video_player']['brand_image'] ), diff --git a/inc/classes/rest-api/class-transcoding.php b/inc/classes/rest-api/class-transcoding.php index 9d9aa7f2e..51b0a4080 100644 --- a/inc/classes/rest-api/class-transcoding.php +++ b/inc/classes/rest-api/class-transcoding.php @@ -7,8 +7,6 @@ namespace RTGODAM\Inc\REST_API; -use RTGODAM\Inc\Media_Library_Ajax; - defined( 'ABSPATH' ) || exit; /** @@ -117,25 +115,6 @@ public function get_rest_routes() { }, ), ), - array( - 'namespace' => $this->namespace, - 'route' => '/' . $this->rest_base . '/check-transcoded-status/', - 'args' => array( - 'methods' => \WP_REST_Server::READABLE, - 'callback' => array( $this, 'check_transcoded_status' ), - 'permission_callback' => function () { - return current_user_can( 'edit_others_posts' ); - }, - 'args' => array( - 'ids' => array( - 'required' => true, - 'type' => 'string', - 'description' => __( 'The comma-separated string of attachment IDs to check.', 'godam' ), - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - ), - ), ); } @@ -261,7 +240,7 @@ private function get_status_object_from_attachment( int $attachment_id ) { $thumbnail_id = get_post_meta( $attachment_id, 'rtgodam_media_video_thumbnail', true ); // Handle retry logic for missing thumbnails when transcoding is complete. - if ( 'transcoded' === strtolower( $status ) && empty( $thumbnail_id ) ) { + if ( 'Transcoded' === $status && empty( $thumbnail_id ) ) { $retry_count = intval( get_post_meta( $attachment_id, 'rtgodam_thumbnail_retry_count', true ) ); $max_retries = 3; @@ -413,75 +392,10 @@ public function get_media_require_retranscoding( $request ) { } } while ( true ); - // Get counts for transcoded and untranscoded media. - $total_video_count = array_sum( (array) wp_count_attachments( 'video' ) ); - - // Count transcoded media (have rtgodam_transcoded_url meta). - $transcoded_args = array( - 'post_type' => 'attachment', - 'post_mime_type' => 'video', - 'post_status' => 'any', - 'posts_per_page' => 1, - 'fields' => 'ids', - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- This is a necessary query to find posts that have the rtgodam_transcoded_url meta. - 'meta_query' => array( - array( - 'key' => 'rtgodam_transcoded_url', - 'compare' => 'EXISTS', - ), - ), - ); - $transcoded_query = new \WP_Query( $transcoded_args ); - $transcoded_count = $transcoded_query->found_posts; // This will return the number of posts that have the rtgodam_transcoded_url meta. - - // Count untranscoded media (don't have rtgodam_transcoded_url meta). - $untranscoded_count = $total_video_count - $transcoded_count; - return new \WP_REST_Response( array( 'data' => $all_posts, - 'total_media_count' => $total_video_count, - 'transcode_count' => $untranscoded_count, - 'retranscode_count' => $transcoded_count, - ), - 200 - ); - } - - /** - * Check if specific media IDs are transcoded. - * - * @param \WP_REST_Request $request REST request object. - * - * @return \WP_REST_Response - */ - public function check_transcoded_status( $request ) { - $ids_param = $request->get_param( 'ids' ); - - // If it's a string (comma-separated), split it into array. - if ( is_string( $ids_param ) ) { - $attachment_ids = array_map( 'intval', explode( ',', $ids_param ) ); - } else { - $attachment_ids = array_map( 'intval', (array) $ids_param ); - } - - $transcode_count = 0; - $retranscode_count = 0; - - foreach ( $attachment_ids as $attachment_id ) { - $transcoded_url = get_post_meta( $attachment_id, 'rtgodam_transcoded_url', true ); - // If transcoded, it should have URL. - if ( ! empty( $transcoded_url ) ) { - ++$retranscode_count; - } else { - ++$transcode_count; - } - } - - return new \WP_REST_Response( - array( - 'transcode_count' => $transcode_count, - 'retranscode_count' => $retranscode_count, + 'total_media_count' => array_sum( (array) wp_count_attachments( 'video' ) ), ), 200 ); @@ -535,7 +449,7 @@ public function retranscode_media( \WP_REST_Request $request ) { if ( ! empty( $godam_original_id ) ) { $message = sprintf( // translators: 1: Attachment title, 2: Attachment ID. - __( '%1$s (ID %2$d) is virtual media from GoDAM Central. Please retranscode this media on GoDAM Central.', 'godam' ), + __( '%1$s (ID %2$d) is virtual media from GoDAM Central. Please retranscode this video on GoDAM Central.', 'godam' ), esc_html( $title ), absint( $attachment_id ) ); @@ -605,13 +519,8 @@ public function retranscode_media( \WP_REST_Request $request ) { $wp_metadata['mime_type'] = $mime_type; // Retranscode the media. - if ( preg_match( '/image/i', $mime_type ) ) { - $transcoder = Media_Library_Ajax::get_instance(); - $transcoder->upload_media_to_frappe_backend( $attachment_id, true ); - } else { - $transcoder = new \RTGODAM_Transcoder_Handler( true ); - $transcoder->wp_media_transcoding( $wp_metadata, $attachment_id, true, true ); - } + $transcoder = new \RTGODAM_Transcoder_Handler( true ); + $transcoder->wp_media_transcoding( $wp_metadata, $attachment_id ); // Check if the transcoding job ID is set. $is_sent = get_post_meta( $attachment_id, 'rtgodam_transcoding_job_id', true ); diff --git a/inc/classes/shortcodes/class-godam-video-gallery.php b/inc/classes/shortcodes/class-godam-video-gallery.php index 99f05de70..2af0ff9cb 100644 --- a/inc/classes/shortcodes/class-godam-video-gallery.php +++ b/inc/classes/shortcodes/class-godam-video-gallery.php @@ -220,7 +220,7 @@ public function render( $atts ) { $video_settings = get_option( 'rtgodam_video_post_settings', array() ); $cpt_url_slug = ! empty( $video_settings['video_slug'] ) ? sanitize_title( $video_settings['video_slug'] ) : 'videos'; - $cpt_base_url = home_url( '/' ); + $cpt_base_url = home_url( '/' . $cpt_url_slug ); $query = new \WP_Query( $args ); @@ -230,19 +230,14 @@ public function render( $atts ) { // Add action before gallery output. do_action( 'rtgodam_gallery_before_output', $query, $atts ); - $godam_figure_attributes = get_block_wrapper_attributes( - array( - 'class' => 'godam-video-gallery-wrapper', - ) - ); - // Calculate these values before using them. $total_videos = $query->found_posts; $shown_videos = count( $query->posts ); - echo '
'; + $alignment_class = ! empty( $atts['align'] ) ? ' align' . $atts['align'] : ''; echo '