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


Generated : Wed Apr 15 08:20:10 2026 Cross-referenced by PHPXref