$args * @param bool $echo */ function aksrt_icon_card(array $args = [], bool $echo = false): string { $defaults = [ 'type' => '', 'class' => 'box-body nopw-sm', 'icon' => '', 'icon_size' => 0, 'customize_icon' => '', 'icon_image' => '', 'link' => [ 'url' => '', 'target' => '', ], 'icon_radius' => false, 'icon_color' => '', 'icon_custom_color' => '', 'icon_class' => '', 'title' => '', 'desc' => '', ]; $args = wp_parse_args($args, $defaults); $icon_fa = sanitize_text_field((string) $args['icon']); $icon_img = esc_url_raw(trim((string) ($args['icon_image'] !== '' ? $args['icon_image'] : $args['customize_icon']))); // customize_icon may be raw HTML (img) from older data. $custom_html = ''; if ($icon_img === '' && is_string($args['customize_icon']) && str_contains((string) $args['customize_icon'], '<')) { $custom_html = wp_kses((string) $args['customize_icon'], [ 'i' => ['class' => true, 'aria-hidden' => true], 'img' => ['src' => true, 'alt' => true, 'class' => true, 'width' => true, 'height' => true, 'loading' => true], 'span'=> ['class' => true], ]); } if ($icon_fa === '' && $icon_img === '' && $custom_html === '') { return ''; } $link = $args['link']; if (is_string($link)) { $link = ['url' => $link, 'target' => '']; } if (!is_array($link)) { $link = ['url' => '', 'target' => '']; } $url = esc_url(trim((string) ($link['url'] ?? ''))); $target = sanitize_text_field((string) ($link['target'] ?? '')); $tgt = $target !== '' ? ' target="' . esc_attr($target) . '" rel="noopener noreferrer"' : ''; $icon_color = sanitize_html_class((string) $args['icon_color']); $icon_class_extra = sanitize_html_class((string) $args['icon_class']); $radius = !empty($args['icon_radius']); $icon_class = $radius ? 'card-icon toggle-radius fa-3x ' . ($icon_class_extra !== '' ? $icon_class_extra : $icon_color) : 'card-icon fa-4x ' . $icon_color; if ($icon_class_extra !== '' && !$radius) { $icon_class .= ' ' . $icon_class_extra; } $style = ''; $custom_color = sanitize_hex_color((string) $args['icon_custom_color']); if (!$radius && $custom_color) { $style = ' style="color:' . esc_attr($custom_color) . ';"'; } if ($custom_html !== '') { $icon_inner = $custom_html; } elseif ($icon_img !== '') { $icon_inner = ''; } else { if ($icon_fa !== '' && !str_contains($icon_fa, 'fa-') && !str_contains($icon_fa, ' ')) { $icon_fa = 'fa fa-' . $icon_fa; } $icon_inner = ''; } $icon = '' . $icon_inner . ''; if ($url !== '') { $icon = '' . $icon . ''; } $icon_size = (int) $args['icon_size']; if ($icon_size > 0) { $icon = '' . $icon . ''; } $title = trim((string) $args['title']); $desc = trim((string) $args['desc']); if ($url !== '' && $title !== '') { $title_html = '' . esc_html($title) . ''; } else { $title_html = $title !== '' ? esc_html($title) : ''; } $body = ''; if ($title_html !== '') { $body .= '
' . $title_html . '
'; } if ($desc !== '') { $body .= '
' . esc_html($desc) . '
'; } $class = trim((string) $args['class'] . ' ' . (string) $args['type']); $html = '
'; $html .= $icon; $html .= $body !== '' ? '
' . $body . '
' : ''; $html .= '
'; if ($echo) { echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return ''; } return $html; } endif; if (!function_exists('aksrt_icon_cover_card')) : /** * Horizontal icon cover card (icon left, title/desc right). * * @param array $args * @param bool $echo */ function aksrt_icon_cover_card(array $args = [], bool $echo = false): string { $defaults = [ 'class' => 'zib-widget', 'icon' => '', 'icon_size' => 25, 'customize_icon' => '', 'custom_icon' => '', 'icon_image' => '', 'link' => [ 'url' => '', 'target' => '', ], // Flat keys used by header_components_icon_cards admin schema. 'url' => '', 'target' => '', 'icon_radius' => false, 'icon_color' => '', 'icon_custom_color' => '', 'icon_class' => '', 'title' => '', 'desc' => '', ]; $args = wp_parse_args($args, $defaults); // Normalize link: accept nested link[] or flat url/target (SPA schema). $link = $args['link']; if (is_string($link)) { $link = ['url' => $link, 'target' => (string) $args['target']]; } if (!is_array($link)) { $link = ['url' => '', 'target' => '']; } if (trim((string) ($link['url'] ?? '')) === '' && trim((string) $args['url']) !== '') { $link['url'] = (string) $args['url']; } if (trim((string) ($link['target'] ?? '')) === '' && trim((string) $args['target']) !== '') { $link['target'] = (string) $args['target']; } $icon_fa = trim((string) $args['icon']); // custom_icon (SPA) + customize_icon (zibll) + icon_image URL. $custom_src = (string) ($args['custom_icon'] !== '' ? $args['custom_icon'] : $args['customize_icon']); $icon_img = esc_url_raw(trim((string) ($args['icon_image'] !== '' ? $args['icon_image'] : ''))); $custom_html = ''; if ($icon_img === '' && $custom_src !== '' && str_contains($custom_src, '<')) { $custom_html = wp_kses($custom_src, [ 'i' => ['class' => true, 'aria-hidden' => true], 'img' => ['src' => true, 'alt' => true, 'class' => true, 'loading' => true, 'width' => true, 'height' => true], 'span' => ['class' => true], 'svg' => ['class' => true, 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'fill' => true, 'xmlns' => true, 'aria-hidden' => true], 'path' => ['d' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true], 'use' => ['href' => true, 'xlink:href' => true], ]); } elseif ($icon_img === '' && $custom_src !== '' && filter_var($custom_src, FILTER_VALIDATE_URL)) { $icon_img = esc_url_raw($custom_src); } if ($icon_fa === '' && $icon_img === '' && $custom_html === '') { return ''; } $url = esc_url(trim((string) ($link['url'] ?? ''))); $target = sanitize_text_field((string) ($link['target'] ?? '')); // zibll only adds target= when non-empty; keep same for 1:1. $tgt = ($target !== '' && $target !== '_self') ? ' target="' . esc_attr($target) . '"' : ''; // zibll: class = "icon-cover-icon badg cir" + optional icon_class (may include radius4 / c-* / jb-*). // Do NOT sanitize_html_class on the whole string — it collapses multi-token classes. $icon_class_extra = trim(preg_replace('/\s+/', ' ', (string) $args['icon_class'])); $icon_class = 'icon-cover-icon badg cir'; if ($icon_class_extra !== '') { $tokens = preg_split('/\s+/', $icon_class_extra) ?: []; $safe = []; foreach ($tokens as $tok) { $tok = preg_replace('/[^a-zA-Z0-9_-]/', '', $tok); if ($tok !== '') { $safe[] = $tok; } } if ($safe) { $icon_class .= ' ' . implode(' ', $safe); } } if (!empty($args['icon_radius']) && !str_contains($icon_class, 'radius4')) { $icon_class .= ' radius4'; } $icon_color = trim((string) $args['icon_color']); if ($icon_color !== '' && !str_contains($icon_class, $icon_color)) { $icon_class .= ' ' . preg_replace('/[^a-zA-Z0-9_-]/', '', $icon_color); } $icon_size = max(14, min(64, (int) $args['icon_size'])); // zibll always sets font-size on the icon badge. $style = 'font-size: ' . $icon_size . 'px;'; $custom_color = sanitize_hex_color((string) $args['icon_custom_color']); if ($custom_color) { $style .= 'color:' . $custom_color . ';'; } if ($custom_html !== '') { $icon_inner = $custom_html; } elseif ($icon_img !== '') { $icon_inner = ''; } else { // zibll: zib_get_cfs_icon($args['icon'], 'em09') — keep FA class + em09. if ($icon_fa !== '' && !str_contains($icon_fa, 'fa-') && !str_contains($icon_fa, ' ') && !str_starts_with($icon_fa, 'zibsvg-')) { $icon_fa = 'fa fa-' . $icon_fa; } if (str_contains($icon_fa, '<')) { $icon_inner = wp_kses($icon_fa, [ 'i' => ['class' => true, 'aria-hidden' => true], 'svg' => ['class' => true, 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'fill' => true, 'xmlns' => true, 'aria-hidden' => true], 'path' => ['d' => true, 'fill' => true], 'use' => ['href' => true, 'xlink:href' => true], ]); } else { $icon_inner = ''; } } $icon = '
' . $icon_inner . '
'; // zibll allows HTML in title/desc (admin stores badges etc.). $title = (string) $args['title']; $desc = (string) $args['desc']; $allow = [ 'badge' => ['class' => true], 'span' => ['class' => true], 'i' => ['class' => true, 'aria-hidden' => true], 'em' => ['class' => true], 'strong' => ['class' => true], 'b' => [], 'small' => ['class' => true], 'br' => [], ]; $text = ''; if (trim(wp_strip_all_tags($title)) !== '' || str_contains($title, '<')) { $text .= '
' . wp_kses($title, $allow) . '
'; } if (trim(wp_strip_all_tags($desc)) !== '' || str_contains($desc, '<')) { $text .= '
' . wp_kses($desc, $allow) . '
'; } $html = ''; if ($url !== '') { // zibll: $html .= ''; } $html .= '
'; $html .= $icon; $html .= $text !== '' ? '
' . $text . '
' : ''; $html .= '
'; if ($url !== '') { $html .= '
'; } if ($echo) { echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return ''; } return $html; } endif; if (!function_exists('aksrt_obs_animate_attrs')) : /** * Build class + attribute string for scroll-in animation (maps to data-aksrt-animate). * * @param string $animation fade|slideup|slidedown|slideright|slideleft|zoomin|zoomout|'' * @param bool $repeat * @param float|int $delay_s * @return array{class:string,attr:string} */ function aksrt_obs_animate_attrs(string $animation, bool $repeat = false, $delay_s = 0): array { $animation = sanitize_key($animation); $map = [ 'fade' => 'fadeIn', 'slideup' => 'fadeInUp', 'slidedown' => 'fadeInDown', 'slideright' => 'fadeInRight', 'slideleft' => 'fadeInLeft', 'zoomin' => 'zoomIn', 'zoomout' => 'fadeIn', 'fadein' => 'fadeIn', 'fadeinup' => 'fadeInUp', ]; if ($animation === '' || !isset($map[$animation]) && !in_array($animation, $map, true)) { if (!isset($map[$animation])) { return ['class' => '', 'attr' => '']; } } $aksrt = $map[$animation] ?? $animation; $delay_ms = (int) round(((float) $delay_s) * 1000); $class = ' obs-animate ani-' . $animation; $attr = ' data-aksrt-animate="' . esc_attr($aksrt) . '"'; if ($delay_ms > 0) { $attr .= ' data-aksrt-animate-delay="' . esc_attr((string) $delay_ms) . '"'; } if ($repeat) { $attr .= ' data-animation-repeat="true"'; } return ['class' => $class, 'attr' => $attr]; } endif;