@@ -3464,3 +3464,153 @@ function wp_remove_surrounding_empty_script_tags( $contents ) {
34643464 );
34653465 }
34663466}
3467+
3468+ /**
3469+ * Return the corresponding JavaScript `dataset` name for an attribute
3470+ * if it represents a custom data attribute, or `null` if not.
3471+ *
3472+ * Custom data attributes appear in an element's `dataset` property in a
3473+ * browser, but there's a specific way the names are translated from HTML
3474+ * into JavaScript. This function indicates how the name would appear in
3475+ * JavaScript if a browser would recognize it as a custom data attribute.
3476+ *
3477+ * Example:
3478+ *
3479+ * // Dash-letter pairs turn into capital letters.
3480+ * 'postId' === wp_js_dataset_name( 'data-post-id' );
3481+ * 'Before' === wp_js_dataset_name( 'data--before' );
3482+ * '-One--Two---' === wp_js_dataset_name( 'data---one---two---' );
3483+ *
3484+ * // Not every attribute name will be interpreted as a custom data attribute.
3485+ * null === wp_js_dataset_name( 'post-id' );
3486+ * null === wp_js_dataset_name( 'data' );
3487+ *
3488+ * // Some very surprising names will; for example, a property whose name is the empty string.
3489+ * '' === wp_js_dataset_name( 'data-' );
3490+ * 0 === strlen( wp_js_dataset_name( 'data-' ) );
3491+ *
3492+ * @since 6.9.0
3493+ *
3494+ * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3495+ * @see wp_html_custom_data_attribute_name()
3496+ *
3497+ * @param string $html_attribute_name Raw attribute name as found in the source HTML.
3498+ * @return string|null Transformed `dataset` name, if interpretable as a custom data attribute, else `null`.
3499+ */
3500+ function wp_js_dataset_name ( string $ html_attribute_name ): ?string {
3501+ if ( 0 !== substr_compare ( $ html_attribute_name , 'data- ' , 0 , 5 , true ) ) {
3502+ return null ;
3503+ }
3504+
3505+ $ end = strlen ( $ html_attribute_name );
3506+
3507+ /*
3508+ * If it contains characters which would end the attribute name parsing then
3509+ * something else is wrong and this contains more than just an attribute name.
3510+ */
3511+ if ( ( $ end - 5 ) !== strcspn ( $ html_attribute_name , "=/> \t\f\r\n" , 5 ) ) {
3512+ return null ;
3513+ }
3514+
3515+ /*
3516+ * > For each name in list, for each U+002D HYPHEN-MINUS character (-)
3517+ * > in the name that is followed by an ASCII lower alpha, remove the
3518+ * > U+002D HYPHEN-MINUS character (-) and replace the character that
3519+ * > followed it by the same character converted to ASCII uppercase.
3520+ *
3521+ * @link https://html.spec.whatwg.org/#concept-domstringmap-pairs
3522+ */
3523+ $ custom_name = '' ;
3524+ $ at = 5 ;
3525+ $ was_at = $ at ;
3526+
3527+ while ( $ at < $ end ) {
3528+ $ next_dash_at = strpos ( $ html_attribute_name , '- ' , $ at );
3529+ if ( false === $ next_dash_at || $ next_dash_at === $ end - 1 ) {
3530+ break ;
3531+ }
3532+
3533+ // Transform `-a` to `A`, for example.
3534+ $ c = $ html_attribute_name [ $ next_dash_at + 1 ];
3535+ if ( ( $ c >= 'A ' && $ c <= 'Z ' ) || ( $ c >= 'a ' && $ c <= 'z ' ) ) {
3536+ $ prefix = substr ( $ html_attribute_name , $ was_at , $ next_dash_at - $ was_at );
3537+ $ custom_name .= strtolower ( $ prefix );
3538+ $ custom_name .= strtoupper ( $ c );
3539+ $ at = $ next_dash_at + 2 ;
3540+ $ was_at = $ at ;
3541+ continue ;
3542+ }
3543+
3544+ $ at = $ next_dash_at + 1 ;
3545+ }
3546+
3547+ // If nothing has been added it means there are no dash-letter pairs; return the name as-is.
3548+ return '' === $ custom_name
3549+ ? strtolower ( substr ( $ html_attribute_name , 5 ) )
3550+ : ( $ custom_name . strtolower ( substr ( $ html_attribute_name , $ was_at ) ) );
3551+ }
3552+
3553+ /**
3554+ * Returns a corresponding HTML attribute name for the given name,
3555+ * if that name were found in a JS element’s `dataset` property.
3556+ *
3557+ * Example:
3558+ *
3559+ * 'data-post-id' === wp_html_custom_data_attribute_name( 'postId' );
3560+ * 'data--before' === wp_html_custom_data_attribute_name( 'Before' );
3561+ * 'data---one---two---' === wp_html_custom_data_attribute_name( '-One--Two---' );
3562+ *
3563+ * // Not every attribute name will be interpreted as a custom data attribute.
3564+ * null === wp_html_custom_data_attribute_name( '/not-an-attribute/' );
3565+ * null === wp_html_custom_data_attribute_name( 'no spaces' );
3566+ *
3567+ * // Some very surprising names will; for example, a property whose name is the empty string.
3568+ * 'data-' === wp_html_custom_data_attribute_name( '' );
3569+ *
3570+ * @since 6.9.0
3571+ *
3572+ * @see https://html.spec.whatwg.org/#concept-domstringmap-pairs
3573+ * @see wp_js_dataset_name()
3574+ *
3575+ * @param string $js_dataset_name Name of JS `dataset` property to transform.
3576+ * @return string|null Corresponding name of an HTML custom data attribute for the given dataset name,
3577+ * if possible to represent in HTML, otherwise `null`.
3578+ */
3579+ function wp_html_custom_data_attribute_name ( string $ js_dataset_name ): ?string {
3580+ $ end = strlen ( $ js_dataset_name );
3581+ if ( 0 === $ end ) {
3582+ return 'data- ' ;
3583+ }
3584+
3585+ /*
3586+ * If it contains characters which would end the attribute name parsing then
3587+ * something it’s not possible to represent this in HTML.
3588+ */
3589+ if ( strcspn ( $ js_dataset_name , "=/> \t\f\r\n" ) !== $ end ) {
3590+ return null ;
3591+ }
3592+
3593+ $ html_name = 'data- ' ;
3594+ $ at = 0 ;
3595+ $ was_at = $ at ;
3596+
3597+ while ( $ at < $ end ) {
3598+ $ next_upper_after = strcspn ( $ js_dataset_name , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , $ at );
3599+ $ next_upper_at = $ at + $ next_upper_after ;
3600+ if ( $ next_upper_at >= $ end ) {
3601+ break ;
3602+ }
3603+
3604+ $ prefix = substr ( $ js_dataset_name , $ was_at , $ next_upper_at - $ was_at );
3605+ $ html_name .= strtolower ( $ prefix );
3606+ $ html_name .= '- ' . strtolower ( $ js_dataset_name [ $ next_upper_at ] );
3607+ $ at = $ next_upper_at + 1 ;
3608+ $ was_at = $ at ;
3609+ }
3610+
3611+ if ( $ was_at < $ end ) {
3612+ $ html_name .= strtolower ( substr ( $ js_dataset_name , $ was_at ) );
3613+ }
3614+
3615+ return $ html_name ;
3616+ }
0 commit comments