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