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


Generated : Wed Oct 22 08:20:04 2025 Cross-referenced by PHPXref