code wiki / (root) / nx_reveal_lib.nx

nx_reveal_lib.nx source

↩ module page · 393 lines · 21668 B

1// nx_reveal_lib.nx -- THE ONE CLICK-TO-OPEN PRIMITIVE FOR PUBLIC BOARDS (ecosystem EC64, 2026-09-17). 2// Operator 2026-09-17: "i dont want nudity or other things shown without a clickthrough to open or expand on public boards" 3// and, of result pages, "i dont want them strewn about". Every emitter that puts result media on a public page composes 4// THIS lib, so the rule lives in the path and not in a seat's memory: 5// * a content CLASS is data on every media item; the class decides the MODE through the policy rows: 6// open = may render inline (charts, diagrams) 7// click = collapsed behind a native disclosure; nothing is drawn until the reader opens it, and with scripting 8// enabled a lazy image inside a closed disclosure is not fetched either 9// gated = never emitted on a public page: a named placeholder, and the href is NOT written (a URL is a leak) 10// * policy rows are DATA: class|<id>|<mode>|<rating>|<source>|<label>, the built-in rows below plus an optional file 11// (knowledge/reveal_policy.conf). THE FILE MAY ONLY TIGHTEN: the effective mode is the stricter of the two, a class the 12// file declares with rating adult can never be open, and a class NOBODY declares is gated. Wrong in the direction of 13// showing less, by construction. 14// * rating adult on any emitted item makes the page carry the rating meta the search engines' safe filters read. 15// * source is the IPTC digital source type term for the item (algorithmicMedia = "created purely by an algorithm not 16// based on any sampled training data", which is what the estate's SDF renders are), written as a data attribute so a 17// synthetic render says so in a machine-readable place as well as in words. 18// * rvl_audit is the VERIFIER: it counts media tags in an emitted page that sit outside a reveal block or an open 19// figure. Every consumer's gate holds that count at zero over its own output. 20// The disclosure is the platform's own (details/summary): keyboard operable, announced by assistive technology, and it 21// works with scripting off. A click-to-open is a CONTENT WARNING, NOT AGE ASSURANCE: that is why explicit media and nude 22// reference media of real people are gated off the public surface here rather than hidden behind a click. 23// license_tier: ORIGINAL No hw writes (Rule 26). 24import "nx_syscalls.nx" 25 26const RVL_MODE_OPEN: i64 = 0 27const RVL_MODE_CLICK: i64 = 1 28const RVL_MODE_GATED: i64 = 2 29const RVL_RATING_GENERAL: i64 = 0 30const RVL_RATING_ADULT: i64 = 1 31const RVL_OVERFLOW: i64 = 0 - 1 32const RVL_NL: i64 = 10 33const RVL_CR: i64 = 13 34const RVL_PIPE: i64 = 124 35const RVL_LT: i64 = 60 36const RVL_GT: i64 = 62 37const RVL_AMP: i64 = 38 38const RVL_QUOT: i64 = 34 39const RVL_APOS: i64 = 39 40const RVL_SLASH: i64 = 47 41const RVL_I64: i64 = 8 42const RVL_BYTE: i64 = 255 43// policy row fields after the leading "class" tag 44const RVL_F_ID: i64 = 1 45const RVL_F_MODE: i64 = 2 46const RVL_F_RATING: i64 = 3 47const RVL_F_SOURCE: i64 = 4 48const RVL_F_LABEL: i64 = 5 49const RVL_FIELDS: i64 = 6 50// a lookup record: found flag, mode, rating, then the source and label spans inside the policy buffer 51const RVL_L_FOUND: i64 = 0 52const RVL_L_MODE: i64 = 1 53const RVL_L_RATING: i64 = 2 54const RVL_L_SRC_S: i64 = 3 55const RVL_L_SRC_E: i64 = 4 56const RVL_L_LAB_S: i64 = 5 57const RVL_L_LAB_E: i64 = 6 58const RVL_L_N: i64 = 7 59// the emit tally a consumer prints on its receipt: the partition open + click + gated = items, unknown is a subset of gated 60const RVL_T_OPEN: i64 = 0 61const RVL_T_CLICK: i64 = 1 62const RVL_T_GATED: i64 = 2 63const RVL_T_UNKNOWN: i64 = 3 64const RVL_T_ADULT: i64 = 4 65const RVL_T_N: i64 = 5 66// the audit record 67const RVL_A_COVERED: i64 = 0 68const RVL_A_OPEN: i64 = 1 69const RVL_A_BARE: i64 = 2 70const RVL_A_BLOCKS: i64 = 3 71const RVL_A_N: i64 = 4 72const RVL_POLICY_FILE: *u8 = "knowledge/reveal_policy.conf" 73const RVL_POLICY_FILE_BUILDROOT: *u8 = "buildroot/knowledge/reveal_policy.conf" 74const RVL_POLICY_FILE_UP: *u8 = "../knowledge/reveal_policy.conf" 75const RVL_WHICH_NONE: i64 = 0 76const RVL_WHICH_HERE: i64 = 1 77const RVL_WHICH_BUILDROOT: i64 = 2 78const RVL_WHICH_UP: i64 = 3 79const RVL_BUILTIN: *u8 = "class|chart|open|general|algorithmicMedia|Chart or diagram drawn by the estate\nclass|face-synthetic|click|general|algorithmicMedia|Synthetic face render, generated by the estate\nclass|body-synthetic-clothed|click|general|algorithmicMedia|Synthetic body render, clothed, generated by the estate\nclass|reference-photo|click|general|digitalCapture|Reference photo of a real person, beta review only\nclass|measured-overlay|click|general|compositeCapture|Reference photo with the rulers' overlay, beta review only\nclass|model-3d|click|general|algorithmicMedia|3D model viewer, opens on request\nclass|unclassified-image|click|general||Image whose publisher declared no content class\nclass|anatomy-synthetic|click|adult|algorithmicMedia|Synthetic anatomical render, may show an unclothed body\nclass|nudity-synthetic|click|adult|algorithmicMedia|Synthetic render containing nudity\nclass|nudity-reference|gated|adult|digitalCapture|Reference media containing nudity, withheld from public pages\nclass|explicit|gated|adult|algorithmicMedia|Explicit media, withheld from public pages\n" 80const RVL_CLASS_TAG: *u8 = "class" 81const RVL_W_OPEN: *u8 = "open" 82const RVL_W_CLICK: *u8 = "click" 83const RVL_W_GATED: *u8 = "gated" 84const RVL_W_ADULT: *u8 = "adult" 85const RVL_UNKNOWN_LABEL: *u8 = "Undeclared content class, withheld from public pages until a policy row names it" 86const RVL_BLOCK_OPEN: *u8 = "<details class=\"nx-reveal\"" 87const RVL_BLOCK_CLOSE: *u8 = "</details>" 88const RVL_FIG_OPEN: *u8 = "<figure class=\"nx-open\"" 89const RVL_FIG_CLOSE: *u8 = "</figure>" 90 91func rvl_slen(s: *u8) -> i64 { var n: i64 = 0; while ((s[n] as i64) & RVL_BYTE) != 0 { n = n + 1 } return n } 92// bounded append: RVL_OVERFLOW once the buffer would overflow, and every later append keeps returning it (the caller 93// checks ONCE at the end; a page that did not fit is refused, never truncated) 94func rvl_cat(out: *u8, o: i64, cap: i64, s: *u8) -> i64 { 95 if o < 0 { return RVL_OVERFLOW } 96 let n: i64 = rvl_slen(s) 97 if o + n + 1 > cap { return RVL_OVERFLOW } 98 var i: i64 = 0 99 while i < n { out[o + i] = s[i]; i = i + 1 } 100 out[o + n] = 0 as u8 101 return o + n 102} 103func rvl_esc_byte(out: *u8, o: i64, cap: i64, c: i64) -> i64 { 104 if o < 0 { return RVL_OVERFLOW } 105 if c == RVL_LT { return rvl_cat(out, o, cap, "&lt;" as *u8) } 106 if c == RVL_GT { return rvl_cat(out, o, cap, "&gt;" as *u8) } 107 if c == RVL_AMP { return rvl_cat(out, o, cap, "&amp;" as *u8) } 108 if c == RVL_QUOT { return rvl_cat(out, o, cap, "&quot;" as *u8) } 109 if c == RVL_APOS { return rvl_cat(out, o, cap, "&apos;" as *u8) } 110 if o + 2 > cap { return RVL_OVERFLOW } 111 out[o] = c as u8 112 out[o + 1] = 0 as u8 113 return o + 1 114} 115// HTML-escaped append of a span [s, e) of buf: text and attribute values both go through this 116func rvl_cat_span_esc(out: *u8, o: i64, cap: i64, buf: *u8, s: i64, e: i64) -> i64 { 117 var p: i64 = o 118 var i: i64 = s 119 while i < e { 120 p = rvl_esc_byte(out, p, cap, (buf[i] as i64) & RVL_BYTE) 121 i = i + 1 122 } 123 return p 124} 125func rvl_cat_esc(out: *u8, o: i64, cap: i64, s: *u8) -> i64 { return rvl_cat_span_esc(out, o, cap, s, 0, rvl_slen(s)) } 126 127// does buf[s, e) equal the literal 128func rvl_span_is(buf: *u8, s: i64, e: i64, lit: *u8) -> i64 { 129 let n: i64 = rvl_slen(lit) 130 if e - s != n { return 0 } 131 var i: i64 = 0 132 while i < n { if buf[s + i] != lit[i] { return 0 } i = i + 1 } 133 return 1 134} 135func rvl_mode_of_span(buf: *u8, s: i64, e: i64) -> i64 { 136 if rvl_span_is(buf, s, e, RVL_W_OPEN) == 1 { return RVL_MODE_OPEN } 137 if rvl_span_is(buf, s, e, RVL_W_CLICK) == 1 { return RVL_MODE_CLICK } 138 return RVL_MODE_GATED // "gated" and every word nobody declared 139} 140// find the row class|<cls>|... in a policy buffer; rec gets the parsed row. Returns 1 when found 141func rvl_find(pol: *u8, pn: i64, cls: *u8, rec: *i64) -> i64 { 142 rec[RVL_L_FOUND] = 0 143 let fs: *i64 = sys_mmap((RVL_FIELDS + 1) * RVL_I64) as *i64 144 var i: i64 = 0 145 while i < pn { 146 var e: i64 = i 147 var going: i64 = 1 148 while going == 1 { 149 if e >= pn { going = 0 } else { 150 let c: i64 = (pol[e] as i64) & RVL_BYTE 151 if c == RVL_NL { going = 0 } else { e = e + 1 } 152 } 153 } 154 var le: i64 = e 155 if le > i { if ((pol[le - 1] as i64) & RVL_BYTE) == RVL_CR { le = le - 1 } } 156 // split the line on pipes 157 var nf: i64 = 0 158 fs[0] = i 159 var k: i64 = i 160 while k < le { 161 if ((pol[k] as i64) & RVL_BYTE) == RVL_PIPE { if nf + 1 < RVL_FIELDS { nf = nf + 1; fs[nf] = k + 1 } } 162 k = k + 1 163 } 164 if nf + 1 == RVL_FIELDS { 165 if rvl_span_is(pol, fs[0], fs[1] - 1, RVL_CLASS_TAG) == 1 { 166 if rvl_span_is(pol, fs[RVL_F_ID], fs[RVL_F_ID + 1] - 1, cls) == 1 { 167 rec[RVL_L_FOUND] = 1 168 rec[RVL_L_MODE] = rvl_mode_of_span(pol, fs[RVL_F_MODE], fs[RVL_F_MODE + 1] - 1) 169 rec[RVL_L_RATING] = rvl_span_is(pol, fs[RVL_F_RATING], fs[RVL_F_RATING + 1] - 1, RVL_W_ADULT) 170 rec[RVL_L_SRC_S] = fs[RVL_F_SOURCE] 171 rec[RVL_L_SRC_E] = fs[RVL_F_SOURCE + 1] - 1 172 rec[RVL_L_LAB_S] = fs[RVL_F_LABEL] 173 rec[RVL_L_LAB_E] = le 174 return 1 175 } 176 } 177 } 178 i = e + 1 179 } 180 return 0 181} 182// the optional policy file: probed at the three places a caller's CWD can put it; which[0] says where it came from 183// (RVL_WHICH_NONE = built-in rows only). Returns the buffer (0 when absent) and its length in len[0] 184func rvl_policy_load(len: *i64, which: *i64) -> *u8 { 185 which[0] = RVL_WHICH_NONE 186 len[0] = 0 187 var buf: *u8 = sys_read_file(RVL_POLICY_FILE, len) 188 if (buf as i64) != 0 { if len[0] > 0 { which[0] = RVL_WHICH_HERE; return buf } } 189 buf = sys_read_file(RVL_POLICY_FILE_BUILDROOT, len) 190 if (buf as i64) != 0 { if len[0] > 0 { which[0] = RVL_WHICH_BUILDROOT; return buf } } 191 buf = sys_read_file(RVL_POLICY_FILE_UP, len) 192 if (buf as i64) != 0 { if len[0] > 0 { which[0] = RVL_WHICH_UP; return buf } } 193 len[0] = 0 194 return 0 as *u8 195} 196// THE DECISION: the effective record for a class. The built-in row and the file row are both consulted and the STRICTER 197// mode wins; an adult-rated class is never open; a class neither declares is gated and flagged unknown (returns 0). 198// rec spans point into srcbuf[0] (whichever buffer supplied the label) 199func rvl_decide(file: *u8, fn: i64, cls: *u8, rec: *i64, srcbuf: *i64) -> i64 { 200 let rb: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 201 let rf: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 202 let hb: i64 = rvl_find(RVL_BUILTIN, rvl_slen(RVL_BUILTIN), cls, rb) 203 var hf: i64 = 0 204 if (file as i64) != 0 { if fn > 0 { hf = rvl_find(file, fn, cls, rf) } } 205 if hb == 0 { if hf == 0 { 206 rec[RVL_L_FOUND] = 0 207 rec[RVL_L_MODE] = RVL_MODE_GATED 208 rec[RVL_L_RATING] = RVL_RATING_ADULT 209 rec[RVL_L_SRC_S] = 0; rec[RVL_L_SRC_E] = 0 210 rec[RVL_L_LAB_S] = 0; rec[RVL_L_LAB_E] = rvl_slen(RVL_UNKNOWN_LABEL) 211 srcbuf[0] = RVL_UNKNOWN_LABEL as i64 212 return 0 213 } } 214 var mode: i64 = RVL_MODE_OPEN 215 var rating: i64 = RVL_RATING_GENERAL 216 if hb == 1 { mode = rb[RVL_L_MODE]; rating = rb[RVL_L_RATING] } 217 if hf == 1 { 218 if rf[RVL_L_MODE] > mode { mode = rf[RVL_L_MODE] } 219 if hb == 0 { mode = rf[RVL_L_MODE] } 220 if rf[RVL_L_RATING] == RVL_RATING_ADULT { rating = RVL_RATING_ADULT } 221 } 222 if rating == RVL_RATING_ADULT { if mode == RVL_MODE_OPEN { mode = RVL_MODE_CLICK } } 223 rec[RVL_L_FOUND] = 1 224 rec[RVL_L_MODE] = mode 225 rec[RVL_L_RATING] = rating 226 // the label and source come from the file row when it has one (the operator's words), else the built-in row 227 if hf == 1 { 228 rec[RVL_L_SRC_S] = rf[RVL_L_SRC_S]; rec[RVL_L_SRC_E] = rf[RVL_L_SRC_E] 229 rec[RVL_L_LAB_S] = rf[RVL_L_LAB_S]; rec[RVL_L_LAB_E] = rf[RVL_L_LAB_E] 230 srcbuf[0] = file as i64 231 } else { 232 rec[RVL_L_SRC_S] = rb[RVL_L_SRC_S]; rec[RVL_L_SRC_E] = rb[RVL_L_SRC_E] 233 rec[RVL_L_LAB_S] = rb[RVL_L_LAB_S]; rec[RVL_L_LAB_E] = rb[RVL_L_LAB_E] 234 srcbuf[0] = RVL_BUILTIN as i64 235 } 236 return 1 237} 238func rvl_mode(file: *u8, fn: i64, cls: *u8) -> i64 { 239 let rec: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 240 let sb: *i64 = sys_mmap(RVL_I64) as *i64 241 rvl_decide(file, fn, cls, rec, sb) 242 return rec[RVL_L_MODE] 243} 244func rvl_rating(file: *u8, fn: i64, cls: *u8) -> i64 { 245 let rec: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 246 let sb: *i64 = sys_mmap(RVL_I64) as *i64 247 rvl_decide(file, fn, cls, rec, sb) 248 if rec[RVL_L_MODE] == RVL_MODE_GATED { return RVL_RATING_GENERAL } // a gated item puts nothing on the page 249 return rec[RVL_L_RATING] 250} 251func rvl_mode_name(m: i64) -> *u8 { 252 if m == RVL_MODE_OPEN { return RVL_W_OPEN } 253 if m == RVL_MODE_CLICK { return RVL_W_CLICK } 254 return RVL_W_GATED 255} 256func rvl_tally_clear(t: *i64) -> i64 { var i: i64 = 0; while i < RVL_T_N { t[i] = 0; i = i + 1 } return 0 } 257 258// the page head: the rating meta when any emitted item is adult-rated (pass the tally of a dry pass, or the OR of 259// rvl_rating over the rows the page will carry) 260func rvl_head_meta(out: *u8, o: i64, cap: i64, any_adult: i64) -> i64 { 261 if any_adult == 0 { return o } 262 return rvl_cat(out, o, cap, "<meta name=\"rating\" content=\"adult\">\n" as *u8) 263} 264// the stylesheet of the disclosure: colours are the house tokens with a fallback, so a page that defines the tokens 265// themes the block and a bare page still reads 266func rvl_css(out: *u8, o: i64, cap: i64) -> i64 { 267 var p: i64 = rvl_cat(out, o, cap, ".nx-reveal{border:1px solid var(--nx-line,rgb(42,51,64));border-radius:10px;background:var(--nx-panel,rgb(18,24,33));margin:.6rem 0}\n" as *u8) 268 p = rvl_cat(out, p, cap, ".nx-reveal>summary{cursor:pointer;list-style:none;display:flex;flex-wrap:wrap;gap:.5rem .8rem;align-items:baseline;padding:.7rem .9rem}\n" as *u8) 269 p = rvl_cat(out, p, cap, ".nx-reveal>summary::-webkit-details-marker{display:none}\n" as *u8) 270 p = rvl_cat(out, p, cap, ".nx-reveal>summary:focus-visible{outline:2px solid var(--nx-accent,rgb(124,196,255));outline-offset:2px;border-radius:8px}\n" as *u8) 271 p = rvl_cat(out, p, cap, ".nx-reveal-act{font-weight:600;border:1px solid currentColor;border-radius:999px;padding:.1rem .7rem;white-space:nowrap}\n" as *u8) 272 p = rvl_cat(out, p, cap, ".nx-reveal-act::before{content:\"Open\"}.nx-reveal[open] .nx-reveal-act::before{content:\"Close\"}\n" as *u8) 273 p = rvl_cat(out, p, cap, ".nx-reveal-cls{opacity:.85}.nx-reveal[data-rating=adult] .nx-reveal-cls{color:var(--nx-warn,rgb(255,180,84));font-weight:600}\n" as *u8) 274 p = rvl_cat(out, p, cap, ".nx-reveal-cap{opacity:.7;flex-basis:100%}\n" as *u8) 275 p = rvl_cat(out, p, cap, ".nx-reveal figure,.nx-open{margin:0;padding:0 .9rem .9rem}.nx-reveal img,.nx-open img{max-width:100%;height:auto;display:block;border-radius:6px}\n" as *u8) 276 p = rvl_cat(out, p, cap, ".nx-gated{border:1px dashed var(--nx-line,rgb(42,51,64));border-radius:10px;padding:.7rem .9rem;opacity:.8}\n" as *u8) 277 return p 278} 279// a gated item: a named placeholder. The href is deliberately not a parameter -- a gated URL cannot leak from a 280// function that never receives it 281func rvl_emit_gated(out: *u8, o: i64, cap: i64, file: *u8, fn: i64, cls: *u8, caption: *u8, tally: *i64) -> i64 { 282 let rec: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 283 let sb: *i64 = sys_mmap(RVL_I64) as *i64 284 let known: i64 = rvl_decide(file, fn, cls, rec, sb) 285 tally[RVL_T_GATED] = tally[RVL_T_GATED] + 1 286 if known == 0 { tally[RVL_T_UNKNOWN] = tally[RVL_T_UNKNOWN] + 1 } 287 var p: i64 = rvl_cat(out, o, cap, "<p class=\"nx-gated\" data-class=\"" as *u8) 288 p = rvl_cat_esc(out, p, cap, cls) 289 p = rvl_cat(out, p, cap, "\"><b>Withheld from this public page.</b> " as *u8) 290 p = rvl_cat_span_esc(out, p, cap, sb[0] as *u8, rec[RVL_L_LAB_S], rec[RVL_L_LAB_E]) 291 p = rvl_cat(out, p, cap, ": " as *u8) 292 p = rvl_cat_esc(out, p, cap, caption) 293 p = rvl_cat(out, p, cap, "</p>\n" as *u8) 294 return p 295} 296// open a block for a non-gated item: a closed disclosure (click) or an open figure. The consumer writes its own media 297// tag between rvl_block_open and rvl_block_close. Callers ask rvl_mode FIRST and send gated items to rvl_emit_gated; 298// a gated class that reaches this function anyway is written as a gated placeholder and returns with mode_out[0] = 299// RVL_MODE_GATED so the consumer knows to write NO media and NO close 300func rvl_block_open(out: *u8, o: i64, cap: i64, file: *u8, fn: i64, cls: *u8, caption: *u8, tally: *i64, mode_out: *i64) -> i64 { 301 let rec: *i64 = sys_mmap(RVL_L_N * RVL_I64) as *i64 302 let sb: *i64 = sys_mmap(RVL_I64) as *i64 303 rvl_decide(file, fn, cls, rec, sb) 304 mode_out[0] = rec[RVL_L_MODE] 305 if rec[RVL_L_MODE] == RVL_MODE_GATED { return rvl_emit_gated(out, o, cap, file, fn, cls, caption, tally) } 306 if rec[RVL_L_RATING] == RVL_RATING_ADULT { tally[RVL_T_ADULT] = tally[RVL_T_ADULT] + 1 } 307 var p: i64 = o 308 if rec[RVL_L_MODE] == RVL_MODE_OPEN { 309 tally[RVL_T_OPEN] = tally[RVL_T_OPEN] + 1 310 p = rvl_cat(out, p, cap, RVL_FIG_OPEN) 311 } else { 312 tally[RVL_T_CLICK] = tally[RVL_T_CLICK] + 1 313 p = rvl_cat(out, p, cap, RVL_BLOCK_OPEN) 314 } 315 p = rvl_cat(out, p, cap, " data-class=\"" as *u8) 316 p = rvl_cat_esc(out, p, cap, cls) 317 p = rvl_cat(out, p, cap, "\" data-rating=\"" as *u8) 318 if rec[RVL_L_RATING] == RVL_RATING_ADULT { p = rvl_cat(out, p, cap, RVL_W_ADULT) } else { p = rvl_cat(out, p, cap, "general" as *u8) } 319 p = rvl_cat(out, p, cap, "\" data-digital-source-type=\"" as *u8) 320 p = rvl_cat_span_esc(out, p, cap, sb[0] as *u8, rec[RVL_L_SRC_S], rec[RVL_L_SRC_E]) 321 p = rvl_cat(out, p, cap, "\">" as *u8) 322 if rec[RVL_L_MODE] == RVL_MODE_CLICK { 323 p = rvl_cat(out, p, cap, "<summary><span class=\"nx-reveal-act\"></span><span class=\"nx-reveal-cls\">" as *u8) 324 p = rvl_cat_span_esc(out, p, cap, sb[0] as *u8, rec[RVL_L_LAB_S], rec[RVL_L_LAB_E]) 325 p = rvl_cat(out, p, cap, "</span><span class=\"nx-reveal-cap\">" as *u8) 326 p = rvl_cat_esc(out, p, cap, caption) 327 p = rvl_cat(out, p, cap, "</span></summary><figure>" as *u8) 328 } 329 return p 330} 331func rvl_block_close(out: *u8, o: i64, cap: i64, mode: i64, caption: *u8) -> i64 { 332 if mode == RVL_MODE_GATED { return o } 333 var p: i64 = rvl_cat(out, o, cap, "<figcaption>" as *u8) 334 p = rvl_cat_esc(out, p, cap, caption) 335 p = rvl_cat(out, p, cap, "</figcaption>" as *u8) 336 if mode == RVL_MODE_CLICK { p = rvl_cat(out, p, cap, "</figure>" as *u8); p = rvl_cat(out, p, cap, RVL_BLOCK_CLOSE) } else { p = rvl_cat(out, p, cap, RVL_FIG_CLOSE) } 337 return rvl_cat(out, p, cap, "\n" as *u8) 338} 339// one image, whole: the block, the lazy image, the caption. alt is the image's text alternative 340func rvl_emit_img(out: *u8, o: i64, cap: i64, file: *u8, fn: i64, cls: *u8, href: *u8, alt: *u8, caption: *u8, tally: *i64) -> i64 { 341 let md: *i64 = sys_mmap(RVL_I64) as *i64 342 var p: i64 = rvl_block_open(out, o, cap, file, fn, cls, caption, tally, md) 343 if md[0] == RVL_MODE_GATED { return p } 344 p = rvl_cat(out, p, cap, "<img src=\"" as *u8) 345 p = rvl_cat_esc(out, p, cap, href) 346 p = rvl_cat(out, p, cap, "\" alt=\"" as *u8) 347 p = rvl_cat_esc(out, p, cap, alt) 348 p = rvl_cat(out, p, cap, "\" loading=\"lazy\" decoding=\"async\">" as *u8) 349 return rvl_block_close(out, p, cap, md[0], caption) 350} 351 352// does html[i..] start with the literal 353func rvl_at(html: *u8, n: i64, i: i64, lit: *u8) -> i64 { 354 let m: i64 = rvl_slen(lit) 355 if i + m > n { return 0 } 356 var k: i64 = 0 357 while k < m { if html[i + k] != lit[k] { return 0 } k = k + 1 } 358 return 1 359} 360func rvl_media_tag_at(html: *u8, n: i64, i: i64) -> i64 { 361 if rvl_at(html, n, i, "<img" as *u8) == 1 { return 1 } 362 if rvl_at(html, n, i, "<video" as *u8) == 1 { return 1 } 363 if rvl_at(html, n, i, "<canvas" as *u8) == 1 { return 1 } 364 if rvl_at(html, n, i, "<object" as *u8) == 1 { return 1 } 365 if rvl_at(html, n, i, "<embed" as *u8) == 1 { return 1 } 366 if rvl_at(html, n, i, "<iframe" as *u8) == 1 { return 1 } 367 if rvl_at(html, n, i, "<picture" as *u8) == 1 { return 1 } 368 return 0 369} 370// THE VERIFIER: every media tag of an emitted page is counted as covered (inside a reveal block), open (inside an open 371// figure, a class the policy allows inline) or BARE. Returns the bare count; a consumer's gate holds it at zero 372func rvl_audit(html: *u8, n: i64, rep: *i64) -> i64 { 373 var k: i64 = 0 374 while k < RVL_A_N { rep[k] = 0; k = k + 1 } 375 var in_block: i64 = 0 376 var in_fig: i64 = 0 377 var i: i64 = 0 378 while i < n { 379 if ((html[i] as i64) & RVL_BYTE) == RVL_LT { 380 if rvl_at(html, n, i, RVL_BLOCK_OPEN) == 1 { in_block = 1; rep[RVL_A_BLOCKS] = rep[RVL_A_BLOCKS] + 1 } 381 if rvl_at(html, n, i, RVL_BLOCK_CLOSE) == 1 { in_block = 0 } 382 if rvl_at(html, n, i, RVL_FIG_OPEN) == 1 { in_fig = 1 } 383 if rvl_at(html, n, i, RVL_FIG_CLOSE) == 1 { if in_block == 0 { in_fig = 0 } } 384 if rvl_media_tag_at(html, n, i) == 1 { 385 if in_block == 1 { rep[RVL_A_COVERED] = rep[RVL_A_COVERED] + 1 } else { 386 if in_fig == 1 { rep[RVL_A_OPEN] = rep[RVL_A_OPEN] + 1 } else { rep[RVL_A_BARE] = rep[RVL_A_BARE] + 1 } 387 } 388 } 389 } 390 i = i + 1 391 } 392 return rep[RVL_A_BARE] 393}