File size: 4,044 Bytes
ec94fc1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""Utility functions shared across renderers.

Mirrors PHP helpers: h(), formatMoneyFigures(), handbook_anchor(), etc.
"""

from __future__ import annotations

import html
import re


def h(s: str) -> str:
    """HTML-escape (mirrors PHP h())."""
    return html.escape(str(s), quote=True)


def is_assoc(a: list | dict) -> bool:
    """Check if an array is associative (dict-like) vs sequential list."""
    return isinstance(a, dict)


def hb_slug(s: str) -> str:
    """Slug helper for anchors."""
    tmp = s.lower().strip()
    tmp = re.sub(r"[^a-z0-9]+", "_", tmp, flags=re.IGNORECASE)
    tmp = re.sub(r"_+", "_", tmp)
    return tmp.strip("_")


def handbook_anchor(prefix: str, text: str, idx: int) -> str:
    """Normalise a string into a safe anchor id. Mirrors PHP handbook_anchor."""
    base = text.lower().strip()
    base = re.sub(r"[^a-z0-9]+", "-", base, flags=re.IGNORECASE)
    base = base.strip("-")
    if not base:
        base = f"{prefix}-{idx}"
    return f"{prefix}-{base}-{idx}"


def is_truthy(val) -> bool:
    """Mirrors PHP handbook_true."""
    if isinstance(val, bool):
        return val
    if isinstance(val, int):
        return val != 0
    v = str(val).lower().strip()
    return v not in ("0", "false", "")


def format_money_figures(text: str) -> str:
    """Mirrors PHP formatMoneyFigures().

    - Strips "USD " prefix
    - Adds $ to large numbers
    - Formats with commas
    """
    if not text:
        return text

    # Remove "USD " prefix
    text = re.sub(r"\bUSD\s*", "", text, flags=re.IGNORECASE)

    def _format_match(m: re.Match) -> str:
        num_str = m.group(1).replace(",", "")
        dec = m.group(2) if m.group(2) else ""
        num = float(num_str)
        if dec:
            formatted = f"{num:,.{len(dec)}f}"
        else:
            formatted = f"{num:,.0f}"
        return "$" + formatted

    # Add $ to large numbers (with optional decimals)
    text = re.sub(
        r"(?<![$0-9])(?<!\$)((?:\d{1,3}(?:,\d{3})+)|(?:\d{4,}))(?:\.(\d+))?(?![%\d/])",
        _format_match,
        text,
    )

    # Ensure remaining 4+ digit numbers are formatted with commas and $
    def _format_remaining(m: re.Match) -> str:
        num_str = m.group(1).replace(",", "")
        formatted = f"{float(num_str):,.0f}"
        return "$" + formatted

    text = re.sub(
        r"(?<!\$)\b(\d{1,3}(?:,\d{3})+|\d{4,})(?![%\d/])",
        _format_remaining,
        text,
    )

    return text


def sort_sections_stable(sections: list[dict]) -> list[dict]:
    """Stable sort: sort_order ASC, then id ASC, then insertion order."""
    for i, s in enumerate(sections):
        s.setdefault("_i", i)

    def sort_key(s: dict):
        so = s.get("sort_order")
        sid = s.get("id")
        so_key = (0, so) if so is not None else (1, 0)
        sid_key = (0, sid) if sid is not None else (1, 0)
        return (so_key, sid_key, s.get("_i", 0))

    sections.sort(key=sort_key)
    for s in sections:
        s.pop("_i", None)
    return sections


def get_any(d: dict, keys: list[str]) -> str:
    """Return the first non-empty string value found for one of the keys."""
    for k in keys:
        v = d.get(k)
        if v is None or isinstance(v, (dict, list)):
            continue
        t = str(v).strip()
        if t:
            return t
    return ""


def emphasize_keywords(text: str) -> str:
    """Add bold HTML emphasis to key handbook terms in already-escaped text.

    Bolds: REGULAR, PRIME, dollar amounts ($X,XXX), and other critical terms.
    Input must already be HTML-escaped. Returns HTML with <strong> tags.
    """
    if not text:
        return text

    escaped = h(text)

    # Bold REGULAR and PRIME (case-insensitive, whole word)
    escaped = re.sub(
        r'\b(REGULAR|PRIME)\b',
        r'<strong>\1</strong>',
        escaped,
        flags=re.IGNORECASE,
    )

    # Bold dollar amounts like $1,000 or $500
    escaped = re.sub(
        r'(\$[\d,]+(?:\.\d+)?)',
        r'<strong>\1</strong>',
        escaped,
    )

    return escaped