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