module

HtmlFilters

Functions that take a Lexbor document and return a modified version To create a Lexbor document, use Lexbor::Parser.new(html)

Constants

LINK_FIX_UNSAFE_CONTEXT = /(?:<script[^>]*>[^<]*|\<!--[^>]*)href\s*=\s*["']/

A href/src lookalike inside a script body or an HTML comment: the parser-based pass ignores those (they are not element attributes), so when they are present the string rewrite is not equivalent and callers must use the parser path instead.

NEEDS_CODE_FIX = /<code[^>]*\sclass\s*=\s*["'](?!language-|tz-)/

A code tag whose class doesn't already start with language-, which fix_code_classes would rewrite.

NEEDS_LINK_FIX = /(?:href|src)\s*=\s*(?:"(?!["\/#]|[a-zA-Z][a-zA-Z0-9+.\-]*:)[^"]*"|'(?!['\/#]|[a-zA-Z][a-zA-Z0-9+.\-]*:)[^']*')/

A href/src attribute value that make_links_relative would rewrite: anything not starting with "/", "#", a URL scheme (scheme'd values are absolute URLs; resolve+relativize round-trips a different-host absolute URL unchanged), and not empty (an empty value round-trips to empty, and the canonical link that carries it is skipped by the filter anyway).

NEEDS_LINK_FIX_CAPTURE = /(?:href|src)\s*=\s*(["'])(?!\/|#|[a-zA-Z][a-zA-Z0-9+.\-]*:)(.*?)\1/

Capture form of NEEDS_LINK_FIX: matches a href/src value that make_links_relative would rewrite, capturing the value and the quote character around it.

ROOT_RELATIVE_FIX = /(?:href|src)\s*=\s*(["'])\/(?!\/)(.*?)\1/

A root-relative href/src value that make_links_relative would rewrite (starts with "/" but not a protocol like "//").

UNFIXED_CODE_CLASS = /(?i:<code)(?:[^>"']|"[^"]*"|'[^']*')*?\s(?i:class)\s*=\s*(?:["'](?![^"'>]*(?<=["'\s])(?:language-|tz-))|(?!["']|language-|tz-)[^\s>])/

Stricter than NEEDS_CODE_FIX: a code class none of whose tokens starts with language- or tz-, the only ones fix_code_classes still rewrites. Already fixed blocks ("crystal language-crystal") do not match, so whole pages can be checked cheaply. It cannot tell whether the sits in a

, so a match is only a reason to
parse and try (see fix_code_classes?). Tag and attribute
names are case-insensitive, quoted values of earlier attributes
may hold ">" (or a "class=" that is not one), and the value may be
unquoted (one token then); the value check stays case-sensitive, like
fix_code_classes.

Class methods

downgrade_headers(doc, n = 2)

Shift headers so the highest level is n (n=2 means h1->h3, h2->h4, etc.) If n=2 and doc has h3 as the highest, then h3->h2, h4->h3, etc.

Source
fix_code_classes(doc)

Post-process HTML to add language- prefix to code blocks. Idempotent: safe to run repeatedly (e.g. once per content pass and again on the page render), since it only adds the language- prefix when no token already carries one.

Source
fix_code_classes?(doc) : Bool

fix_code_classes, telling whether any code block changed

Source
fragment_html(doc : Lexbor::Parser) : String

Serialize a document parsed from an HTML fragment back into a fragment. Parser#to_html emits the whole document Lexbor builds around it (<html><head></head><body>...</body></html>), which used to leak into pages and feeds when the content was embedded elsewhere. Leading , , <link> or <meta> elements end up in the parsed <head>, so its contents come first. A comment opening the fragment (a leading <!--more-->) is a child of the document itself, outside <html>, so the walk starts there; a doctype also lands there and is dropped, since the fragment gets embedded in a page that has its own.

Frees doc (see to_html): don't use it afterwards.

Source
remove_empty_paragraphs(doc)

Remove empty paragraph tags

Source
string_rewrite_safe?(html : String) : Bool

Whether the string-based link rewrite is safe for this html: no href/src lookalikes hiding in script bodies or comments, and no uppercase attribute spellings the capture regex would miss.

Source
to_html(doc : Lexbor::Parser) : String

Serialize a whole document and free it; don't use doc afterwards. Lexbor's Node#to_html hands only the raw element pointer to C, so without a later use of doc nothing keeps the Parser reachable while lexbor serializes. The serialize callback allocates, a collection can run, and the Parser's finalizer destroys the document mid-walk (LXB_STATUS_ERROR or a segfault in parallel builds). Freeing it here is that later use, and returns lexbor's memory right away instead of at the next GC.

Source