self::body( $s, (string) $head ); switch ( (string) ( $s['_display'] ?? '' ) ) { case AIEO_Content_Promo::NAME: return AIEO_Content_Promo::section( $s, $kind, $layout ); case AIEO_Content_Rooster_One::NAME: return AIEO_Content_Rooster_One::section( $s, $kind, $layout ); } return AIEO_Content_Sections::section( $s, $kind, $layout ); } public static function place( array $s, $head, callable $layout ) { if ( '' !== (string) $head && self::head_column( $s ) ) { return (string) $layout( $head ); } return (string) $head . (string) $layout( '' ); } public static function head_column( array $s ) { $column = (int) ( $s['head_column'] ?? 0 ); $count = self::count( $s['columns'] ?? 1 ); return $column >= 1 && $count > 1 && $column <= $count && ! self::head_only( $s ) ? $column : 0; } public static function count( $columns ) { return self::split( $columns ) ? 2 : max( 1, min( 4, (int) $columns ) ); } public static function alias_targets( array $s ) { $drawn = empty( $s['headless'] ); $present = array( 'heading' => $drawn && '' !== trim( (string) ( $s['heading'] ?? '' ) ), 'head' => $drawn && ( '' !== trim( (string) ( $s['heading'] ?? '' ) ) || ! empty( $s['eyebrow'] ) || ! empty( $s['lede'] ) || ! empty( $s['icon'] ) || ! empty( $s['badge'] ) || ! empty( $s['notice'] ) ), 'eyebrow' => $drawn && ! empty( $s['eyebrow'] ), 'badge' => $drawn && ! empty( $s['badge'] ), 'notice' => $drawn && ! empty( $s['notice'] ), 'body' => ! self::head_only( $s ), ); return array_values( array_filter( self::ALIAS_TARGETS, static fn ( $t ) => $present[ $t ] ) ); } public static function aliases( array $s ) { $ids = self::alias_ids( $s ); $targets = self::alias_targets( $s ); $n = min( count( $ids ), count( $targets ) ); return $n ? array_combine( array_slice( $targets, 0, $n ), array_slice( $ids, 0, $n ) ) : array(); } public static function alias_ids( array $s ) { $own = array( AIEO_Content_Sections::anchor( $s + array( 'key' => '' ) ) ); foreach ( is_array( $s['blocks'] ?? null ) ? $s['blocks'] : array() as $item ) { if ( is_array( $item ) && is_string( $item['anchor'] ?? null ) ) { $own[] = sanitize_title( $item['anchor'] ); } if ( is_array( $item ) ) { $own = array_merge( $own, self::card_anchors( $item ) ); } } $ids = array_map( static fn ( $a ) => sanitize_title( (string) $a ), (array) ( $s['aliases'] ?? array() ) ); return array_values( array_unique( array_filter( $ids, static fn ( $id ) => '' !== $id && ! in_array( $id, $own, true ) ) ) ); } public static function card_anchors( array $item ) { if ( 'card' !== (string) ( $item['block'] ?? '' ) || 'grid' !== (string) ( $item['variant'] ?? '' ) ) { return array(); } $lines = is_array( $item['items'] ?? null ) ? array_map( 'strval', $item['items'] ) : preg_split( '/\r\n|\r|\n/', (string) ( $item['items'] ?? '' ) ); $out = array(); foreach ( (array) $lines as $line ) { foreach ( array_slice( explode( '|', (string) $line ), 2 ) as $cell ) { if ( preg_match( '/^anchor:([a-z0-9][a-z0-9_-]*)$/', trim( $cell ), $m ) ) { $out[] = $m[1]; } } } return $out; } public static function split( $columns ) { if ( ! is_string( $columns ) || ! preg_match( self::SPLIT, trim( $columns ), $m ) ) { return null; } $a = (float) $m[1]; $b = (float) $m[3]; if ( $a <= 0 || $b <= 0 ) { return null; } $pct = static fn ( $v ) => rtrim( rtrim( number_format( 100 * $v / ( $a + $b ), 4, '.', '' ), '0' ), '.' ) . '%'; return array( $pct( $a ), $pct( $b ) ); } public static function block( array $item, $tone = '' ) { $name = sanitize_key( (string) ( $item['block'] ?? '' ) ); if ( ! in_array( $name, self::BLOCKS, true ) ) { return ''; } $attrs = array(); foreach ( $item as $key => $value ) { if ( in_array( $key, array( 'block', 'column' ), true ) ) { continue; } $attrs[ $key ] = is_array( $value ) && array_is_list( $value ) && ! array_filter( $value, 'is_array' ) ? implode( "\n", array_map( 'strval', $value ) ) : $value; } if ( '' !== (string) $tone && empty( $attrs['tone'] ) ) { $attrs['tone'] = (string) $tone; } $type = WP_Block_Type_Registry::get_instance()->get_registered( 'rooster/' . $name ); foreach ( $attrs as $key => $value ) { $schema = (string) ( $type->attributes[ $key ]['type'] ?? '' ); if ( 'integer' === $schema && is_string( $value ) && is_numeric( $value ) ) { $attrs[ $key ] = (int) $value; } elseif ( 'string' === $schema && ( is_int( $value ) || is_float( $value ) ) ) { $attrs[ $key ] = (string) $value; } } if ( isset( $attrs['variant'] ) ) { $attrs = array( 'variant' => $attrs['variant'] ) + $attrs; } return AIEO_Content_Blocks::block( $name, $attrs ); } public static function item( array $item, $tone = '' ) { if ( ! isset( $item['block'] ) && isset( $item['pattern'] ) ) { $slug = trim( (string) $item['pattern'] ); return '' !== $slug ? AIEO_Content_Blocks::comment( 'core/pattern', array( 'slug' => $slug ) ) : ''; } return self::block( $item, $tone ); } private static function body( array $s, $head = '' ) { $tone = sanitize_key( (string) ( $s['tone'] ?? '' ) ); $items = array_values( array_filter( (array) ( $s['blocks'] ?? array() ), 'is_array' ) ); $columns = $s['columns'] ?? 1; $split = self::split( $columns ); $count = self::count( $columns ); $gap = AIEO_Content_Sections::padding_value( $s['gap'] ?? 5 ); $gap = '' !== $gap ? $gap : 'var:preset|spacing|5'; $cells = array(); $at = array(); foreach ( $items as $i => $item ) { $markup = AIEO_Content_Tabs::is( $item ) ? AIEO_Content_Tabs::compile( $item, $s ) : self::item( $item, $tone ); if ( '' === $markup ) { continue; } $col = (int) ( $item['column'] ?? 0 ); if ( $col < 1 || $col > $count ) { $col = $split ? ( 0 === $i ? 1 : 2 ) : 0; } if ( $split ) { $cells[ $col ][] = $markup; } elseif ( $col ) { if ( ! isset( $at[ $col ] ) ) { $at[ $col ] = count( $cells ); $cells[] = array(); } $cells[ $at[ $col ] ][] = $markup; } else { $cells[] = array( $markup ); } } $in = '' !== (string) $head ? self::head_column( $s ) : 0; if ( $in ) { $index = $split ? $in : ( $at[ $in ] ?? ( isset( $cells[ $in - 1 ] ) ? $in - 1 : count( $cells ) ) ); $cells[ $index ] = array_merge( array( (string) $head ), $cells[ $index ] ?? array() ); } if ( ! $cells ) { return ''; } $attrs = array( 'metadata' => array( 'name' => __( 'Blocks', 'ai-eshop-optimizer' ) ), 'lock' => array( 'move' => true, 'remove' => true ), ); if ( '' !== AIEO_Content_Sections::alias( $s, 'body' ) ) { $attrs['anchor'] = AIEO_Content_Sections::alias( $s, 'body' ); } $attrs += array( 'align' => 'wide', 'className' => 'rooster-section__blocks', 'style' => array( 'spacing' => array( 'blockGap' => $gap, 'margin' => array( 'top' => $in ? '0' : 'var:preset|spacing|6' ) ) ), ); if ( ! empty( $s['defer'] ) && in_array( (string) ( $s['kind'] ?? '' ), AIEO_Content_Sections::DEFERRABLE, true ) && ! AIEO_Content_Tabs::holds( $s ) ) { $label = AIEO_Content_Inline::heading_plain( (string) ( $s['heading'] ?? '' ) ); $attrs['roosterDefer'] = array( 'enabled' => true, 'height' => '480px' ) + ( '' !== $label ? array( 'label' => $label ) : array() ); } if ( $split ) { $attrs['layout'] = array( 'type' => 'constrained' ); $columns_html = AIEO_Content_Sections::columns( AIEO_Content_Sections::column( implode( '', $cells[1] ?? array() ), $split[0], 'rooster-section__column', 'center' === (string) ( $s['align'] ?? '' ) ), AIEO_Content_Sections::column( implode( '', $cells[2] ?? array() ), $split[1], 'rooster-section__column', 'center' === (string) ( $s['align'] ?? '' ) ), array( 'verticalAlignment' => 'center' === (string) ( $s['align'] ?? '' ) ? 'center' : null, 'style' => array( 'spacing' => array( 'blockGap' => array( 'top' => $gap, 'left' => $gap ) ) ) ) ); return AIEO_Content_Blocks::group( $attrs, $columns_html ); } $attrs['layout'] = 1 === $count ? array( 'type' => 'flex', 'orientation' => 'vertical', 'flexWrap' => 'nowrap', 'justifyContent' => 'stretch' ) : array( 'type' => 'grid', 'columnCount' => $count, 'minimumColumnWidth' => '16rem' ); $inner = ''; ksort( $cells ); foreach ( $cells as $cell ) { $inner .= 1 === count( $cell ) || 1 === $count ? implode( '', $cell ) : AIEO_Content_Blocks::group( array( 'className' => 'rooster-section__cell', 'style' => array( 'spacing' => array( 'blockGap' => $gap ) ), 'layout' => array( 'type' => 'flex', 'orientation' => 'vertical', 'flexWrap' => 'nowrap', 'justifyContent' => 'stretch' ) ), implode( '', $cell ) ); } return AIEO_Content_Blocks::group( $attrs, $inner ); } }