Make WordPress Core

source: trunk/src/wp-includes/script-loader.php

Last change on this file was 61469, checked in by westonruter, 8 hours ago

Themes: Use WP_HTML_Tag_Processor to insert the block template skip link instead of JavaScript.

  • The skip link now works when JavaScript is turned off.
  • By removing the script, the amount of JavaScript sent to the client is reduced for a very marginal performance improvement.
  • A new wp-block-template-skip-link stylesheet is registered, with minification and path data for inlining.
  • The CSS for the skip link now has an RTL version generated, although it is not yet served when the styles are inlined. See #61625.
  • The wp_enqueue_block_template_skip_link() function now exclusively enqueues the stylesheet since the script is removed.
  • For backwards-compatibility, the skip link will continue to be omitted if the_block_template_skip_link() is unhooked from the wp_footer action or wp_enqueue_block_template_skip_link() is unhooked from wp_enqueue_scripts.

Developed in https://github.com/WordPress/wordpress-develop/pull/10676

Follow-up to [56932], [51003].

Props rutviksavsani, westonruter, dmsnell, whiteshadow01, Slieptsov.
See #59505, #53176.
Fixes #64361.

  • Property svn:eol-style set to native
File size: 148.6 KB
Line 
1<?php
2/**
3 * WordPress scripts and styles default loader.
4 *
5 * Several constants are used to manage the loading, concatenating and compression of scripts and CSS:
6 * define('SCRIPT_DEBUG', true); loads the development (non-minified) versions of all scripts and CSS, and disables compression and concatenation,
7 * define('CONCATENATE_SCRIPTS', false); disables compression and concatenation of scripts and CSS,
8 * define('COMPRESS_SCRIPTS', false); disables compression of scripts,
9 * define('COMPRESS_CSS', false); disables compression of CSS,
10 * define('ENFORCE_GZIP', true); forces gzip for compression (default is deflate).
11 *
12 * The globals $concatenate_scripts, $compress_scripts and $compress_css can be set by plugins
13 * to temporarily override the above settings. Also a compression test is run once and the result is saved
14 * as option 'can_compress_scripts' (0/1). The test will run again if that option is deleted.
15 *
16 * @package WordPress
17 */
18
19/** WordPress Dependency Class */
20require ABSPATH . WPINC . '/class-wp-dependency.php';
21
22/** WordPress Dependencies Class */
23require ABSPATH . WPINC . '/class-wp-dependencies.php';
24
25/** WordPress Scripts Class */
26require ABSPATH . WPINC . '/class-wp-scripts.php';
27
28/** WordPress Scripts Functions */
29require ABSPATH . WPINC . '/functions.wp-scripts.php';
30
31/** WordPress Styles Class */
32require ABSPATH . WPINC . '/class-wp-styles.php';
33
34/** WordPress Styles Functions */
35require ABSPATH . WPINC . '/functions.wp-styles.php';
36
37/**
38 * Registers TinyMCE scripts.
39 *
40 * @since 5.0.0
41 *
42 * @global string $tinymce_version
43 * @global bool   $concatenate_scripts
44 * @global bool   $compress_scripts
45 *
46 * @param WP_Scripts $scripts            WP_Scripts object.
47 * @param bool       $force_uncompressed Whether to forcibly prevent gzip compression. Default false.
48 */
49function wp_register_tinymce_scripts( $scripts, $force_uncompressed = false ) {
50        global $tinymce_version, $concatenate_scripts, $compress_scripts;
51
52        $suffix     = wp_scripts_get_suffix();
53        $dev_suffix = wp_scripts_get_suffix( 'dev' );
54
55        script_concat_settings();
56
57        $compressed = $compress_scripts && $concatenate_scripts && ! $force_uncompressed;
58
59        /*
60         * Load tinymce.js when running from /src, otherwise load wp-tinymce.js (in production)
61         * or tinymce.min.js (when SCRIPT_DEBUG is true).
62         */
63        if ( $compressed ) {
64                $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.js', array(), $tinymce_version );
65        } else {
66                $scripts->add( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce$dev_suffix.js", array(), $tinymce_version );
67                $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin$dev_suffix.js", array( 'wp-tinymce-root' ), $tinymce_version );
68        }
69
70        $scripts->add( 'wp-tinymce-lists', includes_url( "js/tinymce/plugins/lists/plugin$suffix.js" ), array( 'wp-tinymce' ), $tinymce_version );
71}
72
73/**
74 * Registers all the WordPress vendor scripts that are in the standardized
75 * `js/dist/vendor/` location.
76 *
77 * For the order of `$scripts->add` see `wp_default_scripts`.
78 *
79 * @since 5.0.0
80 *
81 * @global WP_Locale $wp_locale WordPress date and time locale object.
82 *
83 * @param WP_Scripts $scripts WP_Scripts object.
84 */
85function wp_default_packages_vendor( $scripts ) {
86        global $wp_locale;
87
88        $suffix = wp_scripts_get_suffix();
89
90        $vendor_scripts = array(
91                'react'                       => array(),
92                'react-dom'                   => array( 'react' ),
93                'react-jsx-runtime'           => array( 'react' ),
94                'regenerator-runtime'         => array(),
95                'moment'                      => array(),
96                'lodash'                      => array(),
97                'wp-polyfill-fetch'           => array(),
98                'wp-polyfill-formdata'        => array(),
99                'wp-polyfill-node-contains'   => array(),
100                'wp-polyfill-url'             => array(),
101                'wp-polyfill-dom-rect'        => array(),
102                'wp-polyfill-element-closest' => array(),
103                'wp-polyfill-object-fit'      => array(),
104                'wp-polyfill-inert'           => array(),
105                'wp-polyfill'                 => array(),
106        );
107
108        $vendor_scripts_versions = array(
109                'react'                       => '18.3.1.1', // Final .1 due to switch to UMD build, can be removed in the next update.
110                'react-dom'                   => '18.3.1.1', // Final .1 due to switch to UMD build, can be removed in the next update.
111                'react-jsx-runtime'           => '18.3.1',
112                'regenerator-runtime'         => '0.14.1',
113                'moment'                      => '2.30.1',
114                'lodash'                      => '4.17.21',
115                'wp-polyfill-fetch'           => '3.6.20',
116                'wp-polyfill-formdata'        => '4.0.10',
117                'wp-polyfill-node-contains'   => '4.8.0',
118                'wp-polyfill-url'             => '3.6.4',
119                'wp-polyfill-dom-rect'        => '4.8.0',
120                'wp-polyfill-element-closest' => '3.0.2',
121                'wp-polyfill-object-fit'      => '2.3.5',
122                'wp-polyfill-inert'           => '3.1.3',
123                'wp-polyfill'                 => '3.15.0',
124        );
125
126        foreach ( $vendor_scripts as $handle => $dependencies ) {
127                $scripts->add(
128                        $handle,
129                        "/wp-includes/js/dist/vendor/$handle$suffix.js",
130                        $dependencies,
131                        $vendor_scripts_versions[ $handle ],
132                        1
133                );
134        }
135
136        did_action( 'init' ) && $scripts->add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
137
138        did_action( 'init' ) && $scripts->add_inline_script(
139                'moment',
140                sprintf(
141                        "moment.updateLocale( '%s', %s );",
142                        esc_js( get_user_locale() ),
143                        wp_json_encode(
144                                array(
145                                        'months'         => array_values( $wp_locale->month ),
146                                        'monthsShort'    => array_values( $wp_locale->month_abbrev ),
147                                        'weekdays'       => array_values( $wp_locale->weekday ),
148                                        'weekdaysShort'  => array_values( $wp_locale->weekday_abbrev ),
149                                        'week'           => array(
150                                                'dow' => (int) get_option( 'start_of_week', 0 ),
151                                        ),
152                                        'longDateFormat' => array(
153                                                'LT'   => get_option( 'time_format', __( 'g:i a' ) ),
154                                                'LTS'  => null,
155                                                'L'    => null,
156                                                'LL'   => get_option( 'date_format', __( 'F j, Y' ) ),
157                                                'LLL'  => __( 'F j, Y g:i a' ),
158                                                'LLLL' => null,
159                                        ),
160                                ),
161                                JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
162                        )
163                ),
164                'after'
165        );
166}
167
168/**
169 * Returns contents of an inline script used in appending polyfill scripts for
170 * browsers which fail the provided tests. The provided array is a mapping from
171 * a condition to verify feature support to its polyfill script handle.
172 *
173 * @since 5.0.0
174 *
175 * @param WP_Scripts $scripts WP_Scripts object.
176 * @param string[]   $tests   Features to detect.
177 * @return string Conditional polyfill inline script.
178 */
179function wp_get_script_polyfill( $scripts, $tests ) {
180        $polyfill = '';
181        foreach ( $tests as $test => $handle ) {
182                if ( ! array_key_exists( $handle, $scripts->registered ) ) {
183                        continue;
184                }
185
186                $src = $scripts->registered[ $handle ]->src;
187                $ver = $scripts->registered[ $handle ]->ver;
188
189                if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $scripts->content_url && str_starts_with( $src, $scripts->content_url ) ) ) {
190                        $src = $scripts->base_url . $src;
191                }
192
193                if ( ! empty( $ver ) ) {
194                        $src = add_query_arg( 'ver', $ver, $src );
195                }
196
197                /** This filter is documented in wp-includes/class-wp-scripts.php */
198                $src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) );
199
200                if ( ! $src ) {
201                        continue;
202                }
203
204                $polyfill .= (
205                        // Test presence of feature...
206                        '( ' . $test . ' ) || ' .
207                        /*
208                         * ...appending polyfill on any failures. Cautious viewers may balk
209                         * at the `document.write`. Its caveat of synchronous mid-stream
210                         * blocking write is exactly the behavior we need though.
211                         */
212                        'document.write( \'<script src="' .
213                        $src .
214                        '"></scr\' + \'ipt>\' );'
215                );
216        }
217
218        return $polyfill;
219}
220
221/**
222 * Registers all the WordPress packages scripts that are in the standardized
223 * `js/dist/` location.
224 *
225 * For the order of `$scripts->add` see `wp_default_scripts`.
226 *
227 * @since 5.0.0
228 *
229 * @param WP_Scripts $scripts WP_Scripts object.
230 */
231function wp_default_packages_scripts( $scripts ) {
232        $suffix = defined( 'WP_RUN_CORE_TESTS' ) ? '.min' : wp_scripts_get_suffix();
233        /*
234         * Expects multidimensional array like:
235         *
236         *     'a11y.js' => array('dependencies' => array(...), 'version' => '...'),
237         *     'annotations.js' => array('dependencies' => array(...), 'version' => '...'),
238         *     'api-fetch.js' => array(...
239         */
240        $assets = include ABSPATH . WPINC . "/assets/script-loader-packages{$suffix}.php";
241
242        foreach ( $assets as $file_name => $package_data ) {
243                $basename = str_replace( $suffix . '.js', '', basename( $file_name ) );
244                $handle   = 'wp-' . $basename;
245                $path     = "/wp-includes/js/dist/{$basename}{$suffix}.js";
246
247                if ( ! empty( $package_data['dependencies'] ) ) {
248                        $dependencies = $package_data['dependencies'];
249                } else {
250                        $dependencies = array();
251                }
252
253                // Add dependencies that cannot be detected and generated by build tools.
254                switch ( $handle ) {
255                        case 'wp-block-library':
256                                array_push( $dependencies, 'editor' );
257                                break;
258                        case 'wp-edit-post':
259                                array_push( $dependencies, 'media-models', 'media-views', 'postbox', 'wp-dom-ready' );
260                                break;
261                        case 'wp-preferences':
262                                array_push( $dependencies, 'wp-preferences-persistence' );
263                                break;
264                }
265
266                $scripts->add( $handle, $path, $dependencies, $package_data['version'], 1 );
267
268                if ( in_array( 'wp-i18n', $dependencies, true ) ) {
269                        $scripts->set_translations( $handle );
270                }
271
272                /*
273                 * Manually set the text direction localization after wp-i18n is printed.
274                 * This ensures that wp.i18n.isRTL() returns true in RTL languages.
275                 * We cannot use $scripts->set_translations( 'wp-i18n' ) to do this
276                 * because WordPress prints a script's translations *before* the script,
277                 * which means, in the case of wp-i18n, that wp.i18n.setLocaleData()
278                 * is called before wp.i18n is defined.
279                 */
280                if ( 'wp-i18n' === $handle ) {
281                        $ltr    = _x( 'ltr', 'text direction' );
282                        $script = sprintf( "wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ '%s' ] } );", $ltr );
283                        $scripts->add_inline_script( $handle, $script, 'after' );
284                }
285        }
286}
287
288/**
289 * Adds inline scripts required for the WordPress JavaScript packages.
290 *
291 * @since 5.0.0
292 * @since 6.4.0 Added relative time strings for the `wp-date` inline script output.
293 *
294 * @global WP_Locale $wp_locale WordPress date and time locale object.
295 * @global wpdb      $wpdb      WordPress database abstraction object.
296 *
297 * @param WP_Scripts $scripts WP_Scripts object.
298 */
299function wp_default_packages_inline_scripts( $scripts ) {
300        global $wp_locale, $wpdb;
301
302        if ( isset( $scripts->registered['wp-api-fetch'] ) ) {
303                $scripts->registered['wp-api-fetch']->deps[] = 'wp-hooks';
304        }
305        $scripts->add_inline_script(
306                'wp-api-fetch',
307                sprintf(
308                        'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );',
309                        sanitize_url( get_rest_url() )
310                ),
311                'after'
312        );
313        $scripts->add_inline_script(
314                'wp-api-fetch',
315                implode(
316                        "\n",
317                        array(
318                                sprintf(
319                                        'wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );',
320                                        wp_installing() ? '' : wp_create_nonce( 'wp_rest' )
321                                ),
322                                'wp.apiFetch.use( wp.apiFetch.nonceMiddleware );',
323                                'wp.apiFetch.use( wp.apiFetch.mediaUploadMiddleware );',
324                                sprintf(
325                                        'wp.apiFetch.nonceEndpoint = "%s";',
326                                        admin_url( 'admin-ajax.php?action=rest-nonce' )
327                                ),
328                        )
329                ),
330                'after'
331        );
332
333        $meta_key     = $wpdb->get_blog_prefix() . 'persisted_preferences';
334        $user_id      = get_current_user_id();
335        $preload_data = get_user_meta( $user_id, $meta_key, true );
336        $scripts->add_inline_script(
337                'wp-preferences',
338                sprintf(
339                        '( function() {
340                                var serverData = %s;
341                                var userId = "%d";
342                                var persistenceLayer = wp.preferencesPersistence.__unstableCreatePersistenceLayer( serverData, userId );
343                                var preferencesStore = wp.preferences.store;
344                                wp.data.dispatch( preferencesStore ).setPersistenceLayer( persistenceLayer );
345                        } ) ();',
346                        wp_json_encode( $preload_data, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
347                        $user_id
348                )
349        );
350
351        // Backwards compatibility - configure the old wp-data persistence system.
352        $scripts->add_inline_script(
353                'wp-data',
354                implode(
355                        "\n",
356                        array(
357                                '( function() {',
358                                '       var userId = ' . get_current_user_id() . ';',
359                                '       var storageKey = "WP_DATA_USER_" + userId;',
360                                '       wp.data',
361                                '               .use( wp.data.plugins.persistence, { storageKey: storageKey } );',
362                                '} )();',
363                        )
364                )
365        );
366
367        // Calculate the timezone abbr (EDT, PST) if possible.
368        $timezone_string = get_option( 'timezone_string', 'UTC' );
369        $timezone_abbr   = '';
370
371        if ( ! empty( $timezone_string ) ) {
372                $timezone_date = new DateTime( 'now', new DateTimeZone( $timezone_string ) );
373                $timezone_abbr = $timezone_date->format( 'T' );
374        }
375
376        $gmt_offset = get_option( 'gmt_offset', 0 );
377
378        $scripts->add_inline_script(
379                'wp-date',
380                sprintf(
381                        'wp.date.setSettings( %s );',
382                        wp_json_encode(
383                                array(
384                                        'l10n'     => array(
385                                                'locale'        => get_user_locale(),
386                                                'months'        => array_values( $wp_locale->month ),
387                                                'monthsShort'   => array_values( $wp_locale->month_abbrev ),
388                                                'weekdays'      => array_values( $wp_locale->weekday ),
389                                                'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
390                                                'meridiem'      => (object) $wp_locale->meridiem,
391                                                'relative'      => array(
392                                                        /* translators: %s: Duration. */
393                                                        'future' => __( '%s from now' ),
394                                                        /* translators: %s: Duration. */
395                                                        'past'   => __( '%s ago' ),
396                                                        /* translators: One second from or to a particular datetime, e.g., "a second ago" or "a second from now". */
397                                                        's'      => __( 'a second' ),
398                                                        /* translators: %d: Duration in seconds from or to a particular datetime, e.g., "4 seconds ago" or "4 seconds from now". */
399                                                        'ss'     => __( '%d seconds' ),
400                                                        /* translators: One minute from or to a particular datetime, e.g., "a minute ago" or "a minute from now". */
401                                                        'm'      => __( 'a minute' ),
402                                                        /* translators: %d: Duration in minutes from or to a particular datetime, e.g., "4 minutes ago" or "4 minutes from now". */
403                                                        'mm'     => __( '%d minutes' ),
404                                                        /* translators: One hour from or to a particular datetime, e.g., "an hour ago" or "an hour from now". */
405                                                        'h'      => __( 'an hour' ),
406                                                        /* translators: %d: Duration in hours from or to a particular datetime, e.g., "4 hours ago" or "4 hours from now". */
407                                                        'hh'     => __( '%d hours' ),
408                                                        /* translators: One day from or to a particular datetime, e.g., "a day ago" or "a day from now". */
409                                                        'd'      => __( 'a day' ),
410                                                        /* translators: %d: Duration in days from or to a particular datetime, e.g., "4 days ago" or "4 days from now". */
411                                                        'dd'     => __( '%d days' ),
412                                                        /* translators: One month from or to a particular datetime, e.g., "a month ago" or "a month from now". */
413                                                        'M'      => __( 'a month' ),
414                                                        /* translators: %d: Duration in months from or to a particular datetime, e.g., "4 months ago" or "4 months from now". */
415                                                        'MM'     => __( '%d months' ),
416                                                        /* translators: One year from or to a particular datetime, e.g., "a year ago" or "a year from now". */
417                                                        'y'      => __( 'a year' ),
418                                                        /* translators: %d: Duration in years from or to a particular datetime, e.g., "4 years ago" or "4 years from now". */
419                                                        'yy'     => __( '%d years' ),
420                                                ),
421                                                'startOfWeek'   => (int) get_option( 'start_of_week', 0 ),
422                                        ),
423                                        'formats'  => array(
424                                                /* translators: Time format, see https://www.php.net/manual/datetime.format.php */
425                                                'time'                => get_option( 'time_format', __( 'g:i a' ) ),
426                                                /* translators: Date format, see https://www.php.net/manual/datetime.format.php */
427                                                'date'                => get_option( 'date_format', __( 'F j, Y' ) ),
428                                                /* translators: Date/Time format, see https://www.php.net/manual/datetime.format.php */
429                                                'datetime'            => __( 'F j, Y g:i a' ),
430                                                /* translators: Abbreviated date/time format, see https://www.php.net/manual/datetime.format.php */
431                                                'datetimeAbbreviated' => __( 'M j, Y g:i a' ),
432                                        ),
433                                        'timezone' => array(
434                                                'offset'          => (float) $gmt_offset,
435                                                'offsetFormatted' => str_replace( array( '.25', '.5', '.75' ), array( ':15', ':30', ':45' ), (string) $gmt_offset ),
436                                                'string'          => $timezone_string,
437                                                'abbr'            => $timezone_abbr,
438                                        ),
439                                ),
440                                JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
441                        )
442                ),
443                'after'
444        );
445
446        // Loading the old editor and its config to ensure the classic block works as expected.
447        $scripts->add_inline_script(
448                'editor',
449                'window.wp.oldEditor = window.wp.editor;',
450                'after'
451        );
452
453        /*
454         * wp-editor module is exposed as window.wp.editor.
455         * Problem: there is quite some code expecting window.wp.oldEditor object available under window.wp.editor.
456         * Solution: fuse the two objects together to maintain backward compatibility.
457         * For more context, see https://github.com/WordPress/gutenberg/issues/33203.
458         */
459        $scripts->add_inline_script(
460                'wp-editor',
461                'Object.assign( window.wp.editor, window.wp.oldEditor );',
462                'after'
463        );
464}
465
466/**
467 * Adds inline scripts required for the TinyMCE in the block editor.
468 *
469 * These TinyMCE init settings are used to extend and override the default settings
470 * from `_WP_Editors::default_settings()` for the Classic block.
471 *
472 * @since 5.0.0
473 *
474 * @global WP_Scripts $wp_scripts
475 */
476function wp_tinymce_inline_scripts() {
477        global $wp_scripts;
478
479        /** This filter is documented in wp-includes/class-wp-editor.php */
480        $editor_settings = apply_filters( 'wp_editor_settings', array( 'tinymce' => true ), 'classic-block' );
481
482        $tinymce_plugins = array(
483                'charmap',
484                'colorpicker',
485                'hr',
486                'lists',
487                'media',
488                'paste',
489                'tabfocus',
490                'textcolor',
491                'fullscreen',
492                'wordpress',
493                'wpautoresize',
494                'wpeditimage',
495                'wpemoji',
496                'wpgallery',
497                'wplink',
498                'wpdialogs',
499                'wptextpattern',
500                'wpview',
501        );
502
503        /** This filter is documented in wp-includes/class-wp-editor.php */
504        $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' );
505        $tinymce_plugins = array_unique( $tinymce_plugins );
506
507        $disable_captions = false;
508        // Runs after `tiny_mce_plugins` but before `mce_buttons`.
509        /** This filter is documented in wp-admin/includes/media.php */
510        if ( apply_filters( 'disable_captions', '' ) ) {
511                $disable_captions = true;
512        }
513
514        $toolbar1 = array(
515                'formatselect',
516                'bold',
517                'italic',
518                'bullist',
519                'numlist',
520                'blockquote',
521                'alignleft',
522                'aligncenter',
523                'alignright',
524                'link',
525                'unlink',
526                'wp_more',
527                'spellchecker',
528                'wp_add_media',
529                'wp_adv',
530        );
531
532        /** This filter is documented in wp-includes/class-wp-editor.php */
533        $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' );
534
535        $toolbar2 = array(
536                'strikethrough',
537                'hr',
538                'forecolor',
539                'pastetext',
540                'removeformat',
541                'charmap',
542                'outdent',
543                'indent',
544                'undo',
545                'redo',
546                'wp_help',
547        );
548
549        /** This filter is documented in wp-includes/class-wp-editor.php */
550        $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' );
551        /** This filter is documented in wp-includes/class-wp-editor.php */
552        $toolbar3 = apply_filters( 'mce_buttons_3', array(), 'classic-block' );
553        /** This filter is documented in wp-includes/class-wp-editor.php */
554        $toolbar4 = apply_filters( 'mce_buttons_4', array(), 'classic-block' );
555        /** This filter is documented in wp-includes/class-wp-editor.php */
556        $external_plugins = apply_filters( 'mce_external_plugins', array(), 'classic-block' );
557
558        $tinymce_settings = array(
559                'plugins'              => implode( ',', $tinymce_plugins ),
560                'toolbar1'             => implode( ',', $toolbar1 ),
561                'toolbar2'             => implode( ',', $toolbar2 ),
562                'toolbar3'             => implode( ',', $toolbar3 ),
563                'toolbar4'             => implode( ',', $toolbar4 ),
564                'external_plugins'     => wp_json_encode( $external_plugins ),
565                'classic_block_editor' => true,
566        );
567
568        if ( $disable_captions ) {
569                $tinymce_settings['wpeditimage_disable_captions'] = true;
570        }
571
572        if ( ! empty( $editor_settings['tinymce'] ) && is_array( $editor_settings['tinymce'] ) ) {
573                $tinymce_settings = array_merge( $tinymce_settings, $editor_settings['tinymce'] );
574        }
575
576        /** This filter is documented in wp-includes/class-wp-editor.php */
577        $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' );
578
579        /*
580         * Do "by hand" translation from PHP array to js object.
581         * Prevents breakage in some custom settings.
582         */
583        $init_obj = '';
584        foreach ( $tinymce_settings as $key => $value ) {
585                if ( is_bool( $value ) ) {
586                        $val       = $value ? 'true' : 'false';
587                        $init_obj .= $key . ':' . $val . ',';
588                        continue;
589                } elseif ( ! empty( $value ) && is_string( $value ) && (
590                        ( '{' === $value[0] && '}' === $value[ strlen( $value ) - 1 ] ) ||
591                        ( '[' === $value[0] && ']' === $value[ strlen( $value ) - 1 ] ) ||
592                        preg_match( '/^\(?function ?\(/', $value ) ) ) {
593                        $init_obj .= $key . ':' . $value . ',';
594                        continue;
595                }
596                $init_obj .= $key . ':"' . $value . '",';
597        }
598
599        $init_obj = '{' . trim( $init_obj, ' ,' ) . '}';
600
601        $script = 'window.wpEditorL10n = {
602                tinymce: {
603                        baseURL: ' . wp_json_encode( includes_url( 'js/tinymce' ), JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ',
604                        suffix: ' . ( SCRIPT_DEBUG ? '""' : '".min"' ) . ',
605                        settings: ' . $init_obj . ',
606                }
607        }';
608
609        $wp_scripts->add_inline_script( 'wp-block-library', $script, 'before' );
610}
611
612/**
613 * Registers all the WordPress packages scripts.
614 *
615 * @since 5.0.0
616 *
617 * @param WP_Scripts $scripts WP_Scripts object.
618 */
619function wp_default_packages( $scripts ) {
620        wp_default_packages_vendor( $scripts );
621        wp_register_tinymce_scripts( $scripts );
622        wp_default_packages_scripts( $scripts );
623
624        if ( did_action( 'init' ) ) {
625                wp_default_packages_inline_scripts( $scripts );
626        }
627}
628
629/**
630 * Returns the suffix that can be used for the scripts.
631 *
632 * There are two suffix types, the normal one and the dev suffix.
633 *
634 * @since 5.0.0
635 *
636 * @param string $type The type of suffix to retrieve.
637 * @return string The script suffix.
638 */
639function wp_scripts_get_suffix( $type = '' ) {
640        static $suffixes;
641
642        if ( null === $suffixes ) {
643                /*
644                 * Include an unmodified $wp_version.
645                 *
646                 * Note: wp_get_wp_version() is not used here, as this file can be included
647                 * via wp-admin/load-scripts.php or wp-admin/load-styles.php, in which case
648                 * wp-includes/functions.php is not loaded.
649                 */
650                require ABSPATH . WPINC . '/version.php';
651
652                /*
653                 * Note: str_contains() is not used here, as this file can be included
654                 * via wp-admin/load-scripts.php or wp-admin/load-styles.php, in which case
655                 * the polyfills from wp-includes/compat.php are not loaded.
656                 */
657                $develop_src = false !== strpos( $wp_version, '-src' );
658
659                if ( ! defined( 'SCRIPT_DEBUG' ) ) {
660                        define( 'SCRIPT_DEBUG', $develop_src );
661                }
662                $suffix     = SCRIPT_DEBUG ? '' : '.min';
663                $dev_suffix = $develop_src ? '' : '.min';
664
665                $suffixes = array(
666                        'suffix'     => $suffix,
667                        'dev_suffix' => $dev_suffix,
668                );
669        }
670
671        if ( 'dev' === $type ) {
672                return $suffixes['dev_suffix'];
673        }
674
675        return $suffixes['suffix'];
676}
677
678/**
679 * Registers all WordPress scripts.
680 *
681 * Localizes some of them.
682 * args order: `$scripts->add( 'handle', 'url', 'dependencies', 'query-string', 1 );`
683 * when last arg === 1 queues the script for the footer
684 *
685 * @since 2.6.0
686 *
687 * @param WP_Scripts $scripts WP_Scripts object.
688 */
689function wp_default_scripts( $scripts ) {
690        $suffix     = wp_scripts_get_suffix();
691        $dev_suffix = wp_scripts_get_suffix( 'dev' );
692        $guessurl   = site_url();
693
694        if ( ! $guessurl ) {
695                $guessed_url = true;
696                $guessurl    = wp_guess_url();
697        }
698
699        $scripts->base_url        = $guessurl;
700        $scripts->content_url     = defined( 'WP_CONTENT_URL' ) ? WP_CONTENT_URL : '';
701        $scripts->default_version = get_bloginfo( 'version' );
702        $scripts->default_dirs    = array( '/wp-admin/js/', '/wp-includes/js/' );
703
704        $scripts->add( 'utils', "/wp-includes/js/utils$suffix.js" );
705        did_action( 'init' ) && $scripts->localize(
706                'utils',
707                'userSettings',
708                array(
709                        'url'    => (string) SITECOOKIEPATH,
710                        'uid'    => (string) get_current_user_id(),
711                        'time'   => (string) time(),
712                        'secure' => (string) ( 'https' === parse_url( site_url(), PHP_URL_SCHEME ) ),
713                )
714        );
715
716        $scripts->add( 'common', "/wp-admin/js/common$suffix.js", array( 'jquery', 'hoverIntent', 'utils', 'wp-a11y' ), false, 1 );
717        $scripts->set_translations( 'common' );
718
719        $bulk_action_observer_ids = array(
720                'bulk_action' => 'action',
721                'changeit'    => 'new_role',
722        );
723        did_action( 'init' ) && $scripts->localize(
724                'common',
725                'bulkActionObserverIds',
726                /**
727                 * Filters the array of field name attributes for bulk actions.
728                 *
729                 * @since 6.8.1
730                 *
731                 * @param array $bulk_action_observer_ids {
732                 *      An array of field name attributes for bulk actions.
733                 *
734                 *      @type string $bulk_action The bulk action field name. Default 'action'.
735                 *      @type string $changeit    The new role field name. Default 'new_role'.
736                 * }
737                 */
738                apply_filters( 'bulk_action_observer_ids', $bulk_action_observer_ids )
739        );
740
741        $scripts->add( 'wp-sanitize', "/wp-includes/js/wp-sanitize$suffix.js", array(), false, 1 );
742
743        $scripts->add( 'sack', "/wp-includes/js/tw-sack$suffix.js", array(), '1.6.1', 1 );
744
745        $scripts->add( 'quicktags', "/wp-includes/js/quicktags$suffix.js", array(), false, 1 );
746        did_action( 'init' ) && $scripts->localize(
747                'quicktags',
748                'quicktagsL10n',
749                array(
750                        'closeAllOpenTags'      => __( 'Close all open tags' ),
751                        'closeTags'             => __( 'close tags' ),
752                        'enterURL'              => __( 'Enter the URL' ),
753                        'enterImageURL'         => __( 'Enter the URL of the image' ),
754                        'enterImageDescription' => __( 'Enter a description of the image' ),
755                        'textdirection'         => __( 'text direction' ),
756                        'toggleTextdirection'   => __( 'Toggle Editor Text Direction' ),
757                        'dfw'                   => __( 'Distraction-free writing mode' ),
758                        'strong'                => __( 'Bold' ),
759                        'strongClose'           => __( 'Close bold tag' ),
760                        'em'                    => __( 'Italic' ),
761                        'emClose'               => __( 'Close italic tag' ),
762                        'link'                  => __( 'Insert link' ),
763                        'blockquote'            => __( 'Blockquote' ),
764                        'blockquoteClose'       => __( 'Close blockquote tag' ),
765                        'del'                   => __( 'Deleted text (strikethrough)' ),
766                        'delClose'              => __( 'Close deleted text tag' ),
767                        'ins'                   => __( 'Inserted text' ),
768                        'insClose'              => __( 'Close inserted text tag' ),
769                        'image'                 => __( 'Insert image' ),
770                        'ul'                    => __( 'Bulleted list' ),
771                        'ulClose'               => __( 'Close bulleted list tag' ),
772                        'ol'                    => __( 'Numbered list' ),
773                        'olClose'               => __( 'Close numbered list tag' ),
774                        'li'                    => __( 'List item' ),
775                        'liClose'               => __( 'Close list item tag' ),
776                        'code'                  => __( 'Code' ),
777                        'codeClose'             => __( 'Close code tag' ),
778                        'more'                  => __( 'Insert Read More tag' ),
779                )
780        );
781
782        $scripts->add( 'colorpicker', "/wp-includes/js/colorpicker$suffix.js", array( 'prototype' ), '3517m' );
783
784        $scripts->add( 'editor', "/wp-admin/js/editor$suffix.js", array( 'utils', 'jquery' ), false, 1 );
785
786        $scripts->add( 'clipboard', "/wp-includes/js/clipboard$suffix.js", array(), '2.0.11', 1 );
787
788        $scripts->add( 'wp-ajax-response', "/wp-includes/js/wp-ajax-response$suffix.js", array( 'jquery', 'wp-a11y' ), false, 1 );
789        did_action( 'init' ) && $scripts->localize(
790                'wp-ajax-response',
791                'wpAjax',
792                array(
793                        'noPerm' => __( 'Sorry, you are not allowed to do that.' ),
794                        'broken' => __( 'An error occurred while processing your request. Please try again later.' ),
795                )
796        );
797
798        $scripts->add( 'wp-api-request', "/wp-includes/js/api-request$suffix.js", array( 'jquery' ), false, 1 );
799        // `wpApiSettings` is also used by `wp-api`, which depends on this script.
800        did_action( 'init' ) && $scripts->localize(
801                'wp-api-request',
802                'wpApiSettings',
803                array(
804                        'root'          => sanitize_url( get_rest_url() ),
805                        'nonce'         => wp_installing() ? '' : wp_create_nonce( 'wp_rest' ),
806                        'versionString' => 'wp/v2/',
807                )
808        );
809
810        $scripts->add( 'wp-pointer', "/wp-includes/js/wp-pointer$suffix.js", array( 'jquery-ui-core' ), false, 1 );
811        $scripts->set_translations( 'wp-pointer' );
812
813        $scripts->add( 'autosave', "/wp-includes/js/autosave$suffix.js", array( 'heartbeat' ), false, 1 );
814
815        $scripts->add( 'heartbeat', "/wp-includes/js/heartbeat$suffix.js", array( 'jquery', 'wp-hooks' ), false, 1 );
816        did_action( 'init' ) && $scripts->localize(
817                'heartbeat',
818                'heartbeatSettings',
819                /**
820                 * Filters the Heartbeat settings.
821                 *
822                 * @since 3.6.0
823                 *
824                 * @param array $settings Heartbeat settings array.
825                 */
826                apply_filters( 'heartbeat_settings', array() )
827        );
828
829        $scripts->add( 'wp-auth-check', "/wp-includes/js/wp-auth-check$suffix.js", array( 'heartbeat' ), false, 1 );
830        $scripts->set_translations( 'wp-auth-check' );
831
832        $scripts->add( 'wp-lists', "/wp-includes/js/wp-lists$suffix.js", array( 'wp-ajax-response', 'jquery-color' ), false, 1 );
833
834        $scripts->add( 'site-icon', '/wp-admin/js/site-icon.js', array( 'jquery' ), false, 1 );
835        $scripts->set_translations( 'site-icon' );
836
837        // WordPress no longer uses or bundles Prototype or script.aculo.us. These are now pulled from an external source.
838        $scripts->add( 'prototype', 'https://ajax.googleapis.com/ajax/libs/prototype/1.7.1.0/prototype.js', array(), '1.7.1' );
839        $scripts->add( 'scriptaculous-root', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/scriptaculous.js', array( 'prototype' ), '1.9.0' );
840        $scripts->add( 'scriptaculous-builder', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/builder.js', array( 'scriptaculous-root' ), '1.9.0' );
841        $scripts->add( 'scriptaculous-dragdrop', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/dragdrop.js', array( 'scriptaculous-builder', 'scriptaculous-effects' ), '1.9.0' );
842        $scripts->add( 'scriptaculous-effects', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/effects.js', array( 'scriptaculous-root' ), '1.9.0' );
843        $scripts->add( 'scriptaculous-slider', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/slider.js', array( 'scriptaculous-effects' ), '1.9.0' );
844        $scripts->add( 'scriptaculous-sound', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/sound.js', array( 'scriptaculous-root' ), '1.9.0' );
845        $scripts->add( 'scriptaculous-controls', 'https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/controls.js', array( 'scriptaculous-root' ), '1.9.0' );
846        $scripts->add( 'scriptaculous', false, array( 'scriptaculous-dragdrop', 'scriptaculous-slider', 'scriptaculous-controls' ) );
847
848        // Not used in core, replaced by Jcrop.js.
849        $scripts->add( 'cropper', '/wp-includes/js/crop/cropper.js', array( 'scriptaculous-dragdrop' ) );
850
851        /*
852         * jQuery.
853         * The unminified jquery.js and jquery-migrate.js are included to facilitate debugging.
854         */
855        $scripts->add( 'jquery', false, array( 'jquery-core', 'jquery-migrate' ), '3.7.1' );
856        $scripts->add( 'jquery-core', "/wp-includes/js/jquery/jquery$suffix.js", array(), '3.7.1' );
857        $scripts->add( 'jquery-migrate', "/wp-includes/js/jquery/jquery-migrate$suffix.js", array(), '3.4.1' );
858
859        /*
860         * Full jQuery UI.
861         * The build process in 1.12.1 has changed significantly.
862         * In order to keep backwards compatibility, and to keep the optimized loading,
863         * the source files were flattened and included with some modifications for AMD loading.
864         * A notable change is that 'jquery-ui-core' now contains 'jquery-ui-position' and 'jquery-ui-widget'.
865         */
866        $scripts->add( 'jquery-ui-core', "/wp-includes/js/jquery/ui/core$suffix.js", array( 'jquery' ), '1.13.3', 1 );
867        $scripts->add( 'jquery-effects-core', "/wp-includes/js/jquery/ui/effect$suffix.js", array( 'jquery' ), '1.13.3', 1 );
868
869        $scripts->add( 'jquery-effects-blind', "/wp-includes/js/jquery/ui/effect-blind$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
870        $scripts->add( 'jquery-effects-bounce', "/wp-includes/js/jquery/ui/effect-bounce$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
871        $scripts->add( 'jquery-effects-clip', "/wp-includes/js/jquery/ui/effect-clip$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
872        $scripts->add( 'jquery-effects-drop', "/wp-includes/js/jquery/ui/effect-drop$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
873        $scripts->add( 'jquery-effects-explode', "/wp-includes/js/jquery/ui/effect-explode$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
874        $scripts->add( 'jquery-effects-fade', "/wp-includes/js/jquery/ui/effect-fade$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
875        $scripts->add( 'jquery-effects-fold', "/wp-includes/js/jquery/ui/effect-fold$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
876        $scripts->add( 'jquery-effects-highlight', "/wp-includes/js/jquery/ui/effect-highlight$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
877        $scripts->add( 'jquery-effects-puff', "/wp-includes/js/jquery/ui/effect-puff$suffix.js", array( 'jquery-effects-core', 'jquery-effects-scale' ), '1.13.3', 1 );
878        $scripts->add( 'jquery-effects-pulsate', "/wp-includes/js/jquery/ui/effect-pulsate$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
879        $scripts->add( 'jquery-effects-scale', "/wp-includes/js/jquery/ui/effect-scale$suffix.js", array( 'jquery-effects-core', 'jquery-effects-size' ), '1.13.3', 1 );
880        $scripts->add( 'jquery-effects-shake', "/wp-includes/js/jquery/ui/effect-shake$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
881        $scripts->add( 'jquery-effects-size', "/wp-includes/js/jquery/ui/effect-size$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
882        $scripts->add( 'jquery-effects-slide', "/wp-includes/js/jquery/ui/effect-slide$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
883        $scripts->add( 'jquery-effects-transfer', "/wp-includes/js/jquery/ui/effect-transfer$suffix.js", array( 'jquery-effects-core' ), '1.13.3', 1 );
884
885        // Widgets
886        $scripts->add( 'jquery-ui-accordion', "/wp-includes/js/jquery/ui/accordion$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
887        $scripts->add( 'jquery-ui-autocomplete', "/wp-includes/js/jquery/ui/autocomplete$suffix.js", array( 'jquery-ui-menu', 'wp-a11y' ), '1.13.3', 1 );
888        $scripts->add( 'jquery-ui-button', "/wp-includes/js/jquery/ui/button$suffix.js", array( 'jquery-ui-core', 'jquery-ui-controlgroup', 'jquery-ui-checkboxradio' ), '1.13.3', 1 );
889        $scripts->add( 'jquery-ui-datepicker', "/wp-includes/js/jquery/ui/datepicker$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
890        $scripts->add( 'jquery-ui-dialog', "/wp-includes/js/jquery/ui/dialog$suffix.js", array( 'jquery-ui-resizable', 'jquery-ui-draggable', 'jquery-ui-button' ), '1.13.3', 1 );
891        $scripts->add( 'jquery-ui-menu', "/wp-includes/js/jquery/ui/menu$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
892        $scripts->add( 'jquery-ui-mouse', "/wp-includes/js/jquery/ui/mouse$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
893        $scripts->add( 'jquery-ui-progressbar', "/wp-includes/js/jquery/ui/progressbar$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
894        $scripts->add( 'jquery-ui-selectmenu', "/wp-includes/js/jquery/ui/selectmenu$suffix.js", array( 'jquery-ui-menu' ), '1.13.3', 1 );
895        $scripts->add( 'jquery-ui-slider', "/wp-includes/js/jquery/ui/slider$suffix.js", array( 'jquery-ui-mouse' ), '1.13.3', 1 );
896        $scripts->add( 'jquery-ui-spinner', "/wp-includes/js/jquery/ui/spinner$suffix.js", array( 'jquery-ui-button' ), '1.13.3', 1 );
897        $scripts->add( 'jquery-ui-tabs', "/wp-includes/js/jquery/ui/tabs$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
898        $scripts->add( 'jquery-ui-tooltip', "/wp-includes/js/jquery/ui/tooltip$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
899
900        // New in 1.12.1
901        $scripts->add( 'jquery-ui-checkboxradio', "/wp-includes/js/jquery/ui/checkboxradio$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
902        $scripts->add( 'jquery-ui-controlgroup', "/wp-includes/js/jquery/ui/controlgroup$suffix.js", array( 'jquery-ui-core' ), '1.13.3', 1 );
903
904        // Interactions
905        $scripts->add( 'jquery-ui-draggable', "/wp-includes/js/jquery/ui/draggable$suffix.js", array( 'jquery-ui-mouse' ), '1.13.3', 1 );
906        $scripts->add( 'jquery-ui-droppable', "/wp-includes/js/jquery/ui/droppable$suffix.js", array( 'jquery-ui-draggable' ), '1.13.3', 1 );
907        $scripts->add( 'jquery-ui-resizable', "/wp-includes/js/jquery/ui/resizable$suffix.js", array( 'jquery-ui-mouse' ), '1.13.3', 1 );
908        $scripts->add( 'jquery-ui-selectable', "/wp-includes/js/jquery/ui/selectable$suffix.js", array( 'jquery-ui-mouse' ), '1.13.3', 1 );
909        $scripts->add( 'jquery-ui-sortable', "/wp-includes/js/jquery/ui/sortable$suffix.js", array( 'jquery-ui-mouse' ), '1.13.3', 1 );
910
911        /*
912         * As of 1.12.1 `jquery-ui-position` and `jquery-ui-widget` are part of `jquery-ui-core`.
913         * Listed here for back-compat.
914         */
915        $scripts->add( 'jquery-ui-position', false, array( 'jquery-ui-core' ), '1.13.3', 1 );
916        $scripts->add( 'jquery-ui-widget', false, array( 'jquery-ui-core' ), '1.13.3', 1 );
917
918        // Deprecated, not used in core, most functionality is included in jQuery 1.3.
919        $scripts->add( 'jquery-form', "/wp-includes/js/jquery/jquery.form$suffix.js", array( 'jquery' ), '4.3.0', 1 );
920
921        // jQuery plugins.
922        $scripts->add( 'jquery-color', '/wp-includes/js/jquery/jquery.color.min.js', array( 'jquery' ), '3.0.0', 1 );
923        $scripts->add( 'schedule', '/wp-includes/js/jquery/jquery.schedule.js', array( 'jquery' ), '20m', 1 );
924        $scripts->add( 'jquery-query', '/wp-includes/js/jquery/jquery.query.js', array( 'jquery' ), '2.2.3', 1 );
925        $scripts->add( 'jquery-serialize-object', '/wp-includes/js/jquery/jquery.serialize-object.js', array( 'jquery' ), '0.2-wp', 1 );
926        $scripts->add( 'jquery-hotkeys', "/wp-includes/js/jquery/jquery.hotkeys$suffix.js", array( 'jquery' ), '0.0.2m', 1 );
927        $scripts->add( 'jquery-table-hotkeys', "/wp-includes/js/jquery/jquery.table-hotkeys$suffix.js", array( 'jquery', 'jquery-hotkeys' ), false, 1 );
928        $scripts->add( 'jquery-touch-punch', '/wp-includes/js/jquery/jquery.ui.touch-punch.js', array( 'jquery-ui-core', 'jquery-ui-mouse' ), '0.2.2', 1 );
929
930        // Not used any more, registered for backward compatibility.
931        $scripts->add( 'suggest', "/wp-includes/js/jquery/suggest$suffix.js", array( 'jquery' ), '1.1-20110113', 1 );
932
933        /*
934         * Masonry v2 depended on jQuery. v3 does not. The older jquery-masonry handle is a shiv.
935         * It sets jQuery as a dependency, as the theme may have been implicitly loading it this way.
936         */
937        $scripts->add( 'imagesloaded', '/wp-includes/js/imagesloaded.min.js', array(), '5.0.0', 1 );
938        $scripts->add( 'masonry', '/wp-includes/js/masonry.min.js', array( 'imagesloaded' ), '4.2.2', 1 );
939        $scripts->add( 'jquery-masonry', '/wp-includes/js/jquery/jquery.masonry.min.js', array( 'jquery', 'masonry' ), '3.1.2b', 1 );
940
941        $scripts->add( 'thickbox', '/wp-includes/js/thickbox/thickbox.js', array( 'jquery' ), '3.1-20121105', 1 );
942        did_action( 'init' ) && $scripts->localize(
943                'thickbox',
944                'thickboxL10n',
945                array(
946                        'next'             => __( 'Next &gt;' ),
947                        'prev'             => __( '&lt; Prev' ),
948                        'image'            => __( 'Image' ),
949                        'of'               => __( 'of' ),
950                        'close'            => __( 'Close' ),
951                        'noiframes'        => __( 'This feature requires inline frames. You have iframes disabled or your browser does not support them.' ),
952                        'loadingAnimation' => includes_url( 'js/thickbox/loadingAnimation.gif' ),
953                )
954        );
955
956        // Not used in core, replaced by imgAreaSelect.
957        $scripts->add( 'jcrop', '/wp-includes/js/jcrop/jquery.Jcrop.min.js', array( 'jquery' ), '0.9.15' );
958
959        // Error messages for Plupload.
960        $uploader_l10n = array(
961                'queue_limit_exceeded'      => __( 'You have attempted to queue too many files.' ),
962                /* translators: %s: File name. */
963                'file_exceeds_size_limit'   => __( '%s exceeds the maximum upload size for this site.' ),
964                'zero_byte_file'            => __( 'This file is empty. Please try another.' ),
965                'invalid_filetype'          => __( 'This file cannot be processed by the web server.' ),
966                'not_an_image'              => __( 'This file is not an image. Please try another.' ),
967                'image_memory_exceeded'     => __( 'Memory exceeded. Please try another smaller file.' ),
968                'image_dimensions_exceeded' => __( 'This is larger than the maximum size. Please try another.' ),
969                'default_error'             => __( 'An error occurred in the upload. Please try again later.' ),
970                'missing_upload_url'        => __( 'There was a configuration error. Please contact the server administrator.' ),
971                'upload_limit_exceeded'     => __( 'You may only upload 1 file.' ),
972                'http_error'                => __( 'Unexpected response from the server. The file may have been uploaded successfully. Check in the Media Library or reload the page.' ),
973                'http_error_image'          => __( 'The server cannot process the image. This can happen if the server is busy or does not have enough resources to complete the task. Uploading a smaller image may help. Suggested maximum size is 2560 pixels.' ),
974                'upload_failed'             => __( 'Upload failed.' ),
975                /* translators: 1: Opening link tag, 2: Closing link tag. */
976                'big_upload_failed'         => __( 'Please try uploading this file with the %1$sbrowser uploader%2$s.' ),
977                /* translators: %s: File name. */
978                'big_upload_queued'         => __( '%s exceeds the maximum upload size for the multi-file uploader when used in your browser.' ),
979                'io_error'                  => __( 'IO error.' ),
980                'security_error'            => __( 'Security error.' ),
981                'file_cancelled'            => __( 'File canceled.' ),
982                'upload_stopped'            => __( 'Upload stopped.' ),
983                'dismiss'                   => __( 'Dismiss' ),
984                'crunching'                 => __( 'Crunching&hellip;' ),
985                'deleted'                   => __( 'moved to the Trash.' ),
986                /* translators: %s: File name. */
987                'error_uploading'           => __( '&#8220;%s&#8221; has failed to upload.' ),
988                'unsupported_image'         => __( 'This image cannot be displayed in a web browser. For best results convert it to JPEG before uploading.' ),
989                'noneditable_image'         => __( 'The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading.' ),
990                'file_url_copied'           => __( 'The file URL has been copied to your clipboard' ),
991        );
992
993        $scripts->add( 'moxiejs', "/wp-includes/js/plupload/moxie$suffix.js", array(), '1.3.5.1' );
994        $scripts->add( 'plupload', "/wp-includes/js/plupload/plupload$suffix.js", array( 'moxiejs' ), '2.1.9' );
995        // Back compat handles:
996        foreach ( array( 'all', 'html5', 'flash', 'silverlight', 'html4' ) as $handle ) {
997                $scripts->add( "plupload-$handle", false, array( 'plupload' ), '2.1.1' );
998        }
999
1000        $scripts->add( 'plupload-handlers', "/wp-includes/js/plupload/handlers$suffix.js", array( 'clipboard', 'jquery', 'plupload', 'underscore', 'wp-a11y', 'wp-i18n' ) );
1001        did_action( 'init' ) && $scripts->localize( 'plupload-handlers', 'pluploadL10n', $uploader_l10n );
1002
1003        $scripts->add( 'wp-plupload', "/wp-includes/js/plupload/wp-plupload$suffix.js", array( 'plupload', 'jquery', 'media-models' ), false, 1 );
1004        did_action( 'init' ) && $scripts->localize( 'wp-plupload', 'pluploadL10n', $uploader_l10n );
1005
1006        $scripts->add( 'comment-reply', "/wp-includes/js/comment-reply$suffix.js", array(), false, 1 );
1007        if ( did_action( 'init' ) ) {
1008                $scripts->add_data( 'comment-reply', 'strategy', 'async' );
1009                $scripts->add_data( 'comment-reply', 'fetchpriority', 'low' ); // In Chrome this is automatically low due to the async strategy, but in Firefox and Safari the priority is normal/medium.
1010        }
1011
1012        // Not used in core, obsolete. Registered for backward compatibility.
1013        $scripts->add( 'json2', "/wp-includes/js/json2$suffix.js", array(), '2015-05-03' );
1014        did_action( 'init' ) && $scripts->add_data( 'json2', 'conditional', '_required-conditional-dependency_' );
1015
1016        $scripts->add( 'underscore', "/wp-includes/js/underscore$dev_suffix.js", array(), '1.13.7', 1 );
1017        $scripts->add( 'backbone', "/wp-includes/js/backbone$dev_suffix.js", array( 'underscore', 'jquery' ), '1.6.0', 1 );
1018
1019        $scripts->add( 'wp-util', "/wp-includes/js/wp-util$suffix.js", array( 'underscore', 'jquery' ), false, 1 );
1020        did_action( 'init' ) && $scripts->localize(
1021                'wp-util',
1022                '_wpUtilSettings',
1023                array(
1024                        'ajax' => array(
1025                                'url' => admin_url( 'admin-ajax.php', 'relative' ),
1026                        ),
1027                )
1028        );
1029
1030        $scripts->add( 'wp-backbone', "/wp-includes/js/wp-backbone$suffix.js", array( 'backbone', 'wp-util' ), false, 1 );
1031
1032        $scripts->add( 'revisions', "/wp-admin/js/revisions$suffix.js", array( 'wp-backbone', 'jquery-ui-slider', 'hoverIntent' ), false, 1 );
1033
1034        $scripts->add( 'imgareaselect', "/wp-includes/js/imgareaselect/jquery.imgareaselect$suffix.js", array( 'jquery' ), false, 1 );
1035
1036        $scripts->add( 'mediaelement', false, array( 'jquery', 'mediaelement-core', 'mediaelement-migrate' ), '4.2.17', 1 );
1037        $scripts->add( 'mediaelement-core', "/wp-includes/js/mediaelement/mediaelement-and-player$suffix.js", array(), '4.2.17', 1 );
1038        $scripts->add( 'mediaelement-migrate', "/wp-includes/js/mediaelement/mediaelement-migrate$suffix.js", array(), false, 1 );
1039
1040        did_action( 'init' ) && $scripts->add_inline_script(
1041                'mediaelement-core',
1042                sprintf(
1043                        'var mejsL10n = %s;',
1044                        wp_json_encode(
1045                                array(
1046                                        'language' => strtolower( strtok( determine_locale(), '_-' ) ),
1047                                        'strings'  => array(
1048                                                'mejs.download-file'       => __( 'Download File' ),
1049                                                'mejs.install-flash'       => __( 'You are using a browser that does not have Flash player enabled or installed. Please turn on your Flash player plugin or download the latest version from https://get.adobe.com/flashplayer/' ),
1050                                                'mejs.fullscreen'          => __( 'Fullscreen' ),
1051                                                'mejs.play'                => __( 'Play' ),
1052                                                'mejs.pause'               => __( 'Pause' ),
1053                                                'mejs.time-slider'         => __( 'Time Slider' ),
1054                                                'mejs.time-help-text'      => __( 'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.' ),
1055                                                'mejs.live-broadcast'      => __( 'Live Broadcast' ),
1056                                                'mejs.volume-help-text'    => __( 'Use Up/Down Arrow keys to increase or decrease volume.' ),
1057                                                'mejs.unmute'              => __( 'Unmute' ),
1058                                                'mejs.mute'                => __( 'Mute' ),
1059                                                'mejs.volume-slider'       => __( 'Volume Slider' ),
1060                                                'mejs.video-player'        => __( 'Video Player' ),
1061                                                'mejs.audio-player'        => __( 'Audio Player' ),
1062                                                'mejs.captions-subtitles'  => __( 'Captions/Subtitles' ),
1063                                                'mejs.captions-chapters'   => __( 'Chapters' ),
1064                                                'mejs.none'                => __( 'None' ),
1065                                                'mejs.afrikaans'           => __( 'Afrikaans' ),
1066                                                'mejs.albanian'            => __( 'Albanian' ),
1067                                                'mejs.arabic'              => __( 'Arabic' ),
1068                                                'mejs.belarusian'          => __( 'Belarusian' ),
1069                                                'mejs.bulgarian'           => __( 'Bulgarian' ),
1070                                                'mejs.catalan'             => __( 'Catalan' ),
1071                                                'mejs.chinese'             => __( 'Chinese' ),
1072                                                'mejs.chinese-simplified'  => __( 'Chinese (Simplified)' ),
1073                                                'mejs.chinese-traditional' => __( 'Chinese (Traditional)' ),
1074                                                'mejs.croatian'            => __( 'Croatian' ),
1075                                                'mejs.czech'               => __( 'Czech' ),
1076                                                'mejs.danish'              => __( 'Danish' ),
1077                                                'mejs.dutch'               => __( 'Dutch' ),
1078                                                'mejs.english'             => __( 'English' ),
1079                                                'mejs.estonian'            => __( 'Estonian' ),
1080                                                'mejs.filipino'            => __( 'Filipino' ),
1081                                                'mejs.finnish'             => __( 'Finnish' ),
1082                                                'mejs.french'              => __( 'French' ),
1083                                                'mejs.galician'            => __( 'Galician' ),
1084                                                'mejs.german'              => __( 'German' ),
1085                                                'mejs.greek'               => __( 'Greek' ),
1086                                                'mejs.haitian-creole'      => __( 'Haitian Creole' ),
1087                                                'mejs.hebrew'              => __( 'Hebrew' ),
1088                                                'mejs.hindi'               => __( 'Hindi' ),
1089                                                'mejs.hungarian'           => __( 'Hungarian' ),
1090                                                'mejs.icelandic'           => __( 'Icelandic' ),
1091                                                'mejs.indonesian'          => __( 'Indonesian' ),
1092                                                'mejs.irish'               => __( 'Irish' ),
1093                                                'mejs.italian'             => __( 'Italian' ),
1094                                                'mejs.japanese'            => __( 'Japanese' ),
1095                                                'mejs.korean'              => __( 'Korean' ),
1096                                                'mejs.latvian'             => __( 'Latvian' ),
1097                                                'mejs.lithuanian'          => __( 'Lithuanian' ),
1098                                                'mejs.macedonian'          => __( 'Macedonian' ),
1099                                                'mejs.malay'               => __( 'Malay' ),
1100                                                'mejs.maltese'             => __( 'Maltese' ),
1101                                                'mejs.norwegian'           => __( 'Norwegian' ),
1102                                                'mejs.persian'             => __( 'Persian' ),
1103                                                'mejs.polish'              => __( 'Polish' ),
1104                                                'mejs.portuguese'          => __( 'Portuguese' ),
1105                                                'mejs.romanian'            => __( 'Romanian' ),
1106                                                'mejs.russian'             => __( 'Russian' ),
1107                                                'mejs.serbian'             => __( 'Serbian' ),
1108                                                'mejs.slovak'              => __( 'Slovak' ),
1109                                                'mejs.slovenian'           => __( 'Slovenian' ),
1110                                                'mejs.spanish'             => __( 'Spanish' ),
1111                                                'mejs.swahili'             => __( 'Swahili' ),
1112                                                'mejs.swedish'             => __( 'Swedish' ),
1113                                                'mejs.tagalog'             => __( 'Tagalog' ),
1114                                                'mejs.thai'                => __( 'Thai' ),
1115                                                'mejs.turkish'             => __( 'Turkish' ),
1116                                                'mejs.ukrainian'           => __( 'Ukrainian' ),
1117                                                'mejs.vietnamese'          => __( 'Vietnamese' ),
1118                                                'mejs.welsh'               => __( 'Welsh' ),
1119                                                'mejs.yiddish'             => __( 'Yiddish' ),
1120                                        ),
1121                                ),
1122                                JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
1123                        )
1124                ),
1125                'before'
1126        );
1127
1128        $scripts->add( 'mediaelement-vimeo', '/wp-includes/js/mediaelement/renderers/vimeo.min.js', array( 'mediaelement' ), '4.2.17', 1 );
1129        $scripts->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement$suffix.js", array( 'mediaelement' ), false, 1 );
1130        $mejs_settings = array(
1131                'pluginPath'            => includes_url( 'js/mediaelement/', 'relative' ),
1132                'classPrefix'           => 'mejs-',
1133                'stretching'            => 'responsive',
1134                /** This filter is documented in wp-includes/media.php */
1135                'audioShortcodeLibrary' => apply_filters( 'wp_audio_shortcode_library', 'mediaelement' ),
1136                /** This filter is documented in wp-includes/media.php */
1137                'videoShortcodeLibrary' => apply_filters( 'wp_video_shortcode_library', 'mediaelement' ),
1138        );
1139        did_action( 'init' ) && $scripts->localize(
1140                'mediaelement',
1141                '_wpmejsSettings',
1142                /**
1143                 * Filters the MediaElement configuration settings.
1144                 *
1145                 * @since 4.4.0
1146                 *
1147                 * @param array $mejs_settings MediaElement settings array.
1148                 */
1149                apply_filters( 'mejs_settings', $mejs_settings )
1150        );
1151
1152        $scripts->add( 'wp-codemirror', '/wp-includes/js/codemirror/codemirror.min.js', array(), '5.29.1-alpha-ee20357' );
1153        $scripts->add( 'csslint', '/wp-includes/js/codemirror/csslint.js', array(), '1.0.5' );
1154        $scripts->add( 'esprima', '/wp-includes/js/codemirror/esprima.js', array(), '4.0.0' );
1155        $scripts->add( 'jshint', '/wp-includes/js/codemirror/fakejshint.js', array( 'esprima' ), '2.9.5' );
1156        $scripts->add( 'jsonlint', '/wp-includes/js/codemirror/jsonlint.js', array(), '1.6.2' );
1157        $scripts->add( 'htmlhint', '/wp-includes/js/codemirror/htmlhint.js', array(), '0.9.14-xwp' );
1158        $scripts->add( 'htmlhint-kses', '/wp-includes/js/codemirror/htmlhint-kses.js', array( 'htmlhint' ) );
1159        $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'wp-codemirror', 'underscore' ) );
1160        $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'common', 'wp-util', 'wp-sanitize', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ), false, 1 );
1161        $scripts->set_translations( 'wp-theme-plugin-editor' );
1162
1163        $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist$suffix.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
1164
1165        $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' );
1166        did_action( 'init' ) && $scripts->localize(
1167                'zxcvbn-async',
1168                '_zxcvbnSettings',
1169                array(
1170                        'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',
1171                )
1172        );
1173
1174        $scripts->add( 'password-strength-meter', "/wp-admin/js/password-strength-meter$suffix.js", array( 'jquery', 'zxcvbn-async' ), false, 1 );
1175        did_action( 'init' ) && $scripts->localize(
1176                'password-strength-meter',
1177                'pwsL10n',
1178                array(
1179                        'unknown'  => _x( 'Password strength unknown', 'password strength' ),
1180                        'short'    => _x( 'Very weak', 'password strength' ),
1181                        'bad'      => _x( 'Weak', 'password strength' ),
1182                        'good'     => _x( 'Medium', 'password strength' ),
1183                        'strong'   => _x( 'Strong', 'password strength' ),
1184                        'mismatch' => _x( 'Mismatch', 'password mismatch' ),
1185                )
1186        );
1187        $scripts->set_translations( 'password-strength-meter' );
1188
1189        $scripts->add( 'password-toggle', "/wp-admin/js/password-toggle$suffix.js", array(), false, 1 );
1190        $scripts->set_translations( 'password-toggle' );
1191
1192        $scripts->add( 'application-passwords', "/wp-admin/js/application-passwords$suffix.js", array( 'jquery', 'wp-util', 'wp-api-request', 'wp-date', 'wp-i18n', 'wp-hooks' ), false, 1 );
1193        $scripts->set_translations( 'application-passwords' );
1194
1195        $scripts->add( 'auth-app', "/wp-admin/js/auth-app$suffix.js", array( 'jquery', 'wp-api-request', 'wp-i18n', 'wp-hooks' ), false, 1 );
1196        $scripts->set_translations( 'auth-app' );
1197
1198        $scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'clipboard', 'jquery', 'password-strength-meter', 'wp-util', 'wp-a11y' ), false, 1 );
1199        $scripts->set_translations( 'user-profile' );
1200        $user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0;
1201        did_action( 'init' ) && $scripts->localize(
1202                'user-profile',
1203                'userProfileL10n',
1204                array(
1205                        'user_id' => $user_id,
1206                        'nonce'   => wp_installing() ? '' : wp_create_nonce( 'reset-password-for-' . $user_id ),
1207                )
1208        );
1209
1210        $scripts->add( 'language-chooser', "/wp-admin/js/language-chooser$suffix.js", array( 'jquery' ), false, 1 );
1211
1212        $scripts->add( 'user-suggest', "/wp-admin/js/user-suggest$suffix.js", array( 'jquery-ui-autocomplete' ), false, 1 );
1213
1214        $scripts->add( 'admin-bar', "/wp-includes/js/admin-bar$suffix.js", array( 'hoverintent-js' ), false, 1 );
1215
1216        $scripts->add( 'wplink', "/wp-includes/js/wplink$suffix.js", array( 'common', 'jquery', 'wp-a11y', 'wp-i18n' ), false, 1 );
1217        $scripts->set_translations( 'wplink' );
1218        did_action( 'init' ) && $scripts->localize(
1219                'wplink',
1220                'wpLinkL10n',
1221                array(
1222                        'title'          => __( 'Insert/edit link' ),
1223                        'update'         => __( 'Update' ),
1224                        'save'           => __( 'Add Link' ),
1225                        'noTitle'        => __( '(no title)' ),
1226                        'noMatchesFound' => __( 'No results found.' ),
1227                        'linkSelected'   => __( 'Link selected.' ),
1228                        'linkInserted'   => __( 'Link inserted.' ),
1229                        /* translators: Minimum input length in characters to start searching posts in the "Insert/edit link" modal. */
1230                        'minInputLength' => (int) _x( '3', 'minimum input length for searching post links' ),
1231                )
1232        );
1233
1234        $scripts->add( 'wpdialogs', "/wp-includes/js/wpdialog$suffix.js", array( 'jquery-ui-dialog' ), false, 1 );
1235
1236        $scripts->add( 'word-count', "/wp-admin/js/word-count$suffix.js", array(), false, 1 );
1237
1238        $scripts->add( 'media-upload', "/wp-admin/js/media-upload$suffix.js", array( 'thickbox', 'shortcode' ), false, 1 );
1239
1240        $scripts->add( 'hoverIntent', "/wp-includes/js/hoverIntent$suffix.js", array( 'jquery' ), '1.10.2', 1 );
1241
1242        // JS-only version of hoverintent (no dependencies).
1243        $scripts->add( 'hoverintent-js', '/wp-includes/js/hoverintent-js.min.js', array(), '2.2.1', 1 );
1244
1245        $scripts->add( 'customize-base', "/wp-includes/js/customize-base$suffix.js", array( 'jquery', 'underscore' ), false, 1 );
1246        $scripts->add( 'customize-loader', "/wp-includes/js/customize-loader$suffix.js", array( 'customize-base' ), false, 1 );
1247        $scripts->add( 'customize-preview', "/wp-includes/js/customize-preview$suffix.js", array( 'wp-a11y', 'customize-base' ), false, 1 );
1248        $scripts->add( 'customize-models', '/wp-includes/js/customize-models.js', array( 'underscore', 'backbone' ), false, 1 );
1249        $scripts->add( 'customize-views', '/wp-includes/js/customize-views.js', array( 'jquery', 'underscore', 'imgareaselect', 'customize-models', 'media-editor', 'media-views' ), false, 1 );
1250        $scripts->add( 'customize-controls', "/wp-admin/js/customize-controls$suffix.js", array( 'customize-base', 'wp-a11y', 'wp-util', 'jquery-ui-core' ), false, 1 );
1251        did_action( 'init' ) && $scripts->localize(
1252                'customize-controls',
1253                '_wpCustomizeControlsL10n',
1254                array(
1255                        'activate'                => __( 'Activate &amp; Publish' ),
1256                        'save'                    => __( 'Save &amp; Publish' ), // @todo Remove as not required.
1257                        'publish'                 => __( 'Publish' ),
1258                        'published'               => __( 'Published' ),
1259                        'saveDraft'               => __( 'Save Draft' ),
1260                        'draftSaved'              => __( 'Draft Saved' ),
1261                        'updating'                => __( 'Updating' ),
1262                        'schedule'                => _x( 'Schedule', 'customizer changeset action/button label' ),
1263                        'scheduled'               => _x( 'Scheduled', 'customizer changeset status' ),
1264                        'invalid'                 => __( 'Invalid' ),
1265                        'saveBeforeShare'         => __( 'Please save your changes in order to share the preview.' ),
1266                        'futureDateError'         => __( 'You must supply a future date to schedule.' ),
1267                        'saveAlert'               => __( 'The changes you made will be lost if you navigate away from this page.' ),
1268                        'saved'                   => __( 'Saved' ),
1269                        'cancel'                  => __( 'Cancel' ),
1270                        'close'                   => __( 'Close' ),
1271                        'action'                  => __( 'Action' ),
1272                        'discardChanges'          => __( 'Discard changes' ),
1273                        'cheatin'                 => __( 'An error occurred. Please try again later.' ),
1274                        'notAllowedHeading'       => __( 'You need a higher level of permission.' ),
1275                        'notAllowed'              => __( 'Sorry, you are not allowed to customize this site.' ),
1276                        'previewIframeTitle'      => __( 'Site Preview' ),
1277                        'loginIframeTitle'        => __( 'Session expired' ),
1278                        'collapseSidebar'         => _x( 'Hide Controls', 'label for hide controls button without length constraints' ),
1279                        'expandSidebar'           => _x( 'Show Controls', 'label for hide controls button without length constraints' ),
1280                        'untitledBlogName'        => __( '(Untitled)' ),
1281                        'unknownRequestFail'      => __( 'Looks like something&#8217;s gone wrong. Wait a couple seconds, and then try again.' ),
1282                        'themeDownloading'        => __( 'Downloading your new theme&hellip;' ),
1283                        'themePreviewWait'        => __( 'Setting up your live preview. This may take a bit.' ),
1284                        'revertingChanges'        => __( 'Reverting unpublished changes&hellip;' ),
1285                        'trashConfirm'            => __( 'Are you sure you want to discard your unpublished changes?' ),
1286                        /* translators: %s: Display name of the user who has taken over the changeset in customizer. */
1287                        'takenOverMessage'        => __( '%s has taken over and is currently customizing.' ),
1288                        /* translators: %s: URL to the Customizer to load the autosaved version. */
1289                        'autosaveNotice'          => __( 'There is a more recent autosave of your changes than the one you are previewing. <a href="%s">Restore the autosave</a>' ),
1290                        'videoHeaderNotice'       => __( 'This theme does not support video headers on this page. Navigate to the front page or another page that supports video headers.' ),
1291                        // Used for overriding the file types allowed in Plupload.
1292                        'allowedFiles'            => __( 'Allowed Files' ),
1293                        'customCssError'          => array(
1294                                /* translators: %d: Error count. */
1295                                'singular' => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 1 ),
1296                                /* translators: %d: Error count. */
1297                                'plural'   => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 2 ),
1298                                // @todo This is lacking, as some languages have a dedicated dual form. For proper handling of plurals in JS, see #20491.
1299                        ),
1300                        'pageOnFrontError'        => __( 'Homepage and posts page must be different.' ),
1301                        'saveBlockedError'        => array(
1302                                /* translators: %s: Number of invalid settings. */
1303                                'singular' => _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', 1 ),
1304                                /* translators: %s: Number of invalid settings. */
1305                                'plural'   => _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', 2 ),
1306                                // @todo This is lacking, as some languages have a dedicated dual form. For proper handling of plurals in JS, see #20491.
1307                        ),
1308                        'scheduleDescription'     => __( 'Schedule your customization changes to publish ("go live") at a future date.' ),
1309                        'themePreviewUnavailable' => __( 'Sorry, you cannot preview new themes when you have changes scheduled or saved as a draft. Please publish your changes, or wait until they publish to preview new themes.' ),
1310                        'themeInstallUnavailable' => sprintf(
1311                                /* translators: %s: URL to Add Themes admin screen. */
1312                                __( 'You will not be able to install new themes from here yet since your install requires SFTP credentials. For now, please <a href="%s">add themes in the admin</a>.' ),
1313                                esc_url( admin_url( 'theme-install.php' ) )
1314                        ),
1315                        'publishSettings'         => __( 'Publish Settings' ),
1316                        'invalidDate'             => __( 'Invalid date.' ),
1317                        'invalidValue'            => __( 'Invalid value.' ),
1318                        'blockThemeNotification'  => sprintf(
1319                                /* translators: 1: Link to Site Editor documentation on HelpHub, 2: HTML button. */
1320                                __( 'Hurray! Your theme supports site editing with blocks. <a href="%1$s">Tell me more</a>. %2$s' ),
1321                                __( 'https://wordpress.org/documentation/article/site-editor/' ),
1322                                sprintf(
1323                                        '<button type="button" data-action="%1$s" class="button switch-to-editor">%2$s</button>',
1324                                        esc_url( admin_url( 'site-editor.php' ) ),
1325                                        __( 'Use Site Editor' )
1326                                )
1327                        ),
1328                )
1329        );
1330        $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 );
1331
1332        $scripts->add( 'customize-widgets', "/wp-admin/js/customize-widgets$suffix.js", array( 'jquery', 'jquery-ui-sortable', 'jquery-ui-droppable', 'wp-backbone', 'customize-controls' ), false, 1 );
1333        $scripts->add( 'customize-preview-widgets', "/wp-includes/js/customize-preview-widgets$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 );
1334
1335        $scripts->add( 'customize-nav-menus', "/wp-admin/js/customize-nav-menus$suffix.js", array( 'jquery', 'wp-backbone', 'customize-controls', 'accordion', 'nav-menu', 'wp-sanitize' ), false, 1 );
1336        $scripts->add( 'customize-preview-nav-menus', "/wp-includes/js/customize-preview-nav-menus$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 );
1337
1338        $scripts->add( 'wp-custom-header', "/wp-includes/js/wp-custom-header$suffix.js", array( 'wp-a11y' ), false, 1 );
1339
1340        $scripts->add( 'accordion', "/wp-admin/js/accordion$suffix.js", array( 'jquery' ), false, 1 );
1341
1342        $scripts->add( 'shortcode', "/wp-includes/js/shortcode$suffix.js", array( 'underscore' ), false, 1 );
1343        $scripts->add( 'media-models', "/wp-includes/js/media-models$suffix.js", array( 'wp-backbone' ), false, 1 );
1344        did_action( 'init' ) && $scripts->localize(
1345                'media-models',
1346                '_wpMediaModelsL10n',
1347                array(
1348                        'settings' => array(
1349                                'ajaxurl' => admin_url( 'admin-ajax.php', 'relative' ),
1350                                'post'    => array( 'id' => 0 ),
1351                        ),
1352                )
1353        );
1354
1355        $scripts->add( 'wp-embed', "/wp-includes/js/wp-embed$suffix.js" );
1356        did_action( 'init' ) && $scripts->add_data( 'wp-embed', 'strategy', 'defer' );
1357
1358        /*
1359         * To enqueue media-views or media-editor, call wp_enqueue_media().
1360         * Both rely on numerous settings, styles, and templates to operate correctly.
1361         */
1362        $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement', 'wp-api-request', 'wp-a11y', 'clipboard' ), false, 1 );
1363        $scripts->set_translations( 'media-views' );
1364
1365        $scripts->add( 'media-editor', "/wp-includes/js/media-editor$suffix.js", array( 'shortcode', 'media-views' ), false, 1 );
1366        $scripts->set_translations( 'media-editor' );
1367        $scripts->add( 'media-audiovideo', "/wp-includes/js/media-audiovideo$suffix.js", array( 'media-editor' ), false, 1 );
1368        $scripts->add( 'mce-view', "/wp-includes/js/mce-view$suffix.js", array( 'shortcode', 'jquery', 'media-views', 'media-audiovideo' ), false, 1 );
1369
1370        $scripts->add( 'wp-api', "/wp-includes/js/wp-api$suffix.js", array( 'jquery', 'backbone', 'underscore', 'wp-api-request' ), false, 1 );
1371
1372        if ( is_admin() ) {
1373                $scripts->add( 'admin-tags', "/wp-admin/js/tags$suffix.js", array( 'jquery', 'wp-ajax-response' ), false, 1 );
1374                $scripts->set_translations( 'admin-tags' );
1375
1376                $scripts->add( 'admin-comments', "/wp-admin/js/edit-comments$suffix.js", array( 'wp-lists', 'quicktags', 'jquery-query', 'wp-a11y' ), false, 1 );
1377                $scripts->set_translations( 'admin-comments' );
1378                did_action( 'init' ) && $scripts->localize(
1379                        'admin-comments',
1380                        'adminCommentsSettings',
1381                        array(
1382                                'hotkeys_highlight_first' => isset( $_GET['hotkeys_highlight_first'] ),
1383                                'hotkeys_highlight_last'  => isset( $_GET['hotkeys_highlight_last'] ),
1384                        )
1385                );
1386
1387                $scripts->add( 'xfn', "/wp-admin/js/xfn$suffix.js", array( 'jquery' ), false, 1 );
1388
1389                $scripts->add( 'postbox', "/wp-admin/js/postbox$suffix.js", array( 'jquery-ui-sortable', 'wp-a11y' ), false, 1 );
1390                $scripts->set_translations( 'postbox' );
1391
1392                $scripts->add( 'tags-box', "/wp-admin/js/tags-box$suffix.js", array( 'jquery', 'tags-suggest' ), false, 1 );
1393                $scripts->set_translations( 'tags-box' );
1394
1395                $scripts->add( 'tags-suggest', "/wp-admin/js/tags-suggest$suffix.js", array( 'common', 'jquery-ui-autocomplete', 'wp-a11y', 'wp-i18n' ), false, 1 );
1396                $scripts->set_translations( 'tags-suggest' );
1397
1398                $scripts->add( 'post', "/wp-admin/js/post$suffix.js", array( 'suggest', 'wp-lists', 'postbox', 'tags-box', 'underscore', 'word-count', 'wp-a11y', 'wp-sanitize', 'clipboard' ), false, 1 );
1399                $scripts->set_translations( 'post' );
1400
1401                $scripts->add( 'editor-expand', "/wp-admin/js/editor-expand$suffix.js", array( 'jquery', 'underscore' ), false, 1 );
1402
1403                $scripts->add( 'link', "/wp-admin/js/link$suffix.js", array( 'wp-lists', 'postbox' ), false, 1 );
1404
1405                $scripts->add( 'comment', "/wp-admin/js/comment$suffix.js", array( 'jquery', 'postbox' ), false, 1 );
1406                $scripts->set_translations( 'comment' );
1407
1408                $scripts->add( 'admin-gallery', "/wp-admin/js/gallery$suffix.js", array( 'jquery-ui-sortable' ) );
1409
1410                $scripts->add( 'admin-widgets', "/wp-admin/js/widgets$suffix.js", array( 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable', 'wp-a11y' ), false, 1 );
1411                $scripts->set_translations( 'admin-widgets' );
1412
1413                $scripts->add( 'media-widgets', "/wp-admin/js/widgets/media-widgets$suffix.js", array( 'jquery', 'media-models', 'media-views', 'wp-api-request' ) );
1414                $scripts->add_inline_script( 'media-widgets', 'wp.mediaWidgets.init();', 'after' );
1415
1416                $scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) );
1417                $scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) );
1418                $scripts->add( 'media-gallery-widget', "/wp-admin/js/widgets/media-gallery-widget$suffix.js", array( 'media-widgets' ) );
1419                $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo', 'wp-api-request' ) );
1420                $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) );
1421                $scripts->add( 'custom-html-widgets', "/wp-admin/js/widgets/custom-html-widgets$suffix.js", array( 'jquery', 'backbone', 'wp-util', 'jquery-ui-core', 'wp-a11y' ) );
1422
1423                $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y', 'customize-base' ), false, 1 );
1424
1425                $scripts->add( 'inline-edit-post', "/wp-admin/js/inline-edit-post$suffix.js", array( 'jquery', 'tags-suggest', 'wp-a11y' ), false, 1 );
1426                $scripts->set_translations( 'inline-edit-post' );
1427
1428                $scripts->add( 'inline-edit-tax', "/wp-admin/js/inline-edit-tax$suffix.js", array( 'jquery', 'wp-a11y' ), false, 1 );
1429                $scripts->set_translations( 'inline-edit-tax' );
1430
1431                $scripts->add( 'plugin-install', "/wp-admin/js/plugin-install$suffix.js", array( 'jquery', 'jquery-ui-core', 'thickbox' ), false, 1 );
1432                $scripts->set_translations( 'plugin-install' );
1433
1434                $scripts->add( 'site-health', "/wp-admin/js/site-health$suffix.js", array( 'clipboard', 'jquery', 'wp-util', 'wp-a11y', 'wp-api-request', 'wp-url', 'wp-i18n', 'wp-hooks' ), false, 1 );
1435                $scripts->set_translations( 'site-health' );
1436
1437                $scripts->add( 'privacy-tools', "/wp-admin/js/privacy-tools$suffix.js", array( 'jquery', 'wp-a11y' ), false, 1 );
1438                $scripts->set_translations( 'privacy-tools' );
1439
1440                $scripts->add( 'updates', "/wp-admin/js/updates$suffix.js", array( 'common', 'jquery', 'wp-util', 'wp-a11y', 'wp-sanitize', 'wp-i18n' ), false, 1 );
1441                $scripts->set_translations( 'updates' );
1442                did_action( 'init' ) && $scripts->localize(
1443                        'updates',
1444                        '_wpUpdatesSettings',
1445                        array(
1446                                'ajax_nonce' => wp_installing() ? '' : wp_create_nonce( 'updates' ),
1447                        )
1448                );
1449
1450                $scripts->add( 'farbtastic', '/wp-admin/js/farbtastic.js', array( 'jquery' ), '1.2' );
1451
1452                $scripts->add( 'iris', '/wp-admin/js/iris.min.js', array( 'jquery-ui-draggable', 'jquery-ui-slider', 'jquery-touch-punch' ), '1.1.1', 1 );
1453                $scripts->add( 'wp-color-picker', "/wp-admin/js/color-picker$suffix.js", array( 'iris' ), false, 1 );
1454                $scripts->set_translations( 'wp-color-picker' );
1455
1456                $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'common', 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y', 'wp-date' ), false, 1 );
1457                $scripts->set_translations( 'dashboard' );
1458
1459                $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" );
1460
1461                $scripts->add( 'media-grid', "/wp-includes/js/media-grid$suffix.js", array( 'media-editor' ), false, 1 );
1462                $scripts->add( 'media', "/wp-admin/js/media$suffix.js", array( 'jquery', 'clipboard', 'wp-i18n', 'wp-a11y' ), false, 1 );
1463                $scripts->set_translations( 'media' );
1464
1465                $scripts->add( 'image-edit', "/wp-admin/js/image-edit$suffix.js", array( 'jquery', 'jquery-ui-core', 'imgareaselect', 'wp-a11y' ), false, 1 );
1466                $scripts->set_translations( 'image-edit' );
1467
1468                $scripts->add( 'set-post-thumbnail', "/wp-admin/js/set-post-thumbnail$suffix.js", array( 'jquery' ), false, 1 );
1469                $scripts->set_translations( 'set-post-thumbnail' );
1470
1471                /*
1472                 * Navigation Menus: Adding underscore as a dependency to utilize _.debounce
1473                 * see https://core.trac.wordpress.org/ticket/42321
1474                 */
1475                $scripts->add( 'nav-menu', "/wp-admin/js/nav-menu$suffix.js", array( 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable', 'wp-lists', 'postbox', 'underscore' ) );
1476                $scripts->set_translations( 'nav-menu' );
1477
1478                $scripts->add( 'custom-header', '/wp-admin/js/custom-header.js', array( 'jquery-masonry' ), false, 1 );
1479                $scripts->add( 'custom-background', "/wp-admin/js/custom-background$suffix.js", array( 'wp-color-picker', 'media-views' ), false, 1 );
1480                $scripts->add( 'media-gallery', "/wp-admin/js/media-gallery$suffix.js", array( 'jquery' ), false, 1 );
1481
1482                $scripts->add( 'svg-painter', '/wp-admin/js/svg-painter.js', array( 'jquery' ), false, 1 );
1483        }
1484}
1485
1486/**
1487 * Assigns default styles to $styles object.
1488 *
1489 * Nothing is returned, because the $styles parameter is passed by reference.
1490 * Meaning that whatever object is passed will be updated without having to
1491 * reassign the variable that was passed back to the same value. This saves
1492 * memory.
1493 *
1494 * Adding default styles is not the only task, it also assigns the base_url
1495 * property, the default version, and text direction for the object.
1496 *
1497 * @since 2.6.0
1498 *
1499 * @global array $editor_styles
1500 *
1501 * @param WP_Styles $styles
1502 */
1503function wp_default_styles( $styles ) {
1504        global $editor_styles;
1505
1506        /*
1507         * Include an unmodified $wp_version.
1508         *
1509         * Note: wp_get_wp_version() is not used here, as this file can be included
1510         * via wp-admin/load-scripts.php or wp-admin/load-styles.php, in which case
1511         * wp-includes/functions.php is not loaded.
1512         */
1513        require ABSPATH . WPINC . '/version.php';
1514
1515        if ( ! defined( 'SCRIPT_DEBUG' ) ) {
1516                /*
1517                 * Note: str_contains() is not used here, as this file can be included
1518                 * via wp-admin/load-scripts.php or wp-admin/load-styles.php, in which case
1519                 * the polyfills from wp-includes/compat.php are not loaded.
1520                 */
1521                define( 'SCRIPT_DEBUG', false !== strpos( $wp_version, '-src' ) );
1522        }
1523
1524        $guessurl = site_url();
1525
1526        if ( ! $guessurl ) {
1527                $guessurl = wp_guess_url();
1528        }
1529
1530        $styles->base_url        = $guessurl;
1531        $styles->content_url     = defined( 'WP_CONTENT_URL' ) ? WP_CONTENT_URL : '';
1532        $styles->default_version = get_bloginfo( 'version' );
1533        $styles->text_direction  = function_exists( 'is_rtl' ) && is_rtl() ? 'rtl' : 'ltr';
1534        $styles->default_dirs    = array( '/wp-admin/', '/wp-includes/css/' );
1535
1536        // Open Sans is no longer used by core, but may be relied upon by themes and plugins.
1537        $open_sans_font_url = '';
1538
1539        /*
1540         * translators: If there are characters in your language that are not supported
1541         * by Open Sans, translate this to 'off'. Do not translate into your own language.
1542         */
1543        if ( 'off' !== _x( 'on', 'Open Sans font: on or off' ) ) {
1544                $subsets = 'latin,latin-ext';
1545
1546                /*
1547                 * translators: To add an additional Open Sans character subset specific to your language,
1548                 * translate this to 'greek', 'cyrillic' or 'vietnamese'. Do not translate into your own language.
1549                 */
1550                $subset = _x( 'no-subset', 'Open Sans font: add new subset (greek, cyrillic, vietnamese)' );
1551
1552                if ( 'cyrillic' === $subset ) {
1553                        $subsets .= ',cyrillic,cyrillic-ext';
1554                } elseif ( 'greek' === $subset ) {
1555                        $subsets .= ',greek,greek-ext';
1556                } elseif ( 'vietnamese' === $subset ) {
1557                        $subsets .= ',vietnamese';
1558                }
1559
1560                // Hotlink Open Sans, for now.
1561                $open_sans_font_url = "https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,300,400,600&subset=$subsets&display=fallback";
1562        }
1563
1564        // Register a stylesheet for the selected admin color scheme.
1565        $styles->add( 'colors', true, array( 'wp-admin', 'buttons' ) );
1566
1567        $suffix = SCRIPT_DEBUG ? '' : '.min';
1568
1569        // Admin CSS.
1570        $styles->add( 'common', "/wp-admin/css/common$suffix.css" );
1571        $styles->add( 'forms', "/wp-admin/css/forms$suffix.css" );
1572        $styles->add( 'admin-menu', "/wp-admin/css/admin-menu$suffix.css" );
1573        $styles->add( 'dashboard', "/wp-admin/css/dashboard$suffix.css" );
1574        $styles->add( 'list-tables', "/wp-admin/css/list-tables$suffix.css" );
1575        $styles->add( 'edit', "/wp-admin/css/edit$suffix.css" );
1576        $styles->add( 'revisions', "/wp-admin/css/revisions$suffix.css" );
1577        $styles->add( 'media', "/wp-admin/css/media$suffix.css" );
1578        $styles->add( 'themes', "/wp-admin/css/themes$suffix.css" );
1579        $styles->add( 'about', "/wp-admin/css/about$suffix.css" );
1580        $styles->add( 'nav-menus', "/wp-admin/css/nav-menus$suffix.css" );
1581        $styles->add( 'widgets', "/wp-admin/css/widgets$suffix.css", array( 'wp-pointer' ) );
1582        $styles->add( 'site-icon', "/wp-admin/css/site-icon$suffix.css" );
1583        $styles->add( 'l10n', "/wp-admin/css/l10n$suffix.css" );
1584        $styles->add( 'code-editor', "/wp-admin/css/code-editor$suffix.css", array( 'wp-codemirror' ) );
1585        $styles->add( 'site-health', "/wp-admin/css/site-health$suffix.css" );
1586
1587        $styles->add( 'wp-admin', false, array( 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus', 'widgets', 'site-icon', 'l10n' ) );
1588
1589        $styles->add( 'login', "/wp-admin/css/login$suffix.css", array( 'dashicons', 'buttons', 'forms', 'l10n' ) );
1590        $styles->add( 'install', "/wp-admin/css/install$suffix.css", array( 'dashicons', 'buttons', 'forms', 'l10n' ) );
1591        $styles->add( 'wp-color-picker', "/wp-admin/css/color-picker$suffix.css" );
1592        $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'imgareaselect' ) );
1593        $styles->add( 'customize-widgets', "/wp-admin/css/customize-widgets$suffix.css", array( 'wp-admin', 'colors' ) );
1594        $styles->add( 'customize-nav-menus', "/wp-admin/css/customize-nav-menus$suffix.css", array( 'wp-admin', 'colors' ) );
1595
1596        // Common dependencies.
1597        $styles->add( 'buttons', "/wp-includes/css/buttons$suffix.css" );
1598        $styles->add( 'dashicons', "/wp-includes/css/dashicons$suffix.css" );
1599
1600        // Includes CSS.
1601        $styles->add( 'admin-bar', "/wp-includes/css/admin-bar$suffix.css", array( 'dashicons' ) );
1602        $styles->add( 'wp-auth-check', "/wp-includes/css/wp-auth-check$suffix.css", array( 'dashicons' ) );
1603        $styles->add( 'editor-buttons', "/wp-includes/css/editor$suffix.css", array( 'dashicons' ) );
1604        $styles->add( 'media-views', "/wp-includes/css/media-views$suffix.css", array( 'buttons', 'dashicons', 'wp-mediaelement' ) );
1605        $styles->add( 'wp-pointer', "/wp-includes/css/wp-pointer$suffix.css", array( 'dashicons' ) );
1606        $styles->add( 'customize-preview', "/wp-includes/css/customize-preview$suffix.css", array( 'dashicons' ) );
1607        $styles->add( 'wp-empty-template-alert', "/wp-includes/css/wp-empty-template-alert$suffix.css" );
1608        $skip_link_style_path = WPINC . "/css/wp-block-template-skip-link$suffix.css";
1609        $styles->add( 'wp-block-template-skip-link', "/$skip_link_style_path" );
1610        $styles->add_data( 'wp-block-template-skip-link', 'path', ABSPATH . $skip_link_style_path );
1611
1612        // External libraries and friends.
1613        $styles->add( 'imgareaselect', '/wp-includes/js/imgareaselect/imgareaselect.css', array(), '0.9.8' );
1614        $styles->add( 'wp-jquery-ui-dialog', "/wp-includes/css/jquery-ui-dialog$suffix.css", array( 'dashicons' ) );
1615        $styles->add( 'mediaelement', '/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css', array(), '4.2.17' );
1616        $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement$suffix.css", array( 'mediaelement' ) );
1617        $styles->add( 'thickbox', '/wp-includes/js/thickbox/thickbox.css', array( 'dashicons' ) );
1618        $styles->add( 'wp-codemirror', '/wp-includes/js/codemirror/codemirror.min.css', array(), '5.29.1-alpha-ee20357' );
1619
1620        // Deprecated CSS.
1621        $styles->add( 'deprecated-media', "/wp-admin/css/deprecated-media$suffix.css" );
1622        $styles->add( 'farbtastic', "/wp-admin/css/farbtastic$suffix.css", array(), '1.3u1' );
1623        $styles->add( 'jcrop', '/wp-includes/js/jcrop/jquery.Jcrop.min.css', array(), '0.9.15' );
1624        $styles->add( 'colors-fresh', false, array( 'wp-admin', 'buttons' ) ); // Old handle.
1625        $styles->add( 'open-sans', $open_sans_font_url ); // No longer used in core as of 4.6.
1626        $styles->add( 'wp-embed-template-ie', false );
1627        $styles->add_data( 'wp-embed-template-ie', 'conditional', '_required-conditional-dependency_' );
1628
1629        // Noto Serif is no longer used by core, but may be relied upon by themes and plugins.
1630        $fonts_url = '';
1631
1632        /*
1633         * translators: Use this to specify the proper Google Font name and variants
1634         * to load that is supported by your language. Do not translate.
1635         * Set to 'off' to disable loading.
1636         */
1637        $font_family = _x( 'Noto Serif:400,400i,700,700i', 'Google Font Name and Variants' );
1638        if ( 'off' !== $font_family ) {
1639                $fonts_url = 'https://fonts.googleapis.com/css?family=' . urlencode( $font_family );
1640        }
1641        $styles->add( 'wp-editor-font', $fonts_url ); // No longer used in core as of 5.7.
1642        $block_library_theme_path = WPINC . "/css/dist/block-library/theme$suffix.css";
1643        $styles->add( 'wp-block-library-theme', "/$block_library_theme_path" );
1644        $styles->add_data( 'wp-block-library-theme', 'path', ABSPATH . $block_library_theme_path );
1645
1646        $classic_theme_styles_path = WPINC . "/css/classic-themes$suffix.css";
1647        $styles->add( 'classic-theme-styles', "/$classic_theme_styles_path" );
1648        $styles->add_data( 'classic-theme-styles', 'path', ABSPATH . $classic_theme_styles_path );
1649
1650        $styles->add(
1651                'wp-reset-editor-styles',
1652                "/wp-includes/css/dist/block-library/reset$suffix.css",
1653                array( 'common', 'forms' ) // Make sure the reset is loaded after the default WP Admin styles.
1654        );
1655
1656        $styles->add(
1657                'wp-editor-classic-layout-styles',
1658                "/wp-includes/css/dist/edit-post/classic$suffix.css",
1659                array()
1660        );
1661
1662        $styles->add(
1663                'wp-block-editor-content',
1664                "/wp-includes/css/dist/block-editor/content$suffix.css",
1665                array( 'wp-components' )
1666        );
1667
1668        // Only add CONTENT styles here that should be enqueued in the iframe!
1669        $wp_edit_blocks_dependencies = array(
1670                'wp-components',
1671                /*
1672                 * This needs to be added before the block library styles,
1673                 * The block library styles override the "reset" styles.
1674                 */
1675                'wp-reset-editor-styles',
1676                'wp-block-library',
1677                'wp-block-editor-content',
1678        );
1679
1680        // Only load the default layout and margin styles for themes without theme.json file.
1681        if ( ! wp_theme_has_theme_json() ) {
1682                $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
1683        }
1684
1685        if (
1686                current_theme_supports( 'wp-block-styles' ) &&
1687                ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 )
1688        ) {
1689                /*
1690                 * Include opinionated block styles if the theme supports block styles and
1691                 * no $editor_styles are declared, so the editor never appears broken.
1692                 */
1693                $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
1694        }
1695
1696        $styles->add(
1697                'wp-edit-blocks',
1698                "/wp-includes/css/dist/block-library/editor$suffix.css",
1699                $wp_edit_blocks_dependencies
1700        );
1701
1702        $package_styles = array(
1703                'block-editor'         => array( 'wp-components', 'wp-preferences' ),
1704                'block-library'        => array(),
1705                'block-directory'      => array(),
1706                'components'           => array(),
1707                'commands'             => array( 'wp-components' ),
1708                'edit-post'            => array(
1709                        'wp-components',
1710                        'wp-block-editor',
1711                        'wp-editor',
1712                        'wp-edit-blocks',
1713                        'wp-block-library',
1714                        'wp-commands',
1715                        'wp-preferences',
1716                ),
1717                'editor'               => array(
1718                        'wp-components',
1719                        'wp-block-editor',
1720                        'wp-reusable-blocks',
1721                        'wp-patterns',
1722                        'wp-preferences',
1723                ),
1724                'format-library'       => array(),
1725                'list-reusable-blocks' => array( 'wp-components' ),
1726                'reusable-blocks'      => array( 'wp-components' ),
1727                'patterns'             => array( 'wp-components' ),
1728                'preferences'          => array( 'wp-components' ),
1729                'nux'                  => array( 'wp-components' ),
1730                'widgets'              => array(
1731                        'wp-components',
1732                ),
1733                'edit-widgets'         => array(
1734                        'wp-widgets',
1735                        'wp-block-editor',
1736                        'wp-editor',
1737                        'wp-edit-blocks',
1738                        'wp-block-library',
1739                        'wp-patterns',
1740                        'wp-preferences',
1741                ),
1742                'customize-widgets'    => array(
1743                        'wp-widgets',
1744                        'wp-block-editor',
1745                        'wp-editor',
1746                        'wp-edit-blocks',
1747                        'wp-block-library',
1748                        'wp-patterns',
1749                        'wp-preferences',
1750                ),
1751                'edit-site'            => array(
1752                        'wp-components',
1753                        'wp-block-editor',
1754                        'wp-editor',
1755                        'wp-edit-blocks',
1756                        'wp-commands',
1757                        'wp-preferences',
1758                ),
1759        );
1760
1761        foreach ( $package_styles as $package => $dependencies ) {
1762                $handle = 'wp-' . $package;
1763                $path   = "/wp-includes/css/dist/$package/style$suffix.css";
1764
1765                if ( 'block-library' === $package && wp_should_load_separate_core_block_assets() ) {
1766                        $path = "/wp-includes/css/dist/$package/common$suffix.css";
1767                }
1768                $styles->add( $handle, $path, $dependencies );
1769                $styles->add_data( $handle, 'path', ABSPATH . $path );
1770        }
1771
1772        // RTL CSS.
1773        $rtl_styles = array(
1774                // Admin CSS.
1775                'common',
1776                'forms',
1777                'admin-menu',
1778                'dashboard',
1779                'list-tables',
1780                'edit',
1781                'revisions',
1782                'media',
1783                'themes',
1784                'about',
1785                'nav-menus',
1786                'widgets',
1787                'site-icon',
1788                'l10n',
1789                'install',
1790                'wp-color-picker',
1791                'customize-controls',
1792                'customize-widgets',
1793                'customize-nav-menus',
1794                'customize-preview',
1795                'login',
1796                'site-health',
1797                'wp-empty-template-alert',
1798                // Includes CSS.
1799                'buttons',
1800                'admin-bar',
1801                'wp-auth-check',
1802                'editor-buttons',
1803                'media-views',
1804                'wp-pointer',
1805                'wp-jquery-ui-dialog',
1806                'wp-block-template-skip-link',
1807                // Package styles.
1808                'wp-reset-editor-styles',
1809                'wp-editor-classic-layout-styles',
1810                'wp-block-library-theme',
1811                'wp-edit-blocks',
1812                'wp-block-editor',
1813                'wp-block-library',
1814                'wp-block-directory',
1815                'wp-commands',
1816                'wp-components',
1817                'wp-customize-widgets',
1818                'wp-edit-post',
1819                'wp-edit-site',
1820                'wp-edit-widgets',
1821                'wp-editor',
1822                'wp-format-library',
1823                'wp-list-reusable-blocks',
1824                'wp-reusable-blocks',
1825                'wp-patterns',
1826                'wp-nux',
1827                'wp-widgets',
1828                // Deprecated CSS.
1829                'deprecated-media',
1830                'farbtastic',
1831        );
1832
1833        foreach ( $rtl_styles as $rtl_style ) {
1834                $styles->add_data( $rtl_style, 'rtl', 'replace' );
1835                if ( $suffix ) {
1836                        $styles->add_data( $rtl_style, 'suffix', $suffix );
1837                }
1838        }
1839}
1840
1841/**
1842 * Reorders JavaScript scripts array to place prototype before jQuery.
1843 *
1844 * @since 2.3.1
1845 *
1846 * @param string[] $js_array JavaScript scripts array
1847 * @return string[] Reordered array, if needed.
1848 */
1849function wp_prototype_before_jquery( $js_array ) {
1850        $prototype = array_search( 'prototype', $js_array, true );
1851
1852        if ( false === $prototype ) {
1853                return $js_array;
1854        }
1855
1856        $jquery = array_search( 'jquery', $js_array, true );
1857
1858        if ( false === $jquery ) {
1859                return $js_array;
1860        }
1861
1862        if ( $prototype < $jquery ) {
1863                return $js_array;
1864        }
1865
1866        unset( $js_array[ $prototype ] );
1867
1868        array_splice( $js_array, $jquery, 0, 'prototype' );
1869
1870        return $js_array;
1871}
1872
1873/**
1874 * Loads localized data on print rather than initialization.
1875 *
1876 * These localizations require information that may not be loaded even by init.
1877 *
1878 * @since 2.5.0
1879 *
1880 * @global array $shortcode_tags
1881 */
1882function wp_just_in_time_script_localization() {
1883
1884        wp_localize_script(
1885                'autosave',
1886                'autosaveL10n',
1887                array(
1888                        'autosaveInterval' => AUTOSAVE_INTERVAL,
1889                        'blog_id'          => get_current_blog_id(),
1890                )
1891        );
1892
1893        wp_localize_script(
1894                'mce-view',
1895                'mceViewL10n',
1896                array(
1897                        'shortcodes' => ! empty( $GLOBALS['shortcode_tags'] ) ? array_keys( $GLOBALS['shortcode_tags'] ) : array(),
1898                )
1899        );
1900
1901        wp_localize_script(
1902                'word-count',
1903                'wordCountL10n',
1904                array(
1905                        'type'       => wp_get_word_count_type(),
1906                        'shortcodes' => ! empty( $GLOBALS['shortcode_tags'] ) ? array_keys( $GLOBALS['shortcode_tags'] ) : array(),
1907                )
1908        );
1909}
1910
1911/**
1912 * Localizes the jQuery UI datepicker.
1913 *
1914 * @since 4.6.0
1915 *
1916 * @link https://api.jqueryui.com/datepicker/#options
1917 *
1918 * @global WP_Locale $wp_locale WordPress date and time locale object.
1919 */
1920function wp_localize_jquery_ui_datepicker() {
1921        global $wp_locale;
1922
1923        if ( ! wp_script_is( 'jquery-ui-datepicker', 'enqueued' ) ) {
1924                return;
1925        }
1926
1927        // Convert the PHP date format into jQuery UI's format.
1928        $datepicker_date_format = str_replace(
1929                array(
1930                        'd',
1931                        'j',
1932                        'l',
1933                        'z', // Day.
1934                        'F',
1935                        'M',
1936                        'n',
1937                        'm', // Month.
1938                        'Y',
1939                        'y', // Year.
1940                ),
1941                array(
1942                        'dd',
1943                        'd',
1944                        'DD',
1945                        'o',
1946                        'MM',
1947                        'M',
1948                        'm',
1949                        'mm',
1950                        'yy',
1951                        'y',
1952                ),
1953                get_option( 'date_format' )
1954        );
1955
1956        $datepicker_defaults = wp_json_encode(
1957                array(
1958                        'closeText'       => __( 'Close' ),
1959                        'currentText'     => __( 'Today' ),
1960                        'monthNames'      => array_values( $wp_locale->month ),
1961                        'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
1962                        'nextText'        => __( 'Next' ),
1963                        'prevText'        => __( 'Previous' ),
1964                        'dayNames'        => array_values( $wp_locale->weekday ),
1965                        'dayNamesShort'   => array_values( $wp_locale->weekday_abbrev ),
1966                        'dayNamesMin'     => array_values( $wp_locale->weekday_initial ),
1967                        'dateFormat'      => $datepicker_date_format,
1968                        'firstDay'        => absint( get_option( 'start_of_week' ) ),
1969                        'isRTL'           => $wp_locale->is_rtl(),
1970                ),
1971                JSON_HEX_TAG | JSON_UNESCAPED_SLASHES
1972        );
1973
1974        wp_add_inline_script( 'jquery-ui-datepicker', "jQuery(function(jQuery){jQuery.datepicker.setDefaults({$datepicker_defaults});});" );
1975}
1976
1977/**
1978 * Localizes community events data that needs to be passed to dashboard.js.
1979 *
1980 * @since 4.8.0
1981 */
1982function wp_localize_community_events() {
1983        if ( ! wp_script_is( 'dashboard' ) ) {
1984                return;
1985        }
1986
1987        require_once ABSPATH . 'wp-admin/includes/class-wp-community-events.php';
1988
1989        $user_id            = get_current_user_id();
1990        $saved_location     = get_user_option( 'community-events-location', $user_id );
1991        $saved_ip_address   = $saved_location['ip'] ?? false;
1992        $current_ip_address = WP_Community_Events::get_unsafe_client_ip();
1993
1994        /*
1995         * If the user's location is based on their IP address, then update their
1996         * location when their IP address changes. This allows them to see events
1997         * in their current city when travelling. Otherwise, they would always be
1998         * shown events in the city where they were when they first loaded the
1999         * Dashboard, which could have been months or years ago.
2000         */
2001        if ( $saved_ip_address && $current_ip_address && $current_ip_address !== $saved_ip_address ) {
2002                $saved_location['ip'] = $current_ip_address;
2003                update_user_meta( $user_id, 'community-events-location', $saved_location );
2004        }
2005
2006        $events_client = new WP_Community_Events( $user_id, $saved_location );
2007
2008        wp_localize_script(
2009                'dashboard',
2010                'communityEventsData',
2011                array(
2012                        'nonce'       => wp_create_nonce( 'community_events' ),
2013                        'cache'       => $events_client->get_cached_events(),
2014                        'time_format' => get_option( 'time_format' ),
2015                )
2016        );
2017}
2018
2019/**
2020 * Administration Screen CSS for changing the styles.
2021 *
2022 * If installing the 'wp-admin/' directory will be replaced with './'.
2023 *
2024 * The $_wp_admin_css_colors global manages the Administration Screens CSS
2025 * stylesheet that is loaded. The option that is set is 'admin_color' and is the
2026 * color and key for the array. The value for the color key is an object with
2027 * a 'url' parameter that has the URL path to the CSS file.
2028 *
2029 * The query from $src parameter will be appended to the URL that is given from
2030 * the $_wp_admin_css_colors array value URL.
2031 *
2032 * @since 2.6.0
2033 *
2034 * @global array $_wp_admin_css_colors
2035 *
2036 * @param string $src    Source URL.
2037 * @param string $handle Either 'colors' or 'colors-rtl'.
2038 * @return string|false URL path to CSS stylesheet for Administration Screens.
2039 */
2040function wp_style_loader_src( $src, $handle ) {
2041        global $_wp_admin_css_colors;
2042
2043        if ( wp_installing() ) {
2044                return preg_replace( '#^wp-admin/#', './', $src );
2045        }
2046
2047        if ( 'colors' === $handle ) {
2048                $color = get_user_option( 'admin_color' );
2049
2050                if ( empty( $color ) || ! isset( $_wp_admin_css_colors[ $color ] ) ) {
2051                        $color = 'fresh';
2052                }
2053
2054                $color = $_wp_admin_css_colors[ $color ] ?? null;
2055                $url   = $color->url ?? '';
2056
2057                if ( ! $url ) {
2058                        return false;
2059                }
2060
2061                $parsed = parse_url( $src );
2062                if ( isset( $parsed['query'] ) && $parsed['query'] ) {
2063                        wp_parse_str( $parsed['query'], $qv );
2064                        $url = add_query_arg( $qv, $url );
2065                }
2066
2067                return $url;
2068        }
2069
2070        return $src;
2071}
2072
2073/**
2074 * Prints the script queue in the HTML head on admin pages.
2075 *
2076 * Postpones the scripts that were queued for the footer.
2077 * print_footer_scripts() is called in the footer to print these scripts.
2078 *
2079 * @since 2.8.0
2080 *
2081 * @see wp_print_scripts()
2082 *
2083 * @global bool $concatenate_scripts
2084 *
2085 * @return string[] Handles of the scripts that were printed.
2086 */
2087function print_head_scripts() {
2088        global $concatenate_scripts;
2089
2090        if ( ! did_action( 'wp_print_scripts' ) ) {
2091                /** This action is documented in wp-includes/functions.wp-scripts.php */
2092                do_action( 'wp_print_scripts' );
2093        }
2094
2095        $wp_scripts = wp_scripts();
2096
2097        script_concat_settings();
2098        $wp_scripts->do_concat = $concatenate_scripts;
2099        $wp_scripts->do_head_items();
2100
2101        /**
2102         * Filters whether to print the head scripts.
2103         *
2104         * @since 2.8.0
2105         *
2106         * @param bool $print Whether to print the head scripts. Default true.
2107         */
2108        if ( apply_filters( 'print_head_scripts', true ) ) {
2109                _print_scripts();
2110        }
2111
2112        $wp_scripts->reset();
2113        return $wp_scripts->done;
2114}
2115
2116/**
2117 * Prints the scripts that were queued for the footer or too late for the HTML head.
2118 *
2119 * @since 2.8.0
2120 *
2121 * @global WP_Scripts $wp_scripts
2122 * @global bool       $concatenate_scripts
2123 *
2124 * @return string[] Handles of the scripts that were printed.
2125 */
2126function print_footer_scripts() {
2127        global $wp_scripts, $concatenate_scripts;
2128
2129        if ( ! ( $wp_scripts instanceof WP_Scripts ) ) {
2130                return array(); // No need to run if not instantiated.
2131        }
2132        script_concat_settings();
2133        $wp_scripts->do_concat = $concatenate_scripts;
2134        $wp_scripts->do_footer_items();
2135
2136        /**
2137         * Filters whether to print the footer scripts.
2138         *
2139         * @since 2.8.0
2140         *
2141         * @param bool $print Whether to print the footer scripts. Default true.
2142         */
2143        if ( apply_filters( 'print_footer_scripts', true ) ) {
2144                _print_scripts();
2145        }
2146
2147        $wp_scripts->reset();
2148        return $wp_scripts->done;
2149}
2150
2151/**
2152 * Prints scripts (internal use only)
2153 *
2154 * @since 2.8.0
2155 *
2156 * @ignore
2157 *
2158 * @global WP_Scripts $wp_scripts
2159 * @global bool       $compress_scripts
2160 */
2161function _print_scripts() {
2162        global $wp_scripts, $compress_scripts;
2163
2164        $zip = $compress_scripts ? 1 : 0;
2165        if ( $zip && defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP ) {
2166                $zip = 'gzip';
2167        }
2168
2169        $concat = trim( $wp_scripts->concat, ', ' );
2170
2171        if ( $concat ) {
2172                if ( ! empty( $wp_scripts->print_code ) ) {
2173                        echo "\n<script>\n";
2174                        echo $wp_scripts->print_code;
2175                        echo sprintf( "\n//# sourceURL=%s\n", rawurlencode( 'js-inline-concat-' . $concat ) );
2176                        echo "</script>\n";
2177                }
2178
2179                $concat       = str_split( $concat, 128 );
2180                $concatenated = '';
2181
2182                foreach ( $concat as $key => $chunk ) {
2183                        $concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}";
2184                }
2185
2186                $src = $wp_scripts->base_url . "/wp-admin/load-scripts.php?c={$zip}" . $concatenated . '&ver=' . $wp_scripts->default_version;
2187                echo "<script src='" . esc_attr( $src ) . "'></script>\n";
2188        }
2189
2190        if ( ! empty( $wp_scripts->print_html ) ) {
2191                echo $wp_scripts->print_html;
2192        }
2193}
2194
2195/**
2196 * Prints the script queue in the HTML head on the front end.
2197 *
2198 * Postpones the scripts that were queued for the footer.
2199 * wp_print_footer_scripts() is called in the footer to print these scripts.
2200 *
2201 * @since 2.8.0
2202 *
2203 * @global WP_Scripts $wp_scripts
2204 *
2205 * @return string[] Handles of the scripts that were printed.
2206 */
2207function wp_print_head_scripts() {
2208        global $wp_scripts;
2209
2210        if ( ! did_action( 'wp_print_scripts' ) ) {
2211                /** This action is documented in wp-includes/functions.wp-scripts.php */
2212                do_action( 'wp_print_scripts' );
2213        }
2214
2215        if ( ! ( $wp_scripts instanceof WP_Scripts ) ) {
2216                return array(); // No need to run if nothing is queued.
2217        }
2218
2219        return print_head_scripts();
2220}
2221
2222/**
2223 * Private, for use in *_footer_scripts hooks
2224 *
2225 * In classic themes, when block styles are loaded on demand via wp_load_classic_theme_block_styles_on_demand(),
2226 * this function is replaced by a closure in wp_hoist_late_printed_styles() which will capture the printing of
2227 * two sets of "late" styles to be hoisted to the HEAD by means of the template enhancement output buffer:
2228 *
2229 * 1. Styles related to blocks are inserted right after the wp-block-library stylesheet.
2230 * 2. All other styles are appended to the end of the HEAD.
2231 *
2232 * The closure calls print_footer_scripts() to print scripts in the footer as usual.
2233 *
2234 * @since 3.3.0
2235 */
2236function _wp_footer_scripts() {
2237        print_late_styles();
2238        print_footer_scripts();
2239}
2240
2241/**
2242 * Hooks to print the scripts and styles in the footer.
2243 *
2244 * @since 2.8.0
2245 */
2246function wp_print_footer_scripts() {
2247        /**
2248         * Fires when footer scripts are printed.
2249         *
2250         * @since 2.8.0
2251         */
2252        do_action( 'wp_print_footer_scripts' );
2253}
2254
2255/**
2256 * Wrapper for do_action( 'wp_enqueue_scripts' ).
2257 *
2258 * Allows plugins to queue scripts for the front end using wp_enqueue_script().
2259 * Runs first in wp_head() where all is_home(), is_page(), etc. functions are available.
2260 *
2261 * @since 2.8.0
2262 */
2263function wp_enqueue_scripts() {
2264        /**
2265         * Fires when scripts and styles are enqueued.
2266         *
2267         * @since 2.8.0
2268         */
2269        do_action( 'wp_enqueue_scripts' );
2270}
2271
2272/**
2273 * Prints the styles queue in the HTML head on admin pages.
2274 *
2275 * @since 2.8.0
2276 *
2277 * @global bool $concatenate_scripts
2278 *
2279 * @return string[] Handles of the styles that were printed.
2280 */
2281function print_admin_styles() {
2282        global $concatenate_scripts;
2283
2284        $wp_styles = wp_styles();
2285
2286        script_concat_settings();
2287        $wp_styles->do_concat = $concatenate_scripts;
2288        $wp_styles->do_items( false );
2289
2290        /**
2291         * Filters whether to print the admin styles.
2292         *
2293         * @since 2.8.0
2294         *
2295         * @param bool $print Whether to print the admin styles. Default true.
2296         */
2297        if ( apply_filters( 'print_admin_styles', true ) ) {
2298                _print_styles();
2299        }
2300
2301        $wp_styles->reset();
2302        return $wp_styles->done;
2303}
2304
2305/**
2306 * Prints the styles that were queued too late for the HTML head.
2307 *
2308 * @since 3.3.0
2309 *
2310 * @global WP_Styles $wp_styles
2311 * @global bool      $concatenate_scripts
2312 *
2313 * @return string[]|void
2314 */
2315function print_late_styles() {
2316        global $wp_styles, $concatenate_scripts;
2317
2318        if ( ! ( $wp_styles instanceof WP_Styles ) ) {
2319                return;
2320        }
2321
2322        script_concat_settings();
2323        $wp_styles->do_concat = $concatenate_scripts;
2324        $wp_styles->do_footer_items();
2325
2326        /**
2327         * Filters whether to print the styles queued too late for the HTML head.
2328         *
2329         * @since 3.3.0
2330         *
2331         * @param bool $print Whether to print the 'late' styles. Default true.
2332         */
2333        if ( apply_filters( 'print_late_styles', true ) ) {
2334                _print_styles();
2335        }
2336
2337        $wp_styles->reset();
2338        return $wp_styles->done;
2339}
2340
2341/**
2342 * Prints styles (internal use only).
2343 *
2344 * @ignore
2345 * @since 3.3.0
2346 *
2347 * @global bool $compress_css
2348 */
2349function _print_styles() {
2350        global $compress_css;
2351
2352        $wp_styles = wp_styles();
2353
2354        $zip = $compress_css ? 1 : 0;
2355        if ( $zip && defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP ) {
2356                $zip = 'gzip';
2357        }
2358
2359        $concat = trim( $wp_styles->concat, ', ' );
2360
2361        if ( $concat ) {
2362                $dir = $wp_styles->text_direction;
2363                $ver = $wp_styles->default_version;
2364
2365                $concat_source_url = 'css-inline-concat-' . $concat;
2366                $concat            = str_split( $concat, 128 );
2367                $concatenated      = '';
2368
2369                foreach ( $concat as $key => $chunk ) {
2370                        $concatenated .= "&load%5Bchunk_{$key}%5D={$chunk}";
2371                }
2372
2373                $href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}" . $concatenated . '&ver=' . $ver;
2374                echo "<link rel='stylesheet' href='" . esc_attr( $href ) . "' media='all' />\n";
2375
2376                if ( ! empty( $wp_styles->print_code ) ) {
2377                        $processor = new WP_HTML_Tag_Processor( '<style></style>' );
2378                        $processor->next_tag();
2379                        $style_tag_contents = "\n{$wp_styles->print_code}\n"
2380                                . sprintf( "/*# sourceURL=%s */\n", rawurlencode( $concat_source_url ) );
2381                        $processor->set_modifiable_text( $style_tag_contents );
2382                        echo "{$processor->get_updated_html()}\n";
2383                }
2384        }
2385
2386        if ( ! empty( $wp_styles->print_html ) ) {
2387                echo $wp_styles->print_html;
2388        }
2389}
2390
2391/**
2392 * Determines the concatenation and compression settings for scripts and styles.
2393 *
2394 * @since 2.8.0
2395 *
2396 * @global bool $concatenate_scripts
2397 * @global bool $compress_scripts
2398 * @global bool $compress_css
2399 */
2400function script_concat_settings() {
2401        global $concatenate_scripts, $compress_scripts, $compress_css;
2402
2403        $compressed_output = ( ini_get( 'zlib.output_compression' ) || 'ob_gzhandler' === ini_get( 'output_handler' ) );
2404
2405        $can_compress_scripts = ! wp_installing() && get_site_option( 'can_compress_scripts' );
2406
2407        if ( ! isset( $concatenate_scripts ) ) {
2408                $concatenate_scripts = defined( 'CONCATENATE_SCRIPTS' ) ? CONCATENATE_SCRIPTS : true;
2409                if ( ( ! is_admin() && ! did_action( 'login_init' ) ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) {
2410                        $concatenate_scripts = false;
2411                }
2412        }
2413
2414        if ( ! isset( $compress_scripts ) ) {
2415                $compress_scripts = defined( 'COMPRESS_SCRIPTS' ) ? COMPRESS_SCRIPTS : true;
2416                if ( $compress_scripts && ( ! $can_compress_scripts || $compressed_output ) ) {
2417                        $compress_scripts = false;
2418                }
2419        }
2420
2421        if ( ! isset( $compress_css ) ) {
2422                $compress_css = defined( 'COMPRESS_CSS' ) ? COMPRESS_CSS : true;
2423                if ( $compress_css && ( ! $can_compress_scripts || $compressed_output ) ) {
2424                        $compress_css = false;
2425                }
2426        }
2427}
2428
2429/**
2430 * Handles the enqueueing of block scripts and styles that are common to both
2431 * the editor and the front-end.
2432 *
2433 * @since 5.0.0
2434 */
2435function wp_common_block_scripts_and_styles() {
2436        if ( is_admin() && ! wp_should_load_block_editor_scripts_and_styles() ) {
2437                return;
2438        }
2439
2440        wp_enqueue_style( 'wp-block-library' );
2441
2442        if ( current_theme_supports( 'wp-block-styles' ) && ! wp_should_load_separate_core_block_assets() ) {
2443                wp_enqueue_style( 'wp-block-library-theme' );
2444        }
2445
2446        /**
2447         * Fires after enqueuing block assets for both editor and front-end.
2448         *
2449         * Call `add_action` on any hook before 'wp_enqueue_scripts'.
2450         *
2451         * In the function call you supply, simply use `wp_enqueue_script` and
2452         * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
2453         *
2454         * @since 5.0.0
2455         */
2456        do_action( 'enqueue_block_assets' );
2457}
2458
2459/**
2460 * Applies a filter to the list of style nodes that comes from WP_Theme_JSON::get_style_nodes().
2461 *
2462 * This particular filter removes all of the blocks from the array.
2463 *
2464 * We want WP_Theme_JSON to be ignorant of the implementation details of how the CSS is being used.
2465 * This filter allows us to modify the output of WP_Theme_JSON depending on whether or not we are
2466 * loading separate assets, without making the class aware of that detail.
2467 *
2468 * @since 6.1.0
2469 *
2470 * @param array<array<string, mixed>> $nodes The nodes to filter.
2471 * @return array<array<string, mixed>> A filtered array of style nodes.
2472 */
2473function wp_filter_out_block_nodes( $nodes ) {
2474        return array_filter(
2475                $nodes,
2476                static function ( $node ) {
2477                        return ! in_array( 'blocks', $node['path'], true );
2478                },
2479                ARRAY_FILTER_USE_BOTH
2480        );
2481}
2482
2483/**
2484 * Enqueues the global styles defined via theme.json.
2485 *
2486 * @since 5.8.0
2487 */
2488function wp_enqueue_global_styles() {
2489        $assets_on_demand = wp_should_load_block_assets_on_demand();
2490        $is_block_theme   = wp_is_block_theme();
2491        $is_classic_theme = ! $is_block_theme;
2492
2493        /*
2494         * Global styles should be printed in the head for block themes, or for classic themes when loading assets on
2495         * demand is disabled, which is the default.
2496         * The footer should only be used for classic themes when loading assets on demand is enabled.
2497         *
2498         * See https://core.trac.wordpress.org/ticket/53494 and https://core.trac.wordpress.org/ticket/61965.
2499         */
2500        if (
2501                ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
2502                ( $is_classic_theme && doing_action( 'wp_footer' ) && ! $assets_on_demand ) ||
2503                ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) && $assets_on_demand )
2504        ) {
2505                return;
2506        }
2507
2508        /*
2509         * If loading the CSS for each block separately, then load the theme.json CSS conditionally.
2510         * This removes the CSS from the global-styles stylesheet and adds it to the inline CSS for each block.
2511         * This filter must be registered before calling wp_get_global_stylesheet();
2512         */
2513        add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
2514
2515        $stylesheet = wp_get_global_stylesheet();
2516
2517        if ( $is_block_theme ) {
2518                /*
2519                 * Dequeue the Customizer's custom CSS
2520                 * and add it before the global styles custom CSS.
2521                 */
2522                remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
2523
2524                /*
2525                 * Get the custom CSS from the Customizer and add it to the global stylesheet.
2526                 * Always do this in Customizer preview for the sake of live preview since it be empty.
2527                 */
2528                $custom_css = trim( wp_get_custom_css() );
2529                if ( $custom_css || is_customize_preview() ) {
2530                        if ( is_customize_preview() ) {
2531                                /*
2532                                 * When in the Customizer preview, wrap the Custom CSS in milestone comments to allow customize-preview.js
2533                                 * to locate the CSS to replace for live previewing. Make sure that the milestone comments are omitted from
2534                                 * the stored Custom CSS if by chance someone tried to add them, which would be highly unlikely, but it
2535                                 * would break live previewing.
2536                                 */
2537                                $before_milestone = '/*BEGIN_CUSTOMIZER_CUSTOM_CSS*/';
2538                                $after_milestone  = '/*END_CUSTOMIZER_CUSTOM_CSS*/';
2539                                $custom_css       = str_replace( array( $before_milestone, $after_milestone ), '', $custom_css );
2540                                $custom_css       = $before_milestone . "\n" . $custom_css . "\n" . $after_milestone;
2541                        }
2542                        $custom_css = "\n" . $custom_css;
2543                }
2544                $stylesheet .= $custom_css;
2545
2546                // Add the global styles custom CSS at the end.
2547                $stylesheet .= wp_get_global_stylesheet( array( 'custom-css' ) );
2548        }
2549
2550        if ( empty( $stylesheet ) ) {
2551                return;
2552        }
2553
2554        wp_register_style( 'global-styles', false );
2555        wp_add_inline_style( 'global-styles', $stylesheet );
2556        wp_enqueue_style( 'global-styles' );
2557
2558        // Add each block as an inline css.
2559        wp_add_global_styles_for_blocks();
2560}
2561
2562/**
2563 * Checks if the editor scripts and styles for all registered block types
2564 * should be enqueued on the current screen.
2565 *
2566 * @since 5.6.0
2567 *
2568 * @global WP_Screen $current_screen WordPress current screen object.
2569 *
2570 * @return bool Whether scripts and styles should be enqueued.
2571 */
2572function wp_should_load_block_editor_scripts_and_styles() {
2573        global $current_screen;
2574
2575        $is_block_editor_screen = ( $current_screen instanceof WP_Screen ) && $current_screen->is_block_editor();
2576
2577        /**
2578         * Filters the flag that decides whether or not block editor scripts and styles
2579         * are going to be enqueued on the current screen.
2580         *
2581         * @since 5.6.0
2582         *
2583         * @param bool $is_block_editor_screen Current value of the flag.
2584         */
2585        return apply_filters( 'should_load_block_editor_scripts_and_styles', $is_block_editor_screen );
2586}
2587
2588/**
2589 * Checks whether separate styles should be loaded for core blocks.
2590 *
2591 * When this function returns true, other functions ensure that core blocks use their own separate stylesheets.
2592 * When this function returns false, all core blocks will use the single combined 'wp-block-library' stylesheet.
2593 *
2594 * As a side effect, the return value will by default result in block assets to be loaded on demand, via the
2595 * {@see wp_should_load_block_assets_on_demand()} function. This behavior can be separately altered via that function.
2596 *
2597 * This only affects front end and not the block editor screens.
2598 *
2599 * @since 5.8.0
2600 * @see @see wp_should_load_block_assets_on_demand()
2601 * @see wp_enqueue_registered_block_scripts_and_styles()
2602 * @see register_block_style_handle()
2603 *
2604 * @return bool Whether separate core block assets will be loaded.
2605 */
2606function wp_should_load_separate_core_block_assets() {
2607        if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
2608                return false;
2609        }
2610
2611        /**
2612         * Filters whether block styles should be loaded separately.
2613         *
2614         * Returning false loads all core block assets, regardless of whether they are rendered
2615         * in a page or not. Returning true loads core block assets only when they are rendered.
2616         *
2617         * @since 5.8.0
2618         *
2619         * @param bool $load_separate_assets Whether separate assets will be loaded.
2620         *                                   Default false (all block assets are loaded, even when not used).
2621         */
2622        return apply_filters( 'should_load_separate_core_block_assets', false );
2623}
2624
2625/**
2626 * Checks whether block styles should be loaded only on-render.
2627 *
2628 * When this function returns true, other functions ensure that blocks only load their assets on-render.
2629 * When this function returns false, all block assets are loaded regardless of whether they are rendered in a page.
2630 *
2631 * The default return value depends on the result of {@see wp_should_load_separate_core_block_assets()}, which controls
2632 * whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library' stylesheet.
2633 *
2634 * This only affects front end and not the block editor screens.
2635 *
2636 * @since 6.8.0
2637 * @see wp_should_load_separate_core_block_assets()
2638 *
2639 * @return bool Whether to load block assets only when they are rendered.
2640 */
2641function wp_should_load_block_assets_on_demand() {
2642        if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
2643                return false;
2644        }
2645
2646        /*
2647         * For backward compatibility, the default return value for this function is based on the return value of
2648         * `wp_should_load_separate_core_block_assets()`. Initially, this function used to control both of these concerns.
2649         */
2650        $load_assets_on_demand = wp_should_load_separate_core_block_assets();
2651
2652        /**
2653         * Filters whether block styles should be loaded on demand.
2654         *
2655         * Returning false loads all block assets, regardless of whether they are rendered in a page or not.
2656         * Returning true loads block assets only when they are rendered.
2657         *
2658         * The default value of the filter depends on the result of {@see wp_should_load_separate_core_block_assets()},
2659         * which controls whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library'
2660         * stylesheet.
2661         *
2662         * @since 6.8.0
2663         *
2664         * @param bool $load_assets_on_demand Whether to load block assets only when they are rendered.
2665         */
2666        return apply_filters( 'should_load_block_assets_on_demand', $load_assets_on_demand );
2667}
2668
2669/**
2670 * Enqueues registered block scripts and styles, depending on current rendered
2671 * context (only enqueuing editor scripts while in context of the editor).
2672 *
2673 * @since 5.0.0
2674 */
2675function wp_enqueue_registered_block_scripts_and_styles() {
2676        if ( wp_should_load_block_assets_on_demand() ) {
2677                return;
2678        }
2679
2680        $load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles();
2681
2682        $block_registry = WP_Block_Type_Registry::get_instance();
2683
2684        /*
2685         * Block styles are only enqueued if they're registered. For core blocks, this is only the case if
2686         * `wp_should_load_separate_core_block_assets()` returns true. Otherwise they use the single combined
2687         * 'wp-block-library` stylesheet. See also `register_core_block_style_handles()`.
2688         * Since `wp_enqueue_style()` does not trigger warnings if the style is not registered, it is okay to not cater for
2689         * this behavior here and simply call `wp_enqueue_style()` unconditionally.
2690         */
2691        foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
2692                // Front-end and editor styles.
2693                foreach ( $block_type->style_handles as $style_handle ) {
2694                        wp_enqueue_style( $style_handle );
2695                }
2696
2697                // Front-end and editor scripts.
2698                foreach ( $block_type->script_handles as $script_handle ) {
2699                        wp_enqueue_script( $script_handle );
2700                }
2701
2702                if ( $load_editor_scripts_and_styles ) {
2703                        // Editor styles.
2704                        foreach ( $block_type->editor_style_handles as $editor_style_handle ) {
2705                                wp_enqueue_style( $editor_style_handle );
2706                        }
2707
2708                        // Editor scripts.
2709                        foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
2710                                wp_enqueue_script( $editor_script_handle );
2711                        }
2712                }
2713        }
2714}
2715
2716/**
2717 * Function responsible for enqueuing the styles required for block styles functionality on the editor and on the frontend.
2718 *
2719 * @since 5.3.0
2720 *
2721 * @global WP_Styles $wp_styles
2722 */
2723function enqueue_block_styles_assets() {
2724        global $wp_styles;
2725
2726        $block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
2727
2728        foreach ( $block_styles as $block_name => $styles ) {
2729                foreach ( $styles as $style_properties ) {
2730                        if ( isset( $style_properties['style_handle'] ) ) {
2731
2732                                // If the site loads block styles on demand, enqueue the stylesheet on render.
2733                                if ( wp_should_load_block_assets_on_demand() ) {
2734                                        add_filter(
2735                                                'render_block',
2736                                                static function ( $html, $block ) use ( $block_name, $style_properties ) {
2737                                                        if ( $block['blockName'] === $block_name ) {
2738                                                                wp_enqueue_style( $style_properties['style_handle'] );
2739                                                        }
2740                                                        return $html;
2741                                                },
2742                                                10,
2743                                                2
2744                                        );
2745                                } else {
2746                                        wp_enqueue_style( $style_properties['style_handle'] );
2747                                }
2748                        }
2749                        if ( isset( $style_properties['inline_style'] ) ) {
2750
2751                                // Default to "wp-block-library".
2752                                $handle = 'wp-block-library';
2753
2754                                // If the site loads block styles on demand, check if the block has a stylesheet registered.
2755                                if ( wp_should_load_block_assets_on_demand() ) {
2756                                        $block_stylesheet_handle = generate_block_asset_handle( $block_name, 'style' );
2757
2758                                        if ( isset( $wp_styles->registered[ $block_stylesheet_handle ] ) ) {
2759                                                $handle = $block_stylesheet_handle;
2760                                        }
2761                                }
2762
2763                                // Add inline styles to the calculated handle.
2764                                wp_add_inline_style( $handle, $style_properties['inline_style'] );
2765                        }
2766                }
2767        }
2768}
2769
2770/**
2771 * Function responsible for enqueuing the assets required for block styles functionality on the editor.
2772 *
2773 * @since 5.3.0
2774 */
2775function enqueue_editor_block_styles_assets() {
2776        $block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
2777
2778        $register_script_lines = array( '( function() {' );
2779        foreach ( $block_styles as $block_name => $styles ) {
2780                foreach ( $styles as $style_properties ) {
2781                        $block_style = array(
2782                                'name'  => $style_properties['name'],
2783                                'label' => $style_properties['label'],
2784                        );
2785                        if ( isset( $style_properties['is_default'] ) ) {
2786                                $block_style['isDefault'] = $style_properties['is_default'];
2787                        }
2788                        $register_script_lines[] = sprintf(
2789                                '       wp.blocks.registerBlockStyle( \'%s\', %s );',
2790                                $block_name,
2791                                wp_json_encode( $block_style, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
2792                        );
2793                }
2794        }
2795        $register_script_lines[] = '} )();';
2796        $inline_script           = implode( "\n", $register_script_lines );
2797
2798        wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, array( 'in_footer' => true ) );
2799        wp_add_inline_script( 'wp-block-styles', $inline_script );
2800        wp_enqueue_script( 'wp-block-styles' );
2801}
2802
2803/**
2804 * Enqueues the assets required for the block directory within the block editor.
2805 *
2806 * @since 5.5.0
2807 */
2808function wp_enqueue_editor_block_directory_assets() {
2809        wp_enqueue_script( 'wp-block-directory' );
2810        wp_enqueue_style( 'wp-block-directory' );
2811}
2812
2813/**
2814 * Enqueues the assets required for the format library within the block editor.
2815 *
2816 * @since 5.8.0
2817 */
2818function wp_enqueue_editor_format_library_assets() {
2819        wp_enqueue_script( 'wp-format-library' );
2820        wp_enqueue_style( 'wp-format-library' );
2821}
2822
2823/**
2824 * Sanitizes an attributes array into an attributes string to be placed inside a `<script>` tag.
2825 *
2826 * Automatically injects type attribute if needed.
2827 * Used by {@see wp_get_script_tag()} and {@see wp_get_inline_script_tag()}.
2828 *
2829 * @since 5.7.0
2830 *
2831 * @param array<string, string|bool> $attributes Key-value pairs representing `<script>` tag attributes.
2832 * @return string String made of sanitized `<script>` tag attributes.
2833 */
2834function wp_sanitize_script_attributes( $attributes ) {
2835        $attributes_string = '';
2836
2837        /*
2838         * If HTML5 script tag is supported, only the attribute name is added
2839         * to $attributes_string for entries with a boolean value, and that are true.
2840         */
2841        foreach ( $attributes as $attribute_name => $attribute_value ) {
2842                if ( is_bool( $attribute_value ) ) {
2843                        if ( $attribute_value ) {
2844                                $attributes_string .= ' ' . esc_attr( $attribute_name );
2845                        }
2846                } else {
2847                        $attributes_string .= sprintf( ' %1$s="%2$s"', esc_attr( $attribute_name ), esc_attr( $attribute_value ) );
2848                }
2849        }
2850
2851        return $attributes_string;
2852}
2853
2854/**
2855 * Formats `<script>` loader tags.
2856 *
2857 * It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
2858 * Automatically injects type attribute if needed.
2859 *
2860 * @since 5.7.0
2861 *
2862 * @param array<string, string|bool> $attributes Key-value pairs representing `<script>` tag attributes.
2863 * @return string String containing `<script>` opening and closing tags.
2864 */
2865function wp_get_script_tag( $attributes ) {
2866        /**
2867         * Filters attributes to be added to a script tag.
2868         *
2869         * @since 5.7.0
2870         *
2871         * @param array $attributes Key-value pairs representing `<script>` tag attributes.
2872         *                          Only the attribute name is added to the `<script>` tag for
2873         *                          entries with a boolean value, and that are true.
2874         */
2875        $attributes = apply_filters( 'wp_script_attributes', $attributes );
2876
2877        return sprintf( "<script%s></script>\n", wp_sanitize_script_attributes( $attributes ) );
2878}
2879
2880/**
2881 * Prints formatted `<script>` loader tag.
2882 *
2883 * It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
2884 * Automatically injects type attribute if needed.
2885 *
2886 * @since 5.7.0
2887 *
2888 * @param array<string, string|bool> $attributes Key-value pairs representing `<script>` tag attributes.
2889 */
2890function wp_print_script_tag( $attributes ) {
2891        echo wp_get_script_tag( $attributes );
2892}
2893
2894/**
2895 * Constructs an inline script tag.
2896 *
2897 * It is possible to inject attributes in the `<script>` tag via the {@see 'wp_inline_script_attributes'} filter.
2898 * Automatically injects type attribute if needed.
2899 *
2900 * @since 5.7.0
2901 *
2902 * @param string                     $data       Data for script tag: JavaScript, importmap, speculationrules, etc.
2903 * @param array<string, string|bool> $attributes Optional. Key-value pairs representing `<script>` tag attributes.
2904 * @return string String containing inline JavaScript code wrapped around `<script>` tag.
2905 */
2906function wp_get_inline_script_tag( $data, $attributes = array() ) {
2907        $data = "\n" . trim( $data, "\n\r " ) . "\n";
2908
2909        /**
2910         * Filters attributes to be added to a script tag.
2911         *
2912         * @since 5.7.0
2913         *
2914         * @param array<string, string|bool> $attributes Key-value pairs representing `<script>` tag attributes.
2915         *                                               Only the attribute name is added to the `<script>` tag for
2916         *                                               entries with a boolean value, and that are true.
2917         * @param string                     $data       Inline data.
2918         */
2919        $attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $data );
2920
2921        return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $data );
2922}
2923
2924/**
2925 * Prints an inline script tag.
2926 *
2927 * It is possible to inject attributes in the `<script>` tag via the {@see 'wp_inline_script_attributes'} filter.
2928 * Automatically injects type attribute if needed.
2929 *
2930 * @since 5.7.0
2931 *
2932 * @param string                     $data       Data for script tag: JavaScript, importmap, speculationrules, etc.
2933 * @param array<string, string|bool> $attributes Optional. Key-value pairs representing `<script>` tag attributes.
2934 */
2935function wp_print_inline_script_tag( $data, $attributes = array() ) {
2936        echo wp_get_inline_script_tag( $data, $attributes );
2937}
2938
2939/**
2940 * Allows small styles to be inlined.
2941 *
2942 * This improves performance and sustainability, and is opt-in. Stylesheets can opt in
2943 * by adding `path` data using `wp_style_add_data`, and defining the file's absolute path:
2944 *
2945 *     wp_style_add_data( $style_handle, 'path', $file_path );
2946 *
2947 * @since 5.8.0
2948 *
2949 * @global WP_Styles $wp_styles
2950 */
2951function wp_maybe_inline_styles() {
2952        global $wp_styles;
2953
2954        $total_inline_limit = 40000;
2955        /**
2956         * The maximum size of inlined styles in bytes.
2957         *
2958         * @since 5.8.0
2959         * @since 6.9.0 The default limit increased from 20K to 40K.
2960         *
2961         * @param int $total_inline_limit The file-size threshold, in bytes. Default 40000.
2962         */
2963        $total_inline_limit = apply_filters( 'styles_inline_size_limit', $total_inline_limit );
2964
2965        $styles = array();
2966
2967        // Build an array of styles that have a path defined.
2968        foreach ( $wp_styles->queue as $handle ) {
2969                if ( ! isset( $wp_styles->registered[ $handle ] ) ) {
2970                        continue;
2971                }
2972                $src  = $wp_styles->registered[ $handle ]->src;
2973                $path = $wp_styles->get_data( $handle, 'path' );
2974                if ( $path && $src ) {
2975                        $size = wp_filesize( $path );
2976                        if ( 0 === $size && ! file_exists( $path ) ) {
2977                                _doing_it_wrong(
2978                                        __FUNCTION__,
2979                                        sprintf(
2980                                                /* translators: 1: 'path', 2: filesystem path, 3: style handle */
2981                                                __( 'Unable to read the "%1$s" key with value "%2$s" for stylesheet "%3$s".' ),
2982                                                'path',
2983                                                esc_html( $path ),
2984                                                esc_html( $handle )
2985                                        ),
2986                                        '7.0.0'
2987                                );
2988                                continue;
2989                        }
2990                        $styles[] = array(
2991                                'handle' => $handle,
2992                                'src'    => $src,
2993                                'path'   => $path,
2994                                'size'   => $size,
2995                        );
2996                }
2997        }
2998
2999        if ( ! empty( $styles ) ) {
3000                // Reorder styles array based on size.
3001                usort(
3002                        $styles,
3003                        static function ( $a, $b ) {
3004                                return ( $a['size'] <= $b['size'] ) ? -1 : 1;
3005                        }
3006                );
3007
3008                /*
3009                 * The total inlined size.
3010                 *
3011                 * On each iteration of the loop, if a style gets added inline the value of this var increases
3012                 * to reflect the total size of inlined styles.
3013                 */
3014                $total_inline_size = 0;
3015
3016                // Loop styles.
3017                foreach ( $styles as $style ) {
3018
3019                        // Size check. Since styles are ordered by size, we can break the loop.
3020                        if ( $total_inline_size + $style['size'] > $total_inline_limit ) {
3021                                break;
3022                        }
3023
3024                        // Get the styles if we don't already have them.
3025                        if ( ! is_readable( $style['path'] ) ) {
3026                                _doing_it_wrong(
3027                                        __FUNCTION__,
3028                                        sprintf(
3029                                                /* translators: 1: 'path', 2: filesystem path, 3: style handle */
3030                                                __( 'Unable to read the "%1$s" key with value "%2$s" for stylesheet "%3$s".' ),
3031                                                'path',
3032                                                esc_html( $style['path'] ),
3033                                                esc_html( $style['handle'] )
3034                                        ),
3035                                        '7.0.0'
3036                                );
3037                                continue;
3038                        }
3039                        $style['css'] = file_get_contents( $style['path'] );
3040
3041                        /*
3042                         * Check if the style contains relative URLs that need to be modified.
3043                         * URLs relative to the stylesheet's path should be converted to relative to the site's root.
3044                         */
3045                        $style['css'] = _wp_normalize_relative_css_links( $style['css'], $style['src'] );
3046
3047                        // Keep track of the original `src` for the style that was inlined so that the `sourceURL` comment can be added.
3048                        $wp_styles->add_data( $style['handle'], 'inlined_src', $style['src'] );
3049
3050                        // Set `src` to `false` and add styles inline.
3051                        $wp_styles->registered[ $style['handle'] ]->src = false;
3052                        if ( empty( $wp_styles->registered[ $style['handle'] ]->extra['after'] ) ) {
3053                                $wp_styles->registered[ $style['handle'] ]->extra['after'] = array();
3054                        }
3055                        array_unshift( $wp_styles->registered[ $style['handle'] ]->extra['after'], $style['css'] );
3056
3057                        // Add the styles size to the $total_inline_size var.
3058                        $total_inline_size += (int) $style['size'];
3059                }
3060        }
3061}
3062
3063/**
3064 * Makes URLs relative to the WordPress installation.
3065 *
3066 * @since 5.9.0
3067 * @access private
3068 *
3069 * @param string $css            The CSS to make URLs relative to the WordPress installation.
3070 * @param string $stylesheet_url The URL to the stylesheet.
3071 *
3072 * @return string The CSS with URLs made relative to the WordPress installation.
3073 */
3074function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
3075        return preg_replace_callback(
3076                '#(url\s*\(\s*[\'"]?\s*)([^\'"\)]+)#',
3077                static function ( $matches ) use ( $stylesheet_url ) {
3078                        list( , $prefix, $url ) = $matches;
3079
3080                        // Short-circuit if the URL does not require normalization.
3081                        if (
3082                                str_starts_with( $url, 'http:' ) ||
3083                                str_starts_with( $url, 'https:' ) ||
3084                                str_starts_with( $url, '/' ) ||
3085                                str_starts_with( $url, '#' ) ||
3086                                str_starts_with( $url, 'data:' )
3087                        ) {
3088                                return $matches[0];
3089                        }
3090
3091                        // Build the absolute URL.
3092                        $absolute_url = dirname( $stylesheet_url ) . '/' . $url;
3093                        $absolute_url = str_replace( '/./', '/', $absolute_url );
3094
3095                        // Convert to URL related to the site root.
3096                        $url = wp_make_link_relative( $absolute_url );
3097
3098                        return $prefix . $url;
3099                },
3100                $css
3101        );
3102}
3103
3104/**
3105 * Function that enqueues the CSS Custom Properties coming from theme.json.
3106 *
3107 * @since 5.9.0
3108 */
3109function wp_enqueue_global_styles_css_custom_properties() {
3110        wp_register_style( 'global-styles-css-custom-properties', false );
3111        wp_add_inline_style( 'global-styles-css-custom-properties', wp_get_global_stylesheet( array( 'variables' ) ) );
3112        wp_enqueue_style( 'global-styles-css-custom-properties' );
3113}
3114
3115/**
3116 * Hooks inline styles in the proper place, depending on the active theme.
3117 *
3118 * @since 5.9.1
3119 * @since 6.1.0 Added the `$priority` parameter.
3120 *
3121 * For block themes, styles are loaded in the head.
3122 * For classic ones, styles are loaded in the body because the wp_head action happens before render_block.
3123 *
3124 * @link https://core.trac.wordpress.org/ticket/53494.
3125 *
3126 * @param string $style    String containing the CSS styles to be added.
3127 * @param int    $priority To set the priority for the add_action.
3128 */
3129function wp_enqueue_block_support_styles( $style, $priority = 10 ) {
3130        $action_hook_name = 'wp_footer';
3131        if ( wp_is_block_theme() ) {
3132                $action_hook_name = 'wp_head';
3133        }
3134        add_action(
3135                $action_hook_name,
3136                static function () use ( $style ) {
3137                        $processor = new WP_HTML_Tag_Processor( '<style></style>' );
3138                        $processor->next_tag();
3139                        $processor->set_modifiable_text( $style );
3140                        echo "{$processor->get_updated_html()}\n";
3141                },
3142                $priority
3143        );
3144}
3145
3146/**
3147 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
3148 * Styles are stored via the style engine API.
3149 *
3150 * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
3151 *
3152 * @since 6.1.0
3153 *
3154 * @param array<string, bool> $options {
3155 *     Optional. An array of options to pass to wp_style_engine_get_stylesheet_from_context().
3156 *     Default empty array.
3157 *
3158 *     @type bool $optimize Whether to optimize the CSS output, e.g., combine rules.
3159 *                          Default false.
3160 *     @type bool $prettify Whether to add new lines and indents to output.
3161 *                          Default to whether the `SCRIPT_DEBUG` constant is defined.
3162 * }
3163 */
3164function wp_enqueue_stored_styles( $options = array() ) {
3165        // Note: Styles printed at wp_footer for classic themes may still end up in the head due to wp_load_classic_theme_block_styles_on_demand().
3166        $is_block_theme   = wp_is_block_theme();
3167        $is_classic_theme = ! $is_block_theme;
3168
3169        /*
3170         * For block themes, this function prints stored styles in the header.
3171         * For classic themes, in the footer.
3172         */
3173        if (
3174                ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
3175                ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
3176        ) {
3177                return;
3178        }
3179
3180        $core_styles_keys         = array( 'block-supports' );
3181        $compiled_core_stylesheet = '';
3182        $style_tag_id             = 'core';
3183        // Adds comment if code is prettified to identify core styles sections in debugging.
3184        $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
3185        foreach ( $core_styles_keys as $style_key ) {
3186                if ( $should_prettify ) {
3187                        $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
3188                }
3189                // Chains core store ids to signify what the styles contain.
3190                $style_tag_id             .= '-' . $style_key;
3191                $compiled_core_stylesheet .= wp_style_engine_get_stylesheet_from_context( $style_key, $options );
3192        }
3193
3194        // Combines Core styles.
3195        if ( ! empty( $compiled_core_stylesheet ) ) {
3196                wp_register_style( $style_tag_id, false );
3197                wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
3198                wp_enqueue_style( $style_tag_id );
3199        }
3200
3201        // Prints out any other stores registered by themes or otherwise.
3202        $additional_stores = WP_Style_Engine_CSS_Rules_Store::get_stores();
3203        foreach ( array_keys( $additional_stores ) as $store_name ) {
3204                if ( in_array( $store_name, $core_styles_keys, true ) ) {
3205                        continue;
3206                }
3207                $styles = wp_style_engine_get_stylesheet_from_context( $store_name, $options );
3208                if ( ! empty( $styles ) ) {
3209                        $key = "wp-style-engine-$store_name";
3210                        wp_register_style( $key, false );
3211                        wp_add_inline_style( $key, $styles );
3212                        wp_enqueue_style( $key );
3213                }
3214        }
3215}
3216
3217/**
3218 * Enqueues a stylesheet for a specific block.
3219 *
3220 * If the theme has opted-in to load block styles on demand,
3221 * then the stylesheet will be enqueued on-render,
3222 * otherwise when the block inits.
3223 *
3224 * @since 5.9.0
3225 *
3226 * @param string                                   $block_name The block-name, including namespace.
3227 * @param array<string, string|string[]|bool|null> $args       {
3228 *     An array of arguments. See wp_register_style() for full information about each argument.
3229 *
3230 *     @type string           $handle The handle for the stylesheet.
3231 *     @type string|false     $src    The source URL of the stylesheet.
3232 *     @type string[]         $deps   Array of registered stylesheet handles this stylesheet depends on.
3233 *     @type string|bool|null $ver    Stylesheet version number.
3234 *     @type string           $media  The media for which this stylesheet has been defined.
3235 *     @type string|null      $path   Absolute path to the stylesheet, so that it can potentially be inlined.
3236 * }
3237 */
3238function wp_enqueue_block_style( $block_name, $args ) {
3239        $args = wp_parse_args(
3240                $args,
3241                array(
3242                        'handle' => '',
3243                        'src'    => '',
3244                        'deps'   => array(),
3245                        'ver'    => false,
3246                        'media'  => 'all',
3247                )
3248        );
3249
3250        /**
3251         * Callback function to register and enqueue styles.
3252         *
3253         * @param string $content When the callback is used for the render_block filter,
3254         *                        the content needs to be returned so the function parameter
3255         *                        is to ensure the content exists.
3256         * @return string Block content.
3257         */
3258        $callback = static function ( $content ) use ( $args ) {
3259                // Register the stylesheet.
3260                if ( ! empty( $args['src'] ) ) {
3261                        wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
3262                }
3263
3264                // Add `path` data if provided.
3265                if ( isset( $args['path'] ) ) {
3266                        wp_style_add_data( $args['handle'], 'path', $args['path'] );
3267
3268                        // Get the RTL file path.
3269                        $rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
3270
3271                        // Add RTL stylesheet.
3272                        if ( file_exists( $rtl_file_path ) ) {
3273                                wp_style_add_data( $args['handle'], 'rtl', 'replace' );
3274
3275                                if ( is_rtl() ) {
3276                                        wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
3277                                }
3278                        }
3279                }
3280
3281                // Enqueue the stylesheet.
3282                wp_enqueue_style( $args['handle'] );
3283
3284                return $content;
3285        };
3286
3287        $hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
3288        if ( wp_should_load_block_assets_on_demand() ) {
3289                /**
3290                 * Callback function to register and enqueue styles.
3291                 *
3292                 * @param string $content The block content.
3293                 * @param array  $block   The full block, including name and attributes.
3294                 * @return string Block content.
3295                 */
3296                $callback_separate = static function ( $content, $block ) use ( $block_name, $callback ) {
3297                        if ( ! empty( $block['blockName'] ) && $block_name === $block['blockName'] ) {
3298                                return $callback( $content );
3299                        }
3300                        return $content;
3301                };
3302
3303                /*
3304                 * The filter's callback here is an anonymous function because
3305                 * using a named function in this case is not possible.
3306                 *
3307                 * The function cannot be unhooked, however, users are still able
3308                 * to dequeue the stylesheets registered/enqueued by the callback
3309                 * which is why in this case, using an anonymous function
3310                 * was deemed acceptable.
3311                 */
3312                add_filter( 'render_block', $callback_separate, 10, 2 );
3313                return;
3314        }
3315
3316        /*
3317         * The filter's callback here is an anonymous function because
3318         * using a named function in this case is not possible.
3319         *
3320         * The function cannot be unhooked, however, users are still able
3321         * to dequeue the stylesheets registered/enqueued by the callback
3322         * which is why in this case, using an anonymous function
3323         * was deemed acceptable.
3324         */
3325        add_filter( $hook, $callback );
3326
3327        // Enqueue assets in the editor.
3328        add_action( 'enqueue_block_assets', $callback );
3329}
3330
3331/**
3332 * Loads classic theme styles when the current theme lacks a theme.json file.
3333 *
3334 * This is used for backwards compatibility for Button and File blocks specifically.
3335 *
3336 * @since 6.1.0
3337 * @since 6.2.0 Added File block styles.
3338 * @since 6.8.0 Moved stylesheet registration outside of this function.
3339 */
3340function wp_enqueue_classic_theme_styles() {
3341        if ( ! wp_theme_has_theme_json() ) {
3342                wp_enqueue_style( 'classic-theme-styles' );
3343        }
3344}
3345
3346/**
3347 * Enqueues the assets required for the Command Palette.
3348 *
3349 * @since 6.9.0
3350 *
3351 * @global array  $menu
3352 * @global array  $submenu
3353 */
3354function wp_enqueue_command_palette_assets() {
3355        global $menu, $submenu;
3356
3357        $command_palette_settings = array(
3358                'is_network_admin' => is_network_admin(),
3359        );
3360
3361        /**
3362         * Extracts root-level text nodes from HTML string.
3363         *
3364         * @ignore
3365         * @param string $label HTML string to extract text from.
3366         * @return string Extracted text content, trimmed.
3367         */
3368        $extract_root_text = static function ( string $label ): string {
3369                if ( '' === $label ) {
3370                        return '';
3371                }
3372
3373                $processor  = new WP_HTML_Tag_Processor( $label );
3374                $text_parts = array();
3375                $depth      = 0;
3376
3377                while ( $processor->next_token() ) {
3378                        $token_type = $processor->get_token_type();
3379
3380                        if ( '#text' === $token_type ) {
3381                                if ( 0 === $depth ) {
3382                                        $text_parts[] = $processor->get_modifiable_text();
3383                                }
3384                                continue;
3385                        }
3386
3387                        if ( '#tag' !== $token_type ) {
3388                                continue;
3389                        }
3390
3391                        if ( $processor->is_tag_closer() ) {
3392                                if ( $depth > 0 ) {
3393                                        --$depth;
3394                                }
3395                                continue;
3396                        }
3397
3398                        $token_name = $processor->get_tag();
3399                        if ( $token_name && ! WP_HTML_Processor::is_void( $token_name ) ) {
3400                                ++$depth;
3401                        }
3402                }
3403
3404                return trim( implode( '', $text_parts ) );
3405        };
3406
3407        if ( $menu ) {
3408                $menu_commands = array();
3409                foreach ( $menu as $menu_item ) {
3410                        if ( empty( $menu_item[0] ) || ! is_string( $menu_item[0] ) || ! empty( $menu_item[1] ) && ! current_user_can( $menu_item[1] ) ) {
3411                                continue;
3412                        }
3413
3414                        $menu_label = $extract_root_text( $menu_item[0] );
3415                        $menu_url   = '';
3416                        $menu_slug  = $menu_item[2];
3417
3418                        if ( preg_match( '/\.php($|\?)/', $menu_slug ) || wp_http_validate_url( $menu_slug ) ) {
3419                                $menu_url = $menu_slug;
3420                        } elseif ( ! empty( menu_page_url( $menu_slug, false ) ) ) {
3421                                $menu_url = WP_HTML_Decoder::decode_attribute( menu_page_url( $menu_slug, false ) );
3422                        }
3423
3424                        if ( $menu_url ) {
3425                                $menu_commands[] = array(
3426                                        'label' => $menu_label,
3427                                        'url'   => $menu_url,
3428                                        'name'  => $menu_slug,
3429                                );
3430                        }
3431
3432                        if ( array_key_exists( $menu_slug, $submenu ) ) {
3433                                foreach ( $submenu[ $menu_slug ] as $submenu_item ) {
3434                                        if ( empty( $submenu_item[0] ) || ! empty( $submenu_item[1] ) && ! current_user_can( $submenu_item[1] ) ) {
3435                                                continue;
3436                                        }
3437
3438                                        $submenu_label = $extract_root_text( $submenu_item[0] );
3439                                        $submenu_url   = '';
3440                                        $submenu_slug  = $submenu_item[2];
3441
3442                                        if ( preg_match( '/\.php($|\?)/', $submenu_slug ) || wp_http_validate_url( $submenu_slug ) ) {
3443                                                $submenu_url = $submenu_slug;
3444                                        } elseif ( ! empty( menu_page_url( $submenu_slug, false ) ) ) {
3445                                                $submenu_url = WP_HTML_Decoder::decode_attribute( menu_page_url( $submenu_slug, false ) );
3446                                        }
3447                                        if ( $submenu_url ) {
3448                                                $menu_commands[] = array(
3449                                                        'label' => sprintf(
3450                                                                /* translators: 1: Menu label, 2: Submenu label. */
3451                                                                __( '%1$s > %2$s' ),
3452                                                                $menu_label,
3453                                                                $submenu_label
3454                                                        ),
3455                                                        'url'   => $submenu_url,
3456                                                        'name'  => $menu_slug . '-' . $submenu_item[2],
3457                                                );
3458                                        }
3459                                }
3460                        }
3461                }
3462                $command_palette_settings['menu_commands'] = $menu_commands;
3463        }
3464
3465        wp_enqueue_script( 'wp-commands' );
3466        wp_enqueue_style( 'wp-commands' );
3467        wp_enqueue_script( 'wp-core-commands' );
3468
3469        wp_add_inline_script(
3470                'wp-core-commands',
3471                sprintf(
3472                        'wp.coreCommands.initializeCommandPalette( %s );',
3473                        wp_json_encode( $command_palette_settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
3474                )
3475        );
3476}
3477
3478/**
3479 * Removes leading and trailing _empty_ script tags.
3480 *
3481 * This is a helper meant to be used for literal script tag construction
3482 * within `wp_get_inline_script_tag()` or `wp_print_inline_script_tag()`.
3483 * It removes the literal values of "<script>" and "</script>" from
3484 * around an inline script after trimming whitespace. Typically this
3485 * is used in conjunction with output buffering, where `ob_get_clean()`
3486 * is passed as the `$contents` argument.
3487 *
3488 * Example:
3489 *
3490 *     // Strips exact literal empty SCRIPT tags.
3491 *     $js = '<script>sayHello();</script>;
3492 *     'sayHello();' === wp_remove_surrounding_empty_script_tags( $js );
3493 *
3494 *     // Otherwise if anything is different it warns in the JS console.
3495 *     $js = '<script type="module">console.log( "hi" );</script>';
3496 *     'console.error( ... )' === wp_remove_surrounding_empty_script_tags( $js );
3497 *
3498 * @since 6.4.0
3499 * @access private
3500 *
3501 * @see wp_print_inline_script_tag()
3502 * @see wp_get_inline_script_tag()
3503 *
3504 * @param string $contents Script body with manually created SCRIPT tag literals.
3505 * @return string Script body without surrounding script tag literals, or
3506 *                original contents if both exact literals aren't present.
3507 */
3508function wp_remove_surrounding_empty_script_tags( $contents ) {
3509        $contents = trim( $contents );
3510        $opener   = '<SCRIPT>';
3511        $closer   = '</SCRIPT>';
3512
3513        if (
3514                strlen( $contents ) > strlen( $opener ) + strlen( $closer ) &&
3515                strtoupper( substr( $contents, 0, strlen( $opener ) ) ) === $opener &&
3516                strtoupper( substr( $contents, -strlen( $closer ) ) ) === $closer
3517        ) {
3518                return substr( $contents, strlen( $opener ), -strlen( $closer ) );
3519        } else {
3520                $error_message = __( 'Expected string to start with script tag (without attributes) and end with script tag, with optional whitespace.' );
3521                _doing_it_wrong( __FUNCTION__, $error_message, '6.4' );
3522                return sprintf(
3523                        'console.error(%s)',
3524                        wp_json_encode(
3525                                sprintf(
3526                                        /* translators: %s: wp_remove_surrounding_empty_script_tags() */
3527                                        __( 'Function %s used incorrectly in PHP.' ),
3528                                        'wp_remove_surrounding_empty_script_tags()'
3529                                ) . ' ' . $error_message
3530                        )
3531                );
3532        }
3533}
3534
3535/**
3536 * Adds hooks to load block styles on demand in classic themes.
3537 *
3538 * @since 6.9.0
3539 *
3540 * @see _add_default_theme_supports()
3541 */
3542function wp_load_classic_theme_block_styles_on_demand() {
3543        // This is not relevant to block themes, as they are opted in to loading separate styles on demand via _add_default_theme_supports().
3544        if ( wp_is_block_theme() ) {
3545                return;
3546        }
3547
3548        /*
3549         * Make sure that wp_should_output_buffer_template_for_enhancement() returns true even if there aren't any
3550         * `wp_template_enhancement_output_buffer` filters added, but do so at priority zero so that applications which
3551         * wish to stream responses can more easily turn this off.
3552         */
3553        add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
3554
3555        // If a site has opted out of the template enhancement output buffer, then bail.
3556        if ( ! wp_should_output_buffer_template_for_enhancement() ) {
3557                return;
3558        }
3559
3560        // The following two filters are added by default for block themes in _add_default_theme_supports().
3561
3562        /*
3563         * Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally, and so
3564         * that block-specific styles will only be enqueued when they are used on the page. A priority of zero allows for
3565         * this to be easily overridden by themes which wish to opt out. If a site has explicitly opted out of loading
3566         * separate block styles, then abort.
3567         */
3568        add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
3569        if ( ! wp_should_load_separate_core_block_assets() ) {
3570                return;
3571        }
3572
3573        /*
3574         * Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
3575         * As above, a priority of zero allows for this to be easily overridden by themes which wish to opt out. If a site
3576         * has explicitly opted out of loading block styles on demand, then abort.
3577         */
3578        add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
3579        if ( ! wp_should_load_block_assets_on_demand() ) {
3580                return;
3581        }
3582
3583        // Add hooks which require the presence of the output buffer. Ideally the above two filters could be added here, but they run too early.
3584        add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
3585}
3586
3587/**
3588 * Adds the hooks needed for CSS output to be delayed until after the content of the page has been established.
3589 *
3590 * @since 6.9.0
3591 *
3592 * @see wp_load_classic_theme_block_styles_on_demand()
3593 * @see _wp_footer_scripts()
3594 */
3595function wp_hoist_late_printed_styles() {
3596        // Skip the embed template on-demand styles aren't relevant, and there is no wp_head action.
3597        if ( is_embed() ) {
3598                return;
3599        }
3600
3601        /*
3602         * Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles
3603         * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
3604         * output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
3605         * `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present.
3606         */
3607        $placeholder = sprintf( '/*%s*/', uniqid( 'wp_block_styles_on_demand_placeholder:' ) );
3608        add_action(
3609                'wp_print_styles',
3610                static function () use ( $placeholder ) {
3611                        wp_add_inline_style( 'wp-block-library', $placeholder );
3612                }
3613        );
3614
3615        /*
3616         * Create a substitute for `print_late_styles()` which is aware of block styles. This substitute does not print
3617         * the styles, but it captures what would be printed for block styles and non-block styles so that they can be
3618         * later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts`
3619         * before `print_footer_scripts()` is called.
3620         */
3621        $printed_block_styles = '';
3622        $printed_late_styles  = '';
3623        $capture_late_styles  = static function () use ( &$printed_block_styles, &$printed_late_styles ) {
3624                // Gather the styles related to on-demand block enqueues.
3625                $all_block_style_handles = array();
3626                foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) {
3627                        foreach ( $block_type->style_handles as $style_handle ) {
3628                                $all_block_style_handles[] = $style_handle;
3629                        }
3630                }
3631                $all_block_style_handles = array_merge(
3632                        $all_block_style_handles,
3633                        array(
3634                                'global-styles',
3635                                'block-style-variation-styles',
3636                                'core-block-supports',
3637                                'core-block-supports-duotone',
3638                        )
3639                );
3640
3641                /*
3642                 * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
3643                 * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
3644                 */
3645                $enqueued_block_styles = array_values( array_intersect( $all_block_style_handles, wp_styles()->queue ) );
3646                if ( count( $enqueued_block_styles ) > 0 ) {
3647                        ob_start();
3648                        wp_styles()->do_items( $enqueued_block_styles );
3649                        $printed_block_styles = ob_get_clean();
3650                }
3651
3652                /*
3653                 * Print all remaining styles not related to blocks. This contains a subset of the logic from
3654                 * `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether
3655                 * late styles are printed (since they are being hoisted anyway).
3656                 */
3657                ob_start();
3658                wp_styles()->do_footer_items();
3659                $printed_late_styles = ob_get_clean();
3660        };
3661
3662        /*
3663         * If `_wp_footer_scripts()` was unhooked from the `wp_print_footer_scripts` action, or if `wp_print_footer_scripts()`
3664         * was unhooked from running at the `wp_footer` action, then only add a callback to `wp_footer` which will capture the
3665         * late-printed styles.
3666         *
3667         * Otherwise, in the normal case where `_wp_footer_scripts()` will run at the `wp_print_footer_scripts` action, then
3668         * swap out `_wp_footer_scripts()` with an alternative which captures the printed styles (for hoisting to HEAD) before
3669         * proceeding with printing the footer scripts.
3670         */
3671        $wp_print_footer_scripts_priority = has_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
3672        if ( false === $wp_print_footer_scripts_priority || false === has_action( 'wp_footer', 'wp_print_footer_scripts' ) ) {
3673                // The normal priority for wp_print_footer_scripts() is to run at 20.
3674                add_action( 'wp_footer', $capture_late_styles, 20 );
3675        } else {
3676                remove_action( 'wp_print_footer_scripts', '_wp_footer_scripts', $wp_print_footer_scripts_priority );
3677                add_action(
3678                        'wp_print_footer_scripts',
3679                        static function () use ( $capture_late_styles ) {
3680                                $capture_late_styles();
3681                                print_footer_scripts();
3682                        },
3683                        $wp_print_footer_scripts_priority
3684                );
3685        }
3686
3687        // Replace placeholder with the captured late styles.
3688        add_filter(
3689                'wp_template_enhancement_output_buffer',
3690                static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_late_styles ) {
3691
3692                        // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
3693                        $processor = new class( $buffer ) extends WP_HTML_Tag_Processor {
3694                                /**
3695                                 * Gets the span for the current token.
3696                                 *
3697                                 * @return WP_HTML_Span Current token span.
3698                                 */
3699                                private function get_span(): WP_HTML_Span {
3700                                        // Note: This call will never fail according to the usage of this class, given it is always called after ::next_tag() is true.
3701                                        $this->set_bookmark( 'here' );
3702                                        return $this->bookmarks['here'];
3703                                }
3704
3705                                /**
3706                                 * Inserts text before the current token.
3707                                 *
3708                                 * @param string $text Text to insert.
3709                                 */
3710                                public function insert_before( string $text ) {
3711                                        $this->lexical_updates[] = new WP_HTML_Text_Replacement( $this->get_span()->start, 0, $text );
3712                                }
3713
3714                                /**
3715                                 * Inserts text after the current token.
3716                                 *
3717                                 * @param string $text Text to insert.
3718                                 */
3719                                public function insert_after( string $text ) {
3720                                        $span = $this->get_span();
3721
3722                                        $this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start + $span->length, 0, $text );
3723                                }
3724
3725                                /**
3726                                 * Removes the current token.
3727                                 */
3728                                public function remove() {
3729                                        $span = $this->get_span();
3730
3731                                        $this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start, $span->length, '' );
3732                                }
3733                        };
3734
3735                        /*
3736                         * Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles
3737                         * at </head> (or else print everything there). The placeholder CSS comment will always be added to the
3738                         * wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered.
3739                         * This means that there may not actually be any block styles to hoist from the footer to insert after this
3740                         * inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but
3741                         * if the resulting inline style is empty after the placeholder is removed, then the inline style is
3742                         * removed.
3743                         */
3744                        while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
3745                                if (
3746                                        'STYLE' === $processor->get_tag() &&
3747                                        'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
3748                                ) {
3749                                        $css_text = $processor->get_modifiable_text();
3750
3751                                        /*
3752                                         * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
3753                                         * be printed. Now that we've located the inline style, the placeholder comment can be removed. If
3754                                         * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
3755                                         * comment, then remove the STYLE entirely.)
3756                                         */
3757                                        $css_text = str_replace( $placeholder, '', $css_text );
3758                                        if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
3759                                                $processor->remove();
3760                                        } else {
3761                                                $processor->set_modifiable_text( $css_text );
3762                                        }
3763
3764                                        // Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
3765                                        if ( '' !== $printed_block_styles ) {
3766                                                $processor->insert_after( $printed_block_styles );
3767
3768                                                // Prevent printing them again at </head>.
3769                                                $printed_block_styles = '';
3770                                        }
3771
3772                                        // If there aren't any late styles, there's no need to continue to finding </head>.
3773                                        if ( '' === $printed_late_styles ) {
3774                                                break;
3775                                        }
3776                                } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
3777                                        $processor->insert_before( $printed_block_styles . $printed_late_styles );
3778                                        break;
3779                                }
3780                        }
3781
3782                        return $processor->get_updated_html();
3783                }
3784        );
3785}
3786
3787/**
3788 * Return the corresponding JavaScript `dataset` name for an attribute
3789 * if it represents a custom data attribute, or `null` if not.
3790 *
3791 * Custom data attributes appear in an element's `dataset` property in a
3792 * browser, but there's a specific way the names are translated from HTML
3793 * into JavaScript. This function indicates how the name would appear in
3794 * JavaScript if a browser would recognize it as a custom data attribute.
3795 *
3796 * Example:
3797 *
3798 *     // Dash-letter pairs turn into capital letters.
3799 *     'postId'       === wp_js_dataset_name( 'data-post-id' );
3800 *     'Before'       === wp_js_dataset_name( 'data--before' );
3801 *     '-One--Two---' === wp_js_dataset_name( 'data---one---two---' );
3802 *
3803 *     // Not every attribute name will be interpreted as a custom data attribute.
3804 *     null === wp_js_dataset_name( 'post-id' );
3805 *     null === wp_js_dataset_name( 'data' );
3806 *
3807 *     // Some very surprising names will; for example, a property whose name is the empty string.
3808 *     '' === wp_js_dataset_name( 'data-' );
3809 *     0  === strlen( wp_js_dataset_name( 'data-' ) );
3810 *
3811 * @since 6.9.0
3812 *
3813 * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3814 * @see \wp_html_custom_data_attribute_name()
3815 *
3816 * @param string $html_attribute_name Raw attribute name as found in the source HTML.
3817 * @return string|null Transformed `dataset` name, if interpretable as a custom data attribute, else `null`.
3818 */
3819function wp_js_dataset_name( string $html_attribute_name ): ?string {
3820        if ( 0 !== substr_compare( $html_attribute_name, 'data-', 0, 5, true ) ) {
3821                return null;
3822        }
3823
3824        $end = strlen( $html_attribute_name );
3825
3826        /*
3827         * If it contains characters which would end the attribute name parsing then
3828         * something else is wrong and this contains more than just an attribute name.
3829         */
3830        if ( ( $end - 5 ) !== strcspn( $html_attribute_name, "=/> \t\f\r\n", 5 ) ) {
3831                return null;
3832        }
3833
3834        /**
3835         * > For each name in list, for each U+002D HYPHEN-MINUS character (-)
3836         * > in the name that is followed by an ASCII lower alpha, remove the
3837         * > U+002D HYPHEN-MINUS character (-) and replace the character that
3838         * > followed it by the same character converted to ASCII uppercase.
3839         *
3840         * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3841         */
3842        $custom_name = '';
3843        $at          = 5;
3844        $was_at      = $at;
3845
3846        while ( $at < $end ) {
3847                $next_dash_at = strpos( $html_attribute_name, '-', $at );
3848                if ( false === $next_dash_at || $next_dash_at === $end - 1 ) {
3849                        break;
3850                }
3851
3852                // Transform `-a` to `A`, for example.
3853                $c = $html_attribute_name[ $next_dash_at + 1 ];
3854                if ( ( $c >= 'A' && $c <= 'Z' ) || ( $c >= 'a' && $c <= 'z' ) ) {
3855                        $prefix       = substr( $html_attribute_name, $was_at, $next_dash_at - $was_at );
3856                        $custom_name .= strtolower( $prefix );
3857                        $custom_name .= strtoupper( $c );
3858                        $at           = $next_dash_at + 2;
3859                        $was_at       = $at;
3860                        continue;
3861                }
3862
3863                $at = $next_dash_at + 1;
3864        }
3865
3866        // If nothing has been added it means there are no dash-letter pairs; return the name as-is.
3867        return '' === $custom_name
3868                ? strtolower( substr( $html_attribute_name, 5 ) )
3869                : ( $custom_name . strtolower( substr( $html_attribute_name, $was_at ) ) );
3870}
3871
3872/**
3873 * Returns a corresponding HTML attribute name for the given name,
3874 * if that name were found in a JS element’s `dataset` property.
3875 *
3876 * Example:
3877 *
3878 *     'data-post-id'        === wp_html_custom_data_attribute_name( 'postId' );
3879 *     'data--before'        === wp_html_custom_data_attribute_name( 'Before' );
3880 *     'data---one---two---' === wp_html_custom_data_attribute_name( '-One--Two---' );
3881 *
3882 *     // Not every attribute name will be interpreted as a custom data attribute.
3883 *     null === wp_html_custom_data_attribute_name( '/not-an-attribute/' );
3884 *     null === wp_html_custom_data_attribute_name( 'no spaces' );
3885 *
3886 *     // Some very surprising names will; for example, a property whose name is the empty string.
3887 *     'data-' === wp_html_custom_data_attribute_name( '' );
3888 *
3889 * @since 6.9.0
3890 *
3891 * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3892 * @see \wp_js_dataset_name()
3893 *
3894 * @param string $js_dataset_name Name of JS `dataset` property to transform.
3895 * @return string|null Corresponding name of an HTML custom data attribute for the given dataset name,
3896 *                     if possible to represent in HTML, otherwise `null`.
3897 */
3898function wp_html_custom_data_attribute_name( string $js_dataset_name ): ?string {
3899        $end = strlen( $js_dataset_name );
3900        if ( 0 === $end ) {
3901                return 'data-';
3902        }
3903
3904        /*
3905         * If it contains characters which would end the attribute name parsing then
3906         * something it’s not possible to represent this in HTML.
3907         */
3908        if ( strcspn( $js_dataset_name, "=/> \t\f\r\n" ) !== $end ) {
3909                return null;
3910        }
3911
3912        $html_name = 'data-';
3913        $at        = 0;
3914        $was_at    = $at;
3915
3916        while ( $at < $end ) {
3917                $next_upper_after = strcspn( $js_dataset_name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', $at );
3918                $next_upper_at    = $at + $next_upper_after;
3919                if ( $next_upper_at >= $end ) {
3920                        break;
3921                }
3922
3923                $prefix     = substr( $js_dataset_name, $was_at, $next_upper_at - $was_at );
3924                $html_name .= strtolower( $prefix );
3925                $html_name .= '-' . strtolower( $js_dataset_name[ $next_upper_at ] );
3926                $at         = $next_upper_at + 1;
3927                $was_at     = $at;
3928        }
3929
3930        if ( $was_at < $end ) {
3931                $html_name .= strtolower( substr( $js_dataset_name, $was_at ) );
3932        }
3933
3934        return $html_name;
3935}
Note: See TracBrowser for help on using the repository browser.