Team Ai
Apppublic

dwrswr/cpp-module-06-guide

sourceHugging Facemitupdated 2mo agoView on Hugging Face
0likes
app.js573 linesDownload Raw Back to assets
1/* ============================================================================
2   CPP Module 06 — guide runtime.
3   No dependencies, no build step. Everything below is hand-written:
4   theme, key-based i18n, a C++/Make tokeniser, note-to-line linking,
5   scroll-spy, and the three interactive widgets.
6   ========================================================================== */
7(function () {
8  "use strict";
9
10  var BS = String.fromCharCode(92);            /* a backslash, without escaping */
11  var $  = function (s, r) { return (r || document).querySelector(s); };
12  var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
13  var store = {
14    get: function (k, d) { try { var v = localStorage.getItem(k); return v === null ? d : v; } catch (e) { return d; } },
15    set: function (k, v) { try { localStorage.setItem(k, v); } catch (e) { } }
16  };
17
18  /* ---------------------------------------------------------------- theme */
19  var root = document.documentElement;
20  function setTheme(t) {
21    root.setAttribute("data-theme", t);
22    store.set("cpp06-theme", t);
23    $$("[data-theme-btn]").forEach(function (b) {
24      var on = b.getAttribute("data-theme-btn") === t;
25      b.classList.toggle("on", on);
26      b.setAttribute("aria-pressed", on ? "true" : "false");
27    });
28  }
29  var saved = store.get("cpp06-theme", null);
30  if (!saved) {
31    saved = (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches)
32      ? "light" : "dark";
33  }
34
35  /* ----------------------------------------------------------------- i18n */
36  /* Keys, not positions: adding or reordering a paragraph can never shift a
37     translation the way an index-based dictionary does.                    */
38  var EN = {};
39  function paint(lang) {
40    var D = (lang === "ar") ? (window.I18N_AR || {}) : null;
41    $$("[data-i18n]").forEach(function (el) {
42      var k = el.getAttribute("data-i18n");
43      if (EN[k] === undefined) EN[k] = el.innerHTML;
44      var v = D ? D[k] : undefined;
45      el.innerHTML = (typeof v === "string" && v.length) ? v : EN[k];
46    });
47    $$("[data-i18n-ph]").forEach(function (el) {
48      var k = el.getAttribute("data-i18n-ph");
49      if (EN["ph:" + k] === undefined) EN["ph:" + k] = el.getAttribute("placeholder") || "";
50      var v = D ? D["ph:" + k] : undefined;
51      el.setAttribute("placeholder", (typeof v === "string" && v.length) ? v : EN["ph:" + k]);
52    });
53    if (EN["#title"] === undefined) EN["#title"] = document.title;
54    var t = D ? D["meta.title"] : undefined;
55    document.title = (typeof t === "string" && t.length) ? t : EN["#title"];
56    root.setAttribute("lang", lang === "ar" ? "ar" : "en");
57    root.setAttribute("dir", lang === "ar" ? "rtl" : "ltr");
58    $$("[data-lang-btn]").forEach(function (b) {
59      b.classList.toggle("on", b.getAttribute("data-lang-btn") === lang);
60    });
61    store.set("cpp06-lang", lang);
62    if (window.__afterPaint) window.__afterPaint(lang);
63  }
64
65  /* ------------------------------------------------------- tokeniser (C++) */
66  var KEY = ("class struct enum public private protected static const virtual return if else while "
67    + "for switch case default break continue new delete try catch throw namespace using typedef "
68    + "sizeof true false this operator inline explicit friend template typename").split(" ");
69  var TYP = ("void char int float double bool unsigned signed long short size_t uintptr_t std string "
70    + "ostream e_type NULL").split(" ");
71  var CAST = { static_cast: "t-cs", reinterpret_cast: "t-cr", dynamic_cast: "t-cd", const_cast: "t-ck" };
72  var KS = {}, TS = {};
73  KEY.forEach(function (k) { KS[k] = 1; });
74  TYP.forEach(function (k) { TS[k] = 1; });
75
76  function esc(s) {
77    return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
78  }
79  function sp(cls, txt) { return '<span class="' + cls + '">' + esc(txt) + "</span>"; }
80  function isAl(c) { return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_"; }
81  function isDg(c) { return c >= "0" && c <= "9"; }
82
83  function cppLine(line, st) {
84    var out = "", i = 0, n = line.length, c, j, w;
85    if (st.blk) {                                  /* continuing a block comment */
86      j = line.indexOf("*/");
87      if (j < 0) return { html: sp("t-com", line), st: st };
88      out += sp("t-com", line.slice(0, j + 2));
89      i = j + 2; st.blk = false;
90    }
91    while (i < n) {
92      c = line[i];
93      if (c === "/" && line[i + 1] === "/") { out += sp("t-com", line.slice(i)); break; }
94      if (c === "/" && line[i + 1] === "*") {
95        j = line.indexOf("*/", i + 2);
96        if (j < 0) { out += sp("t-com", line.slice(i)); st.blk = true; break; }
97        out += sp("t-com", line.slice(i, j + 2)); i = j + 2; continue;
98      }
99      if (c === '"' || c === "'") {
100        j = i + 1;
101        while (j < n) { if (line[j] === BS) { j += 2; continue; } if (line[j] === c) { j++; break; } j++; }
102        out += sp("t-str", line.slice(i, j)); i = j; continue;
103      }
104      if (c === "#" && line.slice(0, i).trim() === "") {
105        var rest = line.slice(i), m = rest.indexOf("<"), q = rest.indexOf('"');
106        var cut = (m >= 0) ? m : (q >= 0 ? q : -1);
107        if (cut > 0) { out += sp("t-pre", rest.slice(0, cut)) + sp("t-str", rest.slice(cut)); }
108        else { out += sp("t-pre", rest); }
109        break;
110      }
111      if (isAl(c)) {
112        j = i; while (j < n && (isAl(line[j]) || isDg(line[j]))) j++;
113        w = line.slice(i, j);
114        if (CAST[w]) out += sp(CAST[w], w);
115        else if (KS[w]) out += sp("t-key", w);
116        else if (TS[w]) out += sp("t-typ", w);
117        else if (line[j] === "(") out += sp("t-fn", w);
118        else out += esc(w);
119        i = j; continue;
120      }
121      if (isDg(c)) {
122        j = i; while (j < n && (isDg(line[j]) || isAl(line[j]) || line[j] === ".")) j++;
123        out += sp("t-num", line.slice(i, j)); i = j; continue;
124      }
125      if (c === " " || c === "\t") { out += c; i++; continue; }
126      out += sp("t-op", c); i++;
127    }
128    return { html: out, st: st };
129  }
130
131  /* --------------------------------------------- tokeniser (Makefile, shell) */
132  function makeLine(line) {
133    var t = line.replace(/^\s+/, "");
134    if (t.indexOf("#") === 0) return sp("t-com", line);
135    var out = "", i = 0, n = line.length, c, j;
136    while (i < n) {
137      c = line[i];
138      if (c === "$" && line[i + 1] === "(") {
139        j = line.indexOf(")", i);
140        if (j < 0) j = n - 1;
141        out += sp("t-typ", line.slice(i, j + 1)); i = j + 1; continue;
142      }
143      if (isAl(c) || c === ".") {
144        j = i; while (j < n && (isAl(line[j]) || isDg(line[j]) || line[j] === "." || line[j] === "_")) j++;
145        var w = line.slice(i, j), after = line.slice(j).replace(/^[ \t]+/, "");
146        if (i === 0 && after.indexOf(":") === 0) out += sp("t-fn", w);
147        else if (i === 0 && after.indexOf("=") === 0) out += sp("t-key", w);
148        else out += esc(w);
149        i = j; continue;
150      }
151      if (c === "-" && isAl(line[i + 1] || "")) {
152        j = i + 1; while (j < n && (isAl(line[j]) || isDg(line[j]) || line[j] === "-" || line[j] === "=" || line[j] === "+")) j++;
153        out += sp("t-num", line.slice(i, j)); i = j; continue;
154      }
155      out += (c === " " || c === "\t") ? c : sp("t-op", c);
156      i++;
157    }
158    return out;
159  }
160  function shLine(line) {
161    if (line.replace(/^\s+/, "").indexOf("#") === 0) return sp("t-com", line);
162    var out = "", i = 0, n = line.length, c, j;
163    while (i < n) {
164      c = line[i];
165      if (c === '"' || c === "'") {
166        j = i + 1; while (j < n && line[j] !== c) j++;
167        out += sp("t-str", line.slice(i, Math.min(j + 1, n))); i = j + 1; continue;
168      }
169      if (c === "-" && (isAl(line[i + 1] || "") || line[i + 1] === "-")) {
170        j = i; while (j < n && line[j] !== " " && line[j] !== "\t") j++;
171        out += sp("t-num", line.slice(i, j)); i = j; continue;
172      }
173      if (isAl(c)) {
174        j = i; while (j < n && (isAl(line[j]) || isDg(line[j]) || line[j] === "_" || line[j] === "-")) j++;
175        var w = line.slice(i, j);
176        out += (i === 0 || line.slice(0, i).trim() === "" ) ? sp("t-fn", w) : esc(w);
177        i = j; continue;
178      }
179      out += (c === " " || c === "\t") ? c : sp("t-op", c);
180      i++;
181    }
182    return out;
183  }
184
185  /* ------------------------------------------- render every code block once */
186  function renderCode() {
187    $$(".code-body pre").forEach(function (pre) {
188      if (pre.getAttribute("data-done")) return;
189      var lang = pre.getAttribute("data-lang") || "cpp";
190      var src = pre.textContent.replace(/\s+$/, "");
191      var lines = src.split("\n");
192      var st = { blk: false }, html = "";
193      lines.forEach(function (ln, k) {
194        var body;
195        if (lang === "make") body = makeLine(ln);
196        else if (lang === "sh") body = shLine(ln);
197        else { var r = cppLine(ln, st); body = r.html; st = r.st; }
198        html += '<span class="row" data-l="' + (k + 1) + '">'
199             +  '<span class="n">' + (k + 1) + "</span>" + (body || " ") + "\n</span>";
200      });
201      pre.innerHTML = html;
202      pre.setAttribute("data-done", "1");
203      pre.setAttribute("data-src", src);
204    });
205  }
206
207  /* ------------------------------------------------------------ copy button */
208  function wireCopy() {
209    $$(".code").forEach(function (box) {
210      var btn = $(".copy", box), pre = $("pre", box);
211      if (!btn || !pre || btn.getAttribute("data-w")) return;
212      btn.setAttribute("data-w", "1");
213      btn.addEventListener("click", function () {
214        var txt = pre.getAttribute("data-src") || pre.textContent;
215        var done = function () {
216          var old = btn.textContent;
217          btn.textContent = "copied"; btn.classList.add("done");
218          setTimeout(function () { btn.textContent = old; btn.classList.remove("done"); }, 1400);
219        };
220        if (navigator.clipboard && navigator.clipboard.writeText) {
221          navigator.clipboard.writeText(txt).then(done, done);
222        } else {
223          var ta = document.createElement("textarea");
224          ta.value = txt; document.body.appendChild(ta); ta.select();
225          try { document.execCommand("copy"); } catch (e) { }
226          document.body.removeChild(ta); done();
227        }
228      });
229    });
230  }
231
232  /* ----------------------------------- notes <-> code lines (click to focus) */
233  function wireNotes() {
234    $$(".notes").forEach(function (list) {
235      var id = list.getAttribute("data-for");
236      var box = id ? document.getElementById(id) : null;
237      if (!box) return;
238      $$("li", list).forEach(function (li) {
239        li.setAttribute("tabindex", "0");
240        li.setAttribute("role", "button");
241        var want = {};                       /* "9,16-17" -> {9:1,16:1,17:1} */
242        (li.getAttribute("data-line") || "").split(",").forEach(function (part) {
243          var g = part.trim().split("-");
244          var a = parseInt(g[0], 10), b = parseInt(g[1] === undefined ? g[0] : g[1], 10);
245          if (isNaN(a)) return;
246          if (isNaN(b) || b < a) b = a;
247          for (var v = a; v <= b; v++) want[v] = 1;
248        });
249        var hit = function () {
250          var was = li.classList.contains("on");
251          $$("li", list).forEach(function (o) { o.classList.remove("on"); });
252          $$(".row", box).forEach(function (r) { r.classList.remove("hl"); });
253          if (was) return;
254          li.classList.add("on");
255          var first = null;
256          $$(".row", box).forEach(function (r) {
257            if (want[parseInt(r.getAttribute("data-l"), 10)]) {
258              r.classList.add("hl"); if (!first) first = r;
259            }
260          });
261          if (first) {
262            var body = $(".code-body", box);
263            if (body) body.scrollTop = Math.max(0, first.offsetTop - body.clientHeight / 2);
264          }
265        };
266        li.addEventListener("click", hit);
267        li.addEventListener("keydown", function (e) {
268          if (e.key === "Enter" || e.key === " ") { e.preventDefault(); hit(); }
269        });
270      });
271    });
272  }
273
274  /* ------------------------------------------- scroll spy, progress, reveal */
275  function wireScroll() {
276    var links = $$(".toc a"), secs = links.map(function (a) {
277      return document.getElementById(a.getAttribute("href").slice(1));
278    });
279    var prog = $("#prog"), up = $(".up");
280    function onScroll() {
281      var y = window.scrollY || window.pageYOffset;
282      var h = document.documentElement.scrollHeight - window.innerHeight;
283      if (prog) prog.style.width = (h > 0 ? (y / h) * 100 : 0) + "%";
284      if (up) up.classList.toggle("show", y > 700);
285      var best = 0;
286      secs.forEach(function (s, i) { if (s && s.getBoundingClientRect().top - 96 <= 0) best = i; });
287      links.forEach(function (a, i) { a.classList.toggle("on", i === best); });
288    }
289    window.addEventListener("scroll", onScroll, { passive: true });
290    window.addEventListener("resize", onScroll);
291    onScroll();
292  }
293  function wireReveal() {
294    var els = $$(".rv");
295    if (!("IntersectionObserver" in window)) {
296      els.forEach(function (e) { e.classList.add("in"); });
297      return;
298    }
299    var io = new IntersectionObserver(function (rows) {
300      rows.forEach(function (r) { if (r.isIntersecting) { r.target.classList.add("in"); io.unobserve(r.target); } });
301    }, { rootMargin: "0px 0px -8% 0px", threshold: .04 });
302    els.forEach(function (e) { io.observe(e); });
303  }
304
305  /* --------------------------------------------------- widget: cast picker */
306  var PICK = {
307    s: { cls: "s", t: "static_cast&lt;T&gt;", k: "pick.ans.s" },
308    r: { cls: "r", t: "reinterpret_cast&lt;T&gt;", k: "pick.ans.r" },
309    d: { cls: "d", t: "dynamic_cast&lt;T&gt;", k: "pick.ans.d" },
310    k: { cls: "k", t: "const_cast&lt;T&gt;", k: "pick.ans.k" }
311  };
312  function wirePicker() {
313    var box = $("#picker");
314    if (!box) return;
315    var out = $(".pick-out", box);
316    $$("button", box).forEach(function (b) {
317      b.addEventListener("click", function () {
318        var p = PICK[b.getAttribute("data-cast")];
319        if (!p) return;
320        var src = document.getElementById("ans-" + b.getAttribute("data-q"));
321        $$("button", box).forEach(function (o) { o.classList.remove("on"); });
322        b.classList.add("on");
323        out.className = "pick-out show " + p.cls;
324        out.innerHTML = "<h5>" + p.t + "</h5>" + (src ? src.innerHTML : "");
325      });
326    });
327  }
328
329  /* ------------------------------ widget: ScalarConverter, ported from C++ */
330  var CHAR_MIN = -128, CHAR_MAX = 127;
331  var INT_MIN = -2147483648, INT_MAX = 2147483647;
332  var FLT_MAX = 3.4028234663852886e38, DBL_MAX = 1.7976931348623157e308;
333
334  function tr(key, fallback) {
335    var lang = root.getAttribute("lang");
336    var D = (lang === "ar") ? (window.I18N_AR || {}) : null;
337    var v = D ? D[key] : undefined;
338    return (typeof v === "string" && v.length) ? v : fallback;
339  }
340  function isNum(s) {                            /* mirrors isNumber() exactly */
341    var i = 0, digits = 0, dot = false;
342    if (i < s.length && (s[i] === "+" || s[i] === "-")) i++;
343    while (i < s.length) {
344      if (s[i] >= "0" && s[i] <= "9") digits++;
345      else if (s[i] === "." && !dot) dot = true;
346      else return { ok: false, dot: dot };
347      i++;
348    }
349    return { ok: digits > 0, dot: dot };
350  }
351  function detect(s) {                            /* mirrors detect() exactly */
352    if (s.length === 3 && s[0] === "'" && s[2] === "'") return { t: "char", why: "why.quoted" };
353    if (s.length === 1 && !(s[0] >= "0" && s[0] <= "9")) return { t: "char", why: "why.single" };
354    if (s === "nanf" || s === "+inff" || s === "-inff") return { t: "float", why: "why.pseudof" };
355    if (s === "nan" || s === "+inf" || s === "-inf") return { t: "double", why: "why.pseudo" };
356    var a = isNum(s);
357    if (a.ok) return { t: a.dot ? "double" : "int", why: a.dot ? "why.dot" : "why.digits" };
358    if (s.length > 1 && s[s.length - 1] === "f" && isNum(s.slice(0, s.length - 1)).ok)
359      return { t: "float", why: "why.suffix" };
360    return { t: "invalid", why: "why.none" };
361  }
362  function fixed1(v) {
363    if (v !== v) return "nan";
364    if (v === Infinity) return "inf";
365    if (v === -Infinity) return "-inf";
366    if (Math.abs(v) < 1e21) return v.toFixed(1);
367    var parts = v.toExponential(20).split("e");
368    var mant = parts[0], ex = parseInt(parts[1], 10);
369    var neg = mant.charAt(0) === "-";
370    if (neg) mant = mant.slice(1);
371    var d = mant.replace(".", "");
372    while (d.length < ex + 1) d += "0";
373    return (neg ? "-" : "") + d.slice(0, ex + 1) + ".0";
374  }
375  function outChar(v) {
376    if (v !== v || v < CHAR_MIN || v > CHAR_MAX) return { s: "impossible", c: "x" };
377    var c = Math.trunc(v), uc = c < 0 ? c + 256 : c;
378    if (!(uc >= 32 && uc <= 126)) return { s: "Non displayable", c: "y" };
379    return { s: "'" + String.fromCharCode(uc) + "'", c: "v" };
380  }
381  function outInt(v) {
382    if (v !== v || v < INT_MIN || v > INT_MAX) return { s: "impossible", c: "x" };
383    return { s: String(Math.trunc(v)), c: "v" };
384  }
385  function outFloat(v, pseudo) {
386    if (!pseudo && (v !== v || v < -FLT_MAX || v > FLT_MAX)) return { s: "impossible", c: "x" };
387    return { s: fixed1(Math.fround(v)) + "f", c: "v" };
388  }
389  function outDouble(v, pseudo) {
390    if (!pseudo && (v !== v || v < -DBL_MAX || v > DBL_MAX)) return { s: "impossible", c: "x" };
391    return { s: fixed1(v), c: "v" };
392  }
393
394  var WHY = {
395    "why.quoted": "length is 3 and it is wrapped in single quotes, so it is a char literal.",
396    "why.single": "one character that is not a digit, so it is treated as a char literal.",
397    "why.pseudof": "an exact float pseudo-literal.",
398    "why.pseudo": "an exact double pseudo-literal.",
399    "why.dot": "sign, digits and exactly one dot, so it is a double.",
400    "why.digits": "sign and digits with no dot, so it is an int.",
401    "why.suffix": "drops the trailing f and the rest is a valid decimal number, so it is a float.",
402    "why.none": "no branch of detect() matches it."
403  };
404  function wireSim() {
405    var box = $("#sim");
406    if (!box) return;
407    var input = $(".sim-in", box), out = $(".sim-out", box), steps = $(".steps", box);
408    function run() {
409      var s = input.value;
410      var d = detect(s), value = 0, pseudo = false, body = s;
411      var head = '<span class="k">$ ./convert "' + esc(s) + '"</span>\n';
412      if (d.t === "invalid") {
413        out.innerHTML = head + '<span class="x">' + esc(s)
414          + " is not a char, an int, a float nor a double.</span>";
415        steps.innerHTML = "<li><b>detect()</b> &rarr; <code>INVALID_LIT</code> &mdash; "
416          + tr(d.why, WHY[d.why]) + "</li><li>"
417          + tr("sim.stepbail", "convert() prints the error line and returns before any cast runs.")
418          + "</li>";
419        return;
420      }
421      if (d.t === "char") {
422        value = (s.length === 3 ? s.charCodeAt(1) : s.charCodeAt(0));
423      } else {
424        if (d.t === "float") body = s.slice(0, s.length - 1);
425        pseudo = (body === "nan" || body === "+inf" || body === "-inf");
426        if (pseudo) value = (body === "nan") ? NaN : (body === "+inf" ? Infinity : -Infinity);
427        else value = Number(body);          /* std::strtod on a decimal literal */
428      }
429      var c = outChar(value), i = outInt(value), f = outFloat(value, pseudo), b = outDouble(value, pseudo);
430      out.innerHTML = head
431        + '<span class="k">char:   </span><span class="' + c.c + '">' + esc(c.s) + "</span>\n"
432        + '<span class="k">int:    </span><span class="' + i.c + '">' + esc(i.s) + "</span>\n"
433        + '<span class="k">float:  </span><span class="' + f.c + '">' + esc(f.s) + "</span>\n"
434        + '<span class="k">double: </span><span class="' + b.c + '">' + esc(b.s) + "</span>";
435      var TY = { char: "CHAR_LIT", int: "INT_LIT", float: "FLOAT_LIT", double: "DOUBLE_LIT" };
436      var h = "<li><b>detect()</b> &rarr; <code>" + TY[d.t] + "</code> &mdash; "
437            + tr(d.why, WHY[d.why]) + "</li>";
438      if (d.t === "char") {
439        h += "<li>" + tr("sim.stepchar", "The character is promoted to its code point: value = ")
440           + "<code>" + value + "</code>.</li>";
441      } else {
442        h += "<li>" + tr("sim.stepnum", "The numeric text is parsed once with <code>std::strtod</code>: value = ")
443           + "<code>" + (pseudo ? String(value) : body) + "</code>"
444           + (pseudo ? " <b>(pseudo-literal, range checks skipped)</b>" : "") + ".</li>";
445      }
446      h += "<li>" + tr("sim.stepcast", "One <code>double</code> is then cast four times with "
447           + "<code>static_cast</code>; each printer rejects what will not fit.") + "</li>";
448      steps.innerHTML = h;
449    }
450    input.addEventListener("input", run);
451    $$(".presets button", box).forEach(function (b) {
452      b.addEventListener("click", function () {
453        input.value = b.getAttribute("data-v");
454        run(); input.focus();
455      });
456    });
457    window.__simRun = run;
458    run();
459  }
460
461  /* ------------------------------------------------- widget: dynamic_cast lab */
462  function wireDyn() {
463    var box = $("#dyn");
464    if (!box) return;
465    var out = $(".sim-out", box), steps = $(".steps", box);
466    var real = "A";
467    function run(target, mode) {
468      var hitP = "", hitR = "";
469      var order = ["A", "B", "C"];
470      var lines = [];
471      order.forEach(function (t) {
472        var ok = (t === real);
473        if (mode === "ptr") {
474          lines.push('<span class="k">dynamic_cast&lt;' + t + ' *&gt;(p)  &rarr; </span>'
475            + (ok ? '<span class="v">' + real + ' *</span>' : '<span class="x">NULL</span>'));
476        } else {
477          lines.push('<span class="k">dynamic_cast&lt;' + t + ' &amp;&gt;(p)  &rarr; </span>'
478            + (ok ? '<span class="v">' + real + ' &amp;</span>' : '<span class="y">throws std::bad_cast</span>'));
479        }
480        if (ok && !hitP) { hitP = t; hitR = t; }
481      });
482      out.innerHTML = '<span class="k">// real object: new ' + real + "()</span>\n"
483        + lines.join("\n") + '\n<span class="k">printed: </span><span class="v">' + real + "</span>";
484      steps.innerHTML = (mode === "ptr")
485        ? "<li>" + tr("dyn.ptr1", "The pointer form is <b>total</b>: a wrong guess is not an error, it is a "
486            + "<code>NULL</code> pointer, so <code>if (dynamic_cast&lt;A *&gt;(p))</code> is the whole test.") + "</li>"
487          + "<li>" + tr("dyn.ptr2", "Three guesses, at most one truthy. That is why the code is a plain if / else-if chain.") + "</li>"
488        : "<li>" + tr("dyn.ref1", "There is no null reference, so the reference form reports failure the only way it can: "
489            + "it <b>throws</b> <code>std::bad_cast</code>.") + "</li>"
490          + "<li>" + tr("dyn.ref2", "Each guess therefore needs its own <code>try</code> / <code>catch</code> block, "
491            + "and catching <code>std::exception &amp;</code> keeps <code>&lt;typeinfo&gt;</code> out of the file.") + "</li>";
492    }
493    $$("[data-real]", box).forEach(function (b) {
494      b.addEventListener("click", function () {
495        real = b.getAttribute("data-real");
496        $$("[data-real]", box).forEach(function (o) { o.classList.remove("on"); });
497        b.classList.add("on");
498        run(real, $(".on[data-mode]", box) ? $(".on[data-mode]", box).getAttribute("data-mode") : "ptr");
499      });
500    });
501    $$("[data-mode]", box).forEach(function (b) {
502      b.addEventListener("click", function () {
503        $$("[data-mode]", box).forEach(function (o) { o.classList.remove("on"); });
504        b.classList.add("on");
505        run(real, b.getAttribute("data-mode"));
506      });
507    });
508    window.__dynRun = function () { run(real, $(".on[data-mode]", box).getAttribute("data-mode")); };
509    run("A", "ptr");
510  }
511
512  /* ------------------------------------------------------ widget: checklist */
513  function wireChecks() {
514    var wrap = $("#checklist");
515    if (!wrap) return;
516    var state = {};
517    try { state = JSON.parse(store.get("cpp06-chk", "{}")) || {}; } catch (e) { state = {}; }
518    var boxes = $$(".chk", wrap), fill = $(".fill", wrap), num = $(".count", wrap);
519    function tally() {
520      var done = boxes.filter(function (b) { return b.classList.contains("done"); }).length;
521      if (fill) fill.style.width = (boxes.length ? (done / boxes.length) * 100 : 0) + "%";
522      if (num) num.textContent = done + " / " + boxes.length;
523    }
524    boxes.forEach(function (b, i) {
525      var cb = $("input", b), key = b.getAttribute("data-k") || ("c" + i);
526      if (state[key]) { cb.checked = true; b.classList.add("done"); }
527      cb.addEventListener("change", function () {
528        b.classList.toggle("done", cb.checked);
529        state[key] = cb.checked;
530        store.set("cpp06-chk", JSON.stringify(state));
531        tally();
532      });
533    });
534    var rst = $(".reset", wrap);
535    if (rst) rst.addEventListener("click", function () {
536      state = {}; store.set("cpp06-chk", "{}");
537      boxes.forEach(function (b) { $("input", b).checked = false; b.classList.remove("done"); });
538      tally();
539    });
540    tally();
541  }
542
543  /* ----------------------------------------------------------------- boot */
544  function boot() {
545    setTheme(saved);
546    renderCode();
547    wireCopy();
548    wireNotes();
549    wirePicker();
550    wireSim();
551    wireDyn();
552    wireChecks();
553    wireScroll();
554    wireReveal();
555
556    $$("[data-theme-btn]").forEach(function (b) {
557      b.addEventListener("click", function () { setTheme(b.getAttribute("data-theme-btn")); });
558    });
559    $$("[data-lang-btn]").forEach(function (b) {
560      b.addEventListener("click", function () { paint(b.getAttribute("data-lang-btn")); });
561    });
562
563    /* re-render the widgets whose text comes from the dictionary */
564    window.__afterPaint = function () {
565      if (window.__simRun) window.__simRun();
566      if (window.__dynRun) window.__dynRun();
567    };
568    paint(store.get("cpp06-lang", "en") === "ar" ? "ar" : "en");
569  }
570  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot);
571  else boot();
572})();
573