<?php /** * WordPress scripts and styles default loader. * * Several constants are used to manage the loading, concatenating and compression of scripts and CSS: * define('SCRIPT_DEBUG', true); loads the development (non-minified) versions of all scripts and CSS, and disables compression and concatenation, * define('CONCATENATE_SCRIPTS', false); disables compression and concatenation of scripts and CSS, * define('COMPRESS_SCRIPTS', false); disables compression of scripts, * define('COMPRESS_CSS', false); disables compression of CSS, * define('ENFORCE_GZIP', true); forces gzip for compression (default is deflate). * * The globals $concatenate_scripts, $compress_scripts and $compress_css can be set by plugins * to temporarily override the above settings. Also a compression test is run once and the result is saved * as option 'can_compress_scripts' (0/1). The test will run again if that option is deleted. * * @package WordPress */ /** WordPress Dependency Class */ require ABSPATH . WPINC . '/class-wp-dependency.php'; /** WordPress Dependencies Class */ require ABSPATH . WPINC . '/class-wp-dependencies.php'; /** WordPress Scripts Class */ require ABSPATH . WPINC . '/class-wp-scripts.php'; /** WordPress Scripts Functions */ require ABSPATH . WPINC . '/functions.wp-scripts.php'; /** WordPress Styles Class */ require ABSPATH . WPINC . '/class-wp-styles.php'; /** WordPress Styles Functions */ require ABSPATH . WPINC . '/functions.wp-styles.php'; /** * Registers TinyMCE scripts. * * @since 5.0.0 * * @global string $tinymce_version * @global bool $concatenate_scripts * @global bool $compress_scripts * * @param WP_Scripts $scripts WP_Scripts object. * @param bool $force_uncompressed Whether to forcibly prevent gzip compression. Default false. */ function wp_register_tinymce_scripts( $scripts, $force_uncompressed = false ) { global $tinymce_version, $concatenate_scripts, $compress_scripts; $suffix = wp_scripts_get_suffix(); $dev_suffix = wp_scripts_get_suffix( 'dev' ); script_concat_settings(); $compressed = $compress_scripts && $concatenate_scripts && ! $force_uncompressed; /* * Load tinymce.js when running from /src, otherwise load wp-tinymce.js (in production) * or tinymce.min.js (when SCRIPT_DEBUG is true). */ if ( $compressed ) { $scripts->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', 'react-dom' => '18.3.1', '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, ), ) ) ), '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( \'<script src="' . $src . '"></scr\' + \'ipt>\' );' ); } return $polyfill; } /** * Registers development scripts that integrate with `@wordpress/scripts`. * * @see https://github.com/WordPress/gutenberg/tree/trunk/packages/scripts#start * * @since 6.0.0 * * @param WP_Scripts $scripts WP_Scripts object. */ function wp_register_development_scripts( $scripts ) { if ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG || empty( $scripts->registered['react'] ) || defined( 'WP_RUN_CORE_TESTS' ) ) { return; } $development_scripts = array( 'react-refresh-entry', 'react-refresh-runtime', ); foreach ( $development_scripts as $script_name ) { $assets = include ABSPATH . WPINC . '/assets/script-loader-' . $script_name . '.php'; if ( ! is_array( $assets ) ) { return; } $scripts->add( 'wp-' . $script_name, '/wp-includes/js/dist/development/' . $script_name . '.js', $assets['dependencies'], $assets['version'] ); } // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react. $scripts->registered['react']->deps[] = 'wp-react-refresh-entry'; } /** * Registers all the WordPress packages scripts that are in the standardized * `js/dist/` location. * * For the order of `$scripts->add` see `wp_default_scripts`. * * @since 5.0.0 * * @param WP_Scripts $scripts WP_Scripts object. */ function wp_default_packages_scripts( $scripts ) { $suffix = defined( 'WP_RUN_CORE_TESTS' ) ? '.min' : wp_scripts_get_suffix(); /* * Expects multidimensional array like: * * 'a11y.js' => array('dependencies' => array(...), 'version' => '...'), * 'annotations.js' => array('dependencies' => array(...), 'version' => '...'), * 'api-fetch.js' => array(... */ $assets = include ABSPATH . WPINC . "/assets/script-loader-packages{$suffix}.php"; foreach ( $assets as $file_name => $package_data ) { $basename = str_replace( $suffix . '.js', '', basename( $file_name ) ); $handle = 'wp-' . $basename; $path = "/wp-includes/js/dist/{$basename}{$suffix}.js"; if ( ! empty( $package_data['dependencies'] ) ) { $dependencies = $package_data['dependencies']; } else { $dependencies = array(); } // Add dependencies that cannot be detected and generated by build tools. switch ( $handle ) { case 'wp-block-library': array_push( $dependencies, 'editor' ); break; case 'wp-edit-post': array_push( $dependencies, 'media-models', 'media-views', 'postbox', 'wp-dom-ready' ); break; case 'wp-preferences': array_push( $dependencies, 'wp-preferences-persistence' ); break; } $scripts->add( $handle, $path, $dependencies, $package_data['version'], 1 ); if ( in_array( 'wp-i18n', $dependencies, true ) ) { $scripts->set_translations( $handle ); } /* * Manually set the text direction localization after wp-i18n is printed. * This ensures that wp.i18n.isRTL() returns true in RTL languages. * We cannot use $scripts->set_translations( 'wp-i18n' ) to do this * because WordPress prints a script's translations *before* the script, * which means, in the case of wp-i18n, that wp.i18n.setLocaleData() * is called before wp.i18n is defined. */ if ( 'wp-i18n' === $handle ) { $ltr = _x( 'ltr', 'text direction' ); $script = sprintf( "wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ '%s' ] } );", $ltr ); $scripts->add_inline_script( $handle, $script, 'after' ); } } } /** * Adds inline scripts required for the WordPress JavaScript packages. * * @since 5.0.0 * @since 6.4.0 Added relative time strings for the `wp-date` inline script output. * * @global WP_Locale $wp_locale WordPress date and time locale object. * @global wpdb $wpdb WordPress database abstraction object. * * @param WP_Scripts $scripts WP_Scripts object. */ function wp_default_packages_inline_scripts( $scripts ) { global $wp_locale, $wpdb; if ( isset( $scripts->registered['wp-api-fetch'] ) ) { $scripts->registered['wp-api-fetch']->deps[] = 'wp-hooks'; } $scripts->add_inline_script( 'wp-api-fetch', sprintf( 'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );', sanitize_url( get_rest_url() ) ), 'after' ); $scripts->add_inline_script( 'wp-api-fetch', implode( "\n", array( sprintf( 'wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );', wp_installing() ? '' : wp_create_nonce( 'wp_rest' ) ), 'wp.apiFetch.use( wp.apiFetch.nonceMiddleware );', 'wp.apiFetch.use( wp.apiFetch.mediaUploadMiddleware );', sprintf( 'wp.apiFetch.nonceEndpoint = "%s";', admin_url( 'admin-ajax.php?action=rest-nonce' ) ), ) ), 'after' ); $meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences'; $user_id = get_current_user_id(); $preload_data = get_user_meta( $user_id, $meta_key, true ); $scripts->add_inline_script( 'wp-preferences', sprintf( '( function() { var serverData = %s; var userId = "%d"; var persistenceLayer = wp.preferencesPersistence.__unstableCreatePersistenceLayer( serverData, userId ); var preferencesStore = wp.preferences.store; wp.data.dispatch( preferencesStore ).setPersistenceLayer( persistenceLayer ); } ) ();', wp_json_encode( $preload_data ), $user_id ) ); // Backwards compatibility - configure the old wp-data persistence system. $scripts->add_inline_script( 'wp-data', implode( "\n", array( '( function() {', ' var userId = ' . get_current_user_ID() . ';', ' var storageKey = "WP_DATA_USER_" + userId;', ' wp.data', ' .use( wp.data.plugins.persistence, { storageKey: storageKey } );', '} )();', ) ) ); // Calculate the timezone abbr (EDT, PST) if possible. $timezone_string = get_option( 'timezone_string', 'UTC' ); $timezone_abbr = ''; if ( ! empty( $timezone_string ) ) { $timezone_date = new DateTime( 'now', new DateTimeZone( $timezone_string ) ); $timezone_abbr = $timezone_date->format( 'T' ); } $gmt_offset = get_option( 'gmt_offset', 0 ); $scripts->add_inline_script( 'wp-date', sprintf( 'wp.date.setSettings( %s );', wp_json_encode( array( 'l10n' => array( 'locale' => get_user_locale(), '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 ), 'meridiem' => (object) $wp_locale->meridiem, 'relative' => array( /* translators: %s: Duration. */ 'future' => __( '%s from now' ), /* translators: %s: Duration. */ 'past' => __( '%s ago' ), /* translators: One second from or to a particular datetime, e.g., "a second ago" or "a second from now". */ 's' => __( 'a second' ), /* translators: %d: Duration in seconds from or to a particular datetime, e.g., "4 seconds ago" or "4 seconds from now". */ 'ss' => __( '%d seconds' ), /* translators: One minute from or to a particular datetime, e.g., "a minute ago" or "a minute from now". */ 'm' => __( 'a minute' ), /* translators: %d: Duration in minutes from or to a particular datetime, e.g., "4 minutes ago" or "4 minutes from now". */ 'mm' => __( '%d minutes' ), /* translators: One hour from or to a particular datetime, e.g., "an hour ago" or "an hour from now". */ 'h' => __( 'an hour' ), /* translators: %d: Duration in hours from or to a particular datetime, e.g., "4 hours ago" or "4 hours from now". */ 'hh' => __( '%d hours' ), /* translators: One day from or to a particular datetime, e.g., "a day ago" or "a day from now". */ 'd' => __( 'a day' ), /* translators: %d: Duration in days from or to a particular datetime, e.g., "4 days ago" or "4 days from now". */ 'dd' => __( '%d days' ), /* translators: One month from or to a particular datetime, e.g., "a month ago" or "a month from now". */ 'M' => __( 'a month' ), /* translators: %d: Duration in months from or to a particular datetime, e.g., "4 months ago" or "4 months from now". */ 'MM' => __( '%d months' ), /* translators: One year from or to a particular datetime, e.g., "a year ago" or "a year from now". */ 'y' => __( 'a year' ), /* translators: %d: Duration in years from or to a particular datetime, e.g., "4 years ago" or "4 years from now". */ 'yy' => __( '%d years' ), ), 'startOfWeek' => (int) get_option( 'start_of_week', 0 ), ), 'formats' => array( /* translators: Time format, see https://www.php.net/manual/datetime.format.php */ 'time' => get_option( 'time_format', __( 'g:i a' ) ), /* translators: Date format, see https://www.php.net/manual/datetime.format.php */ 'date' => get_option( 'date_format', __( 'F j, Y' ) ), /* translators: Date/Time format, see https://www.php.net/manual/datetime.format.php */ 'datetime' => __( 'F j, Y g:i a' ), /* translators: Abbreviated date/time format, see https://www.php.net/manual/datetime.format.php */ 'datetimeAbbreviated' => __( 'M j, Y g:i a' ), ), 'timezone' => array( 'offset' => (float) $gmt_offset, 'offsetFormatted' => str_replace( array( '.25', '.5', '.75' ), array( ':15', ':30', ':45' ), (string) $gmt_offset ), 'string' => $timezone_string, 'abbr' => $timezone_abbr, ), ) ) ), 'after' ); // Loading the old editor and its config to ensure the classic block works as expected. $scripts->add_inline_script( 'editor', 'window.wp.oldEditor = window.wp.editor;', 'after' ); /* * wp-editor module is exposed as window.wp.editor. * Problem: there is quite some code expecting window.wp.oldEditor object available under window.wp.editor. * Solution: fuse the two objects together to maintain backward compatibility. * For more context, see https://github.com/WordPress/gutenberg/issues/33203. */ $scripts->add_inline_script( 'wp-editor', 'Object.assign( window.wp.editor, window.wp.oldEditor );', 'after' ); } /** * Adds inline scripts required for the TinyMCE in the block editor. * * These TinyMCE init settings are used to extend and override the default settings * from `_WP_Editors::default_settings()` for the Classic block. * * @since 5.0.0 * * @global WP_Scripts $wp_scripts */ function wp_tinymce_inline_scripts() { global $wp_scripts; /** This filter is documented in wp-includes/class-wp-editor.php */ $editor_settings = apply_filters( 'wp_editor_settings', array( 'tinymce' => true ), 'classic-block' ); $tinymce_plugins = array( 'charmap', 'colorpicker', 'hr', 'lists', 'media', 'paste', 'tabfocus', 'textcolor', 'fullscreen', 'wordpress', 'wpautoresize', 'wpeditimage', 'wpemoji', 'wpgallery', 'wplink', 'wpdialogs', 'wptextpattern', 'wpview', ); /** This filter is documented in wp-includes/class-wp-editor.php */ $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' ); $tinymce_plugins = array_unique( $tinymce_plugins ); $disable_captions = false; // Runs after `tiny_mce_plugins` but before `mce_buttons`. /** This filter is documented in wp-admin/includes/media.php */ if ( apply_filters( 'disable_captions', '' ) ) { $disable_captions = true; } $toolbar1 = array( 'formatselect', 'bold', 'italic', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker', 'wp_add_media', 'wp_adv', ); /** This filter is documented in wp-includes/class-wp-editor.php */ $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' ); $toolbar2 = array( 'strikethrough', 'hr', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help', ); /** This filter is documented in wp-includes/class-wp-editor.php */ $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' ); /** This filter is documented in wp-includes/class-wp-editor.php */ $toolbar3 = apply_filters( 'mce_buttons_3', array(), 'classic-block' ); /** This filter is documented in wp-includes/class-wp-editor.php */ $toolbar4 = apply_filters( 'mce_buttons_4', array(), 'classic-block' ); /** This filter is documented in wp-includes/class-wp-editor.php */ $external_plugins = apply_filters( 'mce_external_plugins', array(), 'classic-block' ); $tinymce_settings = array( 'plugins' => implode( ',', $tinymce_plugins ), 'toolbar1' => implode( ',', $toolbar1 ), 'toolbar2' => implode( ',', $toolbar2 ), 'toolbar3' => implode( ',', $toolbar3 ), 'toolbar4' => implode( ',', $toolbar4 ), 'external_plugins' => wp_json_encode( $external_plugins ), 'classic_block_editor' => true, ); if ( $disable_captions ) { $tinymce_settings['wpeditimage_disable_captions'] = true; } if ( ! empty( $editor_setting