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


Generated : Sun Jun 14 08:20:09 2026 Cross-referenced by PHPXref