Make WordPress Core

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

Last change on this file was 61322, checked in by westonruter, 2 weeks ago

Docs: Improve accuracy of wp_enqueue_classic_theme_styles() description.

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

Follow-up to [59980], [54687], [54358].

Props manhphucofficial, wildworks, sabernhardt, iflairwebtechnologies, westonruter.
See #61892, #64224.
Fixes #64317.

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