[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> script-loader.php (source)

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


Generated : Sun Nov 1 08:20:35 2026 Cross-referenced by PHPXref