PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
← All changes | includes/widgets/Media_Grid/Media_Grid.php +558 -26 51.1.63 → 51.1.86 View file →
@@ -49,8 +49,9 @@
49 49
50 50 public function get_script_depends()
51 51 {
52 52 return [
53 + 'jquery',
53 54 KING_ADDONS_ASSETS_UNIQUE_KEY . '-grid-media',
54 55 KING_ADDONS_ASSETS_UNIQUE_KEY . '-isotope-kng',
55 56 KING_ADDONS_ASSETS_UNIQUE_KEY . '-slick-slick',
56 57 KING_ADDONS_ASSETS_UNIQUE_KEY . '-lightgallery-lightgallery'
@@ -799,10 +800,11 @@
799 800
800 801 $this->add_control(
801 802 'query_manual_attachment',
802 803 [
803 - 'label' => esc_html__('Add Images', 'king-addons'),
804 + 'label' => esc_html__('Add Images (Legacy)', 'king-addons'),
804 805 'type' => Controls_Manager::GALLERY,
806 + 'description' => esc_html__('Used only when "Add Images & Videos" is empty. Prefer the repeater below for images and videos together.', 'king-addons'),
805 807 'separator' => 'before',
806 808 'dynamic' => [
807 809 'active' => true,
808 810 ],
@@ -811,9 +813,79 @@
811 813 ],
812 814 ]
813 815 );
814 816
817 + $media_repeater = new Repeater();
818 +
819 + $media_repeater->add_control(
820 + 'item_type',
821 + [
822 + 'label' => esc_html__('Media Type', 'king-addons'),
823 + 'type' => Controls_Manager::SELECT,
824 + 'default' => 'image',
825 + 'options' => [
826 + 'image' => esc_html__('Image', 'king-addons'),
827 + 'video' => esc_html__('Video', 'king-addons'),
828 + ],
829 + ]
830 + );
831 +
832 + $media_repeater->add_control(
833 + 'item_image',
834 + [
835 + 'label' => esc_html__('Image', 'king-addons'),
836 + 'type' => Controls_Manager::MEDIA,
837 + 'dynamic' => [
838 + 'active' => true,
839 + ],
840 + 'description' => esc_html__('For videos, this image is used as the cover/poster in the gallery.', 'king-addons'),
841 + ]
842 + );
843 +
844 + $media_repeater->add_control(
845 + 'item_video',
846 + [
847 + 'label' => esc_html__('Video File', 'king-addons'),
848 + 'type' => Controls_Manager::MEDIA,
849 + 'media_types' => [
850 + 'video',
851 + ],
852 + 'condition' => [
853 + 'item_type' => 'video',
854 + ],
855 + ]
856 + );
857 +
815 858 $this->add_control(
859 + 'query_manual_media',
860 + [
861 + 'label' => esc_html__('Add Images & Videos', 'king-addons'),
862 + 'type' => Controls_Manager::REPEATER,
863 + 'fields' => $media_repeater->get_controls(),
864 + 'default' => [],
865 + 'title_field' => '{{{ item_type.charAt(0).toUpperCase() + item_type.slice(1) }}}',
866 + 'separator' => 'before',
867 + 'condition' => [
868 + 'query_selection' => 'manual',
869 + ],
870 + ]
871 + );
872 +
873 + $this->add_control(
874 + 'query_include_videos',
875 + [
876 + 'label' => esc_html__('Include Videos', 'king-addons'),
877 + 'type' => Controls_Manager::SWITCHER,
878 + 'default' => '',
879 + 'description' => esc_html__('Include video files from the Media Library in the Auto selection.', 'king-addons'),
880 + 'condition' => [
881 + 'query_selection' => 'dynamic',
882 + ],
883 + 'separator' => 'before',
884 + ]
885 + );
886 +
887 + $this->add_control(
816 888 'query_posts_per_page',
817 889 [
818 890 'label' => esc_html__('Items Per Page', 'king-addons'),
819 891 'type' => Controls_Manager::NUMBER,
@@ -879,8 +951,79 @@
879 951 'label_block' => true
880 952 ]
881 953 );
882 954
955 + $this->add_responsive_control(
956 + 'layout_fitrows_media_height',
957 + [
958 + 'label' => esc_html__('Media Height', 'king-addons'),
959 + 'type' => Controls_Manager::SLIDER,
960 + 'size_units' => ['px', 'vh'],
961 + 'range' => [
962 + 'px' => [
963 + 'min' => 50,
964 + 'max' => 800,
965 + ],
966 + 'vh' => [
967 + 'min' => 10,
968 + 'max' => 100,
969 + ],
970 + ],
971 + 'default' => [
972 + 'unit' => 'px',
973 + 'size' => 280,
974 + ],
975 + 'selectors' => [
976 + '{{WRAPPER}} .king-addons-grid[data-settings*="fitRows"] .king-addons-grid-media-wrap' => 'height: {{SIZE}}{{UNIT}};',
977 + ],
978 + 'condition' => [
979 + 'layout_select' => 'fitRows',
980 + ],
981 + 'separator' => 'before',
982 + ]
983 + );
984 +
985 + $this->add_responsive_control(
986 + 'layout_fitrows_image_fit',
987 + [
988 + 'label' => esc_html__('Image Fit', 'king-addons'),
989 + 'type' => Controls_Manager::SELECT,
990 + 'options' => [
991 + 'cover' => esc_html__('Cover', 'king-addons'),
992 + 'contain' => esc_html__('Contain', 'king-addons'),
993 + ],
994 + 'default' => 'cover',
995 + 'selectors' => [
996 + '{{WRAPPER}} .king-addons-grid[data-settings*="fitRows"] .king-addons-grid-image-wrap img' => 'object-fit: {{VALUE}};',
997 + ],
998 + 'condition' => [
999 + 'layout_select' => 'fitRows',
1000 + ],
1001 + ]
1002 + );
1003 +
1004 + $this->add_responsive_control(
1005 + 'layout_fitrows_image_fit_position',
1006 + [
1007 + 'label' => esc_html__('Image Fit Position', 'king-addons'),
1008 + 'type' => Controls_Manager::SELECT,
1009 + 'options' => [
1010 + 'center center' => esc_html__('Center', 'king-addons'),
1011 + 'top center' => esc_html__('Top', 'king-addons'),
1012 + 'bottom center' => esc_html__('Bottom', 'king-addons'),
1013 + 'center left' => esc_html__('Left', 'king-addons'),
1014 + 'center right' => esc_html__('Right', 'king-addons'),
1015 + ],
1016 + 'default' => 'center center',
1017 + 'selectors' => [
1018 + '{{WRAPPER}} .king-addons-grid[data-settings*="fitRows"] .king-addons-grid-image-wrap img' => 'object-position: {{VALUE}};',
1019 + ],
1020 + 'condition' => [
1021 + 'layout_select' => 'fitRows',
1022 + ],
1023 + ]
1024 + );
1025 +
883 1026 $this->add_group_control(
884 1027 Group_Control_Image_Size::get_type(),
885 1028 [
886 1029 'name' => 'layout_image_crop',
@@ -2190,8 +2333,23 @@
2190 2333 'return_value' => 'true',
2191 2334 ]
2192 2335 );
2193 2336
2337 + $this->add_control(
2338 + 'video_play_icon',
2339 + [
2340 + 'label' => esc_html__('Video Play Icon', 'king-addons'),
2341 + 'type' => Controls_Manager::ICONS,
2342 + 'skin' => 'inline',
2343 + 'label_block' => false,
2344 + 'default' => [
2345 + 'value' => 'fas fa-play',
2346 + 'library' => 'fa-solid',
2347 + ],
2348 + 'separator' => 'before',
2349 + ]
2350 + );
2351 +
2194 2352 $this->add_control_lightbox_popup_thumbnails();
2195 2353
2196 2354 $this->add_control_lightbox_popup_thumbnails_default();
2197 2355
@@ -6705,20 +6863,314 @@
6705 6863 }
6706 6864
6707 6865 public function get_max_num_pages()
6708 6866 {
6867 + $settings = $this->get_settings_for_display();
6868 +
6869 + if ('manual' === ($settings['query_selection'] ?? '')) {
6870 + return $this->get_manual_media_max_num_pages($settings);
6871 + }
6872 +
6709 6873 $query = new WP_Query($this->get_main_query_args());
6710 - $pages = (int)ceil($query->max_num_pages);
6874 + $pages = (int) ceil($query->max_num_pages);
6711 6875 wp_reset_postdata();
6876 +
6712 6877 return $pages;
6713 6878 }
6714 6879
6880 + /**
6881 + * Returns the total page count for manual media items.
6882 + *
6883 + * @param array|null $settings Widget settings.
6884 + * @return int Total number of pages.
6885 + */
6886 + public function get_manual_media_max_num_pages($settings = null)
6887 + {
6888 + $settings = $settings ?? $this->get_settings_for_display();
6889 + $items = $this->get_manual_media_items($settings);
6890 + $total = count($items);
6891 + $per_page = (int) ($settings['query_posts_per_page'] ?? 10);
6892 +
6893 + if ($per_page <= 0 || 0 === $total) {
6894 + return 1;
6895 + }
6896 +
6897 + $offset = empty($settings['query_offset']) ? 0 : (int) $settings['query_offset'];
6898 + $remaining = max(0, $total - $offset);
6899 +
6900 + return max(1, (int) ceil($remaining / $per_page));
6901 + }
6902 +
6903 + /**
6904 + * Returns manual media items for the current pagination page.
6905 + *
6906 + * @param array $settings Widget settings.
6907 + * @return array<int, array<string, mixed>> Paginated manual media items.
6908 + */
6909 + public function get_paginated_manual_media_items($settings)
6910 + {
6911 + $items = $this->get_manual_media_items($settings);
6912 + $per_page = (int) ($settings['query_posts_per_page'] ?? 10);
6913 +
6914 + if ($per_page <= 0) {
6915 + return $items;
6916 + }
6917 +
6918 + $paged = get_query_var('paged') ? (int) get_query_var('paged') : ((int) get_query_var('page') ?: 1);
6919 + $offset = empty($settings['query_offset']) ? 0 : (int) $settings['query_offset'];
6920 + $slice_offset = $offset + ($paged - 1) * $per_page;
6921 +
6922 + return array_slice($items, $slice_offset, $per_page);
6923 + }
6924 +
6925 + /**
6926 + * Checks whether an attachment is a video file.
6927 + *
6928 + * @param int $attachment_id Attachment post ID.
6929 + * @return bool True when the attachment mime type is video.
6930 + */
6931 + public function is_video_attachment($attachment_id)
6932 + {
6933 + $mime_type = get_post_mime_type($attachment_id);
6934 +
6935 + return is_string($mime_type) && 0 === strpos($mime_type, 'video/');
6936 + }
6937 +
6938 + /**
6939 + * Returns a video mime type based on the file extension.
6940 + *
6941 + * @param string $url Video file URL.
6942 + * @return string Video mime type.
6943 + */
6944 + public function get_video_mime_from_url($url)
6945 + {
6946 + $extension = strtolower((string) pathinfo((string) wp_parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
6947 + $map = [
6948 + 'mp4' => 'video/mp4',
6949 + 'webm' => 'video/webm',
6950 + 'ogg' => 'video/ogg',
6951 + 'ogv' => 'video/ogg',
6952 + ];
6953 +
6954 + return $map[$extension] ?? 'video/mp4';
6955 + }
6956 +
6957 + /**
6958 + * Returns a poster image URL for a video attachment when available.
6959 + *
6960 + * @param int $attachment_id Video attachment ID.
6961 + * @return string Poster image URL or an empty string.
6962 + */
6963 + public function get_attachment_video_poster_url($attachment_id)
6964 + {
6965 + $image_src = wp_get_attachment_image_src($attachment_id, 'large');
6966 + if (!empty($image_src[0])) {
6967 + return $image_src[0];
6968 + }
6969 +
6970 + $metadata = wp_get_attachment_metadata($attachment_id);
6971 + if (!empty($metadata['image']['file']) && !empty($metadata['file'])) {
6972 + $upload_dir = wp_upload_dir();
6973 +
6974 + return trailingslashit($upload_dir['baseurl']) . trailingslashit(dirname($metadata['file'])) . $metadata['image']['file'];
6975 + }
6976 +
6977 + return '';
6978 + }
6979 +
6980 + /**
6981 + * Builds manual media items from the repeater or legacy gallery control.
6982 + *
6983 + * @param array $settings Widget settings.
6984 + * @return array<int, array<string, mixed>> Manual media items.
6985 + */
6986 + public function get_manual_media_items($settings)
6987 + {
6988 + $items = [];
6989 +
6990 + if (!empty($settings['query_manual_media'])) {
6991 + foreach ($settings['query_manual_media'] as $index => $row) {
6992 + $item_type = $row['item_type'] ?? 'image';
6993 + $item_image = $row['item_image'] ?? [];
6994 + $item_video = $row['item_video'] ?? [];
6995 +
6996 + if ('video' === $item_type) {
6997 + if (empty($item_video['url'])) {
6998 + continue;
6999 + }
7000 + if (empty($item_image['id']) && empty($item_image['url'])) {
7001 + continue;
7002 + }
7003 + } elseif (empty($item_image['id']) && empty($item_image['url'])) {
7004 + continue;
7005 + }
7006 +
7007 + $items[] = [
7008 + 'key' => 'manual-' . $index,
7009 + 'type' => $item_type,
7010 + 'image' => $item_image,
7011 + 'video' => $item_video,
7012 + ];
7013 + }
7014 +
7015 + return $items;
7016 + }
7017 +
7018 + if (empty($settings['query_manual_attachment'])) {
7019 + return $items;
7020 + }
7021 +
7022 + foreach ($settings['query_manual_attachment'] as $index => $attachment) {
7023 + if (empty($attachment['id'])) {
7024 + continue;
7025 + }
7026 +
7027 + $items[] = [
7028 + 'key' => 'legacy-' . $index,
7029 + 'type' => 'image',
7030 + 'image' => $attachment,
7031 + 'video' => [],
7032 + ];
7033 + }
7034 +
7035 + return $items;
7036 + }
7037 +
7038 + /**
7039 + * Renders the play icon overlay for video gallery items.
7040 + *
7041 + * @param array $settings Widget settings.
7042 + * @return void
7043 + */
7044 + public function render_video_play_icon($settings)
7045 + {
7046 + echo '<span class="king-addons-grid-video-play-icon" aria-hidden="true">';
7047 + Icons_Manager::render_icon($settings['video_play_icon'], ['aria-hidden' => 'true']);
7048 + echo '</span>';
7049 + }
7050 +
7051 + /**
7052 + * Renders a hidden HTML5 video element referenced by the lightbox.
7053 + *
7054 + * @param string $video_url Video file URL.
7055 + * @param string $element_id Hidden container element ID.
7056 + * @return void
7057 + */
7058 + public function render_video_html_element($video_url, $element_id)
7059 + {
7060 + if ('' === $video_url) {
7061 + return;
7062 + }
7063 +
7064 + echo '<div class="king-addons-grid-video-html" id="' . esc_attr($element_id) . '" style="display:none;">';
7065 + echo '<video class="lg-video-object lg-html5" controls preload="none" playsinline>';
7066 + echo '<source src="' . esc_url($video_url) . '" type="' . esc_attr($this->get_video_mime_from_url($video_url)) . '">';
7067 + echo '</video>';
7068 + echo '</div>';
7069 + }
7070 +
7071 + /**
7072 + * Renders a gallery media thumbnail for image or video items.
7073 + *
7074 + * @param array $settings Widget settings.
7075 + * @param string $item_key Unique item key for lightbox video references.
7076 + * @param string $media_type Media type: image or video.
7077 + * @param array $image_data Image or cover attachment data.
7078 + * @param string $video_url Video file URL for video items.
7079 + * @return void
7080 + */
7081 + public function render_media_thumbnail($settings, $item_key, $media_type, $image_data, $video_url = '')
7082 + {
7083 + $image_id = !empty($image_data['id']) ? (int) $image_data['id'] : 0;
7084 + $is_video = ('video' === $media_type && '' !== $video_url);
7085 + $video_html_id = 'king-addons-grid-video-' . $this->get_id() . '-' . $item_key;
7086 +
7087 + if ($image_id) {
7088 + $src = Group_Control_Image_Size::get_attachment_image_src($image_id, 'layout_image_crop', $settings);
7089 + $alt = ('' === wp_get_attachment_caption($image_id)) ? get_the_title($image_id) : wp_get_attachment_caption($image_id);
7090 + $lightbox_src = $is_video ? $src : wp_get_attachment_url($image_id);
7091 + } else {
7092 + $src = $image_data['url'] ?? '';
7093 + $alt = '';
7094 + $lightbox_src = $src;
7095 + }
7096 +
7097 + if ($is_video && '' === $src && $image_id) {
7098 + $src = $this->get_attachment_video_poster_url($image_id);
7099 + }
7100 +
7101 + if ($is_video) {
7102 + $lightbox_src = '' !== $src ? $src : $video_url;
7103 + }
7104 +
7105 + $wrap_classes = 'king-addons-grid-image-wrap';
7106 + if ($is_video) {
7107 + $wrap_classes .= ' king-addons-grid-video-lightbox-item';
7108 + }
7109 +
7110 + echo '<div class="' . esc_attr($wrap_classes) . '" data-src="' . esc_url($lightbox_src) . '"';
7111 + if ($is_video) {
7112 + echo ' data-poster="' . esc_url($lightbox_src) . '"';
7113 + echo ' data-html="#' . esc_attr($video_html_id) . '"';
7114 + echo ' data-kng-video-url="' . esc_url($video_url) . '"';
7115 + }
7116 + echo '>';
7117 +
7118 + if ('' !== $src) {
7119 + echo '<img src="' . esc_url($src) . '" alt="' . esc_attr($alt) . '" class="king-addons-animation-timing-' . esc_attr($settings['image_effects_animation_timing']) . '">';
7120 + } elseif ($is_video) {
7121 + echo '<span class="king-addons-grid-video-cover-placeholder" aria-hidden="true"></span>';
7122 + }
7123 +
7124 + if ($is_video) {
7125 + $this->render_video_play_icon($settings);
7126 + }
7127 +
7128 + echo '</div>';
7129 +
7130 + if ($is_video) {
7131 + $this->render_video_html_element($video_url, $video_html_id);
7132 + }
7133 + }
7134 +
7135 + /**
7136 + * Renders one gallery article for manual media items.
7137 + *
7138 + * @param array $settings Widget settings.
7139 + * @param array $item Manual media item data.
7140 + * @return void
7141 + */
7142 + public function render_manual_grid_item($settings, $item)
7143 + {
7144 + $item_type = $item['type'] ?? 'image';
7145 + $video_url = ('video' === $item_type && !empty($item['video']['url'])) ? $item['video']['url'] : '';
7146 + $post_id = !empty($item['image']['id']) ? (int) $item['image']['id'] : 0;
7147 + $item_classes = ['king-addons-grid-item', 'elementor-clearfix'];
7148 +
7149 + if ('video' === $item_type) {
7150 + $item_classes[] = 'king-addons-grid-item-video';
7151 + }
7152 +
7153 + echo '<article class="' . esc_attr(implode(' ', $item_classes)) . '">';
7154 + echo '<div class="king-addons-grid-item-inner">';
7155 + $this->get_elements_by_location('above', $settings, $post_id);
7156 + echo '<div class="king-addons-grid-media-wrap' . esc_attr($this->get_image_effect_class($settings)) . '">';
7157 + $this->render_media_thumbnail($settings, $item['key'], $item_type, $item['image'] ?? [], $video_url);
7158 + echo '<div class="king-addons-grid-media-hover king-addons-animation-wrap">';
7159 + $this->render_media_overlay($settings, $post_id);
7160 + $this->get_elements_by_location('over', $settings, $post_id);
7161 + echo '</div></div>';
7162 + $this->get_elements_by_location('below', $settings, $post_id);
7163 + echo '</div></article>';
7164 + }
7165 +
6715 7166 public function get_main_query_args()
6716 7167 {
6717 7168 $s = $this->get_settings_for_display();
6718 7169 $author = !empty($s['query_author']) ? implode(',', $s['query_author']) : '';
6719 7170 $paged = get_query_var('paged') ? get_query_var('paged') : (get_query_var('page') ? get_query_var('page') : 1);
6720 - $offset = ($paged - 1) * $s['query_posts_per_page'] + (empty($s['query_offset']) ? 0 : $s['query_offset']);
7171 + $offset = ($paged - 1) * Core::jsNumber($s, 'query_posts_per_page', 10)
7172 + + (empty($s['query_offset']) ? 0 : Core::jsNumber($s, 'query_offset', 0));
6721 7173
6722 7174 // Remove premium-only randomize if not available
6723 7175 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
6724 7176 $s['query_randomize'] = '';
@@ -6724,13 +7176,18 @@
6724 7176 $s['query_randomize'] = '';
6725 7177 $s['order_posts'] = 'date';
6726 7178 }
6727 7179 $order_by = $s['query_randomize'] !== '' ? $s['query_randomize'] : $s['order_posts'];
6728 - if ('manual' === $s['query_selection']) $order_by = 'post__in';
7180 + if ('manual' === $s['query_selection']) {
7181 + $order_by = 'post__in';
7182 + }
6729 7183
7184 + $include_videos = !empty($s['query_include_videos']);
7185 + $post_mime_type = $include_videos ? ['image', 'video'] : 'image';
7186 +
6730 7187 $args = [
6731 7188 'post_type' => 'attachment',
6732 - 'post_mime_type' => 'image', // <-- Only images
7189 + 'post_mime_type' => $post_mime_type,
6733 7190 'post_status' => 'inherit',
6734 7191 'tax_query' => $this->get_tax_query_args(),
6735 7192 'post__not_in' => $s['query_exclude_attachment'],
6736 7193 'posts_per_page' => $s['query_posts_per_page'],
@@ -6741,17 +7198,29 @@
6741 7198 ];
6742 7199
6743 7200 if ('manual' === $s['query_selection']) {
6744 7201 $post_ids = [];
6745 - if (!empty($s['query_manual_attachment'])) {
7202 +
7203 + if (!empty($s['query_manual_media'])) {
7204 + foreach ($this->get_manual_media_items($s) as $item) {
7205 + if (!empty($item['image']['id'])) {
7206 + $post_ids[] = (int) $item['image']['id'];
7207 + }
7208 + }
7209 + } elseif (!empty($s['query_manual_attachment'])) {
6746 7210 foreach ($s['query_manual_attachment'] as $attachment) {
6747 7211 $post_ids[] = $attachment['id'];
6748 7212 }
6749 7213 }
7214 +
7215 + if (empty($post_ids)) {
7216 + $post_ids = [0];
7217 + }
7218 +
6750 7219 $orderby = ('' === $s['query_randomize']) ? 'post__in' : 'rand';
6751 7220 $args = [
6752 7221 'post_type' => 'attachment',
6753 - 'post_mime_type' => 'image', // <-- Only images
7222 + 'post_mime_type' => $post_mime_type,
6754 7223 'post_status' => 'inherit',
6755 7224 'post__in' => $post_ids,
6756 7225 'orderby' => $orderby,
6757 7226 'posts_per_page' => $s['query_posts_per_page'],
@@ -6781,15 +7250,21 @@
6781 7250 }
6782 7251
6783 7252 public function get_animation_class($data, $object)
6784 7253 {
6785 - $class = '';
6786 - if ('none' !== $data[$object . '_animation']) {
6787 - $class .= ' king-addons-' . $object . '-' . $data[$object . '_animation'];
6788 - $class .= ' king-addons-anim-size-' . $data[$object . '_animation_size'];
6789 - $class .= ' king-addons-animation-timing-' . $data[$object . '_animation_timing'];
6790 - if ('yes' === $data[$object . '_animation_tr']) $class .= ' king-addons-anim-transparency';
7254 + $animation = Grid_Ajax_Security::sanitize_animation($data[$object . '_animation'] ?? 'none');
7255 + if ('none' === $animation) {
7256 + return '';
6791 7257 }
7258 +
7259 + $class = ' king-addons-' . sanitize_html_class((string) $object) . '-' . $animation;
7260 + $class .= ' king-addons-anim-size-' . Grid_Ajax_Security::sanitize_animation_size($data[$object . '_animation_size'] ?? 'large');
7261 + $class .= ' king-addons-animation-timing-' . Grid_Ajax_Security::sanitize_animation_timing($data[$object . '_animation_timing'] ?? 'ease-default');
7262 +
7263 + if ('yes' === Grid_Ajax_Security::sanitize_yes_no_switcher($data[$object . '_animation_tr'] ?? '')) {
7264 + $class .= ' king-addons-anim-transparency';
7265 + }
7266 +
6792 7267 return $class;
6793 7268 }
6794 7269
6795 7270 public function get_image_effect_class($s)
@@ -6813,18 +7288,28 @@
6813 7288
6814 7289 public function render_post_thumbnail($settings)
6815 7290 {
6816 7291 $id = get_the_ID();
6817 - $src = Group_Control_Image_Size::get_attachment_image_src($id, 'layout_image_crop', $settings);
6818 - $alt = ('' === wp_get_attachment_caption($id)) ? get_the_title() : wp_get_attachment_caption($id);
6819 - echo '<div class="king-addons-grid-image-wrap" data-src="' . esc_url(wp_get_attachment_url($id)) . '">';
6820 - echo '<img src="' . esc_url($src) . '" alt="' . wp_kses_post($alt) . '" class="king-addons-animation-timing-' . esc_html($settings['image_effects_animation_timing']) . '">';
6821 - echo '</div>';
7292 + $is_video = $this->is_video_attachment($id);
7293 + $video_url = $is_video ? (string) wp_get_attachment_url($id) : '';
7294 +
7295 + $this->render_media_thumbnail(
7296 + $settings,
7297 + 'attachment-' . $id,
7298 + $is_video ? 'video' : 'image',
7299 + ['id' => $id],
7300 + $video_url
7301 + );
6822 7302 }
6823 7303
6824 - public function render_media_overlay($s)
7304 + public function render_media_overlay($s, $post_id = 0)
6825 7305 {
6826 - echo '<div class="king-addons-grid-media-hover-bg ' . $this->get_animation_class($s, 'overlay') . '" data-url="' . esc_url(get_the_permalink(get_the_ID())) . '">';
7306 + if (!$post_id) {
7307 + $post_id = get_the_ID();
7308 + }
7309 +
7310 + $overlay_url = $post_id ? get_the_permalink($post_id) : '#';
7311 + echo '<div class="king-addons-grid-media-hover-bg ' . esc_attr($this->get_animation_class($s, 'overlay')) . '" data-url="' . esc_url($overlay_url) . '">';
6827 7312 if (king_addons_freemius()->can_use_premium_code__premium_only()) {
6828 7313 if ('' !== $s['overlay_image']['url']) {
6829 7314 echo '<img src="' . esc_url($s['overlay_image']['url']) . '">';
6830 7315 }
@@ -6959,9 +7444,12 @@
6959 7444 public function render_post_lightbox($s, $class, $post_id)
6960 7445 {
6961 7446 echo '<div class="' . esc_attr($class) . '"><div class="inner-block">';
6962 7447 $lightbox_source = get_the_post_thumbnail_url($post_id);
6963 - if ('audio' === get_post_format()) {
7448 +
7449 + if ($this->is_video_attachment($post_id)) {
7450 + $lightbox_source = $this->get_attachment_video_poster_url($post_id);
7451 + } elseif ('audio' === get_post_format()) {
6964 7452 if ('meta' === $s['element_lightbox_pfa_select']) {
6965 7453 $meta_value = get_post_meta($post_id, $s['element_lightbox_pfa_meta'], true);
6966 7454 if (false === strpos($meta_value, '<iframe ')) {
6967 7455 add_filter('oembed_result', $media_grid_filter = function ($html) {
@@ -6990,9 +7478,11 @@
6990 7478 }
6991 7479 if (isset($video_url)) $lightbox_source = $video_url;
6992 7480 }
6993 7481 }
6994 - if (!$lightbox_source) $lightbox_source = wp_get_attachment_url($post_id);
7482 + if (!$lightbox_source && !$this->is_video_attachment($post_id)) {
7483 + $lightbox_source = wp_get_attachment_url($post_id);
7484 + }
6995 7485 echo '<span data-src="' . esc_url($lightbox_source) . '">';
6996 7486 if ('before' === $s['element_extra_text_pos']) {
6997 7487 echo '<span class="king-addons-grid-extra-text-left">' . esc_html($s['element_extra_text']) . '</span>';
6998 7488 }
@@ -7406,9 +7896,9 @@
7406 7896 'selector' => '.king-addons-grid-image-wrap',
7407 7897 'iframeMaxWidth' => '60%',
7408 7898 'hash' => false,
7409 7899 'autoplay' => $s['lightbox_popup_autoplay'],
7410 - 'pause' => $s['lightbox_popup_pause'] * 1000,
7900 + 'pause' => Core::jsNumber($s, 'lightbox_popup_pause', 5) * 1000,
7411 7901 'progressBar' => $s['lightbox_popup_progressbar'],
7412 7902 'counter' => $s['lightbox_popup_counter'],
7413 7903 'controls' => $s['lightbox_popup_arrows'],
7414 7904 'getCaptionFromTitleOrAlt' => false,
@@ -7477,8 +7967,43 @@
7477 7967
7478 7968 protected function render()
7479 7969 {
7480 7970 $s = $this->get_settings();
7971 +
7972 + if ('manual' === $s['query_selection']) {
7973 + $manual_items = $this->get_paginated_manual_media_items($s);
7974 +
7975 + if (empty($manual_items)) {
7976 + echo '<h2>' . esc_html($s['query_not_found_text']) . '</h2>';
7977 + return;
7978 + }
7979 +
7980 + if ('slider' !== $s['layout_select']) {
7981 + $this->render_grid_filters($s);
7982 + $this->add_grid_settings($s);
7983 + $render_attribute = $this->get_render_attribute_string('grid-settings');
7984 + } else {
7985 + $this->add_slider_settings($s);
7986 + $render_attribute = $this->get_render_attribute_string('slider-settings');
7987 + }
7988 +
7989 + echo '<section class="king-addons-grid king-addons-media-grid elementor-clearfix" ' . $render_attribute . '>';
7990 + foreach ($manual_items as $item) {
7991 + $this->render_manual_grid_item($s, $item);
7992 + }
7993 + echo '</section>';
7994 +
7995 + if ('slider' === $s['layout_select']) {
7996 + echo '<div class="king-addons-grid-slider-arrow-container">';
7997 + echo '<div class="king-addons-grid-slider-prev-arrow king-addons-grid-slider-arrow" id="king-addons-grid-slider-prev-' . esc_attr($this->get_id()) . '">' . Core::getIcon($s['layout_slider_nav_icon'], '') . '</div>';
7998 + echo '<div class="king-addons-grid-slider-next-arrow king-addons-grid-slider-arrow" id="king-addons-grid-slider-next-' . esc_attr($this->get_id()) . '">' . Core::getIcon($s['layout_slider_nav_icon'], '') . '</div>';
7999 + echo '</div><div class="king-addons-grid-slider-dots"></div>';
8000 + }
8001 +
8002 + $this->render_grid_pagination($s);
8003 + return;
8004 + }
8005 +
7481 8006 $posts = new WP_Query($this->get_main_query_args());
7482 8007 if ($posts->have_posts()) {
7483 8008 if ('slider' !== $s['layout_select']) {
7484 8009 $this->render_grid_filters($s);
@@ -7490,12 +8015,19 @@
7490 8015 }
7491 8016 echo '<section class="king-addons-grid king-addons-media-grid elementor-clearfix" ' . $render_attribute . '>';
7492 8017 while ($posts->have_posts()) {
7493 8018 $posts->the_post();
7494 - // Skip non-image attachments (in case)
7495 - if (!wp_attachment_is_image(get_the_ID())) continue;
8019 + $attachment_id = get_the_ID();
7496 8020
7497 - $post_class = implode(' ', get_post_class('king-addons-grid-item elementor-clearfix', get_the_ID()));
8021 + if (!wp_attachment_is_image($attachment_id) && !$this->is_video_attachment($attachment_id)) {
8022 + continue;
8023 + }
8024 +
8025 + $post_class = implode(' ', get_post_class('king-addons-grid-item elementor-clearfix', $attachment_id));
8026 + if ($this->is_video_attachment($attachment_id)) {
8027 + $post_class .= ' king-addons-grid-item-video';
8028 + }
8029 +
7498 8030 echo '<article class="' . esc_attr($post_class) . '">';
7499 8031 echo '<div class="king-addons-grid-item-inner">';
7500 8032 $this->get_elements_by_location('above', $s, get_the_ID());
7501 8033 echo '<div class="king-addons-grid-media-wrap' . $this->get_image_effect_class($s) . ' ">';