PluginProbe
Testimonial Customer Feedback / trunk
Testimonial Customer Feedback vtrunk
1.2.8 1.2.7 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 All 76 releases
testimonial-maker / shortcode.php

shortcode.php in Testimonial Customer Feedback trunk, at shortcode.php

1,192 lines 51.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH'))
3 exit;
4
5 // Star rating display function
6 if (!function_exists('tml_display_star_rating')) {
7 function tml_display_star_rating($rating, $show_rating, $settings = array())
8 {
9 if ($show_rating == 'false')
10 return '';
11 $rating = intval($rating);
12 if ($rating < 0)
13 $rating = 0;
14 if ($rating > 5)
15 $rating = 5;
16
17 $style = isset($settings['style']) ? $settings['style'] : (isset($GLOBALS['tml_active_settings']['tml_rating_style']) ? $GLOBALS['tml_active_settings']['tml_rating_style'] : 'star');
18 $color = isset($settings['color']) ? $settings['color'] : (isset($GLOBALS['tml_active_settings']['tml_star_rating_color']) ? $GLOBALS['tml_active_settings']['tml_star_rating_color'] : '#FFD700');
19 $default_color = isset($settings['default_color']) ? $settings['default_color'] : (isset($GLOBALS['tml_active_settings']['tml_ds_rating_default_color']) ? $GLOBALS['tml_active_settings']['tml_ds_rating_default_color'] : '#E0E0E0');
20 $size = isset($settings['size']) ? $settings['size'] : (isset($GLOBALS['tml_active_settings']['tml_ds_rating_size']) ? $GLOBALS['tml_active_settings']['tml_ds_rating_size'] : '19');
21 $gap = isset($settings['gap']) ? $settings['gap'] : (isset($GLOBALS['tml_active_settings']['tml_ds_rating_gap']) ? $GLOBALS['tml_active_settings']['tml_ds_rating_gap'] : '2');
22 $alignment = isset($settings['alignment']) ? $settings['alignment'] : (isset($GLOBALS['tml_active_settings']['tml_star_rating_alignment']) ? $GLOBALS['tml_active_settings']['tml_star_rating_alignment'] : 'center');
23
24 $justify = 'center';
25 if ($alignment == 'left') {
26 $justify = 'flex-start';
27 } elseif ($alignment == 'right') {
28 $justify = 'flex-end';
29 }
30
31 $icons = array(
32 'star' => array('filled' => 'fa-star', 'empty' => 'fa-star-o'),
33 'heart' => array('filled' => 'fa-heart', 'empty' => 'fa-heart'),
34 'thumbs-up' => array('filled' => 'fa-thumbs-up', 'empty' => 'fa-thumbs-o-up'),
35 'smile' => array('filled' => 'fa-smile-o', 'empty' => 'fa-smile-o'),
36 'diamond' => array('filled' => 'fa-diamond', 'empty' => 'fa-diamond'),
37 'badge' => array('filled' => 'fa-certificate', 'empty' => 'fa-certificate'),
38 'trophy' => array('filled' => 'fa-trophy', 'empty' => 'fa-trophy'),
39 'bell' => array('filled' => 'fa-bell', 'empty' => 'fa-bell-o')
40 );
41
42 $filled_icon = isset($icons[$style]) ? $icons[$style]['filled'] : 'fa-star';
43 $empty_icon = isset($icons[$style]) ? $icons[$style]['empty'] : 'fa-star-o';
44
45 $output = '<div class="testimonial-rating" style="display:flex !important; justify-content:' . esc_attr($justify) . ' !important; gap:' . esc_attr($gap) . 'px; margin-bottom:10px; width:100% !important;">';
46 for ($i = 1; $i <= 5; $i++) {
47 $icon_class = ($i <= $rating) ? $filled_icon : $empty_icon;
48 $icon_color = ($i <= $rating) ? $color : $default_color;
49 $opacity = ($i > $rating && $style == $filled_icon) ? '0.3' : '1'; // Fallback for icons without outline version
50
51 $output .= '<i class="fa ' . esc_attr($icon_class) . '" style="color:' . esc_attr($icon_color) . '; font-size:' . esc_attr($size) . 'px; opacity:' . $opacity . ';"></i>';
52 }
53 $output .= '</div>';
54 return $output;
55 }
56 }
57
58 // Social profiles display function
59 if (!function_exists('tml_display_social_profiles')) {
60 function tml_display_social_profiles($testimonial_post_settings, $settings = array())
61 {
62 if (empty($settings)) {
63 $settings = isset($GLOBALS['tml_active_settings']) ? $GLOBALS['tml_active_settings'] : array();
64 }
65
66 $tml_social_share = isset($settings['tml_social_share']) ? $settings['tml_social_share'] : 'false';
67 if ($tml_social_share == 'false') {
68 return '';
69 }
70
71 $social_profiles = isset($testimonial_post_settings['social_profiles']) ? $testimonial_post_settings['social_profiles'] : array();
72 if (empty($social_profiles)) {
73 return '';
74 }
75
76 $tml_social_margin_top = isset($settings['tml_social_margin_top']) ? $settings['tml_social_margin_top'] : '10';
77 $tml_social_margin_right = isset($settings['tml_social_margin_right']) ? $settings['tml_social_margin_right'] : '0';
78 $tml_social_margin_bottom = isset($settings['tml_social_margin_bottom']) ? $settings['tml_social_margin_bottom'] : '10';
79 $tml_social_margin_left = isset($settings['tml_social_margin_left']) ? $settings['tml_social_margin_left'] : '0';
80
81 $tml_social_border_width = isset($settings['tml_social_border_width']) ? $settings['tml_social_border_width'] : '1';
82 $tml_social_border_style = isset($settings['tml_social_border_style']) ? $settings['tml_social_border_style'] : 'solid';
83 $tml_social_border_color = isset($settings['tml_social_border_color']) ? $settings['tml_social_border_color'] : '#e2e8f0';
84
85 $tml_social_icon_type = isset($settings['tml_social_icon_type']) ? $settings['tml_social_icon_type'] : 'original';
86 $tml_social_icon_bg = isset($settings['tml_social_icon_bg']) ? $settings['tml_social_icon_bg'] : '#f0f6fc';
87 $tml_social_icon_color = isset($settings['tml_social_icon_color']) ? $settings['tml_social_icon_color'] : '#888888';
88
89 static $brand_colors_printed = false;
90 $styles = '';
91 if (!$brand_colors_printed) {
92 $tml_social_icon_hover_bg = isset($settings['tml_social_icon_hover_bg']) ? $settings['tml_social_icon_hover_bg'] : '#f0f6fc';
93 $tml_social_icon_hover_color = isset($settings['tml_social_icon_hover_color']) ? $settings['tml_social_icon_hover_color'] : '#000000';
94 $tml_social_border_hover_color = isset($settings['tml_social_border_hover_color']) ? $settings['tml_social_border_hover_color'] : '#e2e8f0';
95
96 $styles = '<style>
97 /* Brand Colors - Background and Border */
98 .tml-social-original.tml-social-facebook {
99 background: #1877f2 !important;
100 border-color: #1877f2 !important;
101 color: #fff !important;
102 }
103 .tml-social-original.tml-social-twitter {
104 background: #1da1f2 !important;
105 border-color: #1da1f2 !important;
106 color: #fff !important;
107 }
108 .tml-social-original.tml-social-linkedin {
109 background: #0a66c2 !important;
110 border-color: #0a66c2 !important;
111 color: #fff !important;
112 }
113 .tml-social-original.tml-social-instagram {
114 background: #e4405f !important;
115 border-color: #e4405f !important;
116 color: #fff !important;
117 }
118 .tml-social-original.tml-social-youtube {
119 background: #ff0000 !important;
120 border-color: #ff0000 !important;
121 color: #fff !important;
122 }
123 .tml-social-original.tml-social-pinterest {
124 background: #bd081c !important;
125 border-color: #bd081c !important;
126 color: #fff !important;
127 }
128 .tml-social-original.tml-social-whatsapp {
129 background: #25d366 !important;
130 border-color: #25d366 !important;
131 color: #fff !important;
132 }
133 .tml-social-original.tml-social-github {
134 background: #181717 !important;
135 border-color: #181717 !important;
136 color: #fff !important;
137 }
138 .tml-social-original.tml-social-skype {
139 background: #00aff0 !important;
140 border-color: #00aff0 !important;
141 color: #fff !important;
142 }
143 .tml-social-original.tml-social-vimeo {
144 background: #1ab7ea !important;
145 border-color: #1ab7ea !important;
146 color: #fff !important;
147 }
148 .tml-social-original.tml-social-snapchat {
149 background: #fffc00 !important;
150 border-color: #fffc00 !important;
151 color: #000 !important;
152 }
153 .tml-social-original.tml-social-slack {
154 background: #4a154b !important;
155 border-color: #4a154b !important;
156 color: #fff !important;
157 }
158 .tml-social-original.tml-social-dribbble {
159 background: #ea4c89 !important;
160 border-color: #ea4c89 !important;
161 color: #fff !important;
162 }
163 .tml-social-original.tml-social-dropbox {
164 background: #0061ff !important;
165 border-color: #0061ff !important;
166 color: #fff !important;
167 }
168 .tml-social-original.tml-social-wordpress {
169 background: #21759b !important;
170 border-color: #21759b !important;
171 color: #fff !important;
172 }
173 .tml-social-original.tml-social-slideshare {
174 background: #0077b5 !important;
175 border-color: #0077b5 !important;
176 color: #fff !important;
177 }
178 .tml-social-original.tml-social-vk {
179 background: #4c75a1 !important;
180 border-color: #4c75a1 !important;
181 color: #fff !important;
182 }
183 .tml-social-original.tml-social-tumblr {
184 background: #35465c !important;
185 border-color: #35465c !important;
186 color: #fff !important;
187 }
188 .tml-social-original.tml-social-yahoo {
189 background: #410093 !important;
190 border-color: #410093 !important;
191 color: #fff !important;
192 }
193 .tml-social-original.tml-social-stumbleupon {
194 background: #eb4924 !important;
195 border-color: #eb4924 !important;
196 color: #fff !important;
197 }
198 .tml-social-original.tml-social-reddit {
199 background: #ff4500 !important;
200 border-color: #ff4500 !important;
201 color: #fff !important;
202 }
203 .tml-social-original.tml-social-quora {
204 background: #a82400 !important;
205 border-color: #a82400 !important;
206 color: #fff !important;
207 }
208 .tml-social-original.tml-social-yelp {
209 background: #af0606 !important;
210 border-color: #af0606 !important;
211 color: #fff !important;
212 }
213 .tml-social-original.tml-social-weibo {
214 background: #e6162d !important;
215 border-color: #e6162d !important;
216 color: #fff !important;
217 }
218 .tml-social-original.tml-social-product-hunt {
219 background: #da552f !important;
220 border-color: #da552f !important;
221 color: #fff !important;
222 }
223 .tml-social-original.tml-social-hacker-news {
224 background: #ff6600 !important;
225 border-color: #ff6600 !important;
226 color: #fff !important;
227 }
228 .tml-social-original.tml-social-soundcloud {
229 background: #ff3300 !important;
230 border-color: #ff3300 !important;
231 color: #fff !important;
232 }
233 .tml-social-original.tml-social-medium {
234 background: #000 !important;
235 border-color: #000 !important;
236 color: #fff !important;
237 }
238 .tml-social-original.tml-social-vine {
239 background: #00b488 !important;
240 border-color: #00b488 !important;
241 color: #fff !important;
242 }
243 .tml-social-original.tml-social-flickr {
244 background: #ff0084 !important;
245 border-color: #ff0084 !important;
246 color: #fff !important;
247 }
248 .tml-social-original.tml-social-foursquare {
249 background: #f94877 !important;
250 border-color: #f94877 !important;
251 color: #fff !important;
252 }
253 .tml-social-original.tml-social-behance {
254 background: #1769ff !important;
255 border-color: #1769ff !important;
256 color: #fff !important;
257 }
258 .tml-social-original.tml-social-bitbucket {
259 background: #0052cc !important;
260 border-color: #0052cc !important;
261 color: #fff !important;
262 }
263 .tml-social-original.tml-social-stack-overflow {
264 background: #f48024 !important;
265 border-color: #f48024 !important;
266 color: #fff !important;
267 }
268 .tml-social-original.tml-social-codepen {
269 background: #000 !important;
270 border-color: #000 !important;
271 color: #fff !important;
272 }
273 .tml-social-profiles .tml-social-icon:hover {
274 background: ' . esc_attr($tml_social_icon_hover_bg) . ' !important;
275 color: ' . esc_attr($tml_social_icon_hover_color) . ' !important;
276 border-color: ' . esc_attr($tml_social_border_hover_color) . ' !important;
277 transform: translateY(-3px);
278 }
279 </style>';
280 $brand_colors_printed = true;
281 }
282
283 $output = $styles . '<div class="tml-social-profiles" style="margin: ' . esc_attr($tml_social_margin_top) . 'px ' . esc_attr($tml_social_margin_right) . 'px ' . esc_attr($tml_social_margin_bottom) . 'px ' . esc_attr($tml_social_margin_left) . 'px; display:flex; gap:10px; justify-content:center; flex-wrap:wrap;">';
284
285 foreach ($social_profiles as $profile) {
286 if (!empty($profile['url'])) {
287 $network = $profile['network'];
288 if (!in_array($network, array('facebook', 'twitter'))) {
289 continue;
290 }
291 $icon = "fa-" . $network;
292 if ($network == 'facebook')
293 $icon = 'fa-facebook-f';
294 if ($network == 'linkedin')
295 $icon = 'fa-linkedin-in';
296 if ($network == 'twitter')
297 $icon = 'fa-twitter';
298
299 $social_class = "tml-social-icon tml-social-{$network}";
300 $social_style = "width:32px; height:32px; display:flex; align-items:center; justify-content:center; border-radius:50%; text-decoration:none; transition:all 0.3s ease; border:{$tml_social_border_width}px {$tml_social_border_style} {$tml_social_border_color}; margin: 0 5px;";
301
302 if ($tml_social_icon_type == 'custom') {
303 $social_style .= "background:{$tml_social_icon_bg}; color:{$tml_social_icon_color};";
304 } else {
305 $social_class .= " tml-social-original";
306 }
307
308 $output .= '<a href="' . esc_url($profile['url']) . '" target="_blank" class="' . esc_attr($social_class) . '" style="' . esc_attr($social_style) . '">';
309 $output .= '<i class="fa ' . esc_attr($icon) . '"></i>';
310 $output .= '</a>';
311 }
312 }
313
314 $output .= '</div>';
315 return $output;
316 }
317 }
318
319 add_shortcode('TML', 'tml_testimonial_shortcode');
320 function tml_testimonial_shortcode($post_id)
321 {
322 if ( ! is_array( $post_id ) ) {
323 $post_id = array();
324 }
325
326 $is_global_shortcode = false;
327 if ( ! isset( $post_id['id'] ) || empty( $post_id['id'] ) || $post_id['id'] === 'global' ) {
328 $default_id = get_option('tml_default_shortcode_id');
329 if ( ! empty( $default_id ) && get_post( $default_id ) ) {
330 $post_id['id'] = $default_id;
331 $is_global_shortcode = true;
332 } else {
333 $post_id['id'] = 'global';
334 $is_global_shortcode = true;
335 }
336 }
337
338 ob_start();
339 $tml_unified_js = '';
340
341 include(plugin_dir_path(__FILE__) . 'include/shortcode-options.php');
342
343 if ($is_global_shortcode) {
344 $tml_limit = -1;
345 if ( ! isset( $post_id['category'] ) ) {
346 $category_ids = '';
347 }
348 $tml_filter_category_id = '';
349 }
350 $template_number = 0;
351 $owl_template_map = array(
352 1 => 'owl-t1.php',
353 2 => 'owl-t2.php',
354 3 => 'owl-t3.php',
355 );
356 $owl_css_file = '';
357 if (isset($owl_template_map[$testimonial_carousel_design])) {
358 $template_number = $testimonial_carousel_design;
359 $owl_css_file = plugin_dir_path(__FILE__) . 'assets/css/owl-carousal/' . $owl_template_map[$testimonial_carousel_design];
360 }
361 if ($owl_css_file) {
362 include($owl_css_file);
363 } ?>
364 <?php include(plugin_dir_path(__FILE__) . 'assets/css/shortcode-styles.php'); ?>
365 <?php
366 // Set the orderby parameter based on the selected order option
367 $orderby = (isset($tml_order_by) && $tml_order_by != '') ? $tml_order_by : 'date';
368 $order = (isset($tml_post_order) && $tml_post_order != '') ? $tml_post_order : 'DESC';
369
370 // Define the query arguments
371 $paged = isset($post_id['paged']) ? intval($post_id['paged']) : 1;
372 $all_testimonial = array(
373 'post_type' => 'testimonial-maker',
374 'post_status' => 'publish',
375 'orderby' => $orderby,
376 'order' => $order,
377 'posts_per_page' => $tml_limit,
378 'paged' => $paged
379 );
380
381 // Filter by category (from admin settings)
382 if (!empty($tml_filter_category_id)) {
383 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
384 $all_testimonial['tax_query'] = array(
385 array(
386 'taxonomy' => 'testimonial-categories',
387 'field' => 'term_id',
388 'terms' => $tml_filter_category_id,
389 ),
390 );
391 }
392
393 // filter by category
394 if ($category_ids) {
395 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
396 $all_testimonial['tax_query'] = array(
397 array(
398 'taxonomy' => 'testimonial-categories',
399 'field' => 'term_id',
400 'terms' => $category_ids,
401 ),
402 );
403 }
404
405
406 $testimonial_loop = new WP_Query($all_testimonial);
407
408 $total_rating = 0;
409 $rating_count = 0;
410 $schema_reviews = array();
411
412 if ($testimonial_loop->have_posts()) {
413
414 // Pre-calculate ratings for Schema and Summary
415 foreach ($testimonial_loop->posts as $p) {
416 $meta = get_post_meta($p->ID, 'awl_testimonial' . $p->ID, true);
417 $r = isset($meta['star_rating']) ? intval($meta['star_rating']) : 5;
418 $total_rating += $r;
419 $rating_count++;
420
421 $schema_reviews[] = array(
422 '@type' => 'Review',
423 'author' => array(
424 '@type' => 'Person',
425 'name' => get_the_title($p->ID)
426 ),
427 'reviewRating' => array(
428 '@type' => 'Rating',
429 'ratingValue' => $r,
430 'bestRating' => '5'
431 ),
432 'reviewBody' => wp_strip_all_tags($p->post_content)
433 );
434 }
435
436 $average_rating = $rating_count > 0 ? round($total_rating / $rating_count, 1) : 5;
437
438 if ($is_ajax_request == 'true' && $tml_pagi_type != 'number') {
439 ob_clean(); // Clear everything before for AJAX request
440 } else {
441 // Output Schema
442 if (isset($post_id['schema']) && $post_id['schema'] == 'true' && $is_ajax_request != 'true') {
443 $schema_data = array(
444 '@context' => 'https://schema.org',
445 '@type' => 'Product',
446 'name' => get_bloginfo('name') . ' Services',
447 'aggregateRating' => array(
448 '@type' => 'AggregateRating',
449 'ratingValue' => $average_rating,
450 'reviewCount' => $rating_count
451 ),
452 'review' => $schema_reviews
453 );
454 echo '<script type="application/ld+json">' . wp_json_encode($schema_data) . '</script>';
455 }
456
457 // Output Summary
458 if (isset($post_id['show_summary']) && $post_id['show_summary'] == 'true' && $is_ajax_request != 'true') {
459 echo '<div class="tml-average-rating-summary" style="text-align:center; padding:20px; background:#f9f9f9; border-radius:8px; margin-bottom:20px;">';
460 /* translators: %s: average rating value */
461 echo '<h2 style="margin:0 0 10px 0;">' . esc_html(sprintf(__('Overall Rating: %s / 5', 'testimonial-maker'), $average_rating)) . '</h2>';
462 echo '<div class="testimonial-rating">';
463 for ($i = 1; $i <= 5; $i++) {
464 if ($i <= round($average_rating)) {
465 echo '<i class="fa fa-star" style="color:' . esc_attr($tml_star_rating_color) . ';"></i>';
466 } else {
467 echo '<i class="fa fa-star-o" style="color:#ddd;"></i>';
468 }
469 }
470 echo '</div>';
471 /* translators: %d: total reviews count */
472 echo '<p style="margin:10px 0 0 0;">' . esc_html(sprintf(__('Based on %d reviews', 'testimonial-maker'), $rating_count)) . '</p>';
473 echo '</div>';
474 }
475
476 ?>
477 <div class="tml-main-wrapper" id="tml-main-wrapper-<?php echo esc_attr($post_id['id']); ?>"
478 data-social-share="<?php echo esc_attr($tml_social_share); ?>"
479 data-field-order="<?php echo esc_attr($tml_ds_field_order); ?>">
480
481 <?php if ($tml_ds_preloader == 'enabled') { ?>
482 <div class="tml-preloader">
483 <div class="tml-premium-spinner"></div>
484 </div>
485 <?php } ?>
486
487 <?php if ($tml_ds_section_title == 'show' && $is_ajax_request != 'true') { ?>
488 <h2 class="tml-section-title" style="text-align: center; margin-bottom: 30px;">
489 <?php echo esc_html($tml_ds_section_title_text); ?>
490 </h2>
491 <?php } ?>
492
493 <?php if ($tml_ds_avg_rating == 'show' && $is_ajax_request != 'true') {
494 $avg = ($rating_count > 0) ? round($total_rating / $rating_count, 1) : 0;
495 ?>
496 <div class="tml-avg-rating-summary"
497 style="text-align: center; margin-bottom: 30px; padding: 20px; background: #f9f9f9; border-radius: 10px; color: #333;">
498 <h4 style="margin: 0 0 10px 0; color: #333;"><?php esc_html_e('Average Customer Rating', 'testimonial-maker'); ?></h4>
499 <div class="testimonial-rating" style="font-size: 24px; color: #333;">
500 <?php
501 for ($i = 1; $i <= 5; $i++) {
502 if ($i <= round($avg))
503 echo '<i class="fa fa-star"></i>';
504 else
505 echo '<i class="fa fa-star-o"></i>';
506 }
507 ?>
508 <span style="font-size: 18px; margin-left: 10px; color: #333;">(<?php echo esc_html($avg); ?> / 5)</span>
509 </div>
510 <p style="margin: 10px 0 0 0; color: #666;">
511 <?php
512 /* translators: %d: number of reviews */
513 printf(esc_html__('Based on %d reviews', 'testimonial-maker'), intval($rating_count));
514 ?>
515 </p>
516 </div>
517 <?php } ?>
518
519 <?php if ($tml_ds_ajax_search == 'enabled' && $is_ajax_request != 'true') {
520 $search_max_width = ($tml_ds_ajax_search_width == 'full') ? '100%' : '600px';
521 $search_margin = ($tml_ds_ajax_search_width == 'full') ? '0 0 30px 0' : '0 auto 30px auto';
522 $search_radius = ($tml_ds_ajax_search_shape == 'square') ? '4px' : '25px';
523 ?>
524 <style>
525 #tml-search-input-<?php echo esc_attr($post_id['id']); ?>:focus {
526 border-color: #6200ee !important;
527 box-shadow: 0 0 0 4px color-mix(in srgb, #6200ee 12%, transparent) !important;
528 }
529 </style>
530 <div class="tml-ajax-search"
531 style="max-width: <?php echo esc_attr($search_max_width); ?>; margin: <?php echo esc_attr($search_margin); ?>;">
532 <input type="text" id="tml-search-input-<?php echo esc_attr($post_id['id']); ?>"
533 class="form-control tml-search-input"
534 data-container="#tml-main-wrapper-<?php echo esc_attr($post_id['id']); ?>"
535 placeholder="<?php esc_attr_e('Search testimonials...', 'testimonial-maker'); ?>"
536 style="width: 100%; padding: 12px 18px; border-radius: <?php echo esc_attr($search_radius); ?>; border: 1px solid #ddd; outline: none; box-sizing: border-box; transition: all 0.3s ease;">
537 <div class="tml-search-results-grid"
538 style="display:none; margin-top:20px; display:flex; flex-wrap:wrap; justify-content:center; gap:20px;">
539 </div>
540 <div class="tml-search-no-results" style="display:none; text-align:center; padding:40px; color:#999;">
541 <i class="fa fa-search" style="font-size:40px; margin-bottom:15px; opacity:0.3;"></i>
542 <p><?php esc_html_e('Testimonials not found', 'testimonial-maker'); ?></p>
543 </div>
544 </div>
545 <?php } ?>
546 <?php if ($tml_layout == 'grid') { ?>
547 <div class="tml-testimonial-grid" id="tml-grid-container-<?php echo esc_attr($post_id['id']); ?>">
548 <?php } else { ?>
549 <?php if ($tml_progress_bar == 'true') { ?>
550 <div class="tml-slider-progress-wrap"
551 style="width: 100%; height: 4px; background: #eee; margin-bottom: 15px; border-radius: 2px; overflow: hidden;">
552 <div class="tml-slider-progress-bar"
553 style="height: 100%; width: 0%; background: <?php echo esc_attr($tml_star_rating_color); ?>;">
554 </div>
555 </div>
556 <?php } ?>
557 <div id="tml-carousel-<?php echo esc_attr($post_id['id']); ?>"
558 class="owl-carousel owl-theme">
559 <?php } ?>
560 <?php
561 $all_avatar_urls = array();
562 if ($testimonial_loop->have_posts()) {
563 foreach ($testimonial_loop->posts as $p) {
564 $thumb_id = get_post_thumbnail_id($p->ID);
565 $thumb_url = "";
566 if ($thumb_id) {
567 $src = wp_get_attachment_image_src($thumb_id, $tml_ds_image_dim, true);
568 if ($src)
569 $thumb_url = $src[0];
570 }
571 if (empty($thumb_url)) {
572 $thumb_url = TML_PLUGIN_URL . 'assets/img/person.png';
573 }
574 $all_avatar_urls[] = $thumb_url;
575 }
576 }
577 $tml_t13_index = 0;
578 $tml_t13_total = count($all_avatar_urls);
579
580 while ($testimonial_loop->have_posts()):
581 $testimonial_loop->the_post();
582 $testimonial_post_id = get_the_ID();
583 $testimonial_post_settings = get_post_meta(get_the_ID(), 'awl_testimonial' . get_the_ID(), true);
584 $website_link = $testimonial_post_settings['website_link'];
585 $designation = $testimonial_post_settings['designation'];
586 $video_url = '';
587 $star_rating = isset($testimonial_post_settings['star_rating']) ? $testimonial_post_settings['star_rating'] : '5';
588
589 // get testimonial data
590 $t_text = get_the_content();
591 $t_website_url = ($tml_ds_website_show === 'show') ? $website_link : '';
592 $t_designation = $designation;
593 $t_video_url = ($tml_ds_video_icon === 'show') ? $video_url : '';
594 $t_star_rating = intval($star_rating);
595 $t_fe_image_id = get_post_thumbnail_id($testimonial_post_id); // featured image id
596 $tml_fe_image_thumb = array("", 0, 0);
597 $tml_default_img = TML_PLUGIN_URL . 'assets/img/person.png';
598
599 // Integrate legacy text_limit shortcode option with dashboard settings
600 $effective_limit = $tml_ds_content_length;
601 $effective_type = $tml_ds_content_length_type;
602 $is_limit_active = ($tml_ds_content_type == 'limit');
603 $show_read_more = ($tml_ds_read_more == 'show');
604
605 if ($tml_text_limit > 0) {
606 $effective_limit = $tml_text_limit;
607 $effective_type = 'chars';
608 $is_limit_active = true;
609 $show_read_more = true;
610 }
611
612 // Initialize display text and apply character/word limit if applicable
613 $display_text = $t_text;
614 if ($is_limit_active) {
615 $limit = intval($effective_limit);
616 if ($effective_type == 'words') {
617 $display_text = wp_trim_words($t_text, $limit);
618 } else {
619 // Strip HTML tags first to prevent cutting in the middle of a tag (e.g. '<p>' or '<a>')
620 $plain_text = wp_strip_all_tags($t_text);
621 $display_text = (mb_strlen($plain_text) > $limit) ? mb_substr($plain_text, 0, $limit) . '...' : $plain_text;
622 }
623 }
624
625 // Construct the final HTML block for the testimonial content
626 $display_content = wp_kses_post($display_text);
627 if ($is_limit_active && $show_read_more && trim($t_text) !== trim($display_text)) {
628 $read_more_text = __("Read More", 'testimonial-maker');
629 $read_less_text = __("Read Less", 'testimonial-maker');
630
631 if ($tml_ds_rm_action == 'popup') {
632 // Resolve image URL for popup
633 $popup_image_url = $tml_default_img;
634 if ($t_fe_image_id) {
635 $image_size = ($testimonial_carousel_design == 10) ? 'full' : $tml_ds_image_dim;
636 $src = wp_get_attachment_image_src($t_fe_image_id, $image_size, true);
637 if ($src) {
638 $popup_image_url = $src[0];
639 }
640 } else {
641 if ($tml_ds_image_fallback == 'none') {
642 $popup_image_url = 'none';
643 } elseif ($tml_ds_image_fallback == 'avatar') {
644 // Generate SVG initials avatar for popup
645 $author_name = get_the_title($testimonial_post_id);
646 $initials = '';
647 if (!empty($author_name)) {
648 $author_name = wp_strip_all_tags($author_name);
649 $words = preg_split('/\s+/', trim($author_name));
650 if (function_exists('mb_substr')) {
651 if (count($words) >= 2) {
652 $first_char = mb_substr($words[0], 0, 1, 'UTF-8');
653 $second_char = mb_substr($words[1], 0, 1, 'UTF-8');
654 $initials = mb_strtoupper($first_char . $second_char, 'UTF-8');
655 } else {
656 $initials = mb_strtoupper(mb_substr($author_name, 0, 2, 'UTF-8'), 'UTF-8');
657 }
658 } else {
659 if (count($words) >= 2) {
660 $first_char = substr($words[0], 0, 1);
661 $second_char = substr($words[1], 0, 1);
662 $initials = strtoupper($first_char . $second_char);
663 } else {
664 $initials = strtoupper(substr($author_name, 0, 2));
665 }
666 }
667 }
668 if (empty($initials)) {
669 $initials = 'T';
670 }
671
672 $bg_colors = array(
673 '#1abc9c',
674 '#2ecc71',
675 '#3498db',
676 '#9b59b6',
677 '#34495e',
678 '#16a085',
679 '#27ae60',
680 '#2980b9',
681 '#8e44ad',
682 '#2c3e50',
683 '#f1c40f',
684 '#e67e22',
685 '#e74c3c',
686 '#95a5a6',
687 '#f39c12',
688 '#d35400',
689 '#c0392b',
690 '#7f8c8d',
691 '#ff5252',
692 '#7c4dff'
693 );
694 $color_index = abs(crc32($author_name)) % count($bg_colors);
695 $bg_color = $bg_colors[$color_index];
696
697 $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">' .
698 '<rect width="100%" height="100%" fill="' . $bg_color . '"/>' .
699 '<text x="50%" y="54%" font-size="38" font-family="\'Outfit\', \'Inter\', \'Helvetica Neue\', sans-serif" font-weight="700" fill="#ffffff" dominant-baseline="middle" text-anchor="middle">' . $initials . '</text>' .
700 '</svg>';
701
702 $popup_image_url = 'data:image/svg+xml;utf8,' . rawurlencode($svg);
703 }
704 }
705 $rating_val = ($tml_show_star_rating == 'true') ? $t_star_rating : 0;
706
707 $display_content = '<span>' . wp_kses_post($display_text) .
708 '<a href="#" class="popup-btn tml-read-more-link" ' .
709 'data-full-text="' . esc_attr(wp_kses_post($t_text)) . '" ' .
710 'data-title="' . esc_attr(get_the_title()) . '" ' .
711 'data-image="' . esc_url($popup_image_url) . '" ' .
712 'data-designation="' . esc_attr($t_designation) . '" ' .
713 'data-website="' . esc_url($t_website_url) . '" ' .
714 'data-rating="' . esc_attr($rating_val) . '" ' .
715 'style="color:#0073aa; font-weight:bold; text-decoration:none; margin-left:5px;">' . esc_html($read_more_text) . '</a>' .
716 '</span>';
717 } else {
718 // Default to 'expand'
719 $display_content = '<span id="tml-short-' . $testimonial_post_id . '" class="tml-short-text">' . wp_kses_post($display_text) .
720 '<a href="#" class="expand-btn tml-read-more-link" data-id="' . $testimonial_post_id . '" style="color:#0073aa; font-weight:bold; text-decoration:none; margin-left:5px;">' . esc_html($read_more_text) . '</a>' .
721 '</span>' .
722 '<span id="tml-full-' . $testimonial_post_id . '" class="tml-full-text" style="display:none;">' . wp_kses_post($t_text) .
723 '<a href="#" class="expand-btn tml-read-more-link" data-id="' . $testimonial_post_id . '" style="color:#0073aa; font-weight:bold; text-decoration:none; margin-left:5px;">' . esc_html($read_less_text) . '</a>' .
724 '</span>';
725 }
726 }
727
728 if ((function_exists('has_post_thumbnail')) && (has_post_thumbnail())) {
729 $image_size = ($testimonial_carousel_design == 10) ? 'full' : $tml_ds_image_dim;
730 $tml_fe_image_thumb = wp_get_attachment_image_src($t_fe_image_id, $image_size, true);
731 $is_fallback = false;
732 } else {
733 if ($tml_ds_image_fallback == 'none') {
734 $tml_fe_image_thumb[0] = "";
735 } elseif ($tml_ds_image_fallback == 'avatar') {
736 // Generate a gorgeous SVG initials avatar
737 $author_name = get_the_title($testimonial_post_id);
738 $initials = '';
739 if (!empty($author_name)) {
740 $author_name = wp_strip_all_tags($author_name);
741 $words = preg_split('/\s+/', trim($author_name));
742 if (function_exists('mb_substr')) {
743 if (count($words) >= 2) {
744 $first_char = mb_substr($words[0], 0, 1, 'UTF-8');
745 $second_char = mb_substr($words[1], 0, 1, 'UTF-8');
746 $initials = mb_strtoupper($first_char . $second_char, 'UTF-8');
747 } else {
748 $initials = mb_strtoupper(mb_substr($author_name, 0, 2, 'UTF-8'), 'UTF-8');
749 }
750 } else {
751 if (count($words) >= 2) {
752 $first_char = substr($words[0], 0, 1);
753 $second_char = substr($words[1], 0, 1);
754 $initials = strtoupper($first_char . $second_char);
755 } else {
756 $initials = strtoupper(substr($author_name, 0, 2));
757 }
758 }
759 }
760 if (empty($initials)) {
761 $initials = 'T';
762 }
763
764 // Premium, gorgeous colors for backgrounds
765 $bg_colors = array(
766 '#1abc9c',
767 '#2ecc71',
768 '#3498db',
769 '#9b59b6',
770 '#34495e',
771 '#16a085',
772 '#27ae60',
773 '#2980b9',
774 '#8e44ad',
775 '#2c3e50',
776 '#f1c40f',
777 '#e67e22',
778 '#e74c3c',
779 '#95a5a6',
780 '#f39c12',
781 '#d35400',
782 '#c0392b',
783 '#7f8c8d',
784 '#ff5252',
785 '#7c4dff'
786 );
787 $color_index = abs(crc32($author_name)) % count($bg_colors);
788 $bg_color = $bg_colors[$color_index];
789
790 // Generate Inline SVG data URL
791 $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">' .
792 '<rect width="100%" height="100%" fill="' . $bg_color . '"/>' .
793 '<text x="50%" y="54%" font-size="38" font-family="\'Outfit\', \'Inter\', \'Helvetica Neue\', sans-serif" font-weight="700" fill="#ffffff" dominant-baseline="middle" text-anchor="middle">' . $initials . '</text>' .
794 '</svg>';
795
796 $tml_fe_image_thumb[0] = 'data:image/svg+xml;utf8,' . rawurlencode($svg);
797 } else {
798 $tml_fe_image_thumb[0] = "$tml_default_img";
799 }
800 $is_fallback = true;
801 }
802
803 $pic_width = (isset($tml_fe_image_thumb[1]) && $tml_fe_image_thumb[1] > 0) ? $tml_fe_image_thumb[1] : 100;
804 $pic_height = (isset($tml_fe_image_thumb[2]) && $tml_fe_image_thumb[2] > 0) ? $tml_fe_image_thumb[2] : 100;
805 $pic_style = "";
806
807 // Category classes for filtering
808 $terms = get_the_terms($testimonial_post_id, 'testimonial-categories');
809 $term_classes = 'tml-item-wrapper tml-rating-' . $t_star_rating;
810 if ($terms && !is_wp_error($terms)) {
811 foreach ($terms as $term) {
812 $term_classes .= ' tml-cat-' . $term->term_id;
813 }
814 }
815
816 // template settings
817 wp_enqueue_script('tml-owl-js', TML_PLUGIN_URL . 'assets/js/owl.carousel.js', array('jquery'), TML_PLUGIN_VER, false);
818 $image_shape_class = 'tml-image-' . $tml_ds_image_shape;
819 ?>
820
821 <div class="<?php echo esc_attr($term_classes); ?>"
822 data-url="<?php echo urlencode(get_permalink()); ?>"
823 data-text="<?php echo urlencode(wp_trim_words($t_text, 10)); ?>"
824 data-video-url="<?php echo esc_url($t_video_url); ?>">
825 <?php
826 $design_file = TML_PLUGIN_DIR . 'templates/design-' . intval($testimonial_carousel_design) . '.php';
827 if (file_exists($design_file)) {
828 include($design_file);
829 }
830 ?>
831 </div>
832 <?php
833 endwhile;
834 if ($is_ajax_request == 'true' && $tml_pagi_type != 'number') {
835 wp_die(); // end execution for AJAX
836 }
837 ?>
838 </div>
839
840
841
842
843 </div>
844 <?php
845 ob_start();
846 include(plugin_dir_path(__FILE__) . 'assets/js/shortcode-scripts-carousel.php');
847 $tml_unified_js .= ob_get_clean();
848 ?>
849 <?php
850 }
851
852
853 if ($tml_layout != 'grid' && $tml_layout != 'masonry' && $tml_layout != 'list' && $tml_layout != 'isotope') {
854 ?>
855 <?php
856 // Dynamic Navigation Styling
857 $nav_css = '';
858 $nav_id = '#tml-carousel-' . $post_id['id'];
859
860 if ($tml_nav == 'true') {
861 $size_val = !empty($tml_nav_size) ? $tml_nav_size : '35';
862 if (is_numeric($size_val)) {
863 $size_val .= 'px';
864 }
865 // Colors, Borders, and Theme Conflict Fixes
866 $nav_css .= "body $nav_id.owl-carousel { overflow: visible !important; }";
867 $nav_css .= "body $nav_id.owl-carousel .owl-stage-outer { overflow: hidden !important; }";
868 $nav_css .= "body $nav_id.owl-carousel .owl-nav { opacity: 1 !important; visibility: visible !important; pointer-events: none !important; z-index: 99999 !important; display: block !important; }";
869
870 $nav_css .= "body $nav_id.owl-carousel .owl-nav button.owl-prev, body $nav_id.owl-carousel .owl-nav button.owl-next {";
871 $nav_css .= "color: $tml_nav_color !important;";
872 $nav_css .= "background: $tml_nav_bg_color !important;";
873 $nav_css .= "width: $size_val !important; height: $size_val !important; font-size: 18px !important; padding: 0 !important; border-radius: 8px !important; transition: all 0.3s; display: inline-flex !important; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(0,0,0,0.08) !important; opacity: 1 !important; visibility: visible !important; z-index: 999999 !important; pointer-events: auto !important; cursor: pointer !important;";
874 $nav_css .= "}";
875
876 // Hover opacity effect
877 $nav_css .= "body $nav_id.owl-carousel .owl-nav button.owl-prev:hover, body $nav_id.owl-carousel .owl-nav button.owl-next:hover {";
878 $nav_css .= "opacity: 0.9 !important; transform: scale(1.05) !important; color: $tml_nav_color !important; background: $tml_nav_bg_color !important; cursor: pointer !important;";
879 $nav_css .= "}";
880
881 if ($tml_nav_mobile_hide == 'true') {
882 $nav_css .= "@media (max-width: 767px) {";
883 $nav_css .= "body $nav_id.owl-carousel .owl-nav, body $nav_id .owl-nav { display: none !important; }";
884 $nav_css .= "}";
885 }
886
887
888 // Positions
889 switch ($tml_nav_position) {
890 case 'vertical_outer':
891 // Pad the slider wrapper so the "outer" arrows fit inside the element bounds and never collide with sidebars
892 $nav_css .= "body $nav_id.owl-carousel { padding-left: 50px !important; padding-right: 50px !important; box-sizing: border-box !important; }";
893 $nav_css .= "$nav_id { position: relative; }";
894 $nav_css .= "$nav_id .owl-nav { display: contents !important; }";
895 $nav_css .= "body $nav_id .owl-prev, body $nav_id .owl-next { position: absolute !important; top: 50% !important; transform: translateY(-50%) !important; }";
896 $nav_css .= "body $nav_id .owl-prev { left: 5px !important; }";
897 $nav_css .= "body $nav_id .owl-next { right: 5px !important; }";
898 break;
899 case 'vertical_inner':
900 $nav_css .= "$nav_id { position: relative; }";
901 $nav_css .= "$nav_id .owl-nav { display: contents !important; }";
902 $nav_css .= "body $nav_id .owl-prev, body $nav_id .owl-next { position: absolute !important; top: 50% !important; transform: translateY(-50%) !important; }";
903 $nav_css .= "body $nav_id .owl-prev { left: 10px !important; }";
904 $nav_css .= "body $nav_id .owl-next { right: 10px !important; }";
905 break;
906 case 'top_right':
907 $nav_css .= "$nav_id { padding-top: 40px; }";
908 $nav_css .= "$nav_id .owl-nav { position: absolute; top: 0; right: 0; display: flex; gap: 5px; }";
909 break;
910 case 'top_left':
911 $nav_css .= "$nav_id { padding-top: 40px; }";
912 $nav_css .= "$nav_id .owl-nav { position: absolute; top: 0; left: 0; display: flex; gap: 5px; }";
913 break;
914 case 'top_center':
915 $nav_css .= "$nav_id { padding-top: 40px; }";
916 $nav_css .= "$nav_id .owl-nav { position: absolute; top: 0; left: 50%; transform: translateX(-50%); display: flex; gap: 5px; }";
917 break;
918 case 'bottom_center':
919 $nav_css .= "$nav_id { padding-bottom: 40px; }";
920 $nav_css .= "$nav_id .owl-nav { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); display: flex; gap: 5px; }";
921 break;
922 case 'bottom_right':
923 $nav_css .= "$nav_id { padding-bottom: 40px; }";
924 $nav_css .= "$nav_id .owl-nav { position: absolute; bottom: 0; right: 0; display: flex; gap: 5px; }";
925 break;
926 case 'bottom_left':
927 $nav_css .= "$nav_id { padding-bottom: 40px; }";
928 $nav_css .= "$nav_id .owl-nav { position: absolute; bottom: 0; left: 0; display: flex; gap: 5px; }";
929 break;
930 case 'vertical_center':
931 // Pad the slider wrapper slightly to fit "center" outer arrows
932 $nav_css .= "body $nav_id.owl-carousel { padding-left: 35px !important; padding-right: 35px !important; box-sizing: border-box !important; }";
933 $nav_css .= "$nav_id { position: relative; }";
934 $nav_css .= "$nav_id .owl-nav { display: contents !important; }";
935 $nav_css .= "body $nav_id .owl-prev, body $nav_id .owl-next { position: absolute !important; top: 50% !important; transform: translateY(-50%) !important; }";
936 $nav_css .= "body $nav_id .owl-prev { left: 5px !important; }";
937 $nav_css .= "body $nav_id .owl-next { right: 5px !important; }";
938 break;
939 }
940 } else {
941 // Force hide the entire navigation container when disabled to prevent CSS specificity overrides
942 $nav_css .= "body $nav_id.owl-carousel .owl-nav { display: none !important; }";
943 }
944
945 // Pagination Styling
946 $nav_css .= "body $nav_id.owl-theme .owl-dots .owl-dot span { background: #cccccc !important; transition: all 0.3s; }";
947 $nav_css .= "body $nav_id.owl-theme .owl-dots .owl-dot.active span, body $nav_id.owl-theme .owl-dots .owl-dot:hover span { background: #2271b1 !important; }";
948 ?>
949 <style>
950 <?php echo esc_html($nav_css); ?>
951 </style>
952 <?php
953 ob_start();
954 include(plugin_dir_path(__FILE__) . 'assets/js/shortcode-scripts-owl.php');
955 $tml_unified_js .= ob_get_clean();
956 ?>
957 <?php
958 } // end if not grid or masonry
959
960
961
962
963
964
965 // Preloader dismissal & Schema Markup
966 ?>
967 <script>
968 <?php
969 // Strip any opening/closing script tags from unified scripts
970 $tml_clean_js = preg_replace('/<script[^>]*>/i', '', $tml_unified_js);
971 $tml_clean_js = preg_replace('/<\/script>/i', '', $tml_clean_js);
972 echo $tml_clean_js; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
973 ?>
974 jQuery(document).ready(function ($) {
975 // Preloader dismissal
976 <?php if ($tml_ds_preloader == 'enabled') { ?>
977 jQuery(window).on('load', function () {
978 $('.tml-preloader').fadeOut(500);
979 });
980 // Fallback in case load event already fired
981 setTimeout(function () {
982 $('.tml-preloader').fadeOut(500);
983 }, 3000);
984 <?php } ?>
985
986 // Search functionality
987 $('.tml-search-input').on('keyup', function () {
988 var containerId = $(this).data('container');
989 var $container = $(containerId);
990 if (typeof window.applyTmlFilters === 'function') {
991 window.applyTmlFilters($container);
992 } else {
993 // Standalone search logic when other filters are disabled
994 var value = $(this).val().toLowerCase().trim();
995 var $noResults = $(this).siblings('.tml-search-no-results');
996 var isSlider = $container.find('.owl-carousel').length > 0;
997 var matchCount = 0;
998
999 if (isSlider) {
1000 var $owl = $container.find('.owl-carousel');
1001 var originalSlides = $owl.data('original-slides');
1002 if (originalSlides && originalSlides.length) {
1003 $owl.trigger('destroy.owl.carousel');
1004 $owl.removeClass('owl-loaded owl-drag owl-refresh');
1005 $owl.empty();
1006
1007 $.each(originalSlides, function (index, $slide) {
1008 var isMatch = true;
1009 if (value !== '') {
1010 var slideText = $slide.text().toLowerCase();
1011 if (slideText.indexOf(value) === -1) {
1012 isMatch = false;
1013 }
1014 }
1015 if (isMatch) {
1016 $owl.append($slide.clone());
1017 matchCount++;
1018 }
1019 });
1020
1021 if (matchCount === 0) {
1022 $noResults.show();
1023 $owl.hide();
1024 } else {
1025 $noResults.hide();
1026 $owl.show();
1027 var initFn = $owl.data('owl-init-fn');
1028 if (typeof initFn === 'function') {
1029 initFn($owl);
1030 }
1031 }
1032 }
1033 } else {
1034 // Grid / Masonry / List standalone search filtering
1035 $container.find('.tml-item-wrapper').each(function () {
1036 var $item = $(this);
1037 var isMatch = true;
1038 if (value !== '') {
1039 var itemText = $item.text().toLowerCase();
1040 if (itemText.indexOf(value) === -1) {
1041 isMatch = false;
1042 }
1043 }
1044 if (isMatch) {
1045 $item.removeClass('tml-hide');
1046 this.style.removeProperty('display');
1047 matchCount++;
1048 } else {
1049 $item.addClass('tml-hide');
1050 this.style.setProperty('display', 'none', 'important');
1051 }
1052 });
1053
1054 if (matchCount === 0) {
1055 $noResults.show();
1056 } else {
1057 $noResults.hide();
1058 }
1059 }
1060 }
1061 });
1062
1063 // Read More Expand Action - Sibling-based toggle for maximum template compatibility
1064 $(document).off('click.tmlExpandBtn').on('click.tmlExpandBtn', '.expand-btn', function (e) {
1065 e.preventDefault();
1066 var $btn = $(this);
1067 var $currentSpan = $btn.closest('span');
1068 var $nextSpan = $currentSpan.next('span');
1069
1070 if ($nextSpan.length > 0) {
1071 // This is the short span (first span). Hide it and show the next sibling span (full span).
1072 $currentSpan.hide();
1073 $nextSpan.show();
1074 } else {
1075 // This is the full span (second span). Hide it and show the previous sibling span (short span).
1076 var $prevSpan = $currentSpan.prev('span');
1077 if ($prevSpan.length > 0) {
1078 $currentSpan.hide();
1079 $prevSpan.show();
1080 }
1081 }
1082
1083 // Force Owl Carousel recalculation if it exists
1084 var $owl = $btn.closest('.owl-carousel');
1085 if ($owl.length > 0) {
1086 $owl.trigger('refresh.owl.carousel');
1087 }
1088 });
1089
1090 // Read More Popup Action
1091 $(document).off('click.tmlPopupBtn').on('click.tmlPopupBtn', '.popup-btn', function (e) {
1092 e.preventDefault();
1093 var text = $(this).data('full-text');
1094 var title = $(this).data('title');
1095 var image = $(this).data('image');
1096 var designation = $(this).data('designation');
1097 var website = $(this).data('website');
1098 var rating = $(this).data('rating');
1099
1100 // Build HTML for avatar image dynamically respecting the image shape setting
1101 var imageHtml = '';
1102 if (image && image !== 'none') {
1103 var activeImageShape = <?php echo json_encode($tml_ds_image_shape); ?>;
1104 var popupBorderRadius = '50%';
1105 if (activeImageShape === 'rounded') {
1106 popupBorderRadius = '10px';
1107 } else if (activeImageShape === 'square') {
1108 popupBorderRadius = '0%';
1109 }
1110
1111 imageHtml = '<div style="margin-bottom: 18px; display: inline-block;">' +
1112 '<img src="' + image + '" style="width: 90px; height: 90px; border-radius: ' + popupBorderRadius + '; object-fit: cover; border: 3px solid #0073aa; box-shadow: 0 4px 12px rgba(0,0,0,0.15);" />' +
1113 '</div>';
1114 }
1115
1116 // Build HTML for rating stars dynamically matching active settings
1117 var ratingHtml = '';
1118 if (rating && rating > 0) {
1119 var activeRatingStyle = <?php echo json_encode($tml_rating_style); ?>;
1120 var activeRatingColor = <?php echo json_encode($tml_star_rating_color); ?>;
1121 var activeDefaultColor = <?php echo json_encode($tml_ds_rating_default_color); ?>;
1122 var activeGap = <?php echo json_encode($tml_ds_rating_gap); ?>;
1123
1124 var iconsList = {
1125 'star': { 'filled': 'fa-star', 'empty': 'fa-star-o' },
1126 'heart': { 'filled': 'fa-heart', 'empty': 'fa-heart' },
1127 'thumbs-up': { 'filled': 'fa-thumbs-up', 'empty': 'fa-thumbs-o-up' },
1128 'smile': { 'filled': 'fa-smile-o', 'empty': 'fa-smile-o' },
1129 'diamond': { 'filled': 'fa-diamond', 'empty': 'fa-diamond' },
1130 'badge': { 'filled': 'fa-certificate', 'empty': 'fa-certificate' },
1131 'trophy': { 'filled': 'fa-trophy', 'empty': 'fa-trophy' },
1132 'bell': { 'filled': 'fa-bell', 'empty': 'fa-bell-o' }
1133 };
1134
1135 var filledIcon = (iconsList[activeRatingStyle] ? iconsList[activeRatingStyle]['filled'] : 'fa-star');
1136 var emptyIcon = (iconsList[activeRatingStyle] ? iconsList[activeRatingStyle]['empty'] : 'fa-star-o');
1137
1138 ratingHtml = '<div style="font-size: 18px; margin-bottom: 15px; display: flex; justify-content: center; gap: ' + activeGap + 'px;">';
1139 for (var i = 1; i <= 5; i++) {
1140 var iconClass = (i <= rating) ? filledIcon : emptyIcon;
1141 var iconColor = (i <= rating) ? activeRatingColor : activeDefaultColor;
1142 var opacity = (i > rating && activeRatingStyle === filledIcon) ? '0.3' : '1';
1143
1144 ratingHtml += '<i class="fa ' + iconClass + '" style="color:' + iconColor + '; opacity:' + opacity + ';"></i>';
1145 }
1146 ratingHtml += '</div>';
1147 }
1148
1149 // Build HTML for designation
1150 var designationHtml = '';
1151 if (designation) {
1152 designationHtml = '<h4 style="margin: 6px 0 12px 0; font-size: 14px; font-weight: 600; color: #0073aa; text-transform: uppercase; letter-spacing: 0.8px;">' + designation + '</h4>';
1153 }
1154
1155 // Build HTML for website link
1156 var websiteHtml = '';
1157 if (website) {
1158 websiteHtml = '<div style="margin-top: 18px; margin-bottom: 5px;">' +
1159 '<a href="' + website + '" target="_blank" style="color: #0073aa; text-decoration: none; font-weight: bold; font-size: 14px; display: inline-flex; align-items: center; gap: 6px; transition: opacity 0.2s;" onmouseover="this.style.opacity=\'0.8\'" onmouseout="this.style.opacity=\'1\'"><i class="fa fa-external-link"></i> Visit Website</a>' +
1160 '</div>';
1161 }
1162
1163 var modalHtml = '<div id="tml-modal" style="position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.85); z-index:999999; display:flex; align-items:center; justify-content:center; padding:20px; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif; box-sizing: border-box;">' +
1164 '<div style="background:#fff; width:100%; max-width:550px; padding:35px; border-radius:16px; position:relative; max-height:85vh; overflow-y:auto; text-align: center; box-shadow: 0 20px 40px rgba(0,0,0,0.3); box-sizing: border-box;">' +
1165 '<span class="tml-close-modal" style="position:absolute; top:12px; right:18px; font-size:32px; cursor:pointer; color:#bbb; transition: color 0.2s;" onmouseover="this.style.color=\'#666\'" onmouseout="this.style.color=\'#bbb\'">&times;</span>' +
1166 imageHtml +
1167 '<h3 style="margin: 0; font-size: 24px; font-weight: 700; color: #1e293b;">' + title + '</h3>' +
1168 designationHtml +
1169 ratingHtml +
1170 '<div style="line-height:1.7; color:#475569; font-size: 15px; background: #f8fafc; padding: 22px; border-radius: 10px; text-align: center; margin: 18px 0; font-style: italic;">' + text + '</div>' +
1171 websiteHtml +
1172 '</div></div>';
1173
1174 $('body').append(modalHtml);
1175 });
1176
1177 $(document).off('click.tmlCloseModal').on('click.tmlCloseModal', '.tml-close-modal, #tml-modal', function (e) {
1178 if (e.target !== this && !$(e.target).hasClass('tml-close-modal')) return;
1179 $('#tml-modal').remove();
1180 });
1181
1182 // Video logic excised for lightweight free-only version
1183 });
1184 </script>
1185 <?php
1186
1187 }
1188 wp_reset_postdata();
1189 $output = ob_get_contents();
1190 ob_end_clean();
1191 return $output;
1192 } ?>