PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.36
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.36
3.36 3.35 3.34 3.33 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.26 2.27 2.28 2.29 2.30 2.31 2.32 2.33 2.34 2.40 2.41 2.42 2.43 2.44 All 141 releases
← All changes | Layouts/Grid.php +454 -381 2.433.36 View file →
@@ -1,101 +1,88 @@
1 1 <?php
2 +
2 3 namespace Photonic_Plugin\Layouts;
3 4
5 +use Photonic_Plugin\Components\Album_List;
6 +use Photonic_Plugin\Components\Grid_Anchor;
7 +use Photonic_Plugin\Components\Grid_Figure;
8 +use Photonic_Plugin\Components\Grid_Image;
9 +use Photonic_Plugin\Components\Pagination;
10 +use Photonic_Plugin\Components\Photo_List;
11 +use Photonic_Plugin\Core\Photonic;
4 12 use Photonic_Plugin\Layouts\Features\Can_Use_Lightbox;
5 -use Photonic_Plugin\Modules\Core;
13 +use Photonic_Plugin\Platforms\Base;
14 +use Photonic_Plugin\Components\Album;
15 +use Photonic_Plugin\Components\Photo;
6 16
7 -require_once('Level_One_Gallery.php');
8 -require_once('Level_Two_Gallery.php');
17 +require_once 'Level_One_Gallery.php';
18 +require_once 'Level_Two_Gallery.php';
9 19
10 -require_once('Features/Can_Use_Lightbox.php');
20 +require_once PHOTONIC_PATH . '/Components/Grid_Image.php';
11 21
22 +require_once 'Features/Can_Use_Lightbox.php';
23 +
24 +/**
25 + * Class Grid
26 + * This is the generic Grid layout for Photonic. This handles all grids except for slideshows. The following approaches are used for layouts:
27 + * - CSS: Used for square, circle, masonry and justified grids (only when all images have sizes - pretty much every case except Instagram or SmugMug / Zenfolio galleries with missing thumbnails)
28 + * - JS: Used for justified grids (only when at least one image is missing a size, e.g. Instagram with all sizes missing, or SmugMug with missing album thumbnails) and Mosaic
29 + * The Justified Grid CSS approach is based on the solution provided here: https://stackoverflow.com/a/49107319
30 + *
31 + * @package Photonic_Plugin\Layouts
32 + */
12 33 class Grid extends Core_Layout implements Level_One_Gallery, Level_Two_Gallery {
13 34 use Can_Use_Lightbox;
14 35
15 - private static $instance = null;
16 -
17 - public static function get_instance() {
18 - if (self::$instance == null) {
19 - self::$instance = new Grid();
20 - }
21 - return self::$instance;
22 - }
23 -
24 36 /**
25 - * Generates the HTML for the lowest level gallery, i.e. the photos. This is used for both, in-page and popup displays.
37 + * Generates the HTML for the lowest level gallery, i.e. the photos. This is used for local, modal and template displays.
26 38 * The code for the random layouts is handled in JS, but just the HTML markers for it are provided here.
27 39 *
28 - * @param $photos
29 - * @param array $options
30 - * @param $short_code
31 - * @param $module Core
40 + * @param Photo_List $photo_list
41 + * @param array $short_code
42 + * @param $module Base
32 43 * @return string
33 44 */
34 - function generate_level_1_gallery($photos, $options, $short_code, $module) {
45 + public function generate_level_1_gallery(Photo_List $photo_list, array $short_code, Base $module): string {
35 46 $module->push_to_stack('Generate level 1 gallery');
36 47
48 + $short_code = array_merge($this->common_parameters, $short_code);
49 +
50 + $photos = $photo_list->photos;
51 +
37 52 $lightbox = self::get_lightbox();
38 53
39 54 $layout = $short_code['layout'];
40 - $columns = !empty($short_code['columns']) && ($layout !== 'random' && $layout !== 'mosaic') ? $short_code['columns'] : 'auto';
41 - $display = !empty($short_code['display']) ? $short_code['display'] : 'in-page';
42 - $more = !empty($short_code['more']) ? esc_attr($short_code['more']) : '';
43 - $more = (empty($more) && !empty($short_code['photo_more'])) ? esc_attr($short_code['photo_more']) : $more;
44 - $panel = !empty($short_code['panel']) ? $short_code['panel'] : '';
55 + $columns = sanitize_text_field(!empty($short_code['columns']) && ('random' !== $layout && 'mosaic' !== $layout) ? $short_code['columns'] : 'auto');
56 + $more = !empty($short_code['more']) ? sanitize_text_field($short_code['more']) : '';
57 + $more = (empty($more) && !empty($short_code['photo_more'])) ? sanitize_text_field($short_code['photo_more']) : $more;
58 + $title_position = empty($short_code['title_position']) ? $photo_list->title_position : sanitize_text_field($short_code['title_position']);
59 + $row_constraints = $photo_list->row_constraints;
60 + $pagination = $photo_list->pagination;
61 + $indent = $photo_list->indent;
45 62
46 - $title_position = empty($short_code['title_position']) ? $options['title_position'] : $short_code['title_position'];
47 - $row_constraints = isset($options['row_constraints']) && is_array($options['row_constraints']) ? $options['row_constraints'] : [];
48 - $sizes = isset($options['sizes']) && is_array($options['sizes']) ? $options['sizes'] : [];
49 - $type = !empty($options['type']) ? $options['type'] : 'photo';
50 - $parent = !empty($options['parent']) ? $options['parent'] : 'stream';
51 - $pagination = isset($options['level_2_meta']) && is_array($options['level_2_meta']) ? $options['level_2_meta'] : [];
52 - $indent = !isset($options['indent']) ? "\t" : $options['indent'];
63 + $display = sanitize_text_field(!empty($short_code['display']) ? $short_code['display'] : 'local');
64 + $panel = !empty($short_code['panel']) ? sanitize_text_field($short_code['panel']) : '';
65 + $parent = $photo_list->parent;
53 66
54 - list($container_id, $container_end) = $this->get_container_details($short_code, $module);
67 + list($container_start_id, $container_end_id) = $this->get_container_details($short_code, $module);
68 + $non_standard = in_array($layout, ['random', 'masonry', 'masonry-horizontal', 'mosaic'], true);
69 + $gallery_column_class = $this->get_gallery_column_class($non_standard, $columns, $row_constraints);
70 + $effect = esc_attr($this->get_thumbnail_effect($short_code, $layout, $title_position));
55 71
56 - $non_standard = $layout == 'random' || $layout == 'masonry' || $layout == 'mosaic';
72 + $gallery_figure_classes = ['photonic-level-1', 'photonic-thumb'];
57 73
58 - $col_class = '';
59 - if (absint($columns)) {
60 - $col_class = 'photonic-gallery-'.$columns.'c';
61 - }
74 + $lightbox_attributes = $lightbox->get_gallery_attributes($panel, $module); // Function returns escaped values
75 + $anchor_classes = $lightbox_attributes['class'] ?: [];
76 + $anchor_rel = $lightbox_attributes['rel'] ?: [];
77 + $anchor_lightbox_data = $lightbox_attributes['specific'] ?: [];
62 78
63 - if ($col_class == '' && $row_constraints['constraint-type'] == 'padding') {
64 - $col_class = 'photonic-pad-photos';
65 - }
66 - else if ($col_class == '') {
67 - $col_class = 'photonic-gallery-'.$row_constraints['count'].'c';
68 - }
69 - $col_class .= ' photonic-level-1 photonic-thumb photonic-thumb-'.$layout;
70 -
71 - $link_attributes = $lightbox->get_gallery_attributes($panel, $module);
72 - $link_attributes_text = $this->get_text_from_link_attributes($link_attributes);
73 -
74 - $effect = $this->get_thumbnail_effect($short_code, $layout, $title_position);
75 - $ul_class = "class='title-display-$title_position photonic-level-1-container ".($non_standard ? 'photonic-'.$layout.'-layout' : 'photonic-standard-layout')." photonic-thumbnail-effect-$effect'";
76 - if ($display == 'popup') {
77 - $ul_class = "class='slideshow-grid-panel lib-{$this->library} photonic-level-1-container title-display-$title_position'";
78 - }
79 -
80 79 $ret = '';
81 - if (!$non_standard && $display != 'popup') {
82 - $container_tag = 'ul';
83 - $element_tag = 'li';
84 - }
85 - else {
86 - $container_tag = 'div';
87 - $element_tag = 'div';
88 - }
89 80
90 - list($pagination_data, $pagination, $columns_data) = $this->get_gallery_data_attributes($short_code, $module, $pagination, $columns);
91 -
92 - $start_with = "$indent<$container_tag $container_id $ul_class $pagination_data $columns_data>\n";
93 - $ret .= $start_with;
94 -
95 81 global $photonic_external_links_in_new_tab;
96 82 if (!empty($photonic_external_links_in_new_tab)) {
97 83 $target = " target='_blank' ";
84 + $anchor_lightbox_data['target'] = '_blank';
98 85 }
99 86 else {
100 87 $target = '';
101 88 }
@@ -100,162 +87,154 @@
100 87 $target = '';
101 88 }
102 89
103 90 $counter = 0;
104 - $thumbnail_class = " class='$layout' ";
105 91
106 - $element_start = "$indent\t<".$element_tag.' class="photonic-'.$module->provider.'-image photonic-'.$module->provider.'-'.$type.' '.$col_class.'">'."\n";
92 + $layout_engine = $this->get_layout_engine($module, $short_code);
93 + $all_sizes_present = strtolower($layout_engine) === 'css';
107 94
108 - $default_lightbox_text = apply_filters('photonic_default_lightbox_text', esc_attr__('View', 'photonic'));
109 - foreach ($photos as $photo) {
95 + $grid_figures = [];
96 +
97 + /** @var Photo $photo */
98 + foreach ($photos as $idx => $photo) {
110 99 $counter++;
111 100
112 - $thumb = ($non_standard && $display == 'in-page') ? (isset($photo['tile_image']) ? $photo['tile_image'] : $photo['main_image']) : $photo['thumbnail'];
113 - $orig = empty($photo['video']) ? $photo['main_image'] : $photo['video'];
114 -// $orig = $photo['main_image'];
115 - $url = isset($photo['main_page']) ? $photo['main_page'] : '';
116 - $title = esc_attr($photo['title']);
117 - $description = esc_attr($photo['description']);
118 - $alt = esc_attr($photo['alt_title']);
119 - $orig = $this->library == 'none' ? $url : $orig;
120 -
121 - $title = empty($title) ? ((empty($alt) && $module->link_lightbox_title && $this->library != 'thickbox') ? $default_lightbox_text : $alt) : $title;
122 - $ret .= $element_start;
123 -
124 - $deep_value = 'gallery[photonic-'.$module->provider.'-'.$parent.'-'.(empty($panel) ? $module->gallery_index : $panel).']/'.(empty($photo['id']) ? $counter : $photo['id']).'/';
125 - $deep_link = ' data-photonic-deep="'.$deep_value.'" ';
126 -
127 - $buy = '';
128 - if (!empty($photo['buy_link']) && $module->show_buy_link) {
129 - $buy = ' data-photonic-buy="'.$photo['buy_link'].'" ';
101 + $figure_data = [];
102 + if ('masonry-horizontal' === $layout) {
103 + $figure_data['data-photonic-idx'] = $idx;
130 104 }
131 105
132 - $style = [];
133 - if (!empty($sizes['thumb-width'])) $style[] = 'width:'.$sizes['thumb-width'].'px';
134 - if (!empty($sizes['thumb-height'])) $style[] = 'height:'.$sizes['thumb-height'].'px';
135 - if (!empty($style)) $style = 'style="'.implode(';', $style).'"'; else $style = '';
136 - if ($module->link_lightbox_title && $this->library != 'thickbox' && !empty($url)) {
137 - $title_link_start = esc_attr("<a href='$url' $target>");
138 - $title_link_end = esc_attr("</a>");
106 + $additional_image_data = [];
107 + $styles = [];
108 + if ($non_standard && 'local' === $display) {
109 + $thumb = $photo->tile_image ?: $photo->main_image;
110 + if (!empty($photo->tile_size)) {
111 + $inbuilt_sizes = $photo->tile_size;
112 + }
113 + elseif (!empty($photo->main_size)) {
114 + $inbuilt_sizes = $photo->main_size;
115 + }
139 116 }
140 117 else {
141 - $title_link_start = '';
142 - $title_link_end = '';
118 + $thumb = $photo->thumbnail;
119 + if (!empty($photo->thumb_size)) {
120 + $inbuilt_sizes = $photo->thumb_size;
121 + }
143 122 }
144 123
145 - if (!empty($short_code['caption']) && ($short_code['caption'] == 'desc' || ($short_code['caption'] == 'title-desc' && empty($title)) || ($short_code['caption'] == 'desc-title' && !empty($description)))) {
146 - $title = $description;
124 + if (!empty($inbuilt_sizes)) {
125 + if ('random' === $layout) {
126 + $styles['--dw'] = $inbuilt_sizes['w'];
127 + $styles['--dh'] = $inbuilt_sizes['h'];
128 + }
147 129 }
148 - else if (empty($short_code['caption']) || (($short_code['caption'] == 'desc-title' && empty($title)) || $short_code['caption'] == 'none')) {
149 - $title = '';
130 + else {
131 + $all_sizes_present = false;
150 132 }
151 133
152 - if (!empty($title)) {
153 - $title_markup = $title_link_start.esc_attr($title).$title_link_end;
154 - $title_markup = apply_filters('photonic_lightbox_title_markup', $title_markup);
134 + $deep_value = 'gallery[photonic-' . $module->provider . '-' . $parent . '-' . ($panel ?: $module->gallery_index) . ']/' . ($photo->id ?: $counter) . '/';
135 + $additional_image_data['data-photonic-deep'] = esc_attr($deep_value);
136 +
137 + if (!empty($photo->buy_link) && $module->show_buy_link) {
138 + $additional_image_data['data-photonic-buy'] = esc_url($photo->buy_link);
155 139 }
156 - else {
157 - $title_markup = '';
158 - }
159 140
160 - if ($module->link_lightbox_title && $this->library != 'thickbox' && !empty($photo['buy_link']) && $module->show_buy_link) {
161 - $buy_link = esc_attr('<a class="photonic-buy-link" href="'.$photo['buy_link'].'" target="_blank" title="'.__('Buy', 'photonic').'"><div class="icon-buy"></div></a>');
162 - $title_markup .= $buy_link;
163 - }
141 + $title = wp_kses_post($photo->title);
142 + $description = wp_kses_post($photo->description);
143 + $alt = wp_kses_post($photo->alt_title);
164 144
145 + $titles = [
146 + 'title' => $title,
147 + 'desc' => $description,
148 + 'alt' => $alt,
149 + ];
150 +
151 + $title = $this->get_title($title, $titles, $short_code);
152 +
153 + $title_markup = $lightbox->get_lightbox_title($photo, $module, $title, $alt, $target);
154 + $additional_image_data['data-title'] = $title_markup;
155 +
165 156 $shown_title = '';
166 - if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick']) && !empty($title)) {
167 - $shown_title = '<div class="photonic-title-info"><div class="photonic-photo-title photonic-title">'.wp_specialchars_decode($title, ENT_QUOTES).'</div></div>';
157 + if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick'], true) && !empty($title)) {
158 + // Convoluted... we want to remove any funky markup from $title, so wp_filter_nohtml_kses is used. But that does an `addslashes`, which causes more funky markup. So we use stripslashes, but then we decode the special characters.
159 + // $shown_title = '<figcaption class="photonic-title-info"><div class="photonic-photo-title photonic-title">' . wp_specialchars_decode(stripslashes(wp_filter_nohtml_kses($title)), ENT_QUOTES) . '</div></figcaption>';
160 + $shown_title = '<figcaption class="photonic-title-info"><div class="photonic-photo-title photonic-title">' . wp_specialchars_decode(stripslashes(wp_kses($title, Photonic::$safe_description_tags)), ENT_QUOTES) . '</div></figcaption>';
168 161 }
169 162
170 - $photo_data = ['title' => $title_markup, 'deep' => $deep_value, 'raw_title' => $title, 'href' => $orig];
171 - if (!empty($photo['download'])) {
172 - $photo_data['download'] = $photo['download'];
163 + $photo_data = ['title' => $title_markup, 'deep' => $deep_value, 'raw_title' => esc_attr($title)];
164 + if (!empty($photo->download)) {
165 + $photo_data['download'] = esc_url($photo->download);
173 166 }
174 - if (!empty($photo['video'])) {
175 - $photo_data['video'] = $photo['video'];
167 + if (!empty($photo->video)) {
168 + $photo_data['video'] = esc_url($photo->video);
169 + $photo_data['poster'] = esc_url($photo->main_image);
176 170 }
177 171 else {
178 - $photo_data['image'] = $photo['main_image'];
172 + $photo_data['image'] = esc_url($photo->main_image);
179 173 }
180 - $photo_data['provider'] = $module->provider;
181 - $photo_data['gallery_index'] = $module->gallery_index;
182 - $photo_data['id'] = $photo['id'];
174 + $photo_data['id'] = $photo->id;
175 + $photo_data['thumbnail'] = $thumb;
183 176
184 - $lb_specific_data = $lightbox->get_photo_attributes($photo_data, $module);
185 - if (!empty($photo['video'])) {
186 - $lb_specific_data .= ' data-photonic-media-type="video" ';
177 + if (!empty($photo->main_size)) {
178 + $photo_data['width'] = $photo->main_size['w'];
179 + $photo_data['height'] = $photo->main_size['h'];
187 180 }
188 - else {
189 - $lb_specific_data .= ' data-photonic-media-type="image" ';
181 +
182 + $image_lightbox_data = $lightbox->get_photo_attributes($photo_data, $module);
183 +
184 + if ('tooltip' === $title_position) {
185 + $additional_image_data['data-photonic-tooltip'] = esc_attr($title);
190 186 }
191 - if (!empty($photo['video']) && $this->library == 'lightgallery') {
192 - $video_id = $module->provider.'-'.$module->gallery_index.'-'.$photo['id'];
193 - $ret .= $indent."\t\t".'<div style="display:none;" id="photonic-video-'.$video_id.'">'."\n";
194 - $ret .= $indent."\t\t\t".'<video class="lg-video-object lg-html5 photonic" controls preload="none">'."\n";
195 - $ret .= $indent."\t\t\t\t".'<source src="'.$photo['video'].'" type="'.(!empty($photo['mime']) ? $photo['mime']: 'video/mp4').'">'."\n";
196 - $ret .= $indent."\t\t\t\t".esc_html__('Your browser does not support HTML5 videos.', 'photonic')."\n";
197 - $ret .= $indent."\t\t\t".'</video>'."\n";
198 - $ret .= $indent."\t\t".'</div>'."\n";
199 187
200 - $orig = ''; // href should be blank
201 - }
202 - else if (!empty($photo['video']) && (in_array($this->library, ['colorbox', 'fancybox', 'fancybox2', 'magnific', 'photoswipe', 'swipebox',])
203 - || (in_array($this->library, ['fancybox3']) && in_array($module->provider, ['flickr', 'google']))
204 - )) {
205 - $video_id = $module->provider.'-'.$module->gallery_index.'-'.$photo['id'];
206 - $ret .= $indent."\t\t".'<div class="photonic-html5-external" id="photonic-video-'.$video_id.'">'."\n";
207 - $ret .= $indent."\t\t\t".'<video class="photonic" controls preload="none">'."\n";
208 - $ret .= $indent."\t\t\t\t".'<source src="'.$photo['video'].'" type="'.(!empty($photo['mime']) ? $photo['mime']: 'video/mp4').'">'."\n";
209 - $ret .= $indent."\t\t\t\t".esc_html__('Your browser does not support HTML5 videos.', 'photonic')."\n";
210 - $ret .= $indent."\t\t\t".'</video>'."\n";
211 - $ret .= $indent."\t\t".'</div>'."\n";
188 + $grid_image = new Grid_Image();
189 + $grid_image->alt = esc_attr($alt ?: wp_kses_post($photo->title)); // alt and title are the same for everything except WP galleries. This has been put in for cases where users don't specify an alt on WP galleries.
190 + $grid_image->src = $thumb;
191 + $grid_image->classes = [sanitize_html_class($layout)];
192 + $grid_image->dimensions = $inbuilt_sizes ?? [];
212 193
213 - $orig = '#photonic-video-'.$video_id;
214 - }
194 + $grid_anchor = new Grid_Anchor();
195 + $grid_anchor->href = $lightbox->get_grid_link($photo, $short_code, $module); // CANNOT esc_url $lightbox->get_grid_link(...), since it sometimes returns a URL, and sometimes a "#" location. Instead, within get_grid_link there is either esc_attr or esc_url
196 + $grid_anchor->classes = $anchor_classes;
197 + $grid_anchor->rel = $anchor_rel;
198 + $grid_anchor->data = array_merge(
199 + $anchor_lightbox_data,
200 + $image_lightbox_data,
201 + $additional_image_data
202 + );
203 + $grid_anchor->title = 'none' !== $title_position ? esc_attr($title) : '';
204 + $grid_anchor->figcaption = $shown_title;
205 + $grid_anchor->image = $grid_image;
206 + $grid_anchor->indent = $indent;
215 207
216 - if ($this->library == 'magnific') {
217 - $magnific = !empty($photo['video']) ? 'mfp-inline' : 'mfp-image';
218 - $link_attributes['class']['magnific'] = $magnific;
219 - $link_attributes_text = $this->get_text_from_link_attributes($link_attributes);
220 - }
208 + $grid_figure = new Grid_Figure();
209 + $grid_figure->classes = $gallery_figure_classes;
210 + $grid_figure->styles = $styles;
211 + $grid_figure->data = $figure_data;
212 + $grid_figure->video_markup = $lightbox->get_video_markup($photo, $module, $indent);
213 + $grid_figure->anchor = $grid_anchor;
214 + $grid_figure->indent = $indent;
221 215
222 - if ($title_position == 'tooltip') {
223 - $tooltip = 'data-photonic-tooltip="'.esc_attr($title).'" ';
224 - }
225 - else {
226 - $tooltip = '';
227 - }
228 - $ret .= $indent."\t\t".'<a '.$link_attributes_text.' href="'.$orig.'" title="'.($title_position != 'none' ? esc_attr($title) : '').'" data-title="'.$title_markup.'" '.$tooltip.' '.$lb_specific_data.' '.$target.$deep_link.$buy.">\n";
229 - $ret .= $indent."\t\t\t".'<img alt="'.$alt.'" src="'.$thumb.'" '.$style.$thumbnail_class." loading='eager'/>\n";
230 - $ret .= $indent."\t\t\t".$shown_title."\n";
231 - $ret .= $indent."\t\t"."</a>\n";
232 - $ret .= $indent."\t"."</$element_tag>\n";
216 + $grid_figures[] = $grid_figure;
233 217 }
234 218
235 - $ret = trim($ret);
236 - if ($ret != $start_with) {
237 - $trailing = strlen($element_tag) + 3;
238 - if (substr($ret, -$trailing) != "</$element_tag>" && $short_code['popup'] == 'show' && !$non_standard) {
239 - $ret .= "\n$indent</$element_tag><!-- last $element_tag.photonic-pad-photos -->";
240 - }
219 + if (!empty($grid_figures)) {
220 + $this->set_lazy_loading_attributes($all_sizes_present, $grid_figures);
241 221
242 - $ret .= "\n$indent</$container_tag> <!-- ./photonic-level-1-container -->\n";
243 - $ret .= "<span id='$container_end'></span>";
222 + $container_class_list = $this->get_common_gallery_classes(1, $title_position, $non_standard, $layout, $effect, $all_sizes_present, $gallery_column_class, $display);
223 + $container_class_list[] = $lightbox->get_container_classes();
244 224
245 - if (!empty($pagination) && isset($pagination['end']) && isset($pagination['total']) && $pagination['total'] > $pagination['end']) {
246 - $ret .= !empty($more) ? "<a href='#' class='photonic-more-button photonic-more-dynamic'>$more</a>\n" : '';
225 + $data_query = $this->get_updated_data_query($short_code, $pagination);
226 + $container_data_attributes = [
227 + 'data-photonic-platform' => esc_attr($module->provider),
228 + 'data-photonic-gallery-columns' => esc_attr($columns),
229 + 'data-photonic-query' => esc_attr($data_query),
230 + ];
231 +
232 + if ('masonry-horizontal' === $layout) {
233 + $container_data_attributes['data-photonic-max-idx'] = count($photos);
247 234 }
248 - }
249 - else {
250 - $ret = '';
251 - }
252 235
253 - if (is_archive() || is_home()) {
254 - global $photonic_archive_thumbs;
255 - if (!empty($photonic_archive_thumbs) && $counter < $photonic_archive_thumbs) {
256 - $module->is_more_required = false;
257 - }
236 + $ret = $this->generate_container_markup($module, $pagination, $container_class_list, $container_data_attributes, $grid_figures, $container_start_id, $container_end_id, $more, 1, $indent);
258 237 }
259 238
260 239 $module->pop_from_stack();
261 240 return $ret;
@@ -265,173 +244,153 @@
265 244 * Generates the HTML for a group of level-2 items, i.e. Photosets (Albums) and Galleries for Flickr, Albums for Google Photos,
266 245 * Albums for SmugMug, and Photosets (Galleries and Collections) for Zenfolio. No concept of albums
267 246 * exists in native WP and Instagram.
268 247 *
269 - * @param $objects
270 - * @param $options
271 - * @param $short_code
272 - * @param $module Core
248 + * @param Album_List $album_list
249 + * @param array $short_code
250 + * @param Base $module
273 251 * @return string
274 252 */
275 - function generate_level_2_gallery($objects, $options, $short_code, $module) {
253 + public function generate_level_2_gallery(Album_List $album_list, array $short_code, Base $module): string {
276 254 $module->push_to_stack('Generate Level 2 Gallery');
277 - $row_constraints = isset($options['row_constraints']) && is_array($options['row_constraints']) ? $options['row_constraints'] : [];
278 - $type = $options['type'];
279 - $singular_type = $options['singular_type'];
280 - $title_position = empty($short_code['title_position']) ? $options['title_position'] : $short_code['title_position'];
281 - $level_1_count_display = $options['level_1_count_display'];
282 - $indent = !isset($options['indent']) ? '' : $options['indent'];
283 - $provider = $module->provider;
284 255
256 + $short_code = array_merge($this->common_parameters, $short_code);
257 +
258 + $objects = $album_list->albums;
259 +
260 + $layout = esc_attr($short_code['layout'] ?? 'square');
285 261 $columns = $short_code['columns'];
286 - $layout = !isset($short_code['layout']) ? 'square' : $short_code['layout'];
287 - $popup = ' data-photonic-popup="'.$short_code['popup'].'"';
262 + $more = !empty($short_code['more']) ? esc_attr($short_code['more']) : '';
263 + $title_position = esc_attr(empty($short_code['title_position']) ? $album_list->title_position : $short_code['title_position']);
264 + $row_constraints = $album_list->row_constraints;
265 + $pagination = $album_list->pagination;
266 + $indent = $album_list->indent;
288 267
289 - $non_standard = $layout == 'random' || $layout == 'masonry' || $layout == 'mosaic';
290 - $effect = $this->get_thumbnail_effect($short_code, $layout, $title_position);
291 - $ul_class = "class='title-display-$title_position photonic-level-2-container ".($non_standard ? 'photonic-'.$layout.'-layout' : 'photonic-standard-layout')." photonic-thumbnail-effect-$effect'";
268 + $singular_type = esc_attr($album_list->singular_type);
269 + $level_1_count_display = $album_list->level_1_count_display;
270 + $provider = esc_attr($module->provider);
292 271
293 - list($container_id, $container_end) = $this->get_container_details($short_code, $module);
272 + $gallery_data = array_merge(
273 + [
274 + 'platform' => $provider,
275 + 'singular' => $singular_type,
276 + 'popup' => esc_attr($short_code['popup']),
277 + ],
278 + $album_list->gallery_attributes
279 + );
294 280
295 - $pagination = isset($options['pagination']) && is_array($options['pagination']) ? $options['pagination'] : [];
296 - $more = !empty($short_code['more']) ? esc_attr($short_code['more']) : '';
281 + list($container_start_id, $container_end_id) = $this->get_container_details($short_code, $module);
282 + $non_standard = in_array($layout, ['random', 'masonry', 'masonry-horizontal', 'mosaic'], true);
283 + $gallery_column_class = $this->get_gallery_column_class($non_standard, $columns, $row_constraints);
284 + $effect = esc_attr($this->get_thumbnail_effect($short_code, $layout, $title_position));
297 285
298 - list($pagination_data, $pagination, $columns_data) = $this->get_gallery_data_attributes($short_code, $module, $pagination, $columns);
286 + $gallery_figure_classes = ['photonic-level-2', 'photonic-thumb'];
299 287
300 - $ret = "\n$indent<ul $container_id $ul_class $pagination_data $columns_data>";
301 - if ($non_standard) {
302 - $ret = "\n$indent<div $container_id $ul_class $pagination_data $columns_data>";
288 + $gallery_anchor_classes = ['photonic-level-2-thumb'];
289 + if ($album_list->album_opens_gallery) {
290 + $gallery_anchor_classes[] = 'gallery-page';
303 291 }
304 292
305 - if ($columns != 'auto') {
306 - $col_class = 'photonic-gallery-'.$columns.'c';
307 - }
308 - else if ($row_constraints['constraint-type'] == 'padding') {
309 - $col_class = 'photonic-pad-'.$type;
310 - }
311 - else {
312 - $col_class = 'photonic-gallery-'.$row_constraints['count'].'c';
313 - }
293 + $layout_engine = $this->get_layout_engine($module, $short_code);
294 + $all_sizes_present = strtolower($layout_engine) === 'css';
314 295
315 - $col_class .= ' photonic-level-2 photonic-thumb';
296 + $ret = '';
297 + $grid_figures = [];
316 298
317 - $counter = 0;
318 - foreach ($objects as $object) {
319 - $data_attributes = isset($object['data_attributes']) && is_array($object['data_attributes']) ? $object['data_attributes'] : [];
320 - $data_attributes['provider'] = $provider;
321 - $data_attributes['singular'] = $singular_type;
299 + /** @var Album $object */
300 + foreach ($objects as $idx => $object) {
301 + $data_attributes = $object->data_attributes ?: [];
302 + $styles = [];
322 303
323 - $data_array = [];
304 + $figure_data = [];
305 + if ('masonry-horizontal' === $layout) {
306 + $figure_data['data-photonic-idx'] = $idx;
307 + }
308 +
309 + $anchor_data = [];
324 310 foreach ($data_attributes as $attr => $value) {
325 - $data_array[] = 'data-photonic-'.$attr.'="'.$value.'"';
311 + $anchor_data['data-photonic-' . $attr] = $value;
326 312 }
327 - $data_array = implode(' ', $data_array);
328 313
314 + $id = empty($object->id) ? '' : $object->id . '-';
315 + $id = esc_attr($id . $module->gallery_index);
316 + $title = wp_kses_post($object->title);
329 317
330 - $id = empty($object['id_1']) ? '' : $object['id_1'].'-';
331 - $id = $id.$module->gallery_index;
332 - $id = empty($object['id_2']) ? $id : ($id.'-'.$object['id_2']);
333 - $title = esc_attr($object['title']);
334 - $image = "<img src='".(($non_standard && isset($object['tile_image'])) ? $object['tile_image'] : $object['thumbnail'])."' alt='".$title."' class='$layout' loading='eager'/>";
335 - $additional_classes = !empty($object['classes']) ? implode(' ', $object['classes']) : '';
336 - $realm_class = '';
337 - if (!empty($object['classes'])) {
338 - foreach ($object['classes'] as $class) {
339 - if (stripos($class, 'photonic-'.$provider.'-realm') !== FALSE) {
340 - $realm_class = $class;
341 - }
342 - }
318 + $anchor_data['data-title'] = esc_attr($title);
319 +
320 + if ('tooltip' === $title_position) {
321 + $anchor_data['data-photonic-tooltip'] = esc_attr($title);
343 322 }
344 - if ($title_position == 'tooltip') {
345 - $tooltip = "data-photonic-tooltip='".esc_attr($title)."' ";
323 +
324 + if ($non_standard && !empty($object->tile_image)) {
325 + $img_src = $object->tile_image;
326 + $inbuilt_sizes = $object->tile_size ?: [];
346 327 }
347 328 else {
348 - $tooltip = '';
329 + $img_src = $object->thumbnail;
330 + $inbuilt_sizes = $object->thumb_size ?: [];
349 331 }
350 332
351 - if (empty($object['gallery_url'])) {
352 - $anchor = "\n{$indent}\t\t<a href='{$object['main_page']}' class='photonic-{$provider}-{$singular_type}-thumb photonic-level-2-thumb $additional_classes' id='photonic-{$provider}-$singular_type-thumb-$id' title='".($title_position != 'none' ? esc_attr($title) : '')."' data-title='".$title."' $tooltip $data_array$popup>\n$indent\t\t\t".$image;
333 + if (!empty($inbuilt_sizes)) {
334 + if ('random' === $layout) {
335 + $styles['--dw'] = $inbuilt_sizes['w'];
336 + $styles['--dh'] = $inbuilt_sizes['h'];
337 + }
353 338 }
354 339 else {
355 - $anchor = "\n{$indent}\t\t<a href='{$object['gallery_url']}' class='photonic-{$provider}-{$singular_type}-thumb photonic-level-2-thumb gallery-page $additional_classes' id='photonic-{$provider}-$singular_type-thumb-$id' title='".($title_position != 'none' ? esc_attr($title) : '')."' data-title='".$title."' $tooltip $data_array$popup>\n$indent\t\t\t".$image;
340 + $all_sizes_present = false;
356 341 }
357 - $text = '';
358 - if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick'])) {
359 - $text = "\n{$indent}\t\t\t<div class='photonic-title-info'>\n{$indent}\t\t\t\t<div class='photonic-$singular_type-title photonic-title'>".$title."";
360 - if (!$level_1_count_display && !empty($object['counter'])) {
361 - $text .= '<span class="photonic-title-photo-count photonic-'.$singular_type.'-photo-count">'.sprintf(esc_html__('%s photos', 'photonic'), $object['counter']).'</span>';
342 +
343 + $shown_title = '';
344 + if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick'], true)) {
345 + $shown_title = "\n$indent\t\t\t<figcaption class='photonic-title-info'>\n$indent\t\t\t\t<div class='photonic-$singular_type-title photonic-title'>" . wp_specialchars_decode(stripslashes(wp_filter_nohtml_kses($title)), ENT_QUOTES);
346 + if (!$level_1_count_display && !empty($object->counter)) {
347 + $shown_title .= '<span class="photonic-title-photo-count photonic-' . $singular_type . '-photo-count">' . sprintf(esc_html__('%s photos', 'photonic'), $object->counter) . '</span>';
362 348 }
349 + $shown_title .= "</div>\n$indent\t\t\t</figcaption>";
363 350 }
364 - if ($text != '') {
365 - $text .= "</div>\n{$indent}\t\t\t</div>";
366 - }
367 351
368 - $anchor .= $text."\n{$indent}\t\t</a>";
369 - $password_prompt = '';
370 - if (!empty($object['passworded'])) {
371 - $prompt_title = esc_attr__('Protected Content', 'photonic');
372 - $prompt_submit = esc_attr__('Access', 'photonic');
373 - $password_type = " type='password' ";
374 - $prompt_type = 'password';
375 - $prompt_text = esc_attr__('This album is password-protected. Please provide a valid password.', 'photonic');
376 - if (in_array("photonic-$provider-passworded-authkey", $object['classes'])) {
377 - $prompt_text = esc_attr__('This album is protected. Please provide a valid authorization key.', 'photonic');
378 - }
379 - else if (in_array("photonic-$provider-passworded-link", $object['classes'])) {
380 - $prompt_text = esc_attr__('This album is protected. Please provide the short-link for it.', 'photonic');
381 - $password_type = '';
382 - $prompt_type = 'link';
383 - }
352 + $grid_image = new Grid_Image();
353 + $grid_image->alt = esc_attr($title);
354 + $grid_image->src = esc_url($img_src);
355 + $grid_image->classes = [sanitize_html_class($layout)];
356 + $grid_image->dimensions = $inbuilt_sizes ?? [];
384 357
385 - $password_prompt = "
386 - <div class='photonic-password-prompter $realm_class' id='photonic-{$provider}-$singular_type-prompter-$id' title='$prompt_title' data-photonic-prompt='$prompt_type'>
387 - <div class='photonic-password-prompter-content'>
388 - <div class='photonic-prompt-head'>
389 - <h3>
390 - <span class='title'>$prompt_title</span>
391 - <button class='close'>&times;</button>
392 - </h3>
393 - </div>
394 - <div class='photonic-prompt-body'>
395 - <p>$prompt_text</p>
396 - <input $password_type name='photonic-{$provider}-password' />
397 - <button class='photonic-{$provider}-submit photonic-password-submit confirm'>$prompt_submit</button>
398 - </div>
399 - </div>
400 - </div>";
401 - }
358 + $grid_anchor = new Grid_Anchor();
359 + $grid_anchor->id = "photonic-$provider-$singular_type-thumb-$id";
360 + $grid_anchor->href = esc_url($object->gallery_url ?? $object->main_page);
361 + $grid_anchor->classes = array_merge($gallery_anchor_classes, $object->classes);
362 + $grid_anchor->data = $anchor_data;
363 + $grid_anchor->title = 'none' !== $title_position ? esc_attr($title) : '';
364 + $grid_anchor->figcaption = $shown_title;
365 + $grid_anchor->image = $grid_image;
366 + $grid_anchor->indent = $indent;
402 367
403 - if ($non_standard) {
404 - $ret .= "\n$indent\t<div class='photonic-{$provider}-image photonic-{$provider}-$singular_type-thumb $col_class' id='photonic-{$provider}-$singular_type-$id'>{$anchor}{$password_prompt}\n$indent\t</div>";
405 - }
406 - else {
407 - $ret .= "\n$indent\t<li class='photonic-{$provider}-image photonic-{$provider}-$singular_type-thumb $col_class' id='photonic-{$provider}-$singular_type-$id'>{$anchor}{$password_prompt}\n$indent\t</li>";
408 - }
409 - $counter++;
368 + $grid_figure = new Grid_Figure();
369 + $grid_figure->id = "photonic-$provider-$singular_type-$id";
370 + $grid_figure->classes = $gallery_figure_classes;
371 + $grid_figure->styles = $styles;
372 + $grid_figure->data = $figure_data;
373 + $grid_figure->anchor = $grid_anchor;
374 + $grid_figure->indent = $indent;
375 + $grid_figure->prompter_markup = $this->get_password_prompter($object, $provider, $singular_type, $id);
376 +
377 + $grid_figures[] = $grid_figure;
410 378 }
411 379
412 - if ($ret != "\n$indent<ul $container_id $ul_class $pagination_data $columns_data>" && !$non_standard) {
413 - $ret .= "\n$indent</ul>\n";
414 - }
415 - else if ($non_standard) {
416 - $ret .= "\n$indent</div>\n";
417 - }
418 - else {
419 - $ret = '';
420 - }
380 + if (!empty($grid_figures)) {
381 + $this->set_lazy_loading_attributes($all_sizes_present, $grid_figures);
382 + $gallery_class_list = $this->get_common_gallery_classes(2, $title_position, $non_standard, $layout, $effect, $all_sizes_present, $gallery_column_class);
421 383
422 - if (!empty($ret)) {
423 - $ret .= "<span id='$container_end'></span>";
424 - if (!empty($pagination) && isset($pagination['end']) && isset($pagination['total']) && $pagination['total'] > $pagination['end']) {
425 - $ret .= !empty($more) ? "<a href='#' class='photonic-more-button photonic-more-dynamic'>$more</a>\n" : '';
426 - }
427 - }
384 + $data_query = $this->get_updated_data_query($short_code, $pagination);
385 + $container_data_attributes = [
386 + 'data-photonic-platform' => $provider,
387 + 'data-photonic-gallery-columns' => esc_attr($columns),
388 + 'data-photonic-query' => esc_attr($data_query),
389 + 'data-photonic' => esc_attr(wp_json_encode($gallery_data)),
390 + ];
428 391
429 - if (is_archive() || is_home()) {
430 - global $photonic_archive_thumbs;
431 - if (!empty($photonic_archive_thumbs) && $counter < $photonic_archive_thumbs) {
432 - $module->is_more_required = false;
433 - }
392 + $ret = $this->generate_container_markup($module, $pagination, $gallery_class_list, $container_data_attributes, $grid_figures, $container_start_id, $container_end_id, $more, 2, $indent);
434 393 }
435 394
436 395 $module->pop_from_stack();
437 396 return $ret;
@@ -437,87 +396,50 @@
437 396 return $ret;
438 397 }
439 398
440 399 /**
441 - * @param $short_code
442 - * @param $module
400 + * @param array $short_code
401 + * @param Base $module
443 402 * @return array
444 403 */
445 - private function get_container_details($short_code, $module) {
446 - if ($short_code['display'] != 'popup') {
447 - $container_id = "id='photonic-{$module->provider}-stream-{$module->gallery_index}-container'";
448 - $container_end = "photonic-{$module->provider}-stream-{$module->gallery_index}-container-end";
449 - } else {
450 - $container_id = "id='photonic-{$module->provider}-panel-" . $short_code['panel'] . "-container'";
451 - $container_end = "photonic-{$module->provider}-panel-{$short_code['panel']}-container-end";
404 + private function get_container_details(array $short_code, Base $module): array {
405 + if ('modal' !== $short_code['display']) {
406 + $start_id = "photonic-" . esc_attr($module->provider) . "-stream-" . esc_attr($module->gallery_index) . "-container";
407 + $end_id = "photonic-" . esc_attr($module->provider) . "-stream-" . esc_attr($module->gallery_index) . "-container-end";
452 408 }
453 - return [$container_id, $container_end];
409 + else {
410 + $start_id = "photonic-" . esc_attr($module->provider) . "-panel-" . esc_attr(sanitize_text_field($short_code['panel'])) . "-container";
411 + $end_id = "photonic-" . esc_attr($module->provider) . "-panel-" . esc_attr(sanitize_text_field($short_code['panel'])) . "-container-end";
412 + }
413 + return [$start_id, $end_id];
454 414 }
455 415
456 416 /**
457 - * @param array $link_attributes
417 + * @param array $short_code
418 + * @param Pagination $pagination
458 419 * @return string
459 420 */
460 - private function get_text_from_link_attributes($link_attributes) {
461 - $class = '';
462 - $rel = '';
463 - $specific = '';
464 - if (!empty($link_attributes['class'])) {
465 - $class = " class='".implode(' ', array_values($link_attributes['class']))."' ";
466 - }
421 + private function get_updated_data_query(array $short_code, Pagination $pagination): string {
422 + $to_be_glued = '';
467 423
468 - if (!empty($link_attributes['rel'])) {
469 - $rel = " rel='".implode(' ', $link_attributes['rel'])."' ";
470 - }
471 -
472 - if (!empty($link_attributes['specific'])) {
473 - foreach ($link_attributes['specific'] as $key => $val) {
474 - $specific .= $key.'="'.implode(' ', $val).'" ';
475 - }
476 - }
477 - return $class.$rel.$specific;
478 - }
479 -
480 - /**
481 - * @param $short_code
482 - * @param $module
483 - * @param $pagination
484 - * @param $columns
485 - * @return array
486 - */
487 - private function get_gallery_data_attributes($short_code, $module, $pagination, $columns) {
488 - $pagination_data = '';
489 - if (!empty($pagination)) {
490 - $pagination_data = [];
491 - // Should have total, start, end, per-page
492 - foreach ($pagination as $meta => $value) {
493 - $pagination_data[] = 'data-photonic-stream-' . $meta . '="' . $value . '"';
494 - }
495 -
496 - $pagination_data = implode(' ', $pagination_data);
497 - if (empty($pagination['provider'])) {
498 - $pagination_data .= ' data-photonic-stream-provider="' . $module->provider . '"';
499 - }
500 - }
501 -
502 - $to_be_glued = '';
503 424 if (!empty($short_code)) {
504 425 $to_be_glued = [];
505 426 foreach ($short_code as $name => $value) {
506 427 if (is_scalar($value)) {
507 - $to_be_glued[] = $name . '=' . $value;
428 + if ('next_token' !== $name) {
429 + $to_be_glued[] = $name . '=' . $value;
430 + }
508 431 }
509 432 }
510 - if (!empty($pagination['next-token'])) {
511 - $to_be_glued[] = 'next_token=' . $pagination['next-token'];
433 +
434 + if (!empty($pagination->next_token)) {
435 + $to_be_glued[] = 'next_token=' . $pagination->next_token;
512 436 }
437 +
513 438 $to_be_glued = implode('&', $to_be_glued);
514 439 $to_be_glued = esc_attr($to_be_glued);
515 440 }
516 -
517 - $pagination_data .= ' data-photonic-stream-query="' . $to_be_glued . '"';
518 - $columns_data = ' data-photonic-gallery-columns="' . $columns . '"';
519 - return [$pagination_data, $pagination, $columns_data];
441 + return $to_be_glued;
520 442 }
521 443
522 444 /**
523 445 * Returns the thumbnail effect that should be used for a gallery. Not all effects can be used by all types of layouts.
@@ -526,24 +448,25 @@
526 448 * @param $layout
527 449 * @param $title_position
528 450 * @return string
529 451 */
530 - private function get_thumbnail_effect($short_code, $layout, $title_position) {
452 + private function get_thumbnail_effect($short_code, $layout, $title_position): string {
531 453 if (!empty($short_code['thumbnail_effect'])) {
532 454 $effect = $short_code['thumbnail_effect'];
533 455 }
534 456 else {
535 457 global $photonic_standard_thumbnail_effect, $photonic_justified_thumbnail_effect, $photonic_mosaic_thumbnail_effect, $photonic_masonry_thumbnail_effect;
536 - $effect = $layout == 'mosaic' ? $photonic_mosaic_thumbnail_effect :
537 - ($layout == 'masonry' ? $photonic_masonry_thumbnail_effect :
538 - ($layout == 'random' ? $photonic_justified_thumbnail_effect :
458 + $effect = 'mosaic' === $layout ? $photonic_mosaic_thumbnail_effect :
459 + (('masonry' === $layout || 'masonry-horizontal' === $layout)? $photonic_masonry_thumbnail_effect :
460 + ('random' === $layout ? $photonic_justified_thumbnail_effect :
539 461 $photonic_standard_thumbnail_effect));
462 + $effect = esc_attr($effect);
540 463 }
541 464
542 - if ($layout == 'circle' && $effect != 'opacity') { // "Zoom" doesn't work for circle
465 + if ('circle' === $layout && 'opacity' !== $effect) { // "Zoom" doesn't work for circle
543 466 $thumbnail_effect = 'none';
544 467 }
545 - else if (($layout == 'square' || $layout == 'launch' || $layout == 'masonry') && $title_position == 'below') { // For these combinations, Zoom doesn't work
468 + elseif (('square' === $layout || 'launch' === $layout || 'masonry' === $layout) && 'below' === $title_position) { // For these combinations, Zoom doesn't work
546 469 $thumbnail_effect = 'none';
547 470 }
548 471 else {
549 472 $thumbnail_effect = $effect;
@@ -549,5 +472,155 @@
549 472 $thumbnail_effect = $effect;
550 473 }
551 474 return apply_filters('photonic_thumbnail_effect', $thumbnail_effect, $short_code, $layout, $title_position);
552 475 }
553 -}
476 +
477 + /**
478 + * Returns the layout engine to be used, i.e. js or css.
479 + *
480 + * @param Base $module
481 + * @param $short_code
482 + * @return string
483 + */
484 + private function get_layout_engine(Base $module, $short_code): string {
485 + $layout_engine = 'photonic_' . $module->provider . '_layout_engine';
486 + global ${$layout_engine};
487 + return $short_code['layout_engine'] ?? ${$layout_engine};
488 + }
489 +
490 + /**
491 + * @param Album $object
492 + * @param string $provider
493 + * @param string $singular_type
494 + * @param string $id
495 + * @return string
496 + */
497 + private function get_password_prompter(Album $object, string $provider, string $singular_type, string $id): string {
498 + $password_prompt = '';
499 + if (!empty($object->passworded)) {
500 + $password_prompt = "
501 + <div class='photonic-password-prompter' id='photonic-$provider-$singular_type-prompter-$id' title='$this->prompt_title' data-photonic-prompt='password'>
502 + <div class='photonic-password-prompter-content'>
503 + <div class='photonic-prompt-head'>
504 + <h3>
505 + <span class='title'>$this->prompt_title</span>
506 + <button class='close'>&times;</button>
507 + </h3>
508 + </div>
509 + <div class='photonic-prompt-body'>
510 + <p>$this->prompt_text</p>
511 + <input type='password' name='photonic-$provider-password' />
512 + <button class='photonic-$provider-submit photonic-password-submit confirm'>$this->prompt_submit</button>
513 + </div>
514 + </div>
515 + </div>";
516 + }
517 + return $password_prompt;
518 + }
519 +
520 + /**
521 + * @param bool $all_sizes_present
522 + * @param array $grid_figures
523 + */
524 + private function set_lazy_loading_attributes(bool $all_sizes_present, array $grid_figures): void {
525 + if ($all_sizes_present) {
526 + /** @var Grid_Figure $figure */
527 + foreach ($grid_figures as $figure) {
528 + $figure->anchor->image->lazy_load = 'lazy';
529 + $figure->anchor->image->src_attr = 'data-src';
530 + }
531 + }
532 + else {
533 + /** @var Grid_Figure $figure */
534 + foreach ($grid_figures as $figure) {
535 + $figure->anchor->image->lazy_load = 'eager';
536 + $figure->anchor->image->src_attr = 'src';
537 + }
538 + }
539 + }
540 +
541 + private function get_common_gallery_classes($level, $title_position, $non_standard, $layout, $thumbnail_effect, $all_sizes_present, $gallery_column_class, $display = null): array {
542 + $classes = [
543 + 'title-display-' . $title_position,
544 + 'photonic-level-' . $level . '-container',
545 + $all_sizes_present ? 'sizes-present' : 'sizes-missing',
546 + $gallery_column_class,
547 + ];
548 +
549 + if ('modal' === $display) {
550 + $classes[] = 'modal-gallery';
551 + }
552 + else {
553 + $classes[] = $non_standard ? 'photonic-' . $layout . '-layout' : 'photonic-standard-layout';
554 + $classes[] = 'photonic-thumbnail-effect-' . $thumbnail_effect;
555 + }
556 +
557 + return $classes;
558 + }
559 +
560 + /**
561 + * @param bool $non_standard
562 + * @param string $columns
563 + * @param array $row_constraints
564 + * @return string
565 + */
566 + private function get_gallery_column_class(bool $non_standard, string $columns, array $row_constraints): string {
567 + $gallery_column_class = '';
568 + if (!$non_standard) {
569 + if (absint($columns)) {
570 + $gallery_column_class = 'photonic-gallery-' . esc_attr($columns) . 'c';
571 + }
572 + elseif ('padding' === $row_constraints['constraint-type']) {
573 + $gallery_column_class = 'photonic-auto-padded';
574 + }
575 + elseif (!empty($row_constraints['count'])) {
576 + $gallery_column_class = 'photonic-gallery-' . esc_attr($row_constraints['count']) . 'c';
577 + }
578 + }
579 + return $gallery_column_class;
580 + }
581 +
582 + private function glue_data_attributes(array $map): string {
583 + $data_pieces = [];
584 + if (!empty($map)) {
585 + $data_pieces = array_map(
586 + function (string $key, string $value): string {
587 + return $key . '="' . $value . '"';
588 + },
589 + array_keys($map),
590 + array_values($map)
591 + );
592 + }
593 + return implode(' ', $data_pieces);
594 + }
595 +
596 + private function generate_container_markup(Base $module, Pagination $pagination, array $container_class_list, array $container_data_attributes, array $grid_figures, string $start_id, string $end_id, string $more, int $level, string $indent): string {
597 + $ret = '';
598 + if (!empty($grid_figures)) {
599 + $container_class = str_replace(' ', ' ', trim(implode(' ', $container_class_list)));
600 +
601 + $style = '';
602 + if (in_array('photonic-random-layout', $container_class_list, true)) {
603 + global $photonic_tile_min_height;
604 + if (!is_numeric($photonic_tile_min_height)) {
605 + $photonic_tile_min_height = 200;
606 + }
607 + $photonic_tile_min_height = esc_attr($photonic_tile_min_height);
608 + $style = "style='--tile-min-height: {$photonic_tile_min_height}px'";
609 + }
610 +
611 + $container_data_string = $this->glue_data_attributes($container_data_attributes);
612 + $ret = "\n$indent<div id='" . esc_attr($start_id) . "' class='" . esc_attr($container_class) . "' $container_data_string $style>\n";
613 + foreach ($grid_figures as $figure) {
614 + $ret .= $figure->html($module, $this, false);
615 + }
616 +
617 + $ret .= "\n$indent</div> <!-- ./photonic-level-$level-container -->";
618 + $ret .= "\n$indent<span id='$end_id'></span>";
619 +
620 + if ($pagination->end > -1 && $pagination->total > -1 && $pagination->total > $pagination->end) {
621 + $ret .= !empty($more) ? "\n$indent<a href='#' class='photonic-more-button photonic-more-dynamic'>$more</a>\n" : '';
622 + }
623 + }
624 + return $ret;
625 + }
626 +}