# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - 2026-01-20

### Added

- Text extraction with `text/2` function
  - Options: `:deep`, `:separator`, `:strip`, `:include_script`, `:include_style`, `:include_inputs`
- Attribute extraction functions
  - `attr/2` - get attribute from single node
  - `attribute/2` - extract attribute from list of nodes
  - `attribute/3` - query and extract attribute in one step
- `query_one/2` for finding first matching element
- CSS combinator support in selectors
  - Descendant combinator (space): `div p`
  - Child combinator: `div > p`
  - Adjacent sibling combinator: `h1 + p`
  - General sibling combinator: `h1 ~ p`

## [0.1.0] - 2026-01-19

### Added

- CSS selector querying with `query/2` and `children/2` functions
  - Tag selectors: `div`, `p`, `a`
  - Universal selector: `*`
  - Class selectors: `.class`, `.foo.bar`
  - ID selectors: `#id`
  - Attribute selectors: `[attr]`, `[attr=val]`, `[attr^=prefix]`, `[attr$=suffix]`, `[attr*=substring]`
  - Compound selectors: `div.class#id[attr]`
  - Selector lists: `.a, .b`
- `get_attr/3` helper for retrieving attribute values from lists
- `merge_attr_lists/2` helper for merging attribute lists (preserves existing values)
- Serializer options for customizable HTML output (`:print_attributes`, `:escape_comment`, `:escape_empty`)
- `xml_violation_mode` for XML infoset coercion in tokenizer
- All tokenizer initial states enabled in test harness
- All 23 HTML5 insertion modes implemented:
  - `initial`, `before_html`, `before_head`, `in_head`, `in_head_noscript`
  - `after_head`, `in_body`, `text`, `in_table`, `in_table_text`
  - `in_caption`, `in_column_group`, `in_table_body`, `in_row`, `in_cell`
  - `in_select`, `in_select_in_table`, `in_template`, `after_body`
  - `in_frameset`, `after_frameset`, `after_after_body`, `after_after_frameset`
- HTML5lib tree construction test suite integration (1476 tests)
- Adoption agency algorithm for proper formatting element handling
- Active formatting elements list reconstruction
- Foster parenting for misplaced table content
- Foreign content support (SVG and MathML namespaces)
- SVG attribute case adjustments (e.g., `viewbox` -> `viewBox`)
- Template element handling with template mode stack
- Implicit tag closing per HTML5 specification
- Leading newline stripping for `pre`, `textarea`, and `listing` elements
- Ruby element implicit closing (`rb`, `rt`, `rtc`, `rp`)
- Second `<body>` tag attribute merging
- Nested `<form>` handling (ignored when form pointer is set)
- Post-`</html>` content handling (comments as document siblings)
- HTML serializer with html5lib-compliant tree output format
- DOCTYPE quirks mode detection per HTML5 specification

### Changed

- **BREAKING**: Attributes now use list of tuples instead of maps for Floki compatibility
  - Before: `{"p", %{"class" => "foo"}, ["Hi"]}`
  - After: `{"p", [{"class", "foo"}], ["Hi"]}`
  - Attributes are sorted alphabetically for deterministic output
- Simplified `in_table.ex` with guard clauses and reduced helpers
- Consolidated `foreign_namespace` helper in helpers.ex
- Refactored tree builder to ref-only stack architecture
  - Stack holds only refs: `[ref, ref, ref]`
  - Elements map holds all data: `ref => %{tag, attrs, children, parent_ref}`
- Consolidated foster parenting into unified `foster_parent/2` API with tagged tuples
- Extracted all insertion modes to separate modules under `lib/pure_html/tree_builder/modes/`
- Multi-byte scanning optimization in tokenizer for faster parsing
  - `chars_until_null`, `chars_until_comment`, `chars_until_cdata`
  - Entity reference fast path detection

### Fixed

- Active formatting reconstruction before foster-parenting formatting elements in table context
- Frameset/noframes mode transitions
- Ruby nesting edge cases
- Table handling in `foreignObject`
- Multiple tree construction edge cases
- Original mode preservation for style/script in table context
- `</li>` now only closes when `li` is in list item scope (ul/ol are barriers)
- `<head>` in body mode is now properly ignored
- `</br>` in table context is foster-parented as `<br>`
- MathML `mglyph`/`malignmark` namespace preservation in text integration points
- Foreign content breakout for HTML elements inside SVG/MathML
- Heading nesting (h1-h6 now properly close previous headings)
- Frameset handling (body removal from DOM when replaced by frameset)
- `pop_until_one_of` boundary element handling for template context
- `</svg>` and `</math>` now properly close all foreign children
- Foreign content scope boundaries for `in_scope?` checks (SVG: desc, foreignObject, title; MathML: annotation-xml, mi, mn, mo, ms, mtext)
- Select mode end tag handling to close pushed HTML elements
- Void elements (img, br, etc.) treated as self-closing in select mode
- `<selectedcontent>` population with first/selected option content
- Adoption agency for formatting elements inside select mode
- Active formatting reconstruction before text insertion in select mode
- `</p>` handling in table context with foster parenting
- Option closing when new option/optgroup starts
- `<li>` in table context handling
- HTML5 whitespace handling in various contexts
- Table/anchor active formatting handling and scope checks
- Form element pointer handling and nested form detection
- Row mode bogus `<tr>` detection
- `current_parent_ref` handling in foster parenting contexts
  - Fixed `close_tag_ref`, `close_block_end_tag`, `close_foreign_root` to use stack top
  - Fixed adoption agency to use stack top after popping elements
- Active formatting reconstruction for void elements in table context
- Row mode foster parenting with in_body rules delegation

### Current Status

- **All 8,634 html5lib tests passing (100%)**
  - Tokenizer: 7,036 tests
  - Tree construction: 1,476 tests
  - Encoding: 82 tests
  - Serializer: 40 tests
