diff --git a/presentations/ac2024/Templates/Lato-Bold.woff b/presentations/ac2024/Templates/Lato-Bold.woff
new file mode 100644
index 0000000..4ade1ca
Binary files /dev/null and b/presentations/ac2024/Templates/Lato-Bold.woff differ
diff --git a/presentations/ac2024/Templates/Lato-BoldItalic.woff b/presentations/ac2024/Templates/Lato-BoldItalic.woff
new file mode 100644
index 0000000..ed1f24b
Binary files /dev/null and b/presentations/ac2024/Templates/Lato-BoldItalic.woff differ
diff --git a/presentations/ac2024/Templates/Lato-Italic.woff b/presentations/ac2024/Templates/Lato-Italic.woff
new file mode 100644
index 0000000..6e8210b
Binary files /dev/null and b/presentations/ac2024/Templates/Lato-Italic.woff differ
diff --git a/presentations/ac2024/Templates/Lato-Regular.woff b/presentations/ac2024/Templates/Lato-Regular.woff
new file mode 100644
index 0000000..a1cb857
Binary files /dev/null and b/presentations/ac2024/Templates/Lato-Regular.woff differ
diff --git a/presentations/ac2024/Templates/Montserrat-Black.woff b/presentations/ac2024/Templates/Montserrat-Black.woff
new file mode 100644
index 0000000..0143ba1
Binary files /dev/null and b/presentations/ac2024/Templates/Montserrat-Black.woff differ
diff --git a/presentations/ac2024/Templates/Montserrat-BlackItalic.woff b/presentations/ac2024/Templates/Montserrat-BlackItalic.woff
new file mode 100644
index 0000000..d04ea2b
Binary files /dev/null and b/presentations/ac2024/Templates/Montserrat-BlackItalic.woff differ
diff --git a/presentations/ac2024/Templates/Montserrat-Bold.woff b/presentations/ac2024/Templates/Montserrat-Bold.woff
new file mode 100644
index 0000000..5c96097
Binary files /dev/null and b/presentations/ac2024/Templates/Montserrat-Bold.woff differ
diff --git a/presentations/ac2024/Templates/Montserrat-BoldItalic.woff b/presentations/ac2024/Templates/Montserrat-BoldItalic.woff
new file mode 100644
index 0000000..2e87b5b
Binary files /dev/null and b/presentations/ac2024/Templates/Montserrat-BoldItalic.woff differ
diff --git a/presentations/ac2024/Templates/b6plus.js b/presentations/ac2024/Templates/b6plus.js
new file mode 100644
index 0000000..66fca5e
--- /dev/null
+++ b/presentations/ac2024/Templates/b6plus.js
@@ -0,0 +1,2323 @@
+/* b6plus.js $Revision: 1.95 $
+ *
+ * Script to simulate projection mode on browsers that don't support
+ * media=projection or 'overflow-block: paged' (or ‘overflow-block:
+ * optional-paged’, from the 2014 Media Queries draft) but do support
+ * Javascript.
+ *
+ * Documentation and latest version:
+ *
+ * https://www.w3.org/Talks/Tools/b6plus/
+ *
+ * Brief usage instructions:
+ *
+ * Add the script to a page with
+ *
+ *
+ *
+ * The script assumes each slide starts with an H1 or an element with
+ * class "slide", which is a direct child of the BODY. All elements
+ * until the next H1 or class "slide" are part of the slide, except
+ * for those with a class of "comment", which are hidden in slide
+ * mode.
+ *
+ * Elements with a class of "progress", "slidenum" or "numslides" are
+ * treated specially. They can be used to display progress in the
+ * slide show, as follows. Elements with a class of "numslides" will
+ * have their content replaced by the total number of slides in
+ * decimal. Elements with a class of "slidenum" will have their
+ * content replaced by the number of the currently displayed slide in
+ * decimal. The first slide is numbered 1. Elements with a class of
+ * "progress" will get a 'width' property whose value is a percentage
+ * between 0% and 100%, corresponding to the progress in the slide
+ * show: if there are M slide in total and the currently displayed
+ * slide is number N, the 'width' property will be N/M * 100%.
+ *
+ * There can be as many of these elements as desired. If they are
+ * defined as children of the BODY, they will be visible all the time.
+ * Otherwise their visibility depends on their parent.
+ *
+ * Usage:
+ *
+ * - Press A to toggle normal and slide mode. The script starts in
+ * normal mode.
+ *
+ * - Press Page-Down to go to the next slide. Press Page-Up, up arrow
+ * or left arrow to back-up one page.
+ *
+ * - Press Space, right arrow, down arrow or mouse button 1 to advance
+ * (incremental display or next slide)
+ *
+ * On touch screens, a tap with three fingers toggles slide mode, a
+ * wipe right goes back one slide, and wipe left advances.
+ *
+ * TODO: There is a proposal for HTML5 to allow fullscreen popup
+ * windows, maybe via Window.open(url, "window title",
+ * "popup,fullscreen") maybe via Element.Requestfullscreen(), and
+ * possibly with a way to select the screen to pop up on, if there are
+ * several. If that gets implemented, and we detect that there is
+ * another screen, we could present a button to open the slide show
+ * directly in fullscreen on the other screen.
+ *
+ * TODO: don't do anything if media = projection
+ *
+ * TODO: option to allow clicking in the left third of a slide to go
+ * back?
+ *
+ * TODO: Accessibility of the second window.
+ *
+ * TODO: Show an icon in the corner when sync mode is on?
+ *
+ * TODO: Allow a language for localized messages and clocks that is
+ * different from the slides' language?
+ *
+ * TODO: More or other syntaxes for commands in syncSlide()? "all" or
+ * "index" for "0"; "<", "previous" for "-"; ">", "next" for "+";
+ * "last" for "$"...
+ *
+ * Originally derived from code by Dave Raggett.
+ *
+ * Author: Bert Bos
+ * Created: May 23, 2005 (b5)
+ * Modified: Jan 2012 (b5 -> b6)
+ * Modified: Oct 2016 (added jump to ID; fixes bugs with Home/End key handling)
+ * Modified: Apr 2018 (added touch events)
+ * Modified: May 2018 (support 'overflow-block' from Media Queries 4)
+ * Modified: Mar 2019 (support fixed aspect ratio, progress elements, b6 -> b6+)
+ * Modified: Aug 2020 (add class=visited to past elts in incremental display)
+ * Modified: Oct 2020 (start in slide mode if URL contains "?full")
+ * Modified: Apr 2021 (disable navigation if URL contains ‘?static’)
+ * Modified: May 2021 (rescale if window size changes while in slide mode)
+ * Modified: Jun 2021 (only one incremental item active, as in Shower since 3.1)
+ * Modified: Sep 2021 (a11y: added role=application and a live region)
+ * Modified: Dec 2021 (added noclick option; set slide number in URL if no ID)
+ * Modified: Dec 2021 (Added popup help tied to the "?" key)
+ * Modified: Apr 2022 (Added support for a second window, tied to the "2" key)
+ * Modified: Apr 2022 (forwarding of events in the second window to the first)
+ * Modified: Aug 2022 (help popup appears in the 2nd window if requested there)
+ * Modified: Nov 2022 (support server-sent events to sync slides)
+ * Modified: Nov 2022 (added clocks; localized to German, French and Dutch)
+ * Modified: Dec 2022 (protect against loading b6plus.js twice)
+ * Modified: Sep 2023 (show buttons in index mode to go to slide mode and more)
+ * Modified: Jan 2024 (swapped UI: 2nd window for slides, 1st for preview)
+ *
+ * Copyright 2005-2024 W3C, ERCIM
+ * See http://www.w3.org/Consortium/Legal/copyright-software
+ */
+
+(function() {
+
+"use strict";
+
+/* Localized strings */
+const translations = {
+ "min": { // Abbreviation for "minutes"
+ de: "Min",
+ fr: "min",
+ nl: "min"},
+ "current time": {
+ de: "aktuelle Uhrzeit",
+ fr: "heure actuelle",
+ nl: "huidige tijd"},
+ "used": {
+ de: "verbraucht",
+ fr: "utilisé",
+ nl: "gebruikt"},
+ "remaining": { // As in "remaining time"
+ de: "Restzeit",
+ fr: "restant",
+ nl: "resterend"},
+ "pause": { // Label on a button to pause the clock
+ de: "Pause",
+ fr: "pause",
+ nl: "pauze"},
+ "resume": { // Label in a button to pause the clock
+ de: "fortsetzen",
+ fr: "reprendre",
+ nl: "hervatten"},
+ "+1 min": { // Label on a button to add 1 minute
+ de: "+1 Min",
+ fr: "+1 min",
+ nl: "+1 min"},
+ "−1 min": { // Label on a button to shorten time by 1 minute
+ de: "−1 Min",
+ fr: "−1 min",
+ nl: "−1 min"},
+ "restart": { // Label on a button to reset the clock
+ de: "Neustart",
+ fr: "réinitialiser",
+ nl: "herstart"},
+ "No navigation possible while sync mode is on.": {
+ de: "Bei aktiviertem Sync-Modus ist keine Navigation möglich.",
+ fr: "Aucune navigation possible lorsque le mode synchro est activé.",
+ nl: "Geen navigatie mogelijk terwijl de synchronisatiemodus is ingeschakeld."},
+ "Press S to toggle sync mode off.": {
+ de: "Drücken Sie S, um den Sync-Modus auszuschalten.",
+ fr: "Appuyez sur S pour désactiver le mode synchro.",
+ nl: "Druk op S om de synchronisatiemodus uit te schakelen."},
+ "Synchronization error.": {
+ de: "Synchronisierungsfehler",
+ fr: "Erreur de synchronisation.",
+ nl :"Synchronisatiefout."},
+ "You can try to turn synchronization back on with the S key.": {
+ de: "Sie können versuchen, die Synchronisation mit der Taste S wieder einzuschalten.",
+ fr: "Vous pouvez essayer de réactiver la synchronisation avec la touche S.",
+ nl: "U kunt proberen de synchronisatie weer in te schakelen met de S-toets."},
+ "An error occurred while trying to switch into fullscreen mode": {
+ de: "Beim Wechsel in den Vollbildmodus ist ein Fehler aufgetreten",
+ fr: "Une erreur s'est produite en essayant de passer en mode plein écran",
+ nl: "Er is een fout opgetreden bij het overschakelen naar volledig scherm"},
+ "Fullscreen mode is not possible": {
+ de: "Der Vollbildmodus ist nicht möglich",
+ fr: "Le mode plein écran est impossible",
+ nl: "Volledig scherm is niet mogelijk"},
+ "You can try again with the F or F1 key.": {
+ de: "Sie können es mit der Taste F oder F1 erneut versuchen.",
+ fr: "Vous pouvez réessayer avec la touche F ou F1.",
+ nl: "U kunt het opnieuw proberen met de toets F of F1."},
+ "Syncing turned OFF.\nPress S to turn syncing back on.": {
+ de: "Synchronisierung ausgeschaltet.\nDrücken Sie S, um die Synchronisierung wieder einzuschalten.",
+ fr: "Synchronisation désactivée\nAppuyez sur S pour réactiver la synchronisation,",
+ nl: "Synchroniseren uitgeschakeld\nDruk op S om het synchroniseren weer in te schakelen"},
+ "Syncing turned ON\nPress S to turn syncing off": {
+ de: "Synchronisierung eingeschaltet\nDrücken Sie S, um die Synchronisierung auszuschalten",
+ fr: "Synchronisation activée\nAppuyez sur S pour désactiver la synchronisation",
+ nl: "Synchronisatie ingeschakeld\nDruk op S om synchronisatie uit te schakelen"},
+ "Mouse & keyboard commands": {
+ de: "Maus- und Tastaturbefehle",
+ fr: "Commandes de la souris et du clavier",
+ nl: "Muis- en toetsenbordopdrachten"},
+ "A, double click, 3-finger touch": {
+ de: "A, Doppelklick, 3-Finger-Touch",
+ fr: "A, double clic, toucher à 3 doigts",
+ nl: "A, dubbelklik, 3-vinger touch"},
+ "enter slide mode": {
+ de: "Dia-Modus einschalten",
+ fr: "passer en mode diapo",
+ nl: "naar de diamodus gaan"},
+ "A, Esc, 3-finger touch": {
+ de: "A, Esc, 3-Finger-Touch",
+ fr: "A, Esc, toucher à 3 doigts",
+ nl: "A, Esc, 3-vinger touch"},
+ "leave slide mode": {
+ de: "Dia-Modus ausschalten",
+ fr: "quiter le mode diapo",
+ nl: "diamodus verlaten"},
+ "space, →, ↓, swipe left": {
+ de: "Leertaste, →, ↓, links wischen",
+ fr: "espace, →, ↓, glisser vers la gauche",
+ nl: "spatie, →, ↓, veeg naar links",
+ },
+ "space, →, ↓, click": {
+ de: "Leertaste, →, ↓, click",
+ fr: "espace, →, ↓, clic",
+ nl: "spatie, →, ↓, klik"},
+ "next slide or incremental element": {
+ de: "nächstes Dia oder inkrementelles Element",
+ fr: "diapo suivante ou élément incrémentiel",
+ nl: "volgende dia of incrementeel element"},
+ "PgDn": {},
+ "PgDn, swipe left": {
+ de: "PgDn, links wischen",
+ fr: "PgDn, glisser vers la gauche",
+ nl: "PgDn, veeg naar links"},
+ "next slide": {
+ de: "nächstes Dia",
+ fr: "diapo suivante",
+ nl: "volgende dia"},
+ "PgUp, ←, ↑, swipe right": {
+ de: "PgUp, ←, ↑, rechts wischen",
+ fr: "PgUp, ←, ↑, glisser vers la droite",
+ nl: "PgUp, ←, ↑, veeg naar rechts"},
+ "previous slide": {
+ de: "vorheriges Dia",
+ fr: "diapo précédente",
+ nl: "vorige dia"},
+ "End": {},
+ "last slide": {
+ de: "letztes Dia",
+ fr: "dernière diapo",
+ nl: "laatste dia"},
+ "Home": {},
+ "first slide": {
+ de: "erstes Dia",
+ fr: "première diapo",
+ nl: "eerste dia"},
+ "F1, F": {},
+ "toggle fullscreen mode": {
+ de: "Vollbildmodus umschalten",
+ fr: "basculer le mode plein écran",
+ nl: "volledig scherm aan/uit",
+ },
+ "2": {},
+ "show slides in 2nd window": {
+ de: "abspielen in 2. Fenster",
+ fr: "lire dans 2e fenêtre",
+ nl: "afspelen in 2e venster"},
+ "?": {},
+ "this help": {
+ de: "diese Hilfe",
+ fr: "cette aide",
+ nl: "deze hulp"},
+ "S": {},
+ "toggle sync mode on/off": {
+ de: "Sync-Modus ein-/ausschalten",
+ fr: "activer/désactiver le mode synchro",
+ nl: "sync-modus aan/uit"},
+ "(More information in the b6+ manual)": {
+ de: "(Weitere Informationen im b6+ Handbuch)",
+ fr: "(Plus d'informations dans le manuel de b6+)",
+ nl: "(Meer informatie in de b6+ handleiding)"},
+ "▶\uFE0E": {},
+ "play slides or stop playing": {
+ de: "Dias abspielen oder halten",
+ fr: "lancer les diapos ou arrêter",
+ nl: "dia's afspelen of stoppen"},
+ "play/stop": {
+ de: "abspielen/halten",
+ fr: "lire/arrêter",
+ nl: "afspelen/stoppen"},
+ "⧉": {},
+ "play in 2nd window": {
+ de: "abspielen in 2. Fenster",
+ fr: "lire dans 2eme fenêtre",
+ nl: "afspelen in 2de venster"},
+ "play/stop slides in a 2nd window": {
+ de: "Dias abspielen/halten in einem zweiten Fenster",
+ fr: "lancer/arrêter les diapos dans une 2eme fenêtre",
+ nl: "dia's afspelen/stoppen in een 2de venster"},
+ "❮": {},
+ "back": {
+ de: "zurück",
+ fr: "précédent",
+ nl: "terug"},
+ "❯": {},
+ "forward": {
+ de: "vorwärts",
+ fr: "suivant",
+ nl: "vooruit"},
+ "?": {},
+ "help": {
+ de: "Hilfe",
+ fr: "aide",
+ nl: "help"},
+ "◑": {},
+ "dark mode": {
+ de: "Dunkelmodus",
+ fr: "mode sombre",
+ nl: "donkere modus"},
+ "toggle dark mode on/off": {
+ de: "Dunkelmodus ein- oder ausschalten",
+ fr: "activer ou désactiver le mode sombre",
+ nl: "schakel de donkere modus aan of uit"},
+};
+
+/* Global variables */
+var curslide = null;
+var slidemode = false; // In slide show mode or normal mode?
+var switchInProgress = false; // True if waiting for finishToggleMode()
+var incrementals = null; // Array of incrementally displayed items
+var gesture = {}; // Info about touch/pointer gesture
+var numslides = 0; // Number of slides
+var stylesToLoad = 0; // # of load events to wait for
+var limit = 0; // A time limit used by toggleMode()
+var nextid = 0; // For generating unique IDs
+var interactive = true; // Allow navigating to a different slide?
+var fullmode = false; // Whether "?full" was in the URL
+var progressElts = []; // Elements with class=progress
+var slidenumElts = []; // Elements with class=slidenum
+var liveregion = null; // Element [role=region][aria-live=assertive]
+var savedContent = ""; // Initial content of the liveregion
+var noclick = false; // If true, mouse clicks do not advance slides
+var hideMouseTime = null; // If set, hide idle mouse pointer after N ms
+var helptext = null; // List of keyboard and mouse commands
+var hideMouseID = null; // ID of timer to hide the mouse pointer
+var singleClickTimer = null; // Timeout to distinguish single & double click
+var secondwindow = null; // Second window for speaker notes
+var firstwindow = null; // The window that opened this one
+var syncmode = false; // Sync mode
+var syncURL = null; // URL of sync server
+var eventsource = null; // Sync server object
+var startTime = 0; // Start time, used by displayed clocks
+var pauseStartTime = 0; // 0 = clocks not paused, > 0 = start of pause
+var realHoursElts = null; // Elements with wallclock time: hours
+var realMinutesElts = null; // Elements with wallclock time: minutes
+var realSecondsElts = null; // Elements with wallclock time: seconds
+var usedHoursElts = null; // Elements with used time: hours
+var usedMinutesElts = null; // Elements with used time: minutes
+var usedSecondsElts = null; // Elements with used time: seconds
+var leftHoursElts = null; // Elements with remaining time: hours
+var leftMinutesElts = null; // Elements with remaining time: minutes
+var leftSecondsElts = null; // Elements with remaining time: seconds
+var clockTimer = 0; // Interval timer for clocks
+var duration = 30 * 60 * 1000; // Default duration of a presentation 30 min
+var warnTime = 5 * 60 * 1000; // Warn 5 minutes before end of duration
+var language = null; // Language for localization
+var switchFullscreen = false; // True = toggle fullscreen but not slide mode
+var hasDarkMode = false; // Style sheet supports class=darkmode?
+var incrementalsBehavior = "freeze"; // [Experimental]
+
+
+/* _ -- return translation for text, or text, if none is available */
+function _(text)
+{
+ return translations[text]?.[language] ?? text;
+}
+
+
+/* generateID -- make sure elt has a unique ID */
+function generateID(elt)
+{
+ /* This doesn't guarantee that elt has a unique ID, but only that it
+ * is the first element in the document that has this ID. Which
+ * should be enough to make this element scroll into view when it is
+ * the target... */
+ if (!elt.id) elt.id = "s" + ++nextid;
+ while (document.getElementById(elt.id) !== elt) elt.id = "s" + ++nextid;
+}
+
+
+/* cloneNodeWithoutID -- deep clone a node, but not any ID attributes */
+function cloneNodeWithoutID(elt)
+{
+ var clone, h;
+
+ clone = elt.cloneNode(false);
+ if (elt.nodeType === 1 /*Node.ELEMENT_NODE*/) {
+ clone.removeAttribute("id"); // If any
+ for (h = elt.firstChild; h; h = h.nextSibling)
+ clone.appendChild(cloneNodeWithoutID(h)); // Recursive
+ }
+ return clone;
+}
+
+
+/* ticker -- update clock elements */
+function ticker(force)
+{
+ var now, s0, m0, h0, s1, m1, h1, s2, m2, h2, used, left;
+
+ // This function is usually called as interval time handler, but is
+ // also called when the clocks need to be updated, e.g., after a change
+ // in duration or pause/resume.
+
+ now = new Date();
+
+ s0 = now.getSeconds();
+ m0 = now.getMinutes();
+ h0 = now.getHours();
+
+ for (const e of realHoursElts)
+ e.textContent = h0.toString().padStart(2, "0");
+ for (const e of realMinutesElts)
+ e.textContent = m0.toString().padStart(2, "0");
+ for (const e of realSecondsElts)
+ e.textContent = s0.toString().padStart(2, "0");
+
+ if (pauseStartTime === 0 || force) { // Clocks aren't paused or update forced
+
+ used = now.getTime() - startTime;
+ s1 = Math.trunc(used / 1000);
+ if (usedHoursElts.length != 0) { // Used hours are displayed
+ h1 = Math.trunc(s1 / 60 / 60); s1 -= h1 * 60 * 60;
+ m1 = Math.trunc(s1 / 60); s1 -= m1 * 60;
+ } else if (usedMinutesElts.length != 0) { // No hours, but minutes are shown
+ m1 = Math.trunc(s1 / 60); s1 -= m1 * 60;
+ }
+ for (const e of usedHoursElts)
+ e.textContent = h1.toString().padStart(2, "0");
+ for (const e of usedMinutesElts)
+ e.textContent = m1.toString().padStart(2, "0");
+ for (const e of usedSecondsElts)
+ e.textContent = s1.toString().padStart(2, "0");
+
+ left = Math.max(0, duration - used);
+ s2 = Math.trunc(left / 1000);
+ if (leftHoursElts.length != 0) { // Remaining hours are displayed
+ h2 = Math.trunc(s2 / 60 / 60); s2 -= 60 * 60 * h2;
+ m2 = Math.trunc(s2 / 60); s2 -= 60 * m2;
+ } else if (leftMinutesElts.length) { // No hours, but minutes are shown
+ m2 = Math.trunc(s2 / 60); s2 -= 60 * m2;
+ }
+ for (const e of leftHoursElts)
+ e.textContent = h2.toString().padStart(2, "0");
+ for (const e of leftMinutesElts)
+ e.textContent = m2.toString().padStart(2, "0");
+ for (const e of leftSecondsElts)
+ e.textContent = s2.toString().padStart(2, "0");
+
+ // Set a precise factor 0.0..1.0 in a CSS variable on BODY.
+ // Set an integer percentage 00..100 in a data attribute on BODY.
+ // If time left is <= warnTime, set class=time-warning on BODY.
+ document.body.style.setProperty("--time-factor", 1 - left/duration);
+ document.body.setAttribute("data-time-factor",
+ Math.trunc(100 * (1 - left/duration)).toString().padStart(2, "0"));
+ if (left <= warnTime) document.body.classList.add("time-warning");
+ else document.body.classList.remove("time-warning");
+ }
+}
+
+
+/* addMinute -- add 1 minute to the duration */
+function addMinute(ev)
+{
+ duration += 60000;
+
+ if (firstwindow)
+ firstwindow.postMessage({event: "duration", v: duration});
+ else if (secondwindow?.closed === false)
+ secondwindow.postMessage({event: "duration", v: duration});
+
+
+
+ ev.stopPropagation();
+ ev.preventDefault();
+}
+
+
+/* subtractMinute -- subtract 1 minute from the duration */
+function subtractMinute(ev)
+{
+ duration = Math.max(0, duration - 60000);
+
+ if (firstwindow)
+ firstwindow.postMessage({event: "duration", v: duration});
+ else if (secondwindow?.closed === false)
+ secondwindow.postMessage({event: "duration", v: duration});
+
+ ticker(true); // Update the clocks
+
+ ev.stopPropagation();
+ ev.preventDefault();
+}
+
+
+/* pauseTime -- pause or resume the clocks */
+function pauseTime(ev)
+{
+ if (pauseStartTime) { // We're resuming, add paused time to startTime
+ startTime += Date.now() - pauseStartTime;
+ pauseStartTime = 0;
+ document.body.classList.remove("paused");
+ } else { // We're pausing, remember start time of pause
+ pauseStartTime = Date.now();
+ document.body.classList.add("paused");
+ }
+
+ ticker(true); // Update the clocks
+
+ if (firstwindow) {
+ firstwindow.postMessage({event: "startTime", v: startTime});
+ firstwindow.postMessage({event: "pauseStartTime", v: pauseStartTime});
+ } else if (secondwindow?.closed === false) {
+ secondwindow.postMessage({event: "startTime", v: startTime});
+ secondwindow.postMessage({event: "pauseStartTime", v: pauseStartTime});
+ }
+
+ ev.stopPropagation();
+ ev.preventDefault();
+}
+
+
+/* resetTime -- restart the clock */
+function resetTime(ev)
+{
+ startTime = Date.now();
+
+ if (firstwindow)
+ firstwindow.postMessage({event: "startTime", v: startTime});
+ else if (secondwindow?.closed === false)
+ secondwindow.postMessage({event: "startTime", v: startTime});
+
+ ticker(true); // Update the clocks
+
+ ev.stopPropagation();
+ ev.preventDefault();
+}
+
+
+/* ignoreEvent -- cancel an event */
+function ignoreEvent(ev)
+{
+ ev.stopPropagation();
+ ev.preventDefault();
+}
+
+
+/* initClocks -- find and initialize clock elements */
+function initClocks()
+{
+ var t;
+
+ // Get the duration and warn time of the presentation from body.class.
+ for (const c of document.body.classList) {
+ if ((t = c.match(/^duration=([0-9.]+)$/))) duration = 1000 * 60 * t[1];
+ if ((t = c.match(/^warn=([0-9.]+)$/))) warnTime = 1000 * 60 * t[1];
+ }
+
+ // If there are elements with class=fullclock or class=clock
+ // and that don't have child elements already, fill them with
+ // appropriate elements to make a clock.
+ for (const c of document.getElementsByClassName("fullclock"))
+ if (!c.firstElementChild)
+ c.insertAdjacentHTML("beforeend", '' + _('current time') + '' +
+ '' +
+ '' +
+ '' + _('used') + '' +
+ '' +
+ '' + _('remaining') + '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+ for (const c of document.getElementsByClassName("clock"))
+ if (!c.firstElementChild)
+ c.insertAdjacentHTML("beforeend",
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+
+ // Find all elements that will contain time.
+ realHoursElts = document.getElementsByClassName("hours-real");
+ realMinutesElts = document.getElementsByClassName("minutes-real");
+ realSecondsElts = document.getElementsByClassName("seconds-real");
+ usedHoursElts = document.getElementsByClassName("hours-used");
+ usedMinutesElts = document.getElementsByClassName("minutes-used");
+ usedSecondsElts = document.getElementsByClassName("seconds-used");
+ leftHoursElts = document.getElementsByClassName("hours-remaining");
+ leftMinutesElts = document.getElementsByClassName("minutes-remaining");
+ leftSecondsElts = document.getElementsByClassName("seconds-remaining");
+
+ // Find all elements that adjust the clock and install event handlers.
+ for (const e of document.getElementsByClassName("timeinc")) {
+ e.addEventListener("click", addMinute, true);
+ e.addEventListener("dblclick", ignoreEvent, true);
+ }
+ for (const e of document.getElementsByClassName("timedec")) {
+ e.addEventListener("click", subtractMinute, true);
+ e.addEventListener("dblclick", ignoreEvent, true);
+ }
+ for (const e of document.getElementsByClassName("timepause")) {
+ e.addEventListener("click", pauseTime, true);
+ e.addEventListener("dblclick", ignoreEvent, true);
+ }
+ for (const e of document.getElementsByClassName("timereset")) {
+ e.addEventListener("click", resetTime, true);
+ e.addEventListener("dblclick", ignoreEvent, true);
+ }
+
+ // Install a timer to update the clock elements, if needed.
+ if (realHoursElts.length || realMinutesElts.length ||
+ realSecondsElts.length || usedHoursElts.length ||
+ usedMinutesElts.length || usedSecondsElts.length ||
+ leftHoursElts.length || leftMinutesElts.length ||
+ leftSecondsElts.length)
+ clockTimer = setInterval(ticker, 1000, false); // Clock tick every second
+
+ // Remember start time of presentation.
+ if (clockTimer) startTime = Date.now();
+}
+
+
+/* initIncrementals -- find incremental elements in current slide */
+function initIncrementals()
+{
+ var e = curslide;
+
+ // Collect all incremental elements into array incrementals.
+ //
+ // The functions nextSlideOrElt() and previousSlideOrElt() maintain
+ // the following invariant: If there are incrementals, there is at
+ // most one of them with a class of "active". If there is an
+ // "active" element, all incrementals before it, and only those,
+ // have a class of "visited". If there is an "active" element,
+ // incrementals[incrementals.cur] points to that element; if there
+ // is not, incrementals.cur is -1.
+ //
+ // incrementalsBehavior is an experimental variable to evaluate
+ // different behaviors when going backwards inside a slide with
+ // incremental elements:
+ //
+ // "freeze": When you leave a slide, the incremental elements that
+ // are currently displayed become frozen. When going back to that
+ // slide, those elements are still displayed but can no longer be
+ // removed by pressing the left arrow. This is the behavior of
+ // Shower and is currently the default.
+ //
+ // "reset": Every time you enter a slide, all incremental elements
+ // are in their hidden state. E.g., if you leave a slide with all
+ // elements visible and then go back, all elements are hidden again.
+ //
+ // "symmetric": When you return to a slide, the slide is exactly as
+ // you left it. Incremental elements that were displayed when you
+ // left the slide are still displayed and can be hidden by pressing
+ // the left arrow.
+ //
+ // "forwardonly": When you enter a slide, all incremental elements
+ // are in their hidden state (as with "reset"). In addition,
+ // pressing the left arrow when some incremental elements are
+ // displayed, resets all elements to their hidden state.
+ //
+ // Note that will all of these except "symmetric", the left arrow
+ // acts very much like the PageUp key: when you go back to the
+ // previous slide, every next press of the left arrow goes back one
+ // slide.
+ //
+ incrementals = [];
+ incrementals.cur = -1;
+ do {
+ /* Go to the next node, in document source order. */
+ if (e.firstChild) {
+ e = e.firstChild;
+ } else {
+ while (e && !e.nextSibling) e = e.parentNode;
+ if (e) e = e.nextSibling;
+ }
+ if (!e) break; /* End of document */
+ if (e.nodeType != 1) continue; /* Not an element */
+ if (e.b6slidenum) break; /* Reached the next slide */
+ if (e.classList.contains("incremental") || e.classList.contains("overlay"))
+ for (const c of e.children)
+ if (incrementalsBehavior === "symmetric") {
+ if (c.classList.contains("active"))
+ incrementals.cur = incrementals.length; // Start at this element
+ incrementals.push(c);
+ } else if (incrementalsBehavior === "reset" ||
+ incrementalsBehavior == "forwardonly") {
+ c.classList.remove("active");
+ c.classList.remove("visited");
+ incrementals.push(c);
+ } else { // "freeze"
+ if (!c.classList.contains("visited") &&
+ !c.classList.contains("active"))
+ incrementals.push(c);
+ }
+ if (e.classList.contains("next")) { /* It is an incremental element */
+ if (incrementalsBehavior === "symmetric") {
+ if (e.classList.contains("active"))
+ incrementals.cur = incrementals.length; // Start at this element
+ incrementals.push(e);
+ } else if (incrementalsBehavior === "reset" ||
+ incrementalsBehavior == "forwardonly") {
+ e.classList.remove("active");
+ e.classList.remove("visited");
+ incrementals.push(e);
+ } else { // "freeze"
+ if (!e.classList.contains("visited") &&
+ !e.classList.contains("active"))
+ incrementals.push(e);
+ }
+ }
+ } while (1);
+}
+
+
+/* isStartOfSlide -- check if element has class=slide, page-break or is an H1 */
+function isStartOfSlide(elt)
+{
+ if (elt.nodeType != 1) return false; // Not an element
+ if (elt.classList.contains("slide")) return true;
+ if (window.getComputedStyle(elt).getPropertyValue('page-break-before') ==
+ 'always') return true;
+ if (elt.nodeName != "H1") return false;
+
+ /* The element is an H1. It starts a slide unless it is inside class=slide */
+ while (true) {
+ elt = elt.parentNode;
+ if (!elt || elt.nodeType != 1) return true;
+ if (elt.classList.contains("slide")) return false;
+ }
+}
+
+
+/* updateProgress -- update the progress bars and slide numbers, if any */
+function updateProgress()
+{
+ var p = curslide.b6slidenum / numslides;
+
+ /* Set the width of the progress bars */
+ for (const e of progressElts) e.style.width = 100 * p + "%";
+
+ /* Set the content of .slidenum elements to the current slide number */
+ for (const e of slidenumElts) e.textContent = curslide.b6slidenum;
+
+ /* Set a custom variable on BODY for use by style rules */
+ document.body.style.setProperty("--progress", p);
+}
+
+
+/* initProgress -- find .progress and .slidenum elements, count slides */
+function initProgress()
+{
+ var s;
+
+ /* Find all elements that are progress bars, unhide them. */
+ progressElts = document.getElementsByClassName("progress");
+ for (const e of progressElts)
+ if (typeof e.b6savedstyle === "string") e.style.cssText = e.b6savedstyle;
+
+ /* Find all that should contain the current slide number, unhide them. */
+ slidenumElts = document.getElementsByClassName("slidenum");
+ for (const e of slidenumElts)
+ if (typeof e.b6savedstyle === "string") e.style.cssText = e.b6savedstyle;
+
+ /* Find all that should contain the # of slides, fill and unhide them. */
+ for (const e of document.getElementsByClassName("numslides")) {
+ if (typeof e.b6savedstyle == "string") e.style.cssText = e.b6savedstyle;
+ e.textContent = numslides; // Set content to number of slides
+ }
+
+ /* Set the # of slides in a CSS counter on the BODY. */
+ s = window.getComputedStyle(document.body).getPropertyValue("counter-reset");
+ if (s === "none") s = ""; else s += " ";
+ document.body.style.setProperty('counter-reset',s + 'numslides ' + numslides);
+}
+
+
+/* numberSlides -- count slides, number them, and make sure they have IDs */
+function numberSlides()
+{
+ numslides = 0;
+ for (const h of document.body.children)
+ if (isStartOfSlide(h)) {
+ h.b6slidenum = ++numslides; // Save number in element
+ generateID(h); // If the slide has no ID, add one
+ }
+}
+
+
+/* hideMouse -- make the mouse pointer invisible (only in slide mode) */
+function hideMouse()
+{
+ if (slidemode) document.body.style.cursor = 'none';
+ hideMouseID = 0; // 0 = timer has fired, cursor is hidden
+}
+
+
+/* hideMouseReset -- event handler for mousemove to reset the hideMouse timer */
+function hideMouseReset()
+{
+ if (hideMouseID === 0) { // Timer has fired and hid the cursor. Unhide it.
+ document.body.style.cursor = null;
+ hideMouseID = null; // null = cursor is visible
+ } else if (hideMouseID !== null) { // Timer hasn't fired yet. Remove it.
+ clearTimeout(hideMouseID);
+ hideMouseID = null; // null = cursor is visible
+ }
+
+ /* If still in slide mode, set a new timer; otherwise remove ourselves. */
+ if (slidemode) hideMouseID = setTimeout(hideMouse, hideMouseTime);
+ else document.removeEventListener('mousemove', hideMouseReset);
+}
+
+
+/* initHideMouse -- set a timeout to hide the mouse pointer when it is idle */
+function initHideMouse()
+{
+ if (hideMouseTime === null) return;
+
+ /* Add handler to restart the timer when the mouse moves. */
+ document.addEventListener('mousemove', hideMouseReset);
+
+ /* Remove old timer, unhide cursor if hidden, start new timer. */
+ hideMouseReset();
+}
+
+
+/* displaySlide -- make the current slide visible */
+function displaySlide()
+{
+ var h, url;
+
+ /* curslide has class=slide, page-break-before=always or is an H1 */
+ curslide.style.cssText = curslide.b6savedstyle;
+ curslide.classList.add("active"); // Compatibility with Shower
+ liveregion.innerHTML = ""; // Make it empty
+
+ if (!curslide.classList.contains('slide')) {
+ liveregion.appendChild(cloneNodeWithoutID(curslide));
+ /* Unhide all elements until the next slide. And copy the slide to
+ the live region so that it is spoken */
+ for (h = curslide.nextSibling; h && ! h.b6slidenum; h = h.nextSibling)
+ if (h !== liveregion) {
+ if (h.nodeType === 1) h.style.cssText = h.b6savedstyle;
+ liveregion.appendChild(cloneNodeWithoutID(h));
+ }
+
+ } else { // class=slide
+ /* Copy the contents of the slide to the live region so that it is spoken */
+ for (h = curslide.firstChild; h; h = h.nextSibling)
+ liveregion.appendChild(cloneNodeWithoutID(h));
+ }
+
+ updateProgress();
+ initIncrementals();
+
+ /* If there is a first window, tell it to scroll to the same slide. */
+ if (firstwindow)
+ firstwindow.postMessage({event: "slide",
+ v: curslide.id || curslide.b6slidenum}, "*");
+
+ /* If the fragment ID is not the ID of the current slide, remove it. */
+ if (curslide.id && location.hash && curslide.id != location.hash.substring(1))
+ location.hash = "";
+}
+
+
+/* hideSlide -- make the current slide invisible */
+function hideSlide()
+{
+ var h;
+
+ if (!curslide) return;
+
+ /* curslide has class=slide, page-break-before=always or is an H1 */
+ curslide.classList.remove("active"); // Compatibility with Shower
+ curslide.classList.add("visited"); // Compatibility with Shower
+ curslide.style.visibility = "hidden";
+ curslide.style.position = "absolute";
+ curslide.style.top = "0";
+ for (h = curslide.nextSibling; h && ! h.b6slidenum; h = h.nextSibling)
+ if (h.nodeType === 1 /*Node.ELEMENT_NODE*/ && h !== liveregion) {
+ h.style.visibility = "hidden";
+ h.style.position = "absolute";
+ h.style.top = "0";
+ }
+}
+
+
+/* makeCurrent -- hide the previous slide, if any, and display elt */
+function makeCurrent(elt)
+{
+ console.assert(elt);
+ if (curslide != elt) {
+ hideSlide();
+ curslide = elt;
+ displaySlide();
+ }
+}
+
+
+/* fullscreen -- toggle fullscreen mode or turn it on ("on") or off ("off") */
+function toggleFullscreen(onoff)
+{
+ switchFullscreen = true; // For the fullscreenchange event handler
+
+ if (onoff !== "on" && document.fullscreenElement)
+ document.exitFullscreen();
+ else if (onoff !== "on" && document.webkitFullscreenElement)
+ document.webkitExitFullscreen();
+ else if (onoff !== "off" && document.fullscreenEnabled)
+ document.documentElement.requestFullscreen({navigationUI: "hide"})
+ .catch(err => {
+ alert(_("An error occurred while trying to switch into fullscreen mode")
+ + ' (' + err.message + ' – ' + err.name + ")\n\n" +
+ _("You can try again with the F or F1 key."))});
+ else if (onoff !== "off" && document.documentElement.webkitRequestFullscreen)
+ document.documentElement.webkitRequestFullscreen();
+ else if (onoff !== "off")
+ window.alert(_("Fullscreen mode is not possible"));
+}
+
+
+/* createHelpText -- fill the helptext element with help text */
+function createHelpText()
+{
+ var iframe, button;
+
+ /* Put the help text in an IFRAME so it is not affected by the slide style */
+ iframe = document.createElement('iframe');
+ iframe.srcdoc =
+ "" +
+ "" +
+ "" +
+ "" +
+ "
Exploring making site navigation more accessible, with "well-known destinations"
+
+ WAI-Adapt Task Force
+ Matthew Atkinson
+ Samsung R&D Institute UK
+
+
+ AC 2024 Hiroshima, Japan hybrid meeting 8–9 APRIL 2024
+
+
+
+
+
🚧
+
+
+
+
Please note, this proposal is under construction. Feedback is very welcome.
+
+
+
+
+
What?
+
+
+
+
+
The Adapt TF is proposing a standard,
+ mechanical, way
+ for sites to state which popular pages they offer.
+
This allows user agents to
+ automatically discover which of these popular pages a
+ site provides.
+
+
+
+
+
"Well-known…
+
+
+
+
+
…Destinations"
+
+
+
+
+
+
🕸️ Port 80
+
+
+
+
+
Well-known locations are a fundamental technique in computing. For example, web servers run on port 80 by
+ default, to make them easily discoverable.
+
+
+
+
+
+
🔎 "search"
+
+
+
+
+
A magnifying glass icon often indicates where you will find the search feature of a site or app.
+
+
+
+
+
🏡
+
+
+
+
When we talk about destinations, in terms of popular pages on sites, what do we mean? Well there's the home
+ page….
+
+
+
+
+
🛒
+
+
+
+
…the products page…
+
+
+
+
+
📧
+
+
+
+
…the contact page, and so on.
+
+
+
+
+
Why?
+
+
+
+
But why do we need to make these pages easier to find? There are several reasons; here is one big one…
+
+
+
+
+
Variation in terms
+
+
+ Log in
+
+
+ Sign-in
+
+
+ Sign on
+
+
+ Log on
+
+
+ Anmelden
+
+
+ ログイン
+
+
+
+
+
+
There are many different ways that the action of logging into one's account could be phrased—including in
+ other languages, with which the user may be unfamiliar.
Please note, this proposal is under construction. Feedback is very welcome.
+