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