add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.js', array(), $tinymce_version );
} else {
$scripts->add( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce$dev_suffix.js", array(), $tinymce_version );
$scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin$dev_suffix.js", array( 'wp-tinymce-root' ), $tinymce_version );
}
$scripts->add( 'wp-tinymce-lists', includes_url( "js/tinymce/plugins/lists/plugin$suffix.js" ), array( 'wp-tinymce' ), $tinymce_version );
}
/**
* Registers all the WordPress vendor scripts that are in the standardized
* `js/dist/vendor/` location.
*
* For the order of `$scripts->add` see `wp_default_scripts`.
*
* @since 5.0.0
*
* @global WP_Locale $wp_locale WordPress date and time locale object.
*
* @param WP_Scripts $scripts WP_Scripts object.
*/
function wp_default_packages_vendor( $scripts ) {
global $wp_locale;
$suffix = wp_scripts_get_suffix();
$vendor_scripts = array(
'react',
'react-dom' => array( 'react' ),
'react-jsx-runtime' => array( 'react' ),
'regenerator-runtime',
'moment',
'lodash',
'wp-polyfill-fetch',
'wp-polyfill-formdata',
'wp-polyfill-node-contains',
'wp-polyfill-url',
'wp-polyfill-dom-rect',
'wp-polyfill-element-closest',
'wp-polyfill-object-fit',
'wp-polyfill-inert',
'wp-polyfill',
);
$vendor_scripts_versions = array(
'react' => '18.3.1.1', // Final .1 due to switch to UMD build, can be removed in the next update.
'react-dom' => '18.3.1.1', // Final .1 due to switch to UMD build, can be removed in the next update.
'react-jsx-runtime' => '18.3.1',
'regenerator-runtime' => '0.14.1',
'moment' => '2.30.1',
'lodash' => '4.17.21',
'wp-polyfill-fetch' => '3.6.20',
'wp-polyfill-formdata' => '4.0.10',
'wp-polyfill-node-contains' => '4.8.0',
'wp-polyfill-url' => '3.6.4',
'wp-polyfill-dom-rect' => '4.8.0',
'wp-polyfill-element-closest' => '3.0.2',
'wp-polyfill-object-fit' => '2.3.5',
'wp-polyfill-inert' => '3.1.3',
'wp-polyfill' => '3.15.0',
);
foreach ( $vendor_scripts as $handle => $dependencies ) {
if ( is_string( $dependencies ) ) {
$handle = $dependencies;
$dependencies = array();
}
$path = "/wp-includes/js/dist/vendor/$handle$suffix.js";
$version = $vendor_scripts_versions[ $handle ];
$scripts->add( $handle, $path, $dependencies, $version, 1 );
}
did_action( 'init' ) && $scripts->add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
did_action( 'init' ) && $scripts->add_inline_script(
'moment',
sprintf(
"moment.updateLocale( '%s', %s );",
esc_js( get_user_locale() ),
wp_json_encode(
array(
'months' => array_values( $wp_locale->month ),
'monthsShort' => array_values( $wp_locale->month_abbrev ),
'weekdays' => array_values( $wp_locale->weekday ),
'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
'week' => array(
'dow' => (int) get_option( 'start_of_week', 0 ),
),
'longDateFormat' => array(
'LT' => get_option( 'time_format', __( 'g:i a' ) ),
'LTS' => null,
'L' => null,
'LL' => get_option( 'date_format', __( 'F j, Y' ) ),
'LLL' => __( 'F j, Y g:i a' ),
'LLLL' => null,
),
),
JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
)
),
'after'
);
}
/**
* Returns contents of an inline script used in appending polyfill scripts for
* browsers which fail the provided tests. The provided array is a mapping from
* a condition to verify feature support to its polyfill script handle.
*
* @since 5.0.0
*
* @param WP_Scripts $scripts WP_Scripts object.
* @param string[] $tests Features to detect.
* @return string Conditional polyfill inline script.
*/
function wp_get_script_polyfill( $scripts, $tests ) {
$polyfill = '';
foreach ( $tests as $test => $handle ) {
if ( ! array_key_exists( $handle, $scripts->registered ) ) {
continue;
}
$src = $scripts->registered[ $handle ]->src;
$ver = $scripts->registered[ $handle ]->ver;
if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $scripts->content_url && str_starts_with( $src, $scripts->content_url ) ) ) {
$src = $scripts->base_url . $src;
}
if ( ! empty( $ver ) ) {
$src = add_query_arg( 'ver', $ver, $src );
}
/** This filter is documented in wp-includes/class-wp-scripts.php */
$src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) );
if ( ! $src ) {
continue;
}
$polyfill .= (
// Test presence of feature...
'( ' . $test . ' ) || ' .
/*
* ...appending polyfill on any failures. Cautious viewers may balk
* at the `document.write`. Its caveat of synchronous mid-stream
* blocking write is exactly the behavior we need though.
*/
'document.write( \'\n";
}
$concat = str_split( $concat, 128 );
$concatenated = '';
foreach ( $concat as $key => $chunk ) {
$concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}";
}
$src = $wp_scripts->base_url . "/wp-admin/load-scripts.php?c={$zip}" . $concatenated . '&ver=' . $wp_scripts->default_version;
echo "\n";
}
if ( ! empty( $wp_scripts->print_html ) ) {
echo $wp_scripts->print_html;
}
}
/**
* Prints the script queue in the HTML head on the front end.
*
* Postpones the scripts that were queued for the footer.
* wp_print_footer_scripts() is called in the footer to print these scripts.
*
* @since 2.8.0
*
* @global WP_Scripts $wp_scripts
*
* @return string[] Handles of the scripts that were printed.
*/
function wp_print_head_scripts() {
global $wp_scripts;
if ( ! did_action( 'wp_print_scripts' ) ) {
/** This action is documented in wp-includes/functions.wp-scripts.php */
do_action( 'wp_print_scripts' );
}
if ( ! ( $wp_scripts instanceof WP_Scripts ) ) {
return array(); // No need to run if nothing is queued.
}
return print_head_scripts();
}
/**
* Private, for use in *_footer_scripts hooks
*
* In classic themes, when block styles are loaded on demand via wp_load_classic_theme_block_styles_on_demand(),
* this function is replaced by a closure in wp_hoist_late_printed_styles() which will capture the printing of
* two sets of "late" styles to be hoisted to the HEAD by means of the template enhancement output buffer:
*
* 1. Styles related to blocks are inserted right after the wp-block-library stylesheet.
* 2. All other styles are appended to the end of the HEAD.
*
* The closure calls print_footer_scripts() to print scripts in the footer as usual.
*
* @since 3.3.0
*/
function _wp_footer_scripts() {
print_late_styles();
print_footer_scripts();
}
/**
* Hooks to print the scripts and styles in the footer.
*
* @since 2.8.0
*/
function wp_print_footer_scripts() {
/**
* Fires when footer scripts are printed.
*
* @since 2.8.0
*/
do_action( 'wp_print_footer_scripts' );
}
/**
* Wrapper for do_action( 'wp_enqueue_scripts' ).
*
* Allows plugins to queue scripts for the front end using wp_enqueue_script().
* Runs first in wp_head() where all is_home(), is_page(), etc. functions are available.
*
* @since 2.8.0
*/
function wp_enqueue_scripts() {
/**
* Fires when scripts and styles are enqueued.
*
* @since 2.8.0
*/
do_action( 'wp_enqueue_scripts' );
}
/**
* Prints the styles queue in the HTML head on admin pages.
*
* @since 2.8.0
*
* @global bool $concatenate_scripts
*
* @return string[] Handles of the styles that were printed.
*/
function print_admin_styles() {
global $concatenate_scripts;
$wp_styles = wp_styles();
script_concat_settings();
$wp_styles->do_concat = $concatenate_scripts;
$wp_styles->do_items( false );
/**
* Filters whether to print the admin styles.
*
* @since 2.8.0
*
* @param bool $print Whether to print the admin styles. Default true.
*/
if ( apply_filters( 'print_admin_styles', true ) ) {
_print_styles();
}
$wp_styles->reset();
return $wp_styles->done;
}
/**
* Prints the styles that were queued too late for the HTML head.
*
* @since 3.3.0
*
* @global WP_Styles $wp_styles
* @global bool $concatenate_scripts
*
* @return array|void
*/
function print_late_styles() {
global $wp_styles, $concatenate_scripts;
if ( ! ( $wp_styles instanceof WP_Styles ) ) {
return;
}
script_concat_settings();
$wp_styles->do_concat = $concatenate_scripts;
$wp_styles->do_footer_items();
/**
* Filters whether to print the styles queued too late for the HTML head.
*
* @since 3.3.0
*
* @param bool $print Whether to print the 'late' styles. Default true.
*/
if ( apply_filters( 'print_late_styles', true ) ) {
_print_styles();
}
$wp_styles->reset();
return $wp_styles->done;
}
/**
* Prints styles (internal use only).
*
* @ignore
* @since 3.3.0
*
* @global bool $compress_css
*/
function _print_styles() {
global $compress_css;
$wp_styles = wp_styles();
$zip = $compress_css ? 1 : 0;
if ( $zip && defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP ) {
$zip = 'gzip';
}
$concat = trim( $wp_styles->concat, ', ' );
$type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
if ( $concat ) {
$dir = $wp_styles->text_direction;
$ver = $wp_styles->default_version;
$concat_source_url = 'css-inline-concat-' . $concat;
$concat = str_split( $concat, 128 );
$concatenated = '';
foreach ( $concat as $key => $chunk ) {
$concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}";
}
$href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}" . $concatenated . '&ver=' . $ver;
echo "\n";
if ( ! empty( $wp_styles->print_code ) ) {
echo "\n";
}
}
if ( ! empty( $wp_styles->print_html ) ) {
echo $wp_styles->print_html;
}
}
/**
* Determines the concatenation and compression settings for scripts and styles.
*
* @since 2.8.0
*
* @global bool $concatenate_scripts
* @global bool $compress_scripts
* @global bool $compress_css
*/
function script_concat_settings() {
global $concatenate_scripts, $compress_scripts, $compress_css;
$compressed_output = ( ini_get( 'zlib.output_compression' ) || 'ob_gzhandler' === ini_get( 'output_handler' ) );
$can_compress_scripts = ! wp_installing() && get_site_option( 'can_compress_scripts' );
if ( ! isset( $concatenate_scripts ) ) {
$concatenate_scripts = defined( 'CONCATENATE_SCRIPTS' ) ? CONCATENATE_SCRIPTS : true;
if ( ( ! is_admin() && ! did_action( 'login_init' ) ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) {
$concatenate_scripts = false;
}
}
if ( ! isset( $compress_scripts ) ) {
$compress_scripts = defined( 'COMPRESS_SCRIPTS' ) ? COMPRESS_SCRIPTS : true;
if ( $compress_scripts && ( ! $can_compress_scripts || $compressed_output ) ) {
$compress_scripts = false;
}
}
if ( ! isset( $compress_css ) ) {
$compress_css = defined( 'COMPRESS_CSS' ) ? COMPRESS_CSS : true;
if ( $compress_css && ( ! $can_compress_scripts || $compressed_output ) ) {
$compress_css = false;
}
}
}
/**
* Handles the enqueueing of block scripts and styles that are common to both
* the editor and the front-end.
*
* @since 5.0.0
*/
function wp_common_block_scripts_and_styles() {
if ( is_admin() && ! wp_should_load_block_editor_scripts_and_styles() ) {
return;
}
wp_enqueue_style( 'wp-block-library' );
if ( current_theme_supports( 'wp-block-styles' ) && ! wp_should_load_separate_core_block_assets() ) {
wp_enqueue_style( 'wp-block-library-theme' );
}
/**
* Fires after enqueuing block assets for both editor and front-end.
*
* Call `add_action` on any hook before 'wp_enqueue_scripts'.
*
* In the function call you supply, simply use `wp_enqueue_script` and
* `wp_enqueue_style` to add your functionality to the Gutenberg editor.
*
* @since 5.0.0
*/
do_action( 'enqueue_block_assets' );
}
/**
* Applies a filter to the list of style nodes that comes from WP_Theme_JSON::get_style_nodes().
*
* This particular filter removes all of the blocks from the array.
*
* We want WP_Theme_JSON to be ignorant of the implementation details of how the CSS is being used.
* This filter allows us to modify the output of WP_Theme_JSON depending on whether or not we are
* loading separate assets, without making the class aware of that detail.
*
* @since 6.1.0
*
* @param array $nodes The nodes to filter.
* @return array A filtered array of style nodes.
*/
function wp_filter_out_block_nodes( $nodes ) {
return array_filter(
$nodes,
static function ( $node ) {
return ! in_array( 'blocks', $node['path'], true );
},
ARRAY_FILTER_USE_BOTH
);
}
/**
* Enqueues the global styles defined via theme.json.
*
* @since 5.8.0
*/
function wp_enqueue_global_styles() {
$assets_on_demand = wp_should_load_block_assets_on_demand();
$is_block_theme = wp_is_block_theme();
$is_classic_theme = ! $is_block_theme;
/*
* Global styles should be printed in the head for block themes, or for classic themes when loading assets on
* demand is disabled, which is the default.
* The footer should only be used for classic themes when loading assets on demand is enabled.
*
* See https://core.trac.wordpress.org/ticket/53494 and https://core.trac.wordpress.org/ticket/61965.
*/
if (
( $is_block_theme && doing_action( 'wp_footer' ) ) ||
( $is_classic_theme && doing_action( 'wp_footer' ) && ! $assets_on_demand ) ||
( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) && $assets_on_demand )
) {
return;
}
/*
* If loading the CSS for each block separately, then load the theme.json CSS conditionally.
* This removes the CSS from the global-styles stylesheet and adds it to the inline CSS for each block.
* This filter must be registered before calling wp_get_global_stylesheet();
*/
add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
$stylesheet = wp_get_global_stylesheet();
if ( $is_block_theme ) {
/*
* Dequeue the Customizer's custom CSS
* and add it before the global styles custom CSS.
*/
remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
/*
* Get the custom CSS from the Customizer and add it to the global stylesheet.
* Always do this in Customizer preview for the sake of live preview since it be empty.
*/
$custom_css = trim( wp_get_custom_css() );
if ( $custom_css || is_customize_preview() ) {
if ( is_customize_preview() ) {
/*
* When in the Customizer preview, wrap the Custom CSS in milestone comments to allow customize-preview.js
* to locate the CSS to replace for live previewing. Make sure that the milestone comments are omitted from
* the stored Custom CSS if by chance someone tried to add them, which would be highly unlikely, but it
* would break live previewing.
*/
$before_milestone = '/*BEGIN_CUSTOMIZER_CUSTOM_CSS*/';
$after_milestone = '/*END_CUSTOMIZER_CUSTOM_CSS*/';
$custom_css = str_replace( array( $before_milestone, $after_milestone ), '', $custom_css );
$custom_css = $before_milestone . "\n" . $custom_css . "\n" . $after_milestone;
}
$custom_css = "\n" . $custom_css;
}
$stylesheet .= $custom_css;
// Add the global styles custom CSS at the end.
$stylesheet .= wp_get_global_stylesheet( array( 'custom-css' ) );
}
if ( empty( $stylesheet ) ) {
return;
}
wp_register_style( 'global-styles', false );
wp_add_inline_style( 'global-styles', $stylesheet );
wp_enqueue_style( 'global-styles' );
// Add each block as an inline css.
wp_add_global_styles_for_blocks();
}
/**
* Checks if the editor scripts and styles for all registered block types
* should be enqueued on the current screen.
*
* @since 5.6.0
*
* @global WP_Screen $current_screen WordPress current screen object.
*
* @return bool Whether scripts and styles should be enqueued.
*/
function wp_should_load_block_editor_scripts_and_styles() {
global $current_screen;
$is_block_editor_screen = ( $current_screen instanceof WP_Screen ) && $current_screen->is_block_editor();
/**
* Filters the flag that decides whether or not block editor scripts and styles
* are going to be enqueued on the current screen.
*
* @since 5.6.0
*
* @param bool $is_block_editor_screen Current value of the flag.
*/
return apply_filters( 'should_load_block_editor_scripts_and_styles', $is_block_editor_screen );
}
/**
* Checks whether separate styles should be loaded for core blocks.
*
* When this function returns true, other functions ensure that core blocks use their own separate stylesheets.
* When this function returns false, all core blocks will use the single combined 'wp-block-library' stylesheet.
*
* As a side effect, the return value will by default result in block assets to be loaded on demand, via the
* {@see wp_should_load_block_assets_on_demand()} function. This behavior can be separately altered via that function.
*
* This only affects front end and not the block editor screens.
*
* @since 5.8.0
* @see @see wp_should_load_block_assets_on_demand()
* @see wp_enqueue_registered_block_scripts_and_styles()
* @see register_block_style_handle()
*
* @return bool Whether separate core block assets will be loaded.
*/
function wp_should_load_separate_core_block_assets() {
if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
return false;
}
/**
* Filters whether block styles should be loaded separately.
*
* Returning false loads all core block assets, regardless of whether they are rendered
* in a page or not. Returning true loads core block assets only when they are rendered.
*
* @since 5.8.0
*
* @param bool $load_separate_assets Whether separate assets will be loaded.
* Default false (all block assets are loaded, even when not used).
*/
return apply_filters( 'should_load_separate_core_block_assets', false );
}
/**
* Checks whether block styles should be loaded only on-render.
*
* When this function returns true, other functions ensure that blocks only load their assets on-render.
* When this function returns false, all block assets are loaded regardless of whether they are rendered in a page.
*
* The default return value depends on the result of {@see wp_should_load_separate_core_block_assets()}, which controls
* whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library' stylesheet.
*
* This only affects front end and not the block editor screens.
*
* @since 6.8.0
* @see wp_should_load_separate_core_block_assets()
*
* @return bool Whether to load block assets only when they are rendered.
*/
function wp_should_load_block_assets_on_demand() {
if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
return false;
}
/*
* For backward compatibility, the default return value for this function is based on the return value of
* `wp_should_load_separate_core_block_assets()`. Initially, this function used to control both of these concerns.
*/
$load_assets_on_demand = wp_should_load_separate_core_block_assets();
/**
* Filters whether block styles should be loaded on demand.
*
* Returning false loads all block assets, regardless of whether they are rendered in a page or not.
* Returning true loads block assets only when they are rendered.
*
* The default value of the filter depends on the result of {@see wp_should_load_separate_core_block_assets()},
* which controls whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library'
* stylesheet.
*
* @since 6.8.0
*
* @param bool $load_assets_on_demand Whether to load block assets only when they are rendered.
*/
return apply_filters( 'should_load_block_assets_on_demand', $load_assets_on_demand );
}
/**
* Enqueues registered block scripts and styles, depending on current rendered
* context (only enqueuing editor scripts while in context of the editor).
*
* @since 5.0.0
*/
function wp_enqueue_registered_block_scripts_and_styles() {
if ( wp_should_load_block_assets_on_demand() ) {
return;
}
$load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles();
$block_registry = WP_Block_Type_Registry::get_instance();
/*
* Block styles are only enqueued if they're registered. For core blocks, this is only the case if
* `wp_should_load_separate_core_block_assets()` returns true. Otherwise they use the single combined
* 'wp-block-library` stylesheet. See also `register_core_block_style_handles()`.
* Since `wp_enqueue_style()` does not trigger warnings if the style is not registered, it is okay to not cater for
* this behavior here and simply call `wp_enqueue_style()` unconditionally.
*/
foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
// Front-end and editor styles.
foreach ( $block_type->style_handles as $style_handle ) {
wp_enqueue_style( $style_handle );
}
// Front-end and editor scripts.
foreach ( $block_type->script_handles as $script_handle ) {
wp_enqueue_script( $script_handle );
}
if ( $load_editor_scripts_and_styles ) {
// Editor styles.
foreach ( $block_type->editor_style_handles as $editor_style_handle ) {
wp_enqueue_style( $editor_style_handle );
}
// Editor scripts.
foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
wp_enqueue_script( $editor_script_handle );
}
}
}
}
/**
* Function responsible for enqueuing the styles required for block styles functionality on the editor and on the frontend.
*
* @since 5.3.0
*
* @global WP_Styles $wp_styles
*/
function enqueue_block_styles_assets() {
global $wp_styles;
$block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
foreach ( $block_styles as $block_name => $styles ) {
foreach ( $styles as $style_properties ) {
if ( isset( $style_properties['style_handle'] ) ) {
// If the site loads block styles on demand, enqueue the stylesheet on render.
if ( wp_should_load_block_assets_on_demand() ) {
add_filter(
'render_block',
static function ( $html, $block ) use ( $block_name, $style_properties ) {
if ( $block['blockName'] === $block_name ) {
wp_enqueue_style( $style_properties['style_handle'] );
}
return $html;
},
10,
2
);
} else {
wp_enqueue_style( $style_properties['style_handle'] );
}
}
if ( isset( $style_properties['inline_style'] ) ) {
// Default to "wp-block-library".
$handle = 'wp-block-library';
// If the site loads block styles on demand, check if the block has a stylesheet registered.
if ( wp_should_load_block_assets_on_demand() ) {
$block_stylesheet_handle = generate_block_asset_handle( $block_name, 'style' );
if ( isset( $wp_styles->registered[ $block_stylesheet_handle ] ) ) {
$handle = $block_stylesheet_handle;
}
}
// Add inline styles to the calculated handle.
wp_add_inline_style( $handle, $style_properties['inline_style'] );
}
}
}
}
/**
* Function responsible for enqueuing the assets required for block styles functionality on the editor.
*
* @since 5.3.0
*/
function enqueue_editor_block_styles_assets() {
$block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
$register_script_lines = array( '( function() {' );
foreach ( $block_styles as $block_name => $styles ) {
foreach ( $styles as $style_properties ) {
$block_style = array(
'name' => $style_properties['name'],
'label' => $style_properties['label'],
);
if ( isset( $style_properties['is_default'] ) ) {
$block_style['isDefault'] = $style_properties['is_default'];
}
$register_script_lines[] = sprintf(
' wp.blocks.registerBlockStyle( \'%s\', %s );',
$block_name,
wp_json_encode( $block_style, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
);
}
}
$register_script_lines[] = '} )();';
$inline_script = implode( "\n", $register_script_lines );
wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, array( 'in_footer' => true ) );
wp_add_inline_script( 'wp-block-styles', $inline_script );
wp_enqueue_script( 'wp-block-styles' );
}
/**
* Enqueues the assets required for the block directory within the block editor.
*
* @since 5.5.0
*/
function wp_enqueue_editor_block_directory_assets() {
wp_enqueue_script( 'wp-block-directory' );
wp_enqueue_style( 'wp-block-directory' );
}
/**
* Enqueues the assets required for the format library within the block editor.
*
* @since 5.8.0
*/
function wp_enqueue_editor_format_library_assets() {
wp_enqueue_script( 'wp-format-library' );
wp_enqueue_style( 'wp-format-library' );
}
/**
* Sanitizes an attributes array into an attributes string to be placed inside a `\n", wp_sanitize_script_attributes( $attributes ) );
}
/**
* Prints formatted `
*
* In an HTML document this would print "…" to the console,
* but in an XHTML document it would print "…" to the console.
*
*
*
* In an HTML document this would print "An image is in HTML",
* but it's an invalid XHTML document because it interprets the ``
* as an empty tag missing its closing `/`.
*
* @see https://www.w3.org/TR/xhtml1/#h-4.8
*/
if (
! $is_html5 &&
(
! isset( $attributes['type'] ) ||
'module' === $attributes['type'] ||
str_contains( $attributes['type'], 'javascript' ) ||
str_contains( $attributes['type'], 'ecmascript' ) ||
str_contains( $attributes['type'], 'jscript' ) ||
str_contains( $attributes['type'], 'livescript' )
)
) {
/*
* If the string `]]>` exists within the JavaScript it would break
* out of any wrapping CDATA section added here, so to start, it's
* necessary to escape that sequence which requires splitting the
* content into two CDATA sections wherever it's found.
*
* Note: it's only necessary to escape the closing `]]>` because
* an additional `', ']]]]>', $data );
// Wrap the entire escaped script inside a CDATA section.
$data = sprintf( "/* */", $data );
}
$data = "\n" . trim( $data, "\n\r " ) . "\n";
/**
* Filters attributes to be added to a script tag.
*
* @since 5.7.0
*
* @param array $attributes Key-value pairs representing `\n", wp_sanitize_script_attributes( $attributes ), $data );
}
/**
* Prints an inline script tag.
*
* It is possible to inject attributes in the `" from
* around an inline script after trimming whitespace. Typically this
* is used in conjunction with output buffering, where `ob_get_clean()`
* is passed as the `$contents` argument.
*
* Example:
*
* // Strips exact literal empty SCRIPT tags.
* $js = ';
* 'sayHello();' === wp_remove_surrounding_empty_script_tags( $js );
*
* // Otherwise if anything is different it warns in the JS console.
* $js = '';
* 'console.error( ... )' === wp_remove_surrounding_empty_script_tags( $js );
*
* @since 6.4.0
* @access private
*
* @see wp_print_inline_script_tag()
* @see wp_get_inline_script_tag()
*
* @param string $contents Script body with manually created SCRIPT tag literals.
* @return string Script body without surrounding script tag literals, or
* original contents if both exact literals aren't present.
*/
function wp_remove_surrounding_empty_script_tags( $contents ) {
$contents = trim( $contents );
$opener = '';
if (
strlen( $contents ) > strlen( $opener ) + strlen( $closer ) &&
strtoupper( substr( $contents, 0, strlen( $opener ) ) ) === $opener &&
strtoupper( substr( $contents, -strlen( $closer ) ) ) === $closer
) {
return substr( $contents, strlen( $opener ), -strlen( $closer ) );
} else {
$error_message = __( 'Expected string to start with script tag (without attributes) and end with script tag, with optional whitespace.' );
_doing_it_wrong( __FUNCTION__, $error_message, '6.4' );
return sprintf(
'console.error(%s)',
wp_json_encode(
sprintf(
/* translators: %s: wp_remove_surrounding_empty_script_tags() */
__( 'Function %s used incorrectly in PHP.' ),
'wp_remove_surrounding_empty_script_tags()'
) . ' ' . $error_message
)
);
}
}
/**
* Adds hooks to load block styles on demand in classic themes.
*
* @since 6.9.0
*
* @see _add_default_theme_supports()
*/
function wp_load_classic_theme_block_styles_on_demand() {
// This is not relevant to block themes, as they are opted in to loading separate styles on demand via _add_default_theme_supports().
if ( wp_is_block_theme() ) {
return;
}
/*
* Make sure that wp_should_output_buffer_template_for_enhancement() returns true even if there aren't any
* `wp_template_enhancement_output_buffer` filters added, but do so at priority zero so that applications which
* wish to stream responses can more easily turn this off.
*/
add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
// If a site has opted out of the template enhancement output buffer, then bail.
if ( ! wp_should_output_buffer_template_for_enhancement() ) {
return;
}
// The following two filters are added by default for block themes in _add_default_theme_supports().
/*
* Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally, and so
* that block-specific styles will only be enqueued when they are used on the page. A priority of zero allows for
* this to be easily overridden by themes which wish to opt out. If a site has explicitly opted out of loading
* separate block styles, then abort.
*/
add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
if ( ! wp_should_load_separate_core_block_assets() ) {
return;
}
/*
* Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
* As above, a priority of zero allows for this to be easily overridden by themes which wish to opt out. If a site
* has explicitly opted out of loading block styles on demand, then abort.
*/
add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
if ( ! wp_should_load_block_assets_on_demand() ) {
return;
}
// Add hooks which require the presence of the output buffer. Ideally the above two filters could be added here, but they run too early.
add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
}
/**
* Adds the hooks needed for CSS output to be delayed until after the content of the page has been established.
*
* @since 6.9.0
*
* @see wp_load_classic_theme_block_styles_on_demand()
* @see _wp_footer_scripts()
*/
function wp_hoist_late_printed_styles() {
// Skip the embed template on-demand styles aren't relevant, and there is no wp_head action.
if ( is_embed() ) {
return;
}
/*
* Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles
* can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
* output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
* `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present.
*/
$placeholder = sprintf( '/*%s*/', uniqid( 'wp_block_styles_on_demand_placeholder:' ) );
add_action(
'wp_print_styles',
static function () use ( $placeholder ) {
wp_add_inline_style( 'wp-block-library', $placeholder );
}
);
/*
* Create a substitute for `print_late_styles()` which is aware of block styles. This substitute does not print
* the styles, but it captures what would be printed for block styles and non-block styles so that they can be
* later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts`
* before `print_footer_scripts()` is called.
*/
$printed_block_styles = '';
$printed_late_styles = '';
$capture_late_styles = static function () use ( &$printed_block_styles, &$printed_late_styles ) {
// Gather the styles related to on-demand block enqueues.
$all_block_style_handles = array();
foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) {
foreach ( $block_type->style_handles as $style_handle ) {
$all_block_style_handles[] = $style_handle;
}
}
$all_block_style_handles = array_merge(
$all_block_style_handles,
array(
'global-styles',
'block-style-variation-styles',
'core-block-supports',
'core-block-supports-duotone',
)
);
/*
* First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
* to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
*/
$enqueued_block_styles = array_values( array_intersect( $all_block_style_handles, wp_styles()->queue ) );
if ( count( $enqueued_block_styles ) > 0 ) {
ob_start();
wp_styles()->do_items( $enqueued_block_styles );
$printed_block_styles = ob_get_clean();
}
/*
* Print all remaining styles not related to blocks. This contains a subset of the logic from
* `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether
* late styles are printed (since they are being hoisted anyway).
*/
ob_start();
wp_styles()->do_footer_items();
$printed_late_styles = ob_get_clean();
};
/*
* If `_wp_footer_scripts()` was unhooked from the `wp_print_footer_scripts` action, or if `wp_print_footer_scripts()`
* was unhooked from running at the `wp_footer` action, then only add a callback to `wp_footer` which will capture the
* late-printed styles.
*
* Otherwise, in the normal case where `_wp_footer_scripts()` will run at the `wp_print_footer_scripts` action, then
* swap out `_wp_footer_scripts()` with an alternative which captures the printed styles (for hoisting to HEAD) before
* proceeding with printing the footer scripts.
*/
$wp_print_footer_scripts_priority = has_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
if ( false === $wp_print_footer_scripts_priority || false === has_action( 'wp_footer', 'wp_print_footer_scripts' ) ) {
// The normal priority for wp_print_footer_scripts() is to run at 20.
add_action( 'wp_footer', $capture_late_styles, 20 );
} else {
remove_action( 'wp_print_footer_scripts', '_wp_footer_scripts', $wp_print_footer_scripts_priority );
add_action(
'wp_print_footer_scripts',
static function () use ( $capture_late_styles ) {
$capture_late_styles();
print_footer_scripts();
},
$wp_print_footer_scripts_priority
);
}
// Replace placeholder with the captured late styles.
add_filter(
'wp_template_enhancement_output_buffer',
static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_late_styles ) {
// Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
$processor = new class( $buffer ) extends WP_HTML_Tag_Processor {
/**
* Gets the span for the current token.
*
* @return WP_HTML_Span Current token span.
*/
private function get_span(): WP_HTML_Span {
// Note: This call will never fail according to the usage of this class, given it is always called after ::next_tag() is true.
$this->set_bookmark( 'here' );
return $this->bookmarks['here'];
}
/**
* Inserts text before the current token.
*
* @param string $text Text to insert.
*/
public function insert_before( string $text ) {
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $this->get_span()->start, 0, $text );
}
/**
* Inserts text after the current token.
*
* @param string $text Text to insert.
*/
public function insert_after( string $text ) {
$span = $this->get_span();
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start + $span->length, 0, $text );
}
/**
* Removes the current token.
*/
public function remove() {
$span = $this->get_span();
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start, $span->length, '' );
}
};
/*
* Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles
* at (or else print everything there). The placeholder CSS comment will always be added to the
* wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered.
* This means that there may not actually be any block styles to hoist from the footer to insert after this
* inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but
* if the resulting inline style is empty after the placeholder is removed, then the inline style is
* removed.
*/
while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
if (
'STYLE' === $processor->get_tag() &&
'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
) {
$css_text = $processor->get_modifiable_text();
/*
* A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
* be printed. Now that we've located the inline style, the placeholder comment can be removed. If
* there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
* comment, then remove the STYLE entirely.)
*/
$css_text = str_replace( $placeholder, '', $css_text );
if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
$processor->remove();
} else {
$processor->set_modifiable_text( $css_text );
}
// Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
if ( '' !== $printed_block_styles ) {
$processor->insert_after( $printed_block_styles );
// Prevent printing them again at .
$printed_block_styles = '';
}
// If there aren't any late styles, there's no need to continue to finding .
if ( '' === $printed_late_styles ) {
break;
}
} elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
$processor->insert_before( $printed_block_styles . $printed_late_styles );
break;
}
}
return $processor->get_updated_html();
}
);
}
/**
* Return the corresponding JavaScript `dataset` name for an attribute
* if it represents a custom data attribute, or `null` if not.
*
* Custom data attributes appear in an element's `dataset` property in a
* browser, but there's a specific way the names are translated from HTML
* into JavaScript. This function indicates how the name would appear in
* JavaScript if a browser would recognize it as a custom data attribute.
*
* Example:
*
* // Dash-letter pairs turn into capital letters.
* 'postId' === wp_js_dataset_name( 'data-post-id' );
* 'Before' === wp_js_dataset_name( 'data--before' );
* '-One--Two---' === wp_js_dataset_name( 'data---one---two---' );
*
* // Not every attribute name will be interpreted as a custom data attribute.
* null === wp_js_dataset_name( 'post-id' );
* null === wp_js_dataset_name( 'data' );
*
* // Some very surprising names will; for example, a property whose name is the empty string.
* '' === wp_js_dataset_name( 'data-' );
* 0 === strlen( wp_js_dataset_name( 'data-' ) );
*
* @since 6.9.0
*
* @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
* @see \wp_html_custom_data_attribute_name()
*
* @param string $html_attribute_name Raw attribute name as found in the source HTML.
* @return string|null Transformed `dataset` name, if interpretable as a custom data attribute, else `null`.
*/
function wp_js_dataset_name( string $html_attribute_name ): ?string {
if ( 0 !== substr_compare( $html_attribute_name, 'data-', 0, 5, true ) ) {
return null;
}
$end = strlen( $html_attribute_name );
/*
* If it contains characters which would end the attribute name parsing then
* something else is wrong and this contains more than just an attribute name.
*/
if ( ( $end - 5 ) !== strcspn( $html_attribute_name, "=/> \t\f\r\n", 5 ) ) {
return null;
}
/**
* > For each name in list, for each U+002D HYPHEN-MINUS character (-)
* > in the name that is followed by an ASCII lower alpha, remove the
* > U+002D HYPHEN-MINUS character (-) and replace the character that
* > followed it by the same character converted to ASCII uppercase.
*
* @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
*/
$custom_name = '';
$at = 5;
$was_at = $at;
while ( $at < $end ) {
$next_dash_at = strpos( $html_attribute_name, '-', $at );
if ( false === $next_dash_at || $next_dash_at === $end - 1 ) {
break;
}
// Transform `-a` to `A`, for example.
$c = $html_attribute_name[ $next_dash_at + 1 ];
if ( ( $c >= 'A' && $c <= 'Z' ) || ( $c >= 'a' && $c <= 'z' ) ) {
$prefix = substr( $html_attribute_name, $was_at, $next_dash_at - $was_at );
$custom_name .= strtolower( $prefix );
$custom_name .= strtoupper( $c );
$at = $next_dash_at + 2;
$was_at = $at;
continue;
}
$at = $next_dash_at + 1;
}
// If nothing has been added it means there are no dash-letter pairs; return the name as-is.
return '' === $custom_name
? strtolower( substr( $html_attribute_name, 5 ) )
: ( $custom_name . strtolower( substr( $html_attribute_name, $was_at ) ) );
}
/**
* Returns a corresponding HTML attribute name for the given name,
* if that name were found in a JS element’s `dataset` property.
*
* Example:
*
* 'data-post-id' === wp_html_custom_data_attribute_name( 'postId' );
* 'data--before' === wp_html_custom_data_attribute_name( 'Before' );
* 'data---one---two---' === wp_html_custom_data_attribute_name( '-One--Two---' );
*
* // Not every attribute name will be interpreted as a custom data attribute.
* null === wp_html_custom_data_attribute_name( '/not-an-attribute/' );
* null === wp_html_custom_data_attribute_name( 'no spaces' );
*
* // Some very surprising names will; for example, a property whose name is the empty string.
* 'data-' === wp_html_custom_data_attribute_name( '' );
*
* @since 6.9.0
*
* @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
* @see \wp_js_dataset_name()
*
* @param string $js_dataset_name Name of JS `dataset` property to transform.
* @return string|null Corresponding name of an HTML custom data attribute for the given dataset name,
* if possible to represent in HTML, otherwise `null`.
*/
function wp_html_custom_data_attribute_name( string $js_dataset_name ): ?string {
$end = strlen( $js_dataset_name );
if ( 0 === $end ) {
return 'data-';
}
/*
* If it contains characters which would end the attribute name parsing then
* something it’s not possible to represent this in HTML.
*/
if ( strcspn( $js_dataset_name, "=/> \t\f\r\n" ) !== $end ) {
return null;
}
$html_name = 'data-';
$at = 0;
$was_at = $at;
while ( $at < $end ) {
$next_upper_after = strcspn( $js_dataset_name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', $at );
$next_upper_at = $at + $next_upper_after;
if ( $next_upper_at >= $end ) {
break;
}
$prefix = substr( $js_dataset_name, $was_at, $next_upper_at - $was_at );
$html_name .= strtolower( $prefix );
$html_name .= '-' . strtolower( $js_dataset_name[ $next_upper_at ] );
$at = $next_upper_at + 1;
$was_at = $at;
}
if ( $was_at < $end ) {
$html_name .= strtolower( substr( $js_dataset_name, $was_at ) );
}
return $html_name;
}