WordPress.org

Make WordPress Core

Ticket #22249: 22249.8.diff

File 22249.8.diff, 31.1 KB (added by mihai2u, 3 years ago)
  • src/wp-includes/class.wp-scripts.php

    diff --git a/src/wp-includes/class.wp-scripts.php b/src/wp-includes/class.wp-scripts.php
    index 42422d3db4..b5bb0228d5 100644
    a b public function init() { 
    149149        }
    150150
    151151        /**
     152         * Get the expanded source URL for a script.
     153         *
     154         * @since 4.9.2
     155         *
     156         * @param  string $handle The script's handle.
     157         * @return string The URL.
     158         */
     159        public function get_script_src( $handle ) {
     160                $obj = $this->registered[ $handle ];
     161                $src = $obj->src;
     162                if ( null === $obj->ver ) {
     163                        $ver = '';
     164                } else {
     165                        $ver = $obj->ver ? $obj->ver : $this->default_version;
     166                }
     167
     168                // Add any extra arguments to the version string.
     169                if ( isset($this->args[ $handle ]) )
     170                        $ver = $ver ? $ver . '&' . $this->args[ $handle ] : $this->args[ $handle ];
     171
     172                if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && 0 === strpos( $src, $this->content_url ) ) ) {
     173                        $src = $this->base_url . $src;
     174                }
     175
     176                if ( ! empty( $ver ) )
     177                        $src = add_query_arg( 'ver', $ver, $src );
     178
     179                /** This filter is documented in wp-includes/class.wp-scripts.php */
     180                return apply_filters( 'script_loader_src', $src, $handle );
     181        }
     182
     183                /**
    152184         * Prints scripts.
    153185         *
    154186         * Prints the scripts passed to it or the print queue. Also prints all necessary dependencies.
    public function do_item( $handle, $group = false ) { 
    330362                if ( ! $src ) {
    331363                        return true;
    332364                }
    333 
    334                 $tag = "{$cond_before}{$before_handle}<script type='text/javascript' src='$src'></script>\n{$after_handle}{$cond_after}";
     365               
     366                $attributes = $this->get_script_attributes_html( $handle );
     367                $tag = "{$cond_before}{$before_handle}<script$attributes></script>\n{$after_handle}{$cond_after}";
    335368
    336369                /**
    337370                 * Filters the HTML script tag of an enqueued script.
    public function print_inline_script( $handle, $position = 'after', $echo = true 
    407440                return $output;
    408441        }
    409442
     443        /**
     444         * Get the HTML element attributes for a script.
     445         *
     446         * Does not include the `src` attribute.
     447         *
     448         * @since 4.9.2
     449         *
     450         * @param string $handle Script registered handle.
     451         * @return array Attribues.
     452         */
     453        public function get_script_attributes( $handle ) {
     454                $default_attributes = array(
     455                        'type' => 'text/javascript',
     456                        'src' => $this->get_script_src( $handle ),
     457                );
     458
     459                if ( isset( $this->registered[ $handle ]->args['attributes'] ) ) {
     460                        $extra_attributes = $this->registered[ $handle ]->args['attributes'];
     461                        $attributes = wp_parse_args( $extra_attributes, $default_attributes );
     462                } else {
     463                        $attributes = $default_attributes;
     464                }
     465
     466                /**
     467                 * Filter the HTML element attributes for a script.
     468                 *
     469                 * @since 4.6.0
     470                 *
     471                 * @param array  $attributes Array of script element attributes.
     472                 * @param string $handle     Script handle.
     473                 */
     474                $attributes = apply_filters( 'script_additional_attributes', $attributes, $handle );
     475                return $attributes;
     476        }
     477
     478
     479        /**
     480         * Get the concatenated HTML element attributes for a script.
     481         *
     482         * @since 4.9.2
     483         *
     484         * @param  string $handle The script handle.
     485         * @return string Concatenated attributes.
     486         */
     487        public function get_script_attributes_html( $handle ) {
     488                $attributes = $this->get_script_attributes( $handle );
     489                $html = '';
     490                foreach ( $attributes as $attribute => $attribute_value ) {
     491                        if ( 'src' === $attribute ) {
     492                                $escaped_attribute_value = esc_url_raw( $attribute_value );
     493                        } else {
     494                                $escaped_attribute_value = esc_attr( $attribute_value );
     495                        }
     496                        $html .=  sprintf( " %s='%s'", esc_attr_name( $attribute ), $escaped_attribute_value );
     497                }
     498                return $html;
     499        }
     500
    410501        /**
    411502         * Localizes a script, only if the script has already been added.
    412503         *
  • new file src/wp-includes/class.wp-scripts.php.orig

    diff --git a/src/wp-includes/class.wp-scripts.php.orig b/src/wp-includes/class.wp-scripts.php.orig
    new file mode 100644
    index 0000000000..81634ece41
    - +  
     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 * @package WordPress
     15 * @uses WP_Dependencies
     16 * @since 2.1.0
     17 */
     18class 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         * @access public
     26         * @var string
     27         */
     28        public $base_url;
     29
     30        /**
     31         * URL of the content directory.
     32         *
     33         * @since 2.8.0
     34         * @access public
     35         * @var string
     36         */
     37        public $content_url;
     38
     39        /**
     40         * Default version string for stylesheets.
     41         *
     42         * @since 2.6.0
     43         * @access public
     44         * @var string
     45         */
     46        public $default_version;
     47
     48        /**
     49         * Holds handles of scripts which are enqueued in footer.
     50         *
     51         * @since 2.8.0
     52         * @access public
     53         * @var array
     54         */
     55        public $in_footer = array();
     56
     57        /**
     58         * Holds a list of script handles which will be concatenated.
     59         *
     60         * @since 2.8.0
     61         * @access public
     62         * @var string
     63         */
     64        public $concat = '';
     65
     66        /**
     67         * Holds a string which contains script handles and their version.
     68         *
     69         * @since 2.8.0
     70         * @deprecated 3.4.0
     71         * @access public
     72         * @var string
     73         */
     74        public $concat_version = '';
     75
     76        /**
     77         * Whether to perform concatenation.
     78         *
     79         * @since 2.8.0
     80         * @access public
     81         * @var bool
     82         */
     83        public $do_concat = false;
     84
     85        /**
     86         * Holds HTML markup of scripts and additional data if concatenation
     87         * is enabled.
     88         *
     89         * @since 2.8.0
     90         * @access public
     91         * @var string
     92         */
     93        public $print_html = '';
     94
     95        /**
     96         * Holds inline code if concatenation is enabled.
     97         *
     98         * @since 2.8.0
     99         * @access public
     100         * @var string
     101         */
     102        public $print_code = '';
     103
     104        /**
     105         * Holds a list of script handles which are not in the default directory
     106         * if concatenation is enabled.
     107         *
     108         * Unused in core.
     109         *
     110         * @since 2.8.0
     111         * @access public
     112         * @var string
     113         */
     114        public $ext_handles = '';
     115
     116        /**
     117         * Holds a string which contains handles and versions of scripts which
     118         * are not in the default directory if concatenation is enabled.
     119         *
     120         * Unused in core.
     121         *
     122         * @since 2.8.0
     123         * @access public
     124         * @var string
     125         */
     126        public $ext_version = '';
     127
     128        /**
     129         * List of default directories.
     130         *
     131         * @since 2.8.0
     132         * @access public
     133         * @var array
     134         */
     135        public $default_dirs;
     136
     137        /**
     138         * Constructor.
     139         *
     140         * @since 2.6.0
     141         * @access public
     142         */
     143        public function __construct() {
     144                $this->init();
     145                add_action( 'init', array( $this, 'init' ), 0 );
     146        }
     147
     148        /**
     149         * Initialize the class.
     150         *
     151         * @since 3.4.0
     152         * @access public
     153         */
     154        public function init() {
     155                /**
     156                 * Fires when the WP_Scripts instance is initialized.
     157                 *
     158                 * @since 2.6.0
     159                 *
     160                 * @param WP_Scripts &$this WP_Scripts instance, passed by reference.
     161                 */
     162                do_action_ref_array( 'wp_default_scripts', array(&$this) );
     163        }
     164
     165        /**
     166         * Prints scripts.
     167         *
     168         * Prints the scripts passed to it or the print queue. Also prints all necessary dependencies.
     169         *
     170         * @since 2.1.0
     171         * @since 2.8.0 Added the `$group` parameter.
     172         * @access public
     173         *
     174         * @param mixed $handles Optional. Scripts to be printed. (void) prints queue, (string) prints
     175         *                       that script, (array of strings) prints those scripts. Default false.
     176         * @param int   $group   Optional. If scripts were queued in groups prints this group number.
     177         *                       Default false.
     178         * @return array Scripts that have been printed.
     179         */
     180        public function print_scripts( $handles = false, $group = false ) {
     181                return $this->do_items( $handles, $group );
     182        }
     183
     184        /**
     185         * Prints extra scripts of a registered script.
     186         *
     187         * @since 2.1.0
     188         * @since 2.8.0 Added the `$echo` parameter.
     189         * @deprecated 3.3.0
     190         * @access public
     191         *
     192         * @see print_extra_script()
     193         *
     194         * @param string $handle The script's registered handle.
     195         * @param bool   $echo   Optional. Whether to echo the extra script instead of just returning it.
     196         *                       Default true.
     197         * @return bool|string|void Void if no data exists, extra scripts if `$echo` is true, true otherwise.
     198         */
     199        public function print_scripts_l10n( $handle, $echo = true ) {
     200                _deprecated_function( __FUNCTION__, '3.3.0', 'print_extra_script()' );
     201                return $this->print_extra_script( $handle, $echo );
     202        }
     203
     204        /**
     205         * Prints extra scripts of a registered script.
     206         *
     207         * @since 3.3.0
     208         * @access public
     209         *
     210         * @param string $handle The script's registered handle.
     211         * @param bool   $echo   Optional. Whether to echo the extra script instead of just returning it.
     212         *                       Default true.
     213         * @return bool|string|void Void if no data exists, extra scripts if `$echo` is true, true otherwise.
     214         */
     215        public function print_extra_script( $handle, $echo = true ) {
     216                if ( !$output = $this->get_data( $handle, 'data' ) )
     217                        return;
     218
     219                if ( !$echo )
     220                        return $output;
     221
     222                echo "<script type='text/javascript'>\n"; // CDATA and type='text/javascript' is not needed for HTML 5
     223                echo "/* <![CDATA[ */\n";
     224                echo "$output\n";
     225                echo "/* ]]> */\n";
     226                echo "</script>\n";
     227
     228                return true;
     229        }
     230
     231        /**
     232         * Processes a script dependency.
     233         *
     234         * @since 2.6.0
     235         * @since 2.8.0 Added the `$group` parameter.
     236         * @access public
     237         *
     238         * @see WP_Dependencies::do_item()
     239         *
     240         * @param string $handle    The script's registered handle.
     241         * @param int|false $group  Optional. Group level: (int) level, (false) no groups. Default false.
     242         * @return bool True on success, false on failure.
     243         */
     244        public function do_item( $handle, $group = false ) {
     245                if ( !parent::do_item($handle) )
     246                        return false;
     247
     248                if ( 0 === $group && $this->groups[$handle] > 0 ) {
     249                        $this->in_footer[] = $handle;
     250                        return false;
     251                }
     252
     253                if ( false === $group && in_array($handle, $this->in_footer, true) )
     254                        $this->in_footer = array_diff( $this->in_footer, (array) $handle );
     255
     256                $obj = $this->registered[$handle];
     257
     258                if ( null === $obj->ver ) {
     259                        $ver = '';
     260                } else {
     261                        $ver = $obj->ver ? $obj->ver : $this->default_version;
     262                }
     263
     264                if ( isset($this->args[$handle]) )
     265                        $ver = $ver ? $ver . '&amp;' . $this->args[$handle] : $this->args[$handle];
     266
     267                $src = $obj->src;
     268                $cond_before = $cond_after = '';
     269                $conditional = isset( $obj->extra['conditional'] ) ? $obj->extra['conditional'] : '';
     270
     271                if ( $conditional ) {
     272                        $cond_before = "<!--[if {$conditional}]>\n";
     273                        $cond_after = "<![endif]-->\n";
     274                }
     275
     276                $before_handle = $this->print_inline_script( $handle, 'before', false );
     277                $after_handle = $this->print_inline_script( $handle, 'after', false );
     278
     279                if ( $before_handle ) {
     280                        $before_handle = sprintf( "<script type='text/javascript'>\n%s\n</script>\n", $before_handle );
     281                }
     282
     283                if ( $after_handle ) {
     284                        $after_handle = sprintf( "<script type='text/javascript'>\n%s\n</script>\n", $after_handle );
     285                }
     286
     287                if ( $this->do_concat ) {
     288                        /**
     289                         * Filters the script loader source.
     290                         *
     291                         * @since 2.2.0
     292                         *
     293                         * @param string $src    Script loader source path.
     294                         * @param string $handle Script handle.
     295                         */
     296                        $srce = apply_filters( 'script_loader_src', $src, $handle );
     297
     298                        if ( $this->in_default_dir( $srce ) && ( $before_handle || $after_handle ) ) {
     299                                $this->do_concat = false;
     300
     301                                // Have to print the so-far concatenated scripts right away to maintain the right order.
     302                                _print_scripts();
     303                                $this->reset();
     304                        } elseif ( $this->in_default_dir( $srce ) && ! $conditional ) {
     305                                $this->print_code .= $this->print_extra_script( $handle, false );
     306                                $this->concat .= "$handle,";
     307                                $this->concat_version .= "$handle$ver";
     308                                return true;
     309                        } else {
     310                                $this->ext_handles .= "$handle,";
     311                                $this->ext_version .= "$handle$ver";
     312                        }
     313                }
     314
     315                $has_conditional_data = $conditional && $this->get_data( $handle, 'data' );
     316
     317                if ( $has_conditional_data ) {
     318                        echo $cond_before;
     319                }
     320
     321                $this->print_extra_script( $handle );
     322
     323                if ( $has_conditional_data ) {
     324                        echo $cond_after;
     325                }
     326
     327                // A single item may alias a set of items, by having dependencies, but no source.
     328                if ( ! $obj->src ) {
     329                        return true;
     330                }
     331
     332                if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && 0 === strpos( $src, $this->content_url ) ) ) {
     333                        $src = $this->base_url . $src;
     334                }
     335
     336                if ( ! empty( $ver ) )
     337                        $src = add_query_arg( 'ver', $ver, $src );
     338
     339                /** This filter is documented in wp-includes/class.wp-scripts.php */
     340                $src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) );
     341
     342                if ( ! $src )
     343                        return true;
     344
     345                $tag = "{$cond_before}{$before_handle}<script type='text/javascript' src='$src'></script>\n{$after_handle}{$cond_after}";
     346
     347                /**
     348                 * Filters the HTML script tag of an enqueued script.
     349                 *
     350                 * @since 4.1.0
     351                 *
     352                 * @param string $tag    The `<script>` tag for the enqueued script.
     353                 * @param string $handle The script's registered handle.
     354                 * @param string $src    The script's source URL.
     355                 */
     356                $tag = apply_filters( 'script_loader_tag', $tag, $handle, $src );
     357
     358                if ( $this->do_concat ) {
     359                        $this->print_html .= $tag;
     360                } else {
     361                        echo $tag;
     362                }
     363
     364                return true;
     365        }
     366
     367        /**
     368         * Adds extra code to a registered script.
     369         *
     370         * @since 4.5.0
     371         * @access public
     372         *
     373         * @param string $handle   Name of the script to add the inline script to. Must be lowercase.
     374         * @param string $data     String containing the javascript to be added.
     375         * @param string $position Optional. Whether to add the inline script before the handle
     376         *                         or after. Default 'after'.
     377         * @return bool True on success, false on failure.
     378         */
     379        public function add_inline_script( $handle, $data, $position = 'after' ) {
     380                if ( ! $data ) {
     381                        return false;
     382                }
     383
     384                if ( 'after' !== $position ) {
     385                        $position = 'before';
     386                }
     387
     388                $script   = (array) $this->get_data( $handle, $position );
     389                $script[] = $data;
     390
     391                return $this->add_data( $handle, $position, $script );
     392        }
     393
     394        /**
     395         * Prints inline scripts registered for a specific handle.
     396         *
     397         * @since 4.5.0
     398         * @access public
     399         *
     400         * @param string $handle   Name of the script to add the inline script to. Must be lowercase.
     401         * @param string $position Optional. Whether to add the inline script before the handle
     402         *                         or after. Default 'after'.
     403         * @param bool $echo       Optional. Whether to echo the script instead of just returning it.
     404         *                         Default true.
     405         * @return string|false Script on success, false otherwise.
     406         */
     407        public function print_inline_script( $handle, $position = 'after', $echo = true ) {
     408                $output = $this->get_data( $handle, $position );
     409
     410                if ( empty( $output ) ) {
     411                        return false;
     412                }
     413
     414                $output = trim( implode( "\n", $output ), "\n" );
     415
     416                if ( $echo ) {
     417                        printf( "<script type='text/javascript'>\n%s\n</script>\n", $output );
     418                }
     419
     420                return $output;
     421        }
     422
     423        /**
     424         * Localizes a script, only if the script has already been added.
     425         *
     426         * @since 2.1.0
     427         * @access public
     428         *
     429         * @param string $handle
     430         * @param string $object_name
     431         * @param array $l10n
     432         * @return bool
     433         */
     434        public function localize( $handle, $object_name, $l10n ) {
     435                if ( $handle === 'jquery' )
     436                        $handle = 'jquery-core';
     437
     438                if ( is_array($l10n) && isset($l10n['l10n_print_after']) ) { // back compat, preserve the code in 'l10n_print_after' if present
     439                        $after = $l10n['l10n_print_after'];
     440                        unset($l10n['l10n_print_after']);
     441                }
     442
     443                foreach ( (array) $l10n as $key => $value ) {
     444                        if ( !is_scalar($value) )
     445                                continue;
     446
     447                        $l10n[$key] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8');
     448                }
     449
     450                $script = "var $object_name = " . wp_json_encode( $l10n ) . ';';
     451
     452                if ( !empty($after) )
     453                        $script .= "\n$after;";
     454
     455                $data = $this->get_data( $handle, 'data' );
     456
     457                if ( !empty( $data ) )
     458                        $script = "$data\n$script";
     459
     460                return $this->add_data( $handle, 'data', $script );
     461        }
     462
     463        /**
     464         * Sets handle group.
     465         *
     466         * @since 2.8.0
     467         * @access public
     468         *
     469         * @see WP_Dependencies::set_group()
     470         *
     471         * @param string    $handle    Name of the item. Should be unique.
     472         * @param bool      $recursion Internal flag that calling function was called recursively.
     473         * @param int|false $group     Optional. Group level: (int) level, (false) no groups. Default false.
     474         * @return bool Not already in the group or a lower group
     475         */
     476        public function set_group( $handle, $recursion, $group = false ) {
     477                if ( isset( $this->registered[$handle]->args ) && $this->registered[$handle]->args === 1 )
     478                        $grp = 1;
     479                else
     480                        $grp = (int) $this->get_data( $handle, 'group' );
     481
     482                if ( false !== $group && $grp > $group )
     483                        $grp = $group;
     484
     485                return parent::set_group( $handle, $recursion, $grp );
     486        }
     487
     488        /**
     489         * Determines script dependencies.
     490     *
     491         * @since 2.1.0
     492         * @access public
     493         *
     494         * @see WP_Dependencies::all_deps()
     495         *
     496         * @param mixed     $handles   Item handle and argument (string) or item handles and arguments (array of strings).
     497         * @param bool      $recursion Internal flag that function is calling itself.
     498         * @param int|false $group     Optional. Group level: (int) level, (false) no groups. Default false.
     499         * @return bool True on success, false on failure.
     500         */
     501        public function all_deps( $handles, $recursion = false, $group = false ) {
     502                $r = parent::all_deps( $handles, $recursion, $group );
     503                if ( ! $recursion ) {
     504                        /**
     505                         * Filters the list of script dependencies left to print.
     506                         *
     507                         * @since 2.3.0
     508                         *
     509                         * @param array $to_do An array of script dependencies.
     510                         */
     511                        $this->to_do = apply_filters( 'print_scripts_array', $this->to_do );
     512                }
     513                return $r;
     514        }
     515
     516        /**
     517         * Processes items and dependencies for the head group.
     518         *
     519         * @since 2.8.0
     520         * @access public
     521         *
     522         * @see WP_Dependencies::do_items()
     523         *
     524         * @return array Handles of items that have been processed.
     525         */
     526        public function do_head_items() {
     527                $this->do_items(false, 0);
     528                return $this->done;
     529        }
     530
     531        /**
     532         * Processes items and dependencies for the footer group.
     533         *
     534         * @since 2.8.0
     535         * @access public
     536         *
     537         * @see WP_Dependencies::do_items()
     538         *
     539         * @return array Handles of items that have been processed.
     540         */
     541        public function do_footer_items() {
     542                $this->do_items(false, 1);
     543                return $this->done;
     544        }
     545
     546        /**
     547         * Whether a handle's source is in a default directory.
     548         *
     549         * @since 2.8.0
     550         * @access public
     551         *
     552         * @param string $src The source of the enqueued script.
     553         * @return bool True if found, false if not.
     554         */
     555        public function in_default_dir( $src ) {
     556                if ( ! $this->default_dirs ) {
     557                        return true;
     558                }
     559
     560                if ( 0 === strpos( $src, '/' . WPINC . '/js/l10n' ) ) {
     561                        return false;
     562                }
     563
     564                foreach ( (array) $this->default_dirs as $test ) {
     565                        if ( 0 === strpos( $src, $test ) ) {
     566                                return true;
     567                        }
     568                }
     569                return false;
     570        }
     571
     572        /**
     573         * Resets class properties.
     574         *
     575         * @since 2.8.0
     576         * @access public
     577         */
     578        public function reset() {
     579                $this->do_concat = false;
     580                $this->print_code = '';
     581                $this->concat = '';
     582                $this->concat_version = '';
     583                $this->print_html = '';
     584                $this->ext_version = '';
     585                $this->ext_handles = '';
     586        }
     587}
  • src/wp-includes/class.wp-styles.php

    diff --git a/src/wp-includes/class.wp-styles.php b/src/wp-includes/class.wp-styles.php
    index fed69962bc..d21f6a03df 100644
    a b public function __construct() { 
    116116                do_action_ref_array( 'wp_default_styles', array( &$this ) );
    117117        }
    118118
     119        /**
     120         * Get the HTML element attributes for a stylesheet.
     121         *
     122         * Does not include the `href` attribute.
     123         *
     124         * @since 4.9.2
     125         *
     126         * @param  string $handle The stylesheet handle.
     127         * @return array Attribues.
     128         */
     129        public function get_style_additional_attributes( $handle ) {
     130                $obj = $this->registered[ $handle ];
     131                if ( null === $obj->ver ) {
     132                        $ver = '';
     133                } else {
     134                        $ver = $obj->ver ? $obj->ver : $this->default_version;
     135                }
     136
     137                $attributes = array(
     138                        'rel' => isset($obj->extra['alt']) && $obj->extra['alt'] ? 'alternate stylesheet' : 'stylesheet',
     139                        'id' => $handle . '-css',
     140                        'title' => isset($obj->extra['title']) ? $obj->extra['title'] : '',
     141                        'type' => 'text/css',
     142                        'media' => isset( $obj->args ) ? $obj->args : 'all'
     143                );
     144                return $attributes;
     145        }
     146
    119147        /**
    120148         * Processes a style dependency.
    121149         *
    public function do_item( $handle ) { 
    192220                 * @param string $href   The stylesheet's source URL.
    193221                 * @param string $media  The stylesheet's media attribute.
    194222                 */
    195                 $tag = apply_filters( 'style_loader_tag', "<link rel='$rel' id='$handle-css' $title href='$href' type='text/css' media='$media' />\n", $handle, $href, $media );
     223                $tag = apply_filters( 'style_loader_tag', "<link href='$href'" . $this->get_style_attribute_html( $handle ) . " />\n", $handle, $href, $media );
    196224                if ( 'rtl' === $this->text_direction && isset( $obj->extra['rtl'] ) && $obj->extra['rtl'] ) {
    197225                        if ( is_bool( $obj->extra['rtl'] ) || 'replace' === $obj->extra['rtl'] ) {
    198226                                $suffix   = isset( $obj->extra['suffix'] ) ? $obj->extra['suffix'] : '';
    public function do_item( $handle ) { 
    234262                return true;
    235263        }
    236264
     265        /**
     266         * Get the concatenated HTML element attributes for a stylesheet
     267         *
     268         * @since 4.9.2
     269         *
     270         * @param  string $handle The stylesheet handle.
     271         * @return string Concatenated attributes.
     272         */
     273        public function get_style_attribute_html( $handle ) {
     274                $attributes = $this->get_style_additional_attributes( $handle );
     275                $html = '';
     276                foreach ( $attributes as $attribute => $attribute_value ) {
     277                        $html .=  sprintf( " %s='%s'", esc_attr_name( $attribute ), esc_attr( $attribute_value ) );
     278                }
     279                return $html;
     280        }
     281
    237282        /**
    238283         * Adds extra CSS styles to a registered stylesheet.
    239284         *
  • src/wp-includes/formatting.php

    diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php
    index ce2c851bc5..9bc0c25562 100644
    a b function esc_attr( $text ) { 
    42554255        return apply_filters( 'attribute_escape', $safe_text, $text );
    42564256}
    42574257
     4258/**
     4259 * Escaping for HTML attributes.
     4260 *
     4261 * @since 2.8.0
     4262 *
     4263 * @param string $text
     4264 * @return string
     4265 */
     4266function esc_attr_name( $text ) {
     4267        $safe_text = wp_check_invalid_utf8( $text );
     4268        $safe_text = preg_replace( '/[\t\n\f \/>"\'=]+/', '_', $safe_text );
     4269        /**
     4270         * Filters a string cleaned and escaped for output as an HTML attribute name.
     4271         *
     4272         * Text passed to esc_attr_name() is stripped of invalid or special characters
     4273         * before output.
     4274         *
     4275         * @since joe_bopper patch
     4276         *
     4277         * @param string $safe_text The text after it has been escaped.
     4278         * @param string $text      The text prior to being escaped.
     4279         */
     4280        $safe_text = apply_filters( 'attribute_name_escape', $safe_text, $text );
     4281
     4282        //Notably, an attribute name cannot be an empty string.
     4283        return $safe_text ? $safe_text : 'empty_string_supplied_as_attribute_name';
     4284}
     4285
    42584286/**
    42594287 * Escaping for textarea values.
    42604288 *
  • src/wp-includes/functions.wp-scripts.php

    diff --git a/src/wp-includes/functions.wp-scripts.php b/src/wp-includes/functions.wp-scripts.php
    index d9fab991e7..87dd58a7b6 100644
    a b function wp_deregister_script( $handle ) { 
    271271 * @see WP_Dependencies::enqueue()
    272272 *
    273273 * @since 2.1.0
     274 * @since 4.9.2 Introduced the `$attributes` parameter.
    274275 *
    275276 * @param string           $handle    Name of the script. Should be unique.
    276277 * @param string           $src       Full URL of the script, or path of the script relative to the WordPress root directory.
    277  *                                    Default empty.
    278278 * @param array            $deps      Optional. An array of registered script handles this script depends on. Default empty array.
    279279 * @param string|bool|null $ver       Optional. String specifying script version number, if it has one, which is added to the URL
    280280 *                                    as a query string for cache busting purposes. If version is set to false, a version
    function wp_deregister_script( $handle ) { 
    282282 *                                    If set to null, no version is added.
    283283 * @param bool             $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
    284284 *                                    Default 'false'.
     285 * @param array            $args {
     286 *     Optional script arguments.
     287 *
     288 *     @type array $attributes Array of script element attributes.
     289 *                             Default: array( 'type' => 'text/javascript' )
     290 * }
    285291 */
    286 function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $in_footer = false ) {
     292function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $in_footer = false, $attributes = array() ) {
    287293        $wp_scripts = wp_scripts();
    288294
    289295        _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ );
    290296
     297        $attributes = wp_parse_args( $attributes, array( 'type' => 'text/javascript' ) );
     298
    291299        if ( $src || $in_footer ) {
    292300                $_handle = explode( '?', $handle );
    293301
    294302                if ( $src ) {
    295                         $wp_scripts->add( $_handle[0], $src, $deps, $ver );
     303                        $wp_scripts->add( $_handle[0], $src, $deps, $ver, array( 'attributes' => $attributes ) );
    296304                }
    297305
    298306                if ( $in_footer ) {
  • new file tests/phpunit/tests/dependencies/getScriptAttributes.php

    diff --git a/tests/phpunit/tests/dependencies/getScriptAttributes.php b/tests/phpunit/tests/dependencies/getScriptAttributes.php
    new file mode 100644
    index 0000000000..24f84b7cef
    - +  
     1<?php
     2/**
     3 * @group dependencies
     4 * @group scripts
     5 */
     6class Tests_Dependencies_GetScriptAttributes extends WP_UnitTestCase {
     7        var $old_wp_scripts;
     8
     9        function setUp() {
     10                parent::setUp();
     11                $this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null;
     12                remove_action( 'wp_default_scripts', 'wp_default_scripts' );
     13                $GLOBALS['wp_scripts'] = new WP_Scripts();
     14                $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
     15        }
     16
     17        function tearDown() {
     18                $GLOBALS['wp_scripts'] = $this->old_wp_scripts;
     19                add_action( 'wp_default_scripts', 'wp_default_scripts' );
     20                parent::tearDown();
     21        }
     22
     23        /**
     24         * @ticket 22249
     25         */
     26        function test_get_script_attributes_with_defaults() {
     27                global $wp_scripts;
     28                wp_register_script( 'cool-script', '/cool-script.js', array(), '1' );
     29
     30                $expected_attributes = array(
     31                        'type' => 'text/javascript',
     32                        'src' => '/cool-script.js?ver=1',
     33                );
     34                $actual_attributes = $wp_scripts->get_script_attributes( 'cool-script' );
     35                $this->assertEquals( $expected_attributes, $actual_attributes );
     36        }
     37
     38        /**
     39         * @ticket 22249
     40         */
     41        function test_get_script_attributes_with_arbitrary_attributes_added() {
     42                global $wp_scripts;
     43                wp_register_script( 'cool-script', '/cool-script.js', array(), '1', false, array( 'async' => 'async' ) );
     44
     45                $expected_attributes = array(
     46                        'type' => 'text/javascript',
     47                        'src' => '/cool-script.js?ver=1',
     48                        'async' => 'async',
     49                );
     50                $actual_attributes = $wp_scripts->get_script_attributes( 'cool-script' );
     51                $this->assertEquals( $expected_attributes, $actual_attributes );
     52        }
     53
     54}
  • new file tests/phpunit/tests/dependencies/getScriptAttributesHtml.php

    diff --git a/tests/phpunit/tests/dependencies/getScriptAttributesHtml.php b/tests/phpunit/tests/dependencies/getScriptAttributesHtml.php
    new file mode 100644
    index 0000000000..5a2a766fa9
    - +  
     1<?php
     2/**
     3 * @group dependencies
     4 * @group scripts
     5 */
     6class Tests_Dependencies_GetScriptAttributesHtml extends WP_UnitTestCase {
     7        var $old_wp_scripts;
     8
     9        function setUp() {
     10                parent::setUp();
     11                $this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null;
     12                remove_action( 'wp_default_scripts', 'wp_default_scripts' );
     13                $GLOBALS['wp_scripts'] = new WP_Scripts();
     14                $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
     15        }
     16
     17        function tearDown() {
     18                $GLOBALS['wp_scripts'] = $this->old_wp_scripts;
     19                add_action( 'wp_default_scripts', 'wp_default_scripts' );
     20                parent::tearDown();
     21        }
     22
     23        /**
     24         * @ticket 22249
     25         */
     26        function test_get_script_attributes_html_with_defaults() {
     27                global $wp_scripts;
     28                wp_register_script( 'cool-script', '/cool-script.js', array(), '1' );
     29
     30                $actual = $wp_scripts->get_script_attributes_html( 'cool-script' );
     31                $expected = " type='text/javascript' src='/cool-script.js?ver=1'";
     32                $this->assertEquals( $expected, $actual );
     33        }
     34
     35        /**
     36         * @ticket 22249
     37         */
     38        function test_get_script_attributes_html_with_arbitrary_attributes_added() {
     39                global $wp_scripts;
     40                wp_register_script( 'cool-script', '/cool-script.js', array(), '1', false, array( 'async' => 'async' ) );
     41
     42                $actual = $wp_scripts->get_script_attributes_html( 'cool-script' );
     43                $expected = " type='text/javascript' src='/cool-script.js?ver=1' async='async'";
     44                $this->assertEquals( $expected, $actual );
     45        }
     46
     47}
  • new file tests/phpunit/tests/dependencies/getScriptSrc.php

    diff --git a/tests/phpunit/tests/dependencies/getScriptSrc.php b/tests/phpunit/tests/dependencies/getScriptSrc.php
    new file mode 100644
    index 0000000000..64d7007963
    - +  
     1<?php
     2/**
     3 * @group dependencies
     4 * @group scripts
     5 */
     6class Tests_Dependencies_GetScriptSrc extends WP_UnitTestCase {
     7        var $old_wp_scripts;
     8
     9        function setUp() {
     10                parent::setUp();
     11                $this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null;
     12                remove_action( 'wp_default_scripts', 'wp_default_scripts' );
     13                $GLOBALS['wp_scripts'] = new WP_Scripts();
     14                $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
     15        }
     16
     17        function tearDown() {
     18                $GLOBALS['wp_scripts'] = $this->old_wp_scripts;
     19                add_action( 'wp_default_scripts', 'wp_default_scripts' );
     20                parent::tearDown();
     21        }
     22
     23        /**
     24         * @ticket 22249
     25         */
     26        function test_get_script_src_for_script_with_relative_url() {
     27                global $wp_scripts;
     28                wp_register_script( 'cool-script', '/cool-script.js', array(), '1' );
     29
     30                $actual = $wp_scripts->get_script_src( 'cool-script' );
     31                $this->assertEquals( '/cool-script.js?ver=1', $actual );
     32        }
     33
     34        /**
     35         * @ticket 22249
     36         */
     37        function test_get_script_src_for_script_with_full_url() {
     38                global $wp_scripts;
     39                wp_register_script( 'd3', 'https://d3js.org/d3.v3.min.js', array(), '1' );
     40
     41                $actual = $wp_scripts->get_script_src( 'd3' );
     42                $this->assertEquals( 'https://d3js.org/d3.v3.min.js?ver=1', $actual );
     43        }
     44
     45}
  • tests/phpunit/tests/dependencies/scripts.php

    diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php
    index 9bbe638b89..d645ba8756 100644
    a b public function test_wp_enqueue_code_editor_when_simple_array_will_be_passed() { 
    10901090                        array_keys( $wp_enqueue_code_editor['htmlhint'] )
    10911091                );
    10921092        }
     1093
     1094        /**
     1095         * @ticket 22249
     1096         */
     1097        function test_wp_register_script_should_allow_arbitrary_element_attributes() {
     1098                wp_register_script( 'cool-script', '/cool-script.js', array(), '1', false, array( 'async' => 'async' ) );
     1099
     1100                wp_enqueue_script( 'cool-script' );
     1101
     1102                $header = get_echo( 'wp_print_head_scripts' );
     1103                $expected_header  = "<script type='text/javascript' src='/cool-script.js?ver=1' async='async'></script>\n";
     1104
     1105                $this->assertEquals( $expected_header, $header );
     1106        }
     1107
     1108        /**
     1109         * @ticket 22249
     1110         */
     1111        function test_wp_enqueue_script_should_allow_arbitrary_element_attributes() {
     1112                wp_enqueue_script( 'cool-script', '/cool-script.js', array(), '1', false, array( 'async' => 'async' ) );
     1113
     1114                $header = get_echo( 'wp_print_head_scripts' );
     1115                $expected_header  = "<script type='text/javascript' src='/cool-script.js?ver=1' async='async'></script>\n";
     1116
     1117                $this->assertEquals( $expected_header, $header );
     1118        }
    10931119}