*/ function aksrt_widget_common_defaults(): array { return [ 'subtitle' => '', 'title_style' => '', 'title_highlight' => '', 'title_highlight_color'=> 'focus-color', 'title_link' => ['url' => '', 'text' => '', 'target' => ''], 'title_link2' => ['url' => '', 'text' => '', 'target' => ''], 'title_link3' => ['url' => '', 'text' => '', 'target' => ''], 'title_link_style' => ['color_1' => 'jb-blue', 'color_2' => 'jb-yellow', 'color_3' => 'jb-green'], 'sidebar_affix' => false, 'show_type' => 'all', 'show_id_type' => '', 'show_ids' => '', 'animation_in' => '', 'animation_repeat' => false, 'layout_pin_pc' => ['top' => '', 'bottom' => ''], 'layout_pin_m' => ['top' => '', 'bottom' => ''], 'layout_bg' => ['img_white' => [], 'img_dark' => []], ]; } } if (!function_exists('aksrt_widget_normalize_link')) { /** * Normalize a title_linkN array {url,text,target}. * * @param mixed $raw * @return array{url:string,text:string,target:string} */ function aksrt_widget_normalize_link($raw): array { $raw = is_array($raw) ? $raw : []; return [ 'url' => esc_url_raw(trim((string) ($raw['url'] ?? ''))), 'text' => sanitize_text_field((string) ($raw['text'] ?? '')), 'target' => ($raw['target'] ?? '') === '_blank' ? '_blank' : '', ]; } } if (!function_exists('aksrt_widget_normalize_common')) { /** * Normalize common widget instance fields, filling defaults for legacy instances. * * Accepts both the new structured shape (title_link as array) and legacy * flat shapes (title_link as URL string, in_affix bool) emitted by older * widget versions, normalizing them onto the canonical shared schema. * * @param array $instance * @return array */ function aksrt_widget_normalize_common(array $instance): array { $defaults = aksrt_widget_common_defaults(); $result = array_merge($defaults, $instance); $result['subtitle'] = sanitize_text_field($result['subtitle'] ?? ''); $result['title_style'] = in_array($result['title_style'] ?? '', ['', 'big'], true) ? $result['title_style'] : ''; $result['title_highlight'] = sanitize_text_field($result['title_highlight'] ?? ''); $result['title_highlight_color'] = sanitize_html_class((string) ($result['title_highlight_color'] ?? 'focus-color')); if ($result['title_highlight_color'] === '') { $result['title_highlight_color'] = 'focus-color'; } // Legacy flat title_link (URL string) → structured first link. if (is_string($result['title_link'] ?? null)) { $result['title_link'] = ['url' => esc_url_raw(trim($result['title_link'])), 'text' => '', 'target' => '']; } $result['title_link'] = aksrt_widget_normalize_link($result['title_link'] ?? []); $result['title_link2'] = aksrt_widget_normalize_link($result['title_link2'] ?? []); $result['title_link3'] = aksrt_widget_normalize_link($result['title_link3'] ?? []); $tl_style = is_array($result['title_link_style'] ?? null) ? $result['title_link_style'] : []; $result['title_link_style'] = [ 'color_1' => sanitize_html_class((string) ($tl_style['color_1'] ?? 'jb-blue')) ?: 'jb-blue', 'color_2' => sanitize_html_class((string) ($tl_style['color_2'] ?? 'jb-yellow')) ?: 'jb-yellow', 'color_3' => sanitize_html_class((string) ($tl_style['color_3'] ?? 'jb-green')) ?: 'jb-green', ]; // show_type: accept both AKSRT legacy (pc/mobile) and Zibll (only_pc/only_sm) tokens. $show_type = (string) ($result['show_type'] ?? 'all'); if ($show_type === 'only_pc' || $show_type === 'pc') { $show_type = 'only_pc'; } elseif ($show_type === 'only_sm' || $show_type === 'mobile') { $show_type = 'only_sm'; } else { $show_type = 'all'; } $result['show_type'] = $show_type; // show_id_type: Zibll (show/hide) ↔ AKSRT legacy (include/exclude). $id_type = (string) ($result['show_id_type'] ?? ''); if ($id_type === 'include') { $id_type = 'show'; } elseif ($id_type === 'exclude') { $id_type = 'hide'; } elseif (!in_array($id_type, ['', 'show', 'hide'], true)) { $id_type = ''; } $result['show_id_type'] = $id_type; $result['show_ids'] = sanitize_text_field($result['show_ids'] ?? ''); $result['sidebar_affix'] = !empty($result['sidebar_affix']) || !empty($result['in_affix'] ?? false); $result['animation_in'] = sanitize_key($result['animation_in'] ?? ''); $result['animation_repeat'] = !empty($result['animation_repeat']); foreach (['layout_pin_pc', 'layout_pin_m'] as $pin_key) { $pin = is_array($result[$pin_key] ?? null) ? $result[$pin_key] : []; $result[$pin_key] = [ 'top' => preg_match('/^-?\d+$/', (string) ($pin['top'] ?? '')) ? (string) (int) $pin['top'] : '', 'bottom' => preg_match('/^-?\d+$/', (string) ($pin['bottom'] ?? '')) ? (string) (int) $pin['bottom'] : '', ]; } $bg = is_array($result['layout_bg'] ?? null) ? $result['layout_bg'] : []; $result['layout_bg'] = [ 'img_white' => aksrt_widget_normalize_bg($bg['img_white'] ?? []), 'img_dark' => aksrt_widget_normalize_bg($bg['img_dark'] ?? []), ]; return $result; } } if (!function_exists('aksrt_widget_normalize_bg')) { /** * Normalize a layout_bg sub-block (background CSS props). * * @param mixed $raw * @return array */ function aksrt_widget_normalize_bg($raw): array { $raw = is_array($raw) ? $raw : []; $out = []; foreach (['background-color', 'background-image', 'background-position', 'background-repeat', 'background-attachment', 'background-size', 'background-origin'] as $k) { $v = trim((string) ($raw[$k] ?? '')); if ($k === 'background-image' && $v !== '') { $v = esc_url_raw($v); } elseif ($k === 'background-color' && $v !== '') { // Allow hex / rgb / rgba / named; strip tags defensively. $v = sanitize_text_field($v); } else { $v = sanitize_text_field($v); } if ($v !== '') { $out[$k] = $v; } } return $out; } } if (!function_exists('aksrt_widget_animation_options')) { /** * Zibll-aligned module entrance animation options. * * @return array */ function aksrt_widget_animation_options(): array { return [ '' => __('无动画', 'aksrt'), 'fade' => __('淡入', 'aksrt'), 'slideup' => __('从下往上滑出', 'aksrt'), 'slidedown' => __('从上往下滑出', 'aksrt'), 'slideright' => __('从左往右滑出', 'aksrt'), 'slideleft' => __('从右往左滑出', 'aksrt'), 'zoomin' => __('由小变大', 'aksrt'), 'zoomout' => __('由大变小', 'aksrt'), ]; } } if (!function_exists('aksrt_widget_highlight_color_options')) { /** * Highlight color palette (Zibll zib_palette parity, theme classes). * * @return array */ function aksrt_widget_highlight_color_options(): array { return [ 'focus-color' => __('主题色', 'aksrt'), 'jb-blue' => __('渐变蓝', 'aksrt'), 'jb-green' => __('渐变绿', 'aksrt'), 'jb-yellow' => __('渐变黄', 'aksrt'), 'jb-orange' => __('渐变橙', 'aksrt'), 'jb-red' => __('渐变红', 'aksrt'), 'jb-purple' => __('渐变紫', 'aksrt'), 'jb-pink' => __('渐变粉', 'aksrt'), 'jb-cyan' => __('渐变青', 'aksrt'), 'c-blue' => __('蓝色', 'aksrt'), 'c-green' => __('绿色', 'aksrt'), 'c-red' => __('红色', 'aksrt'), 'c-yellow' => __('黄色', 'aksrt'), 'c-purple' => __('紫色', 'aksrt'), ]; } } if (!function_exists('aksrt_widget_render_link_field')) { /** * Render a single title-link sub-field (url/text/target) for the admin form. * * @param string $base Field base name, e.g. 'title_link'. * @param array $link Normalized link {url,text,target}. * @param string $label Field label. */ function aksrt_widget_render_link_field(string $base, array $link, string $label): void { ?>

$instance Current widget instance. */ function aksrt_widget_render_common_form(array $instance): void { $instance = aksrt_widget_normalize_common($instance); $pin_pc = $instance['layout_pin_pc']; $pin_m = $instance['layout_pin_m']; $bg_w = $instance['layout_bg']['img_white']; $bg_d = $instance['layout_bg']['img_dark']; ?>

$new_instance * @param array $old_instance * @return array */ function aksrt_widget_update_common(array $new_instance, array $old_instance): array { $merged = array_merge($old_instance, $new_instance); return aksrt_widget_normalize_common($merged); } } if (!function_exists('aksrt_widget_render_common_before')) { /** * Build common wrapper classes/attrs for a widget instance. * * Zibll CSF_Widget::wrap_attributes() parity: show-type class, sidebar * affix data-affix, entrance animation class, layout padding/bg CSS vars. * Call this at the start of widget() before rendering content. * * @param array $args Sidebar args. * @param array $instance Widget instance. * @return array CSS class names (Zibll show_class style). */ function aksrt_widget_get_common_wrapper_classes(array $args, array $instance): array { $instance = aksrt_widget_normalize_common($instance); $classes = []; // Show type: PC or mobile (Zibll hidden-xs / visible-xs-block). if ($instance['show_type'] === 'only_pc') { $classes[] = 'hidden-xs'; } elseif ($instance['show_type'] === 'only_sm') { $classes[] = 'visible-xs-block'; } // Sidebar affix if ($instance['sidebar_affix']) { $classes[] = 'is-affix'; } // Animation (Zibll obs-animate ani-*). if ($instance['animation_in'] !== '') { $classes[] = 'obs-animate'; $classes[] = 'ani-' . $instance['animation_in']; if ($instance['animation_repeat']) { $classes[] = 'obs-animate-repeat'; } } return $classes; } } if (!function_exists('aksrt_widget_common_wrapper_attr')) { /** * Build the full wrapper attribute string (class + style + data-affix) for * the Zibll .zib-widget-wrap parity layer. * * Combine with a widget-specific class via a leading space, or use as-is. * * @param array $instance Widget instance. * @param string $extra_class Additional class (widget-specific). * @return string Escaped attribute string beginning with a leading space, e.g. ' class="…" style="…" data-affix="true"'. */ function aksrt_widget_common_wrapper_attr(array $instance, string $extra_class = ''): string { $instance = aksrt_widget_normalize_common($instance); $classes = ['zib-widget-wrap']; foreach (aksrt_widget_get_common_wrapper_classes([], $instance) as $c) { $classes[] = $c; } $extra_class = trim($extra_class); if ($extra_class !== '') { $classes[] = $extra_class; } $styles = []; if ($instance['layout_pin_pc']['top'] !== '') { $styles[] = '--pt-pc:' . (int) $instance['layout_pin_pc']['top'] . 'px;'; } if ($instance['layout_pin_pc']['bottom'] !== '') { $styles[] = '--pb-pc:' . (int) $instance['layout_pin_pc']['bottom'] . 'px;'; } if ($instance['layout_pin_m']['top'] !== '') { $styles[] = '--pt-m:' . (int) $instance['layout_pin_m']['top'] . 'px;'; } if ($instance['layout_pin_m']['bottom'] !== '') { $styles[] = '--pb-m:' . (int) $instance['layout_pin_m']['bottom'] . 'px;'; } foreach (['white' => 'img_white', 'dark' => 'img_dark'] as $mode => $bg_key) { $bg = $instance['layout_bg'][$bg_key] ?? []; if (!empty($bg['background-color'])) { $styles[] = '--bg-color-' . $mode . ':' . $bg['background-color'] . ';'; } if (!empty($bg['background-image'])) { $styles[] = '--bg-img-' . $mode . ':url("' . $bg['background-image'] . '");'; } } $attr = ' class="' . esc_attr(implode(' ', $classes)) . '"'; if ($styles) { $attr .= ' style="' . esc_attr(implode('', $styles)) . '"'; } if ($instance['sidebar_affix']) { $attr .= ' data-affix="true"'; } return $attr; } } if (!function_exists('aksrt_widget_affix_attrs')) { /** * Compact affix class+attribute helper for widgets that apply affix onto an * inner wrapper (the two-family pattern from the legacy in_affix widgets). * * @param array $instance Widget instance. * @return array{class:string,attr:string} class includes leading space when active; attr includes leading space. */ function aksrt_widget_affix_attrs(array $instance): array { $instance = aksrt_widget_normalize_common($instance); if (empty($instance['sidebar_affix'])) { return ['class' => '', 'attr' => '']; } return ['class' => ' is-affix', 'attr' => ' data-affix="true"']; } } if (!function_exists('aksrt_widget_render_title')) { /** * Render the Zibll-aligned widget title block (CSF_Widget::show_title parity). * * Two styles: * - '' (简约): .box-body notop > .title-theme with optional subtitle + more link * - 'big' (居中大标题): .big-title-wrap with up to 3 button links * * Highlight wraps the matched substring in a colored span. * * @param array $instance Widget instance. * @return string Title HTML (empty when no title). */ function aksrt_widget_render_title(array $instance): string { $instance = aksrt_widget_normalize_common($instance); $title = trim((string) ($instance['title'] ?? '')); if ($title === '') { return ''; } $highlight = trim((string) $instance['title_highlight']); $subtitle = (string) $instance['subtitle']; $anim = function_exists('aksrt_widget_animation_class') ? aksrt_widget_animation_class($instance) : ''; $title = esc_html($title); if ($highlight !== '' && $highlight !== $title) { $color = $instance['title_highlight_color']; $hl_esc = esc_html($highlight); $title = str_replace($hl_esc, '' . $hl_esc . '', $title); } if ($instance['title_style'] === 'big') { $style = $instance['title_link_style']; $links = ''; foreach (['title_link' => 'color_1', 'title_link2' => 'color_2', 'title_link3' => 'color_3'] as $key => $color_key) { $link = $instance[$key]; if ($link['url'] !== '' && $link['text'] !== '') { $blank = $link['target'] === '_blank' ? ' target="_blank"' : ''; $links .= '' . esc_html($link['text']) . ''; } } $links = $links !== '' ? '
' . $links . '
' : ''; $sub = $subtitle !== '' ? '

' . esc_html($subtitle) . '

' : ''; return '

' . $title . '

' . $sub . $links . '
'; } // Simple style. $link = $instance['title_link']; $sub = $subtitle !== '' ? '' . esc_html($subtitle) . '' : ''; $more = ''; if ($link['url'] !== '') { $text = $link['text'] !== '' ? $link['text'] : '' . __('更多', 'aksrt'); $blank = $link['target'] === '_blank' ? ' target="_blank"' : ''; $more = '
' . wp_kses_post($text) . '
'; } return '
' . $title . $sub . $more . '
'; } } if (!function_exists('aksrt_widget_animation_class')) { /** * Zibll CSF_Widget::animation_class parity: obs-animate ani-* [+repeat]. * * @param array $instance Normalized widget instance. * @param bool $is_title Reserved for title-specific animation. * @return string */ function aksrt_widget_animation_class(array $instance, bool $is_title = false): string { $animation = (string) ($instance['animation_in'] ?? ''); if ($animation === '') { return ''; } $class = ' obs-animate ani-' . $animation; if (!empty($instance['animation_repeat'])) { $class .= ' obs-animate-repeat'; } return $class; } } if (!function_exists('aksrt_widget_should_display_by_ids')) { /** * Check if the current post/page should display the widget based on show_id_type and show_ids. * * show_id_type tokens: '' (no limit) | 'show' (whitelist) | 'hide' (blacklist). * Accepts legacy 'include'/'exclude' via normalize_common. * * @param array $instance Widget instance. * @return bool */ function aksrt_widget_should_display_by_ids(array $instance): bool { $instance = aksrt_widget_normalize_common($instance); if ($instance['show_id_type'] === '' || $instance['show_ids'] === '') { return true; } $current_id = get_the_ID(); if (!$current_id) { return true; // Not on a singular page, allow display } // Zibll splits on comma/Chinese-comma/whitespace/newline. $ids = array_filter(array_map('absint', preg_split('/[,,\s]+/', $instance['show_ids']) ?: [])); if ($instance['show_id_type'] === 'show') { return in_array($current_id, $ids, true); } if ($instance['show_id_type'] === 'hide') { return !in_array($current_id, $ids, true); } return true; } }