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


Generated : Tue Sep 22 08:20:31 2026 Cross-referenced by PHPXref