HTML;
}
/**
* Processes the `data-wp-router-region` directive.
*
* It renders in the footer a set of HTML elements to notify users about
* client-side navigations. More concretely, the elements added are 1) a
* top loading bar to visually inform that a navigation is in progress
* and 2) an `aria-live` region for accessible navigation announcements.
*
* @since 6.5.0
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
*/
private function data_wp_router_region_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
if ( 'enter' === $mode && ! $this->has_processed_router_region ) {
$this->has_processed_router_region = true;
// Enqueues as an inline style.
wp_register_style( 'wp-interactivity-router-animations', false );
wp_add_inline_style( 'wp-interactivity-router-animations', $this->get_router_animation_styles() );
wp_enqueue_style( 'wp-interactivity-router-animations' );
// Adds the necessary markup to the footer.
add_action( 'wp_footer', array( $this, 'print_router_markup' ) );
}
}
/**
* Processes the `data-wp-each` directive.
*
* This directive gets an array passed as reference and iterates over it
* generating new content for each item based on the inner markup of the
* `template` tag.
*
* @since 6.5.0
* @since 6.9.0 Include the list path in the rendered `data-wp-each-child` directives.
*
* @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
* @param string $mode Whether the processing is entering or exiting the tag.
* @param array $tag_stack The reference to the tag stack.
*/
private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$tag_stack ) {
if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
$entries = $this->get_directive_entries( $p, 'each' );
if ( count( $entries ) > 1 || empty( $entries ) ) {
// There should be only one `data-wp-each` directive per template tag.
return;
}
$entry = $entries[0];
if ( null !== $entry['unique_id'] ) {
return;
}
$item_name = isset( $entry['suffix'] ) ? $this->kebab_to_camel_case( $entry['suffix'] ) : 'item';
$result = $this->evaluate( $entry );
// Gets the content between the template tags and leaves the cursor in the closer tag.
$inner_content = $p->get_content_between_balanced_template_tags();
// Checks if there is a manual server-side directive processing.
$template_end = 'data-wp-each: template end';
$p->set_bookmark( $template_end );
$p->next_tag();
$manual_sdp = $p->get_attribute( 'data-wp-each-child' );
$p->seek( $template_end ); // Rewinds to the template closer tag.
$p->release_bookmark( $template_end );
/*
* It doesn't process in these situations:
* - Manual server-side directive processing.
* - Empty or non-array values.
* - Associative arrays because those are deserialized as objects in JS.
* - Templates that contain top-level texts because those texts can't be
* identified and removed in the client.
*/
if (
$manual_sdp ||
empty( $result ) ||
! is_array( $result ) ||
! array_is_list( $result ) ||
! str_starts_with( trim( $inner_content ), '<' ) ||
! str_ends_with( trim( $inner_content ), '>' )
) {
array_pop( $tag_stack );
return;
}
// Processes the inner content for each item of the array.
$processed_content = '';
foreach ( $result as $item ) {
// Creates a new context that includes the current item of the array.
$this->context_stack[] = array_replace_recursive(
end( $this->context_stack ) !== false ? end( $this->context_stack ) : array(),
array( $entry['namespace'] => array( $item_name => $item ) )
);
// Processes the inner content with the new context.
$processed_item = $this->_process_directives( $inner_content );
if ( null === $processed_item ) {
// If the HTML is unbalanced, stop processing it.
array_pop( $this->context_stack );
return;
}
/*
* Adds the `data-wp-each-child` directive to each top-level tag
* rendered by this `data-wp-each` directive. The value is the
* `data-wp-each` directive's namespace and path.
*
* Nested `data-wp-each` directives could render
* `data-wp-each-child` elements at the top level as well, and
* they should be overwritten.
*
* @since 6.9.0
*/
$i = new WP_Interactivity_API_Directives_Processor( $processed_item );
while ( $i->next_tag() ) {
$i->set_attribute( 'data-wp-each-child', $entry['namespace'] . '::' . $entry['value'] );
$i->next_balanced_tag_closer_tag();
}
$processed_content .= $i->get_updated_html();
// Removes the current context from the stack.
array_pop( $this->context_stack );
}
// Appends the processed content after the tag closer of the template.
$p->append_content_after_template_tag_closer( $processed_content );
// Pops the last tag because it skipped the closing tag of the template tag.
array_pop( $tag_stack );
}
}
}