dwrswr/cpp-module-06-guide
0
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, "&").replace(/</g, "<").replace(/>/g, ">");
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<T>", k: "pick.ans.s" },
308 r: { cls: "r", t: "reinterpret_cast<T>", k: "pick.ans.r" },
309 d: { cls: "d", t: "dynamic_cast<T>", k: "pick.ans.d" },
310 k: { cls: "k", t: "const_cast<T>", 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> → <code>INVALID_LIT</code> — "
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> → <code>" + TY[d.t] + "</code> — "
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<' + t + ' *>(p) → </span>'
475 + (ok ? '<span class="v">' + real + ' *</span>' : '<span class="x">NULL</span>'));
476 } else {
477 lines.push('<span class="k">dynamic_cast<' + t + ' &>(p) → </span>'
478 + (ok ? '<span class="v">' + real + ' &</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<A *>(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 &</code> keeps <code><typeinfo></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 