>
*/
private static array $bricks_trees = [];
/**
* JSON keys whose values are user-visible text.
*
* Builder trees mix content with configuration, so a blind string sweep
* would count CSS classes and option slugs as words. Matching on the key
* keeps the word count honest.
*
* @var string[]
*/
private const CONTENT_KEYS = [
'text', 'title', 'subtitle', 'heading', 'subheading', 'content',
'description', 'caption', 'excerpt', 'label', 'value', 'html',
'editor', 'quote', 'answer', 'question', 'body', 'button_text',
];
/**
* JSON keys whose values hold a link destination.
*
* Builders store a link's destination in a structured field separate from
* its label, either as a bare URL string or as a `{ url: … }` object.
* Neither shape survives a text sweep — the key is not content and a bare
* URL contains no `<` — so no `` tag reached the link counters.
*
* @var string[]
*/
private const URL_KEYS = [
'link', 'url', 'href', 'link_url', 'button_link', 'permalink', 'link_to',
];
/**
* JSON keys whose values hold an embedded video's source.
*
* A builder's video widget keeps its destination in a provider-specific
* field — Elementor picks `youtube_url`, `vimeo_url`, `dailymotion_url` or
* `hosted_url` according to the chosen source type — none of which is a
* link field or a content field, so a video on a builder page reached the
* analyzers as nothing at all.
*
* These are deliberately kept out of URL_KEYS. A video is an embed, not an
* outbound link: rendering one as `` would add a spurious external
* link to every page carrying a video and skew the link counts. They are
* reconstructed as `