summaryrefslogtreecommitdiffstats
path: root/wp-includes/blocks/image.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--wp-includes/blocks/image.php338
1 files changed, 131 insertions, 207 deletions
diff --git a/wp-includes/blocks/image.php b/wp-includes/blocks/image.php
index acefd57..0b75bf9 100644
--- a/wp-includes/blocks/image.php
+++ b/wp-includes/blocks/image.php
@@ -20,29 +20,26 @@ function render_block_core_image( $attributes, $content, $block ) {
return '';
}
- $processor = new WP_HTML_Tag_Processor( $content );
+ $p = new WP_HTML_Tag_Processor( $content );
- if ( ! $processor->next_tag( 'img' ) || null === $processor->get_attribute( 'src' ) ) {
+ if ( ! $p->next_tag( 'img' ) || null === $p->get_attribute( 'src' ) ) {
return '';
}
if ( isset( $attributes['data-id'] ) ) {
- // Add the data-id="$id" attribute to the img element
- // to provide backwards compatibility for the Gallery Block,
- // which now wraps Image Blocks within innerBlocks.
- // The data-id attribute is added in a core/gallery `render_block_data` hook.
- $processor->set_attribute( 'data-id', $attributes['data-id'] );
+ // Adds the data-id="$id" attribute to the img element to provide backwards
+ // compatibility for the Gallery Block, which now wraps Image Blocks within
+ // innerBlocks. The data-id attribute is added in a core/gallery
+ // `render_block_data` hook.
+ $p->set_attribute( 'data-id', $attributes['data-id'] );
}
$link_destination = isset( $attributes['linkDestination'] ) ? $attributes['linkDestination'] : 'none';
$lightbox_settings = block_core_image_get_lightbox_settings( $block->parsed_block );
- $view_js_file_handle = 'wp-block-image-view';
- $script_handles = $block->block_type->view_script_handles;
-
/*
- * If the lightbox is enabled and the image is not linked, add the filter
- * and the JavaScript view file.
+ * If the lightbox is enabled and the image is not linked, adds the filter and
+ * the JavaScript view file.
*/
if (
isset( $lightbox_settings ) &&
@@ -50,34 +47,37 @@ function render_block_core_image( $attributes, $content, $block ) {
isset( $lightbox_settings['enabled'] ) &&
true === $lightbox_settings['enabled']
) {
- $block->block_type->supports['interactivity'] = true;
-
- if ( ! in_array( $view_js_file_handle, $script_handles, true ) ) {
- $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file_handle ) );
+ $suffix = wp_scripts_get_suffix();
+ if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
+ $module_url = gutenberg_url( '/build/interactivity/image.min.js' );
}
+ wp_register_script_module(
+ '@wordpress/block-library/image',
+ isset( $module_url ) ? $module_url : includes_url( "blocks/image/view{$suffix}.js" ),
+ array( '@wordpress/interactivity' ),
+ defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' )
+ );
+
+ wp_enqueue_script_module( '@wordpress/block-library/image' );
+
/*
- * This render needs to happen in a filter with priority 15 to ensure
- * that it runs after the duotone filter and that duotone styles are
- * applied to the image in the lightbox. We also need to ensure that the
- * lightbox works with any plugins that might use filters as well. We
- * can consider removing this in the future if the way the blocks are
- * rendered changes, or if a new kind of filter is introduced.
+ * This render needs to happen in a filter with priority 15 to ensure that
+ * it runs after the duotone filter and that duotone styles are applied to
+ * the image in the lightbox. Lightbox has to work with any plugins that
+ * might use filters as well. Removing this can be considered in the future
+ * if the way the blocks are rendered changes, or if a new kind of filter is
+ * introduced.
*/
add_filter( 'render_block_core/image', 'block_core_image_render_lightbox', 15, 2 );
} else {
/*
- * Remove the filter and the JavaScript view file if previously added by
- * other Image blocks.
+ * Remove the filter if previously added by other Image blocks.
*/
remove_filter( 'render_block_core/image', 'block_core_image_render_lightbox', 15 );
- // If the script is not needed, and it is still in the `view_script_handles`, remove it.
- if ( in_array( $view_js_file_handle, $script_handles, true ) ) {
- $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file_handle ) );
- }
}
- return $processor->get_updated_html();
+ return $p->get_updated_html();
}
/**
@@ -90,15 +90,9 @@ function render_block_core_image( $attributes, $content, $block ) {
* @return array Filtered block data.
*/
function block_core_image_get_lightbox_settings( $block ) {
- // Get the lightbox setting from the block attributes.
+ // Gets the lightbox setting from the block attributes.
if ( isset( $block['attrs']['lightbox'] ) ) {
$lightbox_settings = $block['attrs']['lightbox'];
- // If the lightbox setting is not set in the block attributes,
- // check the legacy lightbox settings that are set using the
- // `gutenberg_should_render_lightbox` filter.
- // We can remove this elseif statement when the legacy lightbox settings are removed.
- } elseif ( isset( $block['legacyLightboxSettings'] ) ) {
- $lightbox_settings = $block['legacyLightboxSettings'];
}
if ( ! isset( $lightbox_settings ) ) {
@@ -107,9 +101,9 @@ function block_core_image_get_lightbox_settings( $block ) {
// If not present in global settings, check the top-level global settings.
//
// NOTE: If no block-level settings are found, the previous call to
- // `wp_get_global_settings` will return the whole `theme.json`
- // structure in which case we can check if the "lightbox" key is present at
- // the top-level of the global settings and use its value.
+ // `wp_get_global_settings` will return the whole `theme.json` structure in
+ // which case we can check if the "lightbox" key is present at the top-level
+ // of the global settings and use its value.
if ( isset( $lightbox_settings['lightbox'] ) ) {
$lightbox_settings = wp_get_global_settings( array( 'lightbox' ) );
}
@@ -128,107 +122,76 @@ function block_core_image_get_lightbox_settings( $block ) {
*/
function block_core_image_render_lightbox( $block_content, $block ) {
/*
- * If it's not possible that an IMG element exists then return the given
- * block content as-is. It may be that there's no actual image in the block
- * or it could be that another plugin already modified this HTML.
+ * If there's no IMG tag in the block then return the given block content
+ * as-is. There's nothing that this code can knowingly modify to add the
+ * lightbox behavior.
*/
- if ( false === stripos( $block_content, '<img' ) ) {
- return $block_content;
+ $p = new WP_HTML_Tag_Processor( $block_content );
+ if ( $p->next_tag( 'figure' ) ) {
+ $p->set_bookmark( 'figure' );
}
-
- $processor = new WP_HTML_Tag_Processor( $block_content );
-
- $aria_label = __( 'Enlarge image' );
-
- /*
- * If there's definitely no IMG element in the block then return the given
- * block content as-is. There's nothing that this code can knowingly modify
- * to add the lightbox behavior.
- */
- if ( ! $processor->next_tag( 'img' ) ) {
+ if ( ! $p->next_tag( 'img' ) ) {
return $block_content;
}
- $alt_attribute = $processor->get_attribute( 'alt' );
+ $alt = $p->get_attribute( 'alt' );
+ $img_uploaded_src = $p->get_attribute( 'src' );
+ $img_class_names = $p->get_attribute( 'class' );
+ $img_styles = $p->get_attribute( 'style' );
+ $img_width = 'none';
+ $img_height = 'none';
+ $aria_label = __( 'Enlarge image' );
- // An empty alt attribute `alt=""` is valid for decorative images.
- if ( is_string( $alt_attribute ) ) {
- $alt_attribute = trim( $alt_attribute );
- }
-
- // It only makes sense to append the alt text to the button aria-label when the alt text is non-empty.
- if ( $alt_attribute ) {
+ if ( $alt ) {
/* translators: %s: Image alt text. */
- $aria_label = sprintf( __( 'Enlarge image: %s' ), $alt_attribute );
+ $aria_label = sprintf( __( 'Enlarge image: %s' ), $alt );
}
- // Currently, we are only enabling the zoom animation.
- $lightbox_animation = 'zoom';
-
- // Note: We want to store the `src` in the context so we
- // can set it dynamically when the lightbox is opened.
if ( isset( $block['attrs']['id'] ) ) {
$img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] );
$img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] );
$img_width = $img_metadata['width'] ?? 'none';
$img_height = $img_metadata['height'] ?? 'none';
- } else {
- $img_uploaded_src = $processor->get_attribute( 'src' );
- $img_width = 'none';
- $img_height = 'none';
- }
-
- if ( isset( $block['attrs']['scale'] ) ) {
- $scale_attr = $block['attrs']['scale'];
- } else {
- $scale_attr = false;
}
- $w = new WP_HTML_Tag_Processor( $block_content );
- $w->next_tag( 'figure' );
- $w->add_class( 'wp-lightbox-container' );
- $w->set_attribute( 'data-wp-interactive', true );
-
- $w->set_attribute(
+ // Figure.
+ $p->seek( 'figure' );
+ $figure_class_names = $p->get_attribute( 'class' );
+ $figure_styles = $p->get_attribute( 'style' );
+ $p->add_class( 'wp-lightbox-container' );
+ $p->set_attribute( 'data-wp-interactive', 'core/image' );
+ $p->set_attribute(
'data-wp-context',
- sprintf(
- '{ "core":
- { "image":
- { "imageLoaded": false,
- "initialized": false,
- "lightboxEnabled": false,
- "hideAnimationEnabled": false,
- "preloadInitialized": false,
- "lightboxAnimation": "%s",
- "imageUploadedSrc": "%s",
- "imageCurrentSrc": "",
- "targetWidth": "%s",
- "targetHeight": "%s",
- "scaleAttr": "%s",
- "dialogLabel": "%s"
- }
- }
- }',
- $lightbox_animation,
- $img_uploaded_src,
- $img_width,
- $img_height,
- $scale_attr,
- __( 'Enlarged image' )
+ wp_json_encode(
+ array(
+ 'uploadedSrc' => $img_uploaded_src,
+ 'figureClassNames' => $figure_class_names,
+ 'figureStyles' => $figure_styles,
+ 'imgClassNames' => $img_class_names,
+ 'imgStyles' => $img_styles,
+ 'targetWidth' => $img_width,
+ 'targetHeight' => $img_height,
+ 'scaleAttr' => $block['attrs']['scale'] ?? false,
+ 'ariaLabel' => $aria_label,
+ 'alt' => $alt,
+ ),
+ JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
);
- $w->next_tag( 'img' );
- $w->set_attribute( 'data-wp-init', 'effects.core.image.initOriginImage' );
- $w->set_attribute( 'data-wp-on--load', 'actions.core.image.handleLoad' );
- $w->set_attribute( 'data-wp-effect', 'effects.core.image.setButtonStyles' );
- // We need to set an event callback on the `img` specifically
- // because the `figure` element can also contain a caption, and
- // we don't want to trigger the lightbox when the caption is clicked.
- $w->set_attribute( 'data-wp-on--click', 'actions.core.image.showLightbox' );
- $w->set_attribute( 'data-wp-effect--setStylesOnResize', 'effects.core.image.setStylesOnResize' );
- $body_content = $w->get_updated_html();
- // Add a button alongside image in the body content.
+ // Image.
+ $p->next_tag( 'img' );
+ $p->set_attribute( 'data-wp-init', 'callbacks.setButtonStyles' );
+ $p->set_attribute( 'data-wp-on--load', 'callbacks.setButtonStyles' );
+ $p->set_attribute( 'data-wp-on-window--resize', 'callbacks.setButtonStyles' );
+ // Sets an event callback on the `img` because the `figure` element can also
+ // contain a caption, and we don't want to trigger the lightbox when the
+ // caption is clicked.
+ $p->set_attribute( 'data-wp-on--click', 'actions.showLightbox' );
+
+ $body_content = $p->get_updated_html();
+
+ // Adds a button alongside image in the body content.
$img = null;
preg_match( '/<img[^>]+>/', $body_content, $img );
@@ -239,9 +202,10 @@ function block_core_image_render_lightbox( $block_content, $block ) {
type="button"
aria-haspopup="dialog"
aria-label="' . esc_attr( $aria_label ) . '"
- data-wp-on--click="actions.core.image.showLightbox"
- data-wp-style--right="context.core.image.imageButtonRight"
- data-wp-style--top="context.core.image.imageButtonTop"
+ data-wp-init="callbacks.initTriggerButton"
+ data-wp-on--click="actions.showLightbox"
+ data-wp-style--right="context.imageButtonRight"
+ data-wp-style--top="context.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
@@ -250,46 +214,17 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$body_content = preg_replace( '/<img[^>]+>/', $button, $body_content );
- // We need both a responsive image and an enlarged image to animate
- // the zoom seamlessly on slow internet connections; the responsive
- // image is a copy of the one in the body, which animates immediately
- // as the lightbox is opened, while the enlarged one is a full-sized
- // version that will likely still be loading as the animation begins.
- $m = new WP_HTML_Tag_Processor( $block_content );
- $m->next_tag( 'figure' );
- $m->add_class( 'responsive-image' );
- $m->next_tag( 'img' );
- // We want to set the 'src' attribute to an empty string in the responsive image
- // because otherwise, as of this writing, the wp_filter_content_tags() function in
- // WordPress will automatically add a 'srcset' attribute to the image, which will at
- // times cause the incorrectly sized image to be loaded in the lightbox on Firefox.
- // Because of this, we bind the 'src' attribute explicitly the current src to reliably
- // use the exact same image as in the content when the lightbox is first opened while
- // we wait for the larger image to load.
- $m->set_attribute( 'src', '' );
- $m->set_attribute( 'data-wp-bind--src', 'context.core.image.imageCurrentSrc' );
- $m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
- $initial_image_content = $m->get_updated_html();
+ add_action( 'wp_footer', 'block_core_image_print_lightbox_overlay' );
- $q = new WP_HTML_Tag_Processor( $block_content );
- $q->next_tag( 'figure' );
- $q->add_class( 'enlarged-image' );
- $q->next_tag( 'img' );
+ return $body_content;
+}
- // We set the 'src' attribute to an empty string to prevent the browser from loading the image
- // on initial page load, then bind the attribute to a selector that returns the full-sized image src when
- // the lightbox is opened. We could use 'loading=lazy' in combination with the 'hidden' attribute to
- // accomplish the same behavior, but that approach breaks progressive loading of the image in Safari
- // and Chrome (see https://github.com/WordPress/gutenberg/pull/52765#issuecomment-1674008151). Until that
- // is resolved, manually setting the 'src' seems to be the best solution to load the large image on demand.
- $q->set_attribute( 'src', '' );
- $q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' );
- $q->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
- $enlarged_image_content = $q->get_updated_html();
+function block_core_image_print_lightbox_overlay() {
+ $close_button_label = esc_attr__( 'Close' );
- // If the current theme does NOT have a `theme.json`, or the colors are not defined,
- // we need to set the background color & close button color to some default values
- // because we can't get them from the Global Styles.
+ // If the current theme does NOT have a `theme.json`, or the colors are not
+ // defined, it needs to set the background color & close button color to some
+ // default values because it can't get them from the Global Styles.
$background_color = '#fff';
$close_button_color = '#000';
if ( wp_theme_has_theme_json() ) {
@@ -302,56 +237,45 @@ function block_core_image_render_lightbox( $block_content, $block ) {
}
}
- $close_button_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>';
- $close_button_label = esc_attr__( 'Close' );
-
- $lightbox_html = <<<HTML
- <div data-wp-body="" class="wp-lightbox-overlay $lightbox_animation"
- data-wp-bind--role="selectors.core.image.roleAttribute"
- data-wp-bind--aria-label="selectors.core.image.dialogLabel"
- data-wp-class--initialized="context.core.image.initialized"
- data-wp-class--active="context.core.image.lightboxEnabled"
- data-wp-class--hideAnimationEnabled="context.core.image.hideAnimationEnabled"
- data-wp-bind--aria-modal="selectors.core.image.ariaModal"
- data-wp-effect="effects.core.image.initLightbox"
- data-wp-on--keydown="actions.core.image.handleKeydown"
- data-wp-on--touchstart="actions.core.image.handleTouchStart"
- data-wp-on--touchmove="actions.core.image.handleTouchMove"
- data-wp-on--touchend="actions.core.image.handleTouchEnd"
- data-wp-on--click="actions.core.image.hideLightbox"
- tabindex="-1"
- >
- <button type="button" aria-label="$close_button_label" style="fill: $close_button_color" class="close-button" data-wp-on--click="actions.core.image.hideLightbox">
- $close_button_icon
- </button>
- <div class="lightbox-image-container">$initial_image_content</div>
- <div class="lightbox-image-container">$enlarged_image_content</div>
- <div class="scrim" style="background-color: $background_color" aria-hidden="true"></div>
- </div>
+ echo <<<HTML
+ <div
+ class="wp-lightbox-overlay zoom"
+ data-wp-interactive="core/image"
+ data-wp-context='{}'
+ data-wp-bind--role="state.roleAttribute"
+ data-wp-bind--aria-label="state.currentImage.ariaLabel"
+ data-wp-bind--aria-modal="state.ariaModal"
+ data-wp-class--active="state.overlayEnabled"
+ data-wp-class--show-closing-animation="state.showClosingAnimation"
+ data-wp-watch="callbacks.setOverlayFocus"
+ data-wp-on--keydown="actions.handleKeydown"
+ data-wp-on--touchstart="actions.handleTouchStart"
+ data-wp-on--touchmove="actions.handleTouchMove"
+ data-wp-on--touchend="actions.handleTouchEnd"
+ data-wp-on--click="actions.hideLightbox"
+ data-wp-on-window--resize="callbacks.setOverlayStyles"
+ data-wp-on-window--scroll="actions.handleScroll"
+ tabindex="-1"
+ >
+ <button type="button" aria-label="$close_button_label" style="fill: $close_button_color" class="close-button">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>
+ </button>
+ <div class="lightbox-image-container">
+ <figure data-wp-bind--class="state.currentImage.figureClassNames" data-wp-bind--style="state.currentImage.figureStyles">
+ <img data-wp-bind--alt="state.currentImage.alt" data-wp-bind--class="state.currentImage.imgClassNames" data-wp-bind--style="state.imgStyles" data-wp-bind--src="state.currentImage.currentSrc">
+ </figure>
+ </div>
+ <div class="lightbox-image-container">
+ <figure data-wp-bind--class="state.currentImage.figureClassNames" data-wp-bind--style="state.currentImage.figureStyles">
+ <img data-wp-bind--alt="state.currentImage.alt" data-wp-bind--class="state.currentImage.imgClassNames" data-wp-bind--style="state.imgStyles" data-wp-bind--src="state.enlargedSrc">
+ </figure>
+ </div>
+ <div class="scrim" style="background-color: $background_color" aria-hidden="true"></div>
+ <style data-wp-text="state.overlayStyles"></style>
+ </div>
HTML;
-
- return str_replace( '</figure>', $lightbox_html . '</figure>', $body_content );
-}
-
-/**
- * Ensures that the view script has the `wp-interactivity` dependency.
- *
- * @since 6.4.0
- *
- * @global WP_Scripts $wp_scripts
- */
-function block_core_image_ensure_interactivity_dependency() {
- global $wp_scripts;
- if (
- isset( $wp_scripts->registered['wp-block-image-view'] ) &&
- ! in_array( 'wp-interactivity', $wp_scripts->registered['wp-block-image-view']->deps, true )
- ) {
- $wp_scripts->registered['wp-block-image-view']->deps[] = 'wp-interactivity';
- }
}
-add_action( 'wp_print_scripts', 'block_core_image_ensure_interactivity_dependency' );
-
/**
* Registers the `core/image` block on server.
*/