Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,15 @@ private static function extract_balanced_li(string $html): ?string
*/
private static function get_button_transforms()
{
return array(array('blockName' => 'core/group', 'priority' => 8, 'selector' => 'div,p', 'isMatch' => function ($element) {
return array(array('blockName' => 'core/paragraph', 'priority' => 8, 'selector' => 'div', 'isMatch' => function ($element) {
return self::is_aria_hidden_inline_span_container($element);
}, 'transform' => function ($element) {
return self::create_aria_hidden_inline_span_paragraph($element);
}), array('blockName' => 'core/paragraph', 'priority' => 8, 'selector' => 'div', 'isMatch' => function ($element) {
return self::is_visual_diagram_span_container($element);
}, 'transform' => function ($element) {
return self::create_aria_hidden_inline_span_paragraph($element);
}), array('blockName' => 'core/group', 'priority' => 8, 'selector' => 'div,p', 'isMatch' => function ($element) {
return self::is_static_visual_button_container($element);
}, 'transform' => function ($element) {
return self::create_static_visual_button_group($element);
Expand All @@ -741,6 +749,10 @@ private static function get_button_transforms()
return self::is_button_anchor_container($element) || self::is_single_button_anchor_wrapper($element);
}, 'transform' => function ($element) {
return self::create_buttons_block_from_container($element);
}), array('blockName' => 'core/paragraph', 'priority' => 9, 'selector' => 'a', 'isMatch' => function ($element) {
return $element->get_tag_name() === 'A' && self::is_branded_inline_anchor($element);
}, 'transform' => function ($element) {
return self::create_branded_inline_anchor_paragraph($element);
}), array('blockName' => 'core/buttons', 'priority' => 9, 'selector' => 'a', 'isMatch' => function ($element) {
return $element->get_tag_name() === 'A' && self::is_button_like_anchor($element);
}, 'transform' => function ($element) {
Expand Down Expand Up @@ -900,6 +912,110 @@ private static function create_static_visual_button_paragraph($element): array
$attributes['content'] = $element->get_inner_html();
return HTML_To_Blocks_Block_Factory::create_block('core/paragraph', $attributes);
}
/**
* Checks whether an aria-hidden div is only inline span labels.
*
* Decorative rulers and visual scales commonly use direct span ticks inside an
* aria-hidden div. Convert those wrappers to a paragraph so Gutenberg does not
* insert an extra paragraph inside a group and break span-level CSS layouts.
*
* @param HTML_To_Blocks_HTML_Element $element Element to inspect.
* @return bool True when the wrapper can safely become paragraph markup.
*/
private static function is_aria_hidden_inline_span_container($element): bool
{
if ('DIV' !== $element->get_tag_name() || !$element->has_attribute('aria-hidden')) {
return \false;
}
if ('true' !== \strtolower(\trim((string) $element->get_attribute('aria-hidden')))) {
return \false;
}
if ('' === \trim($element->get_text_content())) {
return \false;
}
return self::html_contains_only_direct_spans($element->get_inner_html());
}
/**
* Checks whether a visible diagram div contains only inline span labels.
*
* Diagram layers commonly position direct span labels. Keep those labels as
* direct paragraph content instead of wrapping them in an inserted paragraph
* inside a group, which changes diagram sizing and vertical rhythm.
*
* @param HTML_To_Blocks_HTML_Element $element Element to inspect.
* @return bool True when the wrapper can safely become paragraph markup.
*/
private static function is_visual_diagram_span_container($element): bool
{
if ('DIV' !== $element->get_tag_name() || !self::class_matches($element, '/(?:^|[-_\s])diagram(?:$|[-_\s])/i')) {
return \false;
}
if ('' === \trim($element->get_text_content())) {
return \false;
}
return self::html_contains_only_direct_spans($element->get_inner_html());
}
/**
* Creates a paragraph preserving direct span markup from an aria-hidden div.
*
* @param HTML_To_Blocks_HTML_Element $element Span-only wrapper.
* @return array Block array.
*/
private static function create_aria_hidden_inline_span_paragraph($element): array
{
$attributes = self::get_block_support_attributes($element, array('anchor' => \true, 'class_name' => \true, 'colors' => \true, 'typography' => \true, 'spacing' => \true, 'border' => \true));
$attributes['content'] = \trim($element->get_inner_html());
return HTML_To_Blocks_Block_Factory::create_block('core/paragraph', $attributes);
}
/**
* Checks whether HTML contains only sibling span elements and whitespace.
*
* @param string $html Inner HTML to inspect.
* @return bool True when no non-span sibling markup or text remains.
*/
private static function html_contains_only_direct_spans(string $html): bool
{
$remaining = $html;
if (!\preg_match_all('/<span\b[^>]*>.*?<\/span>/is', $html, $matches)) {
return \false;
}
foreach ($matches[0] as $span_html) {
$remaining = \str_replace($span_html, '', $remaining);
}
return '' === \trim($remaining);
}
/**
* Checks whether an anchor is a branded inline link with nested visual spans.
*
* @param HTML_To_Blocks_HTML_Element $element Element to inspect.
* @return bool True when the branded link can safely become paragraph markup.
*/
private static function is_branded_inline_anchor($element): bool
{
if ('A' !== $element->get_tag_name()) {
return \false;
}
$href = \trim((string) ($element->get_attribute('href') ?? ''));
if ('' === $href || '#' !== $href[0]) {
return \false;
}
if (!self::class_matches($element, '/(?:^|[-_\s])brand(?:$|[-_\s])/i')) {
return \false;
}
return \preg_match('/<\s*span\b/i', $element->get_inner_html()) === 1;
}
/**
* Creates a paragraph preserving a branded inline anchor as editable markup.
*
* @param HTML_To_Blocks_HTML_Element $element Branded anchor element.
* @return array Block array.
*/
private static function create_branded_inline_anchor_paragraph($element): array
{
$attributes = self::get_block_support_attributes($element, array('anchor' => \true));
$attributes['content'] = \trim($element->get_outer_html());
return HTML_To_Blocks_Block_Factory::create_block('core/paragraph', $attributes);
}
/**
* Checks whether a container is explicitly an action/link row.
*
Expand Down Expand Up @@ -1017,7 +1133,7 @@ private static function is_class_sensitive_cta_anchor($element): bool
if (\preg_match('/(?:^|\s)(?:wp-block-button__link|wp-element-button)(?:$|\s)/i', $class_name) === 1) {
return \false;
}
return \preg_match('/(?:^|\s)(?:cta-(?:btn|link)|(?:btn|link)-cta)(?:$|\s)/i', $class_name) === 1;
return \preg_match('/(?:^|\s)(?:button|cta-(?:btn|link)|(?:btn|link)-cta)(?:$|\s)/i', $class_name) === 1;
}
/**
* Creates a buttons wrapper with one button child from an anchor.
Expand Down Expand Up @@ -1584,7 +1700,7 @@ private static function is_div_line_code_panel($element): bool
return \false;
}
foreach ($child->get_child_elements() as $inline_child) {
if (!\in_array($inline_child->get_tag_name(), array('SPAN', 'BR', 'CODE', 'STRONG', 'B', 'EM', 'I'), \true)) {
if (!\in_array($inline_child->get_tag_name(), array('SPAN', 'BR', 'CODE', 'STRONG', 'B', 'EM', 'I', 'SMALL'), \true)) {
return \false;
}
}
Expand Down Expand Up @@ -2361,19 +2477,6 @@ private static function is_safe_dimension_value(string $value): bool
}
return \preg_match('/^[0-9.]+(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex)?$/i', $value) === 1 || \preg_match('/^calc\(\s*[0-9.]+(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex)?\s*[-+]\s*[0-9.]+(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex)?\s*\)$/i', $value) === 1;
}
/**
* Checks whether a section is a high-confidence full-bleed hero wrapper.
*
* @param HTML_To_Blocks_HTML_Element $element The source element.
* @return bool True when a default flow group would lose hero centering intent.
*/
private static function is_hero_like_section($element): bool
{
if ($element->get_tag_name() !== 'SECTION' || !$element->has_attribute('class')) {
return \false;
}
return self::class_matches($element, '/(?:^|[-_\s])(?:hero|cover|banner|masthead)(?:$|[-_\s])/i');
}
/**
* Applies direct border declarations to block support attributes.
*
Expand Down Expand Up @@ -2810,6 +2913,9 @@ private static function create_card_grid_child_block($child): ?array
if (\in_array($tag, array('OL', 'UL'), \true)) {
return self::create_list_block_from_element($child);
}
if (self::is_numbered_card_label_span($child)) {
return HTML_To_Blocks_Block_Factory::create_block('core/html', array('content' => $child->get_outer_html()));
}
if ('P' === $tag || 'SPAN' === $tag) {
return HTML_To_Blocks_Block_Factory::create_block('core/paragraph', \array_merge(self::get_block_support_attributes($child, array('anchor' => \true, 'class_name' => \true)), array('content' => $child->get_inner_html())));
}
Expand All @@ -2818,6 +2924,22 @@ private static function create_card_grid_child_block($child): ?array
}
return null;
}
/**
* Checks whether a card child span is a numbered label that should keep its tag.
*
* @param HTML_To_Blocks_HTML_Element $child Card child element.
* @return bool True when the span should remain source HTML.
*/
private static function is_numbered_card_label_span($child): bool
{
if ('SPAN' !== $child->get_tag_name() || !$child->has_attribute('class')) {
return \false;
}
if (!self::class_matches($child, '/(?:^|[-_\s])(?:card[-_\s]?number|item[-_\s]?number|service[-_\s]?number)(?:$|[-_\s])/i')) {
return \false;
}
return \preg_match('/^\s*\d{1,3}\s*$/', $child->get_text_content()) === 1;
}
/**
* Gets a single whole-card anchor child when it is the card's only content.
*
Expand Down Expand Up @@ -2990,7 +3112,26 @@ private static function is_decorative_figure_with_caption($element): bool
if ('FIGCAPTION' !== $caption->get_tag_name() || \trim(wp_strip_all_tags($caption->get_inner_html())) === '') {
return \false;
}
return \count($children) === 1 || self::is_empty_decorative_element($children[0]);
return \count($children) === 1 || self::is_empty_decorative_element($children[0]) || self::is_nested_empty_decorative_element($children[0]);
}
/**
* Checks whether an element and its descendants are inert decorative chrome.
*
* @param HTML_To_Blocks_HTML_Element $element Source element.
* @return bool True when the subtree has no meaningful content or controls.
*/
private static function is_nested_empty_decorative_element($element): bool
{
if (!$element->has_attribute('aria-hidden') || 'true' !== \strtolower($element->get_attribute('aria-hidden'))) {
return \false;
}
if (\trim(wp_strip_all_tags($element->get_inner_html())) !== '') {
return \false;
}
foreach ($element->query_selector_all('a, button, input, select, textarea, img, video, audio, iframe, object, embed, svg') as $functional_child) {
return \false;
}
return !empty($element->get_child_elements());
}
/**
* Checks whether an empty element carries visual background styling.
Expand Down Expand Up @@ -3154,6 +3295,9 @@ private static function get_paragraph_transforms()
if (self::is_static_visual_label($element)) {
return \true;
}
if ('SPAN' === $element->get_tag_name() && $element->has_attribute('class')) {
return \false;
}
return \in_array($element->get_tag_name(), array('DIV', 'SPAN'), \true) && array() === $element->get_child_elements() && \trim($element->get_text_content()) !== '';
}, 'transform' => function ($element) {
$content = $element->get_tag_name() === 'A' ? self::get_paragraph_anchor_content($element) : $element->get_inner_html();
Expand Down
21 changes: 21 additions & 0 deletions vendor_prefixed/chubes4/html-to-blocks-converter/raw-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ function html_to_blocks_convert($html, $args = array())
if (empty(\trim($html))) {
return array();
}
if (html_to_blocks_is_standalone_hash_anchor_fragment($html)) {
$html = html_to_blocks_normalise_blocks($html);
}
$collect_metrics = \function_exists('has_action') && \has_action('html_to_blocks_convert_metrics');
$metrics = null;
$convert_started = 0.0;
Expand Down Expand Up @@ -745,6 +748,24 @@ function html_to_blocks_parse_shortcode($shortcode)
}
return HTML_To_Blocks_Block_Factory::create_block('core/shortcode', array('text' => $shortcode));
}
/**
* Checks whether an HTML fragment is one same-page hash anchor.
*
* @param string $html HTML fragment.
* @return bool True when the fragment is a standalone hash anchor.
*/
function html_to_blocks_is_standalone_hash_anchor_fragment(string $html): bool
{
$element = HTML_To_Blocks_HTML_Element::from_html($html);
if (!$element || 'A' !== $element->get_tag_name()) {
return \false;
}
$href = \trim((string) $element->get_attribute('href'));
if ('' === $href || '#' !== $href[0]) {
return \false;
}
return \trim($element->get_outer_html()) === \trim($html);
}
/**
* Normalises blocks in HTML - wraps inline content in paragraphs
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function esc_url($value)
if (!\function_exists('BlockFormatBridge\Vendor\wp_strip_all_tags')) {
function wp_strip_all_tags($value)
{
return wp_strip_all_tags((string) $value);
return \strip_tags((string) $value);
}
}
class WP_Block_Type_Registry
Expand Down Expand Up @@ -154,6 +154,9 @@ public function get_registered($name)
$class_sensitive_cta_row = new HTML_To_Blocks_HTML_Element('div', ['class' => 'cta-actions'], '<div class="cta-actions"><a class="cta-link" href="#commands">Browse the docs</a></div>', '<a class="cta-link" href="#commands">Browse the docs</a>');
$class_sensitive_cta_row_transform = $find_transform($class_sensitive_cta_row);
$smoke_assert('core/buttons' !== $class_sensitive_cta_row_transform['blockName'], 'class-sensitive-cta-row-avoids-buttons');
$button_variant_row = new HTML_To_Blocks_HTML_Element('div', ['class' => 'hero-actions cta'], '<div class="hero-actions cta"><a class="button primary" href="#classes">Find a class</a><a class="button secondary" href="#first-visit">Plan your first visit</a></div>', '<a class="button primary" href="#classes">Find a class</a><a class="button secondary" href="#first-visit">Plan your first visit</a>');
$button_variant_row_transform = $find_transform($button_variant_row);
$smoke_assert('core/buttons' !== $button_variant_row_transform['blockName'], 'class-sensitive-button-variant-row-avoids-buttons');
$ordinary_link = new HTML_To_Blocks_HTML_Element('p', [], '<p>Read <a href="/more">more</a>.</p>', 'Read <a href="/more">more</a>.');
$ordinary_link_transform = $find_transform($ordinary_link);
$smoke_assert('core/paragraph' === $ordinary_link_transform['blockName'], 'ordinary-link-stays-paragraph');
Expand Down Expand Up @@ -227,6 +230,11 @@ public function get_registered($name)
$onclick_submit_button = new HTML_To_Blocks_HTML_Element('button', ['class' => 'tab-btn', 'type' => 'submit', 'onclick' => 'submitForm()'], '<button class="tab-btn" type="submit" onclick="submitForm()">Submit</button>', 'Submit');
$smoke_assert($find_transform($onclick_submit_button) === null, 'onclick-submit-button-falls-through');
// -------------------------------------------------------------------------
// Spans: classed leaf spans preserve source display semantics as fallback HTML.
// -------------------------------------------------------------------------
$classed_leaf_span = new HTML_To_Blocks_HTML_Element('span', ['class' => 'service-number'], '<span class="service-number">01</span>', '01');
$smoke_assert($find_transform($classed_leaf_span) === null, 'classed-leaf-span-falls-through');
// -------------------------------------------------------------------------
// Labels: static visual UI labels become text, real form labels fall through.
// -------------------------------------------------------------------------
$visual_label = new HTML_To_Blocks_HTML_Element('label', ['class' => 'inspector-label'], '<label class="inspector-label">Type</label>', 'Type');
Expand Down
Loading
Loading