'check-circle', 'no' => 'x', 'soon' => 'clock', 'partial' => 'sliders-horizontal', ); public static function boot() { add_action( 'init', array( __CLASS__, 'register' ), 9 ); add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'editor' ), 11 ); add_filter( 'render_block_core/group', array( __CLASS__, 'section_sheet' ), 10, 2 ); add_filter( 'render_block_core/button', array( __CLASS__, 'button' ), 11, 2 ); add_filter( 'render_block_core/icon', array( __CLASS__, 'icon_block' ), 10, 2 ); add_filter( 'render_block_data', array( __CLASS__, 'tabs_data' ) ); add_filter( 'render_block_data', array( __CLASS__, 'tab_stubs' ), 15, 3 ); add_action( 'enqueue_block_assets', array( __CLASS__, 'section_sheet_editor' ) ); } const BUTTON_ICON = 'rooster-button-icon-'; const BUTTON_NOTE = 'rooster-button-note'; public static function button( $content, $block ) { $content = (string) $content; $class = (string) ( $block['attrs']['className'] ?? '' ); $icon = str_contains( $class, self::BUTTON_ICON ) && preg_match( '/(?:^|\s)' . self::BUTTON_ICON . '([a-z0-9_-]+)(?:\s|$)/', $class, $m ) ? $m[1] : ''; $new_tab = str_contains( $content, 'target="_blank"' ) && ! str_contains( $content, 'screen-reader-text' ); $note = str_contains( $class, self::BUTTON_NOTE ) && ! str_contains( $content, 'rooster-button__words' ); if ( '' === $icon && ! $new_tab && ! $note ) { return $content; } $glyph = '' !== $icon && function_exists( 'rooster_icon' ) ? (string) rooster_icon( $icon, array( 'width' => 18, 'height' => 18, 'class' => 'rooster-button__icon' ) ) : ''; $words = $new_tab ? ' ' . esc_html__( '(opens in a new tab)', 'ai-eshop-optimizer' ) . '' : ''; if ( '' === $glyph && '' === $words && ! $note ) { return $content; } $out = preg_replace_callback( '#(<(a|button)\b[^>]*\bwp-block-button__link\b[^>]*>)(.*?)()#s', static function ( $m ) use ( $glyph, $words, $note ) { $inner = $m[3]; if ( $note && preg_match( '#^(.*?)(.*)$#s', $inner, $lines ) ) { $inner = '' . $lines[1] . '' . $lines[2] . ''; } return $m[1] . $glyph . $inner . $words . $m[4]; }, $content, 1 ); if ( null === $out || $out === $content ) { return $content; } if ( wp_style_is( self::handle( self::SECTION_SHEET ), 'registered' ) ) { wp_enqueue_style( self::handle( self::SECTION_SHEET ) ); } return $out; } public static function icon_block( $content, $block ) { $content = (string) $content; if ( ! str_contains( $content, 'rooster-icon-stroke' ) ) { return $content; } $out = preg_replace_callback( '#]*\brooster-icon-stroke\b[^>]*>.*?#s', static fn ( $m ) => (string) preg_replace( '#<(path|circle|rect|ellipse|line|polyline|polygon)\b(?![^>]*\sfill=)#', '<$1 fill="none"', $m[0] ), $content ); return null === $out ? $content : $out; } public static function chips_items( $text, $layout ) { $list = ''; foreach ( self::lines( (string) $text ) as $line ) { $parts = array_map( 'trim', explode( '|', $line, 2 ) ); $words = (string) ( $parts[1] ?? $parts[0] ); if ( '' === $words ) { continue; } $icon = isset( $parts[1] ) && 'pills' !== $layout ? sanitize_key( $parts[0] ) : ''; $glyph = '' !== $icon && 'none' !== $icon && function_exists( 'rooster_icon' ) ? rooster_icon( $icon, array( 'width' => 18, 'height' => 18, 'class' => 'rooster-chips__icon' ) ) : ''; $list .= '
  • ' . $glyph . self::run( $words ) . '
  • '; } return $list; } const SECTION_SHEET = 'section'; public static function section_sheet( $content, $block ) { $class = (string) ( $block['attrs']['className'] ?? '' ); if ( '' !== $class && ( str_contains( $class, 'rooster-section--tone-' ) || str_contains( $class, 'rooster-section__pill' ) || str_contains( $class, 'rooster-hero--center' ) || str_contains( $class, 'rooster-hero__aside' ) || ( str_contains( $class, 'rooster-section__head' ) && str_contains( (string) $content, 'rooster-notice-pill' ) ) ) ) { wp_enqueue_style( self::handle( self::SECTION_SHEET ) ); } return $content; } public static function section_sheet_editor() { if ( is_admin() && wp_style_is( self::handle( self::SECTION_SHEET ), 'registered' ) ) { wp_enqueue_style( self::handle( self::SECTION_SHEET ) ); } } public static function mark( $kind, $words ) { $icons = (array) apply_filters( 'rooster/blocks/mark_icons', self::MARK_ICONS ); $slug = (string) ( $icons[ $kind ] ?? self::MARK_ICONS['yes'] ); return rooster_icon( $slug, array( 'width' => 20, 'height' => 20, 'class' => 'rooster-mark rooster-mark--' . sanitize_html_class( (string) $kind ) ) ) . '' . esc_html( (string) $words ) . ' '; } public static function lines( $text ) { return array_values( array_filter( array_map( 'trim', preg_split( '/\r\n|\r|\n/', (string) $text ) ?: array() ), 'strlen' ) ); } const INLINE = '/(\*\*.+?\*\*|`[^`]+`|\[[^\]]+\]\([^)\s]+\))/su'; public static function inline( $text, $links = true ) { $out = ''; foreach ( preg_split( self::INLINE, (string) $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ) ?: array() as $part ) { if ( preg_match( '/^\*\*(.+)\*\*$/su', $part, $m ) ) { $out .= '' . self::inline( $m[1], $links ) . ''; } elseif ( preg_match( '/^`([^`]+)`$/u', $part, $m ) ) { $out .= '' . esc_html( $m[1] ) . ''; } elseif ( preg_match( '/^\[([^\]]+)\]\(([^)\s]+)\)$/u', $part, $m ) ) { $href = $links ? esc_url( $m[2] ) : ''; $words = self::inline( $m[1], false ); $out .= '' !== $href ? '' . $words . '' : $words; } else { $out .= str_replace( ' // ', '
    ', esc_html( $part ) ); } } return $out; } public static function run( $text, $links = true ) { $html = self::inline( $text, $links ); return str_contains( $html, '<' ) ? '' . $html . '' : $html; } public static function plain( $text ) { $text = (string) preg_replace( '/\*\*(.+?)\*\*/su', '$1', (string) $text ); $text = (string) preg_replace( '/`([^`]+)`/u', '$1', $text ); $text = (string) preg_replace( '/\[([^\]]+)\]\([^)\s]+\)/u', '$1', $text ); return str_replace( ' // ', ' ', $text ); } public static function text_html( $text, $p_class, $ul_class, array $args = array() ) { $links = ! isset( $args['links'] ) || (bool) $args['links']; $level = min( 6, max( 0, (int) ( $args['level'] ?? 0 ) ) ); $line = static fn ( $words ) => self::inline( $words, $links ); $out = ''; $lines = preg_split( '/\r\n|\r|\n/', trim( (string) $text ) ) ?: array(); $para = array(); $list = array(); $flush = static function () use ( &$para, &$list, &$out, $p_class, $ul_class, $line ) { if ( $para ) { $out .= '

    ' . implode( '
    ', array_map( $line, $para ) ) . '

    '; $para = array(); } if ( $list ) { $out .= ''; $list = array(); } }; foreach ( $lines as $row ) { $row = trim( $row ); if ( '' === $row ) { $flush(); } elseif ( $level > 1 && preg_match( '/^#{2,4}\s+(.+)$/u', $row, $m ) ) { $flush(); $out .= sprintf( '%3$s', $level, esc_attr( (string) ( $args['heading'] ?? '' ) ), $line( trim( $m[1] ) ) ); } elseif ( preg_match( '/^[-*]\s+(.+)$/u', $row, $m ) ) { if ( $para ) { $flush(); } $list[] = $m[1]; } else { if ( $list ) { $flush(); } $para[] = $row; } } $flush(); return $out; } const LIBRARY = array( 'card' => array( 'feature' => 'stack', 'family' => 'card', 'grid' => 'cards', 'tagged' => 'panels', 'role' => '', 'link' => '', 'detail' => '', 'category' => '' ), 'plan' => array( 'price' => 'card', 'plan' => 'card' ), 'table' => array( 'compare' => '', 'data' => '', 'matrix' => '' ), 'action' => array( 'panel' => 'band', 'buttons' => '' ), 'list' => array( 'checks' => 'grid', 'facts' => 'list', 'stages' => '', 'chips' => 'tiles' ), 'media' => array( 'frame' => '', 'screens' => '', 'figure' => '', 'stack' => '', 'track' => '' ), 'mark' => array( 'crumb' => '', 'pill' => '', 'note' => 'card', 'nav' => '' ), 'questions' => array( 'rows' => '', 'boxed' => '' ), ); const TONES = array( 'neutral', 'success', 'warning', 'danger' ); const OWN_SHEETS = array( 'tabs' ); const TABS = 'rooster/tabs'; const TAB = 'rooster/tab'; public static function tabs_state( array $parsed ) { $tabs = array(); foreach ( (array) ( $parsed['innerBlocks'] ?? array() ) as $inner ) { if ( self::TAB !== (string) ( $inner['blockName'] ?? '' ) ) { continue; } $a = (array) ( $inner['attrs'] ?? array() ); $tabs[] = array( 'anchor' => sanitize_title( (string) ( $a['anchor'] ?? '' ) ), 'label' => trim( (string) ( $a['label'] ?? '' ) ), 'icon' => sanitize_key( (string) ( $a['icon'] ?? '' ) ), 'tone' => self::tone( (string) ( $a['tone'] ?? '' ) ), ); } $id = sanitize_title( (string) ( $parsed['attrs']['anchor'] ?? '' ) ); if ( '' === $id ) { $id = 'rooster-tabs-' . substr( md5( implode( '|', array_column( $tabs, 'label' ) ) ), 0, 6 ); } foreach ( $tabs as $n => $tab ) { $tabs[ $n ]['id'] = '' !== $tab['anchor'] ? $tab['anchor'] : $id . '-' . ( $n + 1 ); if ( '' === $tab['label'] ) { $tabs[ $n ]['label'] = sprintf( __( 'Tab %d', 'ai-eshop-optimizer' ), $n + 1 ); } unset( $tabs[ $n ]['anchor'] ); } return array( 'id' => $id, 'tabs' => $tabs ); } public static function tabs_data( $parsed ) { if ( ! is_array( $parsed ) || self::TABS !== (string) ( $parsed['blockName'] ?? '' ) ) { return $parsed; } $state = self::tabs_state( $parsed ); $even = ! empty( $parsed['attrs']['even'] ); $n = 0; foreach ( (array) ( $parsed['innerBlocks'] ?? array() ) as $i => $inner ) { if ( self::TAB === (string) ( $inner['blockName'] ?? '' ) ) { $parsed['innerBlocks'][ $i ]['roosterTab'] = array( 'id' => $state['tabs'][ $n ]['id'], 'first' => 0 === $n, 'even' => $even ); ++$n; } } $parsed['roosterTabs'] = $state; return $parsed; } const TAB_ANCHOR = 'rooster/tab-anchor'; const TAB_ANCHOR_CLASS = 'rooster-tabs__anchor'; public static function tab_stubs( $parsed, $source = null, $parent = null ) { if ( null !== $parent || ! is_array( $parsed ) || ! self::layer_defers() || ! self::has_tab( $parsed ) ) { return $parsed; } return self::stub_tree( $parsed ); } private static function layer_defers() { return class_exists( 'Rooster\Core\Defer', false ) && \Rooster\Core\Defer::enabled() && ! \Rooster\Core\Defer::renders_whole(); } private static function has_tab( array $block ) { if ( self::TAB === (string) ( $block['blockName'] ?? '' ) ) { return true; } foreach ( (array) ( $block['innerBlocks'] ?? array() ) as $child ) { if ( is_array( $child ) && self::has_tab( $child ) ) { return true; } } return false; } private static function stub_tree( array $block ) { $children = (array) ( $block['innerBlocks'] ?? array() ); $chunks = (array) ( $block['innerContent'] ?? array() ); if ( ! $children || count( array_filter( $chunks, 'is_null' ) ) !== count( $children ) ) { return $block; } $tab = self::TAB === (string) ( $block['blockName'] ?? '' ); $blocks = array(); $content = array(); $n = 0; foreach ( $chunks as $chunk ) { if ( null !== $chunk ) { $content[] = $chunk; continue; } $child = (array) $children[ $n++ ]; if ( $tab && ! empty( $child['attrs']['roosterDefer']['enabled'] ) ) { foreach ( self::ids_in( $child ) as $id ) { $blocks[] = array( 'blockName' => self::TAB_ANCHOR, 'attrs' => array( 'id' => $id ), 'innerBlocks' => array(), 'innerHTML' => '', 'innerContent' => array() ); $content[] = null; } } elseif ( ! $tab && self::has_tab( $child ) ) { $child = self::stub_tree( $child ); } $blocks[] = $child; $content[] = null; } $block['innerBlocks'] = $blocks; $block['innerContent'] = $content; return $block; } public static function ids_in( array $block ) { $ids = array(); $walk = static function ( array $b ) use ( &$walk, &$ids ) { $a = (array) ( $b['attrs'] ?? array() ); $name = (string) ( $b['blockName'] ?? '' ); if ( is_string( $a['anchor'] ?? null ) ) { $ids[] = $a['anchor']; } if ( 'rooster/card' === $name && preg_match_all( '/\|\s*anchor:([a-z0-9][a-z0-9_-]*)\s*(?=\||$)/m', (string) ( $a['items'] ?? '' ), $m ) ) { $ids = array_merge( $ids, $m[1] ); } if ( 'rooster/questions' === $name && '' !== (string) ( $a['anchorPrefix'] ?? '' ) ) { $count = 0; foreach ( self::lines( (string) ( $a['items'] ?? '' ) ) as $line ) { if ( ( ! $count || str_contains( $line, '|' ) ) && '' !== trim( explode( '|', $line, 2 )[0] ) ) { $ids[] = $a['anchorPrefix'] . '-' . ( ++$count ); } } } foreach ( (array) ( $b['innerBlocks'] ?? array() ) as $child ) { if ( is_array( $child ) ) { $walk( $child ); } } }; $walk( $block ); return array_values( array_unique( array_filter( array_map( 'sanitize_title', $ids ), 'strlen' ) ) ); } public static function render_tab_anchor( $attributes ) { $id = sanitize_title( (string) ( $attributes['id'] ?? '' ) ); return '' !== $id ? '
    ' : ''; } public static function variant( $block, array $attributes ) { $variants = self::LIBRARY[ (string) $block ] ?? array( '' => '' ); $variant = (string) ( $attributes['variant'] ?? '' ); if ( ! isset( $variants[ $variant ] ) ) { $variant = (string) array_key_first( $variants ); } if ( '' === (string) ( $attributes['layout'] ?? '' ) && '' !== (string) $variants[ $variant ] ) { $attributes['layout'] = (string) $variants[ $variant ]; } $class = self::tone_classes( (string) ( $attributes['tone'] ?? '' ), true ); if ( '' !== $class && wp_style_is( self::handle( (string) $block ), 'registered' ) ) { wp_enqueue_style( self::handle( (string) $block ) ); } return array( $variant, $attributes, array( 'block' => (string) $block, 'class' => $class ) ); } public static function wrapper( array $library, array $extra = array() ) { $extra['class'] = trim( (string) ( $extra['class'] ?? '' ) . ' ' . (string) ( $library['class'] ?? '' ) ); $extra['data-rooster-ui'] = 'rooster-one/' . (string) ( $library['block'] ?? '' ); return get_block_wrapper_attributes( $extra ); } public static function tone( $value, $fallback = '' ) { $value = sanitize_key( (string) $value ); return '' !== $value && ! in_array( $value, array( 'green', 'red', 'amber', 'blue' ), true ) ? $value : (string) $fallback; } public static function is_tone( $value ) { $value = trim( (string) $value ); if ( in_array( $value, self::TONES, true ) ) { return true; } if ( ! preg_match( '/^[a-z][a-z0-9-]{0,23}$/', $value ) || '' === self::tone( $value ) ) { return false; } if ( class_exists( '\Rooster\Core\Design' ) && method_exists( '\Rooster\Core\Design', 'accents' ) ) { return in_array( $value, array_map( static fn ( $a ) => (string) ( $a['slug'] ?? '' ), (array) \Rooster\Core\Design::accents() ), true ); } return true; } public static function tone_classes( $tone, $root = false ) { $tone = sanitize_key( (string) $tone ); if ( '' === $tone ) { return ''; } $family = ! in_array( $tone, self::TONES, true ) && ( $root || 'one' !== $tone ); return 'is-tone-' . $tone . ( $family ? ' is-style-accent-' . $tone : '' ); } public static function enqueue( $block, $variant ) { $handle = self::handle( (string) $block, (string) $variant ); if ( wp_style_is( $handle, 'registered' ) ) { wp_enqueue_style( $handle ); } } public static function editor() { if ( ! wp_script_is( 'rooster-editor-blocks', 'enqueued' ) && ! wp_script_is( 'rooster-editor-blocks', 'registered' ) ) { return; } $manifest = AIEO_ENGINE_DIR . '/blocks/blocks-manifest.php'; if ( ! is_file( $manifest ) ) { return; } $theme = class_exists( 'Rooster\Core\BlockTypes' ) ? array_column( \Rooster\Core\BlockTypes::manifest(), 'name' ) : array(); $extra = array(); foreach ( (array) require $manifest as $dir => $meta ) { if ( empty( $meta['name'] ) || in_array( $meta['name'], $theme, true ) ) { continue; } $extra[] = array( 'name' => $meta['name'], 'dir' => $dir, 'innerBlocks' => ! empty( $meta['rooster']['innerBlocks'] ), 'template' => $meta['rooster']['template'] ?? null, 'ui' => $meta['rooster']['ui'] ?? array(), 'notice' => $meta['rooster']['notice'] ?? null, ); } if ( $extra ) { wp_add_inline_script( 'rooster-editor-blocks', 'window.roosterBlocks = ( window.roosterBlocks || [] ).concat( ' . wp_json_encode( $extra ) . ' );', 'before' ); } $editor = AIEO_ENGINE_DIR . '/blocks/mega-menu/editor.js'; if ( is_file( $editor ) ) { wp_enqueue_script( 'aieo-mega-menu-editor', AIEO_ENGINE_URL . '/blocks/mega-menu/editor.js', array( 'wp-hooks', 'wp-compose', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n' ), (string) filemtime( $editor ), true ); wp_set_script_translations( 'aieo-mega-menu-editor', 'ai-eshop-optimizer', AIEO_ENGINE_LANGUAGES ); } } public static function none_rendered() { static $none = null; if ( null === $none ) { $action = wp_doing_ajax() && isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( (string) $_REQUEST['action'] ) ) : ''; $none = '' !== $action && in_array( $action, array( 'aieo_swatch_sync_cart', 'aieo_swatch_batch_add_to_cart', 'aieo_swatch_quick_add_to_cart', 'aieo_swatch_get_cart_variations', 'aieo_swatch_get_all_variations', 'aieo_swatch_refresh_nonce', 'om_swatch_sync_cart', 'om_swatch_batch_add_to_cart', 'om_swatch_quick_add_to_cart', 'aieo_side_cart_get', 'aieo_side_cart_update_qty', 'aieo_side_cart_remove', 'aieo_side_cart_apply_coupon', 'aieo_side_cart_quick_view_add', ), true ); } return $none; } public static function register() { $dir = AIEO_ENGINE_DIR . '/blocks'; $manifest = $dir . '/blocks-manifest.php'; if ( ! is_file( $manifest ) ) { return; } if ( function_exists( 'wp_register_block_metadata_collection' ) ) { wp_register_block_metadata_collection( $dir, $manifest ); } if ( is_file( $dir . '/' . self::SECTION_SHEET . '/style.css' ) ) { self::register_sheet( self::handle( self::SECTION_SHEET ), self::SECTION_SHEET, 'style.css', '2.0.0' ); } $registry = WP_Block_Type_Registry::get_instance(); if ( ! $registry->is_registered( self::TAB_ANCHOR ) ) { register_block_type( self::TAB_ANCHOR, array( 'api_version' => 3, 'title' => __( 'Tab anchor', 'ai-eshop-optimizer' ), 'attributes' => array( 'id' => array( 'type' => 'string' ) ), 'supports' => array( 'inserter' => false, 'html' => false, 'className' => false, 'customClassName' => false ), 'render_callback' => array( __CLASS__, 'render_tab_anchor' ), ) ); } foreach ( (array) require $manifest as $folder => $meta ) { $name = (string) ( $meta['name'] ?? '' ); if ( '' === $name || $registry->is_registered( $name ) || ! is_dir( $dir . '/' . $folder ) ) { continue; } $args = array(); if ( ! empty( $meta['rooster']['when'] ) && ! self::when( (string) $meta['rooster']['when'] ) ) { $args['render_callback'] = static function ( $attributes, $content ) { return (string) $content; }; } if ( isset( self::LIBRARY[ $folder ] ) || in_array( (string) $folder, self::OWN_SHEETS, true ) ) { self::register_sheet( self::handle( (string) $folder ), (string) $folder, 'style.css', (string) ( $meta['version'] ?? '' ) ); } $type = register_block_type( $dir . '/' . $folder, $args ); if ( $type && isset( self::LIBRARY[ $folder ] ) && ! in_array( self::handle( (string) $folder ), (array) $type->style_handles, true ) ) { $type->editor_style_handles = array_values( array_unique( array_merge( (array) $type->editor_style_handles, array( self::handle( (string) $folder ) ) ) ) ); } if ( $type && ! empty( $meta['rooster']['variants'] ) ) { self::register_variants( $type, (string) $folder, (array) $meta['rooster']['variants'], (string) ( $meta['version'] ?? '' ) ); } } } public static function handle( $block, $variant = '' ) { return 'rooster-lib-' . $block . ( '' !== (string) $variant ? '-' . $variant : '' ); } private static function register_sheet( $handle, $folder, $file, $version ) { if ( wp_style_is( $handle, 'registered' ) ) { return; } wp_register_style( $handle, AIEO_ENGINE_URL . '/blocks/' . $folder . '/' . $file, array(), '' !== $version ? $version : null ); wp_style_add_data( $handle, 'path', wp_normalize_path( AIEO_ENGINE_DIR . '/blocks/' . $folder . '/' . $file ) ); } private static function register_variants( $type, $folder, array $variants, $version ) { foreach ( $variants as $variant ) { $variant = sanitize_key( (string) $variant ); if ( '' === $variant ) { continue; } $handle = self::handle( $folder, $variant ); self::register_sheet( $handle, $folder, 'variants/' . $variant . '.css', $version ); $type->editor_style_handles = array_values( array_unique( array_merge( (array) $type->editor_style_handles, array( $handle ) ) ) ); } } const OUT_GLOBAL = array( 'class', 'id', 'hidden', 'inert', 'role', 'tabindex', 'title', 'lang', 'dir', 'style', 'data-*', 'translate', 'popover', 'aria-label', 'aria-labelledby', 'aria-describedby', 'aria-description', 'aria-details', 'aria-hidden', 'aria-expanded', 'aria-controls', 'aria-current', 'aria-haspopup', 'aria-live', 'aria-atomic', 'aria-busy', 'aria-pressed', 'aria-selected', 'aria-checked', 'aria-disabled', 'aria-modal', 'aria-orientation', 'aria-roledescription', 'aria-sort', 'aria-level', 'aria-posinset', 'aria-setsize', 'aria-colcount', 'aria-colindex', 'aria-colspan', 'aria-rowcount', 'aria-rowindex', 'aria-rowspan', 'aria-valuenow', 'aria-valuemin', 'aria-valuemax', 'aria-valuetext', 'aria-invalid', 'aria-required', 'aria-errormessage', 'aria-readonly', 'aria-owns', 'aria-keyshortcuts', 'aria-relevant', 'aria-autocomplete', 'aria-activedescendant', 'aria-multiselectable', ); const OUT_EXTRA = array( 'a' => array( 'href', 'target', 'rel', 'hreflang', 'download', 'type', 'name' ), 'img' => array( 'src', 'srcset', 'sizes', 'alt', 'width', 'height', 'loading', 'decoding', 'fetchpriority', 'usemap' ), 'picture' => array(), 'source' => array( 'srcset', 'sizes', 'media', 'type', 'width', 'height', 'src' ), 'form' => array( 'action', 'method', 'novalidate', 'autocomplete', 'name', 'target', 'enctype' ), 'input' => array( 'type', 'name', 'value', 'checked', 'disabled', 'readonly', 'required', 'placeholder', 'autocomplete', 'autofocus', 'spellcheck', 'maxlength', 'minlength', 'min', 'max', 'step', 'size', 'pattern', 'inputmode', 'list', 'form', 'multiple', 'accept', 'enterkeyhint' ), 'select' => array( 'name', 'disabled', 'required', 'multiple', 'size', 'form', 'autocomplete' ), 'option' => array( 'value', 'selected', 'disabled', 'label' ), 'optgroup' => array( 'label', 'disabled' ), 'textarea' => array( 'name', 'rows', 'cols', 'disabled', 'readonly', 'required', 'placeholder', 'maxlength', 'minlength', 'spellcheck', 'autocomplete', 'form', 'wrap' ), 'button' => array( 'type', 'name', 'value', 'disabled', 'form', 'autofocus', 'popovertarget', 'popovertargetaction', 'commandfor', 'command' ), 'label' => array( 'for', 'form' ), 'fieldset' => array( 'disabled', 'form', 'name' ), 'legend' => array(), 'output' => array( 'for', 'form', 'name' ), 'details' => array( 'open', 'name' ), 'summary' => array(), 'dialog' => array( 'open', 'closedby' ), 'template' => array(), 'noscript' => array(), 'time' => array( 'datetime' ), 'svg' => array( 'xmlns', 'xmlns:xlink', 'width', 'height', 'viewbox', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'focusable', 'preserveaspectratio', 'version', 'x', 'y' ), 'g' => array( 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'mask', 'clip-path', 'fill-rule', 'clip-rule' ), 'defs' => array(), 'symbol' => array( 'viewbox', 'preserveaspectratio', 'fill', 'stroke' ), 'use' => array( 'href', 'xlink:href', 'x', 'y', 'width', 'height', 'fill', 'stroke' ), 'title' => array(), 'desc' => array(), 'path' => array( 'd', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-dasharray', 'stroke-dashoffset', 'fill-rule', 'clip-rule', 'transform', 'opacity', 'fill-opacity', 'stroke-opacity', 'vector-effect', 'mask', 'clip-path', 'pathlength' ), 'circle' => array( 'cx', 'cy', 'r', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'transform', 'opacity', 'fill-opacity', 'stroke-opacity', 'vector-effect', 'mask', 'pathlength' ), 'ellipse' => array( 'cx', 'cy', 'rx', 'ry', 'fill', 'stroke', 'stroke-width', 'transform', 'opacity', 'fill-opacity', 'stroke-opacity', 'vector-effect' ), 'rect' => array( 'x', 'y', 'width', 'height', 'rx', 'ry', 'fill', 'stroke', 'stroke-width', 'stroke-linejoin', 'stroke-dasharray', 'transform', 'opacity', 'fill-opacity', 'stroke-opacity', 'vector-effect', 'mask', 'clip-path' ), 'line' => array( 'x1', 'y1', 'x2', 'y2', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-dasharray', 'transform', 'opacity', 'stroke-opacity', 'vector-effect' ), 'polyline' => array( 'points', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'vector-effect' ), 'polygon' => array( 'points', 'fill', 'stroke', 'stroke-width', 'stroke-linejoin', 'transform', 'opacity', 'fill-opacity', 'vector-effect' ), 'text' => array( 'x', 'y', 'dx', 'dy', 'text-anchor', 'dominant-baseline', 'transform', 'fill', 'font-size', 'font-weight', 'font-family', 'letter-spacing' ), 'tspan' => array( 'x', 'y', 'dx', 'dy', 'fill', 'font-weight' ), 'mask' => array( 'x', 'y', 'width', 'height', 'maskunits', 'maskcontentunits', 'fill' ), 'clippath' => array( 'clippathunits' ), 'lineargradient' => array( 'x1', 'y1', 'x2', 'y2', 'gradientunits', 'gradienttransform', 'spreadmethod', 'href', 'xlink:href' ), 'radialgradient' => array( 'cx', 'cy', 'r', 'fx', 'fy', 'fr', 'gradientunits', 'gradienttransform', 'spreadmethod', 'href', 'xlink:href' ), 'stop' => array( 'offset', 'stop-color', 'stop-opacity' ), 'pattern' => array( 'x', 'y', 'width', 'height', 'patternunits', 'patterncontentunits', 'patterntransform', 'viewbox' ), ); private static $allowed = null; public static function allowed() { if ( null !== self::$allowed ) { return self::$allowed; } $allowed = wp_kses_allowed_html( 'post' ); foreach ( self::OUT_EXTRA as $tag => $attrs ) { $allowed[ $tag ] = array_merge( is_array( $allowed[ $tag ] ?? null ) ? $allowed[ $tag ] : array(), array_fill_keys( $attrs, true ) ); } $global = array_fill_keys( self::OUT_GLOBAL, true ); foreach ( $allowed as $tag => $attrs ) { $allowed[ $tag ] = array_merge( is_array( $attrs ) ? $attrs : array(), $global ); } unset( $allowed['script'], $allowed['style'], $allowed['iframe'], $allowed['object'], $allowed['embed'] ); self::$allowed = $allowed; return $allowed; } public static function out( string $html, array $protocols = array() ): void { echo wp_kses( $html, self::allowed(), array() === $protocols ? wp_allowed_protocols() : array_merge( wp_allowed_protocols(), $protocols ) ); } public static function when( $condition ) { if ( 'elementor' === $condition ) { return function_exists( 'rooster_setting' ) && (bool) rooster_setting( 'elementor', 'compatibility_mode', false ) && ( defined( 'ELEMENTOR_VERSION' ) || class_exists( '\Elementor\Plugin' ) ); } return (bool) apply_filters( 'rooster/blocks/when', false, $condition ); } } }