wp/wp-includes/class-wp-scripts.php
changeset 21 48c4eec2b7e6
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
       
     1 <?php
       
     2 /**
       
     3  * Dependencies API: WP_Scripts class
       
     4  *
       
     5  * @since 2.6.0
       
     6  *
       
     7  * @package WordPress
       
     8  * @subpackage Dependencies
       
     9  */
       
    10 
       
    11 /**
       
    12  * Core class used to register scripts.
       
    13  *
       
    14  * @since 2.1.0
       
    15  *
       
    16  * @see WP_Dependencies
       
    17  */
       
    18 class WP_Scripts extends WP_Dependencies {
       
    19 	/**
       
    20 	 * Base URL for scripts.
       
    21 	 *
       
    22 	 * Full URL with trailing slash.
       
    23 	 *
       
    24 	 * @since 2.6.0
       
    25 	 * @var string
       
    26 	 */
       
    27 	public $base_url;
       
    28 
       
    29 	/**
       
    30 	 * URL of the content directory.
       
    31 	 *
       
    32 	 * @since 2.8.0
       
    33 	 * @var string
       
    34 	 */
       
    35 	public $content_url;
       
    36 
       
    37 	/**
       
    38 	 * Default version string for scripts.
       
    39 	 *
       
    40 	 * @since 2.6.0
       
    41 	 * @var string
       
    42 	 */
       
    43 	public $default_version;
       
    44 
       
    45 	/**
       
    46 	 * Holds handles of scripts which are enqueued in footer.
       
    47 	 *
       
    48 	 * @since 2.8.0
       
    49 	 * @var array
       
    50 	 */
       
    51 	public $in_footer = array();
       
    52 
       
    53 	/**
       
    54 	 * Holds a list of script handles which will be concatenated.
       
    55 	 *
       
    56 	 * @since 2.8.0
       
    57 	 * @var string
       
    58 	 */
       
    59 	public $concat = '';
       
    60 
       
    61 	/**
       
    62 	 * Holds a string which contains script handles and their version.
       
    63 	 *
       
    64 	 * @since 2.8.0
       
    65 	 * @deprecated 3.4.0
       
    66 	 * @var string
       
    67 	 */
       
    68 	public $concat_version = '';
       
    69 
       
    70 	/**
       
    71 	 * Whether to perform concatenation.
       
    72 	 *
       
    73 	 * @since 2.8.0
       
    74 	 * @var bool
       
    75 	 */
       
    76 	public $do_concat = false;
       
    77 
       
    78 	/**
       
    79 	 * Holds HTML markup of scripts and additional data if concatenation
       
    80 	 * is enabled.
       
    81 	 *
       
    82 	 * @since 2.8.0
       
    83 	 * @var string
       
    84 	 */
       
    85 	public $print_html = '';
       
    86 
       
    87 	/**
       
    88 	 * Holds inline code if concatenation is enabled.
       
    89 	 *
       
    90 	 * @since 2.8.0
       
    91 	 * @var string
       
    92 	 */
       
    93 	public $print_code = '';
       
    94 
       
    95 	/**
       
    96 	 * Holds a list of script handles which are not in the default directory
       
    97 	 * if concatenation is enabled.
       
    98 	 *
       
    99 	 * Unused in core.
       
   100 	 *
       
   101 	 * @since 2.8.0
       
   102 	 * @var string
       
   103 	 */
       
   104 	public $ext_handles = '';
       
   105 
       
   106 	/**
       
   107 	 * Holds a string which contains handles and versions of scripts which
       
   108 	 * are not in the default directory if concatenation is enabled.
       
   109 	 *
       
   110 	 * Unused in core.
       
   111 	 *
       
   112 	 * @since 2.8.0
       
   113 	 * @var string
       
   114 	 */
       
   115 	public $ext_version = '';
       
   116 
       
   117 	/**
       
   118 	 * List of default directories.
       
   119 	 *
       
   120 	 * @since 2.8.0
       
   121 	 * @var array
       
   122 	 */
       
   123 	public $default_dirs;
       
   124 
       
   125 	/**
       
   126 	 * Holds a mapping of dependents (as handles) for a given script handle.
       
   127 	 * Used to optimize recursive dependency tree checks.
       
   128 	 *
       
   129 	 * @since 6.3.0
       
   130 	 * @var array
       
   131 	 */
       
   132 	private $dependents_map = array();
       
   133 
       
   134 	/**
       
   135 	 * Holds a reference to the delayed (non-blocking) script loading strategies.
       
   136 	 * Used by methods that validate loading strategies.
       
   137 	 *
       
   138 	 * @since 6.3.0
       
   139 	 * @var string[]
       
   140 	 */
       
   141 	private $delayed_strategies = array( 'defer', 'async' );
       
   142 
       
   143 	/**
       
   144 	 * Constructor.
       
   145 	 *
       
   146 	 * @since 2.6.0
       
   147 	 */
       
   148 	public function __construct() {
       
   149 		$this->init();
       
   150 		add_action( 'init', array( $this, 'init' ), 0 );
       
   151 	}
       
   152 
       
   153 	/**
       
   154 	 * Initialize the class.
       
   155 	 *
       
   156 	 * @since 3.4.0
       
   157 	 */
       
   158 	public function init() {
       
   159 		/**
       
   160 		 * Fires when the WP_Scripts instance is initialized.
       
   161 		 *
       
   162 		 * @since 2.6.0
       
   163 		 *
       
   164 		 * @param WP_Scripts $wp_scripts WP_Scripts instance (passed by reference).
       
   165 		 */
       
   166 		do_action_ref_array( 'wp_default_scripts', array( &$this ) );
       
   167 	}
       
   168 
       
   169 	/**
       
   170 	 * Prints scripts.
       
   171 	 *
       
   172 	 * Prints the scripts passed to it or the print queue. Also prints all necessary dependencies.
       
   173 	 *
       
   174 	 * @since 2.1.0
       
   175 	 * @since 2.8.0 Added the `$group` parameter.
       
   176 	 *
       
   177 	 * @param string|string[]|false $handles Optional. Scripts to be printed: queue (false),
       
   178 	 *                                       single script (string), or multiple scripts (array of strings).
       
   179 	 *                                       Default false.
       
   180 	 * @param int|false             $group   Optional. Group level: level (int), no groups (false).
       
   181 	 *                                       Default false.
       
   182 	 * @return string[] Handles of scripts that have been printed.
       
   183 	 */
       
   184 	public function print_scripts( $handles = false, $group = false ) {
       
   185 		return $this->do_items( $handles, $group );
       
   186 	}
       
   187 
       
   188 	/**
       
   189 	 * Prints extra scripts of a registered script.
       
   190 	 *
       
   191 	 * @since 2.1.0
       
   192 	 * @since 2.8.0 Added the `$display` parameter.
       
   193 	 * @deprecated 3.3.0
       
   194 	 *
       
   195 	 * @see print_extra_script()
       
   196 	 *
       
   197 	 * @param string $handle  The script's registered handle.
       
   198 	 * @param bool   $display Optional. Whether to print the extra script
       
   199 	 *                        instead of just returning it. Default true.
       
   200 	 * @return bool|string|void Void if no data exists, extra scripts if `$display` is true,
       
   201 	 *                          true otherwise.
       
   202 	 */
       
   203 	public function print_scripts_l10n( $handle, $display = true ) {
       
   204 		_deprecated_function( __FUNCTION__, '3.3.0', 'WP_Scripts::print_extra_script()' );
       
   205 		return $this->print_extra_script( $handle, $display );
       
   206 	}
       
   207 
       
   208 	/**
       
   209 	 * Prints extra scripts of a registered script.
       
   210 	 *
       
   211 	 * @since 3.3.0
       
   212 	 *
       
   213 	 * @param string $handle  The script's registered handle.
       
   214 	 * @param bool   $display Optional. Whether to print the extra script
       
   215 	 *                        instead of just returning it. Default true.
       
   216 	 * @return bool|string|void Void if no data exists, extra scripts if `$display` is true,
       
   217 	 *                          true otherwise.
       
   218 	 */
       
   219 	public function print_extra_script( $handle, $display = true ) {
       
   220 		$output = $this->get_data( $handle, 'data' );
       
   221 		if ( ! $output ) {
       
   222 			return;
       
   223 		}
       
   224 
       
   225 		if ( ! $display ) {
       
   226 			return $output;
       
   227 		}
       
   228 
       
   229 		wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-extra" ) );
       
   230 
       
   231 		return true;
       
   232 	}
       
   233 
       
   234 	/**
       
   235 	 * Checks whether all dependents of a given handle are in the footer.
       
   236 	 *
       
   237 	 * If there are no dependents, this is considered the same as if all dependents were in the footer.
       
   238 	 *
       
   239 	 * @since 6.4.0
       
   240 	 *
       
   241 	 * @param string $handle Script handle.
       
   242 	 * @return bool Whether all dependents are in the footer.
       
   243 	 */
       
   244 	private function are_all_dependents_in_footer( $handle ) {
       
   245 		foreach ( $this->get_dependents( $handle ) as $dep ) {
       
   246 			if ( isset( $this->groups[ $dep ] ) && 0 === $this->groups[ $dep ] ) {
       
   247 				return false;
       
   248 			}
       
   249 		}
       
   250 		return true;
       
   251 	}
       
   252 
       
   253 	/**
       
   254 	 * Processes a script dependency.
       
   255 	 *
       
   256 	 * @since 2.6.0
       
   257 	 * @since 2.8.0 Added the `$group` parameter.
       
   258 	 *
       
   259 	 * @see WP_Dependencies::do_item()
       
   260 	 *
       
   261 	 * @param string    $handle The script's registered handle.
       
   262 	 * @param int|false $group  Optional. Group level: level (int), no groups (false).
       
   263 	 *                          Default false.
       
   264 	 * @return bool True on success, false on failure.
       
   265 	 */
       
   266 	public function do_item( $handle, $group = false ) {
       
   267 		if ( ! parent::do_item( $handle ) ) {
       
   268 			return false;
       
   269 		}
       
   270 
       
   271 		if ( 0 === $group && $this->groups[ $handle ] > 0 ) {
       
   272 			$this->in_footer[] = $handle;
       
   273 			return false;
       
   274 		}
       
   275 
       
   276 		if ( false === $group && in_array( $handle, $this->in_footer, true ) ) {
       
   277 			$this->in_footer = array_diff( $this->in_footer, (array) $handle );
       
   278 		}
       
   279 
       
   280 		$obj = $this->registered[ $handle ];
       
   281 
       
   282 		if ( null === $obj->ver ) {
       
   283 			$ver = '';
       
   284 		} else {
       
   285 			$ver = $obj->ver ? $obj->ver : $this->default_version;
       
   286 		}
       
   287 
       
   288 		if ( isset( $this->args[ $handle ] ) ) {
       
   289 			$ver = $ver ? $ver . '&amp;' . $this->args[ $handle ] : $this->args[ $handle ];
       
   290 		}
       
   291 
       
   292 		$src               = $obj->src;
       
   293 		$strategy          = $this->get_eligible_loading_strategy( $handle );
       
   294 		$intended_strategy = (string) $this->get_data( $handle, 'strategy' );
       
   295 		$cond_before       = '';
       
   296 		$cond_after        = '';
       
   297 		$conditional       = isset( $obj->extra['conditional'] ) ? $obj->extra['conditional'] : '';
       
   298 
       
   299 		if ( ! $this->is_delayed_strategy( $intended_strategy ) ) {
       
   300 			$intended_strategy = '';
       
   301 		}
       
   302 
       
   303 		/*
       
   304 		 * Move this script to the footer if:
       
   305 		 * 1. The script is in the header group.
       
   306 		 * 2. The current output is the header.
       
   307 		 * 3. The intended strategy is delayed.
       
   308 		 * 4. The actual strategy is not delayed.
       
   309 		 * 5. All dependent scripts are in the footer.
       
   310 		 */
       
   311 		if (
       
   312 			0 === $group &&
       
   313 			0 === $this->groups[ $handle ] &&
       
   314 			$intended_strategy &&
       
   315 			! $this->is_delayed_strategy( $strategy ) &&
       
   316 			$this->are_all_dependents_in_footer( $handle )
       
   317 		) {
       
   318 			$this->in_footer[] = $handle;
       
   319 			return false;
       
   320 		}
       
   321 
       
   322 		if ( $conditional ) {
       
   323 			$cond_before = "<!--[if {$conditional}]>\n";
       
   324 			$cond_after  = "<![endif]-->\n";
       
   325 		}
       
   326 
       
   327 		$before_script = $this->get_inline_script_tag( $handle, 'before' );
       
   328 		$after_script  = $this->get_inline_script_tag( $handle, 'after' );
       
   329 
       
   330 		if ( $before_script || $after_script ) {
       
   331 			$inline_script_tag = $cond_before . $before_script . $after_script . $cond_after;
       
   332 		} else {
       
   333 			$inline_script_tag = '';
       
   334 		}
       
   335 
       
   336 		/*
       
   337 		 * Prevent concatenation of scripts if the text domain is defined
       
   338 		 * to ensure the dependency order is respected.
       
   339 		 */
       
   340 		$translations_stop_concat = ! empty( $obj->textdomain );
       
   341 
       
   342 		$translations = $this->print_translations( $handle, false );
       
   343 		if ( $translations ) {
       
   344 			$translations = wp_get_inline_script_tag( $translations, array( 'id' => "{$handle}-js-translations" ) );
       
   345 		}
       
   346 
       
   347 		if ( $this->do_concat ) {
       
   348 			/**
       
   349 			 * Filters the script loader source.
       
   350 			 *
       
   351 			 * @since 2.2.0
       
   352 			 *
       
   353 			 * @param string $src    Script loader source path.
       
   354 			 * @param string $handle Script handle.
       
   355 			 */
       
   356 			$srce = apply_filters( 'script_loader_src', $src, $handle );
       
   357 
       
   358 			if (
       
   359 				$this->in_default_dir( $srce )
       
   360 				&& ( $before_script || $after_script || $translations_stop_concat || $this->is_delayed_strategy( $strategy ) )
       
   361 			) {
       
   362 				$this->do_concat = false;
       
   363 
       
   364 				// Have to print the so-far concatenated scripts right away to maintain the right order.
       
   365 				_print_scripts();
       
   366 				$this->reset();
       
   367 			} elseif ( $this->in_default_dir( $srce ) && ! $conditional ) {
       
   368 				$this->print_code     .= $this->print_extra_script( $handle, false );
       
   369 				$this->concat         .= "$handle,";
       
   370 				$this->concat_version .= "$handle$ver";
       
   371 				return true;
       
   372 			} else {
       
   373 				$this->ext_handles .= "$handle,";
       
   374 				$this->ext_version .= "$handle$ver";
       
   375 			}
       
   376 		}
       
   377 
       
   378 		$has_conditional_data = $conditional && $this->get_data( $handle, 'data' );
       
   379 
       
   380 		if ( $has_conditional_data ) {
       
   381 			echo $cond_before;
       
   382 		}
       
   383 
       
   384 		$this->print_extra_script( $handle );
       
   385 
       
   386 		if ( $has_conditional_data ) {
       
   387 			echo $cond_after;
       
   388 		}
       
   389 
       
   390 		// A single item may alias a set of items, by having dependencies, but no source.
       
   391 		if ( ! $src ) {
       
   392 			if ( $inline_script_tag ) {
       
   393 				if ( $this->do_concat ) {
       
   394 					$this->print_html .= $inline_script_tag;
       
   395 				} else {
       
   396 					echo $inline_script_tag;
       
   397 				}
       
   398 			}
       
   399 
       
   400 			return true;
       
   401 		}
       
   402 
       
   403 		if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && str_starts_with( $src, $this->content_url ) ) ) {
       
   404 			$src = $this->base_url . $src;
       
   405 		}
       
   406 
       
   407 		if ( ! empty( $ver ) ) {
       
   408 			$src = add_query_arg( 'ver', $ver, $src );
       
   409 		}
       
   410 
       
   411 		/** This filter is documented in wp-includes/class-wp-scripts.php */
       
   412 		$src = esc_url_raw( apply_filters( 'script_loader_src', $src, $handle ) );
       
   413 
       
   414 		if ( ! $src ) {
       
   415 			return true;
       
   416 		}
       
   417 
       
   418 		$attr = array(
       
   419 			'src' => $src,
       
   420 			'id'  => "{$handle}-js",
       
   421 		);
       
   422 		if ( $strategy ) {
       
   423 			$attr[ $strategy ] = true;
       
   424 		}
       
   425 		if ( $intended_strategy ) {
       
   426 			$attr['data-wp-strategy'] = $intended_strategy;
       
   427 		}
       
   428 		$tag  = $translations . $cond_before . $before_script;
       
   429 		$tag .= wp_get_script_tag( $attr );
       
   430 		$tag .= $after_script . $cond_after;
       
   431 
       
   432 		/**
       
   433 		 * Filters the HTML script tag of an enqueued script.
       
   434 		 *
       
   435 		 * @since 4.1.0
       
   436 		 *
       
   437 		 * @param string $tag    The `<script>` tag for the enqueued script.
       
   438 		 * @param string $handle The script's registered handle.
       
   439 		 * @param string $src    The script's source URL.
       
   440 		 */
       
   441 		$tag = apply_filters( 'script_loader_tag', $tag, $handle, $src );
       
   442 
       
   443 		if ( $this->do_concat ) {
       
   444 			$this->print_html .= $tag;
       
   445 		} else {
       
   446 			echo $tag;
       
   447 		}
       
   448 
       
   449 		return true;
       
   450 	}
       
   451 
       
   452 	/**
       
   453 	 * Adds extra code to a registered script.
       
   454 	 *
       
   455 	 * @since 4.5.0
       
   456 	 *
       
   457 	 * @param string $handle   Name of the script to add the inline script to.
       
   458 	 *                         Must be lowercase.
       
   459 	 * @param string $data     String containing the JavaScript to be added.
       
   460 	 * @param string $position Optional. Whether to add the inline script
       
   461 	 *                         before the handle or after. Default 'after'.
       
   462 	 * @return bool True on success, false on failure.
       
   463 	 */
       
   464 	public function add_inline_script( $handle, $data, $position = 'after' ) {
       
   465 		if ( ! $data ) {
       
   466 			return false;
       
   467 		}
       
   468 
       
   469 		if ( 'after' !== $position ) {
       
   470 			$position = 'before';
       
   471 		}
       
   472 
       
   473 		$script   = (array) $this->get_data( $handle, $position );
       
   474 		$script[] = $data;
       
   475 
       
   476 		return $this->add_data( $handle, $position, $script );
       
   477 	}
       
   478 
       
   479 	/**
       
   480 	 * Prints inline scripts registered for a specific handle.
       
   481 	 *
       
   482 	 * @since 4.5.0
       
   483 	 * @deprecated 6.3.0 Use methods get_inline_script_tag() or get_inline_script_data() instead.
       
   484 	 *
       
   485 	 * @param string $handle   Name of the script to print inline scripts for.
       
   486 	 *                         Must be lowercase.
       
   487 	 * @param string $position Optional. Whether to add the inline script
       
   488 	 *                         before the handle or after. Default 'after'.
       
   489 	 * @param bool   $display  Optional. Whether to print the script tag
       
   490 	 *                         instead of just returning the script data. Default true.
       
   491 	 * @return string|false Script data on success, false otherwise.
       
   492 	 */
       
   493 	public function print_inline_script( $handle, $position = 'after', $display = true ) {
       
   494 		_deprecated_function( __METHOD__, '6.3.0', 'WP_Scripts::get_inline_script_data() or WP_Scripts::get_inline_script_tag()' );
       
   495 
       
   496 		$output = $this->get_inline_script_data( $handle, $position );
       
   497 		if ( empty( $output ) ) {
       
   498 			return false;
       
   499 		}
       
   500 
       
   501 		if ( $display ) {
       
   502 			echo $this->get_inline_script_tag( $handle, $position );
       
   503 		}
       
   504 		return $output;
       
   505 	}
       
   506 
       
   507 	/**
       
   508 	 * Gets data for inline scripts registered for a specific handle.
       
   509 	 *
       
   510 	 * @since 6.3.0
       
   511 	 *
       
   512 	 * @param string $handle   Name of the script to get data for.
       
   513 	 *                         Must be lowercase.
       
   514 	 * @param string $position Optional. Whether to add the inline script
       
   515 	 *                         before the handle or after. Default 'after'.
       
   516 	 * @return string Inline script, which may be empty string.
       
   517 	 */
       
   518 	public function get_inline_script_data( $handle, $position = 'after' ) {
       
   519 		$data = $this->get_data( $handle, $position );
       
   520 		if ( empty( $data ) || ! is_array( $data ) ) {
       
   521 			return '';
       
   522 		}
       
   523 
       
   524 		return trim( implode( "\n", $data ), "\n" );
       
   525 	}
       
   526 
       
   527 	/**
       
   528 	 * Gets tags for inline scripts registered for a specific handle.
       
   529 	 *
       
   530 	 * @since 6.3.0
       
   531 	 *
       
   532 	 * @param string $handle   Name of the script to get associated inline script tag for.
       
   533 	 *                         Must be lowercase.
       
   534 	 * @param string $position Optional. Whether to get tag for inline
       
   535 	 *                         scripts in the before or after position. Default 'after'.
       
   536 	 * @return string Inline script, which may be empty string.
       
   537 	 */
       
   538 	public function get_inline_script_tag( $handle, $position = 'after' ) {
       
   539 		$js = $this->get_inline_script_data( $handle, $position );
       
   540 		if ( empty( $js ) ) {
       
   541 			return '';
       
   542 		}
       
   543 
       
   544 		$id = "{$handle}-js-{$position}";
       
   545 
       
   546 		return wp_get_inline_script_tag( $js, compact( 'id' ) );
       
   547 	}
       
   548 
       
   549 	/**
       
   550 	 * Localizes a script, only if the script has already been added.
       
   551 	 *
       
   552 	 * @since 2.1.0
       
   553 	 *
       
   554 	 * @param string $handle      Name of the script to attach data to.
       
   555 	 * @param string $object_name Name of the variable that will contain the data.
       
   556 	 * @param array  $l10n        Array of data to localize.
       
   557 	 * @return bool True on success, false on failure.
       
   558 	 */
       
   559 	public function localize( $handle, $object_name, $l10n ) {
       
   560 		if ( 'jquery' === $handle ) {
       
   561 			$handle = 'jquery-core';
       
   562 		}
       
   563 
       
   564 		if ( is_array( $l10n ) && isset( $l10n['l10n_print_after'] ) ) { // back compat, preserve the code in 'l10n_print_after' if present.
       
   565 			$after = $l10n['l10n_print_after'];
       
   566 			unset( $l10n['l10n_print_after'] );
       
   567 		}
       
   568 
       
   569 		if ( ! is_array( $l10n ) ) {
       
   570 			_doing_it_wrong(
       
   571 				__METHOD__,
       
   572 				sprintf(
       
   573 					/* translators: 1: $l10n, 2: wp_add_inline_script() */
       
   574 					__( 'The %1$s parameter must be an array. To pass arbitrary data to scripts, use the %2$s function instead.' ),
       
   575 					'<code>$l10n</code>',
       
   576 					'<code>wp_add_inline_script()</code>'
       
   577 				),
       
   578 				'5.7.0'
       
   579 			);
       
   580 
       
   581 			if ( false === $l10n ) {
       
   582 				// This should really not be needed, but is necessary for backward compatibility.
       
   583 				$l10n = array( $l10n );
       
   584 			}
       
   585 		}
       
   586 
       
   587 		if ( is_string( $l10n ) ) {
       
   588 			$l10n = html_entity_decode( $l10n, ENT_QUOTES, 'UTF-8' );
       
   589 		} elseif ( is_array( $l10n ) ) {
       
   590 			foreach ( $l10n as $key => $value ) {
       
   591 				if ( ! is_scalar( $value ) ) {
       
   592 					continue;
       
   593 				}
       
   594 
       
   595 				$l10n[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
       
   596 			}
       
   597 		}
       
   598 
       
   599 		$script = "var $object_name = " . wp_json_encode( $l10n ) . ';';
       
   600 
       
   601 		if ( ! empty( $after ) ) {
       
   602 			$script .= "\n$after;";
       
   603 		}
       
   604 
       
   605 		$data = $this->get_data( $handle, 'data' );
       
   606 
       
   607 		if ( ! empty( $data ) ) {
       
   608 			$script = "$data\n$script";
       
   609 		}
       
   610 
       
   611 		return $this->add_data( $handle, 'data', $script );
       
   612 	}
       
   613 
       
   614 	/**
       
   615 	 * Sets handle group.
       
   616 	 *
       
   617 	 * @since 2.8.0
       
   618 	 *
       
   619 	 * @see WP_Dependencies::set_group()
       
   620 	 *
       
   621 	 * @param string    $handle    Name of the item. Should be unique.
       
   622 	 * @param bool      $recursion Internal flag that calling function was called recursively.
       
   623 	 * @param int|false $group     Optional. Group level: level (int), no groups (false).
       
   624 	 *                             Default false.
       
   625 	 * @return bool Not already in the group or a lower group.
       
   626 	 */
       
   627 	public function set_group( $handle, $recursion, $group = false ) {
       
   628 		if ( isset( $this->registered[ $handle ]->args ) && 1 === $this->registered[ $handle ]->args ) {
       
   629 			$grp = 1;
       
   630 		} else {
       
   631 			$grp = (int) $this->get_data( $handle, 'group' );
       
   632 		}
       
   633 
       
   634 		if ( false !== $group && $grp > $group ) {
       
   635 			$grp = $group;
       
   636 		}
       
   637 
       
   638 		return parent::set_group( $handle, $recursion, $grp );
       
   639 	}
       
   640 
       
   641 	/**
       
   642 	 * Sets a translation textdomain.
       
   643 	 *
       
   644 	 * @since 5.0.0
       
   645 	 * @since 5.1.0 The `$domain` parameter was made optional.
       
   646 	 *
       
   647 	 * @param string $handle Name of the script to register a translation domain to.
       
   648 	 * @param string $domain Optional. Text domain. Default 'default'.
       
   649 	 * @param string $path   Optional. The full file path to the directory containing translation files.
       
   650 	 * @return bool True if the text domain was registered, false if not.
       
   651 	 */
       
   652 	public function set_translations( $handle, $domain = 'default', $path = '' ) {
       
   653 		if ( ! isset( $this->registered[ $handle ] ) ) {
       
   654 			return false;
       
   655 		}
       
   656 
       
   657 		/** @var \_WP_Dependency $obj */
       
   658 		$obj = $this->registered[ $handle ];
       
   659 
       
   660 		if ( ! in_array( 'wp-i18n', $obj->deps, true ) ) {
       
   661 			$obj->deps[] = 'wp-i18n';
       
   662 		}
       
   663 
       
   664 		return $obj->set_translations( $domain, $path );
       
   665 	}
       
   666 
       
   667 	/**
       
   668 	 * Prints translations set for a specific handle.
       
   669 	 *
       
   670 	 * @since 5.0.0
       
   671 	 *
       
   672 	 * @param string $handle  Name of the script to add the inline script to.
       
   673 	 *                        Must be lowercase.
       
   674 	 * @param bool   $display Optional. Whether to print the script
       
   675 	 *                        instead of just returning it. Default true.
       
   676 	 * @return string|false Script on success, false otherwise.
       
   677 	 */
       
   678 	public function print_translations( $handle, $display = true ) {
       
   679 		if ( ! isset( $this->registered[ $handle ] ) || empty( $this->registered[ $handle ]->textdomain ) ) {
       
   680 			return false;
       
   681 		}
       
   682 
       
   683 		$domain = $this->registered[ $handle ]->textdomain;
       
   684 		$path   = '';
       
   685 
       
   686 		if ( isset( $this->registered[ $handle ]->translations_path ) ) {
       
   687 			$path = $this->registered[ $handle ]->translations_path;
       
   688 		}
       
   689 
       
   690 		$json_translations = load_script_textdomain( $handle, $domain, $path );
       
   691 
       
   692 		if ( ! $json_translations ) {
       
   693 			return false;
       
   694 		}
       
   695 
       
   696 		$output = <<<JS
       
   697 ( function( domain, translations ) {
       
   698 	var localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
       
   699 	localeData[""].domain = domain;
       
   700 	wp.i18n.setLocaleData( localeData, domain );
       
   701 } )( "{$domain}", {$json_translations} );
       
   702 JS;
       
   703 
       
   704 		if ( $display ) {
       
   705 			wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-translations" ) );
       
   706 		}
       
   707 
       
   708 		return $output;
       
   709 	}
       
   710 
       
   711 	/**
       
   712 	 * Determines script dependencies.
       
   713 	 *
       
   714 	 * @since 2.1.0
       
   715 	 *
       
   716 	 * @see WP_Dependencies::all_deps()
       
   717 	 *
       
   718 	 * @param string|string[] $handles   Item handle (string) or item handles (array of strings).
       
   719 	 * @param bool            $recursion Optional. Internal flag that function is calling itself.
       
   720 	 *                                   Default false.
       
   721 	 * @param int|false       $group     Optional. Group level: level (int), no groups (false).
       
   722 	 *                                   Default false.
       
   723 	 * @return bool True on success, false on failure.
       
   724 	 */
       
   725 	public function all_deps( $handles, $recursion = false, $group = false ) {
       
   726 		$r = parent::all_deps( $handles, $recursion, $group );
       
   727 		if ( ! $recursion ) {
       
   728 			/**
       
   729 			 * Filters the list of script dependencies left to print.
       
   730 			 *
       
   731 			 * @since 2.3.0
       
   732 			 *
       
   733 			 * @param string[] $to_do An array of script dependency handles.
       
   734 			 */
       
   735 			$this->to_do = apply_filters( 'print_scripts_array', $this->to_do );
       
   736 		}
       
   737 		return $r;
       
   738 	}
       
   739 
       
   740 	/**
       
   741 	 * Processes items and dependencies for the head group.
       
   742 	 *
       
   743 	 * @since 2.8.0
       
   744 	 *
       
   745 	 * @see WP_Dependencies::do_items()
       
   746 	 *
       
   747 	 * @return string[] Handles of items that have been processed.
       
   748 	 */
       
   749 	public function do_head_items() {
       
   750 		$this->do_items( false, 0 );
       
   751 		return $this->done;
       
   752 	}
       
   753 
       
   754 	/**
       
   755 	 * Processes items and dependencies for the footer group.
       
   756 	 *
       
   757 	 * @since 2.8.0
       
   758 	 *
       
   759 	 * @see WP_Dependencies::do_items()
       
   760 	 *
       
   761 	 * @return string[] Handles of items that have been processed.
       
   762 	 */
       
   763 	public function do_footer_items() {
       
   764 		$this->do_items( false, 1 );
       
   765 		return $this->done;
       
   766 	}
       
   767 
       
   768 	/**
       
   769 	 * Whether a handle's source is in a default directory.
       
   770 	 *
       
   771 	 * @since 2.8.0
       
   772 	 *
       
   773 	 * @param string $src The source of the enqueued script.
       
   774 	 * @return bool True if found, false if not.
       
   775 	 */
       
   776 	public function in_default_dir( $src ) {
       
   777 		if ( ! $this->default_dirs ) {
       
   778 			return true;
       
   779 		}
       
   780 
       
   781 		if ( str_starts_with( $src, '/' . WPINC . '/js/l10n' ) ) {
       
   782 			return false;
       
   783 		}
       
   784 
       
   785 		foreach ( (array) $this->default_dirs as $test ) {
       
   786 			if ( str_starts_with( $src, $test ) ) {
       
   787 				return true;
       
   788 			}
       
   789 		}
       
   790 		return false;
       
   791 	}
       
   792 
       
   793 	/**
       
   794 	 * This overrides the add_data method from WP_Dependencies, to support normalizing of $args.
       
   795 	 *
       
   796 	 * @since 6.3.0
       
   797 	 *
       
   798 	 * @param string $handle Name of the item. Should be unique.
       
   799 	 * @param string $key    The data key.
       
   800 	 * @param mixed  $value  The data value.
       
   801 	 * @return bool True on success, false on failure.
       
   802 	 */
       
   803 	public function add_data( $handle, $key, $value ) {
       
   804 		if ( ! isset( $this->registered[ $handle ] ) ) {
       
   805 			return false;
       
   806 		}
       
   807 
       
   808 		if ( 'strategy' === $key ) {
       
   809 			if ( ! empty( $value ) && ! $this->is_delayed_strategy( $value ) ) {
       
   810 				_doing_it_wrong(
       
   811 					__METHOD__,
       
   812 					sprintf(
       
   813 						/* translators: 1: $strategy, 2: $handle */
       
   814 						__( 'Invalid strategy `%1$s` defined for `%2$s` during script registration.' ),
       
   815 						$value,
       
   816 						$handle
       
   817 					),
       
   818 					'6.3.0'
       
   819 				);
       
   820 				return false;
       
   821 			} elseif ( ! $this->registered[ $handle ]->src && $this->is_delayed_strategy( $value ) ) {
       
   822 				_doing_it_wrong(
       
   823 					__METHOD__,
       
   824 					sprintf(
       
   825 						/* translators: 1: $strategy, 2: $handle */
       
   826 						__( 'Cannot supply a strategy `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ),
       
   827 						$value,
       
   828 						$handle
       
   829 					),
       
   830 					'6.3.0'
       
   831 				);
       
   832 				return false;
       
   833 			}
       
   834 		}
       
   835 		return parent::add_data( $handle, $key, $value );
       
   836 	}
       
   837 
       
   838 	/**
       
   839 	 * Gets all dependents of a script.
       
   840 	 *
       
   841 	 * @since 6.3.0
       
   842 	 *
       
   843 	 * @param string $handle The script handle.
       
   844 	 * @return string[] Script handles.
       
   845 	 */
       
   846 	private function get_dependents( $handle ) {
       
   847 		// Check if dependents map for the handle in question is present. If so, use it.
       
   848 		if ( isset( $this->dependents_map[ $handle ] ) ) {
       
   849 			return $this->dependents_map[ $handle ];
       
   850 		}
       
   851 
       
   852 		$dependents = array();
       
   853 
       
   854 		// Iterate over all registered scripts, finding dependents of the script passed to this method.
       
   855 		foreach ( $this->registered as $registered_handle => $args ) {
       
   856 			if ( in_array( $handle, $args->deps, true ) ) {
       
   857 				$dependents[] = $registered_handle;
       
   858 			}
       
   859 		}
       
   860 
       
   861 		// Add the handles dependents to the map to ease future lookups.
       
   862 		$this->dependents_map[ $handle ] = $dependents;
       
   863 
       
   864 		return $dependents;
       
   865 	}
       
   866 
       
   867 	/**
       
   868 	 * Checks if the strategy passed is a valid delayed (non-blocking) strategy.
       
   869 	 *
       
   870 	 * @since 6.3.0
       
   871 	 *
       
   872 	 * @param string $strategy The strategy to check.
       
   873 	 * @return bool True if $strategy is one of the delayed strategies, otherwise false.
       
   874 	 */
       
   875 	private function is_delayed_strategy( $strategy ) {
       
   876 		return in_array(
       
   877 			$strategy,
       
   878 			$this->delayed_strategies,
       
   879 			true
       
   880 		);
       
   881 	}
       
   882 
       
   883 	/**
       
   884 	 * Gets the best eligible loading strategy for a script.
       
   885 	 *
       
   886 	 * @since 6.3.0
       
   887 	 *
       
   888 	 * @param string $handle The script handle.
       
   889 	 * @return string The best eligible loading strategy.
       
   890 	 */
       
   891 	private function get_eligible_loading_strategy( $handle ) {
       
   892 		$intended = (string) $this->get_data( $handle, 'strategy' );
       
   893 
       
   894 		// Bail early if there is no intended strategy.
       
   895 		if ( ! $intended ) {
       
   896 			return '';
       
   897 		}
       
   898 
       
   899 		/*
       
   900 		 * If the intended strategy is 'defer', limit the initial list of eligible
       
   901 		 * strategies, since 'async' can fallback to 'defer', but not vice-versa.
       
   902 		 */
       
   903 		$initial = ( 'defer' === $intended ) ? array( 'defer' ) : null;
       
   904 
       
   905 		$eligible = $this->filter_eligible_strategies( $handle, $initial );
       
   906 
       
   907 		// Return early once we know the eligible strategy is blocking.
       
   908 		if ( empty( $eligible ) ) {
       
   909 			return '';
       
   910 		}
       
   911 
       
   912 		return in_array( 'async', $eligible, true ) ? 'async' : 'defer';
       
   913 	}
       
   914 
       
   915 	/**
       
   916 	 * Filter the list of eligible loading strategies for a script.
       
   917 	 *
       
   918 	 * @since 6.3.0
       
   919 	 *
       
   920 	 * @param string              $handle   The script handle.
       
   921 	 * @param string[]|null       $eligible Optional. The list of strategies to filter. Default null.
       
   922 	 * @param array<string, true> $checked  Optional. An array of already checked script handles, used to avoid recursive loops.
       
   923 	 * @return string[] A list of eligible loading strategies that could be used.
       
   924 	 */
       
   925 	private function filter_eligible_strategies( $handle, $eligible = null, $checked = array() ) {
       
   926 		// If no strategies are being passed, all strategies are eligible.
       
   927 		if ( null === $eligible ) {
       
   928 			$eligible = $this->delayed_strategies;
       
   929 		}
       
   930 
       
   931 		// If this handle was already checked, return early.
       
   932 		if ( isset( $checked[ $handle ] ) ) {
       
   933 			return $eligible;
       
   934 		}
       
   935 
       
   936 		// Mark this handle as checked.
       
   937 		$checked[ $handle ] = true;
       
   938 
       
   939 		// If this handle isn't registered, don't filter anything and return.
       
   940 		if ( ! isset( $this->registered[ $handle ] ) ) {
       
   941 			return $eligible;
       
   942 		}
       
   943 
       
   944 		// If the handle is not enqueued, don't filter anything and return.
       
   945 		if ( ! $this->query( $handle, 'enqueued' ) ) {
       
   946 			return $eligible;
       
   947 		}
       
   948 
       
   949 		$is_alias          = (bool) ! $this->registered[ $handle ]->src;
       
   950 		$intended_strategy = $this->get_data( $handle, 'strategy' );
       
   951 
       
   952 		// For non-alias handles, an empty intended strategy filters all strategies.
       
   953 		if ( ! $is_alias && empty( $intended_strategy ) ) {
       
   954 			return array();
       
   955 		}
       
   956 
       
   957 		// Handles with inline scripts attached in the 'after' position cannot be delayed.
       
   958 		if ( $this->has_inline_script( $handle, 'after' ) ) {
       
   959 			return array();
       
   960 		}
       
   961 
       
   962 		// If the intended strategy is 'defer', filter out 'async'.
       
   963 		if ( 'defer' === $intended_strategy ) {
       
   964 			$eligible = array( 'defer' );
       
   965 		}
       
   966 
       
   967 		$dependents = $this->get_dependents( $handle );
       
   968 
       
   969 		// Recursively filter eligible strategies for dependents.
       
   970 		foreach ( $dependents as $dependent ) {
       
   971 			// Bail early once we know the eligible strategy is blocking.
       
   972 			if ( empty( $eligible ) ) {
       
   973 				return array();
       
   974 			}
       
   975 
       
   976 			$eligible = $this->filter_eligible_strategies( $dependent, $eligible, $checked );
       
   977 		}
       
   978 
       
   979 		return $eligible;
       
   980 	}
       
   981 
       
   982 	/**
       
   983 	 * Gets data for inline scripts registered for a specific handle.
       
   984 	 *
       
   985 	 * @since 6.3.0
       
   986 	 *
       
   987 	 * @param string $handle   Name of the script to get data for. Must be lowercase.
       
   988 	 * @param string $position The position of the inline script.
       
   989 	 * @return bool Whether the handle has an inline script (either before or after).
       
   990 	 */
       
   991 	private function has_inline_script( $handle, $position = null ) {
       
   992 		if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) {
       
   993 			return (bool) $this->get_data( $handle, $position );
       
   994 		}
       
   995 
       
   996 		return (bool) ( $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ) );
       
   997 	}
       
   998 
       
   999 	/**
       
  1000 	 * Resets class properties.
       
  1001 	 *
       
  1002 	 * @since 2.8.0
       
  1003 	 */
       
  1004 	public function reset() {
       
  1005 		$this->do_concat      = false;
       
  1006 		$this->print_code     = '';
       
  1007 		$this->concat         = '';
       
  1008 		$this->concat_version = '';
       
  1009 		$this->print_html     = '';
       
  1010 		$this->ext_version    = '';
       
  1011 		$this->ext_handles    = '';
       
  1012 	}
       
  1013 }