PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / includes / Frontend / Preview / ExceptionalElements.php
kirki / includes / Frontend / Preview Last commit date
templates 3 months ago DataHelper.php 4 days ago ExceptionalElements.php 4 days ago Preview.php 4 days ago Utils.php 1 month ago
ExceptionalElements.php
1526 lines
1 <?php
2 /**
3 * Preview script helper class for handle exceptional element
4 *
5 * @package kirki
6 */
7
8 namespace Kirki\Frontend\Preview;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13
14 use Kirki\Ajax\Symbol;
15 use Kirki\Ajax\Users;
16 use Kirki\HelperFunctions;
17 use Kirki\Ajax\WordpressData;
18
19 /**
20 * ExceptionalElements Class
21 */
22 class ExceptionalElements {
23 /**
24 * Get this exceptional element
25 *
26 * @param array $this_data single element block.
27 * @param array $attributes single element all attributes.
28 * @param array $options single element all options.
29 * @return string HTML markup.
30 */
31 public function get_this_exceptional_element( $this_data, $attributes, $options ) {
32 switch ( $this_data['name'] ) {
33 case 'custom-code': {
34 return $this->custom_code( $this_data, $attributes );
35 }
36 case 'form': {
37 return $this->form_element( $this_data, $attributes, $options );
38 }
39 case 'map':
40 case 'google-map': {
41 return $this->map_element( $this_data, $attributes );
42 }
43 case 'svg':
44 case 'svg-icon':
45 {
46 return $this->svg_and_icon_element( $this_data, $attributes, $options );
47 }
48 case 'textarea': {
49 return '<textarea ' . $attributes . '></textarea>';
50 }
51 case 'select': {
52 return $this->select_element( $this_data, $attributes, $options );
53 }
54 case 'section': {
55 return $this->section_element( $this_data, $attributes, $options );
56 }
57 case 'video': {
58 return $this->video_element( $this_data, $attributes, $options );
59 }
60 case 'radio-group': {
61 return $this->radio_group( $this_data, $attributes );
62 }
63 case 'checkbox-element': {
64 return $this->checkbox_element( $this_data, $attributes, $options );
65 }
66 case 'radio-button': {
67 return '<input type="radio" ' . $attributes . ' />';
68 }
69 case 'image': {
70 return $this->image_element( $this_data, $attributes, $options );
71 }
72 case 'link-block': {
73 return $this->link_block_element( $this_data, $attributes, $options );
74 }
75
76 case 'file-upload-inner': {
77 return $this->file_input_element( $this_data, $attributes, $options );
78 }
79
80 case 'file-upload-threshold-text': {
81 return $this->file_upload_threshold_text( $this_data, $attributes, $options );
82 }
83
84 case 'file-upload': {
85 return $this->file_upload_element( $this_data, $attributes, $options );
86 }
87 case 'slider':
88 case 'collection': {
89 $element_name = $this_data['name'];
90 $properties = $this_data['properties'];
91
92 $dynamic_content = $element_name === 'slider'
93 ? ( $properties['dynamicSliderContent'] ?? array() )
94 : ( $properties['dynamicContent'] ?? array() );
95
96 if ( ! empty( $dynamic_content['offset'] ) ) {
97 $options['offset'] = (int) $dynamic_content['offset'];
98 }
99
100 if ( $element_name === 'slider' ) {
101 $options['slider_mode'] = $properties['slider']['mode'] ?? 'manual'; // default manual
102 $options['element_name'] = $element_name;
103 $slider_mask_id = $this_data['children'][0];
104
105 if ( $slider_mask_id && isset( $this->data[ $slider_mask_id ] ) ) {
106 $slider_mask_data = $this->data[ $slider_mask_id ];
107 $options['slider_mask_children'] = $slider_mask_data['children'] ?? array();
108 }
109 }
110
111 $children = array();
112
113 if ( empty( $dynamic_content['collectionType'] ) ) {
114 $dynamic_content['collectionType'] = 'posts'; // default value
115 }
116
117 $children = $this->construct_items_collection_markup( $dynamic_content, $this_data, $options );
118
119 return $this->construct_collection_markup( $children, $this_data, $attributes, $element_name );
120 }
121 case 'loading': {
122 return $this->collection_loading_element( $this_data, $attributes, $options );
123 }
124 case 'terms': // TODO: migrate and remove
125 case 'users': // TODO: migrate and remove
126 case 'collection-wrapper': // TODO: migrate and remove
127 case 'slider_mask':
128 case 'items': {
129 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
130 $collection = isset( $options['collection'] ) ? $options['collection'] : array();
131 $itemType = isset( $options['itemType'] ) ? $options['itemType'] : 'post';
132 $collection_item_id = $this_data['children'][0];
133 $slider_mask_children = $this_data['children'];
134 $slider_mode = $options['slider_mode'] ?? 'manual';
135 $is_slider = isset( $options['element_name'] ) && $options['element_name'] === 'slider';
136
137 $children = array();
138
139 if ( ! $options ) {
140 $options = array();
141 }
142
143 if ( $is_slider && $slider_mode === 'manual' ) {
144 foreach ( $slider_mask_children as $key => $child_id ) {
145 $children[] = $this->recGenHTML( $child_id, $options );
146 }
147 } else { // for collection and slider (dynamic)
148 foreach ( $collection as $key => $collection_item ) {
149 $item_index = HelperFunctions::get_current_item_index( $key + 1, $options );
150 $options = array_merge(
151 $options,
152 array(
153 'itemType' => $itemType,
154 $itemType => $collection_item,
155 'item_index' => $item_index,
156 )
157 );
158
159 $children[] = HelperFunctions::rec_update_data_id_then_return_new_html( $this->data, $this->style_blocks, $collection_item_id, $options, ( $key === 0 ) );
160 };
161 }
162
163 return $this->get_template(
164 'items',
165 array(
166 'attributes' => $attributes,
167 'children' => $children,
168 'tag' => $tag,
169 )
170 );
171 }
172
173 case 'pagination': {
174 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
175 $markup = '';
176 $show_pagination = isset( $options['pagination'] ) ? $options['pagination'] : true;
177 $pagination_type = isset( $options['pagination_type'] ) ? $options['pagination_type'] : 'numeric';
178 $offset = isset( $options['offset'] ) ? (int) $options['offset'] : 0;
179
180 if ( $show_pagination ) {
181 $collection_count = 0;
182
183 if ( isset( $options['itemType'], $options['collection_count'] ) ) {
184 $collection_count = $options['collection_count'];
185 }
186
187 $current_page = isset( $options['page_no'] ) ? $options['page_no'] : 1;
188 $items_per_page = isset( $options['items_per_page'] ) ? $options['items_per_page'] : 3;
189 $total_pages = $collection_count ? (int) ceil( ( $collection_count - $offset ) / $items_per_page ) : 0;
190 // $pagination_item_id = $this_data['children'][0];
191 $pagination_item_id = isset( $this_data['children'][0] ) ? $this_data['children'][0] : '';
192
193 $children = array();
194
195 // pagination type: numeric or infinite_scroll
196 if ( $pagination_type === 'numeric' ) {
197 $start = 1;
198 $end = $total_pages;
199
200 /**
201 * custom for large pagination start
202 */
203 // Determine the range to display
204 $range = 10;
205 $half_range = floor( $range / 2 );
206
207 // Calculate new $start and $end
208 $start = max( 1, $current_page - $half_range );
209 $end = min( $total_pages, $current_page + $half_range );
210
211 // Adjust $start and $end if at the boundaries
212 if ( $end - $start < $range - 1 ) {
213 if ( $start == 1 ) {
214 $end = min( $start + $range - 1, $total_pages );
215 } else {
216 $start = max( $end - $range + 1, 1 );
217 }
218 }
219 /**
220 * custom for large pagination end
221 */
222
223 if ( $end > 1 ) {
224 for ( $i = $start; $i <= $end; $i++ ) {
225 $options = array(
226 'page_number' => $i,
227 'current_page' => $current_page === $i,
228 );
229 $children[] = HelperFunctions::rec_update_data_id_then_return_new_html( $this->data, $this->style_blocks, $pagination_item_id, $options, ( $i === $start ) );
230 }
231 }
232
233 $markup = $this->get_template(
234 'pagination',
235 array(
236 'attributes' => $attributes,
237 'children' => $children,
238 'tag' => $tag,
239 )
240 );
241 } elseif ( $pagination_type === 'infinite_scroll' ) {
242 $markup = $this->get_template(
243 'pagination',
244 array(
245 'attributes' => $attributes . ' data-total-pages="' . $total_pages . '" data-current-page="' . $current_page . '"' . ' data-pagination-type="' . $pagination_type . '"' . 'style="height:1px "',
246 'children' => $children,
247 'tag' => $tag,
248 )
249 );
250 } elseif ( $pagination_type === 'load_more' && $current_page !== $total_pages ) {
251 $children = isset( $this_data['children'] ) ? $this_data['children'] : array();
252
253 $child_markup = $this->construct_children_markup( $children, $options );
254
255 $attr = $attributes . ' data-total-pages="' . $total_pages . '" data-current-page="' . $current_page . '"' . ' data-pagination-type="' . $pagination_type . '"';
256 return "<$tag $attr>
257 $child_markup
258 </$tag>";
259 }
260 }
261
262 return $markup;
263 }
264
265 case 'pagination-item': {
266 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
267 $current_page = isset( $options['current_page'] ) ? $options['current_page'] : false;
268 $page_number = isset( $options['page_number'] ) ? $options['page_number'] : false;
269
270 $children = array();
271
272 foreach ( $this_data['children'] as $child ) {
273 $children[] = $this->recGenHTML(
274 $child,
275 array(
276 'page_number' => $page_number,
277 )
278 );
279 }
280
281 return $this->get_template(
282 'pagination-item',
283 array(
284 'attributes' => $attributes,
285 'children' => $children,
286 'current_page' => $current_page,
287 'page_number' => $page_number,
288 'tag' => $tag,
289 )
290 );
291 }
292
293 case 'pagination-number': {
294 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'span';
295 $page_number = isset( $options['page_number'] ) ? $options['page_number'] : 1;
296
297 return $this->get_template(
298 'pagination-number',
299 array(
300 'attributes' => $attributes,
301 'page_number' => $page_number,
302 'tag' => $tag,
303 )
304 );
305 }
306
307 case 'common': {
308 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
309 $type = isset( $this_data['properties']['type'] ) ? $this_data['properties']['type'] : false;
310
311 if ( $type ) {
312 $attributes .= ' data-kirki-type="' . $type . '"';
313 }
314
315 $children = isset( $this_data['children'] ) ? $this_data['children'] : array();
316
317 $child_markup = $this->construct_children_markup( $children, $options );
318
319 return "<$tag $attributes>
320 $child_markup
321 </$tag>";
322 }
323
324 case 'symbol': {
325 return $this->generate_symbol_html( $this_data, $attributes, $options );
326 }
327 case 'popup-body': {
328 return $this->popup_element( $this_data, $attributes, $options );
329 }
330
331 case 'button': {
332 return $this->button_element( $this_data, $attributes, $options );
333 }
334
335 case 'navigation': {
336 return $this->navigation_element( $this_data, $attributes, $options );
337 }
338
339 case 'navigation-item': {
340 return $this->navigation_item_element($this_data, $attributes, $options);
341 }
342
343 case 'navigation-items': {
344 return $this->navigation_items_element( $this_data, $attributes, $options );
345 }
346
347 case 'slider_nav':{
348 return $this->slider_nav_element( $this_data, $attributes, $options );
349 }
350 case 'tabs':{
351 return $this->tabs_element( $this_data, $attributes, $options );
352 }
353 case 'tab':{
354 return $this->tab_element( $this_data, $attributes, $options );
355 }
356 case 'tab_pane':{
357 return $this->tab_pane_element( $this_data, $attributes, $options );
358 }
359 case 'tab_content':
360 case 'tab_menu':{
361 return $this->tab_menu_and_content_element( $this_data, $attributes, $options );
362 }
363 }
364 }
365
366 /**
367 * Generate collection loading element markup
368 *
369 * @param array $this_data single element block.
370 * @param array $attributes single element all attributes.
371 * @param array $options single element all options.
372 * @return string HTML markup.
373 */
374 private function collection_loading_element( $this_data, $attributes, $options ) {
375 $tag = isset( $this_data['properties'], $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
376
377 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
378
379 return "<$tag $attributes kirki-collection=\"loading\" data-element_hide=\"true\">
380 $children_markup
381 </$tag>";
382 }
383
384 /**
385 * Generate section element markup
386 *
387 * @param array $this_data single element block.
388 * @param array $attributes single element all attributes.
389 * @param array $options single element all options.
390 * @return string HTML markup.
391 */
392 private function section_element( $this_data, $attributes, $options ) {
393 $id = isset( $this_data['id'] ) ? $this_data['id'] : '';
394 if ( $id ) {
395 $id = $this->get_unique_section_id_from_title( $id );
396 }
397
398 $tag = isset( $this_data['properties'], $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'a';
399
400 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
401
402 return "<$tag $attributes id=\"$id\">
403 $children_markup
404 </$tag>";
405 }
406
407 private function navigation_element( $this_data, $attributes, $options ) {
408 $tag = isset( $this_data['properties'], $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'a';
409
410 $hamburger = isset($this_data['properties']['navigation']['hamburger']) ? $this_data['properties']['navigation']['hamburger'] : false;
411
412 if ( ! is_array( $options ) ) {
413 $options = array();
414 }
415
416 if ( ! $options ) {
417 $options = array();
418 }
419 $options = array_merge(
420 $options,
421 array(
422 'navigation' => array(
423 'id' => $this_data['id'],
424 'hamburger' => $hamburger,
425 'showNavigationChildrenOn' => isset($this_data['properties']['navigation']['showNavigationChildrenOn']) ? $this_data['properties']['navigation']['showNavigationChildrenOn'] : 'hover',
426 ),
427 )
428 );
429
430 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
431
432 return "<$tag $attributes>
433 $children_markup
434 </$tag>";
435 }
436
437 private function navigation_item_element($this_data, $attributes, $options)
438 {
439 $tag = isset($this_data['properties'], $this_data['properties']['tag']) ? $this_data['properties']['tag'] : 'a';
440
441 $children_markup = $this->construct_children_markup(isset($this_data['children']) ? $this_data['children'] : array(), $options);
442
443 $extra_attributes = '';
444
445 $show_on = 'hover';
446
447 // check showNavigationChildrenOn is hover or click or always then add kirki-navigation-item-trigger-on='hover'/'click'/'always'
448 if (isset($this_data['properties']['navigationItem']['showNavigationChildrenOn'])) {
449 $show_on = $this_data['properties']['navigationItem']['showNavigationChildrenOn'];
450 $extra_attributes .= ' kirki-navigation-item-trigger-on="' . $show_on . '"';
451 } else if (isset($options['navigation']['showNavigationChildrenOn'], $options['navigation']['hamburger']) && $options['navigation']['hamburger']) {
452 $show_on = $options['navigation']['showNavigationChildrenOn'];
453 $extra_attributes .= ' kirki-navigation-item-trigger-on="' . $show_on . '"';
454 } else {
455 $extra_attributes .= ' kirki-navigation-item-trigger-on="hover"';
456 }
457
458 if ('always' !== $show_on) {
459 $extra_attributes .= ' kirki-navigation-item-dismiss-on="' . ($this_data['properties']['navigationItem']['hideNavigationChildrenOn'] ?? 'mouseleave') . '"';
460 }
461
462 if (!$options) {
463 $options = array();
464 }
465
466 // add if kirki-navigation="nav-item" has in $this_data['properties']['attributes'] then set inside_nav_item = true
467 $options = array_merge(
468 $options,
469 array(
470 'inside_nav_item' => isset($this_data['properties']['attributes']['kirki-navigation']) && $this_data['properties']['attributes']['kirki-navigation'] === 'nav-item',
471 )
472 );
473
474 $children_markup = $this->construct_children_markup(isset($this_data['children']) ? $this_data['children'] : array(), $options);
475
476 return "<$tag $attributes $extra_attributes>
477 $children_markup
478 </$tag>";
479 }
480
481 private function navigation_items_element($this_data, $attributes, $options)
482 {
483
484 $tag = isset($this_data['properties'], $this_data['properties']['tag']) ? $this_data['properties']['tag'] : 'a';
485
486 $extra_attr = isset($options['inside_navigation']) && $options['inside_navigation'] ? 'kirki-navigation-hide="true"' : '';
487
488 if (isset($options['navigation']['hamburger']) && $options['navigation']['hamburger']) {
489 $extra_attr .= ' kirki-navigation-type="close"';
490 }
491
492 if (!$options) {
493 $options = array();
494 }
495 $options = array_merge(
496 $options,
497 array(
498 'inside_navigation' => true,
499 )
500 );
501
502 $children_markup = $this->construct_children_markup(isset($this_data['children']) ? $this_data['children'] : array(), $options);
503 // check it has kirki-menu-wrapper: true as attribute
504 // if (isset($this_data['properties']['attributes']['kirki-menu-wrapper']) && $this_data['properties']['attributes']['kirki-menu-wrapper'] === 'true') {
505 if (isset($options['inside_nav_item']) && $options['inside_nav_item'] === true) {
506 // Match the content inside class="..." and data-kirki="..."
507 preg_match('/class="([^"]+)"/', $attributes, $classMatch);
508 preg_match('/data-kirki="([^"]+)"/', $attributes, $dataMatch);
509
510 $class = $classMatch[1] ?? null;
511 $data_kirki = $dataMatch[1] ?? null;
512
513 return "
514 <div kirki-menu-wrapper-div='true'>
515 <$tag class='$class' data-kirki='$data_kirki'>
516 $children_markup
517 </$tag>
518 </div>
519 ";
520 } else {
521 return "<$tag $attributes $extra_attr>
522 $children_markup
523 </$tag>";
524 }
525
526 }
527
528 private function form_element( $this_data, $attributes, $options ) {
529 $properties = $this_data['properties'];
530 $post_id = HelperFunctions::get_post_id_if_possible_from_url();
531 $form_id = $this_data['id'];
532 $post_data = $form_id . '|' . $post_id;
533 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
534 $form_id_base64 = base64_encode( base64_encode( $post_data ) );
535
536 $form_nonce_field = wp_nonce_field( 'wp_rest', '_wpnonce', true, false );
537
538 if ( ! $options ) {
539 $options = array();
540 }
541
542 $options = array_merge(
543 $options,
544 array(
545 'form' => array(
546 'id' => $form_id,
547 ),
548 )
549 );
550
551 $form_children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
552
553 if ( isset( $properties['form']['type'] ) && $properties['form']['type'] !== 'external' ) {
554 $attributes = $this->getAllAttributes(
555 $this_data,
556 array(
557 'action' => false,
558 'method' => false,
559 'rest' => true,
560 ),
561 );
562 }
563
564 return "<form $attributes>
565 <input type=\"hidden\" name=\"_kirki_form\" value=\"$form_id_base64\" />
566 $form_nonce_field
567 $form_children_markup
568 </form>";
569 }
570
571 private function select_element( $this_data, $attributes, $options ) {
572 $properties = $this_data['properties'];
573 $select_options = isset( $properties['options']['list'] ) ? $properties['options']['list'] : array();
574 $selected = isset( $properties['options']['selectedOption'] ) ? $properties['options']['selectedOption'] : '';
575 $html = '<select ' . $attributes . '>';
576 foreach ( $select_options as $option ) {
577 $html .= "<option value='";
578 $html .= $option['value'] . "'";
579 $html .= $selected === $option['value'] ? ' selected' : '';
580 $html .= '>';
581 $html .= $option['contents'];
582 $html .= '</option>';
583 }
584 $html .= '</select>';
585
586 return $html;
587 }
588
589 /**
590 * Generate checkbox element markup
591 *
592 * @param array $this_data single element block.
593 * @param array $attributes single element all attributes.
594 * @param array $options single element all options.
595 * @return string HTML markup.
596 */
597 private function checkbox_element( $this_data, $attributes, $options ) {
598 $relation_type = isset( $options['relation_type'] ) ? $options['relation_type'] : 'posts';
599 if ( ! isset( $options['form'] ) ) {
600 // Start: Filter functionality
601 if ( $relation_type === 'posts' ) {
602 $checked_ids = isset( $options['selected_post_ids'] ) ? $options['selected_post_ids'] : array();
603 $checked = in_array( (string) $options['post']->ID, $checked_ids, true );
604
605 if ( $checked ) {
606 $attributes = 'checked ' . $attributes;
607 }
608
609 $attributes = "name='cm_post' value='" . $options['post']->ID . "' " . $attributes;
610 } elseif ( $relation_type === 'terms' ) {
611 $taxonomy = $options['term']['taxonomy'];
612 $term_id = $options['term']['term_id'];
613
614 $checked_ids = isset( $options['selected_taxonomies'] ) ? $options['selected_taxonomies'] : array();
615 $checked = in_array( (string) $term_id, $checked_ids, true );
616
617 if ( $checked ) {
618 $attributes = 'checked ' . $attributes;
619 }
620
621 $attributes = "name='$taxonomy' value='$term_id' " . $attributes;
622 }
623 }
624 // End: Filter functionality
625
626 return '<input type="checkbox" ' . $attributes . ' />';
627 }
628
629 private function link_block_element( $this_data, $attributes, $options ) {
630 $href = $this->get_href_value( $this_data, $options );
631
632 $preload_link = '';
633
634 if ( $href ) {
635 $attributes = preg_replace( '/href="([^"]+")/i', '', $attributes );
636
637 // check if this link is active
638 $attributes = $this->add_link_active_class( $href, $attributes, $this_data, $options );
639
640 $preload_link = $this->get_preload_link( $href, $this_data );
641 }
642
643 return '<a ' . $attributes . ' ' . ' href="' . $href . '" >' . $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options ) . '</a>' . $preload_link;
644 }
645
646 private function button_element( $this_data, $attributes, $options ) {
647 $href = $this->get_href_value( $this_data, $options );
648 $preload_link = '';
649
650 if ( $href ) {
651 $attributes = preg_replace( '/href="([^"]+")/i', '', $attributes );
652
653 // check if this link is active
654 $attributes = $this->add_link_active_class( $href, $attributes, $this_data, $options );
655
656 $preload_link = $this->get_preload_link( $href, $this_data );
657 }
658
659 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
660 $tag = isset( $this_data['properties'], $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'button';
661 if ( $href ) {
662 $tag = 'a';
663 }
664
665 return "<$tag $attributes href=\"$href\">
666 $children_markup
667 </$tag>
668 $preload_link";
669 }
670
671 private function svg_and_icon_element( $this_data, $attributes, $options ) {
672 $properties = $this_data['properties'];
673 $svg = isset( $properties['svgOuterHtml'] ) ? $properties['svgOuterHtml'] : '';
674 return str_replace( '<svg', "<svg $attributes", $svg );
675 }
676
677 /**
678 * Construct children markup
679 *
680 * @param array $children single element all childrens.
681 * @param array $options single element all options.
682 * @return string HTML markup.
683 */
684 private function construct_children_markup( $children, $options ) {
685 $markup = '';
686 if ( isset( $children ) && is_array( $children ) ) {
687 foreach ( $children as $child ) {
688 $markup .= $this->recGenHTML( $child, $options );
689 }
690 }
691 return $markup;
692 }
693 private function construct_items_collection_markup( $dynamic_content, $this_data, $options ) {
694 $collection_type = isset( $dynamic_content['collectionType'] ) ? $dynamic_content['collectionType'] : 'posts';
695 $show_pagination = isset( $dynamic_content['pagination'] ) ? $dynamic_content['pagination'] : true;
696 $pagination_type = isset( $dynamic_content['pagination_type'] ) ? $dynamic_content['pagination_type'] : 'numeric';
697
698 if ( ! $options ) {
699 $options = array();
700 }
701
702 $items_data = Utils::get_items_data_from_dynamic_contents( $dynamic_content, $options );
703
704 $data = $items_data['data'];
705 $pagination = $items_data['pagination'];
706 $itemType = $items_data['itemType'];
707
708 $additional_options = array();
709
710 // START: Filter functionality
711 if ( $collection_type === 'posts' && isset( $dynamic_content['post_type'] ) && $dynamic_content['post_type'] ) {
712 if ( isset( $_GET['post'][ $dynamic_content['post_type'] ] ) && is_array( $_GET['post'][ $dynamic_content['post_type'] ] ) ) {
713 $additional_options['selected_post_ids'] = $_GET['post'][ $dynamic_content['post_type'] ];
714 }
715 } elseif ( $collection_type === 'terms' ) {
716 if ( isset( $_GET['taxonomy'][ $dynamic_content['taxonomy'] ] ) && is_array( $_GET['taxonomy'][ $dynamic_content['taxonomy'] ] ) ) {
717 $additional_options['selected_taxonomies'] = $_GET['taxonomy'][ $dynamic_content['taxonomy'] ];
718 }
719 }
720
721 $additional_options['relation_type'] = $collection_type;
722 // END: Filter functionality
723
724 $options = array_merge(
725 $options,
726 $additional_options
727 );
728
729 $children = array();
730
731 foreach ( $this_data['children'] as $child ) {
732
733 if ( count( $data ) > 0 ) {
734 if ( isset( $this->data[ $child ] ) && $this->data[ $child ]['name'] === 'empty' ) {
735 continue;
736 }
737 } elseif ( count( $data ) === 0 ) {
738 if ( isset( $this->data[ $child ] ) && ( $this->data[ $child ]['name'] === 'pagination' || $this->data[ $child ]['name'] === 'items' ) ) {
739 continue;
740 }
741 }
742
743 $children[] = $this->recGenHTML(
744 $child,
745 array_merge(
746 $options,
747 array(
748 'collection' => $data,
749 'collection_count' => $pagination['total_count'] ?? 0,
750 'items_per_page' => $pagination['per_page'] ?? 0,
751 'page_no' => $pagination['current_page'] ?? 1,
752 'pagination' => $show_pagination,
753 'itemType' => $itemType,
754 'pagination_type' => $pagination_type,
755 'inside_collection' => true
756 )
757 )
758 );
759 }
760
761 return $children;
762 }
763
764
765 /**
766 * Construct collection markup
767 *
768 * @param array $collection single element collection data.
769 * @param array $this_data single element.
770 * @param array $attributes single element all attributes.
771 * @return string HTML markup.
772 */
773 private function construct_collection_markup( $children, $this_data, $attributes, $element_name = 'collection' ) {
774 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
775
776 $data_n_styles = array(
777 'blocks' => array(),
778 'styles' => array(),
779 );
780
781 DataHelper::get_data_and_styles_from_root( $this_data['id'], $data_n_styles, $this->data, $this->style_blocks );
782
783 if ( is_array( $children ) ) {
784 return $this->get_template(
785 'collection',
786 array(
787 'data' => $data_n_styles,
788 'attributes' => $attributes,
789 'children' => $children,
790 'tag' => $tag,
791 )
792 );
793 } else {
794 return '';
795 }
796 }
797
798 /**
799 * Generate custom code markup
800 *
801 * @param array $this_data single element block.
802 * @param array $attributes single element all attributes.
803 * @return string HTML markup.
804 */
805 private function custom_code( $this_data, $attributes ) {
806 $properties = $this_data['properties'];
807 if ( isset( $properties['content'] ) ) {
808 if ( isset( $properties['data-type'] ) && $properties['data-type'] === 'url' ) {
809 return '<div ' . $attributes . '>
810 <iframe
811 src="' . $properties['content'] . '"
812 title="' . $this_data['id'] . '"
813 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
814 style="width:100%;height:100%;"
815 ></iframe>
816 </div>';
817 } else {
818 return '<div ' . $attributes . '>' . $properties['content'] . '</div>';
819 }
820 }
821 return '';
822 }
823
824 /**
825 * Generate Map element markup
826 *
827 * @param array $this_data single element block.
828 * @param array $attributes single element all attributes.
829 * @return string HTML markup.
830 */
831 private function map_element( $this_data, $attributes ) {
832 $properties = isset( $this_data['properties']['map'] ) ? $this_data['properties']['map'] : false;
833 if ( ! $properties ) {
834 return '';
835 }
836 return '<div ' . $attributes . '></div>';
837 }
838
839 /**
840 * Generate Video element markup
841 *
842 * @param array $this_data single element block.
843 * @param array $attributes single element all attributes.
844 * @return string HTML markup.
845 */
846 private function video_element( $this_data, $attributes, $options ) {
847 $properties = $this_data['properties'];
848 $name = $properties['attributes']['name'];
849 $src = $properties['attributes']['src'];
850 $scale = isset( $properties['scale'] ) ? $properties['scale'] : 'cover';
851 $aspectRatio = isset( $properties['aspectRatio'] ) ? $properties['aspectRatio'] : '16 / 9';
852 $type = $properties['attributes']['type'];
853 $controls = $properties['attributes']['controls'];
854 $play_inline = isset( $properties['attributes']['playInline'] ) ? $properties['attributes']['playInline'] : true;
855 $duration = isset( $properties['attributes']['duration'] ) ? $properties['attributes']['duration'] : 0;
856 $start_time = $properties['attributes']['startTime'];
857 $end_time = isset( $properties['attributes']['endTime'] ) ? $properties['attributes']['endTime'] : $duration;
858 $span_attr_string = $this->getAllAttributes(
859 $this_data,
860 array(
861 'class' => true,
862 'data-kirki' => true,
863 'rest' => false,
864 ),
865 );
866 $dynamic_content = isset( $properties['dynamicContent'] ) ? $properties['dynamicContent'] : false;
867
868 if ( isset( $this_data['properties']['attributes']['muted'] ) && $this_data['properties']['attributes']['muted'] === false ) {
869 unset( $this_data['properties']['attributes']['muted'] );
870 }
871
872 $video_attr_string = $this->getAllAttributes(
873 $this_data,
874 array(
875 'class' => false,
876 'data-kirki' => false,
877 'src' => false,
878 'autoPlay' => false,
879 'controls' => false,
880 'dataVideoPlayListener' => false,
881 'rest' => true,
882 ),
883 );
884
885 if ( $play_inline ) {
886 $video_attr_string .= ' playsinline';
887 }
888
889 $poster = '';
890 if ( isset( $properties['thumbnail'], $properties['thumbnail']['status'] ) && $properties['thumbnail']['status'] ) {
891 $poster = isset( $properties['thumbnail'], $properties['thumbnail']['url'] ) ? $properties['thumbnail']['url'] : null;
892 }
893
894 $video_src = '';
895 if ( $dynamic_content ) {
896 // TODO: Need to get dynamic course video or image url for tutor lms using hook.
897 if ( isset( $options['itemType'] ) ) {
898 if ( $options['itemType'] === 'post' ) {
899 if ( $options['post']->{$dynamic_content['value']} ) {
900 $video_obj = $options['post']->{$dynamic_content['value']};
901
902 if ( isset( $video_obj['url'] ) ) {
903 $video_src = $video_obj['url'];
904 }
905 } else {
906 $video_obj = HelperFunctions::get_post_dynamic_content( $dynamic_content['value'], isset( $options['post'] ) ? $options['post'] : null );
907
908 if ( isset( $video_obj['url'] ) ) {
909 $video_src = $video_obj['url'];
910 }
911 }
912 }
913 } else {
914 $video_obj = HelperFunctions::get_post_dynamic_content( $dynamic_content['value'], isset( $options['post'] ) ? $options['post'] : null );
915
916 if ( isset( $video_obj['url'] ) ) {
917 $video_src = $video_obj['url'];
918 }
919 }
920 }
921
922 // if no dynamic video src then check original source for fallback video url
923 if ( ! $video_src && ! $src ) {
924 return '';
925 } else {
926 $src = $video_src ? $video_src : $src;
927 }
928
929 $video_url_type = $this->video_type( $src );
930
931 if ( $video_url_type === 'youtube' ) {
932 $video_src = $this->get_youtube_embed_url( $src ) . '?controls=' . $controls . '&amp;start=' . $start_time . '&end=' . $end_time;
933 } elseif ( $video_url_type === 'vimeo' ) {
934 $video_src = $this->get_vimeo_embed_url( $src ) . '?controls=' . $controls . '&amp;start=' . $start_time . '&end=' . $end_time;
935 } elseif ( $video_url_type === 'tiktok' ) {
936 $video_src = $this->get_tiktok_embed_url( $src ) . '?controls=' . $controls . '&amp;start=' . $start_time . '&end=' . $end_time;
937 } else {
938 $video_src = $src . '#t=' . $start_time . ',' . $duration;
939 }
940
941 $html = '<span ' . $span_attr_string . '>';
942 if ( $video_url_type === 'youtube' || $video_url_type === 'vimeo' || $video_url_type === 'tiktok' ) {
943 $html .= '<iframe height="100%" width="100%" title="' . $name . '" src="' . $video_src . '" style="aspect-ratio: ' . $aspectRatio . ';" ></iframe>';
944 } else {
945 if ( isset( $properties['attributes']['lazy'] ) && $properties['attributes']['lazy'] ) {
946 // `data-src` is used in `source` tag to lazy load the video using JS. Check JS preview script for videos.
947 $html .= '<video' . $video_attr_string . ' style="object-fit: ' . $scale . '; aspect-ratio: ' . $aspectRatio . ';" poster="' . $poster . '">
948 <source data-src="' . $video_src . '" type="' . $type . '"></source></video>';
949 } else {
950 $html .= '<video' . $video_attr_string . ' style="object-fit: ' . $scale . '; aspect-ratio: ' . $aspectRatio . ';" poster="' . $poster . '">
951 <source src="' . $video_src . '" type="' . $type . '"></source></video>';
952 }
953 }
954 $html .= '</span>';
955 return $html;
956 }
957
958 /**
959 * Generate Video element markup
960 *
961 * @param array $this_data single element block.
962 * @param array $attributes single element all attributes.
963 * @return string HTML markup.
964 */
965 private function radio_group( $this_data, $attributes ) {
966 $properties = $this_data['properties'];
967 $data = $properties['data']['child'];
968 $name = $properties['attributes']['name'];
969
970 $str = '<div ' . $attributes . '>';
971
972 foreach ( $data as $key => $child ) {
973 $attrs = "type='radio' name='{$name}' value='{$child['value']}'";
974 $attrs .= $child['checked'] ? ' checked' : '';
975
976 $str .= '<label>
977 <input ' . $attrs . '/>
978 <span>' . $child['content'] . '</span>
979 </label>';
980 }
981 $str .= '</div>';
982 return $str;
983 }
984
985 /**
986 * Generate popup-body element markup
987 *
988 * @param array $this_data single element block.
989 * @param array $attributes single element all attributes.
990 * @return string HTML markup.
991 */
992 private function popup_element( $this_data, $attributes, $options ) {
993 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
994 return '<div class="kirki-popup-body-wrapper"><div ' . $attributes . '>' . $children_markup . '</div></div>';
995 }
996
997 /**
998 * Recursively Generate menu items element markup
999 *
1000 * @param array $submenus all submenus.
1001 * @param int $depth menu nesting info.
1002 * @return string HTML markup.
1003 */
1004 private function rec_gen_menu_items( $submenus, $depth ) {
1005 if ( count( $submenus ) === 0 ) {
1006 return '';
1007 }
1008 if ( $depth === 0 ) {
1009 $str = '<ul class="' . 'kirki-nav-menu">';
1010 } else {
1011 $str = '<ul class="' . 'kirki-element-submenu">';
1012 }
1013 $depth++;
1014 foreach ( $submenus as $key => $menu ) {
1015 $str .= '<li class="' . 'kirki-element-nav-item"><a class="' . 'kirki-link-element" href="' . $menu->url . '" target="' . $menu->target . '">' . $menu->title . '</a>' . $this->rec_gen_menu_items( $menu->submenus, $depth ) . '</li>';
1016 }
1017 $str .= '</ul>';
1018 return $str;
1019 }
1020
1021 /**
1022 * This method will add css value and unit
1023 *
1024 * @param array $value css value and unit pair.
1025 * @return string 5px | auto.
1026 */
1027 private function add_unit( $value ) {
1028 if ( isset( $value['unit'] ) && $value['unit'] !== 'auto' ) {
1029 return $value['value'] . $value['unit'];
1030 }
1031 return 'auto';
1032 }
1033
1034 /**
1035 * Generate Image element markup
1036 *
1037 * @param array $this_data single element block.
1038 * @param array $attributes single element all attributes.
1039 * @param array $options single element all options.
1040 * @return string HTML markup.
1041 */
1042 private function image_element( $this_data, $attributes, $options ) {
1043 $properties = $this_data['properties'];
1044 $svg_outer_html = isset( $properties['svgOuterHtml'] ) ? $properties['svgOuterHtml'] : false;
1045 $dynamic_content = isset( $properties['dynamicContent'] ) ? $properties['dynamicContent'] : false;
1046 $dynamic_alt = isset( $properties['dynamicAlt'] ) ? $properties['dynamicAlt'] : false;
1047
1048 $user_initials = false;
1049
1050 $src = '';
1051
1052 if ( $dynamic_content ) {
1053 $image_data = array();
1054
1055 if ( $dynamic_content['type'] === 'reference' && isset( $options['post'], $options['post']->ID ) ) {
1056 $content_info = array(
1057 'collectionItem' => array(
1058 'ID' => $options['post']->ID,
1059 ),
1060 'dynamicContent' => $dynamic_content,
1061 );
1062
1063 $content = apply_filters( 'kirki_dynamic_content', false, $content_info );
1064 $image_data = $content;
1065 } elseif ( $dynamic_content['type'] === 'gallery' ) {
1066 $content_info = array(
1067 'collectionItem' => array(
1068 'id' => $options['gallery']['id'],
1069 'url' => $options['gallery']['url'],
1070
1071 ),
1072 'dynamicContent' => $dynamic_content,
1073 );
1074
1075 $content = apply_filters( 'kirki_dynamic_content', false, $content_info );
1076
1077 $image_data = $content;
1078 } elseif ( $dynamic_content['type'] === 'user' ) {
1079 $user_id = isset( $options['user'], $options['user']['ID'] ) ? $options['user']['ID'] : null;
1080 $src = HelperFunctions::get_user_dynamic_content( $dynamic_content['value'], $user_id, $dynamic_content['meta'] ?? '' );
1081 $image_data = array(
1082 'src' => $src,
1083 );
1084 } elseif ( isset( $options['itemType'] ) ) {
1085 if ( $options['itemType'] === 'post' ) {
1086 if ( isset( $options['post']->{$dynamic_content['value']} ) && $options['post']->{$dynamic_content['value']} ) {
1087 $image_data = $options['post']->{$dynamic_content['value']};
1088 } else {
1089 $image_data = HelperFunctions::get_post_dynamic_content( $dynamic_content['value'], isset( $options['post'] ) ? $options['post'] : null );
1090 }
1091 } elseif ( $options['itemType'] === 'comment' ) {
1092 if ( isset( $options['comment']->{$dynamic_content['value']} ) && $options['comment']->{$dynamic_content['value']} ) {
1093 $image_data = $options['comment']->{$dynamic_content['value']};
1094 }
1095 } elseif ( $options['itemType'] === 'user' ) {
1096 if ( isset( $options['user'], $options['user'][ $dynamic_content['value'] ] ) ) {
1097 $image_data = array(
1098 'src' => $options['user'][ $dynamic_content['value'] ],
1099 );
1100 }
1101 }
1102 } else {
1103 $post = isset( $options['post'] ) ? $options['post'] : get_post( HelperFunctions::get_post_id_if_possible_from_url() );
1104 $image_data = HelperFunctions::get_post_dynamic_content( $dynamic_content['value'], $post, isset( $dynamic_content['meta'] ) ? $dynamic_content['meta'] : null );
1105 if ( is_string( $image_data ) ) {
1106 $image_data = array(
1107 'src' => $image_data,
1108 );
1109 }
1110 }
1111
1112 if ( isset( $image_data['src'] ) ) {
1113 $src = $image_data['src'];
1114 }
1115
1116 if ( isset( $image_data['wp_attachment_id'] ) ) {
1117 $properties['wp_attachment_id'] = $image_data['wp_attachment_id'];
1118 }
1119 }
1120
1121 if ( $dynamic_content && $src ) {
1122 // replace src and alt.
1123 $attributes = preg_replace( array( '/src="([^"]+")/i' ), array( '' ), $attributes );
1124 } else {
1125 $src = isset( $properties['attributes']['src'] ) ? $properties['attributes']['src'] : '';
1126 }
1127
1128 // set dynamic alt here
1129 if ( $dynamic_alt ) {
1130 $new_alt = Utils::getDynamicRichTextValue( $dynamic_alt, $options );
1131
1132 // Update properties array for consistency
1133 $properties['attributes']['alt'] = $new_alt;
1134
1135 // Replace alt attribute inside the HTML string
1136 $attributes = preg_replace(
1137 '/alt="[^"]*"/i',
1138 'alt="' . esc_attr( $new_alt ) . '"',
1139 $attributes
1140 );
1141 } elseif ( empty( $properties['attributes']['alt'] ) ) {
1142 // get WordPress attachment alt text if alt is empty
1143 if ( isset( $properties['wp_attachment_id'] ) && $properties['wp_attachment_id'] ) {
1144 $alt_text = get_post_meta( $properties['wp_attachment_id'], '_wp_attachment_image_alt', true );
1145 $attributes = preg_replace( '/alt="([^"]*)"/i', 'alt="' . esc_attr( $alt_text ) . '"', $attributes );
1146 }
1147 }
1148
1149 $srcset = null;
1150 if ( ! $dynamic_content && isset( $properties['wp_attachment_id'] ) && $properties['wp_attachment_id'] ) {
1151 $srcset = wp_get_attachment_image_srcset( $properties['wp_attachment_id'] );
1152 $attributes .= $srcset ? ' srcset="' . $srcset . '"' : '';
1153
1154 $sizes = '';
1155 $a_meta_data = wp_get_attachment_metadata( $properties['wp_attachment_id'] );
1156 if ( $a_meta_data && isset( $a_meta_data['width'] ) ) {
1157 $width = $a_meta_data['width'];
1158 $sizes = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
1159 $attributes .= $sizes ? ' sizes="' . $sizes . '"' : '';
1160 }
1161 }
1162
1163 if ( ( isset( $properties['load'] ) && $properties['load'] !== 'auto' ) || ! isset( $properties['load'] ) ) {
1164 $attributes .= isset( $properties['load'] ) ? ' loading="' . $properties['load'] . '"' : ' loading="lazy"';
1165 }
1166
1167 if ( isset( $properties['width'] ) ) {
1168 $attributes .= ' width="' . $this->add_unit( $properties['width'] ) . '"';
1169
1170 }
1171 if ( isset( $properties['height'] ) ) {
1172 $attributes .= ' height="' . $this->add_unit( $properties['height'] ) . '"';
1173 }
1174
1175 if ( ! $src ) {
1176 return '';
1177 }
1178
1179 $str = '';
1180
1181 if ( $svg_outer_html ) {
1182 $str .= '<img ' . $attributes . ' src="' . $src . '"/>
1183 <span>' . $svg_outer_html . '</span>';
1184 } else {
1185 $str .= '<img ' . $attributes . ' src="' . $src . '"/>';
1186 }
1187
1188 return $str;
1189 }
1190
1191 /**
1192 * Get video type from url
1193 *
1194 * @param string $url single element block.
1195 * @return string|bool video url type.
1196 */
1197 private function video_type( $url ) {
1198 if ( strpos( $url, 'youtube' ) > 0 || strpos( $url, 'youtu.be' ) > 0 ) {
1199 return 'youtube';
1200 } elseif ( strpos( $url, 'vimeo' ) > 0 ) {
1201 return 'vimeo';
1202 } elseif ( strpos( $url, 'tiktok' ) > 0 ) {
1203 return 'tiktok';
1204 } else {
1205 return false;
1206 }
1207 }
1208
1209 /**
1210 * Get video type from url
1211 *
1212 * @param string $url single element block.
1213 * @return string youtube url type.
1214 */
1215 private function get_youtube_embed_url( $url ) {
1216 preg_match( '%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $url, $match );
1217 return isset( $match[1] ) ? 'https://www.youtube.com/embed/' . $match[1] : '#';
1218 }
1219
1220 private function get_vimeo_embed_url( $url ) {
1221 preg_match( '/vimeo\.com\/(?:.*\/)?(\d+)/', $url, $matches );
1222 return isset( $matches[1] ) ? 'https://player.vimeo.com/video/' . $matches[1] : '#';
1223 }
1224
1225 private function get_tiktok_embed_url( $url ) {
1226 preg_match( '/tiktok\.com\/(?:@[\w.-]+\/)?video\/(\d+)/', $url, $matches );
1227 return isset( $matches[1] ) ? 'https://www.tiktok.com/embed/' . $matches[1] : '#';
1228 }
1229
1230 /**
1231 * Generate symbol markup
1232 *
1233 * @param array $this_data single element block.
1234 * @param array $attributes single element all attributes.
1235 * @return string HTML markup.
1236 */
1237 private function generate_symbol_html( $this_data, $attributes, $options = array() ) {
1238 $properties = $this_data['properties'];
1239 $symbol_id = $properties['symbolId'];
1240 $symbolElementProp = isset( $properties['symbolElProps'] ) ? $properties['symbolElProps'] : false;
1241 $symbol = Symbol::get_single_symbol( $symbol_id, true, false, $symbolElementProp );
1242
1243 if ( ! $symbol ) {
1244 return '';
1245 }
1246
1247 $symbol_data = $symbol['symbolData'];
1248 if ( ! $symbol_data || ! isset( $symbol_data['data'] ) ) {
1249 return '';
1250 }
1251 $s = HelperFunctions::rec_update_data_id_then_return_new_html( $symbol_data['data'], $symbol_data['styleBlocks'], $symbol_data['root'], $options );
1252 $fonts_links = '';
1253 if ( isset( $symbol_data['customFonts'] ) ) {
1254 foreach ( $symbol_data['customFonts'] as $key => $f ) {
1255 if ( isset( $f['fontUrl'] ) ) {
1256 $fonts_links .= HelperFunctions::getFontsHTMLMarkup( $f );
1257 }
1258 }
1259 }
1260 return '<div ' . $attributes . '>' . $fonts_links . $s . '</div>';
1261 }
1262
1263 /**
1264 * Get html template.
1265 *
1266 * @param string $template template file name.
1267 * @param array $vars if extra variable needs inside template file.
1268 * @return string $message
1269 */
1270 private function get_template( $template, $vars ) {
1271 ob_start();
1272 include __DIR__ . "/templates/$template.view.php";
1273 $message = ob_get_contents();
1274 ob_end_clean();
1275 return $message;
1276 }
1277
1278 /**
1279 * Generate File upload element markup
1280 *
1281 * @param array $this_data single element block.
1282 * @param array $attributes single element all attributes.
1283 * @param array $options single element all options.
1284 * @return string HTML markup.
1285 */
1286 private function file_upload_element( $this_data, $attributes, $options ) {
1287 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1288 $data_attr = isset( $this_data['properties']['attributes'] ) ? $this_data['properties']['attributes'] : array();
1289 $options = array_merge(
1290 $options,
1291 array(
1292 'file_upload' => array(
1293 'name' => isset( $data_attr['name'] ) ? $data_attr['name'] : '',
1294 'accept' => isset( $data_attr['accept'] ) ? $data_attr['accept'] : '',
1295 'required' => isset( $data_attr['required'] ) ? $data_attr['required'] : '',
1296 'maxFileSize' => isset( $this_data['properties']['maxFileSize'] ) ? $this_data['properties']['maxFileSize'] : 2,
1297 ),
1298 )
1299 );
1300
1301 $attributes = $this->getAllAttributes(
1302 $this_data,
1303 array(
1304 'name' => false,
1305 'accept' => false,
1306 'required' => false,
1307 'rest' => true,
1308 ),
1309 );
1310
1311 $children_markup = $this->construct_children_markup( isset( $this_data['children'] ) ? $this_data['children'] : array(), $options );
1312 return "<$tag $attributes>
1313 $children_markup
1314 </$tag>";
1315 }
1316
1317 /**
1318 * Generate File input threshold text element markup
1319 *
1320 * @param array $this_data single element block.
1321 * @param array $attributes single element all attributes.
1322 * @param array $options single element all options.
1323 * @return string HTML markup.
1324 */
1325
1326 private function file_upload_threshold_text( $this_data, $attributes, $options ) {
1327 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'span';
1328 $max_file_size = isset( $options['file_upload']['maxFileSize'] ) ? $options['file_upload']['maxFileSize'] : 2;
1329
1330 return "<$tag $attributes>Max file size " . $max_file_size . "MB.</$tag>";
1331 }
1332
1333 /**
1334 * Generate File input element markup
1335 *
1336 * @param array $this_data single element block.
1337 * @param array $attributes single element all attributes.
1338 * @param array $options single element all options.
1339 * @return string HTML markup.
1340 */
1341 private function file_input_element( $this_data, $attributes, $options ) {
1342 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1343 $children = $this_data['children'] ?? array();
1344 $markup = '';
1345
1346 $name = isset( $options['file_upload']['name'] ) ? $options['file_upload']['name'] : '';
1347 $accept = isset( $options['file_upload']['accept'] ) ? $options['file_upload']['accept'] : '';
1348 $required = isset( $options['file_upload']['required'] ) ? $options['file_upload']['required'] : '';
1349 $max_file_size = isset( $options['file_upload']['maxFileSize'] ) ? $options['file_upload']['maxFileSize'] : 2;
1350
1351 foreach ( $children as $child ) {
1352 $markup .= $this->recGenHTML( $child, $options );
1353 }
1354
1355 return "<$tag $attributes>
1356 $markup
1357 <input
1358 type=\"file\"
1359 readonly
1360 style=\"display: none;\"
1361 name=\"$name\"
1362 accept=\"$accept\"
1363 required=\"$required\"
1364 kirki-max_file_size=$max_file_size
1365 />
1366 </$tag>";
1367 }
1368
1369 private function slider_nav_element( $this_data, $attributes, $options ) {
1370 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1371 $collection = isset( $options['collection'] ) ? $options['collection'] : array();
1372 $itemType = isset( $options['itemType'] ) ? $options['itemType'] : 'post';
1373 $slider_nav_item_id = isset( $this_data['children'][0] ) ? $this_data['children'][0] : '';
1374 $slider_nav_children = isset( $this_data['children'] ) ? $this_data['children'] : array();
1375 $slider_mode = $options['slider_mode'] ?? 'manual';
1376 $page_no = isset( $options['page_no'] ) ? (int) $options['page_no'] : 1;
1377
1378 $children = array();
1379
1380 if ( $slider_mode === 'manual' ) {
1381 foreach ( $slider_nav_children as $key => $child_id ) {
1382 $children[] = $this->recGenHTML( $child_id, $options );
1383 }
1384 } else { // dynamic
1385 if ( empty( $collection ) || ! $slider_nav_item_id ) {
1386 return '';
1387 }
1388
1389 $items_per_page = isset( $options['items_per_page'] ) ? (int) $options['items_per_page'] : count( $collection );
1390
1391 $start = ( $page_no - 1 ) * $items_per_page;
1392 $items_to_render = array_slice( $collection, $start, $items_per_page );
1393
1394 foreach ( $items_to_render as $key => $collection_item ) {
1395 $item_index = HelperFunctions::get_current_item_index( $key + 1, $options );
1396
1397 $merged_options = array_merge(
1398 $options,
1399 array(
1400 'itemType' => $itemType,
1401 $itemType => $collection_item,
1402 'item_index' => $item_index,
1403 'nav_index' => $start + $key,
1404 'is_active' => $key === 0,
1405 )
1406 );
1407
1408 $children[] = HelperFunctions::rec_update_data_id_then_return_new_html( $this->data, $this->style_blocks, $slider_nav_item_id, $merged_options, ( $key === 0 ) );
1409 }
1410 }
1411
1412 return $this->get_template(
1413 'pagination-item',
1414 array(
1415 'attributes' => $attributes,
1416 'children' => $children,
1417 'current_page' => 1,
1418 'page_number' => $page_no,
1419 'tag' => $tag,
1420 )
1421 );
1422 }
1423
1424 private function tabs_element( $this_data, $attributes, $options ) {
1425 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1426 $children = $this_data['children'] ?? array();
1427 $markup = '';
1428
1429 foreach ( $children as $child ) {
1430 $merged_options = array_merge(
1431 $options,
1432 array(
1433 'active_tab_index' => isset($this_data['properties'],$this_data['properties']['active_tab']) ? $this_data['properties']['active_tab'] : 0,
1434 )
1435 );
1436
1437 $markup .= $this->recGenHTML( $child, $merged_options );
1438 }
1439
1440 return "<$tag $attributes>
1441 $markup
1442 </$tag>";
1443 }
1444
1445 private function tab_element( $this_data, $attributes, $options ) {
1446 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1447 $children = $this_data['children'] ?? array();
1448 $markup = '';
1449
1450 $active_tab_index = isset( $options['active_tab_index'] ) ? $options['active_tab_index'] : 0;
1451 $item_index = isset( $options['item_index'] ) ? $options['item_index'] : 0;
1452
1453 if ( $active_tab_index === $item_index ) {
1454 // Add kirki-current-tab class to attributes
1455 if ( preg_match( '/class="([^"]*)"/', $attributes, $matches ) ) {
1456 // Class attribute exists, append to it
1457 $existing_classes = $matches[1];
1458 $new_classes = trim( $existing_classes . ' kirki-current-tab' );
1459 $attributes = preg_replace(
1460 '/class="[^"]*"/',
1461 'class="' . esc_attr( $new_classes ) . '"',
1462 $attributes
1463 );
1464 } else {
1465 // No class attribute, add it
1466 $attributes .= ' class="kirki-current-tab"';
1467 }
1468 }
1469
1470 // foreach ( $children as $child ) {
1471 // $markup .= $this->recGenHTML( $child, $options );
1472 // }
1473 $markup = $this->get_child_content_or_childrens( $this_data, $options );
1474 return "<$tag $attributes>
1475 $markup
1476 </$tag>";
1477 }
1478
1479 private function tab_pane_element( $this_data, $attributes, $options ) {
1480 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1481 $children = $this_data['children'] ?? array();
1482 $markup = '';
1483
1484 $active_tab_index = isset( $options['active_tab_index'] ) ? $options['active_tab_index'] : 0;
1485 $item_index = isset( $options['item_index'] ) ? $options['item_index'] : 0;
1486
1487 if ( $active_tab_index === $item_index ) {
1488 // Add kirki-tab-active class to attributes
1489 if ( preg_match( '/class="([^"]*)"/', $attributes, $matches ) ) {
1490 // Class attribute exists, append to it
1491 $existing_classes = $matches[1];
1492 $new_classes = trim( $existing_classes . ' kirki-tab-active' );
1493 $attributes = preg_replace(
1494 '/class="[^"]*"/',
1495 'class="' . esc_attr( $new_classes ) . '"',
1496 $attributes
1497 );
1498 } else {
1499 // No class attribute, add it
1500 $attributes .= ' class="kirki-tab-active"';
1501 }
1502 }
1503
1504 $markup = $this->get_child_content_or_childrens( $this_data, $options );
1505 return "<$tag $attributes>
1506 $markup
1507 </$tag>";
1508 }
1509
1510 private function tab_menu_and_content_element( $this_data, $attributes, $options ) {
1511 $tag = isset( $this_data['properties']['tag'] ) ? $this_data['properties']['tag'] : 'div';
1512 $children = $this_data['children'] ?? array();
1513 $markup = '';
1514
1515 $markup = $this->get_child_content_or_childrens( $this_data, $options );
1516 if($this_data['name'] === 'tab_menu'){
1517 $attributes .= ' data-kirki_tab="tab_menu"';
1518 }else{
1519 $attributes .= ' data-kirki_tab="tab_content"';
1520 }
1521 return "<$tag $attributes>
1522 $markup
1523 </$tag>";
1524 }
1525
1526 }