PluginProbe
Themify Builder / trunk
Themify Builder vtrunk
7.8.1 7.8.0 7.7.9 7.7.8 7.7.7 7.7.6 7.7.5 7.7.4 7.7.3 7.7.2 trunk 7.6.0 7.6.1 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.8 7.6.9 7.7.0 7.7.1
themify-builder / themify / themify-utils.php

themify-utils.php in Themify Builder trunk, at themify/themify-utils.php

2,448 lines 90.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /* * *************************************************************************
4 *
5 * ----------------------------------------------------------------------
6 * DO NOT EDIT THIS FILE
7 * ----------------------------------------------------------------------
8 *
9 * Copyright (C) Themify
10 *
11 * ----------------------------------------------------------------------
12 *
13 * ************************************************************************* */
14
15 if (!defined('ABSPATH'))
16 exit; // Exit if accessed directly
17
18 /* Utilities
19 /************************************************************************** */
20
21 function themify_clear_menu_cache() {
22 global $wpdb;
23 $wpdb->query("DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient_tf_menu_%')");
24 return Themify_Storage::deleteByPrefix('tf_menu_');
25 }
26
27
28 /**
29 * Image Helper - Echoes themify_get_image
30 * @param string $args Format string.
31 */
32 function themify_image($args) {//deprecated use themify_get_image
33 echo themify_get_image($args);
34 }
35
36 function themify_find_nearest_image_size(?int $attachment_id,int $width,int $height ) : string {
37 $image_meta = wp_get_attachment_metadata( $attachment_id );
38 $size = '';
39 if ( ! empty( $image_meta['sizes'] ) ) {
40 $last_pixel_count = 0;
41 foreach ( $image_meta['sizes'] as $key => $value ) {
42 /* image size is smaller than what we need, skip */
43 if ( $width > $value['width'] || $height > $value['height'] ) {
44 continue;
45 }
46
47 /* find smallest size */
48 $pixels = $value['width'] * $value['height'];
49 if ( $last_pixel_count === 0 || $pixels < $last_pixel_count ) {
50 $last_pixel_count = $pixels;
51 $size = $key;
52 }
53 }
54 }
55
56 return $size;
57 }
58
59 /**
60 * Returns the post image, either from Themify Custom Panel fields or from WordPress Featured Image.
61 * @param string|array $args Format string.
62 * @return string String with <img> tag and optional content prepended and/or appended
63 */
64 function themify_get_image($args):string {
65 global $themify;
66 /**
67 * List of parameters
68 * @var array
69 */
70 $defaults = array(
71 'src' => '',
72 'class' => '',
73 'style' => '',
74 'preload' => false,
75 'prefetch' => false,
76 'lazy_load' => true,
77 'is_slider' => false,
78 'disable_responsive' => false,
79 'w' => '', // width
80 'h' => '', // height
81 'before' => '',
82 'after' => '',
83 'alt' => '',
84 'alt_fallback' => true,
85 'title' => '',
86 'crop' => true,
87 'field_name' => 'post_image,image,wp_thumb,feature_image',
88 'urlonly' => false,
89 'image_size' => '',
90 'image_meta' => false,
91 'f_image' => false,
92 'attr' => array(),
93 'fallback' => ''//when there is no image
94 );
95 if (is_string($args)) {
96 $arr = array();
97 parse_str($args, $arr);
98 $args = array();
99 foreach ($arr as $k => $v) {
100 if ($v === 'true') {
101 $args[$k] = true;
102 } elseif ($v === 'false') {
103 $args[$k] = false;
104 } else {
105 $args[$k] = $v;
106 }
107 }
108 }
109 $args+=$defaults;
110 unset($defaults);
111 /**
112 * Post ID for single, query or archive views.
113 * Page ID is stored separately in $themify->page_id.
114 * @var string
115 */
116 $post_id = get_the_ID();
117
118 /**
119 * URL of the image to use
120 * @var string
121 */
122 $img_url = '';
123
124 /**
125 * Image width
126 * @var string
127 */
128 $width = $args['w'];
129 if ($width !== '') {
130 $width = (int) $width;
131 }
132 /**
133 * Image height
134 * @var string
135 */
136 $height = $args['h'];
137 if ($height !== '') {
138 $height = (int) $height;
139 }
140
141 $attachment_id = 0;
142
143 $is_disabled = themify_is_image_script_disabled();
144 if (is_numeric($args['src'])) {
145 $attachment_id = $args['src'];
146 $img = wp_get_attachment_image_src($attachment_id, 'large');
147 $args['src'] = !empty($img[0]) ? $img[0] : '';
148 unset($img);
149 } elseif ($args['fallback'] !== '' && empty($args['src']) && !has_post_thumbnail()) {
150 $args['src'] = $args['fallback'];
151 }
152 if ($is_disabled === true) { // Use WP standard image sizes
153 if (!empty($args['image_size'])) { // If image_size parameter is set
154 $feature_size = $args['image_size'];
155 } elseif (!empty($themify->image_size)) { // or if Themify::image_size is set
156 $feature_size = $themify->image_size;
157 } elseif (empty($themify->is_shortcode) && in_the_loop()) { // Main query area
158 if (is_single()) {
159 $feature_size = get_post_meta($post_id, 'feature_size', true);
160 if (empty($feature_size) || 'blank' === $feature_size) {
161 $feature_size = themify_get('setting-image_post_single_feature_size', null, true);
162 }
163 } elseif (!empty($themify->page_id) && !empty($themify->query_post_type) && themify_is_query_page()) {
164 $feature_size = get_post_meta($themify->page_id, $themify->query_post_type . 'feature_size_page', true);
165 if (empty($feature_size)) {
166 $feature_size = null;
167 }
168 } elseif (is_archive() || is_tax() || is_search() || is_home()) {
169 $feature_size = themify_get('setting-image_post_feature_size', null, true);
170 }
171 }
172 if (!empty($args['src'])) {
173 $attachment_id = themify_get_attachment_id_from_url($args['src']);
174 }
175 if (!isset($feature_size) || 'blank' === $feature_size) {
176 $feature_size = $attachment_id && ! empty( $width ) && ! empty( $height ) ? themify_find_nearest_image_size( $attachment_id, $width, $height ) : '';
177 if ( $feature_size === '' ) {
178 $feature_size = apply_filters('themify_global_feature_size', themify_get('setting-global_feature_size', 'large', true));
179 }
180 }
181 if (!empty($attachment_id)) {
182 $tmp = wp_get_attachment_image_src($attachment_id, $feature_size);
183 if (!empty($tmp)) {
184 $img_url = $tmp[0];
185 }
186 unset($tmp);
187 }
188 if ($img_url === '') {
189 // Set URL to use for final output.
190 $img_url = empty($args['src']) ? themify_image_url(false, $feature_size) : $args['src'];
191 $img_url = trim($img_url);
192 // Fix for showing feature_image when Themify Image Script is disabled
193 if ('' === $img_url) {
194 foreach (explode(',', $args['field_name']) as $field) {
195 if ($img_url = get_post_meta($post_id, trim($field), true))
196 break;
197 }
198 }
199 }
200 } else { // Use Image Script
201 if (empty($args['src'])) {
202 if (has_post_thumbnail()) {
203 $img_url = $width !== '' && $height !== '' ? ((int) get_post_thumbnail_id()) : get_the_post_thumbnail_url(); /* Image script works with thumbnail IDs as well as URLs, use ID which is faster */
204 } elseif ('attachment' === get_post_type()) {
205 $img_url = wp_get_attachment_url($post_id);
206 } else {
207 foreach (explode(',', $args['field_name']) as $field) {
208 if ($img_url = get_post_meta($post_id, trim($field), true)) {
209 break;
210 }
211 }
212 }
213 } else {
214 $img_url = $args['src'];
215 }
216 if ($height !== '' && 0 === ((int) $height )) {
217 $height = 0;
218 $args['crop'] = false;
219 }
220 /** filter $img_url before it goes off to themify_do_img for processing * */
221 $img_url = apply_filters('themify_get_image_before_do_img', $img_url, $width, $height, $args);
222
223 // Set URL to use for final output.
224 $temp = themify_do_img((empty($attachment_id) ? $img_url : $attachment_id), $width, $height, (bool) $args['crop']);
225 $img_url = $temp['url'];
226 if ($temp['width'] !== '' && $temp['height'] !== '') {
227 $width = (int) $temp['width'];
228 $height = (int) $temp['height'];
229 }
230 // Get title/alt text by attachment id if it was returned.
231 if (!$attachment_id && isset($temp['attachment_id'])) {
232 $attachment_id = $temp['attachment_id'];
233 }
234 }
235
236 if ($attachment_id) {
237 $attachment_id = themify_maybe_translate_object_id($attachment_id);
238 }
239
240 // No image was defined, parse content to find the first image.
241 if (empty($img_url) && ($args['f_image'] || themify_check('setting-auto_featured_image', true) )) {
242
243 $content = get_the_content();
244 $upload_dir = themify_upload_dir('baseurl');
245 foreach ( [ 'img', 'iframe' ] as $tag) {
246 $count = substr_count($content, '<' . $tag);
247 if ($count >= 1) {
248 $start = strpos($content, '<' . $tag, 0);
249 $pos = substr($content, $start);
250 $end = strpos($pos, '>');
251 $temp = themify_prep_image(substr($pos, 0, $end + 1));
252 $src = $temp['src'];
253 $parse = parse_url($src);
254 if (!empty($parse['query'])) {
255 $src = str_replace('?' . $parse['query'], '', $src);
256 }
257 $auto_image_url = isset($temp['src']) ? $temp['src'] : '';
258 if (isset($temp['class'])) {
259 $args['class'] .= ' ' . $temp['class'];
260 }
261 $args['alt'] = $temp['alt'];
262 if ($is_disabled === true) {
263 $img_url = themify_image_url(false, $feature_size, themify_get_attachment_id_from_url($auto_image_url, $upload_dir));
264 if (empty($img_url)) {
265 $img_url = esc_url($auto_image_url);
266 }
267 } elseif ($temp = themify_do_img($auto_image_url, $width, $height, (bool) $args['crop'])) {
268 $img_url = $temp['url'];
269 }
270 break;
271 }
272 }
273 unset($content, $upload_dir);
274 }
275
276 if (!empty($img_url)) {
277 if (isset($temp['is_large']) && current_user_can('edit_post', $post_id)) {
278 $args['class'] .= ' tf_large_img';
279 } else {
280 if ($args['preload'] !== false || $args['prefetch'] !== false) {
281 Themify_Enqueue_Assets::addPreLoadMedia($img_url, $args['preload'] !== false ? 'preload' : 'prefetch');
282 }
283 themify_generateWebp($img_url);
284 }
285 unset($temp);
286 if ($args['urlonly']) {
287 $out = $img_url;
288 } else {
289 // Build final image
290 $out = '<img src="' . $img_url . '"';
291 if ($width !== '' && $width !== 0) {
292 $out .= ' width="' . $width . '"';
293 }
294 if ($height !== '' && $height !== 0) {
295 $out .= ' height="' . $height . '"';
296 }
297 if ($attachment_id != 0) {
298 $args['class'] .= ' wp-post-image wp-image-' . $attachment_id; /* add attachment_id class to img tag */
299 }
300 if ($args['class'] !== '') {
301 $out .= ' class="' . trim($args['class']) . '"';
302 }
303 if ($args['style'] !== '') {
304 $out .= ' style="' . $args['style'] . '"';
305 }
306 $title = '';
307 if (!empty($args['alt'])) {
308 $out_alt = $args['alt'];
309 } else {
310 $out_alt = $attachment_id ? get_post_meta($attachment_id, '_wp_attachment_image_alt', true) : '';
311 if (!$out_alt && $args['alt_fallback'] !== false) {
312 $out_alt = !empty($args['title']) ? $args['title'] : '';
313 if (!$out_alt && $attachment_id) {
314 $p = get_post($attachment_id);
315 $out_alt = !empty($p) ? $p->post_title : '';
316 $p = null;
317 }
318 if (!$out_alt) {
319 $out_alt = the_title_attribute('echo=0');
320 }
321 $title = $out_alt;
322 }
323 }
324 if($args['title'] !== false ){
325 if ($title === '') {
326 if (!empty($args['title'])) {
327 $title = $args['title'];
328 } elseif ($attachment_id) {
329 $p = get_post($attachment_id);
330 if (!empty($p)) {
331 $title = $p->post_title;
332 }
333 $p = null;
334 }
335 }
336 // Add title attribute only if explicitly set in $args
337 if (!empty($title)) {
338 $out .= ' title="' . esc_attr($title) . '"';
339 }
340 }
341 if ($args['lazy_load'] === false || $args['lazy_load'] === 'eager') {
342 $out .= ' data-tf-not-load="1"';
343 if ($args['lazy_load'] === 'eager') {
344 $out .= ' loading="eager" decoding="auto"';
345 }
346 }
347 if (!empty($args['attr'])) {
348 foreach ($args['attr'] as $k => $v) {
349 $out .= ' ' . $k . '="' . esc_attr($v) . '"';
350 }
351 }
352 $out .= ' alt="' . esc_attr( (string) $out_alt ) . '"';
353 $out .= '>';
354 if ($attachment_id && $args['disable_responsive'] === false) {
355 $out = function_exists('wp_filter_content_tags') ? wp_img_tag_add_srcset_and_sizes_attr($out, null, $attachment_id) // WP 5.5
356 : wp_make_content_images_responsive($out);
357 }
358 if (($args['is_slider'] === true || $themify->post_layout === 'slider') && $args['lazy_load'] === true) {
359 $out = themify_make_lazy($out, false);
360 }
361 if ($args['image_meta'] == true) {
362 $out .= "<meta itemprop=\"width\" content=\"{$width}\"><meta itemprop=\"height\" content=\"{$height}\"><meta itemprop=\"url\" content=\"{$img_url}\">" . $out;
363 }
364 $out = $args['before'] . $out . $args['after'];
365 }
366 } else {
367 $out = '';
368 }
369
370 return $out;
371 }
372
373 if (!function_exists('themify_edit_link')) {
374
375 function themify_edit_link(string $title = '') : bool {
376 static $is = null;
377 if ($is === null) {
378 $is = is_user_logged_in() && (!class_exists('Themify_Builder_Model',false) || !Themify_Builder::$frontedit_active === true) && !themify_is_rest();
379 }
380 if ($is === true) {
381 if ($title === '') {
382 $title = sprintf( __('Edit %s', 'themify'), '<span class="tf_hide">' . get_post_type_object( get_post_type() )->labels->singular_name . '</span>' );
383 }
384 edit_post_link($title, '<span class="edit-button tf_edit_post_' . get_the_ID() . '">', '</span>');
385 }
386
387 return $is;
388 }
389
390 }
391
392 if (!function_exists('themify_image_url')) {
393
394 /**
395 * Returns the featured image url
396 * @param bool $echo Specify to echo or return the url
397 * @param string $size The image size to return
398 * @param null|int $attachment_id ID of image to load.
399 * @return void|string
400 */
401 function themify_image_url(bool $echo = false, string $size = 'full', $attachment_id = null):string {
402 $url = '';
403 if (has_post_thumbnail()) {
404 $image = wp_get_attachment_image_src(get_post_thumbnail_id(), $size);
405 $url = $image === false ? '' : $image[0];
406 } elseif ($attachment_id !== null) {
407 $image = wp_get_attachment_image_src($attachment_id, $size);
408 if ($image) {
409 $url = $image[0];
410 }
411 }
412 $url = apply_filters('themify_image_url', $url);
413 if ($echo) {
414 echo esc_url($url);
415 return '';
416 }
417 return $url;
418 }
419
420 }
421
422 /**
423 * Image Helper - Prep Image
424 * @param $tag
425 * @return array
426 */
427 function themify_prep_image(string $tag):array {
428 $image = [];
429 preg_match_all( '/(alt|title|src|class)=(("|\')[^("|\')]*("|\'))/i', $tag, $image_reg );
430 foreach ( $image_reg[1] as $index => $attribute ) {
431 $image[ $attribute ] = substr( $image_reg[2][ $index ], 1, -1 );
432 }
433 $image += [ 'src' => '', 'alt' => '', 'title' => '' ];
434
435 if (strpos($image['src'], 'youtube.com') !== false || strpos($image['src'], 'vimeo.com') !== false) {
436 $image['src'] = themify_video_image($image['src']);
437 }
438
439 $image['src'] = preg_replace('/(-\d+x\d+)(?=\.\w{3,4})/', '', $image['src']);
440
441 return $image;
442 }
443
444 /**
445 * Vimeo / Youtube Thumbnail grab
446 *
447 * @param $url
448 *
449 * @return string
450 */
451 function themify_video_image(string $url):string {
452 $image_url = parse_url($url);
453 $return_url = '';
454 if ($image_url['host'] === 'www.youtube.com' || $image_url['host'] === 'youtube.com') {
455 parse_str($image_url['query'], $query);
456 if (!empty($query['v'])) {
457 $id = $query['v'];
458 } else {
459 $path = explode('/', $image_url['path']);
460 $id = $path[count($path) - 1];
461 }
462 $return_url = 'https://img.youtube.com/vi/' . $id . '/hqdefault.jpg';
463 } elseif ($image_url['host'] === 'www.vimeo.com' || $image_url['host'] === 'vimeo.com' || $image_url['host'] === 'player.vimeo.com') {
464 parse_str($image_url['query'], $query);
465 if (!empty($query['clip_id'])) {
466 $id = $query['clip_id'];
467 } else {
468 $path = explode('/', $image_url['path']);
469 $id = $path[(count($path) - 1)];
470 }
471 $content = Themify_Filesystem::get_contents('https://vimeo.com/api/v2/video/' . $id . '.php');
472 if (!empty($content)) {
473 $hash = themify_maybe_unserialize( $content );
474 if ( ! is_array( $hash ) ) {
475 $hash = array();
476 }
477 if (!empty($hash[0])) {
478 $return_url = $hash[0]["thumbnail_large"];
479 }
480 }
481 }
482 return $return_url;
483 }
484
485 /**
486 * Checks if a value referenced by $var exists in theme settings or post meta data.
487 * @param $var
488 * @return bool
489 */
490 function themify_check(string $var, bool $data_only = false):bool {
491 $val = themify_get($var, null, $data_only);
492 return $val !== null && $val !== '' && $val !== 'off';
493 }
494
495 /**
496 * Returns a value referenced by $var checking in theme settings or post meta data.
497 * @param $var
498 * @return mixed
499 */
500 function themify_get(string $var, $default = null, bool $data_only = false) {
501 $data = themify_get_data();
502 if (isset($data[$var]) && $data[$var] !== '') {
503 return $data[$var];
504 } elseif ($data_only !== true) {
505 global $post;
506
507 if (themify_is_shop() && !in_the_loop()) {
508 $id = themify_shop_pageId();
509 } elseif (is_object($post)) {
510 $id = $post->ID;
511 } else {
512 $id = false;
513 }
514
515 $val = ( $id !== false && $id !== 0 ) ? get_post_meta($id, $var, true) : '';
516
517 if ($val !== '') {
518 return $val;
519 }
520 }
521
522 return $default;
523 }
524
525 /**
526 * Returns a value referenced by $meta,$var checking in theme settings or post meta data.
527 * @param $meta - post meta
528 * @param $var - theme settings
529 * @param $default
530 * @return mixed
531 */
532 function themify_get_both(string $meta, string $var, $default = null) {
533 $val = themify_get($meta);
534 if ($val === null || $val === 'default') {
535 $val = themify_get($var, $default, true);
536 }
537 return $val;
538 }
539
540 /**
541 * Get a color value, uses themify_get and sanitizes the value
542 *
543 * @return string
544 */
545 function themify_get_color_both(string $meta, string $var, $default = null) {
546 $val = themify_get_color($meta);
547 if ($val === null || $val === 'default') {
548 $val = themify_get_color($var, $default, true);
549 }
550 return $val;
551 }
552
553 /**
554 * Returns a value referenced by $meta,$var checking in theme settings or post meta data.
555 * @param $meta - post meta
556 * @param $var - theme settings
557 * @return mixed
558 */
559 function themify_check_both(string $meta, string $var):bool {
560 $val = themify_get($meta, null);
561 if ($val === null || $val === 'default') {
562 $val = themify_get($var, null, true);
563 }
564 return $val !== null && $val !== '' && $val !== 'off';
565 }
566
567 function themify_shop_pageId() {
568 static $id = null;
569 if ($id === null) {
570 if (themify_is_woocommerce_active() && function_exists( 'wc_get_page_id' ) ) {
571 $id = wc_get_page_id('shop');
572 if ($id <= 0) {//wc bug, page id isn't from wc settings,the default should be page with slug 'shop'
573 $page = get_page_by_path('shop');
574 $id = !empty($page) ? (int) $page->ID : false;
575 }
576 } else {
577 $id = false;
578 }
579 }
580 return $id;
581 }
582
583 /**
584 * Get a color value, uses themify_get and sanitizes the value
585 *
586 * @return string
587 */
588 function themify_get_color(string $var, $default = null, bool $data_only = false) {
589 $value = themify_get($var, $default, $data_only);
590 if ( $value === null || false === $value || '' === $value ) {
591 return $value;
592 }
593 $value = trim( (string) $value );
594 if ( '' === $value ) {
595 return $value;
596 }
597 if ( 0 === stripos( $value, 'var(' ) ) {
598 return $value;
599 }
600 if ( 0 === strpos( $value, '--' ) ) {
601 return 'var(' . $value . ')';
602 }
603 return themify_sanitize_hex_color( $value );
604 }
605
606 /**
607 * Sanitize a string to ensure it's valid hex color code
608 *
609 * @since 3.1.2
610 */
611 function themify_sanitize_hex_color($value) {
612 if ($value===null || false === $value) {
613 return $value;
614 }
615 $value = ltrim($value, '#');
616 /* match 3 to 6 hex digits */
617 if (preg_match('|^([A-Fa-f0-9]{3}){1,2}$|', $value)) {
618 $value = '#' . $value;
619 }
620
621 return $value;
622 }
623
624 /**
625 * Sanitize space-separated CSS class names for safe HTML class attributes.
626 *
627 * @since 7.0
628 */
629 function themify_sanitize_css_classes( $classes ) {
630 if ( $classes === '' || $classes === null ) {
631 return '';
632 }
633 if ( ! is_string( $classes ) ) {
634 return '';
635 }
636 $parts = preg_split( '/\s+/', trim( $classes ) );
637 $sanitized = array();
638 foreach ( $parts as $part ) {
639 if ( $part === '' ) {
640 continue;
641 }
642 $clean = sanitize_html_class( $part );
643 if ( $clean !== '' ) {
644 $sanitized[] = $clean;
645 }
646 }
647 return implode( ' ', $sanitized );
648 }
649
650 /**
651 * Sanitize a CSS length value with an allowed unit.
652 *
653 * @since 7.0
654 */
655 function themify_sanitize_css_size( $value, $unit = 'px' ) {
656 $value = preg_replace( '/[^0-9.]/', '', (string) $value );
657 if ( $value === '' ) {
658 return '';
659 }
660 $allowed_units = array( 'px', '%', 'em', 'rem', 'vw', 'vh', 'vmin', 'vmax' );
661 if ( ! in_array( $unit, $allowed_units, true ) ) {
662 $unit = 'px';
663 }
664 return $value . $unit;
665 }
666
667 /**
668 * Sanitize a CSS unit token.
669 *
670 * @since 7.0
671 */
672 function themify_sanitize_css_unit( $unit, $default = 'px' ) {
673 $allowed_units = array( 'px', '%', 'em', 'rem', 'vw', 'vh', 'vmin', 'vmax' );
674 $unit = strtolower( trim( (string) $unit ) );
675 if ( ! in_array( $unit, $allowed_units, true ) ) {
676 $unit = in_array( $default, $allowed_units, true ) ? $default : 'px';
677 }
678 return $unit;
679 }
680
681 /**
682 * Sanitize slider height option for data-height attributes.
683 *
684 * @since 7.0
685 */
686 function themify_sanitize_slider_height( $value ) {
687 $value = (string) $value;
688 if ( in_array( $value, array( 'variable', 'auto', '' ), true ) ) {
689 return $value;
690 }
691 if ( preg_match( '/^([0-9.]+)(px|%|em|rem|vw|vh|vmin|vmax)$/i', $value, $matches ) ) {
692 return themify_sanitize_css_size( $matches[1], strtolower( $matches[2] ) );
693 }
694 return 'variable';
695 }
696
697 /**
698 * Sanitize CSS border-style values.
699 *
700 * @since 7.0
701 */
702 function themify_sanitize_border_style( $value, $default = 'solid' ) {
703 $allowed = array( 'solid', 'dashed', 'dotted', 'double', 'none' );
704 $value = strtolower( trim( (string) $value ) );
705 return in_array( $value, $allowed, true ) ? $value : $default;
706 }
707
708 /**
709 * Sanitize inline CSS declarations for style attributes.
710 *
711 * @since 7.0
712 */
713 function themify_sanitize_inline_css( $css ) {
714 if ( $css === '' || $css === null ) {
715 return '';
716 }
717 $css = wp_strip_all_tags( (string) $css );
718 $css = preg_replace( '/expression\s*\(/i', '', $css );
719 $css = preg_replace( '/javascript\s*:/i', '', $css );
720 $css = preg_replace( '/@import/i', '', $css );
721 $css = preg_replace( '/<\/?style/i', '', $css );
722 return trim( $css );
723 }
724
725 /**
726 * Sanitize builder custom CSS before writing to a stylesheet file.
727 *
728 * @since 7.0
729 */
730 function themify_sanitize_builder_custom_css( $css ) {
731 if ( ! is_string( $css ) || $css === '' ) {
732 return '';
733 }
734 $css = preg_replace( '/<\/style/i', '', $css );
735 $css = preg_replace( '/<script/i', '', $css );
736 return $css;
737 }
738
739 /**
740 * Sanitize builder custom JS before printing on the page.
741 *
742 * @since 7.0
743 */
744 function themify_sanitize_builder_custom_js( $js ) {
745 if ( ! is_string( $js ) || $js === '' ) {
746 return '';
747 }
748 $js = preg_replace( '/<\/script/i', '', $js );
749 return $js;
750 }
751
752 if (!function_exists('themify_get_image_sizes_list')) {
753
754 /**
755 * Return list of image sizes with labels for translation.
756 * @param bool $nested
757 * @return mixed|void
758 * @since 1.6.8
759 */
760 function themify_get_image_sizes_list(bool $nested = true) {
761 $size_names = apply_filters('image_size_names_choose',
762 array(
763 'thumbnail' => __('Thumbnail', 'themify'),
764 'medium' => __('Medium', 'themify'),
765 'large' => __('Large', 'themify'),
766 'full' => __('Original Image', 'themify')
767 )
768 );
769 $out = array(
770 array('value' => 'blank', 'name' => ''),
771 );
772 foreach ($size_names as $size => $label) {
773 $out[] = array('value' => $size, 'name' => $label);
774 }
775 return apply_filters('themify_get_image_sizes_list', $nested ? $out : $size_names, $nested);
776 }
777
778 }
779
780 /**
781 * Check if the site is using an HTTPS scheme and returns the proper url
782 * @param String $url requested url
783 * @return String
784 * @since 1.1.5
785 */
786 function themify_https_esc(string $url = ''):string {
787 if (is_ssl()) {
788 $url = str_replace('http:', 'https:', $url);
789 }
790 return $url;
791 }
792
793 /**
794 * Returns an array with the post types managed by Themify,
795 * where the Themify Custom Panel is initialized.
796 * Filterable using themify_post_types
797 * @param Array $types additional post types
798 * @return Array
799 * @since 1.1.5
800 */
801 function themify_post_types(array $types = array()):array {
802 $defaults = array_merge(array('post', 'page'), apply_filters('themify_specific_post_types', array('menu', 'slider', 'highlight', 'portfolio', 'testimonial', 'section')), $types);
803 if (themify_is_themify_theme() && themify_is_woocommerce_active() && is_file(THEME_DIR . '/admin/post-type-product.php')) {
804 $defaults[] = 'product';
805 }
806 return array_keys(array_flip(apply_filters('themify_post_types', $defaults)));
807 }
808
809 if (!function_exists('themify_options_module')) {
810
811 /**
812 * Returns list of <option>
813 * @param array $options List of options
814 * @param string $key
815 * @param bool $associative
816 * @param string $default
817 * @return string
818 * @since 1.3.5
819 */
820 function themify_options_module($options = array(), $key = '', $associative = true, $default = ''):string {
821 $data = themify_get_data();
822 $sel = isset($data[$key]) ? $data[$key] : $default;
823 $data = null;
824 $output = '';
825 if (true === $associative) {
826 foreach ($options as $option) {
827 $output .= '<option ' . selected($option['value'], $sel, false) . ' value="' . esc_attr($option['value']) . '">' . esc_html($option['name']) . '</option>';
828 }
829 } else {
830 foreach ($options as $option) {
831 $option = esc_attr($option);
832 $selected = $sel === $option ? ' selected="selected"' : '';
833 $output .= '<option value="' . $option . '"' . $selected . '>' . esc_html($option) . '</option>';
834 }
835 }
836 return $output;
837 }
838
839 }
840
841 if (!function_exists('themify_lightbox_vars_init')) {
842
843 /**
844 * Post Gallery lightbox/fullscreen and single lightbox definition
845 * @return array Lightbox/Fullscreen galleries initialization parameters
846 */
847 function themify_lightbox_vars_init():array {
848 $gallery_lightbox = themify_get('setting-gallery_lightbox', 'lightbox', true);
849 // Lightbox default settings
850 $overlay_args = array();
851 if (themify_check('setting-lightbox_content_images', true)) {
852 $overlay_args['contentImagesAreas'] = '.post, .type-page, .type-highlight, .type-slider';
853 }
854 if (themify_check('setting-lightbox_disable_share', true)) {
855 $overlay_args['disable_sharing'] = true;
856 }
857 if ($gallery_lightbox === 'none') {
858 $overlay_args['gallerySelector'] = '';
859 }
860
861 $overlay_args = apply_filters('themify_gallery_plugins_args', $overlay_args);
862 // sanitize contentImagesAreas, ensure it's a valid CSS selector
863 if (isset($overlay_args['contentImagesAreas'])) {
864 $overlay_args['contentImagesAreas'] = trim($overlay_args['contentImagesAreas'], ',');
865 }
866 $overlay_args['i18n'] = array(
867 'tCounter' => __('%curr% of %total%', 'themify'),
868 );
869
870 return $overlay_args;
871 }
872
873 }
874
875 if (!function_exists('themify_get_shortcode_template')) {
876
877 /**
878 * Returns markup
879 * @param $posts
880 * @param string $slug
881 * @param string $name
882 * @return mixed|void
883 */
884 function themify_get_shortcode_template($posts, string $slug = 'includes/loop', string $name = 'index', array $args = array()):string {
885 global $post, $themify;
886 Themify_Enqueue_Assets::loadGridCss($themify->post_layout);
887
888 if (is_object($post)){
889 $saved_post = clone $post;
890 }
891 if(property_exists($themify, 'is_shortcode')){
892 $isShortcode = $themify->is_shortcode === true;
893 $themify->is_shortcode = true;
894 }
895
896 // Add flag that template loop is in builder loop
897 if (class_exists('\Themify_Builder',false)) {
898 $isLoop = Themify_Builder::$is_loop;
899 Themify_Builder::$is_loop = true;
900 }
901
902 // get_template_part, defined in wp-includes/general-template.php
903 $templates = array();
904 if ('' !== $name) {
905 $templates[] = "{$slug}-{$name}.php";
906 }
907 $templates[] = "{$slug}.php";
908 $template_file = apply_filters('themify_get_shortcode_template_file', locate_template($templates, false, false), $slug, $name);
909 $isSlider = $themify->post_layout === 'slider' || (isset($args['isSlider']) && $args['isSlider'] === true);
910 ob_start();
911 if (!empty($template_file)) {
912 $before = isset($args['before_post']) ? $args['before_post'] : '';
913 $after = isset($args['after_post']) ? $args['after_post'] : '';
914 foreach ($posts as $p) {
915 $post=$p;
916 setup_postdata($post);
917 if ($isSlider === true) {
918 echo '<div class="tf_lazy tf_swiper-slide">';
919 }
920 echo $before;
921 include $template_file;
922 echo $after;
923 if ($isSlider === true) {
924 echo '</div>';
925 }
926 }
927 $posts = null;
928 }
929
930 if (isset($saved_post)) {
931 $post = $saved_post;
932 setup_postdata($saved_post);
933 unset($saved_post);
934 }
935 // Add flag that template loop is in builder loop
936 if (isset($isLoop)) {
937 Themify_Builder::$is_loop = $isLoop;
938 }
939 if(isset($isShortcode)){
940 $themify->is_shortcode = $isShortcode;
941 }
942 $res=ob_get_clean();
943 return $res?$res:'';
944 }
945
946 }
947
948 if (!function_exists('themify_resolve_gallery_shortcode_string')) {
949
950 /**
951 * Expand wrapper shortcodes (e.g. ACF) that return a literal [gallery ids="..."] string.
952 * Stops before WordPress would render [gallery] to HTML.
953 *
954 * @param string|null $shortcode Raw field value from Builder (may be [acf_...] or [gallery ...]).
955 * @return string String containing [gallery ... ids=...] when resolvable, otherwise original/expanded text.
956 */
957 function themify_resolve_gallery_shortcode_string(?string $shortcode): string {
958 if ($shortcode === null || $shortcode === '') {
959 return '';
960 }
961 $shortcode = trim($shortcode);
962 if (strpos($shortcode, '[') === false) {
963 return $shortcode;
964 }
965 // Already a gallery shortcode with ids — do not run do_shortcode() or core will output HTML.
966 if (preg_match('/\[gallery\s+[^\]]*ids\s*=/i', $shortcode)) {
967 return $shortcode;
968 }
969 $prev = '';
970 for ($i = 0; $i < 8; ++$i) {
971 if ($shortcode === $prev) {
972 break;
973 }
974 $prev = $shortcode;
975 $next = do_shortcode($shortcode);
976 if ($next === $shortcode) {
977 break;
978 }
979 $shortcode = $next;
980 if (preg_match('/\[gallery\s+[^\]]*ids\s*=/i', $shortcode)) {
981 break;
982 }
983 }
984 return $shortcode;
985 }
986 }
987
988 if (!function_exists('themify_get_gallery_shortcode')) {
989
990 /**
991 * Get images from gallery shortcode
992 * @return object
993 */
994 function themify_get_gallery_shortcode(?string $shortcode):array {
995 if ($shortcode) {
996 $shortcode = themify_resolve_gallery_shortcode_string($shortcode);
997 preg_match('/\[gallery.*ids.*?=.(.*).\]/si', $shortcode, $ids);
998 if (isset($ids[1])) {
999 $ids = trim($ids[1], '\\');
1000 $ids = trim($ids, '"');
1001 $image_ids = explode(',', $ids);
1002
1003 // Check if post has more than one image in gallery
1004 return get_posts(array(
1005 'post__in' => $image_ids,
1006 'post_type' => 'attachment',
1007 'post_mime_type' => 'image',
1008 'numberposts' => -1,
1009 'no_found_rows' => true,
1010 'cache_results' => false,
1011 'update_post_term_cache' => false,
1012 'update_post_meta_cache' => false,
1013 'orderby' => themify_get_gallery_shortcode_params($shortcode, 'orderby', 'post__in'),
1014 'order' => themify_get_gallery_shortcode_params($shortcode, 'order', 'ASC')
1015 ));
1016 }
1017 }
1018 return array();
1019 }
1020
1021 }
1022
1023 if (!function_exists('themify_get_gallery_shortcode_params')) {
1024
1025 /**
1026 * Get gallery shortcode options
1027 * @param $shortcode
1028 * @param $param
1029 */
1030 function themify_get_gallery_shortcode_params(string $shortcode, string $param = 'link', $default = '') {
1031 $shortcode = themify_resolve_gallery_shortcode_string($shortcode);
1032 $pattern = '/\[gallery .*?(?=' . $param . ')' . $param . '=.([^\']+)./si';
1033 preg_match($pattern, $shortcode, $out);
1034 $out = isset($out[1]) ? explode('"', $out[1]) : array('');
1035 return $out[0] !== '' ? $out[0] : $default;
1036 }
1037
1038 }
1039
1040 function themify_get_additional_image_sizes(string $size = 'all', $default = false) {
1041 $allSizes = wp_get_additional_image_sizes();
1042 if ($size === 'all') {
1043 return $allSizes;
1044 }
1045 return isset($allSizes[$size]) ? $allSizes[$size] : $default;
1046 }
1047
1048 if (!function_exists('themify_get_all_terms_ids')) {
1049
1050 /**
1051 * Returns all IDs from the given taxonomy
1052 * @param string $tax Taxonomy to retrieve terms from.
1053 * @return array $term_ids Array of all taxonomy terms
1054 * @since 1.5.6
1055 */
1056 function themify_get_all_terms_ids(string $tax = 'category') {
1057 if (!$term_ids = wp_cache_get('all_' . $tax . '_ids', $tax)) {
1058 $term_ids = get_terms(array('taxonomy' => $tax, 'fields' => 'ids', 'get' => 'all'));
1059 wp_cache_add('all_' . $tax . '_ids', $term_ids, $tax);
1060 }
1061 return $term_ids;
1062 }
1063
1064 }
1065
1066 if (!function_exists('themify_is_touch')) {
1067
1068 /**
1069 * According to what $check parameter specifies to check, returns true if it's a phone, tablet, or both.
1070 * @param string $check
1071 * @return mixed
1072 * @since 1.6.8
1073 */
1074 function themify_is_touch($check = 'all') {
1075 static $arr = array();
1076
1077 if (!isset($arr[$check])) {
1078 if (!class_exists('Themify_Mobile_Detect',false)) {
1079 require_once 'class-themify-mobile-detect.php';
1080 }
1081 $detect = new Themify_Mobile_Detect();
1082 $is_tablet = isset($arr['tablet']) ? $arr['tablet'] : $detect->isTablet();
1083 $is_mobile = isset($arr['phone']) ? $arr['phone'] : (wp_is_mobile() || $detect->isMobile());
1084 $arr = array(
1085 'phone' => $is_mobile && !$is_tablet,
1086 'tablet' => $is_tablet,
1087 'all' => $is_mobile
1088 );
1089 $detect = null;
1090 }
1091
1092 return $arr[$check];
1093 }
1094
1095 }
1096
1097 /**
1098 * Checks that status of the image script.
1099 * @return bool
1100 * @since 1.7.4
1101 */
1102 function themify_is_image_script_disabled():bool {
1103 static $is = null;
1104 if ($is === null) {
1105 $is = wp_image_editor_supports() ? themify_check('setting-img_settings_use', true) : false;
1106 }
1107
1108 return $is;
1109 }
1110
1111
1112 if (!function_exists('themify_get_available_menus')) {
1113
1114 /**
1115 * Returns available navigation menus.
1116 *
1117 * @since 1.9.5
1118 *
1119 * @return array
1120 */
1121 function themify_get_available_menus():array {
1122 $out = array(array('name' => '', 'value' => '', 'selected' => true));
1123 $menus = get_terms(array(
1124 'taxonomy' => 'nav_menu',
1125 'hide_empty' => false,
1126 ));
1127 if (!empty($menus) && !is_wp_error($menus)) {
1128 foreach ($menus as $menu) {
1129 $out[] = array('name' => $menu->name, 'value' => $menu->slug);
1130 }
1131 }
1132 return apply_filters('themify_get_available_menus', $out);
1133 }
1134
1135 }
1136
1137 /**
1138 * Returns true if the active theme is using Themify framework
1139 *
1140 * @since 2.2.5
1141 * @return bool
1142 */
1143 function themify_is_themify_theme():bool {
1144 static $is = null;
1145 if ($is === null) {
1146 $is = is_file(trailingslashit(get_template_directory()) . 'themify/themify-utils.php');
1147 }
1148
1149 return $is;
1150 }
1151
1152 if (!function_exists('themify_is_woocommerce_active')) {
1153
1154 /**
1155 * Checks if Woocommerce plugin is active and returns the proper value
1156 * @return bool
1157 * @since 1.4.6
1158 */
1159 function themify_is_woocommerce_active():bool {
1160 static $is = null;
1161 if ($is === null) {
1162 $plugin = 'woocommerce/woocommerce.php';
1163 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1164 return is_plugin_active($plugin)
1165 // validate if $plugin actually exists, the plugin might be active however not installed.
1166 && is_file(trailingslashit(WP_PLUGIN_DIR) . $plugin);
1167 }
1168 return $is;
1169 }
1170
1171 }
1172
1173 /**
1174 * Calls is_shop() in a safe manner
1175 *
1176 * @note: this function should not be called before template_redirect.
1177 *
1178 * @since 2.9.0
1179 */
1180 function themify_is_shop($pageId = false):bool {
1181 static $is = null;
1182 if ($is === null) {
1183 $is = themify_is_woocommerce_active();
1184 if ($is === true) {
1185 $is = is_post_type_archive('product') || ($pageId > 0 && $pageId === themify_shop_pageId()); //don't use shop function will give error
1186 if ($is === false) {
1187 $shop_url = themify_get_shop_permalink(); //wc bug, page id isn't set from wc settings,the default should be page with slug 'shop'
1188 if ($shop_url !== '') {
1189 $current_url = 'http';
1190 if (is_ssl()) {
1191 $current_url .= 's';
1192 }
1193 $current_url .= '://';
1194 if (isset($_SERVER['HTTP_HOST'])) {
1195 $current_url .= $_SERVER['HTTP_HOST'];
1196 } else {
1197 $host = parse_url(home_url(), PHP_URL_HOST);
1198 if ($host !== false) {
1199 $current_url .= $host;
1200 }
1201 unset($host);
1202 }
1203 $current_url .= strtok($_SERVER['REQUEST_URI'], '?');
1204 $is = $current_url === $shop_url;
1205 } else {
1206 $is = false;
1207 }
1208 }
1209 }
1210 }
1211 return $is;
1212 }
1213
1214 /**
1215 * Modified version of wp_parse_args which adds filters to modify the args
1216 *
1217 * @return array
1218 * @since 2.7.7
1219 */
1220 function themify_parse_args($args, $defaults = '', $filter_key = '') {
1221 // Setup a temporary array from $args
1222 if (is_object($args)) {
1223 $r = get_object_vars($args);
1224 } elseif (is_array($args)) {
1225 $r = & $args;
1226 } else {
1227 wp_parse_str($args, $r);
1228 }
1229 // Passively filter the args before the parse
1230 if (!empty($filter_key)) {
1231 $r = apply_filters('themify_before_' . $filter_key . '_parse_args', $r);
1232 }
1233 // Parse
1234 if (is_array($defaults)) {
1235 $r = array_merge($defaults, $r);
1236 }
1237 // Aggressively filter the args after the parse
1238 if (!empty($filter_key)) {
1239 $r = apply_filters('themify_after_' . $filter_key . '_parse_args', $r);
1240 }
1241 // Return the parsed results
1242 return $r;
1243 }
1244
1245 /**
1246 * Display the Builder's backend editor in Themify Custom Panel
1247 *
1248 * @return null
1249 * @since 2.8.8
1250 */
1251 function themify_meta_field_page_builder() {
1252 do_action('themify_builder_metabox');
1253 }
1254
1255 /**
1256 * Get an array of key => value pairs and outputs them as HTML attributes
1257 *
1258 * @since 2.9.1
1259 * @return string
1260 */
1261 function themify_get_element_attributes(array $props):string {
1262 $out = '';
1263 foreach ($props as $k => $v) {
1264 $out .= ' ' . $k . '="' . esc_attr($v) . '"';
1265 }
1266
1267 return $out;
1268 }
1269
1270
1271 /**
1272 * Get breakpoints settings
1273 * if it's framework return customizer breakpoints,else if it's builder plugin builder breakpoints
1274 * @since 3.0.0
1275 * @return mixed array/int
1276 */
1277 function themify_get_breakpoints(string $select = 'all', bool $max_min = false) {
1278 static $res = array();
1279 if (($select === 'all' && empty($res)) || (empty($res[$select]))) {
1280 $breakpoints = array(
1281 'tablet_landscape' => array(769, 1280),
1282 'tablet' => array(681, 768),
1283 'mobile' => 680
1284 );
1285 if ($max_min === true) {
1286 return $breakpoints;
1287 }
1288 foreach ($breakpoints as $bp => $value) {
1289 $v = themify_builder_get('setting-customizer_responsive_design_' . $bp, 'builder_responsive_design_' . $bp);
1290 if ('' != $v) {
1291 if (is_array($value)) {
1292 $res[$bp] = array();
1293 $res[$bp][0] = $value[0];
1294 $res[$bp][1] = (int) $v;
1295 } else {
1296 $res[$bp] = (int) $v;
1297 }
1298 } else {
1299 $res[$bp] = $value;
1300 }
1301 }
1302 $res['tablet'][0] = $res['mobile'] + 1;
1303 $res['tablet_landscape'][0] = $res['tablet'][1] + 1;
1304 }
1305 return 'all' === $select ? $res : $res[$select];
1306 }
1307
1308 /**
1309 * Unserialize data if it is serialized
1310 *
1311 * If PHP supports it disables unserializing PHP objects in order to prevent object injection.
1312 *
1313 * @param string $original Maybe unserialized original, if is needed.
1314 * @return mixed Unserialized data can be any type.
1315 */
1316 function themify_maybe_unserialize(string $original) {
1317 if (is_serialized($original)) { // don't attempt to unserialize data that wasn't serialized going in
1318 return @unserialize($original, array('allowed_classes' => false));
1319 }
1320
1321 return $original;
1322 }
1323
1324 /**
1325 * Display the post video
1326 * Must be used inside the loop
1327 *
1328 * @since 2.7.3
1329 */
1330 function themify_post_video(string $url = '', bool $echo = true) {
1331 $video_file = $url ? $url : themify_get('video_url');
1332 $output = '';
1333 if (!empty($video_file)) {
1334 if ('mp4' !== pathinfo($video_file, PATHINFO_EXTENSION)) {
1335 global $wp_embed;
1336 $output = $wp_embed->run_shortcode('[embed]' . $video_file . '[/embed]');
1337 } else {
1338 $output = '<div class="post-video">';
1339 $output .= do_shortcode('[video src="' . $video_file . '"]');
1340 $output .= '</div>';
1341 }
1342 }
1343 if ($echo === true) {
1344 echo $output;
1345 } else {
1346 return $output;
1347 }
1348 }
1349
1350 function themify_get_embed(string $url, array $args):string {
1351 $reg = '#(vimeo\.com|youtu(be\.com|\.be|be))\/(shorts\/|video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?\/?([A-Za-z0-9._%-]*)(\&\S+)?#i';
1352 preg_match($reg, $url, $m);
1353 if (!empty($m) && isset($m[1], $m[4])) {
1354 $type = ($m[1] === 'youtube.com' || strpos($m[1], 'youtu') !== false) ? 'youtube' : ($m[1] === 'vimeo.com' || strpos($m[1], 'vimeo') ? 'vimeo' : false);
1355 if ($type !== false) {
1356 $id = $m[4];
1357 $query_args = array();
1358 if ($type !== 'youtube' && isset($m[6])) {//h query argument should be first in vimeo
1359 $query_args['h'] = $m[6];
1360 }
1361 $query_args += array(
1362 'pip' => 1,
1363 'playsinline' => 1
1364 );
1365 $params = parse_url($url);
1366 if (!empty($params['query'])) {
1367 parse_str($params['query'], $output);
1368 unset($output['v']);
1369 $query_args += $output;
1370 unset($output);
1371 }
1372 if ($type === 'youtube') {
1373 $allow = 'accelerometer;encrypted-media;gyroscope;picture-in-picture;fullscreen';
1374 $src = 'https://www.youtube';
1375 if (!empty($args['privacy'])) {
1376 $src .= '-nocookie';
1377 }
1378 $src .= '.com/embed/' . $id;
1379 if (!empty($args['start']) ) {
1380 $query_args['start'] = (float) $args['start'];
1381 }
1382 elseif (!empty($params['fragment'])) {
1383 $query_args['start'] = (float) $params['fragment'];
1384 if(empty($query_args['start'])){
1385 unset($query_args['start']);
1386 }
1387 }
1388 if (!empty($args['end'])) {
1389 $query_args['end'] = (float) $args['end'];
1390 }
1391 }
1392 else {
1393
1394 $allow = 'fullscreen';
1395 $src = 'https://player.vimeo.com/video/' . $id;
1396 if (!empty($args['start'])) {
1397 $src .= '#t=' . $args['start'];
1398 } elseif (!empty($params['fragment'])) {
1399 $src .= '#' . $params['fragment'];
1400 }
1401 if (!empty($args['privacy'])) {
1402 $query_args['dnt'] = 1;
1403 }
1404 $query_args['byline'] =$query_args['title'] = $query_args['portrait'] = 0;
1405 }
1406 unset(
1407 $params,
1408 $query_args['modestbranding'],
1409 $query_args['controls'],
1410 $query_args['loop'],
1411 $query_args['mute'],
1412 $query_args['muted']
1413 );
1414 if (!empty($args['hide_controls'])) {
1415 $query_args['controls'] = 0;
1416 }
1417
1418 if (!empty($args['loop'])) {
1419 $query_args['loop'] = 1;
1420 if (!isset($query_args['playlist'])) {
1421 $query_args['playlist'] = $id;
1422 }
1423 }
1424 if (!empty($args['autoplay']) || !empty($query_args['autoplay'])) {
1425 $allow .= ';autoplay';
1426 $query_args['autoplay'] = 1;
1427 }
1428 if (!empty($args['muted'])) {
1429 if ($type === 'youtube') {
1430 $query_args['mute'] = 1;
1431 } else {
1432 $query_args['muted'] = 1;
1433 }
1434 }
1435 $lazy = !empty($args['disable_lazy']) ? ' data-no-script' : '';
1436 $src .= '?' . http_build_query($query_args);
1437 $class=isset($args['class'])?$args['class']:'tf_abs tf_w tf_h';
1438 return '<iframe' . $lazy . ' src="' . esc_url( $src ) . '" allow="' . $allow . '" class="'.$class.'"></iframe>';
1439 }
1440 }
1441 return '';
1442 }
1443
1444 function themify_enque_style(string $handle,string $src = '',?array $deps = array(),?string $ver = THEMIFY_VERSION,?string $media = 'all', bool $in_footer = false) {
1445 Themify_Enqueue_Assets::add_css($handle, $src, $deps, $ver, $media, $in_footer);
1446 }
1447
1448 function themify_enque_script(string $handle, string $src = '', string $ver = THEMIFY_VERSION,?array $deps = array(), bool $in_footer = true) {
1449 Themify_Enqueue_Assets::add_js($handle, $src, $deps, $ver, $in_footer);
1450 }
1451
1452 if (!function_exists('themify_get_query_categories')) {
1453
1454 function themify_get_query_categories():array {
1455 global $themify;
1456
1457 $taxes = $themify->query_category == '0' ? themify_get_all_terms_ids($themify->query_taxonomy) : explode(',', str_replace(' ', '', $themify->query_category));
1458 if (empty($taxes) || is_wp_error($taxes)) {
1459 $taxes= array();
1460 }
1461 return $taxes;
1462 }
1463
1464 }
1465
1466 /**
1467 * Returns the URL to the WooCommerce Shop page
1468 *
1469 * @return string
1470 * @since 4.5.7
1471 */
1472 function themify_get_shop_permalink():string {
1473 static $link = null;
1474 if ($link === null) {
1475 $id = themify_shop_pageId();
1476 $link = !empty($id) ? get_permalink($id) : '';
1477 }
1478 return $link;
1479 }
1480
1481 function themify_is_login_page():bool {
1482 static $is = null;
1483 if ($is === null) {
1484 global $pagenow;
1485 $is = !empty($pagenow) && in_array($pagenow, array('wp-login.php', 'wp-register.php'), true);
1486 }
1487 return $is;
1488 }
1489
1490 function themify_is_ajax():bool {
1491 static $is = null;
1492 if ($is === null) {
1493 $is = defined('DOING_AJAX') || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
1494 }
1495 return $is;
1496 }
1497
1498 function themify_is_rest():bool {
1499 static $is = null;
1500 if ($is === null) {
1501 $is = (defined('REST_REQUEST') && REST_REQUEST) || strpos($_SERVER['REQUEST_URI'], '/wp-json/') !== false;
1502 }
1503 return $is;
1504 }
1505
1506 function themify_is_lazyloading():bool {
1507 static $is = null;
1508 if ($is === null) {
1509 $is = !themify_builder_check('setting-disable-lazy', 'performance-disable-lazy', true);
1510 if ($is === true) {
1511 global $wp_customize;
1512 $is = !is_admin() && themify_is_ajax() === false && !isset($_GET['tf-scroll']) && !isset($_GET['post_in_lightbox']) && themify_is_prefetch_request() === false && (!class_exists('Themify_Builder',false) || !Themify_Builder_Model::is_front_builder_activate()) && !( $wp_customize instanceof WP_Customize_Manager && $wp_customize->is_preview());
1513 if ($is === true) {
1514 $is = apply_filters('tf_lazy_enable', true);
1515 }
1516 } else {
1517 add_filter('wp_lazy_loading_enabled', '__return_false', 100);
1518 }
1519 }
1520 return $is;
1521 }
1522
1523 function themify_make_lazy(?string $html, bool $load = true):?string {//@todo move to class
1524 if (isset($html) && (strpos($html, ' src=') !== false || strpos($html, '<audio') !== false || strpos($html, '<video') !== false)) {
1525 $tags = array(
1526 'img' => '<img.+?>',
1527 'iframe' => '<iframe.+?>.*?<\/iframe>',
1528 'audio' => '<audio.+?>.*?<\/audio>',
1529 'video' => '<video.+?>.*?<\/video>'
1530 );
1531 $hasLazy = themify_is_lazyloading();
1532 if($hasLazy===false){
1533 unset($tags['img'],$tags['iframe']);
1534 }
1535 foreach ($tags as $k => $v) {
1536 if (strpos($html, '<' . $k) === false) {
1537 unset($tags[$k]);
1538 }
1539 }
1540 if (empty($tags)) {
1541 return $html;
1542 }
1543 if (isset($tags['img']) || isset($tags['iframe'])) {//skip noscript img/iframe
1544 preg_match_all('/<noscript>.*?<\/noscript>/is', $html, $m);
1545 if (!empty($m[0])) {
1546 $search = $replace = array();
1547 $m = !is_array($m[0]) ? array($m[0]) : $m[0];
1548 foreach ($m as $item) {
1549 $orig = $item;
1550 $item = str_replace(array("\r" . 'src=', "\n" . 'src='), ' src=', $item);
1551 if (strpos($item, ' src=') !== false && strpos($item, 'data-no-script') === false && (strpos($item, '<img') !== false || strpos($item, '<iframe') !== false)) {
1552 $search[] = $orig;
1553 $replace[] = str_replace(' src=', ' data-no-script src=', $item);
1554 }
1555 }
1556 unset($m);
1557 if (!empty($search)) {
1558 $html = str_replace($search, $replace, $html);
1559 }
1560 }
1561 }
1562
1563 $reg = '/' . implode('|', $tags) . '/is';
1564 preg_match_all($reg, $html, $matches);
1565 unset($reg);
1566 if (!empty($matches[0])) {
1567 $svgRows=4;
1568 $svgStep=round(100/$svgRows,2);
1569 $count = 0;
1570 $search = $replace = array();
1571 $matches = !is_array($matches[0]) ? array($matches[0]) : $matches[0];
1572 $tags = array_keys($tags);
1573 $extCount = count($tags) - 1;
1574 $placeHolder="data:image/svg+xml,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20width='{w}'%20height='{h}'%20viewBox=%270%200%20{w}%20{h}%27%3E%3C/svg%3E";
1575 if ($load === false) {//$load need for some modules,which should be init and than load images(e.g slider)
1576 $stopLazy = false;
1577 $useJs = $isJsLazy=true;
1578 }
1579 else{
1580 $_WITHOUTLAZY_ =2;
1581 $stopLazy = $useJs =$isJsLazy= false;
1582 }
1583 foreach ($matches as $item) {
1584 if (!empty($item) && strpos($item, 'data-lazy', 4) === false && strpos($item, 'data-tf-src', 4) === false && strpos($item, 'data-no-script', 4) === false && strpos($item, 'data:image/', 4) === false && strpos($item, 'display:none', 4) === false && strpos($item, 'application/x-mpegURL', 4) === false && strpos($item, 'display: none', 4) === false && ($count !== 0 || strpos($item, 'gravatar.com', 4) === false)) {
1585 if (strpos($item, 'data-tf-not-load', 4) === false) {
1586 for ($i = $extCount; $i > -1; --$i) {
1587 if (strpos($item, '<' . $tags[$i]) !== false) {
1588 $ext = $tags[$i];
1589 break;
1590 }
1591 }
1592 if (!isset($ext)) {
1593 continue;
1594 }
1595 $orig = $item;
1596 $item = preg_replace('/\s+/', ' ', $item);
1597 if (($ext === 'audio' || $ext === 'video') && (strpos($item, 'wcmp-player') !== false || strpos($item, 'data-lazyloading=') !== false || strpos($item, 'mejs__player') !== false || strpos($item, 'vjs-tech') !== false || strpos($item, 'product_video_gallery_for_wc') !== false)) {
1598 continue;
1599 }
1600 if ($ext !== 'audio') {
1601 preg_match('/ src=["\']([^"]+?)["\']/', $item, $image_src,0,4);
1602 }
1603 if ($ext === 'audio' || $ext === 'video' || !empty($image_src[1])) {
1604 $url = $ext === 'audio' ? true : (isset($image_src[1]) ? trim($image_src[1]) : '');
1605 unset($image_src);
1606 if ($url === '' && $ext === 'video') {
1607 $url = true;
1608 }
1609 if ($url !== '') {
1610 if ($url !== true && $url[0] === '{') {
1611 continue;
1612 }
1613 if ($ext === 'img') {//skip large images converting,otherwise can crash the site
1614 if (strpos($item, ' srcset', 4) !== false) {
1615 preg_match('/ srcset=["\']([^"]+?)["\']/', $item, $srcset,0,4);
1616 $srcset=$srcset[1]??'';
1617 }else{
1618 $srcset='';
1619 }
1620 if ($count === 0) {
1621 $srcSetSizes='';
1622 if ($srcset!=='') {
1623 preg_match('/ sizes=["\']([^"]+?)["\']/', $item, $srcSetSizes,0,4);
1624 $srcSetSizes = $srcSetSizes[1] ?? '';
1625 }
1626 Themify_Enqueue_Assets::addPreLoadMedia($url, 'preload', 'image', $srcset, $srcSetSizes, 'high'); //load the first images in the page with preload for fast LCP
1627 unset($srcSetSizes);
1628 }
1629 if(strpos($item, 'tf_large_img') === false){
1630 themify_generateWebp($url);
1631 if ($srcset!=='') {
1632 foreach ( explode(',', trim($srcset)) as $_src) {
1633 $tmpS = trim(explode(' ', trim($_src))[0]);
1634 if ($tmpS !== '' && strpos($tmpS, 'data:image') === false) {
1635 themify_generateWebp($tmpS);
1636 }
1637 }
1638 unset($tmpS,$srcset,$_src);
1639 }
1640 if (strpos($item, ' data-src', 4) !== false) {
1641 preg_match('/ data-src=["\']([^"]+?)["\']/', $item, $tmpsrc,0,4);
1642 if (!empty($tmpsrc[1])) {
1643 themify_generateWebp($tmpsrc[1]);
1644 }
1645 $tmpsrc = null;
1646 }
1647 }
1648 }
1649 if (strpos($item, 'data-src', 4) !== false || ($hasLazy === false && $ext !== 'audio' && $ext !== 'video')) {
1650 continue;
1651 }
1652 $s = '';
1653 if ($ext === 'audio' || $ext === 'video') {
1654 $s = ' data-lazy="1"';
1655 if (strpos($item, 'preload="none"', 6) === false) {
1656 $s .= ' preload="none"';
1657 $item = str_replace(array('preload="auto"', 'preload="metadata"'), '', $item);
1658 }
1659 } else {
1660 if($isJsLazy===false) {
1661 $stopLazy = $count < $_WITHOUTLAZY_; /* skip the first $_WITHOUTLAZY_ matches, assume they're above the fold */
1662 $useJs = $count > 9;
1663 ++$count;
1664 }
1665 if ($ext === 'iframe') {
1666 if ($stopLazy === false) {
1667 if (strpos($item, ' loading=', 6) === false) {
1668 $s .= ' loading="lazy"';
1669 }
1670 if ($isJsLazy === true) {
1671 $s .= ' data-lazy="1" src="about:blank" data-tf-not-load="1"';
1672 $class = 'tf_iframe_lazy';
1673 if (strpos($item, 'class=', 6) === false) {
1674 $s .= ' class="' . $class . '"';
1675 } else {
1676 $item = str_replace(' class="', ' class="' . $class . ' ', $item);
1677 }
1678 $item = strtr($item, array(' src=' => ' data-tf-src='));
1679 }
1680 }
1681 else {
1682 $s .= ' data-tf-not-load="1"';
1683 }
1684 }
1685 elseif ($ext === 'img') {
1686 $sizes = themify_get_image_size($url,false,$stopLazy===false && $useJs===true?$svgRows:0);
1687 if ($stopLazy === false) {
1688 $useJs = $isJsLazy === true || $useJs === true;
1689 if (strpos($item, ' loading=', 4) === false) {
1690 $s .= ' loading="lazy"';
1691 }
1692 if ($useJs === true) {
1693 $s .= ' data-lazy="1"';
1694 if($isJsLazy===true){
1695 $s .= ' data-tf-not-load="1"';
1696 }
1697 $class = 'tf_svg_lazy';
1698 if (strpos($item, ' class=', 4) === false) {
1699 $s .= ' class="' . $class . '"';
1700 } else {
1701 $item = str_replace(' class="', ' class="' . $class . ' ', $item);
1702 }
1703 $width=$height=0;
1704 if(!empty($sizes)){
1705 $width=$sizes['w'];
1706 $height=$sizes['h'];
1707 if(!empty($sizes['c'])){
1708 $color='';
1709 foreach($sizes['c'] as $i=>$c){
1710 if($i%$svgRows===0){
1711 $j=1;
1712 if($color!==''){
1713 $color.='),';
1714 }
1715 $color.='linear-gradient(to right';
1716 }
1717 $color.=',#'.$c;
1718 if($j!==1 ){
1719 $color.=' '.($j-1)*$svgStep.'%';
1720 }
1721 if($j!==$svgRows){
1722 $color.=' '.$j*$svgStep.'%';
1723 }
1724 ++$j;
1725 }
1726 $color='background:'.$color.')';
1727 if (strpos($item, ' style=', 4) === false) {
1728 $s .= ' style="'.$color.'"';
1729 } else {
1730 $item = str_replace(' style="', ' style="' . $color . ';', $item);
1731 }
1732 }
1733 }else{
1734 if(strpos($item, ' width=', 4)!==false){
1735 preg_match('/ width=["\']([^"]+?)["\']/', $item, $w,0,4);
1736 if(!empty($w[1])){
1737 $width=$w[1];
1738 unset($w);
1739 }
1740 }
1741 if (strpos($item, ' height=', 4) === false) {
1742 preg_match('/ height=["\']([^"]+?)["\']/', $item, $h,0,4);
1743 $height=!empty($h[1])?$h[1]:$width;
1744 unset($h);
1745 }
1746 elseif(!empty($width)){
1747 $height=$width;
1748 }
1749 }
1750 $item = strtr($item, array(' srcset=' => ' data-tf-srcset=', ' sizes=' => ' data-tf-sizes=', ' src=' => ' data-tf-src=')); //sizes need to be replaced to pass html validation
1751 $s = ' src="' . strtr($placeHolder, array('{w}'=>$width,'{h}'=>$height)) . '"' . $s;
1752 }
1753 }
1754 else {
1755 $s = ' data-tf-not-load="1"';
1756 if ($count === 1) {
1757 if (strpos($s, 'fetchpriority=') === false) {
1758 $s .= ' fetchpriority="high"';
1759 }
1760 $s .= ' loading="auto" decoding="auto"';
1761 }
1762 }
1763 if (strpos($item, ' decoding=', 4) === false) {
1764 $s .= ' decoding="async"';
1765 }
1766 if(!empty($sizes)){
1767 if (strpos($item, ' width=', 4) === false) {
1768 $s .= ' width="' . $sizes['w'] . '"';
1769 }
1770 if (strpos($item, ' height=', 4) === false) {
1771 $s .= ' height="' . $sizes['h'] . '"';
1772 }
1773 }
1774 unset($sizes);
1775 }
1776 }
1777 $item = str_replace('<' . $ext, '<' . $ext . $s, $item);
1778 if ($ext === 'audio' || $ext === 'video') {
1779 $c = 'tf_lazy tf_' . ($ext === 'video' ? 'vd' : $ext) . '_lazy tf_w tf_rel tf_box';
1780 $r = array();
1781 if (strpos($item, ' data-lazy') !== false) {
1782 $r['<' . $ext] = '<' . $ext . ' data-lazy="1"';
1783 }
1784 if ($ext === 'video') {
1785 $c .= ' tf_rel tf_overflow';
1786 themify_get_icon('fas volume-mute', 'fa');
1787 themify_get_icon('fas volume-up', 'fa');
1788 //themify_get_icon('fas undo', 'fa');
1789 //themify_get_icon('fas redo', 'fa');
1790 //themify_get_icon('far closed-captioning', 'fa');
1791 themify_get_icon('fas external-link-alt', 'fa');
1792 themify_get_icon('fas airplay', 'fa');
1793 themify_get_icon('fas expand', 'fa');
1794 themify_get_icon('fas download', 'fa');
1795 if (strpos($item, ' poster') !== false) {
1796 $r[' poster'] = ' data-poster';
1797 }
1798 if ($url && $url !== true && strpos($item, ' width') === false || strpos($item, ' height') === false) {
1799 $size = themify_get_video_size($url);
1800 if ($size !== null) {
1801 $sv = '';
1802 if ($size['w'] !== '' && strpos($item, ' width') === false) {
1803 $sv = ' width="' . $size['w'] . '"';
1804 }
1805 if ($size['h'] !== '' && strpos($item, ' height') === false) {
1806 $sv .= ' height="' . $size['h'] . '"';
1807 }
1808 if ($sv !== '') {
1809 $r[' preload'] = $sv . ' preload';
1810 }
1811 $sv = null;
1812 }
1813 }
1814 }
1815 if (strpos($item, ' autoplay') !== false) {
1816 $r[' autoplay'] = ' data-autoplay';
1817 }
1818 if (!empty($r)) {
1819 $item = strtr($item, $r);
1820 }
1821 unset($r);
1822 $item = '<div class="' . $c . '" data-playtitle="' . esc_attr__( 'Play/Pause', 'themify' ) . '" data-next="' . esc_attr__( 'Next', 'themify' ) . '" data-prev="' . esc_attr__( 'Previous', 'themify' ) . '" data-timeslider="' . esc_attr__( 'Time Slider', 'themify' ) . '">' . $item . '</div>';
1823 $c = null;
1824 }
1825 $part = $item;
1826 if ($ext === 'img' && $stopLazy === false && $useJs === true) {
1827 $part .= '<noscript>' . strtr($orig, array(' src=' => ' data-tf-not-load src=')) . '</noscript>';
1828 }
1829 $search[] = $orig;
1830 $replace[] = $part;
1831 }
1832 }
1833 } else {
1834 ++$count;
1835 }
1836 }
1837 }
1838 unset($matches, $tags, $extCount);
1839 $html = str_replace($search, $replace, $html);
1840 }
1841 }
1842 return $html;
1843 }
1844
1845 function themify_upload_dir(string $mode = 'all', bool $reinit = false) {
1846 static $dir = null;
1847 if ($dir === null || $reinit === true) {
1848 /* foolproof the paths, in case they mistakenly have trailing slash */
1849 $dir = array_map('untrailingslashit', wp_get_upload_dir());
1850 $dir['baseurl'] = themify_https_esc($dir['baseurl']);
1851 }
1852
1853 return $mode === 'all' ? $dir : $dir[$mode];
1854 }
1855
1856 function themify_generateWebp(string $url):string {
1857 if (empty($url)) {
1858 return $url;
1859 }
1860 static $is = null;
1861 if ($is === null) {
1862 $is = !themify_builder_check('setting-webp', 'performance-webp', true) ? true : (is_admin() && !themify_is_ajax());
1863 }
1864 if ($is === true) {
1865 return $url;
1866 }
1867 return themify_create_webp($url);
1868 }
1869
1870 function themify_is_prefetch_request():bool {
1871 static $is = null;
1872 if ($is === null) {
1873 if (isset($_SERVER['HTTP_PURPOSE'])) {
1874 $prev = 'HTTP_PURPOSE';
1875 } elseif (isset($_SERVER["HTTP_X_PURPOSE"])) {
1876 $prev = 'HTTP_X_PURPOSE';
1877 } elseif (isset($_SERVER['HTTP_X_MOZ'])) {
1878 $prev = 'HTTP_X_MOZ';
1879 } else {
1880 $prev = false;
1881 }
1882 if ($prev !== false) {
1883 $prev = strtolower($_SERVER[$prev]);
1884 $is = $prev === 'prefetch' || $prev === 'preview';
1885 } else {
1886 $is = false;
1887 }
1888 }
1889 return $is;
1890 }
1891
1892 function themify_is_dev_mode():bool {
1893 static $is = null;
1894 if ($is === null) {
1895 $is = themify_check('setting-dev-mode', true) || (defined('THEMIFY_DEV') && THEMIFY_DEV);
1896 $is = apply_filters('themify_dev_mode', $is);
1897 }
1898 return $is;
1899 }
1900
1901 function themify_is_concate_disabled():bool {
1902 $disable_dev_concate = themify_check('setting-dev-mode-concate', true) || (defined('THEMIFY_DEV') && THEMIFY_DEV);
1903 $dev_context = themify_check('setting-dev-mode', true)
1904 || (defined('THEMIFY_DEV') && THEMIFY_DEV)
1905 || (class_exists('Themify_Builder_Model', false) && Themify_Builder_Model::is_front_builder_activate());
1906 $is_disabled = themify_check('setting-disable-concate-css', true)
1907 || (defined('THEMIFY_DISABLE_CONCATE_CSS') && THEMIFY_DISABLE_CONCATE_CSS)
1908 || ($disable_dev_concate && $dev_context);
1909
1910 return apply_filters('themify_disable_concate_css', $is_disabled);
1911 }
1912
1913 function themify_is_amp():bool {
1914 static $is = null;
1915 if ($is !== null) {
1916 return $is;
1917 }
1918 $is = false;
1919 if ((function_exists('amp_is_request') && amp_is_request())
1920 || (function_exists('is_amp_endpoint') && is_amp_endpoint())
1921 || (function_exists('ampforwp_is_amp_endpoint') && ampforwp_is_amp_endpoint())) {
1922 $is = true;
1923 }
1924 return (bool) apply_filters('themify_is_amp', $is);
1925 }
1926
1927 function themify_disable_other_lazy() {
1928 add_filter('wp_lazy_loading_enabled', '__return_false', 100);
1929 add_filter('lazyload_is_enabled', '__return_false', 1, 100); //disable jetpack lazy load
1930 add_filter('rocket_use_native_lazyload', '__return_false', 1, 100);
1931 }
1932
1933 function themify_get_server():string {
1934 static $is = null;
1935 if ($is === null) {
1936 $is = 'apache';
1937 if (!empty($_SERVER['SERVER_SOFTWARE'])) {
1938 $is = explode('/', $_SERVER['SERVER_SOFTWARE']);
1939 $is = str_replace('microsoft-', '', strtolower($is[0]));
1940 } elseif (is_file(get_home_path() . 'web.config')) {
1941 $is = 'iis';
1942 } elseif (!is_file(Themify_Enqueue_Assets::getHtaccessFile()) || is_file('/etc/nginx/nginx.conf')) {
1943 $is = 'nginx';
1944 }
1945 }
1946 return $is;
1947 }
1948
1949 /**
1950 * Help tooltip Module
1951 */
1952 function themify_help(string $content):string {
1953 return sprintf('<span class="tf_help"><i tabindex="-1" class="icon" onclick="return false;">%s</i><span class="tf_help_content">%s</span></span>',
1954 themify_get_icon('ti-help', 'ti'),
1955 $content
1956 );
1957 }
1958
1959 /**
1960 * Search $subject for $search and replace the first occurrence of it.
1961 *
1962 * @return string
1963 */
1964 function themify_str_replace_first(string $search, string $replace,string $subject):string {
1965 return implode($replace, explode($search, $subject, 2));
1966 }
1967
1968 /**
1969 * Search $subject for $search and replace the last occurrence of it.
1970 *
1971 * @return string
1972 */
1973 function themify_str_replace_last(string $search, string $replace, string $subject):string {
1974 if (( $pos = strrpos($subject, $search) ) !== false) {
1975 $subject = substr_replace($subject, $replace, $pos, strlen($search));
1976 }
1977
1978 return $subject;
1979 }
1980
1981 /**
1982 * Gets the ID of an object (post or term) and returns that object ID in current language.
1983 *
1984 * @return int|mixed
1985 */
1986 function themify_maybe_translate_object_id(?int $id, string $type = 'page'):?int {
1987 if (!empty($id)) {
1988 if (defined('ICL_SITEPRESS_VERSION')) {
1989 $id = apply_filters('wpml_object_id', $id, $type, true);
1990 } elseif (defined('POLYLANG_VERSION') && function_exists('pll_get_post')) {
1991 $translatedpageid = pll_get_post($id);
1992 if (!empty($translatedpageid) && 'publish' === get_post_status($translatedpageid)) {
1993 $id = $translatedpageid;
1994 }
1995 }
1996 }
1997
1998 return $id;
1999 }
2000
2001 /**
2002 * Returns and caches URL to the homepage, properly filtered for multilingual setups
2003 *
2004 * @return string
2005 */
2006 function themify_home_url():string {
2007 static $url = null;
2008 if ($url === null) {
2009 $url = function_exists('pll_home_url') ? pll_home_url() : home_url();
2010 }
2011 return $url;
2012 }
2013
2014 /**
2015 * Download file from external URL and returns the file
2016 *
2017 * @param $post_id Attachments may be associated with a parent post or page.
2018 *
2019 * @return WP_Error|int ID of created attachment, or WP_Error
2020 */
2021 function tf_fetch_remote_file($url, $post_id = null, $title = '') {
2022
2023 // extract the file name and extension from the url
2024 $file_name = basename($url);
2025
2026 // get placeholder file in the upload dir with a unique, sanitized filename
2027 $upload = wp_upload_bits($file_name, 0, '');
2028 if ($upload['error'])
2029 return new WP_Error('upload_dir_error', $upload['error']);
2030
2031 // fetch the remote url and write it to the placeholder file
2032 $remote_response = wp_safe_remote_get($url, array(
2033 'timeout' => 300,
2034 'stream' => true,
2035 'filename' => $upload['file'],
2036 ));
2037
2038 $headers = wp_remote_retrieve_headers($remote_response);
2039
2040 // request failed
2041 if (!$headers) {
2042 @unlink($upload['file']);
2043 return new WP_Error('import_file_error', __('Remote server did not respond', 'themify'));
2044 }
2045
2046 $remote_response_code = wp_remote_retrieve_response_code($remote_response);
2047
2048 // make sure the fetch was successful
2049 if ($remote_response_code != '200') {
2050 @unlink($upload['file']);
2051 return new WP_Error('import_file_error', sprintf(__('Remote server returned error response %1$d %2$s', 'themify'), esc_html($remote_response_code), get_status_header_desc($remote_response_code)));
2052 }
2053
2054 $filesize = filesize($upload['file']);
2055
2056 if (0 == $filesize) {
2057 @unlink($upload['file']);
2058 return new WP_Error('import_file_error', __('Zero size file downloaded', 'themify'));
2059 }
2060
2061 $post = array(
2062 'post_title' => $title,
2063 'post_content' => '',
2064 'post_status' => 'inherit',
2065 );
2066 if ($info = wp_check_filetype($upload['file']))
2067 $post['post_mime_type'] = $info['type'];
2068 else
2069 return new WP_Error('mime_type_error', __('Invalid file type', 'themify'));
2070
2071 $attach_id = wp_insert_attachment($post, $upload['file'], $post_id);
2072 wp_update_attachment_metadata($attach_id, wp_generate_attachment_metadata($attach_id, $upload['file']));
2073
2074 return $attach_id;
2075 }
2076
2077 /**
2078 * Mimics get_template_part but loads a template from THEMIFY_DIR
2079 *
2080 * @since 5.5.3
2081 */
2082 function themify_get_template(string $slug, string $name = '', array $args = array()) {
2083 $base_dir = THEMIFY_DIR;
2084 $templates = array();
2085 $located = '';
2086 if ($name !== null && '' !== $name) {
2087 $templates[] = "{$base_dir}/{$slug}-{$name}.php";
2088 }
2089
2090 $templates[] = "{$base_dir}/{$slug}.php";
2091
2092 foreach ($templates as $template) {
2093 if (is_file($template)) {
2094 $located = $template;
2095 break;
2096 }
2097 }
2098
2099 if ($located !== '') {
2100 load_template($located, false, $args);
2101 }
2102 }
2103
2104 /* Map assigned terms to filter taxonomy term IDs (matches by slug across taxonomies). */
2105 if ( ! function_exists( 'themify_map_term_to_filter_taxonomy' ) ) :
2106 function themify_map_term_to_filter_taxonomy( $term, $filter_taxonomy ) {
2107 if ( ! $term || is_wp_error( $term ) || ! taxonomy_exists( $filter_taxonomy ) ) {
2108 return false;
2109 }
2110 if ( $term->taxonomy === $filter_taxonomy ) {
2111 return $term;
2112 }
2113 $slug = $term->slug;
2114 while ( $slug !== '' ) {
2115 $match = get_term_by( 'slug', $slug, $filter_taxonomy );
2116 if ( $match && ! is_wp_error( $match ) ) {
2117 return $match;
2118 }
2119 $pos = strrpos( $slug, '-' );
2120 if ( false === $pos ) {
2121 break;
2122 }
2123 $slug = substr( $slug, 0, $pos );
2124 }
2125 return false;
2126 }
2127 endif;
2128
2129 if ( ! function_exists( 'themify_get_cross_taxonomy_term_ids' ) ) :
2130 function themify_get_cross_taxonomy_term_ids( $filter_term, $object_taxonomy ): array {
2131 if ( ! $filter_term || is_wp_error( $filter_term ) || ! taxonomy_exists( $object_taxonomy ) ) {
2132 return array();
2133 }
2134 if ( $filter_term->taxonomy === $object_taxonomy ) {
2135 return array( (int) $filter_term->term_id );
2136 }
2137 $ids = array();
2138 foreach ( array_unique( array( $filter_term->slug, $filter_term->slug . '-product' ) ) as $slug ) {
2139 $term = get_term_by( 'slug', $slug, $object_taxonomy );
2140 if ( ! $term || is_wp_error( $term ) ) {
2141 continue;
2142 }
2143 $ids[] = (int) $term->term_id;
2144 $children = get_term_children( $term->term_id, $object_taxonomy );
2145 if ( ! is_wp_error( $children ) && ! empty( $children ) ) {
2146 $ids = array_merge( $ids, array_map( 'intval', $children ) );
2147 }
2148 }
2149 return array_values( array_unique( $ids ) );
2150 }
2151 endif;
2152
2153 if ( ! function_exists( 'themify_normalize_filter_taxonomy' ) ) :
2154 function themify_normalize_filter_taxonomy( $taxonomy, $post_type, $fallback = '' ) {
2155 $taxonomy = sanitize_key( (string) $taxonomy );
2156 $fallback = sanitize_key( (string) $fallback );
2157 if ( is_array( $post_type ) ) {
2158 $post_type = reset( $post_type );
2159 }
2160 $post_type = sanitize_key( (string) $post_type );
2161 if ( $post_type === 'product' ) {
2162 if ( $taxonomy === 'category' || ( $taxonomy === '' && ( $fallback === 'category' || $fallback === '' ) ) ) {
2163 return 'product_cat';
2164 }
2165 if ( $taxonomy === 'tag' || ( $taxonomy === '' && $fallback === 'tag' ) ) {
2166 return 'product_tag';
2167 }
2168 if ( $taxonomy === '' ) {
2169 return $fallback !== '' ? $fallback : 'product_cat';
2170 }
2171 return $taxonomy;
2172 }
2173 if ( $taxonomy === '' ) {
2174 return $fallback !== '' ? $fallback : 'category';
2175 }
2176 return $taxonomy;
2177 }
2178 endif;
2179
2180 if ( ! function_exists( 'themify_get_post_filter_classes' ) ) :
2181 function themify_get_post_filter_classes( $object_id, $filter_taxonomy ): array {
2182 $classes = array();
2183 if ( ! taxonomy_exists( $filter_taxonomy ) ) {
2184 return $classes;
2185 }
2186 $post_type = get_post_type( $object_id );
2187 if ( ! $post_type ) {
2188 return $classes;
2189 }
2190 $added = array();
2191 foreach ( get_object_taxonomies( $post_type, 'names' ) as $taxonomy ) {
2192 $terms = wp_get_object_terms( $object_id, $taxonomy );
2193 if ( is_wp_error( $terms ) || empty( $terms ) ) {
2194 continue;
2195 }
2196 foreach ( $terms as $term ) {
2197 $filter_term = themify_map_term_to_filter_taxonomy( $term, $filter_taxonomy );
2198 if ( $filter_term && ! isset( $added[ $filter_term->term_id ] ) ) {
2199 $classes[] = 'cat-' . $filter_term->term_id;
2200 $added[ $filter_term->term_id ] = true;
2201 }
2202 }
2203 }
2204 return $classes;
2205 }
2206 endif;
2207
2208 if ( ! function_exists( 'themify_build_filter_tax_query' ) ) :
2209 function themify_build_filter_tax_query( $filter_taxonomy, $term_id, $post_type ): array {
2210 $term_id = (int) $term_id;
2211 if ( $term_id <= 0 || ! taxonomy_exists( $filter_taxonomy ) ) {
2212 return array();
2213 }
2214 if ( is_array( $post_type ) ) {
2215 $post_type = reset( $post_type );
2216 }
2217 $filter_term = get_term( $term_id, $filter_taxonomy );
2218 if ( ! $filter_term || is_wp_error( $filter_term ) ) {
2219 return array(
2220 array(
2221 'taxonomy' => $filter_taxonomy,
2222 'field' => 'term_id',
2223 'terms' => $term_id,
2224 'operator' => 'IN',
2225 ),
2226 );
2227 }
2228 $queries = array();
2229 foreach ( get_object_taxonomies( $post_type, 'names' ) as $taxonomy ) {
2230 if ( $taxonomy === $filter_taxonomy ) {
2231 $queries[] = array(
2232 'taxonomy' => $taxonomy,
2233 'field' => 'term_id',
2234 'terms' => (int) $filter_term->term_id,
2235 'operator' => 'IN',
2236 );
2237 continue;
2238 }
2239 $term_ids = themify_get_cross_taxonomy_term_ids( $filter_term, $taxonomy );
2240 if ( ! empty( $term_ids ) ) {
2241 $queries[] = array(
2242 'taxonomy' => $taxonomy,
2243 'field' => 'term_id',
2244 'terms' => $term_ids,
2245 'operator' => 'IN',
2246 );
2247 }
2248 }
2249 if ( empty( $queries ) ) {
2250 return array(
2251 array(
2252 'taxonomy' => $filter_taxonomy,
2253 'field' => 'term_id',
2254 'terms' => $term_id,
2255 'operator' => 'IN',
2256 ),
2257 );
2258 }
2259 if ( count( $queries ) === 1 ) {
2260 return $queries;
2261 }
2262 return array_merge( array( 'relation' => 'OR' ), $queries );
2263 }
2264 endif;
2265
2266 /* Add category id class in post loop for Masonry filter */
2267 if ( ! function_exists( 'themify_post_filter_class' ) ) :
2268 function themify_post_filter_class(array $classes, $class, $post_id):array {
2269 $filter_tax = get_query_var( 'tf_query_tax', 'category' );
2270 foreach ( themify_get_post_filter_classes( $post_id, $filter_tax ) as $cat_class ) {
2271 $classes[] = ' ' . $cat_class;
2272 }
2273 if ( true === get_query_var( 'tf_ajax_filter', false ) ) {
2274 if ( isset( $_POST['action'], $_POST['tax'] ) && $_POST['action'] === 'themify_ajax_load_more' ) {
2275 $classes[] = 'ajax-cat-' . (int) $_POST['tax'];
2276 } else {
2277 $classes[] = 'initial-cat';
2278 }
2279 }
2280 return $classes;
2281 }
2282 endif;
2283
2284 function themify_custom_except($excerpt) {
2285 if (has_excerpt()) {
2286 $excerpt = wp_trim_words(get_the_excerpt(), apply_filters('excerpt_length', 55));
2287 }
2288 return $excerpt;
2289 }
2290
2291 function themify_set_headers(array $headers) {
2292 if (!headers_sent()) {
2293 $list = headers_list();
2294 foreach ($list as $h) {
2295 $head = strtoupper(trim(explode(':', $h)[0]));
2296 if (isset($headers[$head])) {
2297 $vals = trim(trim(str_ireplace($head, '', $h)), ':');
2298 if ($head === 'CONTENT-SECURITY-POLICY' || is_array($headers[$head])) {
2299 $vals = explode(';', $vals);
2300 $policy = $headers[$head];
2301 $hasChange = false;
2302 foreach ($vals as $i => $c) {
2303 $c = trim($c);
2304 if ($c !== '') {
2305 $c = preg_replace('!\s+!', ' ', $c);
2306 $values = explode(' ', $c);
2307 if (isset($policy[$values[0]])) {
2308 $none = array_search('none', $values, true);
2309 if ($none !== false) {
2310 unset($values[$none]);
2311 }
2312 $values = array_merge($values, explode(' ', $policy[$values[0]]));
2313 $vals[$i] = implode(' ', array_keys(array_flip($values)));
2314 $hasChange = true;
2315 }
2316 } else {
2317 unset($vals[$i]);
2318 }
2319 }
2320 if ($hasChange === true) {
2321 header($head . ':' . implode(';', $vals));
2322 }
2323 } elseif ($vals !== $headers[$head]) {
2324 header($head . ':' . $headers[$head]);
2325 }
2326 unset($headers[$head]);
2327 if (empty($headers)) {
2328 break;
2329 }
2330 }
2331 }
2332 }
2333 }
2334
2335 function themify_get_lottie(array $arr, string $sel = '') {
2336
2337 if (!empty($arr['path']) && !empty($arr['seg'])) {
2338 $lottie = array(
2339 'path' => $arr['path'],
2340 'seg' => $arr['seg']
2341 );
2342 if (!empty($arr['st'])) {
2343 $lottie['st'] = $arr['st'];
2344 }
2345 if (isset($arr['sp']) && $arr['sp'] != 1) {
2346 $lottie['sp'] = $arr['sp'];
2347 }
2348 if (!empty($arr['dir'])) {
2349 $lottie['dir'] = $arr['dir'];
2350 }
2351 if (!empty($arr['fid'])) {
2352 $lottie['fid'] = $arr['fid'];
2353 }
2354 if (isset($arr['r']) && $arr['r'] !== 'svg') {
2355 $lottie['r'] = $arr['r'];
2356 }
2357 if (isset($arr['count']) && ($arr['count'] > 1 || $arr['count'] == -1)) {
2358 $lottie['count'] = $arr['count'];
2359 }
2360 if ($sel !== '') {
2361 $lottie['sel'] = $sel;
2362 }
2363 if (!isset($arr['lp'])) {
2364 $lottie = array('actions' => $lottie, 'loop' => 1);
2365 }
2366 return sprintf('<tf-lottie data-lazy="1" class="tf_lazy"><template>%s</template></tf-lottie>', json_encode($lottie));
2367 }
2368 return '';
2369 }
2370
2371 /**
2372 * Load/Check minified file is exist
2373 */
2374 function themify_enque(string $url, bool $check = false, bool $cdn = true) {//backward
2375 return $check===true?false:$url;
2376 }
2377
2378 function themify_is_minify_enabled():bool{//backward
2379 return false;
2380 }
2381
2382 function themify_whitelist_tag( string $input, string $default = 'div' ) : string {
2383 if ( ! in_array( $input, [ 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nav' ], true ) ) {
2384 return $default;
2385 } else {
2386 return $input;
2387 }
2388 }
2389
2390 /**
2391 * Uses wp_safe_remote_get and retries once with sslverify=false only if the first request fails.
2392 *
2393 * @since 7.0.0
2394 */
2395 function themify_safe_remote_get_with_ssl_fallback( string $url, array $args = array() ) {
2396 $defaults = array(
2397 'timeout' => 5,
2398 'redirection' => 3,
2399 'sslverify' => true,
2400 );
2401 $args = wp_parse_args( $args, $defaults );
2402
2403 $resp = wp_safe_remote_get( $url, $args );
2404 if ( is_wp_error( $resp ) ) {
2405 // Backward-compatibility: some hosts may still have SSL chain issues.
2406 $args['sslverify'] = false;
2407 $resp = wp_safe_remote_get( $url, $args );
2408 }
2409 return $resp;
2410 }
2411
2412
2413 if ( ! function_exists( 'themify_safe_gzbase64_decode' ) ) :
2414 /**
2415 * Safely base64-decode and gzdecode a payload with size limits.
2416 *
2417 * @param string $payload Base64 string.
2418 * @param int $max_decoded_bytes Maximum decoded bytes allowed.
2419 * @param int $max_encoded_bytes Maximum base64 string length allowed.
2420 *
2421 * @return string|false Decoded string on success, false on failure.
2422 */
2423 function themify_safe_gzbase64_decode( $payload, $max_decoded_bytes = 10485760, $max_encoded_bytes = 15728640 ) {
2424 if ( ! is_string( $payload ) || $payload === '' ) {
2425 return false;
2426 }
2427 $len = strlen( $payload );
2428 if ( $len > $max_encoded_bytes ) {
2429 return false;
2430 }
2431
2432 $decoded = base64_decode( $payload, true );
2433 if ( $decoded === false ) {
2434 return false;
2435 }
2436 if ( strlen( $decoded ) > $max_decoded_bytes ) {
2437 return false;
2438 }
2439
2440 $out = function_exists( 'gzdecode' ) ? @gzdecode( $decoded ) : false;
2441 if ( $out === false ) {
2442 return false;
2443 }
2444 return $out;
2445 }
2446 endif;
2447
2448