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 >' ),
990 'prev' => __( '< 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…' ),
1028 'deleted' => __( 'moved to the Trash.' ),
1029 /* translators: %s: File name. */
1030 'error_uploading' => __( '“%s” 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 & Publish' ),
1299 'save' => __( 'Save & 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’s gone wrong. Wait a couple seconds, and then try again.' ),
1325 'themeDownloading' => __( 'Downloading your new theme…' ),
1326 'themePreviewWait' => __( 'Setting up your live preview. This may take a bit.' ),
1327 'revertingChanges' => __( 'Reverting unpublished changes…' ),
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('…');</script>
2977 *
2978 * In an HTML document this would print "…" 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 on classic themes in the frontend.
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 if ( $menu ) {
3442 $menu_commands = array();
3443 foreach ( $menu as $menu_item ) {
3444 if ( empty( $menu_item[0] ) || ! empty( $menu_item[1] ) && ! current_user_can( $menu_item[1] ) ) {
3445 continue;
3446 }
3447
3448 // Remove all HTML tags and their contents.
3449 $menu_label = $menu_item[0];
3450 while ( preg_match( '/<[^>]*>/', $menu_label ) ) {
3451 $menu_label = preg_replace( '/<[^>]*>.*?<\/[^>]*>|<[^>]*\/>|<[^>]*>/s', '', $menu_label );
3452 }
3453 $menu_label = trim( $menu_label );
3454 $menu_url = '';
3455 $menu_slug = $menu_item[2];
3456
3457 if ( preg_match( '/\.php($|\?)/', $menu_slug ) || wp_http_validate_url( $menu_slug ) ) {
3458 $menu_url = $menu_slug;
3459 } elseif ( ! empty( menu_page_url( $menu_slug, false ) ) ) {
3460 $menu_url = html_entity_decode( menu_page_url( $menu_slug, false ), ENT_QUOTES, get_bloginfo( 'charset' ) );
3461 }
3462
3463 if ( $menu_url ) {
3464 $menu_commands[] = array(
3465 'label' => $menu_label,
3466 'url' => $menu_url,
3467 'name' => $menu_slug,
3468 );
3469 }
3470
3471 if ( array_key_exists( $menu_slug, $submenu ) ) {
3472 foreach ( $submenu[ $menu_slug ] as $submenu_item ) {
3473 if ( empty( $submenu_item[0] ) || ! empty( $submenu_item[1] ) && ! current_user_can( $submenu_item[1] ) ) {
3474 continue;
3475 }
3476
3477 // Remove all HTML tags and their contents.
3478 $submenu_label = $submenu_item[0];
3479 while ( preg_match( '/<[^>]*>/', $submenu_label ) ) {
3480 $submenu_label = preg_replace( '/<[^>]*>.*?<\/[^>]*>|<[^>]*\/>|<[^>]*>/s', '', $submenu_label );
3481 }
3482 $submenu_label = trim( $submenu_label );
3483 $submenu_url = '';
3484 $submenu_slug = $submenu_item[2];
3485
3486 if ( preg_match( '/\.php($|\?)/', $submenu_slug ) || wp_http_validate_url( $submenu_slug ) ) {
3487 $submenu_url = $submenu_slug;
3488 } elseif ( ! empty( menu_page_url( $submenu_slug, false ) ) ) {
3489 $submenu_url = html_entity_decode( menu_page_url( $submenu_slug, false ), ENT_QUOTES, get_bloginfo( 'charset' ) );
3490 }
3491
3492 if ( $submenu_url ) {
3493 $menu_commands[] = array(
3494 'label' => sprintf(
3495 /* translators: 1: Menu label, 2: Submenu label. */
3496 __( '%1$s > %2$s' ),
3497 $menu_label,
3498 $submenu_label
3499 ),
3500 'url' => $submenu_url,
3501 'name' => $menu_slug . '-' . $submenu_item[2],
3502 );
3503 }
3504 }
3505 }
3506 }
3507 $command_palette_settings['menu_commands'] = $menu_commands;
3508 }
3509
3510 wp_enqueue_script( 'wp-commands' );
3511 wp_enqueue_style( 'wp-commands' );
3512 wp_enqueue_script( 'wp-core-commands' );
3513
3514 wp_add_inline_script(
3515 'wp-core-commands',
3516 sprintf(
3517 'wp.coreCommands.initializeCommandPalette( %s );',
3518 wp_json_encode( $command_palette_settings, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
3519 )
3520 );
3521}
3522
3523/**
3524 * Removes leading and trailing _empty_ script tags.
3525 *
3526 * This is a helper meant to be used for literal script tag construction
3527 * within `wp_get_inline_script_tag()` or `wp_print_inline_script_tag()`.
3528 * It removes the literal values of "<script>" and "</script>" from
3529 * around an inline script after trimming whitespace. Typically this
3530 * is used in conjunction with output buffering, where `ob_get_clean()`
3531 * is passed as the `$contents` argument.
3532 *
3533 * Example:
3534 *
3535 * // Strips exact literal empty SCRIPT tags.
3536 * $js = '<script>sayHello();</script>;
3537 * 'sayHello();' === wp_remove_surrounding_empty_script_tags( $js );
3538 *
3539 * // Otherwise if anything is different it warns in the JS console.
3540 * $js = '<script type="text/javascript">console.log( "hi" );</script>';
3541 * 'console.error( ... )' === wp_remove_surrounding_empty_script_tags( $js );
3542 *
3543 * @since 6.4.0
3544 * @access private
3545 *
3546 * @see wp_print_inline_script_tag()
3547 * @see wp_get_inline_script_tag()
3548 *
3549 * @param string $contents Script body with manually created SCRIPT tag literals.
3550 * @return string Script body without surrounding script tag literals, or
3551 * original contents if both exact literals aren't present.
3552 */
3553function wp_remove_surrounding_empty_script_tags( $contents ) {
3554 $contents = trim( $contents );
3555 $opener = '<SCRIPT>';
3556 $closer = '</SCRIPT>';
3557
3558 if (
3559 strlen( $contents ) > strlen( $opener ) + strlen( $closer ) &&
3560 strtoupper( substr( $contents, 0, strlen( $opener ) ) ) === $opener &&
3561 strtoupper( substr( $contents, -strlen( $closer ) ) ) === $closer
3562 ) {
3563 return substr( $contents, strlen( $opener ), -strlen( $closer ) );
3564 } else {
3565 $error_message = __( 'Expected string to start with script tag (without attributes) and end with script tag, with optional whitespace.' );
3566 _doing_it_wrong( __FUNCTION__, $error_message, '6.4' );
3567 return sprintf(
3568 'console.error(%s)',
3569 wp_json_encode(
3570 sprintf(
3571 /* translators: %s: wp_remove_surrounding_empty_script_tags() */
3572 __( 'Function %s used incorrectly in PHP.' ),
3573 'wp_remove_surrounding_empty_script_tags()'
3574 ) . ' ' . $error_message
3575 )
3576 );
3577 }
3578}
3579
3580/**
3581 * Adds hooks to load block styles on demand in classic themes.
3582 *
3583 * @since 6.9.0
3584 *
3585 * @see _add_default_theme_supports()
3586 */
3587function wp_load_classic_theme_block_styles_on_demand() {
3588 // This is not relevant to block themes, as they are opted in to loading separate styles on demand via _add_default_theme_supports().
3589 if ( wp_is_block_theme() ) {
3590 return;
3591 }
3592
3593 /*
3594 * Make sure that wp_should_output_buffer_template_for_enhancement() returns true even if there aren't any
3595 * `wp_template_enhancement_output_buffer` filters added, but do so at priority zero so that applications which
3596 * wish to stream responses can more easily turn this off.
3597 */
3598 add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
3599
3600 // If a site has opted out of the template enhancement output buffer, then bail.
3601 if ( ! wp_should_output_buffer_template_for_enhancement() ) {
3602 return;
3603 }
3604
3605 // The following two filters are added by default for block themes in _add_default_theme_supports().
3606
3607 /*
3608 * Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally, and so
3609 * that block-specific styles will only be enqueued when they are used on the page. A priority of zero allows for
3610 * this to be easily overridden by themes which wish to opt out. If a site has explicitly opted out of loading
3611 * separate block styles, then abort.
3612 */
3613 add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
3614 if ( ! wp_should_load_separate_core_block_assets() ) {
3615 return;
3616 }
3617
3618 /*
3619 * Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
3620 * As above, a priority of zero allows for this to be easily overridden by themes which wish to opt out. If a site
3621 * has explicitly opted out of loading block styles on demand, then abort.
3622 */
3623 add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
3624 if ( ! wp_should_load_block_assets_on_demand() ) {
3625 return;
3626 }
3627
3628 // Add hooks which require the presence of the output buffer. Ideally the above two filters could be added here, but they run too early.
3629 add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
3630}
3631
3632/**
3633 * Adds the hooks needed for CSS output to be delayed until after the content of the page has been established.
3634 *
3635 * @since 6.9.0
3636 *
3637 * @see wp_load_classic_theme_block_styles_on_demand()
3638 * @see _wp_footer_scripts()
3639 */
3640function wp_hoist_late_printed_styles() {
3641 // Skip the embed template on-demand styles aren't relevant, and there is no wp_head action.
3642 if ( is_embed() ) {
3643 return;
3644 }
3645
3646 // Capture the styles enqueued at the enqueue_block_assets action, so that non-core block styles and global styles can be inserted afterwards during hoisting.
3647 $style_handles_at_enqueue_block_assets = array();
3648 add_action(
3649 'enqueue_block_assets',
3650 static function () use ( &$style_handles_at_enqueue_block_assets ) {
3651 $style_handles_at_enqueue_block_assets = wp_styles()->queue;
3652 },
3653 PHP_INT_MIN
3654 );
3655 add_action(
3656 'enqueue_block_assets',
3657 static function () use ( &$style_handles_at_enqueue_block_assets ) {
3658 $style_handles_at_enqueue_block_assets = array_values( array_diff( wp_styles()->queue, $style_handles_at_enqueue_block_assets ) );
3659 },
3660 PHP_INT_MAX
3661 );
3662
3663 /*
3664 * Add a placeholder comment into the inline styles for wp-block-library, after which the late block styles
3665 * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
3666 * output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
3667 * `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present.
3668 */
3669 $placeholder = sprintf( '/*%s*/', uniqid( 'wp_block_styles_on_demand_placeholder:' ) );
3670 add_action(
3671 'wp_print_styles',
3672 static function () use ( $placeholder ) {
3673 wp_add_inline_style( 'wp-block-library', $placeholder );
3674 }
3675 );
3676
3677 /*
3678 * Create a substitute for `print_late_styles()` which is aware of block styles. This substitute does not print
3679 * the styles, but it captures what would be printed for block styles and non-block styles so that they can be
3680 * later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts`
3681 * before `print_footer_scripts()` is called.
3682 */
3683 $printed_core_block_styles = '';
3684 $printed_other_block_styles = '';
3685 $printed_global_styles = '';
3686 $printed_late_styles = '';
3687
3688 $capture_late_styles = static function () use ( &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) {
3689 // Gather the styles related to on-demand block enqueues.
3690 $all_core_block_style_handles = array();
3691 $all_other_block_style_handles = array();
3692 foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) {
3693 if ( str_starts_with( $block_type->name, 'core/' ) ) {
3694 foreach ( $block_type->style_handles as $style_handle ) {
3695 $all_core_block_style_handles[] = $style_handle;
3696 }
3697 } else {
3698 foreach ( $block_type->style_handles as $style_handle ) {
3699 $all_other_block_style_handles[] = $style_handle;
3700 }
3701 }
3702 }
3703
3704 /*
3705 * First print all styles related to blocks which should be inserted right after the wp-block-library stylesheet
3706 * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
3707 */
3708 $enqueued_core_block_styles = array_values( array_intersect( $all_core_block_style_handles, wp_styles()->queue ) );
3709 if ( count( $enqueued_core_block_styles ) > 0 ) {
3710 ob_start();
3711 wp_styles()->do_items( $enqueued_core_block_styles );
3712 $printed_core_block_styles = ob_get_clean();
3713 }
3714
3715 // Non-core block styles get printed after the classic-theme-styles stylesheet.
3716 $enqueued_other_block_styles = array_values( array_intersect( $all_other_block_style_handles, wp_styles()->queue ) );
3717 if ( count( $enqueued_other_block_styles ) > 0 ) {
3718 ob_start();
3719 wp_styles()->do_items( $enqueued_other_block_styles );
3720 $printed_other_block_styles = ob_get_clean();
3721 }
3722
3723 // Capture the global-styles so that it can be printed separately after classic-theme-styles and other styles enqueued at enqueue_block_assets.
3724 if ( wp_style_is( 'global-styles' ) ) {
3725 ob_start();
3726 wp_styles()->do_items( array( 'global-styles' ) );
3727 $printed_global_styles = ob_get_clean();
3728 }
3729
3730 /*
3731 * Print all remaining styles not related to blocks. This contains a subset of the logic from
3732 * `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether
3733 * late styles are printed (since they are being hoisted anyway).
3734 */
3735 ob_start();
3736 wp_styles()->do_footer_items();
3737 $printed_late_styles = ob_get_clean();
3738 };
3739
3740 /*
3741 * If `_wp_footer_scripts()` was unhooked from the `wp_print_footer_scripts` action, or if `wp_print_footer_scripts()`
3742 * was unhooked from running at the `wp_footer` action, then only add a callback to `wp_footer` which will capture the
3743 * late-printed styles.
3744 *
3745 * Otherwise, in the normal case where `_wp_footer_scripts()` will run at the `wp_print_footer_scripts` action, then
3746 * swap out `_wp_footer_scripts()` with an alternative which captures the printed styles (for hoisting to HEAD) before
3747 * proceeding with printing the footer scripts.
3748 */
3749 $wp_print_footer_scripts_priority = has_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
3750 if ( false === $wp_print_footer_scripts_priority || false === has_action( 'wp_footer', 'wp_print_footer_scripts' ) ) {
3751 // The normal priority for wp_print_footer_scripts() is to run at 20.
3752 add_action( 'wp_footer', $capture_late_styles, 20 );
3753 } else {
3754 remove_action( 'wp_print_footer_scripts', '_wp_footer_scripts', $wp_print_footer_scripts_priority );
3755 add_action(
3756 'wp_print_footer_scripts',
3757 static function () use ( $capture_late_styles ) {
3758 $capture_late_styles();
3759 print_footer_scripts();
3760 },
3761 $wp_print_footer_scripts_priority
3762 );
3763 }
3764
3765 // Replace placeholder with the captured late styles.
3766 add_filter(
3767 'wp_template_enhancement_output_buffer',
3768 static function ( $buffer ) use ( $placeholder, &$style_handles_at_enqueue_block_assets, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) {
3769
3770 // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
3771 $processor = new class( $buffer ) extends WP_HTML_Tag_Processor {
3772 /**
3773 * Gets the span for the current token.
3774 *
3775 * @return WP_HTML_Span Current token span.
3776 */
3777 private function get_span(): WP_HTML_Span {
3778 // Note: This call will never fail according to the usage of this class, given it is always called after ::next_tag() is true.
3779 $this->set_bookmark( 'here' );
3780 return $this->bookmarks['here'];
3781 }
3782
3783 /**
3784 * Inserts text before the current token.
3785 *
3786 * @param string $text Text to insert.
3787 */
3788 public function insert_before( string $text ) {
3789 $this->lexical_updates[] = new WP_HTML_Text_Replacement( $this->get_span()->start, 0, $text );
3790 }
3791
3792 /**
3793 * Inserts text after the current token.
3794 *
3795 * @param string $text Text to insert.
3796 */
3797 public function insert_after( string $text ) {
3798 $span = $this->get_span();
3799
3800 $this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start + $span->length, 0, $text );
3801 }
3802
3803 /**
3804 * Removes the current token.
3805 */
3806 public function remove() {
3807 $span = $this->get_span();
3808
3809 $this->lexical_updates[] = new WP_HTML_Text_Replacement( $span->start, $span->length, '' );
3810 }
3811 };
3812
3813 // Locate the insertion points in the HEAD.
3814 while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
3815 if (
3816 'STYLE' === $processor->get_tag() &&
3817 'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
3818 ) {
3819 $processor->set_bookmark( 'wp_block_library' );
3820 } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
3821 $processor->set_bookmark( 'head_end' );
3822 break;
3823 } elseif ( ( 'STYLE' === $processor->get_tag() || 'LINK' === $processor->get_tag() ) && $processor->get_attribute( 'id' ) ) {
3824 $id = $processor->get_attribute( 'id' );
3825 $handle = null;
3826 if ( 'STYLE' === $processor->get_tag() ) {
3827 if ( preg_match( '/^(.+)-inline-css$/', $id, $matches ) ) {
3828 $handle = $matches[1];
3829 }
3830 } elseif ( preg_match( '/^(.+)-css$/', $id, $matches ) ) {
3831 $handle = $matches[1];
3832 }
3833
3834 if ( 'classic-theme-styles' === $handle ) {
3835 $processor->set_bookmark( 'classic_theme_styles' );
3836 }
3837
3838 if ( $handle && in_array( $handle, $style_handles_at_enqueue_block_assets, true ) ) {
3839 if ( ! $processor->has_bookmark( 'first_style_at_enqueue_block_assets' ) ) {
3840 $processor->set_bookmark( 'first_style_at_enqueue_block_assets' );
3841 }
3842 $processor->set_bookmark( 'last_style_at_enqueue_block_assets' );
3843 }
3844 }
3845 }
3846
3847 /*
3848 * Insert block styles right after wp-block-library (if it is present). The placeholder CSS comment will
3849 * always be added to the wp-block-library inline style since it gets printed at `wp_head` before the blocks
3850 * are rendered. This means that there may not actually be any block styles to hoist from the footer to
3851 * insert after this inline style. The placeholder CSS comment needs to be added so that the inline style
3852 * gets printed, but if the resulting inline style is empty after the placeholder is removed, then the
3853 * inline style is removed.
3854 */
3855 if ( $processor->has_bookmark( 'wp_block_library' ) ) {
3856 $processor->seek( 'wp_block_library' );
3857
3858 $css_text = $processor->get_modifiable_text();
3859
3860 /*
3861 * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
3862 * be printed. Now that we've located the inline style, the placeholder comment can be removed. If
3863 * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
3864 * comment), then remove the STYLE entirely.
3865 */
3866 $css_text = str_replace( $placeholder, '', $css_text );
3867 if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
3868 $processor->remove();
3869 } else {
3870 $processor->set_modifiable_text( $css_text );
3871 }
3872
3873 $inserted_after = $printed_core_block_styles;
3874 $printed_core_block_styles = '';
3875
3876 // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here.
3877 if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) {
3878 if ( '' !== $printed_other_block_styles ) {
3879 $inserted_after .= $printed_other_block_styles;
3880 }
3881 $printed_other_block_styles = '';
3882
3883 // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here.
3884 if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) {
3885 if ( '' !== $printed_global_styles ) {
3886 $inserted_after .= $printed_global_styles;
3887 }
3888 $printed_global_styles = '';
3889 }
3890 }
3891
3892 if ( '' !== $inserted_after ) {
3893 $processor->insert_after( "\n" . $inserted_after );
3894 }
3895 }
3896
3897 // Insert global-styles after the styles enqueued at enqueue_block_assets.
3898 if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) {
3899 $processor->seek( 'last_style_at_enqueue_block_assets' );
3900
3901 $processor->insert_after( "\n" . $printed_global_styles );
3902 $printed_global_styles = '';
3903
3904 if ( ! $processor->has_bookmark( 'classic_theme_styles' ) && '' !== $printed_other_block_styles ) {
3905 $processor->insert_after( "\n" . $printed_other_block_styles );
3906 $printed_other_block_styles = '';
3907 }
3908 }
3909
3910 // Insert third-party block styles right after the classic-theme-styles.
3911 if ( '' !== $printed_other_block_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) {
3912 $processor->seek( 'classic_theme_styles' );
3913 $processor->insert_after( "\n" . $printed_other_block_styles );
3914 $printed_other_block_styles = '';
3915 }
3916
3917 // Print all remaining styles.
3918 $remaining_styles = $printed_core_block_styles . $printed_other_block_styles . $printed_global_styles . $printed_late_styles;
3919 if ( $remaining_styles && $processor->has_bookmark( 'head_end' ) ) {
3920 $processor->seek( 'head_end' );
3921 $processor->insert_before( $remaining_styles . "\n" );
3922 }
3923 return $processor->get_updated_html();
3924 }
3925 );
3926}
3927
3928/**
3929 * Return the corresponding JavaScript `dataset` name for an attribute
3930 * if it represents a custom data attribute, or `null` if not.
3931 *
3932 * Custom data attributes appear in an element's `dataset` property in a
3933 * browser, but there's a specific way the names are translated from HTML
3934 * into JavaScript. This function indicates how the name would appear in
3935 * JavaScript if a browser would recognize it as a custom data attribute.
3936 *
3937 * Example:
3938 *
3939 * // Dash-letter pairs turn into capital letters.
3940 * 'postId' === wp_js_dataset_name( 'data-post-id' );
3941 * 'Before' === wp_js_dataset_name( 'data--before' );
3942 * '-One--Two---' === wp_js_dataset_name( 'data---one---two---' );
3943 *
3944 * // Not every attribute name will be interpreted as a custom data attribute.
3945 * null === wp_js_dataset_name( 'post-id' );
3946 * null === wp_js_dataset_name( 'data' );
3947 *
3948 * // Some very surprising names will; for example, a property whose name is the empty string.
3949 * '' === wp_js_dataset_name( 'data-' );
3950 * 0 === strlen( wp_js_dataset_name( 'data-' ) );
3951 *
3952 * @since 6.9.0
3953 *
3954 * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3955 * @see \wp_html_custom_data_attribute_name()
3956 *
3957 * @param string $html_attribute_name Raw attribute name as found in the source HTML.
3958 * @return string|null Transformed `dataset` name, if interpretable as a custom data attribute, else `null`.
3959 */
3960function wp_js_dataset_name( string $html_attribute_name ): ?string {
3961 if ( 0 !== substr_compare( $html_attribute_name, 'data-', 0, 5, true ) ) {
3962 return null;
3963 }
3964
3965 $end = strlen( $html_attribute_name );
3966
3967 /*
3968 * If it contains characters which would end the attribute name parsing then
3969 * something else is wrong and this contains more than just an attribute name.
3970 */
3971 if ( ( $end - 5 ) !== strcspn( $html_attribute_name, "=/> \t\f\r\n", 5 ) ) {
3972 return null;
3973 }
3974
3975 /**
3976 * > For each name in list, for each U+002D HYPHEN-MINUS character (-)
3977 * > in the name that is followed by an ASCII lower alpha, remove the
3978 * > U+002D HYPHEN-MINUS character (-) and replace the character that
3979 * > followed it by the same character converted to ASCII uppercase.
3980 *
3981 * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3982 */
3983 $custom_name = '';
3984 $at = 5;
3985 $was_at = $at;
3986
3987 while ( $at < $end ) {
3988 $next_dash_at = strpos( $html_attribute_name, '-', $at );
3989 if ( false === $next_dash_at || $next_dash_at === $end - 1 ) {
3990 break;
3991 }
3992
3993 // Transform `-a` to `A`, for example.
3994 $c = $html_attribute_name[ $next_dash_at + 1 ];
3995 if ( ( $c >= 'A' && $c <= 'Z' ) || ( $c >= 'a' && $c <= 'z' ) ) {
3996 $prefix = substr( $html_attribute_name, $was_at, $next_dash_at - $was_at );
3997 $custom_name .= strtolower( $prefix );
3998 $custom_name .= strtoupper( $c );
3999 $at = $next_dash_at + 2;
4000 $was_at = $at;
4001 continue;
4002 }
4003
4004 $at = $next_dash_at + 1;
4005 }
4006
4007 // If nothing has been added it means there are no dash-letter pairs; return the name as-is.
4008 return '' === $custom_name
4009 ? strtolower( substr( $html_attribute_name, 5 ) )
4010 : ( $custom_name . strtolower( substr( $html_attribute_name, $was_at ) ) );
4011}
4012
4013/**
4014 * Returns a corresponding HTML attribute name for the given name,
4015 * if that name were found in a JS element’s `dataset` property.
4016 *
4017 * Example:
4018 *
4019 * 'data-post-id' === wp_html_custom_data_attribute_name( 'postId' );
4020 * 'data--before' === wp_html_custom_data_attribute_name( 'Before' );
4021 * 'data---one---two---' === wp_html_custom_data_attribute_name( '-One--Two---' );
4022 *
4023 * // Not every attribute name will be interpreted as a custom data attribute.
4024 * null === wp_html_custom_data_attribute_name( '/not-an-attribute/' );
4025 * null === wp_html_custom_data_attribute_name( 'no spaces' );
4026 *
4027 * // Some very surprising names will; for example, a property whose name is the empty string.
4028 * 'data-' === wp_html_custom_data_attribute_name( '' );
4029 *
4030 * @since 6.9.0
4031 *
4032 * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
4033 * @see \wp_js_dataset_name()
4034 *
4035 * @param string $js_dataset_name Name of JS `dataset` property to transform.
4036 * @return string|null Corresponding name of an HTML custom data attribute for the given dataset name,
4037 * if possible to represent in HTML, otherwise `null`.
4038 */
4039function wp_html_custom_data_attribute_name( string $js_dataset_name ): ?string {
4040 $end = strlen( $js_dataset_name );
4041 if ( 0 === $end ) {
4042 return 'data-';
4043 }
4044
4045 /*
4046 * If it contains characters which would end the attribute name parsing then
4047 * something it’s not possible to represent this in HTML.
4048 */
4049 if ( strcspn( $js_dataset_name, "=/> \t\f\r\n" ) !== $end ) {
4050 return null;
4051 }
4052
4053 $html_name = 'data-';
4054 $at = 0;
4055 $was_at = $at;
4056
4057 while ( $at < $end ) {
4058 $next_upper_after = strcspn( $js_dataset_name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', $at );
4059 $next_upper_at = $at + $next_upper_after;
4060 if ( $next_upper_at >= $end ) {
4061 break;
4062 }
4063
4064 $prefix = substr( $js_dataset_name, $was_at, $next_upper_at - $was_at );
4065 $html_name .= strtolower( $prefix );
4066 $html_name .= '-' . strtolower( $js_dataset_name[ $next_upper_at ] );
4067 $at = $next_upper_at + 1;
4068 $was_at = $at;
4069 }
4070
4071 if ( $was_at < $end ) {
4072 $html_name .= strtolower( substr( $js_dataset_name, $was_at ) );
4073 }
4074
4075 return $html_name;
4076}
4077