diff --git a/presentation/deck/deck.css b/presentation/deck/deck.css new file mode 100644 index 00000000..d9c62a3c --- /dev/null +++ b/presentation/deck/deck.css @@ -0,0 +1,266 @@ +/* AritmoLab deck theme — consolidated light layout (variant 1) */ +:root { + --bordeaux: #cb333b; --bordeaux-hover: #b7252d; --bordeaux-deep: #8f1204; + --bordeaux-tint: #f4dfe1; + --surface: #fdfcfc; --surface-alt: #f6f5f5; --surface-sunken: #ecebeb; + --ink: #2a2426; --ink-muted: #6b6568; + --line: #e3e0e1; --line-strong: #c9c5c6; + --sans: 'Source Sans 3', system-ui, sans-serif; + --display: 'Source Serif 4', Georgia, serif; + --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; +} + +.reveal { font-family: var(--sans); } +.reveal .slides { text-align: left; } +.reveal .slides section.arit { + width: 100%; height: 100%; box-sizing: border-box; + display: flex; flex-direction: column; + background: var(--surface); color: var(--ink); + text-align: left; padding: 0; font-family: var(--sans); font-size: 16px; +} +.reveal .arit *, .reveal .arit *::before, .reveal .arit *::after { box-sizing: border-box; } + +/* ---------- chrome ---------- */ +.arit .head { + flex: 0 0 104px; height: 104px; display: flex; align-items: center; justify-content: space-between; + padding: 0 40px; background: var(--surface); border-bottom: 1px solid var(--line); +} +.arit .head img { height: 80px; display: block; } +.arit .head-org { font: 700 11.5px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-muted); } +.arit .band { + height: 46px; display: flex; align-items: center; justify-content: space-between; + padding: 0 40px; background: var(--bordeaux); color: var(--surface); flex: 0 0 46px; +} +.arit .band-section { font: 700 13px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; } +.arit .band select.jump { + background: transparent; color: #fff; border: none; + padding: 6px 12px; font: 700 11.5px/1.2 var(--sans); + letter-spacing: .04em; cursor: pointer; +} +.arit .band select.jump option { color: var(--ink); background: #fff; } +.arit .foot { + flex: 0 0 34px; height: 34px; display: flex; align-items: center; justify-content: space-between; gap: 24px; + padding: 0 40px; border-top: 1px solid var(--line); + font: 600 10px/1.4 var(--sans); letter-spacing: .03em; color: var(--ink-muted); +} +.arit .foot span { white-space: nowrap; flex-shrink: 0; } +.arit .foot span:first-child { overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; min-width: 0; } +.arit .foot span:first-child { overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; } +.arit .sbody { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 30px 48px 22px; text-align: left; } +.arit .title-slide .sbody { justify-content: center; } + +/* ---------- typography ---------- */ +.arit .kicker { font: 700 12px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--bordeaux); margin-bottom: 14px; } +.arit h1 { font: 700 46px/1.15 var(--sans); letter-spacing: -.01em; color: var(--ink); margin: 0; } +.arit h2 { font: 700 30px/1.15 var(--sans); letter-spacing: -.01em; color: var(--ink); margin: 0 0 8px; } +.arit .lead { font: 400 16px/1.5 var(--sans); color: var(--ink-muted); margin: 8px 0 0; } +.arit .hgap { height: 16px; flex: 0 0 16px; } + +/* title slide */ +.arit .title-meta { display: flex; align-items: center; gap: 14px; margin-top: 28px; } +.arit .title-meta .pill { + display: inline-flex; align-items: center; height: 34px; padding: 0 18px; + border-radius: 9999px; background: var(--bordeaux); color: #fdfcfc; + font: 700 12.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; +} +.arit .title-meta .what { font: 400 14.5px/1.5 var(--sans); color: var(--ink-muted); } +.arit .speakers { margin-top: 22px; display: flex; flex-direction: column; gap: 8px; } +.arit .speakers span { font: 600 15.5px/1.4 var(--sans); color: var(--ink); } +.arit .event { margin: 16px 0 0; font: italic 400 15px/1.5 var(--display); color: var(--ink-muted); } +.arit .event-where { margin: 8px 0 0; font: 600 14px/1.4 var(--sans); color: var(--ink-muted); } + +/* quote */ +.arit .quote { + margin: 14px 0 20px; padding: 16px 22px; border-left: 3px solid var(--bordeaux); + background: var(--surface-alt); border-radius: 0 8px 8px 0; + font: italic 400 16px/1.5 var(--display); color: var(--ink); +} +.arit .quote small { display: block; margin-top: 6px; font: 600 11px/1.4 var(--sans); font-style: normal; + letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted); } + +/* mini pipeline (problem / thoth slides) */ +.arit .pipeline { display: flex; align-items: stretch; gap: 10px; } +.arit .stage { flex: 1; padding: 12px 10px; text-align: center; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-alt); } +.arit .stage b { display: block; font: 700 13.5px/1.2 var(--sans); margin-bottom: 4px; } +.arit .stage span { display: block; font: 400 11px/1.35 var(--sans); color: var(--ink-muted); } +.arit .stage .tag { display: inline-block; margin-top: 6px; padding: 3px 10px; border-radius: 9999px; + background: var(--bordeaux); color: #fdfcfc; font: 700 9.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; } +.arit .stage.ai { background: var(--bordeaux-tint); border-color: var(--bordeaux); } +.arit .arrow { align-self: center; color: var(--line-strong); font: 700 18px/1 var(--sans); } +.arit .thoth-wrap { display: flex; justify-content: flex-end; margin-top: 14px; } +.arit .thoth-col { width: 470px; text-align: center; } +.arit .thoth-arrow { color: var(--line-strong); font: 700 16px/1 var(--sans); margin: 2px 0; } +.arit .thoth-box { border: 1px solid var(--bordeaux); border-radius: 8px; padding: 10px 14px; + background: var(--surface-alt); font: 600 12.5px/1.4 var(--sans); } +.arit .thoth-box .tag { margin-right: 8px; } + +/* points */ +.arit .points { list-style: none; margin: 0; padding: 0; } +.arit .points li { position: relative; padding: 11px 0 11px 26px; border-bottom: 1px solid var(--line); + font: 400 15px/1.45 var(--sans); color: var(--ink); } +.arit .points li::before { content: ''; position: absolute; left: 0; top: 18px; + width: 10px; height: 10px; border-radius: 9999px; background: var(--bordeaux); } +.arit .points code, .arit .stat-note code, .arit .ai-pop-list code { + font: 600 0.92em var(--mono); color: var(--bordeaux-deep); background: var(--surface-alt); + padding: 1px 5px; border-radius: 4px; } + +/* split + code panel */ +.arit .split { display: flex; gap: 40px; flex: 1; min-height: 0; } +.arit .split .left { flex: 0 0 430px; display: flex; flex-direction: column; } +.arit .split .right { flex: 1; min-width: 0; display: flex; } +.arit .code { width: 100%; display: flex; flex-direction: column; overflow: hidden; + border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface-sunken); } +.arit .code-head { flex: 0 0 38px; display: flex; align-items: center; justify-content: space-between; + padding: 0 16px; border-bottom: 1px solid var(--line); font: 600 11px/1 var(--mono); color: var(--ink-muted); } +.arit .code pre { flex: 1; padding: 16px 18px; overflow: hidden; margin: 0; + font: 400 13px/1.6 var(--mono); color: var(--ink); text-align: left; box-shadow: none; } +.arit .code b { font-weight: 700; color: var(--bordeaux-deep); } +.arit .code i { font-style: normal; color: #2f6f4f; } + +/* stats */ +.arit .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; flex: 1; align-content: center; } +.arit .stat { border: 1px solid var(--line); border-radius: 8px; background: var(--surface-alt); + padding: 26px 22px; display: flex; flex-direction: column; gap: 10px; } +.arit .stat .num { font: 700 46px/1 var(--sans); color: var(--bordeaux); letter-spacing: -.02em; } +.arit .stat .lbl { font: 600 13.5px/1.35 var(--sans); color: var(--ink); } +.arit .stat .sub { font: 400 11.5px/1.4 var(--sans); color: var(--ink-muted); } +.arit .stat-note { margin-top: 20px; font: 400 12.5px/1.5 var(--sans); color: var(--ink-muted); } + +/* divider */ +.arit.divider .sbody { justify-content: center; } +.arit .numeral { font: 700 110px/1 var(--sans); color: var(--bordeaux); } +.arit .divider-h { font: 700 40px/1.15 var(--sans); margin-top: 10px; } +.arit .cue { margin-top: 22px; font: 600 12px/1 var(--sans); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-muted); } + +/* ontology chips */ +.arit .tier { margin-bottom: 18px; } +.arit .tier-label { display: block; font: 700 11px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; + color: var(--ink-muted); margin-bottom: 8px; } +.arit .chips { display: flex; flex-wrap: wrap; gap: 8px; } +.arit .chipc { border: 1px solid var(--bordeaux); color: var(--bordeaux-deep); background: #fff; + border-radius: 9999px; padding: 7px 14px; font: 700 13px/1 var(--sans); } +.arit .chipc.t2 { border-color: var(--line-strong); color: var(--ink-muted); } + +/* flow */ +.arit .flow { margin-top: 16px; display: flex; align-items: stretch; gap: 14px; } +.arit .zone { border-radius: 8px; padding: 30px 14px 14px; position: relative; } +.arit .zone > .zlabel { position: absolute; top: 8px; left: 14px; + font: 700 10px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase; } +.arit .zone.existed { flex: 0 0 168px; background: var(--surface-alt); border: 1px dashed var(--line-strong); } +.arit .zone.existed > .zlabel { color: var(--ink-muted); } +.arit .zone.built { flex: 1; background: #faf4f5; border: 1px solid #e7c3c6; } +.arit .zone.built > .zlabel { color: var(--bordeaux); } +.arit .fcol { flex: 1; display: flex; flex-direction: column; gap: 8px; min-width: 0; } +.arit .fbox { background: #fff; border: 1px solid var(--line-strong); border-radius: 6px; padding: 10px; text-align: center; } +.arit .fbox b { display: block; font: 700 13.5px/1.25 var(--sans); } +.arit .fbox span { display: block; margin-top: 3px; font: 400 11px/1.3 var(--sans); color: var(--ink-muted); } +.arit .fbox.src { background: var(--surface-alt); border-color: var(--line); } +.arit .fbox.src.future { background: transparent; border: 1.5px dashed var(--line-strong); } +.arit .fbox.src.future b { color: var(--ink-muted); font-weight: 700; } +.arit .fbox.src.future .plus { display: inline-flex; width: 16px; height: 16px; margin-right: 5px; vertical-align: -3px; + align-items: center; justify-content: center; border-radius: 9999px; border: 1.5px solid var(--bordeaux); + color: var(--bordeaux); font: 800 11px/1 var(--sans); } +.arit .fbox.end { background: var(--bordeaux); border-color: var(--bordeaux); color: #fff; padding: 14px 10px; } +.arit .fbox.end span { color: rgba(255,255,255,.85); } +.arit .farrow { align-self: center; color: var(--bordeaux); font: 700 15px/1 var(--sans); } +.arit .fcol.ai { position: relative; padding-top: 12px; } +.arit .brain { + position: absolute; top: -6px; right: 6px; z-index: 5; + width: 30px; height: 30px; border-radius: 9999px; + background: #fff; border: 1.5px solid var(--bordeaux); + display: flex; align-items: center; justify-content: center; font-size: 15px; +} +.arit .fcap { font: italic 400 11px/1.4 var(--sans); color: var(--ink-muted); text-align: center; } +.arit .fcol.ai-hit { flex: 0 0 44px; align-items: center; justify-content: center; gap: 8px; } +.arit .fcol.ai-hit .brain-btn { position: static; } + +/* clickable brains */ +button.brain-btn { cursor: pointer; padding: 0; transition: transform .15s ease, box-shadow .15s ease; } +button.brain-btn:hover, button.brain-btn:focus-visible { transform: scale(1.18); box-shadow: 0 0 0 5px rgba(203,51,59,.14); outline: none; } + +/* popup */ +.ai-pop { position: fixed; inset: 0; z-index: 100; background: rgba(42,36,38,.45); backdrop-filter: blur(2px); + display: block; opacity: 0; pointer-events: none; transition: opacity .2s ease; } +.ai-pop.open { opacity: 1; pointer-events: auto; } +.ai-line { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; } +.ai-line line { stroke: var(--bordeaux); stroke-width: 2.5; } +.ai-line circle { fill: var(--bordeaux); } +.ai-pop-card { position: absolute; width: min(580px, 92vw); background: #fff; border: 1px solid var(--line); + border-radius: 12px; box-shadow: 0 18px 50px rgba(42,36,38,.28); padding: 24px 28px 22px; + transform: translateY(14px) scale(.95); transition: transform .24s cubic-bezier(.2,.9,.3,1.15); text-align: left; } +.ai-pop.open .ai-pop-card { transform: none; } +.ai-pop-kicker { font: 700 10.5px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--bordeaux); margin-bottom: 10px; } +.ai-pop-title { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; } +.ai-pop-title .brain { position: static; } +.ai-pop-title .brain-btn { cursor: default; pointer-events: none; } +.ai-pop-title b { font: 700 18px/1.3 var(--sans); color: var(--ink); font-family: var(--sans); } +.ai-pop-card p { font: 400 14px/1.65 var(--sans); color: var(--ink); margin: 0; text-align: left; } +.ai-pop-card p.lead { margin-bottom: 14px; } +.ai-pop-list { list-style: none; display: flex; flex-direction: column; gap: 10px; margin: 0; padding: 0; } +.ai-pop-list li { position: relative; padding-left: 20px; font: 400 13.5px/1.5 var(--sans); color: var(--ink); } +.ai-pop-list li::before { content: ''; position: absolute; left: 0; top: 7px; width: 8px; height: 8px; border-radius: 9999px; background: var(--bordeaux); } +.ai-pop-list b { font-weight: 800; } +.ai-pop-list .n { color: var(--bordeaux-deep); font-weight: 800; } +.roi { margin-top: 14px; border: 1.5px solid var(--bordeaux); background: #faf4f5; border-radius: 8px; + padding: 10px 14px; font: 400 13.5px/1.55 var(--sans); color: var(--ink); } +.roi .k { display: block; margin-bottom: 4px; font: 800 10.5px/1 var(--mono); letter-spacing: .14em; + text-transform: uppercase; color: var(--bordeaux-deep); } +.ai-pop-x { position: absolute; top: 12px; right: 14px; width: 30px; height: 30px; border: none; + border-radius: 9999px; background: var(--surface-alt); font: 700 15px/1 var(--sans); color: var(--ink-muted); cursor: pointer; } +.ai-pop-x:hover { background: var(--bordeaux-tint); color: var(--bordeaux-deep); } + +/* chips on slides (wording TBD) */ +.arit .chip.tbd { color: var(--bordeaux-deep); border: 1px solid var(--bordeaux); background: #fff; + border-radius: 9999px; padding: 6px 12px; font: 700 10.5px/1 var(--sans); letter-spacing: .06em; } + +/* where we started */ +.arit .sysmap { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; flex: 1; align-content: center; } +.arit .syscard { position: relative; display: block; width: 100%; border: 1.5px solid var(--line-strong); + border-radius: 8px; background: var(--surface-alt); padding: 20px 16px 14px; text-align: left; + cursor: pointer; font-family: var(--sans); transition: transform .15s ease, box-shadow .15s ease; } +.arit button.syscard:hover, .arit button.syscard:focus-visible { transform: translateY(-3px); + box-shadow: 0 6px 18px rgba(42,36,38,.14); outline: none; } +.arit .syscard b { display: block; font: 700 17px/1.3 var(--sans); color: var(--ink); margin-bottom: 6px; } +.arit .syscard .d { display: block; font: 400 12.5px/1.45 var(--sans); color: var(--ink-muted); } +.arit .syscard .tag20 { position: absolute; top: -10px; right: 12px; background: var(--bordeaux); color: #fff; + border-radius: 9999px; padding: 3px 10px; font: 700 9.5px/1 var(--sans); letter-spacing: .08em; } +.arit .syscard.ghost { border: 1.5px dashed var(--bordeaux); background: #fff; cursor: default; } +.arit .syscard.ghost b { color: var(--bordeaux-deep); } +.arit .syscard.ghost .tag20 { background: var(--bordeaux-deep); } +.arit .hint { margin-top: 12px; font: italic 400 12px/1.4 var(--sans); color: var(--ink-muted); } + +/* snapshot tours + chart placeholders */ +.arit .shots { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr 1fr; + gap: 14px; flex: 1; min-height: 0; align-content: center; } +.arit .shot { border: 1.5px dashed var(--line-strong); border-radius: 8px; + background: var(--surface-alt); display: flex; align-items: center; justify-content: center; + font: italic 400 12px/1.4 var(--sans); color: var(--ink-muted); min-height: 0; overflow: hidden; } +.arit .charts { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; flex: 1; min-height: 0; align-content: center; } +.arit .chart { border: 1.5px solid var(--line); border-radius: 8px; background: #fff; min-height: 0; + display: flex; flex-direction: column; overflow: hidden; } +.arit .chart b { display: block; padding: 12px 16px; font: 700 14px/1.3 var(--sans); color: var(--ink); + border-bottom: 1px solid var(--line); } +.arit .chart span { flex: 1; display: flex; align-items: center; justify-content: center; + font: italic 400 12px/1.4 var(--sans); color: var(--ink-muted); text-align: center; padding: 0 20px; } + +/* pillars strip (vision slide) */ +.arit .pillars { margin-top: auto; padding-top: 14px; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; } +.arit .pillars .agents { font: italic 600 12px/1.4 var(--sans); color: var(--bordeaux-deep); + border: 1.5px dashed var(--bordeaux); border-radius: 8px; padding: 7px 12px; background: #faf4f5; } +.arit .pillars .pillar { border: 1px solid var(--line-strong); border-radius: 9999px; + padding: 6px 12px; font: 600 11.5px/1 var(--sans); color: var(--ink-muted); } +.arit .pillars .pillar b { color: var(--ink); font-weight: 700; } + +/* Reveal sets inline "display: block" on the presented section — beat it + (author !important wins over inline) so our flex column layout applies */ +.reveal .slides section.arit.present { display: flex !important; } + +/* hide reveal default noise on our chrome */ +.reveal .slides section.arit aside.notes { display: none; } + +/* vision slide: tighter vertical rhythm to keep pillars clear of the footer */ +.arit.flow-slide .kicker { margin-bottom: 10px; } +.arit.flow-slide h2 { margin-bottom: 6px; } +.arit.flow-slide .lead { margin-top: 6px; font-size: 15px; } +.arit.flow-slide .flow { margin-top: 14px; } +.arit.flow-slide .pillars { padding-top: 8px; } diff --git a/presentation/deck/fonts.css b/presentation/deck/fonts.css new file mode 100644 index 00000000..291dc325 --- /dev/null +++ b/presentation/deck/fonts.css @@ -0,0 +1,11 @@ +/* Embedded fonts — Source Sans 3 & Source Serif 4 (SIL Open Font License) */ +@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 400; font-display: swap; + src: url('fonts/ss3-400.woff2') format('woff2'); } +@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 600; font-display: swap; + src: url('fonts/ss3-600.woff2') format('woff2'); } +@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 700; font-display: swap; + src: url('fonts/ss3-700.woff2') format('woff2'); } +@font-face { font-family: 'Source Serif 4'; font-style: normal; font-weight: 400; font-display: swap; + src: url('fonts/ss4-400.woff2') format('woff2'); } +@font-face { font-family: 'Source Serif 4'; font-style: italic; font-weight: 400; font-display: swap; + src: url('fonts/ss4-italic-400.woff2') format('woff2'); } diff --git a/presentation/deck/fonts/ss3-400.woff2 b/presentation/deck/fonts/ss3-400.woff2 new file mode 100644 index 00000000..ea8c1457 Binary files /dev/null and b/presentation/deck/fonts/ss3-400.woff2 differ diff --git a/presentation/deck/fonts/ss3-600.woff2 b/presentation/deck/fonts/ss3-600.woff2 new file mode 100644 index 00000000..ea8c1457 Binary files /dev/null and b/presentation/deck/fonts/ss3-600.woff2 differ diff --git a/presentation/deck/fonts/ss3-700.woff2 b/presentation/deck/fonts/ss3-700.woff2 new file mode 100644 index 00000000..ea8c1457 Binary files /dev/null and b/presentation/deck/fonts/ss3-700.woff2 differ diff --git a/presentation/deck/fonts/ss4-400.woff2 b/presentation/deck/fonts/ss4-400.woff2 new file mode 100644 index 00000000..12f5b101 Binary files /dev/null and b/presentation/deck/fonts/ss4-400.woff2 differ diff --git a/presentation/deck/fonts/ss4-italic-400.woff2 b/presentation/deck/fonts/ss4-italic-400.woff2 new file mode 100644 index 00000000..f1d4ac7b Binary files /dev/null and b/presentation/deck/fonts/ss4-italic-400.woff2 differ diff --git a/presentation/deck/index.html b/presentation/deck/index.html new file mode 100644 index 00000000..e7d259e2 --- /dev/null +++ b/presentation/deck/index.html @@ -0,0 +1,690 @@ + + + + +AritmoLab — Role of AI in the Analysis of Unstructured Clinical Databases + + + + + +
+ + +
+
+ Policlinico San Donato + AritmoLab · Policlinico San Donato +
+ +
+

Role of AI in the Analysis of Unstructured Clinical Databases

+
+

How we turned a mix of structured and free-text clinical records into a research data platform — the AritmoLab platform experience.

+
+ AritmoLab + A clinical data platform hosting cardiology data and analysis tools +
+
+ Dr. Marco Pancotti - MultiPhysixLab + Dr. Sara Paratico - I.R.C.C.S. Policlinico San Donato +
+

“Multidimensional Characterization of Cardiac Arrhythmias: Role of Electrocardiology in the Artificial Intelligence Era”

+

San Donato Milanese, Milan, Italy · 2–3 October 2026

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202601 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Twenty years ago
+

Four islands and two missing pieces

+
+ + + + +
MISSINGNo Health Intelligencenothing usable to analyse historical data
+
MISSINGNo ML-ready datamartsnothing ready for predictive statistics or machine learning
+
+

▸ click a system: how it was used — and what held it back

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202602 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
The plan
+

What we wanted to build

+

One platform, two destinations: the 360° patient portal and a research-ready warehouse. Built on open-source pillars, with AI coding agents wherever they helped. This talk follows the hardest piece — the unstructured data.

+
+
+ What existed +
+
Cardiorefcardiology records · procedures · letters
+
Genetic datalabs · variants · nomenclature
+
ECGsignals · device follow-up
+
+Future sourcesnew subsystems can be connected as sources
+
+
+
+ +
→
+
+
+ What we built +
+
Stagingraw replica of the sources
+
→
+
+ +
Integrationcleaned, normalized, deduplicated
+
AI reads the clinical text: pathologies, procedures, drug-challenge outcomes
+
+
→
+
Star schemadimensional transformation
+
→
+
+ +
Datamartsresearch-ready marts
+
AI builds them on demand from plain-English questions (ThothII)
+
+
→
+
+
AritmoLab Data Warehousequeried for research
+
AritmoLab Portalmanagement & exploration
+
+
+
+
+
+ AI coding agents — Anthropic · OpenAI — used wherever they helped, 360° in the code + Airflow · orchestration + Superset · dashboards + Django · portal scaffolding + Authentik · auth — GSD LDAP +
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202603 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Mappings as code
+

LLM agents draft it. Humans review it. Git versions it.

+
+
+
+
    +
  • Staging → integration mappings are declarative YAML: schema-validated, reviewed, versioned
  • +
  • LLM agents authored them from prompts (etl/prompts/fill_integration_yaml.md)
  • +
  • The same file wires the runtime text mining: analysis_function + version_function per enrichment
  • +
  • Every extracted row records its pattern-library version — reproducible by design
  • +
+
+
+
+
etl/config/integration-map.yaml19,764 lines · 42 mappings
+
text_cascade_enrichments:
+- source_columns:
+  - cr_diagnosi
+  - cr_anamnesi_cardio
+  - cr_anamnesi_extra_cardio
+  analysis_function: get_letter_pathologies_cascade
+  version_function: get_letter_pathology_version
+  integration_table: cr_lettera_dimissione_diagnosi
+  output_columns:
+  - column: patologia
+    postgres_type: TEXT NOT NULL
+
+
+
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202604 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
From free text to a research platform
+

The clinical truth lives in unstructured columns

+
+ “Il paziente riferisce sincope ricorrente; ECG basale con sopraslivellamento ST in V1–V3; + test provocativo con flecainide positivo per pattern Brugada.” + One sentence from a discharge letter (example) — diagnosis, history and test outcome, all in free text +
+
+
SourcesCardioref · SQL Server
Kokoro · PostgreSQL
+
→
+
Stagingraw replica
+
→
+
Integration3NF · text miningAI
+
→
+
Data Warehousestar schema
+
→
+
Martsdbt · Superset
+
+
+
+
↑
+
AIThothII — natural-language → SQL over the DWH, builds the final datamarts
+
+
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202605 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
What the text mining produced
+

Deterministic AI, audited numbers

+
+
58,438discharge letters readfree-text fields, bilingual IT/EN patterns
+
73,389pathologies extractedtwo-tier clinical ontology
+
10,908drug-challenge tests parsedflecainide · ajmaline · adrenaline · isoprenaline
+
2,307Brugada patients identifiedconfirmed by clinical review on dashboards
+
+

Every row carries its pattern-library semver (pattern_version) · quality gate: ≥95% accuracy on 100 manual reviews.

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202606 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Clinical NLP
+

Reading clinical text is not keyword matching

+
+
+
+
    +
  • Negation — “fibrillazione atriale esclusa”, “non FA”: the rule scans a window around every match before deciding
  • +
  • Family ≠ patient — “padre con FA” is attributed to the family member, not to the patient
  • +
  • Two languages — every pattern matches IT and EN forms: fibrillazione atriale / atrial fibrillation
  • +
  • Abbreviations & noise — FA, f.a., “TA 140/90” (blood pressure, not a diagnosis)
  • +
  • One field, many statements — the text is split into clauses before matching
  • +
+
+
+
+
what the rules seeexamples from the corpus
+
“padre con fibrillazione atriale”
+  → patologia: FA · attribuzione: familiare
+
+“fibrillazione atriale esclusa”
+  → patologia: FA · negato
+
+“test provocativo con flecainide
+  positivo per pattern Brugada”
+  → test: flecainide · esito: POSITIVO
+
+
+
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202607 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
The clinical ontology
+

Two tiers, one clinical order

+
+ TIER 1 · arrhythmic +
+ FABrugadaFlutterTATRNWPW / TPSVTVFVExtrasistolia V.SincopeQT lungo +
+
+
+ TIER 2 · structural +
+ Blocco AVBlocco di brancaCardiomiopatiaScompensoValvulopatia +
+
+
    +
  • Order encodes clinical precedence — “Brugada” is matched before “TV”, so “substrato per TV” can't mask a Brugada pattern
  • +
  • Synonyms live inline — FA / f.a. / fib. atriale / atrial fibrillation → one canonical label
  • +
  • Versioned like software — semver per pattern library, pattern_version on every extracted row
  • +
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202608 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Validation
+

Trusting the text is a process, not a promise

+
+
    +
  • Quality gates — ≥95% procedure classification, ≥85% pathology detection, validated on 100 manually reviewed records
  • +
  • Clinical review — the extracted Brugada cohort was validated by clinicians on Superset dashboards
  • +
  • Feedback loop — ~893 Brugada false positives found and removed in pattern-library v1.3.1
  • +
  • Nothing is silently dropped — negated and family-attributed findings are stored too, filtered only at the mart layer
  • +
+
Quality gateAn extraction is accepted only after 100 manual reviews confirm the agreed accuracy — the same standard for every new pattern release.
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202609 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
The portal, today
+

AritmoLab — a quick tour

+
+
screenshot
screenshot
screenshot
+
screenshot
screenshot
screenshot
+
+

[MP: 6-7 real screenshots of the portal, anonymized or demo data]

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202610 / 15
+ +
+ +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
All good? Not yet.
+

The warehouse speaks SQL. Research needs more.

+
+
    +
  • Clinicians and researchers don't write SQL — a star schema is built for analysts, not for the ward
  • +
  • Health Intelligence — to present the past, the Unit needs ready-made aggregates: volumes, incidence, outcomes
  • +
  • Predictive research — multivariate analysis and ML models need clean, wide, cohort-shaped tables
  • +
  • Hand-made datamarts are the bottleneck — even with coding agents, each one takes hours of careful work
  • +
+
The gapTwenty years of data, one warehouse — and no fast road from a research question to an answer.
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202611 / 15
+ +
+ +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Role 3 — AI for analysis
+

Ask the warehouse in plain English

+
+
“How many Brugada patients had an effective ablation?” + a researcher, in natural language +
+
+
NL → SQLAI writes the query over the star schemaAI
+
→
+
Human reviewthe researcher checks every proposed query
+
→
+
Datamartcurated, research-ready martsAI
+
→
+
Supersetstatistics & dashboards
+
+

The session is a conversation: refine the question, iterate on the datamart — every step reviewable.

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202612 / 15
+ +
+ + +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
ThothII, step by step
+

From question to datamart — the app

+
+
screenshot
screenshot
screenshot
+
screenshot
screenshot
screenshot
+
+

[MP: 6-7 real screenshots of ThothII — ask, review SQL, datamart, dashboard]

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202613 / 15
+ +
+ +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
From datamart to discovery
+

One datamart — many questions answered

+
+
Multivariate analysis[SVG chart — next step]
+
Machine learning[SVG chart — next step]
+
+

[Illustrative data, modeled on published arrhythmology predictors — Brugada focus]

+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202614 / 15
+ +
+ +
+
+ + AritmoLab · Policlinico San Donato +
+ +
+
Questions?
+

Thank you

+
+

Want the deep dives? The technical walkthroughs of the text miner, the mapping agents and ThothII are coming as videos on our Substack.

+
+ Dr. Marco Pancotti - MultiPhysixLab + Dr. Sara Paratico - I.R.C.C.S. Policlinico San Donato +
+
+
Role of AI in the Analysis of Unstructured Clinical DatabasesDr. Marco Pancotti - MultiPhysixLabDr. Sara Paratico - Gruppo San DonatoSan Donato Milanese, Milan, Italy · 2–3 October 202615 / 15
+ +
+ +
+ + +
+ + +
+ + + + + + diff --git a/presentation/deck/logo.png b/presentation/deck/logo.png new file mode 100644 index 00000000..94049294 Binary files /dev/null and b/presentation/deck/logo.png differ diff --git a/presentation/deck/vendor/reveal/.github/CONTRIBUTING.md b/presentation/deck/vendor/reveal/.github/CONTRIBUTING.md new file mode 100644 index 00000000..bd0fab29 --- /dev/null +++ b/presentation/deck/vendor/reveal/.github/CONTRIBUTING.md @@ -0,0 +1,21 @@ +## Contributing +Please keep the [issue tracker](https://github.com/hakimel/reveal.js/issues) limited to **bug reports**. + + +### General Questions and Support +If you have questions about how to use reveal.js the best place to ask is in the [Discussions](https://github.com/hakimel/reveal.js/discussions). Anything that isn't a bug report should be posted as a dicussion instead. + + +### Bug Reports +When reporting a bug make sure to include information about which browser and operating system you are on as well as the necessary steps to reproduce the issue. If possible please include a link to a sample presentation where the bug can be tested. + + +### Pull Requests +- Should be submitted from a feature/topic branch (not your master) +- Should follow the coding style of the file you work in, most importantly: + - Tabs to indent + - Single-quoted strings + + +### Plugins +Please do not submit plugins as pull requests. They should be maintained in their own separate repository. More information here: https://github.com/hakimel/reveal.js/wiki/Plugin-Guidelines diff --git a/presentation/deck/vendor/reveal/.github/FUNDING.yml b/presentation/deck/vendor/reveal/.github/FUNDING.yml new file mode 100644 index 00000000..972831e9 --- /dev/null +++ b/presentation/deck/vendor/reveal/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [hakimel] diff --git a/presentation/deck/vendor/reveal/.github/workflows/js.yml b/presentation/deck/vendor/reveal/.github/workflows/js.yml new file mode 100644 index 00000000..071e030f --- /dev/null +++ b/presentation/deck/vendor/reveal/.github/workflows/js.yml @@ -0,0 +1,31 @@ +name: tests + +on: + - push + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: + - 18 + - 20 + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - run: npm install + - run: npm run build --if-present + - run: npm test + env: + CI: true diff --git a/presentation/deck/vendor/reveal/.gitignore b/presentation/deck/vendor/reveal/.gitignore new file mode 100644 index 00000000..ba5aa844 --- /dev/null +++ b/presentation/deck/vendor/reveal/.gitignore @@ -0,0 +1,11 @@ +.idea/ +*.iml +*.iws +*.eml +out/ +.DS_Store +.svn +log/*.log +tmp/** +node_modules/ +.sass-cache \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/.npmignore b/presentation/deck/vendor/reveal/.npmignore new file mode 100644 index 00000000..362d6127 --- /dev/null +++ b/presentation/deck/vendor/reveal/.npmignore @@ -0,0 +1,5 @@ +/test +/examples +.github +.sass-cache +gulpfile.js diff --git a/presentation/deck/vendor/reveal/LICENSE b/presentation/deck/vendor/reveal/LICENSE new file mode 100644 index 00000000..38ff0eb4 --- /dev/null +++ b/presentation/deck/vendor/reveal/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2011-2024 Hakim El Hattab, http://hakim.se, and reveal.js contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/README.md b/presentation/deck/vendor/reveal/README.md new file mode 100644 index 00000000..19956851 --- /dev/null +++ b/presentation/deck/vendor/reveal/README.md @@ -0,0 +1,50 @@ +

+ + reveal.js + +

+ + Slides +

+ +reveal.js is an open source HTML presentation framework. It enables anyone with a web browser to create beautiful presentations for free. Check out the live demo at [revealjs.com](https://revealjs.com/). + +The framework comes with a powerful feature set including [nested slides](https://revealjs.com/vertical-slides/), [Markdown support](https://revealjs.com/markdown/), [Auto-Animate](https://revealjs.com/auto-animate/), [PDF export](https://revealjs.com/pdf-export/), [speaker notes](https://revealjs.com/speaker-view/), [LaTeX typesetting](https://revealjs.com/math/), [syntax highlighted code](https://revealjs.com/code/) and an [extensive API](https://revealjs.com/api/). + +--- + +Want to create reveal.js presentation in a graphical editor? Try . It's made by the same people behind reveal.js. + +--- + +### Sponsors +Hakim's open source work is supported by GitHub sponsors. Special thanks to: +
+ + +
+ +
+ WorkOS +
+ Your app, enterprise-ready. +
+ Start selling to enterprise customers with just a few lines of code. Add Single Sign-On (and more) in minutes instead of months. +
+
+
+
+ +--- + +### Getting started +- 🚀 [Install reveal.js](https://revealjs.com/installation) +- 👀 [View the demo presentation](https://revealjs.com/demo) +- 📖 [Read the documentation](https://revealjs.com/markup/) +- 🖌 [Try the visual editor for reveal.js at Slides.com](https://slides.com/) +- 🎬 [Watch the reveal.js video course (paid)](https://revealjs.com/course) + +--- +
+ MIT licensed | Copyright © 2011-2024 Hakim El Hattab, https://hakim.se +
diff --git a/presentation/deck/vendor/reveal/css/layout.scss b/presentation/deck/vendor/reveal/css/layout.scss new file mode 100644 index 00000000..5d0536f9 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/layout.scss @@ -0,0 +1,70 @@ +/** + * Layout helpers. + */ + +// Stretch an element vertically based on available space +.reveal .stretch, +.reveal .r-stretch { + max-width: none; + max-height: none; +} + +.reveal pre.stretch code, +.reveal pre.r-stretch code { + height: 100%; + max-height: 100%; + box-sizing: border-box; +} + +// Text that auto-fits its container +.reveal .r-fit-text { + display: inline-block; // https://github.com/rikschennink/fitty#performance + white-space: nowrap; +} + +// Stack multiple elements on top of each other +.reveal .r-stack { + display: grid; + grid-template-rows: 100%; +} + +.reveal .r-stack > * { + grid-area: 1/1; + margin: auto; +} + +// Horizontal and vertical stacks +.reveal .r-vstack, +.reveal .r-hstack { + display: flex; + + img, video { + min-width: 0; + min-height: 0; + object-fit: contain; + } +} + +.reveal .r-vstack { + flex-direction: column; + align-items: center; + justify-content: center; +} + +.reveal .r-hstack { + flex-direction: row; + align-items: center; + justify-content: center; +} + +// Naming based on tailwindcss +.reveal .items-stretch { align-items: stretch; } +.reveal .items-start { align-items: flex-start; } +.reveal .items-center { align-items: center; } +.reveal .items-end { align-items: flex-end; } + +.reveal .justify-between { justify-content: space-between; } +.reveal .justify-around { justify-content: space-around; } +.reveal .justify-start { justify-content: flex-start; } +.reveal .justify-center { justify-content: center; } +.reveal .justify-end { justify-content: flex-end; } diff --git a/presentation/deck/vendor/reveal/css/print/paper.scss b/presentation/deck/vendor/reveal/css/print/paper.scss new file mode 100644 index 00000000..32fab8a4 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/print/paper.scss @@ -0,0 +1,166 @@ + +@media print { + html:not(.print-pdf) { + overflow: visible; + width: auto; + height: auto; + + body { + margin: 0; + padding: 0; + overflow: visible; + } + } + + html:not(.print-pdf) .reveal { + background: #fff; + font-size: 20pt; + + .controls, + .state-background, + .progress, + .backgrounds, + .slide-number { + display: none !important; + } + + p, td, li { + font-size: 20pt!important; + color: #000; + } + + h1,h2,h3,h4,h5,h6 { + color: #000!important; + height: auto; + line-height: normal; + text-align: left; + letter-spacing: normal; + } + + h1 { font-size: 28pt !important; } + h2 { font-size: 24pt !important; } + h3 { font-size: 22pt !important; } + h4 { font-size: 22pt !important; font-variant: small-caps; } + h5 { font-size: 21pt !important; } + h6 { font-size: 20pt !important; font-style: italic; } + + a:link, + a:visited { + color: #000 !important; + font-weight: bold; + text-decoration: underline; + } + + ul, ol, div, p { + visibility: visible; + position: static; + width: auto; + height: auto; + display: block; + overflow: visible; + margin: 0; + text-align: left !important; + } + pre, + table { + margin-left: 0; + margin-right: 0; + } + pre code { + padding: 20px; + } + blockquote { + margin: 20px 0; + } + + .slides { + position: static !important; + width: auto !important; + height: auto !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 0 !important; + zoom: 1 !important; + transform: none !important; + + overflow: visible !important; + display: block !important; + + text-align: left !important; + perspective: none; + + perspective-origin: 50% 50%; + } + .slides section { + visibility: visible !important; + position: static !important; + width: auto !important; + height: auto !important; + display: block !important; + overflow: visible !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 60px 20px !important; + z-index: auto !important; + + opacity: 1 !important; + + page-break-after: always !important; + + transform-style: flat !important; + transform: none !important; + transition: none !important; + } + .slides section.stack { + padding: 0 !important; + } + .slides section:last-of-type { + page-break-after: avoid !important; + } + .slides section .fragment { + opacity: 1 !important; + visibility: visible !important; + + transform: none !important; + } + + .r-fit-text { + white-space: normal !important; + } + + section img { + display: block; + margin: 15px 0px; + background: rgba(255,255,255,1); + border: 1px solid #666; + box-shadow: none; + } + + section small { + font-size: 0.8em; + } + + .hljs { + max-height: 100%; + white-space: pre-wrap; + word-wrap: break-word; + word-break: break-word; + font-size: 15pt; + } + + .hljs .hljs-ln-numbers { + white-space: nowrap; + } + + .hljs td { + font-size: inherit !important; + color: inherit !important; + } + } +} diff --git a/presentation/deck/vendor/reveal/css/print/pdf.scss b/presentation/deck/vendor/reveal/css/print/pdf.scss new file mode 100644 index 00000000..0a1c2bf6 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/print/pdf.scss @@ -0,0 +1,159 @@ +/** + * This stylesheet is used to print reveal.js + * presentations to PDF. + * + * https://revealjs.com/pdf-export/ + */ + +html.reveal-print { + * { + -webkit-print-color-adjust: exact; + } + + & { + width: 100%; + height: 100%; + overflow: visible; + } + + body { + margin: 0 auto !important; + border: 0; + padding: 0; + float: none !important; + overflow: visible; + } + + /* Remove any elements not needed in print. */ + .nestedarrow, + .reveal .controls, + .reveal .progress, + .reveal .playback, + .reveal.overview, + .state-background { + display: none !important; + } + + .reveal pre code { + overflow: hidden !important; + } + + .reveal { + width: auto !important; + height: auto !important; + overflow: hidden !important; + } + .reveal .slides { + position: static; + width: 100% !important; + height: auto !important; + zoom: 1 !important; + pointer-events: initial; + + left: auto; + top: auto; + margin: 0 !important; + padding: 0 !important; + + overflow: visible; + display: block; + + perspective: none; + perspective-origin: 50% 50%; + } + + .reveal .slides .pdf-page { + position: relative; + overflow: hidden; + z-index: 1; + + page-break-after: always; + } + + .reveal .slides .pdf-page:last-of-type { + page-break-after: avoid; + } + + .reveal .slides section { + visibility: visible !important; + display: block !important; + position: absolute !important; + + margin: 0 !important; + padding: 0 !important; + box-sizing: border-box !important; + min-height: 1px; + + opacity: 1 !important; + + transform-style: flat !important; + transform: none !important; + } + + .reveal section.stack { + position: relative !important; + margin: 0 !important; + padding: 0 !important; + page-break-after: avoid !important; + height: auto !important; + min-height: auto !important; + } + + .reveal img { + box-shadow: none; + } + + /* Slide backgrounds are placed inside of their slide when exporting to PDF */ + .reveal .backgrounds { + display: none; + } + .reveal .slide-background { + display: block !important; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: auto !important; + } + + /* Display slide speaker notes when 'showNotes' is enabled */ + .reveal.show-notes { + max-width: none; + max-height: none; + } + .reveal .speaker-notes-pdf { + display: block; + width: 100%; + height: auto; + max-height: none; + top: auto; + right: auto; + bottom: auto; + left: auto; + z-index: 100; + } + + /* Layout option which makes notes appear on a separate page */ + .reveal .speaker-notes-pdf[data-layout="separate-page"] { + position: relative; + color: inherit; + background-color: transparent; + padding: 20px; + page-break-after: always; + border: 0; + } + + /* Display slide numbers when 'slideNumber' is enabled */ + .reveal .slide-number-pdf { + display: block; + position: absolute; + font-size: 14px; + visibility: visible; + } + + /* This accessibility tool is not useful in PDF and breaks it visually */ + .aria-status { + display: none; + } +} diff --git a/presentation/deck/vendor/reveal/css/reveal.scss b/presentation/deck/vendor/reveal/css/reveal.scss new file mode 100644 index 00000000..0bd0da35 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/reveal.scss @@ -0,0 +1,2114 @@ +@use "sass:math"; + +/** + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) Hakim El Hattab, https://hakim.se + */ + +@import 'layout'; + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +html.reveal-full-page { + width: 100%; + height: 100%; + height: 100vh; + height: calc( var(--vh, 1vh) * 100 ); + height: 100svh; + overflow: hidden; +} + +.reveal-viewport { + height: 100%; + overflow: hidden; + position: relative; + line-height: 1; + margin: 0; + + background-color: #fff; + color: #000; + + --r-controls-spacing: 12px; +} + +// Force the presentation to cover the full viewport when we +// enter fullscreen mode. Fixes sizing issues in Safari. +.reveal-viewport:fullscreen { + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + transform: none !important; +} + + +/********************************************* + * VIEW FRAGMENTS + *********************************************/ + +.reveal .fragment { + transition: all .2s ease; + + &:not(.custom) { + opacity: 0; + visibility: hidden; + will-change: opacity; + } + + &.visible { + opacity: 1; + visibility: inherit; + } + + &.disabled { + transition: none; + } +} + +.reveal .fragment.grow { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 1.3 ); + } +} + +.reveal .fragment.shrink { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 0.7 ); + } +} + +.reveal .fragment.zoom-in { + transform: scale( 0.1 ); + + &.visible { + transform: none; + } +} + +.reveal .fragment.fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0; + visibility: hidden; + } +} + +.reveal .fragment.semi-fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0.5; + visibility: inherit; + } +} + +.reveal .fragment.strike { + opacity: 1; + visibility: inherit; + + &.visible { + text-decoration: line-through; + } +} + +.reveal .fragment.fade-up { + transform: translate(0, 40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .fragment.fade-down { + transform: translate(0, -40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .fragment.fade-right { + transform: translate(-40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .fragment.fade-left { + transform: translate(40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .fragment.fade-in-then-out, +.reveal .fragment.current-visible { + opacity: 0; + visibility: hidden; + + &.current-fragment { + opacity: 1; + visibility: inherit; + } +} + +.reveal .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; + + &.visible { + opacity: 0.5; + visibility: inherit; + } + + &.current-fragment { + opacity: 1; + visibility: inherit; + } +} + +.reveal .fragment.highlight-red, +.reveal .fragment.highlight-current-red, +.reveal .fragment.highlight-green, +.reveal .fragment.highlight-current-green, +.reveal .fragment.highlight-blue, +.reveal .fragment.highlight-current-blue { + opacity: 1; + visibility: inherit; +} + .reveal .fragment.highlight-red.visible { + color: #ff2c2d + } + .reveal .fragment.highlight-green.visible { + color: #17ff2e; + } + .reveal .fragment.highlight-blue.visible { + color: #1b91ff; + } + +.reveal .fragment.highlight-current-red.current-fragment { + color: #ff2c2d +} +.reveal .fragment.highlight-current-green.current-fragment { + color: #17ff2e; +} +.reveal .fragment.highlight-current-blue.current-fragment { + color: #1b91ff; +} + + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ + +/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ +.reveal:after { + content: ''; + font-style: italic; +} + +.reveal iframe { + z-index: 1; +} + +/** Prevents layering issues in certain browser/transition combinations */ +.reveal a { + position: relative; +} + + +/********************************************* + * CONTROLS + *********************************************/ + +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(10px);} + 30% {transform: translateX(-5px);} +} + +@keyframes bounce-left { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(-10px);} + 30% {transform: translateX(5px);} +} + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% {transform: translateY(0);} + 20% {transform: translateY(10px);} + 30% {transform: translateY(-5px);} +} + +$controlArrowSize: 3.6em; +$controlArrowSpacing: 1.4em; +$controlArrowLength: 2.6em; +$controlArrowThickness: 0.5em; +$controlsArrowAngle: 45deg; +$controlsArrowAngleHover: 40deg; +$controlsArrowAngleActive: 36deg; + +@mixin controlsArrowTransform( $angle ) { + &:before { + transform: translateX(($controlArrowSize - $controlArrowLength)*0.5) translateY(($controlArrowSize - $controlArrowThickness)*0.5) rotate( $angle ); + } + + &:after { + transform: translateX(($controlArrowSize - $controlArrowLength)*0.5) translateY(($controlArrowSize - $controlArrowThickness)*0.5) rotate( -$angle ); + } +} + +.reveal .controls { + display: none; + position: absolute; + top: auto; + bottom: var(--r-controls-spacing); + right: var(--r-controls-spacing); + left: auto; + z-index: 11; + color: #000; + pointer-events: none; + font-size: 10px; + + button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + transform: scale(.9999); + transition: color 0.2s ease, + opacity 0.2s ease, + transform 0.2s ease; + z-index: 2; // above slides + pointer-events: auto; + font-size: inherit; + + visibility: hidden; + opacity: 0; + + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + } + + .controls-arrow:before, + .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: $controlArrowLength; + height: $controlArrowThickness; + border-radius: $controlArrowThickness*0.5; + background-color: currentColor; + + transition: all 0.15s ease, background-color 0.8s ease; + transform-origin: math.div(floor(($controlArrowThickness*0.5)*10), 10) 50%; + will-change: transform; + } + + .controls-arrow { + position: relative; + width: $controlArrowSize; + height: $controlArrowSize; + + @include controlsArrowTransform( $controlsArrowAngle ); + + &:hover { + @include controlsArrowTransform( $controlsArrowAngleHover ); + } + + &:active { + @include controlsArrowTransform( $controlsArrowAngleActive ); + } + } + + .navigate-left { + right: $controlArrowSize + $controlArrowSpacing*2; + bottom: $controlArrowSpacing + $controlArrowSize*0.5; + transform: translateX( -10px ); + + &.highlight { + animation: bounce-left 2s 50 both ease-out; + } + } + + .navigate-right { + right: 0; + bottom: $controlArrowSpacing + $controlArrowSize*0.5; + transform: translateX( 10px ); + + .controls-arrow { + transform: rotate( 180deg ); + } + + &.highlight { + animation: bounce-right 2s 50 both ease-out; + } + } + + .navigate-up { + right: $controlArrowSpacing + $controlArrowSize*0.5; + bottom: $controlArrowSpacing*2 + $controlArrowSize; + transform: translateY( -10px ); + + .controls-arrow { + transform: rotate( 90deg ); + } + } + + .navigate-down { + right: $controlArrowSpacing + $controlArrowSize*0.5; + bottom: -$controlArrowSpacing; + padding-bottom: $controlArrowSpacing; + transform: translateY( 10px ); + + .controls-arrow { + transform: rotate( -90deg ); + } + + &.highlight { + animation: bounce-down 2s 50 both ease-out; + } + } + + // Back arrow style: "faded": + // Deemphasize backwards navigation arrows in favor of drawing + // attention to forwards navigation + &[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show arrows for backwards navigation + &[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; + } + + // Any control button that can be clicked is "enabled" + .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + transform: none; + } + + // Any control button that leads to showing or hiding + // a fragment + .enabled.fragmented { + opacity: 0.5; + } + + .enabled:hover, + .enabled.fragmented:hover { + opacity: 1; + } +} + +.reveal:not(.rtl) .controls { + // Back arrow style: "faded": + // Deemphasize left arrow + &[data-controls-back-arrows="faded"] .navigate-left.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show left arrow + &[data-controls-back-arrows="hidden"] .navigate-left.enabled { + opacity: 0; + visibility: hidden; + } +} + +.reveal.rtl .controls { + // Back arrow style: "faded": + // Deemphasize right arrow in RTL mode + &[data-controls-back-arrows="faded"] .navigate-right.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show right arrow in RTL mode + &[data-controls-back-arrows="hidden"] .navigate-right.enabled { + opacity: 0; + visibility: hidden; + } +} + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-up, +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-down { + display: none; +} + +// Adjust the layout when there are no vertical slides +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-left, +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: $controlArrowSpacing; + right: 0.5em + $controlArrowSpacing + $controlArrowSize; +} + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-right, +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: $controlArrowSpacing; + right: 0.5em; +} + +// Adjust the layout when there are no horizontal slides +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: $controlArrowSpacing; + bottom: $controlArrowSpacing + $controlArrowSize; +} +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: $controlArrowSpacing; + bottom: 0.5em; +} + +// Invert arrows based on background color +.reveal.has-dark-background .controls { + color: #fff; +} +.reveal.has-light-background .controls { + color: #000; +} + +// Disable active states on touch devices +.reveal.no-hover .controls .controls-arrow:hover, +.reveal.no-hover .controls .controls-arrow:active { + @include controlsArrowTransform( $controlsArrowAngle ); +} + +// Edge aligned controls layout +@media screen and (min-width: 500px) { + + .reveal-viewport { + --r-controls-spacing: 0.8em; + } + + .reveal .controls[data-controls-layout="edges"] { + & { + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + .navigate-left, + .navigate-right, + .navigate-up, + .navigate-down { + bottom: auto; + right: auto; + } + + .navigate-left { + top: 50%; + left: var(--r-controls-spacing); + margin-top: -$controlArrowSize*0.5; + } + + .navigate-right { + top: 50%; + right: var(--r-controls-spacing); + margin-top: -$controlArrowSize*0.5; + } + + .navigate-up { + top: var(--r-controls-spacing); + left: 50%; + margin-left: -$controlArrowSize*0.5; + } + + .navigate-down { + bottom: calc(var(--r-controls-spacing) - #{$controlArrowSpacing} + 0.3em); + left: 50%; + margin-left: -$controlArrowSize*0.5; + } + } + +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + position: absolute; + display: none; + height: 3px; + width: 100%; + bottom: 0; + left: 0; + z-index: 10; + + background-color: rgba( 0, 0, 0, 0.2 ); + color: #fff; +} + .reveal .progress:after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + top: -10px; + } + .reveal .progress span { + display: block; + height: 100%; + width: 100%; + + background-color: currentColor; + transition: transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transform-origin: 0 0; + transform: scaleX(0); + } + +/********************************************* + * SLIDE NUMBER + *********************************************/ + +.reveal .slide-number { + position: absolute; + display: block; + right: 8px; + bottom: 8px; + z-index: 31; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba( 0, 0, 0, 0.4 ); + padding: 5px; +} + +.reveal .slide-number a { + color: currentColor; +} + +.reveal .slide-number-delimiter { + margin: 0 3px; +} + +/********************************************* + * SLIDES + *********************************************/ + +.reveal { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + touch-action: pinch-zoom; +} + +// Swiping on an embedded deck should not block page scrolling... +.reveal.embedded { + touch-action: pan-y; +} + +// ... unless we're on a vertical slide +.reveal.embedded.is-vertical-slide { + touch-action: none; +} + +.reveal .slides { + position: absolute; + width: 100%; + height: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + pointer-events: none; + + overflow: visible; + z-index: 1; + text-align: center; + perspective: 600px; + perspective-origin: 50% 40%; +} + +.reveal .slides>section { + perspective: 600px; +} + +.reveal .slides>section, +.reveal .slides>section>section { + display: none; + position: absolute; + width: 100%; + pointer-events: auto; + + z-index: 10; + transform-style: flat; + transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); +} + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] .slides section { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"] .slides section { + transition-duration: 1200ms; +} + +/* Slide-specific transition speed overrides */ +.reveal .slides section[data-transition-speed="fast"] { + transition-duration: 400ms; +} +.reveal .slides section[data-transition-speed="slow"] { + transition-duration: 1200ms; +} + +.reveal .slides>section.stack { + padding-top: 0; + padding-bottom: 0; + pointer-events: none; + height: 100%; +} + +.reveal .slides>section.present, +.reveal .slides>section>section.present { + display: block; + z-index: 11; + opacity: 1; +} + +.reveal .slides>section:empty, +.reveal .slides>section>section:empty, +.reveal .slides>section[data-background-interactive], +.reveal .slides>section>section[data-background-interactive] { + pointer-events: none; +} + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { + min-height: 0 !important; +} + +/* Don't allow interaction with invisible slides */ +.reveal .slides>section:not(.present), +.reveal .slides>section>section:not(.present) { + pointer-events: none; +} + +.reveal.overview .slides>section, +.reveal.overview .slides>section>section { + pointer-events: auto; +} + +.reveal .slides>section.past, +.reveal .slides>section.future, +.reveal .slides>section.past>section, +.reveal .slides>section.future>section, +.reveal .slides>section>section.past, +.reveal .slides>section>section.future { + opacity: 0; +} + + +/********************************************* + * Mixins for readability of transitions + *********************************************/ + +@mixin transition-global($style) { + .reveal .slides section[data-transition=#{$style}], + .reveal.#{$style} .slides section:not([data-transition]) { + @content; + } +} +@mixin transition-stack($style) { + .reveal .slides section[data-transition=#{$style}].stack, + .reveal.#{$style} .slides section.stack { + @content; + } +} +@mixin transition-horizontal-past($style) { + .reveal .slides>section[data-transition=#{$style}].past, + .reveal .slides>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section:not([data-transition]).past { + @content; + } +} +@mixin transition-horizontal-future($style) { + .reveal .slides>section[data-transition=#{$style}].future, + .reveal .slides>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section:not([data-transition]).future { + @content; + } +} + +@mixin transition-vertical-past($style) { + .reveal .slides>section>section[data-transition=#{$style}].past, + .reveal .slides>section>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section>section:not([data-transition]).past { + @content; + } +} +@mixin transition-vertical-future($style) { + .reveal .slides>section>section[data-transition=#{$style}].future, + .reveal .slides>section>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section>section:not([data-transition]).future { + @content; + } +} + +/********************************************* + * SLIDE TRANSITION + * Aliased 'linear' for backwards compatibility + *********************************************/ + +@each $stylename in slide, linear { + @include transition-horizontal-past(#{$stylename}) { + transform: translate(-150%, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate(150%, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate(0, -150%); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate(0, 150%); + } +} + +/********************************************* + * CONVEX TRANSITION + * Aliased 'default' for backwards compatibility + *********************************************/ + +@each $stylename in default, convex { + @include transition-stack(#{$stylename}) { + transform-style: preserve-3d; + } + + @include transition-horizontal-past(#{$stylename}) { + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + } +} + +/********************************************* + * CONCAVE TRANSITION + *********************************************/ + +@include transition-stack(concave) { + transform-style: preserve-3d; +} + +@include transition-horizontal-past(concave) { + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +@include transition-horizontal-future(concave) { + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} +@include transition-vertical-past(concave) { + transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); +} +@include transition-vertical-future(concave) { + transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); +} + + +/********************************************* + * ZOOM TRANSITION + *********************************************/ + +@include transition-global(zoom) { + transition-timing-function: ease; +} +@include transition-horizontal-past(zoom) { + visibility: hidden; + transform: scale(16); +} +@include transition-horizontal-future(zoom) { + visibility: hidden; + transform: scale(0.2); +} +@include transition-vertical-past(zoom) { + transform: scale(16); +} +@include transition-vertical-future(zoom) { + transform: scale(0.2); +} + + +/********************************************* + * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.cube .slides { + perspective: 1300px; +} + +.reveal.cube .slides section { + padding: 30px; + min-height: 700px; + backface-visibility: hidden; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.center.cube .slides section { + min-height: 0; + } + .reveal.cube .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + border-radius: 4px; + transform: translateZ( -20px ); + } + .reveal.cube .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.cube .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.cube .slides>section.past { + transform-origin: 100% 0%; + transform: translate3d(-100%, 0, 0) rotateY(-90deg); +} + +.reveal.cube .slides>section.future { + transform-origin: 0% 0%; + transform: translate3d(100%, 0, 0) rotateY(90deg); +} + +.reveal.cube .slides>section>section.past { + transform-origin: 0% 100%; + transform: translate3d(0, -100%, 0) rotateX(90deg); +} + +.reveal.cube .slides>section>section.future { + transform-origin: 0% 0%; + transform: translate3d(0, 100%, 0) rotateX(-90deg); +} + + +/********************************************* + * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.page .slides { + perspective-origin: 0% 50%; + perspective: 3000px; +} + +.reveal.page .slides section { + padding: 30px; + min-height: 700px; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.page .slides section.past { + z-index: 12; + } + .reveal.page .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + transform: translateZ( -20px ); + } + .reveal.page .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + + -webkit-transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.page .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.page .slides>section.past { + transform-origin: 0% 0%; + transform: translate3d(-40%, 0, 0) rotateY(-80deg); +} + +.reveal.page .slides>section.future { + transform-origin: 100% 0%; + transform: translate3d(0, 0, 0); +} + +.reveal.page .slides>section>section.past { + transform-origin: 0% 0%; + transform: translate3d(0, -40%, 0) rotateX(80deg); +} + +.reveal.page .slides>section>section.future { + transform-origin: 0% 100%; + transform: translate3d(0, 0, 0); +} + + +/********************************************* + * FADE TRANSITION + *********************************************/ + +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides>section>section:not([data-transition]) { + transform: none; + transition: opacity 0.5s; +} + + +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides>section>section { + transition: none; +} + + +/********************************************* + * NO TRANSITION + *********************************************/ + +@include transition-global(none) { + transform: none; + transition: none; +} + + +/********************************************* + * PAUSED MODE + *********************************************/ + +.reveal .pause-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + visibility: hidden; + opacity: 0; + z-index: 100; + transition: all 1s ease; +} + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; + + &:hover { + color: #fff; + border-color: #fff; + } +} + +.reveal.paused .pause-overlay { + visibility: visible; + opacity: 1; +} + + +/********************************************* + * FALLBACK + *********************************************/ + +.reveal .no-transition, +.reveal .no-transition *, +.reveal .slides.disable-slide-transitions section { + transition: none !important; +} + +.reveal .slides.disable-slide-transitions section { + transform: none !important; +} + + +/********************************************* + * PER-SLIDE BACKGROUNDS + *********************************************/ + +.reveal .backgrounds { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + perspective: 600px; +} + .reveal .slide-background { + display: none; + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + overflow: hidden; + + background-color: rgba( 0, 0, 0, 0 ); + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + } + + .reveal .slide-background.stack { + display: block; + } + + .reveal .slide-background.present { + opacity: 1; + visibility: visible; + z-index: 2; + } + + .print-pdf .reveal .slide-background { + opacity: 1 !important; + visibility: visible !important; + } + +/* Video backgrounds */ +.reveal .slide-background video { + position: absolute; + width: 100%; + height: 100%; + max-width: none; + max-height: none; + top: 0; + left: 0; + object-fit: cover; +} + .reveal .slide-background[data-background-size="contain"] video { + object-fit: contain; + } + +/* Immediate transition style */ +.reveal[data-background-transition=none]>.backgrounds .slide-background:not([data-background-transition]), +.reveal>.backgrounds .slide-background[data-background-transition=none] { + transition: none; +} + +/* Slide */ +.reveal[data-background-transition=slide]>.backgrounds .slide-background:not([data-background-transition]), +.reveal>.backgrounds .slide-background[data-background-transition=slide] { + opacity: 1; +} + .reveal[data-background-transition=slide]>.backgrounds .slide-background.past:not([data-background-transition]), + .reveal>.backgrounds .slide-background.past[data-background-transition=slide] { + transform: translate(-100%, 0); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background.future:not([data-background-transition]), + .reveal>.backgrounds .slide-background.future[data-background-transition=slide] { + transform: translate(100%, 0); + } + + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]), + .reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide] { + transform: translate(0, -100%); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]), + .reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide] { + transform: translate(0, 100%); + } + + +/* Convex */ +.reveal[data-background-transition=convex]>.backgrounds .slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); +} + + +/* Concave */ +.reveal[data-background-transition=concave]>.backgrounds .slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); +} + +/* Zoom */ +.reveal[data-background-transition=zoom]>.backgrounds .slide-background:not([data-background-transition]), +.reveal>.backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]), +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"]>.backgrounds .slide-background { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"]>.backgrounds .slide-background { + transition-duration: 1200ms; +} + + +/********************************************* + * AUTO ANIMATE + *********************************************/ + +.reveal [data-auto-animate-target^="unmatched"] { + will-change: opacity; +} + +.reveal section[data-auto-animate]:not(.stack):not([data-auto-animate="running"]) [data-auto-animate-target^="unmatched"] { + opacity: 0; +} + + +/********************************************* + * OVERVIEW + *********************************************/ + +.reveal.overview { + perspective-origin: 50% 50%; + perspective: 700px; + + .slides { + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .slides section { + height: 100%; + top: 0 !important; + opacity: 1 !important; + overflow: hidden; + visibility: visible !important; + cursor: pointer; + box-sizing: border-box; + } + .slides section:hover, + .slides section.present { + outline: 10px solid rgba(150,150,150,0.4); + outline-offset: 10px; + } + .slides section .fragment { + opacity: 1; + transition: none; + } + .slides section:after, + .slides section:before { + display: none !important; + } + .slides>section.stack { + padding: 0; + top: 0 !important; + background: none; + outline: none; + overflow: visible; + } + + .backgrounds { + perspective: inherit; + + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .backgrounds .slide-background { + opacity: 1; + visibility: visible; + + // This can't be applied to the slide itself in Safari + outline: 10px solid rgba(150,150,150,0.1); + outline-offset: 10px; + } + + .backgrounds .slide-background.stack { + overflow: visible; + } +} + +// Disable transitions transitions while we're activating +// or deactivating the overview mode. +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; +} + +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; +} + + +/********************************************* + * RTL SUPPORT + *********************************************/ + +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { + direction: rtl; + font-family: sans-serif; +} + +.reveal.rtl pre, +.reveal.rtl code { + direction: ltr; +} + +.reveal.rtl ol, +.reveal.rtl ul { + text-align: right; +} + +.reveal.rtl .progress span { + transform-origin: 100% 0; +} + +/********************************************* + * PARALLAX BACKGROUND + *********************************************/ + +.reveal.has-parallax-background .backgrounds { + transition: all 0.8s ease; +} + +/* Global transition speed settings */ +.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { + transition-duration: 400ms; +} +.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { + transition-duration: 1200ms; +} + + +/********************************************* + * OVERLAY FOR LINK PREVIEWS AND HELP + *********************************************/ + +$overlayHeaderHeight: 40px; +$overlayHeaderPadding: 5px; + +.reveal > .overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + background: rgba( 0, 0, 0, 0.95 ); + backdrop-filter: blur( 6px ); + transition: all 0.3s ease; +} + + .reveal > .overlay .spinner { + position: absolute; + display: block; + top: 50%; + left: 50%; + width: 32px; + height: 32px; + margin: -16px 0 0 -16px; + z-index: 10; + background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); + + visibility: visible; + opacity: 0.6; + transition: all 0.3s ease; + } + + .reveal > .overlay header { + position: absolute; + left: 0; + top: 0; + width: 100%; + padding: $overlayHeaderPadding; + z-index: 2; + box-sizing: border-box; + } + .reveal > .overlay header a { + display: inline-block; + width: $overlayHeaderHeight; + height: $overlayHeaderHeight; + line-height: 36px; + padding: 0 10px; + float: right; + opacity: 0.6; + + box-sizing: border-box; + } + .reveal > .overlay header a:hover { + opacity: 1; + } + .reveal > .overlay header a .icon { + display: inline-block; + width: 20px; + height: 20px; + + background-position: 50% 50%; + background-size: 100%; + background-repeat: no-repeat; + } + .reveal > .overlay header a.close .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); + } + .reveal > .overlay header a.external .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); + } + + .reveal > .overlay .viewport { + position: absolute; + display: flex; + top: $overlayHeaderHeight + $overlayHeaderPadding*2; + right: 0; + bottom: 0; + left: 0; + } + + .reveal > .overlay.overlay-preview .viewport iframe { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: 0; + + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .reveal > .overlay.overlay-preview.loaded .viewport iframe { + opacity: 1; + visibility: visible; + } + + .reveal > .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; + } + .reveal > .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; + } + .reveal > .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; + } + + .reveal > .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + transform: scale(0.2); + } + + .reveal > .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner .title { + font-size: 20px; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table { + border: 1px solid #fff; + border-collapse: collapse; + font-size: 16px; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table th, + .reveal > .overlay.overlay-help .viewport .viewport-inner table td { + width: 200px; + padding: 14px; + border: 1px solid #fff; + vertical-align: middle; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table th { + padding-top: 20px; + padding-bottom: 20px; + } + +/********************************************* + * PLAYBACK COMPONENT + *********************************************/ + +.reveal .playback { + position: absolute; + left: 15px; + bottom: 20px; + z-index: 30; + cursor: pointer; + transition: all 400ms ease; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); +} + +.reveal.overview .playback { + opacity: 0; + visibility: hidden; +} + + +/********************************************* + * CODE HIGHLGIHTING + *********************************************/ + +.reveal .hljs { + min-height: 100%; +} + +.reveal .hljs table { + margin: initial; +} + +.reveal .hljs-ln-code, +.reveal .hljs-ln-numbers { + padding: 0; + border: 0; +} + +.reveal .hljs-ln-numbers { + opacity: 0.6; + padding-right: 0.75em; + text-align: right; + vertical-align: top; +} + +.reveal .hljs.has-highlights tr:not(.highlight-line) { + opacity: 0.4; +} + +.reveal .hljs.has-highlights.fragment { + transition: all .2s ease; +} + +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; +} + +.reveal pre[data-auto-animate-target] { + overflow: hidden; +} +.reveal pre[data-auto-animate-target] code { + height: 100%; +} + + +/********************************************* + * ROLLING LINKS + *********************************************/ + +.reveal .roll { + display: inline-block; + line-height: 1.2; + overflow: hidden; + + vertical-align: top; + perspective: 400px; + perspective-origin: 50% 50%; +} + .reveal .roll:hover { + background: none; + text-shadow: none; + } +.reveal .roll span { + display: block; + position: relative; + padding: 0 2px; + + pointer-events: none; + transition: all 400ms ease; + transform-origin: 50% 0%; + transform-style: preserve-3d; + backface-visibility: hidden; +} + .reveal .roll:hover span { + background: rgba(0,0,0,0.5); + transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); + } +.reveal .roll span:after { + content: attr(data-title); + + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0 2px; + backface-visibility: hidden; + transform-origin: 50% 0%; + transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); +} + + +/********************************************* + * SPEAKER NOTES + *********************************************/ + +$notesWidthPercent: 25%; + +// Hide on-page notes +.reveal aside.notes { + display: none; +} + +// An interface element that can optionally be used to show the +// speaker notes to all viewers, on top of the presentation +.reveal .speaker-notes { + display: none; + position: absolute; + width: math.div($notesWidthPercent, (1 - math.div($notesWidthPercent,100))) * 1%; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba( 0, 0, 0, 0.05 ); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; + + .notes-placeholder { + color: #ccc; + font-style: italic; + } + + &:focus { + outline: none; + } + + &:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; + } +} + + +.reveal.show-notes { + max-width: 100% - $notesWidthPercent; + overflow: visible; +} + +.reveal.show-notes .speaker-notes { + display: block; +} + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; + } +} + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + max-height: 70vh; + overflow: visible; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: 30vh; + border: 0; + } +} + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + max-height: 60vh; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + height: 40vh; + } + + .reveal .speaker-notes { + font-size: 14px; + } +} + + +/********************************************* + * JUMP-TO-SLIDE COMPONENT + *********************************************/ + + .reveal .jump-to-slide { + position: absolute; + top: 15px; + left: 15px; + z-index: 30; + font-size: 32px; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); +} + +.reveal .jump-to-slide-input { + background: transparent; + padding: 8px; + font-size: inherit; + color: currentColor; + border: 0; +} +.reveal .jump-to-slide-input::placeholder { + color: currentColor; + opacity: 0.5; +} + +.reveal.has-dark-background .jump-to-slide-input { + color: #fff; +} +.reveal.has-light-background .jump-to-slide-input { + color: #222; +} + +.reveal .jump-to-slide-input:focus { + outline: none; +} + + +/********************************************* + * ZOOM PLUGIN + *********************************************/ + +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { + backface-visibility: visible !important; +} + +.zoomed .reveal .progress, +.zoomed .reveal .controls { + opacity: 0; +} + +.zoomed .reveal .roll span { + background: none; +} + +.zoomed .reveal .roll span:after { + visibility: hidden; +} + + +/********************************************* + * SCROLL VIEW + *********************************************/ +.reveal-viewport.loading-scroll-mode { + visibility: hidden; +} + +.reveal-viewport.reveal-scroll { + & { + margin: 0 auto; + overflow: auto; + overflow-x: hidden; + overflow-y: auto; + z-index: 1; + + --r-scrollbar-width: 7px; + --r-scrollbar-trigger-size: 5px; + --r-controls-spacing: 8px; + } + + @media screen and (max-width: 500px) { + --r-scrollbar-width: 3px; + --r-scrollbar-trigger-size: 3px; + } + + .controls, + .progress, + .playback, + .backgrounds, + .slide-number, + .speaker-notes { + display: none !important; + } + + .overlay, + .pause-overlay { + position: fixed; + } + + .reveal { + overflow: visible; + touch-action: manipulation; + } + + .slides { + position: static; + pointer-events: initial; + + left: auto; + top: auto; + width: 100% !important; + margin: 0; + padding: 0; + + overflow: visible; + display: block; + + perspective: none; + perspective-origin: 50% 50%; + } + + .scroll-page { + position: relative; + width: 100%; + height: calc(var(--page-height) + var(--page-scroll-padding)); + z-index: 1; + overflow: visible; + } + + .scroll-page-sticky { + position: sticky; + height: var(--page-height); + top: 0px; + } + + .scroll-page-content { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + } + + .scroll-page section { + visibility: visible !important; + display: block !important; + position: absolute !important; + width: var(--slide-width) !important; + height: var(--slide-height) !important; + top: 50% !important; + left: 50% !important; + opacity: 1 !important; + transform: scale(var(--slide-scale)) translate(-50%, -50%) !important; + transform-style: flat !important; + transform-origin: 0 0 !important; + } + + .slide-background { + display: block !important; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: auto !important; + visibility: visible; + opacity: 1; + touch-action: manipulation; + } +} + +// Chromium +.reveal-viewport.reveal-scroll[data-scrollbar="true"]::-webkit-scrollbar, +.reveal-viewport.reveal-scroll[data-scrollbar="auto"]::-webkit-scrollbar { + display: none; +} + +// Firefox +.reveal-viewport.reveal-scroll[data-scrollbar="true"], +.reveal-viewport.reveal-scroll[data-scrollbar="auto"] { + scrollbar-width: none; +} + +.reveal.has-dark-background, +.reveal-viewport.has-dark-background { + --r-overlay-element-bg-color: 240, 240, 240; + --r-overlay-element-fg-color: 0, 0, 0; +} +.reveal.has-light-background, +.reveal-viewport.has-light-background { + --r-overlay-element-bg-color: 0, 0, 0; + --r-overlay-element-fg-color: 240, 240, 240; +} + +.reveal-viewport.reveal-scroll .scrollbar { + position: sticky; + top: 50%; + z-index: 20; + opacity: 0; + transition: all 0.3s ease; + + &.visible, + &:hover { + opacity: 1; + } + + .scrollbar-inner { + position: absolute; + width: var(--r-scrollbar-width); + height: calc(var(--viewport-height) - var(--r-controls-spacing) * 2); + right: var(--r-controls-spacing); + top: 0; + transform: translateY(-50%); + border-radius: var(--r-scrollbar-width); + z-index: 10; + } + + .scrollbar-playhead { + position: absolute; + width: var(--r-scrollbar-width); + height: var(--r-scrollbar-width); + top: 0; + left: 0; + border-radius: var(--r-scrollbar-width); + background-color: rgba(var(--r-overlay-element-bg-color), 1); + z-index: 11; + transition: background-color 0.2s ease; + } + + .scrollbar-slide { + position: absolute; + width: 100%; + background-color: rgba(var(--r-overlay-element-bg-color), 0.2); + box-shadow: 0 0 0px 1px rgba(var(--r-overlay-element-fg-color), 0.1); + border-radius: var(--r-scrollbar-width); + transition: background-color 0.2s ease; + } + + // Hit area + .scrollbar-slide:after { + content: ''; + position: absolute; + width: 200%; + height: 100%; + top: 0; + left: -50%; + background: rgba( 0, 0, 0, 0 ); + z-index: -1; + } + + .scrollbar-slide:hover, + .scrollbar-slide.active { + background-color: rgba(var(--r-overlay-element-bg-color), 0.4); + } + + .scrollbar-trigger { + position: absolute; + width: 100%; + transition: background-color 0.2s ease; + } + + .scrollbar-slide.active.has-triggers { + background-color: rgba(var(--r-overlay-element-bg-color), 0.4); + z-index: 10; + } + + .scrollbar-slide.active .scrollbar-trigger:after { + content: ''; + position: absolute; + width: var(--r-scrollbar-trigger-size); + height: var(--r-scrollbar-trigger-size); + border-radius: 20px; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: rgba(var(--r-overlay-element-bg-color), 1); + transition: transform 0.2s ease, opacity 0.2s ease; + opacity: 0.4; + } + + .scrollbar-slide.active .scrollbar-trigger.active:after, + .scrollbar-slide.active .scrollbar-trigger.active ~ .scrollbar-trigger:after { + opacity: 1; + } + + .scrollbar-slide.active .scrollbar-trigger ~ .scrollbar-trigger.active:after { + transform: translate(calc( var(--r-scrollbar-width) * -2), 0); + background-color: rgba(var(--r-overlay-element-bg-color), 1); + } +} + + +/********************************************* + * PRINT STYLES + *********************************************/ + +@import 'print/pdf.scss'; +@import 'print/paper.scss'; + diff --git a/presentation/deck/vendor/reveal/css/theme/README.md b/presentation/deck/vendor/reveal/css/theme/README.md new file mode 100644 index 00000000..30916c45 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/README.md @@ -0,0 +1,21 @@ +## Dependencies + +Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment installed before proceeding: https://revealjs.com/installation/#full-setup + +## Creating a Theme + +To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled from Sass to CSS (see the [gulpfile](https://github.com/hakimel/reveal.js/blob/master/gulpfile.js)) when you run `npm run build -- css-themes`. + +Each theme file does four things in the following order: + +1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)** +Shared utility functions. + +2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)** +Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3. + +3. **Override** +This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding any selectors and styles you please. + +4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** +The template theme file which will generate final CSS output based on the currently defined variables. diff --git a/presentation/deck/vendor/reveal/css/theme/source/beige.scss b/presentation/deck/vendor/reveal/css/theme/source/beige.scss new file mode 100644 index 00000000..7598b94f --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/beige.scss @@ -0,0 +1,44 @@ +/** + * Beige theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(./fonts/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainColor: #333; +$headingColor: #333; +$headingTextShadow: none; +$backgroundColor: #f7f3de; +$linkColor: #8b743d; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(79, 64, 28, 0.99); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) ); +} + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/black-contrast.scss b/presentation/deck/vendor/reveal/css/theme/source/black-contrast.scss new file mode 100644 index 00000000..9e1a2caf --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/black-contrast.scss @@ -0,0 +1,49 @@ +/** + * Black compact & high contrast reveal.js theme, with headers not in capitals. + * + * By Peter Kehl. Based on black.(s)css by Hakim El Hattab, http://hakim.se + * + * - Keep the source similar to black.css - for easy comparison. + * - $mainFontSize controls code blocks, too (although under some ratio). + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(./fonts/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #000000; + +$mainColor: #fff; +$headingColor: #fff; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #42affa; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +// Change text colors against light slide backgrounds +@include light-bg-text-color(#000); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/black.scss b/presentation/deck/vendor/reveal/css/theme/source/black.scss new file mode 100644 index 00000000..7c655c4f --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/black.scss @@ -0,0 +1,46 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(./fonts/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #191919; + +$mainColor: #fff; +$headingColor: #fff; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #42affa; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: rgba( $linkColor, 0.75 ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +// Change text colors against light slide backgrounds +@include light-bg-text-color(#222); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/blood.scss b/presentation/deck/vendor/reveal/css/theme/source/blood.scss new file mode 100644 index 00000000..b5a86796 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/blood.scss @@ -0,0 +1,87 @@ +/** + * Blood theme for reveal.js + * Author: Walther http://github.com/Walther + * + * Designed to be used with highlight.js theme + * "monokai_sublime.css" available from + * https://github.com/isagalaev/highlight.js/ + * + * For other themes, change $codeBackground accordingly. + * + */ + + // Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + +// Include theme-specific fonts + +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); + +// Colors used in the theme +$blood: #a23; +$coal: #222; +$codeBackground: #23241f; + +$backgroundColor: $coal; + +// Main text +$mainFont: Ubuntu, 'sans-serif'; +$mainColor: #eee; + +// Headings +$headingFont: Ubuntu, 'sans-serif'; +$headingTextShadow: 2px 2px 2px $coal; + +// h1 shadow, borrowed humbly from +// (c) Default theme by Hakim El Hattab +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Links +$linkColor: $blood; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: $blood; +$selectionColor: #fff; + +// Change text colors against dark slide backgrounds +@include light-bg-text-color(#222); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- + +// some overrides after theme template import + +.reveal p { + font-weight: 300; + text-shadow: 1px 1px $coal; +} + +section.has-light-background { + p, h1, h2, h3, h4 { + text-shadow: none; + } +} + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + font-weight: 700; +} + +.reveal p code { + background-color: $codeBackground; + display: inline-block; + border-radius: 7px; +} + +.reveal small code { + vertical-align: baseline; +} \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/css/theme/source/dracula.scss b/presentation/deck/vendor/reveal/css/theme/source/dracula.scss new file mode 100644 index 00000000..ae968b8c --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/dracula.scss @@ -0,0 +1,106 @@ +/** + * Dracula Dark theme for reveal.js. + * Based on https://draculatheme.com + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +$systemFontsSansSerif: -apple-system, + BlinkMacSystemFont, + avenir next, + avenir, + segoe ui, + helvetica neue, + helvetica, + Cantarell, + Ubuntu, + roboto, + noto, + arial, + sans-serif; +$systemFontsMono: Menlo, + Consolas, + Monaco, + Liberation Mono, + Lucida Console, + monospace; + +/** + * Dracula colors by Zeno Rocha + * https://draculatheme.com/contribute + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +$background: #282A36; +$foreground: #F8F8F2; +$selection: #44475A; +$comment: #6272A4; +$red: #FF5555; +$orange: #FFB86C; +$yellow: #F1FA8C; +$green: #50FA7B; +$purple: #BD93F9; +$cyan: #8BE9FD; +$pink: #FF79C6; + + + +// Override theme settings (see ../template/settings.scss) +$mainColor: $foreground; +$headingColor: $purple; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: $background; +$linkColor: $pink; +$linkColorHover: $cyan; +$selectionBackgroundColor: $selection; +$inlineCodeColor: $green; +$listBulletColor: $cyan; + +$mainFont: $systemFontsSansSerif; +$codeFont: "Fira Code", $systemFontsMono; + +// Change text colors against light slide backgrounds +@include light-bg-text-color($background); + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- + +// Define additional color effects based on Dracula spec +// https://spec.draculatheme.com/ +:root { + --r-bold-color: #{$orange}; + --r-italic-color: #{$yellow}; + --r-inline-code-color: #{$inlineCodeColor}; + --r-list-bullet-color: #{$listBulletColor}; +} + +.reveal { + strong, b { + color: var(--r-bold-color); + } + em, i, blockquote { + color: var(--r-italic-color); + } + code { + color: var(--r-inline-code-color); + } + // Dracula colored list bullets and numbers + ul, ol { + li::marker { + color: var(--r-list-bullet-color); + } + } +} + diff --git a/presentation/deck/vendor/reveal/css/theme/source/league.scss b/presentation/deck/vendor/reveal/css/theme/source/league.scss new file mode 100644 index 00000000..ee012583 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/league.scss @@ -0,0 +1,36 @@ +/** + * League theme for reveal.js. + * + * This was the default theme pre-3.0.0. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(./fonts/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +// Override theme settings (see ../template/settings.scss) +$headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) ); +} + +// Change text colors against light slide backgrounds +@include light-bg-text-color(#222); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/moon.scss b/presentation/deck/vendor/reveal/css/theme/source/moon.scss new file mode 100644 index 00000000..59ef0a95 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/moon.scss @@ -0,0 +1,54 @@ +/** + * Solarized Dark theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(./fonts/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +/** + * Solarized colors by Ethan Schoonover + */ + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base1; +$headingColor: $base2; +$headingTextShadow: none; +$backgroundColor: $base03; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + +// Change text colors against light slide backgrounds +@include light-bg-text-color(#222); + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/night.scss b/presentation/deck/vendor/reveal/css/theme/source/night.scss new file mode 100644 index 00000000..98a20628 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/night.scss @@ -0,0 +1,37 @@ +/** + * Black theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #111; + +$mainFont: 'Open Sans', sans-serif; +$linkColor: #e7ad52; +$linkColorHover: lighten( $linkColor, 20% ); +$headingFont: 'Montserrat', Impact, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: -0.03em; +$headingTextTransform: none; +$selectionBackgroundColor: #e7ad52; + +// Change text colors against light slide backgrounds +@include light-bg-text-color(#222); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/css/theme/source/serif.scss b/presentation/deck/vendor/reveal/css/theme/source/serif.scss new file mode 100644 index 00000000..babec4d1 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/serif.scss @@ -0,0 +1,41 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is brown. + * + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$mainColor: #000; +$headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$headingColor: #383D3D; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #F0F1EB; +$linkColor: #51483D; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #26351C; + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +.reveal a { + line-height: 1.3em; +} + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/simple.scss b/presentation/deck/vendor/reveal/css/theme/source/simple.scss new file mode 100644 index 00000000..51a21af2 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/simple.scss @@ -0,0 +1,43 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is darkblue. + * + * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Lato', sans-serif; +$mainColor: #000; +$headingFont: 'News Cycle', Impact, sans-serif; +$headingColor: #000; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #fff; +$linkColor: #00008B; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(0, 0, 0, 0.99); + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/css/theme/source/sky.scss b/presentation/deck/vendor/reveal/css/theme/source/sky.scss new file mode 100644 index 00000000..457e9e50 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/sky.scss @@ -0,0 +1,52 @@ +/** + * Sky theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Open Sans', sans-serif; +$mainColor: #333; +$headingFont: 'Quicksand', sans-serif; +$headingColor: #333; +$headingLetterSpacing: -0.08em; +$headingTextShadow: none; +$backgroundColor: #f7fbfc; +$linkColor: #3b759e; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #134674; + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Fix links so they are not cut off +.reveal a { + line-height: 1.3em; +} + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( #add9e4, #f7fbfc ); +} + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/solarized.scss b/presentation/deck/vendor/reveal/css/theme/source/solarized.scss new file mode 100644 index 00000000..f3253458 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/solarized.scss @@ -0,0 +1,66 @@ +/** + * Solarized Light theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(./fonts/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base00; +$headingColor: $base01; +$headingTextShadow: none; +$backgroundColor: $base3; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Background generator +// @mixin bodyBackground() { +// @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) ); +// } + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/white-contrast.scss b/presentation/deck/vendor/reveal/css/theme/source/white-contrast.scss new file mode 100644 index 00000000..e22007e2 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/white-contrast.scss @@ -0,0 +1,52 @@ +/** + * White compact & high contrast reveal.js theme, with headers not in capitals. + * + * By Peter Kehl. Based on white.(s)css by Hakim El Hattab, http://hakim.se + * + * - Keep the source similar to black.css - for easy comparison. + * - $mainFontSize controls code blocks, too (although under some ratio). + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(./fonts/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #fff; + +$mainColor: #000; +$headingColor: #000; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #2a76dd; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/source/white.scss b/presentation/deck/vendor/reveal/css/theme/source/white.scss new file mode 100644 index 00000000..a2b1292f --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/source/white.scss @@ -0,0 +1,49 @@ +/** + * White theme for reveal.js. This is the opposite of the 'black' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(./fonts/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #fff; + +$mainColor: #222; +$headingColor: #222; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #2a76dd; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +$overlayElementBgColor: 0, 0, 0; +$overlayElementFgColor: 240, 240, 240; + +// Change text colors against dark slide backgrounds +@include dark-bg-text-color(#fff); + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/presentation/deck/vendor/reveal/css/theme/template/exposer.scss b/presentation/deck/vendor/reveal/css/theme/template/exposer.scss new file mode 100644 index 00000000..2e9288d3 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/template/exposer.scss @@ -0,0 +1,30 @@ +// Exposes theme's variables for easy re-use in CSS for plugin authors + +:root { + --r-background-color: #{$backgroundColor}; + --r-main-font: #{$mainFont}; + --r-main-font-size: #{$mainFontSize}; + --r-main-color: #{$mainColor}; + --r-block-margin: #{$blockMargin}; + --r-heading-margin: #{$headingMargin}; + --r-heading-font: #{$headingFont}; + --r-heading-color: #{$headingColor}; + --r-heading-line-height: #{$headingLineHeight}; + --r-heading-letter-spacing: #{$headingLetterSpacing}; + --r-heading-text-transform: #{$headingTextTransform}; + --r-heading-text-shadow: #{$headingTextShadow}; + --r-heading-font-weight: #{$headingFontWeight}; + --r-heading1-text-shadow: #{$heading1TextShadow}; + --r-heading1-size: #{$heading1Size}; + --r-heading2-size: #{$heading2Size}; + --r-heading3-size: #{$heading3Size}; + --r-heading4-size: #{$heading4Size}; + --r-code-font: #{$codeFont}; + --r-link-color: #{$linkColor}; + --r-link-color-dark: #{darken($linkColor , 15% )}; + --r-link-color-hover: #{$linkColorHover}; + --r-selection-background-color: #{$selectionBackgroundColor}; + --r-selection-color: #{$selectionColor}; + --r-overlay-element-bg-color: #{$overlayElementBgColor}; + --r-overlay-element-fg-color: #{$overlayElementFgColor}; +} diff --git a/presentation/deck/vendor/reveal/css/theme/template/mixins.scss b/presentation/deck/vendor/reveal/css/theme/template/mixins.scss new file mode 100644 index 00000000..17a3db5e --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/template/mixins.scss @@ -0,0 +1,45 @@ +@mixin vertical-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); + background: -o-linear-gradient( top, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); + background: linear-gradient( top, $top 0%, $bottom 100% ); +} + +@mixin horizontal-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); + background: -o-linear-gradient( left, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); + background: linear-gradient( left, $top 0%, $bottom 100% ); +} + +@mixin radial-gradient( $outer, $inner, $type: circle ) { + background: $outer; + background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); + background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); +} + +@mixin light-bg-text-color( $color ) { + section.has-light-background { + &, h1, h2, h3, h4, h5, h6 { + color: $color; + } + } +} + +@mixin dark-bg-text-color( $color ) { + section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: $color; + } + } +} \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/css/theme/template/settings.scss b/presentation/deck/vendor/reveal/css/theme/template/settings.scss new file mode 100644 index 00000000..3d54ac82 --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/template/settings.scss @@ -0,0 +1,50 @@ +// Base settings for all themes that can optionally be +// overridden by the super-theme + +// Background of the presentation +$backgroundColor: #2b2b2b; + +// Primary/body text +$mainFont: 'Lato', sans-serif; +$mainFontSize: 40px; +$mainColor: #eee; + +// Vertical spacing between blocks of text +$blockMargin: 20px; + +// Headings +$headingMargin: 0 0 $blockMargin 0; +$headingFont: 'League Gothic', Impact, sans-serif; +$headingColor: #eee; +$headingLineHeight: 1.2; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingTextShadow: none; +$headingFontWeight: normal; +$heading1TextShadow: $headingTextShadow; + +$heading1Size: 3.77em; +$heading2Size: 2.11em; +$heading3Size: 1.55em; +$heading4Size: 1.00em; + +$codeFont: monospace; + +// Links and actions +$linkColor: #13DAEC; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: #FF5E99; +$selectionColor: #fff; + +// Colors used for UI elements that are overlaid on top of +// the presentation +$overlayElementBgColor: 240, 240, 240; +$overlayElementFgColor: 0, 0, 0; + +// Generates the presentation background, can be overridden +// to return a background image or gradient +@mixin bodyBackground() { + background: $backgroundColor; +} diff --git a/presentation/deck/vendor/reveal/css/theme/template/theme.scss b/presentation/deck/vendor/reveal/css/theme/template/theme.scss new file mode 100644 index 00000000..bc377d3c --- /dev/null +++ b/presentation/deck/vendor/reveal/css/theme/template/theme.scss @@ -0,0 +1,331 @@ +// Base theme template for reveal.js + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +@import "./exposer"; + +.reveal-viewport { + @include bodyBackground(); + background-color: var(--r-background-color); +} + +.reveal { + font-family: var(--r-main-font); + font-size: var(--r-main-font-size); + font-weight: normal; + color: var(--r-main-color); +} + +.reveal ::selection { + color: var(--r-selection-color); + background: var(--r-selection-background-color); + text-shadow: none; +} + +.reveal ::-moz-selection { + color: var(--r-selection-color); + background: var(--r-selection-background-color); + text-shadow: none; +} + +.reveal .slides section, +.reveal .slides section>section { + line-height: 1.3; + font-weight: inherit; +} + +/********************************************* + * HEADERS + *********************************************/ + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: var(--r-heading-margin); + color: var(--r-heading-color); + + font-family: var(--r-heading-font); + font-weight: var(--r-heading-font-weight); + line-height: var(--r-heading-line-height); + letter-spacing: var(--r-heading-letter-spacing); + + text-transform: var(--r-heading-text-transform); + text-shadow: var(--r-heading-text-shadow); + + word-wrap: break-word; +} + +.reveal h1 {font-size: var(--r-heading1-size); } +.reveal h2 {font-size: var(--r-heading2-size); } +.reveal h3 {font-size: var(--r-heading3-size); } +.reveal h4 {font-size: var(--r-heading4-size); } + +.reveal h1 { + text-shadow: var(--r-heading1-text-shadow); +} + + +/********************************************* + * OTHER + *********************************************/ + +.reveal p { + margin: var(--r-block-margin) 0; + line-height: 1.3; +} + +/* Remove trailing margins after titles */ +.reveal h1:last-child, +.reveal h2:last-child, +.reveal h3:last-child, +.reveal h4:last-child, +.reveal h5:last-child, +.reveal h6:last-child { + margin-bottom: 0; +} + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; +} +.reveal strong, +.reveal b { + font-weight: bold; +} + +.reveal em { + font-style: italic; +} + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + + text-align: left; + margin: 0 0 0 1em; +} + +.reveal ol { + list-style-type: decimal; +} + +.reveal ul { + list-style-type: disc; +} + +.reveal ul ul { + list-style-type: square; +} + +.reveal ul ul ul { + list-style-type: circle; +} + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; +} + +.reveal dt { + font-weight: bold; +} + +.reveal dd { + margin-left: 40px; +} + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: var(--r-block-margin) auto; + padding: 5px; + + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0,0,0,0.2); +} + .reveal blockquote p:first-child, + .reveal blockquote p:last-child { + display: inline-block; + } + +.reveal q { + font-style: italic; +} + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: var(--r-block-margin) auto; + + text-align: left; + font-size: 0.55em; + font-family: var(--r-code-font); + line-height: 1.2em; + + word-wrap: break-word; + + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); +} + +.reveal code { + font-family: var(--r-code-font); + text-transform: none; + tab-size: 2; +} + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; +} + +.reveal .code-wrapper { + white-space: normal; +} + +.reveal .code-wrapper code { + white-space: pre; +} + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; +} + +.reveal table th { + font-weight: bold; +} + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; +} + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; +} + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; +} + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; +} + +.reveal sup { + vertical-align: super; + font-size: smaller; +} +.reveal sub { + vertical-align: sub; + font-size: smaller; +} + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; +} + +.reveal small * { + vertical-align: top; +} + +.reveal img { + margin: var(--r-block-margin) 0; +} + + +/********************************************* + * LINKS + *********************************************/ + +.reveal a { + color: var(--r-link-color); + text-decoration: none; + transition: color .15s ease; +} + .reveal a:hover { + color: var(--r-link-color-hover); + text-shadow: none; + border: none; + } + +.reveal .roll span:after { + color: #fff; + // background: darken( var(--r-link-color), 15% ); + background: var(--r-link-color-dark); + +} + + +/********************************************* + * Frame helper + *********************************************/ + +.reveal .r-frame { + border: 4px solid var(--r-main-color); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); +} + +.reveal a .r-frame { + transition: all .15s linear; +} + +.reveal a:hover .r-frame { + border-color: var(--r-link-color); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); +} + + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ + +.reveal .controls { + color: var(--r-link-color); +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + background: rgba(0,0,0,0.2); + color: var(--r-link-color); +} + +/********************************************* + * PRINT BACKGROUND + *********************************************/ + @media print { + .backgrounds { + background-color: var(--r-background-color); + } +} diff --git a/presentation/deck/vendor/reveal/demo.html b/presentation/deck/vendor/reveal/demo.html new file mode 100644 index 00000000..36d83028 --- /dev/null +++ b/presentation/deck/vendor/reveal/demo.html @@ -0,0 +1,481 @@ + + + + + + + reveal.js – The HTML Presentation Framework + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + +

The HTML Presentation Framework

+

+ Created by Hakim El Hattab and contributors +

+
+ +
+

Hello There

+

+ reveal.js enables you to create beautiful interactive slide decks using HTML. This presentation will show you examples of what it can do. +

+
+ + +
+
+

Vertical Slides

+

Slides can be nested inside of each other.

+

Use the Space key to navigate through all slides.

+
+ + Down arrow + +
+
+

Basement Level 1

+

Nested slides are useful for adding additional detail underneath a high level horizontal slide.

+
+
+

Basement Level 2

+

That's it, time to go back up.

+
+ + Up arrow + +
+
+ +
+

Slides

+

+ Not a coder? Not a problem. There's a fully-featured visual editor for authoring these, try it out at https://slides.com. +

+
+ +
+

Hidden Slides

+

+ This slide is visible in the source, but hidden when the presentation is viewed. You can show all hidden slides by setting the `showHiddenSlides` config option to `true`. +

+
+ +
+

Pretty Code

+

+						import React, { useState } from 'react';
+
+						function Example() {
+						  const [count, setCount] = useState(0);
+
+						  return (
+						    ...
+						  );
+						}
+					
+

Code syntax highlighting courtesy of highlight.js.

+
+ +
+

With Animations

+
+
+ +
+

Point of View

+

+ Press ESC to enter the slide overview. +

+

+ Hold down the alt key (ctrl in Linux) and click on any element to zoom towards it using zoom.js. Click again to zoom back out. +

+

+ (NOTE: Use ctrl + click in Linux.) +

+
+ +
+

Auto-Animate

+

Automatically animate matching elements across slides with Auto-Animate.

+
+
+
+
+
+
+
+
+
+
+
+
+

Auto-Animate

+
+
+
+
+
+
+
+

Auto-Animate

+
+ +
+

Touch Optimized

+

+ Presentations look great on touch devices, like mobile phones and tablets. Simply swipe through your slides. +

+
+ +
+ +
+ +
+

Add the r-fit-text class to auto-size text

+

FIT TEXT

+
+ +
+
+

Fragments

+

Hit the next arrow...

+

... to step through ...

+

... a fragmented slide.

+ + +
+
+

Fragment Styles

+

There's different types of fragments, like:

+

grow

+

shrink

+

fade-out

+

+ fade-right, + up, + down, + left +

+

fade-in-then-out

+

fade-in-then-semi-out

+

Highlight red blue green

+
+
+ +
+

Transition Styles

+

+ You can select from different transitions, like:
+ None - + Fade - + Slide - + Convex - + Concave - + Zoom +

+
+ +
+

Themes

+

+ reveal.js comes with a few themes built in:
+ + Black (default) - + White - + League - + Sky - + Beige - + Simple
+ Serif - + Blood - + Night - + Moon - + Solarized +

+
+ +
+
+

Slide Backgrounds

+

+ Set data-background="#dddddd" on a slide to change the background color. All CSS color formats are supported. +

+ + Down arrow + +
+
+

Gradient Backgrounds

+
<section data-background-gradient=
+							"linear-gradient(to bottom, #ddd, #191919)">
+
+
+

Image Backgrounds

+
<section data-background="image.png">
+
+
+

Tiled Backgrounds

+
<section data-background="image.png" data-background-repeat="repeat" data-background-size="100px">
+
+
+
+

Video Backgrounds

+
<section data-background-video="video.mp4,video.webm">
+
+
+
+

... and GIFs!

+
+
+ +
+

Background Transitions

+

+ Different background transitions are available via the backgroundTransition option. This one's called "zoom". +

+
Reveal.configure({ backgroundTransition: 'zoom' })
+
+ +
+

Background Transitions

+

+ You can override background transitions per-slide. +

+
<section data-background-transition="zoom">
+
+ +
+
+

Iframe Backgrounds

+

Since reveal.js runs on the web, you can easily embed other web content. Try interacting with the page in the background.

+
+
+ +
+

Marvelous List

+
    +
  • No order here
  • +
  • Or here
  • +
  • Or here
  • +
  • Or here
  • +
+
+ +
+

Fantastic Ordered List

+
    +
  1. One is smaller than...
  2. +
  3. Two is smaller than...
  4. +
  5. Three!
  6. +
+
+ +
+

Tabular Tables

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ItemValueQuantity
Apples$17
Lemonade$218
Bread$32
+
+ +
+

Clever Quotes

+

+ These guys come in two forms, inline: The nice thing about standards is that there are so many to choose from and block: +

+
+ “For years there has been a theory that millions of monkeys typing at random on millions of typewriters would + reproduce the entire works of Shakespeare. The Internet has proven this theory to be untrue.” +
+
+ +
+

Intergalactic Interconnections

+

+ You can link between slides internally, + like this. +

+
+ +
+

Speaker View

+

There's a speaker view. It includes a timer, preview of the upcoming slide as well as your speaker notes.

+

Press the S key to try it out.

+ + +
+ +
+

Export to PDF

+

Presentations can be exported to PDF, here's an example:

+ +
+ +
+

Global State

+

+ Set data-state="something" on a slide and "something" + will be added as a class to the document element when the slide is open. This lets you + apply broader style changes, like switching the page background. +

+
+ +
+

State Events

+

+ Additionally custom events can be triggered on a per slide basis by binding to the data-state name. +

+

+Reveal.on( 'customevent', function() {
+	console.log( '"customevent" has fired' );
+} );
+					
+
+ +
+

Take a Moment

+

+ Press B or . on your keyboard to pause the presentation. This is helpful when you're on stage and want to take distracting slides off the screen. +

+
+ +
+

Much more

+ +
+ +
+

THE END

+

+ - Try the online editor
+ - Source code & documentation +

+
+ +
+ +
+ + + + + + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/500-slides.html b/presentation/deck/vendor/reveal/examples/500-slides.html new file mode 100644 index 00000000..2270c747 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/500-slides.html @@ -0,0 +1,526 @@ + + + + + + reveal.js - 500 slides + + + + + + + + +
+
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+

8

+

9

+

10

+

11

+

12

+

13

+

14

+

15

+

16

+

17

+

18

+

19

+

20

+

21

+

22

+

23

+

24

+

25

+

26

+

27

+

28

+

29

+

30

+

31

+

32

+

33

+

34

+

35

+

36

+

37

+

38

+

39

+

40

+

41

+

42

+

43

+

44

+

45

+

46

+

47

+

48

+

49

+

50

+

51

+

52

+

53

+

54

+

55

+

56

+

57

+

58

+

59

+

60

+

61

+

62

+

63

+

64

+

65

+

66

+

67

+

68

+

69

+

70

+

71

+

72

+

73

+

74

+

75

+

76

+

77

+

78

+

79

+

80

+

81

+

82

+

83

+

84

+

85

+

86

+

87

+

88

+

89

+

90

+

91

+

92

+

93

+

94

+

95

+

96

+

97

+

98

+

99

+

100

+

101

+

102

+

103

+

104

+

105

+

106

+

107

+

108

+

109

+

110

+

111

+

112

+

113

+

114

+

115

+

116

+

117

+

118

+

119

+

120

+

121

+

122

+

123

+

124

+

125

+

126

+

127

+

128

+

129

+

130

+

131

+

132

+

133

+

134

+

135

+

136

+

137

+

138

+

139

+

140

+

141

+

142

+

143

+

144

+

145

+

146

+

147

+

148

+

149

+

150

+

151

+

152

+

153

+

154

+

155

+

156

+

157

+

158

+

159

+

160

+

161

+

162

+

163

+

164

+

165

+

166

+

167

+

168

+

169

+

170

+

171

+

172

+

173

+

174

+

175

+

176

+

177

+

178

+

179

+

180

+

181

+

182

+

183

+

184

+

185

+

186

+

187

+

188

+

189

+

190

+

191

+

192

+

193

+

194

+

195

+

196

+

197

+

198

+

199

+

200

+

201

+

202

+

203

+

204

+

205

+

206

+

207

+

208

+

209

+

210

+

211

+

212

+

213

+

214

+

215

+

216

+

217

+

218

+

219

+

220

+

221

+

222

+

223

+

224

+

225

+

226

+

227

+

228

+

229

+

230

+

231

+

232

+

233

+

234

+

235

+

236

+

237

+

238

+

239

+

240

+

241

+

242

+

243

+

244

+

245

+

246

+

247

+

248

+

249

+

250

+

251

+

252

+

253

+

254

+

255

+

256

+

257

+

258

+

259

+

260

+

261

+

262

+

263

+

264

+

265

+

266

+

267

+

268

+

269

+

270

+

271

+

272

+

273

+

274

+

275

+

276

+

277

+

278

+

279

+

280

+

281

+

282

+

283

+

284

+

285

+

286

+

287

+

288

+

289

+

290

+

291

+

292

+

293

+

294

+

295

+

296

+

297

+

298

+

299

+

300

+

301

+

302

+

303

+

304

+

305

+

306

+

307

+

308

+

309

+

310

+

311

+

312

+

313

+

314

+

315

+

316

+

317

+

318

+

319

+

320

+

321

+

322

+

323

+

324

+

325

+

326

+

327

+

328

+

329

+

330

+

331

+

332

+

333

+

334

+

335

+

336

+

337

+

338

+

339

+

340

+

341

+

342

+

343

+

344

+

345

+

346

+

347

+

348

+

349

+

350

+

351

+

352

+

353

+

354

+

355

+

356

+

357

+

358

+

359

+

360

+

361

+

362

+

363

+

364

+

365

+

366

+

367

+

368

+

369

+

370

+

371

+

372

+

373

+

374

+

375

+

376

+

377

+

378

+

379

+

380

+

381

+

382

+

383

+

384

+

385

+

386

+

387

+

388

+

389

+

390

+

391

+

392

+

393

+

394

+

395

+

396

+

397

+

398

+

399

+

400

+

401

+

402

+

403

+

404

+

405

+

406

+

407

+

408

+

409

+

410

+

411

+

412

+

413

+

414

+

415

+

416

+

417

+

418

+

419

+

420

+

421

+

422

+

423

+

424

+

425

+

426

+

427

+

428

+

429

+

430

+

431

+

432

+

433

+

434

+

435

+

436

+

437

+

438

+

439

+

440

+

441

+

442

+

443

+

444

+

445

+

446

+

447

+

448

+

449

+

450

+

451

+

452

+

453

+

454

+

455

+

456

+

457

+

458

+

459

+

460

+

461

+

462

+

463

+

464

+

465

+

466

+

467

+

468

+

469

+

470

+

471

+

472

+

473

+

474

+

475

+

476

+

477

+

478

+

479

+

480

+

481

+

482

+

483

+

484

+

485

+

486

+

487

+

488

+

489

+

490

+

491

+

492

+

493

+

494

+

495

+

496

+

497

+

498

+

499

+
+
+ + + + + diff --git a/presentation/deck/vendor/reveal/examples/assets/beeping.txt b/presentation/deck/vendor/reveal/examples/assets/beeping.txt new file mode 100644 index 00000000..bf419974 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/assets/beeping.txt @@ -0,0 +1,2 @@ +Source: https://freesound.org/people/fennelliott/sounds/379419/ +License: CC0 (public domain) \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/examples/assets/beeping.wav b/presentation/deck/vendor/reveal/examples/assets/beeping.wav new file mode 100644 index 00000000..38747a53 Binary files /dev/null and b/presentation/deck/vendor/reveal/examples/assets/beeping.wav differ diff --git a/presentation/deck/vendor/reveal/examples/assets/image1.png b/presentation/deck/vendor/reveal/examples/assets/image1.png new file mode 100644 index 00000000..87475945 Binary files /dev/null and b/presentation/deck/vendor/reveal/examples/assets/image1.png differ diff --git a/presentation/deck/vendor/reveal/examples/assets/image2.png b/presentation/deck/vendor/reveal/examples/assets/image2.png new file mode 100644 index 00000000..6c403a0d Binary files /dev/null and b/presentation/deck/vendor/reveal/examples/assets/image2.png differ diff --git a/presentation/deck/vendor/reveal/examples/auto-animate.html b/presentation/deck/vendor/reveal/examples/auto-animate.html new file mode 100644 index 00000000..199810e4 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/auto-animate.html @@ -0,0 +1,225 @@ + + + + + + + reveal.js - Auto Animate + + + + + + + + + + +
+ +
+
+

Auto-Animate Example

+

This will fade out

+ +

+						function Example() {
+						  const [count, setCount] = useState(0);
+						}
+					
+
+
+

Auto-Animate Example

+

This will fade out

+

This element is unmatched

+ +

+						function Example() {
+						  New line!
+						  const [count, setCount] = useState(0);
+						}
+					
+
+ +
+

Line Height & Letter Spacing

+
+
+

Line Height & Letter Spacing

+
+ +
+
+

+							import React, { useState } from 'react';
+
+							function Example() {
+							  const [count, setCount] = useState(0);
+
+							  return (
+							    ...
+							  );
+							}
+						
+
+
+

+							function Example() {
+							  const [count, setCount] = useState(0);
+
+							  return (
+							    <div>
+							      <p>You clicked {count} times</p>
+							      <button onClick={() => setCount(count + 1)}>
+							        Click me
+							      </button>
+							    </div>
+							  );
+							}
+						
+
+
+

+							function Example() {
+							  // A comment!
+							  const [count, setCount] = useState(0);
+
+							  return (
+							    <div>
+							      <p>You clicked {count} times</p>
+							      <button onClick={() => setCount(count + 1)}>
+							        Click me
+							      </button>
+							    </div>
+							  );
+							}
+						
+
+
+ +
+
+

Swapping list items

+
    +
  • One
  • +
  • Two
  • +
  • Three
  • +
+
+
+

Swapping list items

+
    +
  • Two
  • +
  • One
  • +
  • Three
  • +
+
+
+

Swapping list items

+
    +
  • Two
  • +
  • Three
  • +
  • One
  • +
+
+
+ +
+

SLIDE 1

+

Animate Anything

+
+
+
+
+
+
+

SLIDE 2

+

With Auto Animate

+
+
+
+
+
+
+

SLIDE 3

+

With Auto Animate

+
+
+
+
+
+
+

SLIDE 3

+

With Auto Animate

+
+
+
+
+
+ +
+

data-auto-animate-id="a"

+

A1

+
+
+

data-auto-animate-id="a"

+

A1

+

A2

+
+
+

data-auto-animate-id="b"

+

B1

+
+
+

data-auto-animate-id="b"

+

B1

+

B2

+
+ +
+
+ Slide 1
+ Slide 2
+ Slide 3
+ Slide 4
+
A
+
+
+ Slide 1
+ Slide 2
+ Slide 3
+ Slide 4
+
A
+
+
+ Slide 1
+ Slide 2
+ Slide 3
+ Slide 4
+
A
+
+
+ Slide 1
+ Slide 2
+ Slide 3
+ Slide 4
+
A
+
+
+ +
+ +
+ + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/backgrounds.html b/presentation/deck/vendor/reveal/examples/backgrounds.html new file mode 100644 index 00000000..19d40c34 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/backgrounds.html @@ -0,0 +1,141 @@ + + + + + + + reveal.js - Slide Backgrounds + + + + + + + + + + +
+ +
+ +
+

data-background: #00ffff

+
+ +
+

data-background: #bb00bb

+
+ +
+

data-background: lightblue

+
+ +
+
+

data-background: #ff0000

+
+
+

data-background: rgba(0, 0, 0, 0.2)

+
+
+

data-background: salmon

+
+
+ +
+
+

Background applied to stack

+
+
+

Background applied to stack

+
+
+

Background applied to slide inside of stack

+
+
+ +
+

Background image

+
+ +
+
+

Background image

+
+
+

Background image

+
+
+ +
+

Background image

+
data-background-size="100px" data-background-repeat="repeat" data-background-color="#111"
+
+ +
+

Same background twice (1/2)

+
+
+

Same background twice (2/2)

+
+ +
+

Video background

+
+ +
+

Iframe background

+
+ +
+
+

Same background twice vertical (1/2)

+
+
+

Same background twice vertical (2/2)

+
+
+ +
+

Same background from horizontal to vertical (1/3)

+
+
+
+

Same background from horizontal to vertical (2/3)

+
+
+

Same background from horizontal to vertical (3/3)

+
+
+ +
+ +
+ + + + + + diff --git a/presentation/deck/vendor/reveal/examples/barebones.html b/presentation/deck/vendor/reveal/examples/barebones.html new file mode 100644 index 00000000..50adcb86 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/barebones.html @@ -0,0 +1,32 @@ + + + + + reveal.js - Barebones + + + + +
+
+ +
+

Barebones Presentation

+

This example contains the bare minimum includes and markup required to run a reveal.js presentation.

+
+ +
+

No Theme

+

There's no theme included, so it will fall back on browser defaults.

+
+ +
+
+ + + + + + diff --git a/presentation/deck/vendor/reveal/examples/layout-helpers.html b/presentation/deck/vendor/reveal/examples/layout-helpers.html new file mode 100644 index 00000000..a129811f --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/layout-helpers.html @@ -0,0 +1,160 @@ + + + + + + + reveal.js - Layout Helpers + + + + + + + + + + +
+ +
+ +
+

Layout Helper Examples

+ +
+ +
+

Fit Text

+

Resizes text to be as large as possible within its container.

+

+					  

FIT

+
+
+ +
+

FIT

+
+ +
+

HELLO WORLD

+

BOTH THESE TITLES USE FIT-TEXT

+
+ +
+

Stretch

+

Makes an element as tall as possible while remaining within the slide bounds.

+

+					  

Stretch Example

+ +

Image byline

+
+
+ +
+

Stretch Example

+ +

Image byline

+
+ +
+

Stack

+

Stacks multiple elements on top of each other, for use with fragments.

+

+					  
+ <img class="fragment" width="450" height="300" src="..."> + <img class="fragment" width="300" height="450" src="..."> + <img class="fragment" width="400" height="400" src="..."> +
+
+
+ +
+

Stack Example

+
+

One

+

Two

+

Three

+

Four

+
+
+ + + +
+
+ +
+

Stack Example

+

fade-in-then-out fragments

+
+ + + +
+
+ +
+

HStack

+

Stacks multiple elements horizontally.

+

+					  
+ <img width="450" height="300" src="..."> + <img width="300" height="450" src="..."> + <img width="400" height="400" src="..."> +
+
+
+ +
+

HStack Example

+
+

One

+

Two

+

Three

+
+
+ +
+

VStack

+

Stacks multiple elements vertically.

+

+					  
+ <img width="450" height="300" src="..."> + <img width="300" height="450" src="..."> + <img width="400" height="400" src="..."> +
+
+
+ +
+

VStack Example

+
+

One

+

Two

+

Three

+
+
+ +
+ +
+ + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/markdown.html b/presentation/deck/vendor/reveal/examples/markdown.html new file mode 100644 index 00000000..4d69f052 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/markdown.html @@ -0,0 +1,161 @@ + + + + + + + reveal.js - Markdown Example + + + + + + + + + +
+ +
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ ## The Lorenz Equations + `\[\begin{aligned} + \dot{x} & = \sigma(y-x) \\ + \dot{y} & = \rho x - y - xz \\ + \dot{z} & = -\beta z + xy + \end{aligned} \]` +
+ +
+
+ + + + + + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/markdown.md b/presentation/deck/vendor/reveal/examples/markdown.md new file mode 100644 index 00000000..1315172c --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/markdown.md @@ -0,0 +1,41 @@ +# Markdown Demo + + + +## External 1.1 + +Content 1.1 + +Note: This will only appear in the speaker notes window. + + +## External 1.2 + +Content 1.2 + + + +## External 2 + +Content 2.1 + + + +## External 3.1 + +Content 3.1 + + +## External 3.2 + +Content 3.2 + + +## External 3.3 (Image) + +![External Image](https://s3.amazonaws.com/static.slid.es/logo/v2/slides-symbol-512x512.png) + + +## External 3.4 (Math) + +`\[ J(\theta_0,\theta_1) = \sum_{i=0} \]` diff --git a/presentation/deck/vendor/reveal/examples/math.html b/presentation/deck/vendor/reveal/examples/math.html new file mode 100644 index 00000000..bd2e75a6 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/math.html @@ -0,0 +1,206 @@ + + + + + + + reveal.js - Math Plugin + + + + + + + + + +
+ +
+ +
+

reveal.js Math Plugin

+

Render math with KaTeX, MathJax 2 or MathJax 3

+
+ +
+

The Lorenz Equations

+ + \[\begin{aligned} + \dot{x} & = \sigma(y-x) \\ + \dot{y} & = \rho x - y - xz \\ + \dot{z} & = -\beta z + xy + \end{aligned} \] +
+ +
+

The Cauchy-Schwarz Inequality

+ + +
+ +
+

A Cross Product Formula

+ + \[\mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} + \mathbf{i} & \mathbf{j} & \mathbf{k} \\ + \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ + \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 + \end{vmatrix} \] +
+ +
+

The probability of getting \(k\) heads when flipping \(n\) coins is

+ + \[P(E) = {n \choose k} p^k (1-p)^{ n-k} \] +
+ +
+

An Identity of Ramanujan

+ + \[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = + 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} + {1+\frac{e^{-8\pi}} {1+\ldots} } } } \] +
+ +
+

A Rogers-Ramanujan Identity

+ + \[ 1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = + \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}\] +
+ +
+

Maxwell’s Equations

+ + \[ \begin{aligned} + \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ + \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ + \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} + \] +
+ +
+

TeX Macros

+ + Here is a common vector space: + \[L^2(\R) = \set{u : \R \to \R}{\int_\R |u|^2 < +\infty}\] + used in functional analysis. +
+ +
+
+

The Lorenz Equations

+ +
+ \[\begin{aligned} + \dot{x} & = \sigma(y-x) \\ + \dot{y} & = \rho x - y - xz \\ + \dot{z} & = -\beta z + xy + \end{aligned} \] +
+
+ +
+

The Cauchy-Schwarz Inequality

+ +
+ \[ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \] +
+
+ +
+

A Cross Product Formula

+ +
+ \[\mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} + \mathbf{i} & \mathbf{j} & \mathbf{k} \\ + \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ + \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 + \end{vmatrix} \] +
+
+ +
+

The probability of getting \(k\) heads when flipping \(n\) coins is

+ +
+ \[P(E) = {n \choose k} p^k (1-p)^{ n-k} \] +
+
+ +
+

An Identity of Ramanujan

+ +
+ \[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = + 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} + {1+\frac{e^{-8\pi}} {1+\ldots} } } } \] +
+
+ +
+

A Rogers-Ramanujan Identity

+ +
+ \[ 1 + \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots = + \prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})}\] +
+
+ +
+

Maxwell’s Equations

+ +
+ \[ \begin{aligned} + \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ + \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ + \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} + \] +
+
+ +
+

TeX Macros

+ + Here is a common vector space: + \[L^2(\R) = \set{u : \R \to \R}{\int_\R |u|^2 < +\infty}\] + used in functional analysis. +
+
+ +
+ +
+ + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/media.html b/presentation/deck/vendor/reveal/examples/media.html new file mode 100644 index 00000000..388208f8 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/media.html @@ -0,0 +1,75 @@ + + + + + + + reveal.js - Video, Audio and Iframes + + + + + + + + + +
+ +
+ +
+

Examples of embedded Video, Audio and Iframes

+
+ +
+

Iframe

+ +
+ +
+

Iframe Background

+
+ +
+

Video

+ +
+ +
+

Background Video

+
+ +
+

Auto-playing audio

+ +
+ +
+

Audio inside slide fragments

+
+ Beep 1 + +
+
+ Beep 2 + +
+
+ +
+

Audio with controls

+ +
+ +
+ +
+ + + + + + diff --git a/presentation/deck/vendor/reveal/examples/multiple-presentations.html b/presentation/deck/vendor/reveal/examples/multiple-presentations.html new file mode 100644 index 00000000..e5347d4a --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/multiple-presentations.html @@ -0,0 +1,102 @@ + + + + + + + reveal.js - Multiple Presentations + + + + + + + + + + +
+
+
+
Deck 1, Slide 1
+
Deck 1, Slide 2
+
+

+							import React, { useState } from 'react';
+							function Example() {
+							  const [count, setCount] = useState(0);
+							}
+						
+
+
+
+ +
+
+
Deck 2, Slide 1
+
Deck 2, Slide 2
+
+ +
+
+

The Lorenz Equations

+ + \[\begin{aligned} + \dot{x} & = \sigma(y-x) \\ + \dot{y} & = \rho x - y - xz \\ + \dot{z} & = -\beta z + xy + \end{aligned} \] +
+
+
+
+ + + + + + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/scroll.html b/presentation/deck/vendor/reveal/examples/scroll.html new file mode 100644 index 00000000..c87903cc --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/scroll.html @@ -0,0 +1,118 @@ + + + + + + + reveal.js - Scroll View + + + + + + + + + + + +
+ +
+ +

Scroll View

+
+

Scroll triggered fragments

+
    +
  • Step one
  • +
  • Step two
  • +
  • Step three
  • +
+
+

Scrollbar inverts
based on slide bg

+
+

Auto-Animate

+

Scroll triggered auto-animations 😍

+
+
+
+
+
+
+
+
+
+
+
+
+

Auto-Animate

+
+
+
+
+
+
+
+

Auto-Animate

+
+
+

Code highlights,
meet scroll triggers

+
+
+
+
+

Image Backgrounds

+
+
+

Video background

+
+
+

The end

+ +
+ +
+ + + + + + + + + diff --git a/presentation/deck/vendor/reveal/examples/transitions.html b/presentation/deck/vendor/reveal/examples/transitions.html new file mode 100644 index 00000000..adbfd150 --- /dev/null +++ b/presentation/deck/vendor/reveal/examples/transitions.html @@ -0,0 +1,97 @@ + + + + + + + reveal.js - Slide Transitions + + + + + + + + +
+ +
+ +
+

Default

+
+ +
+

Default

+
+ +
+

data-transition: zoom

+
+ +
+

data-transition: zoom-in fade-out

+
+ +
+

Default

+
+ +
+

data-transition: convex

+
+ +
+

data-transition: convex-in concave-out

+
+ +
+
+

Default

+
+
+

data-transition: concave

+
+
+

data-transition: convex-in fade-out

+
+
+

Default

+
+
+ +
+

data-transition: none

+
+ +
+

Default

+
+ +
+ +
+ + + + + + diff --git a/presentation/deck/vendor/reveal/gulpfile.js b/presentation/deck/vendor/reveal/gulpfile.js new file mode 100644 index 00000000..003616e2 --- /dev/null +++ b/presentation/deck/vendor/reveal/gulpfile.js @@ -0,0 +1,321 @@ +const pkg = require('./package.json') +const glob = require('glob') +const yargs = require('yargs') +const through = require('through2'); +const qunit = require('node-qunit-puppeteer') + +const {rollup} = require('rollup') +const terser = require('@rollup/plugin-terser') +const babel = require('@rollup/plugin-babel').default +const commonjs = require('@rollup/plugin-commonjs') +const resolve = require('@rollup/plugin-node-resolve').default +const sass = require('sass') + +const gulp = require('gulp') +const tap = require('gulp-tap') +const zip = require('gulp-zip') +const header = require('gulp-header') +const eslint = require('gulp-eslint') +const minify = require('gulp-clean-css') +const connect = require('gulp-connect') +const autoprefixer = require('gulp-autoprefixer') + +const root = yargs.argv.root || '.' +const port = yargs.argv.port || 8000 +const host = yargs.argv.host || 'localhost' + +const banner = `/*! +* reveal.js ${pkg.version} +* ${pkg.homepage} +* MIT licensed +* +* Copyright (C) 2011-2024 Hakim El Hattab, https://hakim.se +*/\n` + +// Prevents warnings from opening too many test pages +process.setMaxListeners(20); + +const babelConfig = { + babelHelpers: 'bundled', + ignore: ['node_modules'], + compact: false, + extensions: ['.js', '.html'], + plugins: [ + 'transform-html-import-to-string' + ], + presets: [[ + '@babel/preset-env', + { + corejs: 3, + useBuiltIns: 'usage', + modules: false + } + ]] +}; + +// Our ES module bundle only targets newer browsers with +// module support. Browsers are targeted explicitly instead +// of using the "esmodule: true" target since that leads to +// polyfilling older browsers and a larger bundle. +const babelConfigESM = JSON.parse( JSON.stringify( babelConfig ) ); +babelConfigESM.presets[0][1].targets = { browsers: [ + 'last 2 Chrome versions', + 'last 2 Safari versions', + 'last 2 iOS versions', + 'last 2 Firefox versions', + 'last 2 Edge versions', +] }; + +let cache = {}; + +// Creates a bundle with broad browser support, exposed +// as UMD +gulp.task('js-es5', () => { + return rollup({ + cache: cache.umd, + input: 'js/index.js', + plugins: [ + resolve(), + commonjs(), + babel( babelConfig ), + terser() + ] + }).then( bundle => { + cache.umd = bundle.cache; + return bundle.write({ + name: 'Reveal', + file: './dist/reveal.js', + format: 'umd', + banner: banner, + sourcemap: true + }); + }); +}) + +// Creates an ES module bundle +gulp.task('js-es6', () => { + return rollup({ + cache: cache.esm, + input: 'js/index.js', + plugins: [ + resolve(), + commonjs(), + babel( babelConfigESM ), + terser() + ] + }).then( bundle => { + cache.esm = bundle.cache; + return bundle.write({ + file: './dist/reveal.esm.js', + format: 'es', + banner: banner, + sourcemap: true + }); + }); +}) +gulp.task('js', gulp.parallel('js-es5', 'js-es6')); + +// Creates a UMD and ES module bundle for each of our +// built-in plugins +gulp.task('plugins', () => { + return Promise.all([ + { name: 'RevealHighlight', input: './plugin/highlight/plugin.js', output: './plugin/highlight/highlight' }, + { name: 'RevealMarkdown', input: './plugin/markdown/plugin.js', output: './plugin/markdown/markdown' }, + { name: 'RevealSearch', input: './plugin/search/plugin.js', output: './plugin/search/search' }, + { name: 'RevealNotes', input: './plugin/notes/plugin.js', output: './plugin/notes/notes' }, + { name: 'RevealZoom', input: './plugin/zoom/plugin.js', output: './plugin/zoom/zoom' }, + { name: 'RevealMath', input: './plugin/math/plugin.js', output: './plugin/math/math' }, + ].map( plugin => { + return rollup({ + cache: cache[plugin.input], + input: plugin.input, + plugins: [ + resolve(), + commonjs(), + babel({ + ...babelConfig, + ignore: [/node_modules\/(?!(highlight\.js|marked)\/).*/], + }), + terser() + ] + }).then( bundle => { + cache[plugin.input] = bundle.cache; + bundle.write({ + file: plugin.output + '.esm.js', + name: plugin.name, + format: 'es' + }) + + bundle.write({ + file: plugin.output + '.js', + name: plugin.name, + format: 'umd' + }) + }); + } )); +}) + +// a custom pipeable step to transform Sass to CSS +function compileSass() { + return through.obj( ( vinylFile, encoding, callback ) => { + const transformedFile = vinylFile.clone(); + + sass.render({ + data: transformedFile.contents.toString(), + file: transformedFile.path, + }, ( err, result ) => { + if( err ) { + callback(err); + } + else { + transformedFile.extname = '.css'; + transformedFile.contents = result.css; + callback( null, transformedFile ); + } + }); + }); +} + +gulp.task('css-themes', () => gulp.src(['./css/theme/source/*.{sass,scss}']) + .pipe(compileSass()) + .pipe(gulp.dest('./dist/theme'))) + +gulp.task('css-core', () => gulp.src(['css/reveal.scss']) + .pipe(compileSass()) + .pipe(autoprefixer()) + .pipe(minify({compatibility: 'ie9'})) + .pipe(header(banner)) + .pipe(gulp.dest('./dist'))) + +gulp.task('css', gulp.parallel('css-themes', 'css-core')) + +gulp.task('qunit', () => { + + let serverConfig = { + root, + port: 8009, + host: 'localhost', + name: 'test-server' + } + + let server = connect.server( serverConfig ) + + let testFiles = glob.sync('test/*.html' ) + + let totalTests = 0; + let failingTests = 0; + + let tests = Promise.all( testFiles.map( filename => { + return new Promise( ( resolve, reject ) => { + qunit.runQunitPuppeteer({ + targetUrl: `http://${serverConfig.host}:${serverConfig.port}/${filename}`, + timeout: 20000, + redirectConsole: false, + puppeteerArgs: ['--allow-file-access-from-files'] + }) + .then(result => { + if( result.stats.failed > 0 ) { + console.log(`${'!'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.red); + // qunit.printResultSummary(result, console); + qunit.printFailedTests(result, console); + } + else { + console.log(`${'✔'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.green); + } + + totalTests += result.stats.total; + failingTests += result.stats.failed; + + resolve(); + }) + .catch(error => { + console.error(error); + reject(); + }); + } ) + } ) ); + + return new Promise( ( resolve, reject ) => { + + tests.then( () => { + if( failingTests > 0 ) { + reject( new Error(`${failingTests}/${totalTests} tests failed`.red) ); + } + else { + console.log(`${'✔'} Passed ${totalTests} tests`.green.bold); + resolve(); + } + } ) + .catch( () => { + reject(); + } ) + .finally( () => { + server.close(); + } ); + + } ); +} ) + +gulp.task('eslint', () => gulp.src(['./js/**', 'gulpfile.js']) + .pipe(eslint()) + .pipe(eslint.format())) + +gulp.task('test', gulp.series( 'eslint', 'qunit' )) + +gulp.task('default', gulp.series(gulp.parallel('js', 'css', 'plugins'), 'test')) + +gulp.task('build', gulp.parallel('js', 'css', 'plugins')) + +gulp.task('package', gulp.series(() => + + gulp.src( + [ + './index.html', + './dist/**', + './lib/**', + './images/**', + './plugin/**', + './**/*.md' + ], + { base: './' } + ) + .pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./')) + +)) + +gulp.task('reload', () => gulp.src(['index.html']) + .pipe(connect.reload())); + +gulp.task('serve', () => { + + connect.server({ + root: root, + port: port, + host: host, + livereload: true + }) + + const slidesRoot = root.endsWith('/') ? root : root + '/' + gulp.watch([ + slidesRoot + '**/*.html', + slidesRoot + '**/*.md', + `!${slidesRoot}**/node_modules/**`, // ignore node_modules + ], gulp.series('reload')) + + gulp.watch(['js/**'], gulp.series('js', 'reload', 'eslint')) + + gulp.watch(['plugin/**/plugin.js', 'plugin/**/*.html'], gulp.series('plugins', 'reload')) + + gulp.watch([ + 'css/theme/source/**/*.{sass,scss}', + 'css/theme/template/*.{sass,scss}', + ], gulp.series('css-themes', 'reload')) + + gulp.watch([ + 'css/*.scss', + 'css/print/*.{sass,scss,css}' + ], gulp.series('css-core', 'reload')) + + gulp.watch(['test/*.html'], gulp.series('test')) + +}) diff --git a/presentation/deck/vendor/reveal/index.html b/presentation/deck/vendor/reveal/index.html new file mode 100644 index 00000000..2097df32 --- /dev/null +++ b/presentation/deck/vendor/reveal/index.html @@ -0,0 +1,40 @@ + + + + + + + reveal.js + + + + + + + + + +
+
+
Slide 1
+
Slide 2
+
+
+ + + + + + + + diff --git a/presentation/deck/vendor/reveal/js/components/playback.js b/presentation/deck/vendor/reveal/js/components/playback.js new file mode 100644 index 00000000..06fa7baa --- /dev/null +++ b/presentation/deck/vendor/reveal/js/components/playback.js @@ -0,0 +1,165 @@ +/** + * UI component that lets the use control auto-slide + * playback via play/pause. + */ +export default class Playback { + + /** + * @param {HTMLElement} container The component will append + * itself to this + * @param {function} progressCheck A method which will be + * called frequently to get the current playback progress on + * a range of 0-1 + */ + constructor( container, progressCheck ) { + + // Cosmetics + this.diameter = 100; + this.diameter2 = this.diameter/2; + this.thickness = 6; + + // Flags if we are currently playing + this.playing = false; + + // Current progress on a 0-1 range + this.progress = 0; + + // Used to loop the animation smoothly + this.progressOffset = 1; + + this.container = container; + this.progressCheck = progressCheck; + + this.canvas = document.createElement( 'canvas' ); + this.canvas.className = 'playback'; + this.canvas.width = this.diameter; + this.canvas.height = this.diameter; + this.canvas.style.width = this.diameter2 + 'px'; + this.canvas.style.height = this.diameter2 + 'px'; + this.context = this.canvas.getContext( '2d' ); + + this.container.appendChild( this.canvas ); + + this.render(); + + } + + setPlaying( value ) { + + const wasPlaying = this.playing; + + this.playing = value; + + // Start repainting if we weren't already + if( !wasPlaying && this.playing ) { + this.animate(); + } + else { + this.render(); + } + + } + + animate() { + + const progressBefore = this.progress; + + this.progress = this.progressCheck(); + + // When we loop, offset the progress so that it eases + // smoothly rather than immediately resetting + if( progressBefore > 0.8 && this.progress < 0.2 ) { + this.progressOffset = this.progress; + } + + this.render(); + + if( this.playing ) { + requestAnimationFrame( this.animate.bind( this ) ); + } + + } + + /** + * Renders the current progress and playback state. + */ + render() { + + let progress = this.playing ? this.progress : 0, + radius = ( this.diameter2 ) - this.thickness, + x = this.diameter2, + y = this.diameter2, + iconSize = 28; + + // Ease towards 1 + this.progressOffset += ( 1 - this.progressOffset ) * 0.1; + + const endAngle = ( - Math.PI / 2 ) + ( progress * ( Math.PI * 2 ) ); + const startAngle = ( - Math.PI / 2 ) + ( this.progressOffset * ( Math.PI * 2 ) ); + + this.context.save(); + this.context.clearRect( 0, 0, this.diameter, this.diameter ); + + // Solid background color + this.context.beginPath(); + this.context.arc( x, y, radius + 4, 0, Math.PI * 2, false ); + this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )'; + this.context.fill(); + + // Draw progress track + this.context.beginPath(); + this.context.arc( x, y, radius, 0, Math.PI * 2, false ); + this.context.lineWidth = this.thickness; + this.context.strokeStyle = 'rgba( 255, 255, 255, 0.2 )'; + this.context.stroke(); + + if( this.playing ) { + // Draw progress on top of track + this.context.beginPath(); + this.context.arc( x, y, radius, startAngle, endAngle, false ); + this.context.lineWidth = this.thickness; + this.context.strokeStyle = '#fff'; + this.context.stroke(); + } + + this.context.translate( x - ( iconSize / 2 ), y - ( iconSize / 2 ) ); + + // Draw play/pause icons + if( this.playing ) { + this.context.fillStyle = '#fff'; + this.context.fillRect( 0, 0, iconSize / 2 - 4, iconSize ); + this.context.fillRect( iconSize / 2 + 4, 0, iconSize / 2 - 4, iconSize ); + } + else { + this.context.beginPath(); + this.context.translate( 4, 0 ); + this.context.moveTo( 0, 0 ); + this.context.lineTo( iconSize - 4, iconSize / 2 ); + this.context.lineTo( 0, iconSize ); + this.context.fillStyle = '#fff'; + this.context.fill(); + } + + this.context.restore(); + + } + + on( type, listener ) { + this.canvas.addEventListener( type, listener, false ); + } + + off( type, listener ) { + this.canvas.removeEventListener( type, listener, false ); + } + + destroy() { + + this.playing = false; + + if( this.canvas.parentNode ) { + this.container.removeChild( this.canvas ); + } + + } + +} \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/js/config.js b/presentation/deck/vendor/reveal/js/config.js new file mode 100644 index 00000000..de44bd60 --- /dev/null +++ b/presentation/deck/vendor/reveal/js/config.js @@ -0,0 +1,330 @@ +/** + * The default reveal.js config object. + */ +export default { + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.04, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 2.0, + + // Display presentation control arrows + controls: true, + + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + + // Display a presentation progress bar + progress: true, + + // Display the page number of the current slide + // - true: Show slide number + // - false: Hide slide number + // + // Can optionally be set as a string that specifies the number formatting: + // - "h.v": Horizontal . vertical slide number (default) + // - "h/v": Horizontal / vertical slide number + // - "c": Flattened slide number + // - "c/t": Flattened slide number / total slides + // + // Alternatively, you can provide a function that returns the slide + // number for the current slide. The function should take in a slide + // object and return an array with one string [slideNumber] or + // three strings [n1,delimiter,n2]. See #formatSlideNumber(). + slideNumber: false, + + // Can be used to limit the contexts in which the slide number appears + // - "all": Always show the slide number + // - "print": Only when printing to PDF + // - "speaker": Only in the speaker view + showSlideNumber: 'all', + + // Use 1 based indexing for # links to match slide number (default is zero + // based) + hashOneBasedIndex: false, + + // Add the current slide number to the URL hash so that reloading the + // page/copying the URL will return you to the same slide + hash: false, + + // Flags if we should monitor the hash and change slides accordingly + respondToHashChanges: true, + + // Enable support for jump-to-slide navigation shortcuts + jumpToSlide: true, + + // Push each slide change to the browser history. Implies `hash: true` + history: false, + + // Enable keyboard shortcuts for navigation + keyboard: true, + + // Optional function that blocks keyboard events when retuning false + // + // If you set this to 'focused', we will only capture keyboard events + // for embedded decks when they are in focus + keyboardCondition: null, + + // Disables the default reveal.js slide layout (scaling and centering) + // so that you can use custom CSS layout + disableLayout: false, + + // Enable the slide overview mode + overview: true, + + // Vertical centering of slides + center: true, + + // Enables touch navigation on devices with touch input + touch: true, + + // Loop the presentation + loop: false, + + // Change the presentation direction to be RTL + rtl: false, + + // Changes the behavior of our navigation directions. + // + // "default" + // Left/right arrow keys step between horizontal slides, up/down + // arrow keys step between vertical slides. Space key steps through + // all slides (both horizontal and vertical). + // + // "linear" + // Removes the up/down arrows. Left/right arrows step through all + // slides (both horizontal and vertical). + // + // "grid" + // When this is enabled, stepping left/right from a vertical stack + // to an adjacent vertical stack will land you at the same vertical + // index. + // + // Consider a deck with six slides ordered in two vertical stacks: + // 1.1 2.1 + // 1.2 2.2 + // 1.3 2.3 + // + // If you're on slide 1.3 and navigate right, you will normally move + // from 1.3 -> 2.1. If "grid" is used, the same navigation takes you + // from 1.3 -> 2.3. + navigationMode: 'default', + + // Randomizes the order of slides each time the presentation loads + shuffle: false, + + // Turns fragments on and off globally + fragments: true, + + // Flags whether to include the current fragment in the URL, + // so that reloading brings you to the same fragment position + fragmentInURL: true, + + // Flags if the presentation is running in an embedded mode, + // i.e. contained within a limited portion of the screen + embedded: false, + + // Flags if we should show a help overlay when the question-mark + // key is pressed + help: true, + + // Flags if it should be possible to pause the presentation (blackout) + pause: true, + + // Flags if speaker notes should be visible to all viewers + showNotes: false, + + // Flags if slides with data-visibility="hidden" should be kep visible + showHiddenSlides: false, + + // Global override for autoplaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Global override for preloading lazy-loaded iframes + // - null: Iframes with data-src AND data-preload will be loaded when within + // the viewDistance, iframes with only data-src will be loaded when visible + // - true: All iframes with data-src will be loaded when within the viewDistance + // - false: All iframes with data-src will be loaded only when visible + preloadIframes: null, + + // Can be used to globally disable auto-animation + autoAnimate: true, + + // Optionally provide a custom element matcher that will be + // used to dictate which elements we can animate between. + autoAnimateMatcher: null, + + // Default settings for our auto-animate transitions, can be + // overridden per-slide or per-element via data arguments + autoAnimateEasing: 'ease', + autoAnimateDuration: 1.0, + autoAnimateUnmatched: true, + + // CSS properties that can be auto-animated. Position & scale + // is matched separately so there's no need to include styles + // like top/right/bottom/left, width/height or margin. + autoAnimateStyles: [ + 'opacity', + 'color', + 'background-color', + 'padding', + 'font-size', + 'line-height', + 'letter-spacing', + 'border-width', + 'border-color', + 'border-radius', + 'outline', + 'outline-offset' + ], + + // Controls automatic progression to the next slide + // - 0: Auto-sliding only happens if the data-autoslide HTML attribute + // is present on the current slide or fragment + // - 1+: All slides will progress automatically at the given interval + // - false: No auto-sliding, even if data-autoslide is present + autoSlide: 0, + + // Stop auto-sliding after user input + autoSlideStoppable: true, + + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + + // Specify the average time in seconds that you think you will spend + // presenting each slide. This is used to show a pacing timer in the + // speaker view + defaultTiming: null, + + // Enable slide navigation via mouse wheel + mouseWheel: false, + + // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually + previewLinks: false, + + // Exposes the reveal.js API through window.postMessage + postMessage: true, + + // Dispatches all reveal.js events to the parent window through postMessage + postMessageEvents: false, + + // Focuses body when page changes visibility to ensure keyboard shortcuts work + focusBodyOnPageVisibilityChange: true, + + // Transition style + transition: 'slide', // none/fade/slide/convex/concave/zoom + + // Transition speed + transitionSpeed: 'default', // default/fast/slow + + // Transition style for full page slide backgrounds + backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom + + // Parallax background image + parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg" + + // Parallax background size + parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px" + + // Parallax background repeat + parallaxBackgroundRepeat: '', // repeat/repeat-x/repeat-y/no-repeat/initial/inherit + + // Parallax background position + parallaxBackgroundPosition: '', // CSS syntax, e.g. "top left" + + // Amount of pixels to move the parallax background per slide step + parallaxBackgroundHorizontal: null, + parallaxBackgroundVertical: null, + + // Can be used to initialize reveal.js in one of the following views: + // - print: Render the presentation so that it can be printed to PDF + // - scroll: Show the presentation as a tall scrollable page with scroll + // triggered animations + view: null, + + // Adjusts the height of each slide in the scroll view. + // - full: Each slide is as tall as the viewport + // - compact: Slides are as small as possible, allowing multiple slides + // to be visible in parallel on tall devices + scrollLayout: 'full', + + // Control how scroll snapping works in the scroll view. + // - false: No snapping, scrolling is continuous + // - proximity: Snap when close to a slide + // - mandatory: Always snap to the closest slide + // + // Only applies to presentations in scroll view. + scrollSnap: 'mandatory', + + // Enables and configure the scroll view progress bar. + // - 'auto': Show the scrollbar while scrolling, hide while idle + // - true: Always show the scrollbar + // - false: Never show the scrollbar + scrollProgress: 'auto', + + // Automatically activate the scroll view when we the viewport falls + // below the given width. + scrollActivationWidth: 435, + + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + + // Prints each fragment on a separate slide + pdfSeparateFragments: true, + + // Offset used to reduce the height of content within exported PDF pages. + // This exists to account for environment differences based on how you + // print to PDF. CLI printing options, like phantomjs and wkpdf, can end + // on precisely the total height of the document whereas in-browser + // printing has to end one pixel before. + pdfPageHeightOffset: -1, + + // Number of slides away from the current that are visible + viewDistance: 3, + + // Number of slides away from the current that are visible on mobile + // devices. It is advisable to set this to a lower number than + // viewDistance in order to save resources. + mobileViewDistance: 2, + + // The display mode that will be used to show slides + display: 'block', + + // Hide cursor if inactive + hideInactiveCursor: true, + + // Time before the cursor is hidden (in ms) + hideCursorTime: 5000, + + // Should we automatically sort and set indices for fragments + // at each sync? (See Reveal.sync) + sortFragmentsOnSync: true, + + // Script dependencies to load + dependencies: [], + + // Plugin objects to register and use for this presentation + plugins: [] + +} \ No newline at end of file diff --git a/presentation/deck/vendor/reveal/js/controllers/autoanimate.js b/presentation/deck/vendor/reveal/js/controllers/autoanimate.js new file mode 100644 index 00000000..5da88fa2 --- /dev/null +++ b/presentation/deck/vendor/reveal/js/controllers/autoanimate.js @@ -0,0 +1,643 @@ +import { queryAll, extend, createStyleSheet, matches, closest } from '../utils/util.js' +import { FRAGMENT_STYLE_REGEX } from '../utils/constants.js' + +// Counter used to generate unique IDs for auto-animated elements +let autoAnimateCounter = 0; + +/** + * Automatically animates matching elements across + * slides with the [data-auto-animate] attribute. + */ +export default class AutoAnimate { + + constructor( Reveal ) { + + this.Reveal = Reveal; + + } + + /** + * Runs an auto-animation between the given slides. + * + * @param {HTMLElement} fromSlide + * @param {HTMLElement} toSlide + */ + run( fromSlide, toSlide ) { + + // Clean up after prior animations + this.reset(); + + let allSlides = this.Reveal.getSlides(); + let toSlideIndex = allSlides.indexOf( toSlide ); + let fromSlideIndex = allSlides.indexOf( fromSlide ); + + // Ensure that; + // 1. Both slides exist. + // 2. Both slides are auto-animate targets with the same + // data-auto-animate-id value (including null if absent on both). + // 3. data-auto-animate-restart isn't set on the physically latter + // slide (independent of slide direction). + if( fromSlide && toSlide && fromSlide.hasAttribute( 'data-auto-animate' ) && toSlide.hasAttribute( 'data-auto-animate' ) + && fromSlide.getAttribute( 'data-auto-animate-id' ) === toSlide.getAttribute( 'data-auto-animate-id' ) + && !( toSlideIndex > fromSlideIndex ? toSlide : fromSlide ).hasAttribute( 'data-auto-animate-restart' ) ) { + + // Create a new auto-animate sheet + this.autoAnimateStyleSheet = this.autoAnimateStyleSheet || createStyleSheet(); + + let animationOptions = this.getAutoAnimateOptions( toSlide ); + + // Set our starting state + fromSlide.dataset.autoAnimate = 'pending'; + toSlide.dataset.autoAnimate = 'pending'; + + // Flag the navigation direction, needed for fragment buildup + animationOptions.slideDirection = toSlideIndex > fromSlideIndex ? 'forward' : 'backward'; + + // If the from-slide is hidden because it has moved outside + // the view distance, we need to temporarily show it while + // measuring + let fromSlideIsHidden = fromSlide.style.display === 'none'; + if( fromSlideIsHidden ) fromSlide.style.display = this.Reveal.getConfig().display; + + // Inject our auto-animate styles for this transition + let css = this.getAutoAnimatableElements( fromSlide, toSlide ).map( elements => { + return this.autoAnimateElements( elements.from, elements.to, elements.options || {}, animationOptions, autoAnimateCounter++ ); + } ); + + if( fromSlideIsHidden ) fromSlide.style.display = 'none'; + + // Animate unmatched elements, if enabled + if( toSlide.dataset.autoAnimateUnmatched !== 'false' && this.Reveal.getConfig().autoAnimateUnmatched === true ) { + + // Our default timings for unmatched elements + let defaultUnmatchedDuration = animationOptions.duration * 0.8, + defaultUnmatchedDelay = animationOptions.duration * 0.2; + + this.getUnmatchedAutoAnimateElements( toSlide ).forEach( unmatchedElement => { + + let unmatchedOptions = this.getAutoAnimateOptions( unmatchedElement, animationOptions ); + let id = 'unmatched'; + + // If there is a duration or delay set specifically for this + // element our unmatched elements should adhere to those + if( unmatchedOptions.duration !== animationOptions.duration || unmatchedOptions.delay !== animationOptions.delay ) { + id = 'unmatched-' + autoAnimateCounter++; + css.push( `[data-auto-animate="running"] [data-auto-animate-target="${id}"] { transition: opacity ${unmatchedOptions.duration}s ease ${unmatchedOptions.delay}s; }` ); + } + + unmatchedElement.dataset.autoAnimateTarget = id; + + }, this ); + + // Our default transition for unmatched elements + css.push( `[data-auto-animate="running"] [data-auto-animate-target="unmatched"] { transition: opacity ${defaultUnmatchedDuration}s ease ${defaultUnmatchedDelay}s; }` ); + + } + + // Setting the whole chunk of CSS at once is the most + // efficient way to do this. Using sheet.insertRule + // is multiple factors slower. + this.autoAnimateStyleSheet.innerHTML = css.join( '' ); + + // Start the animation next cycle + requestAnimationFrame( () => { + if( this.autoAnimateStyleSheet ) { + // This forces our newly injected styles to be applied in Firefox + getComputedStyle( this.autoAnimateStyleSheet ).fontWeight; + + toSlide.dataset.autoAnimate = 'running'; + } + } ); + + this.Reveal.dispatchEvent({ + type: 'autoanimate', + data: { + fromSlide, + toSlide, + sheet: this.autoAnimateStyleSheet + } + }); + + } + + } + + /** + * Rolls back all changes that we've made to the DOM so + * that as part of animating. + */ + reset() { + + // Reset slides + queryAll( this.Reveal.getRevealElement(), '[data-auto-animate]:not([data-auto-animate=""])' ).forEach( element => { + element.dataset.autoAnimate = ''; + } ); + + // Reset elements + queryAll( this.Reveal.getRevealElement(), '[data-auto-animate-target]' ).forEach( element => { + delete element.dataset.autoAnimateTarget; + } ); + + // Remove the animation sheet + if( this.autoAnimateStyleSheet && this.autoAnimateStyleSheet.parentNode ) { + this.autoAnimateStyleSheet.parentNode.removeChild( this.autoAnimateStyleSheet ); + this.autoAnimateStyleSheet = null; + } + + } + + /** + * Creates a FLIP animation where the `to` element starts out + * in the `from` element position and animates to its original + * state. + * + * @param {HTMLElement} from + * @param {HTMLElement} to + * @param {Object} elementOptions Options for this element pair + * @param {Object} animationOptions Options set at the slide level + * @param {String} id Unique ID that we can use to identify this + * auto-animate element in the DOM + */ + autoAnimateElements( from, to, elementOptions, animationOptions, id ) { + + // 'from' elements are given a data-auto-animate-target with no value, + // 'to' elements are are given a data-auto-animate-target with an ID + from.dataset.autoAnimateTarget = ''; + to.dataset.autoAnimateTarget = id; + + // Each element may override any of the auto-animate options + // like transition easing, duration and delay via data-attributes + let options = this.getAutoAnimateOptions( to, animationOptions ); + + // If we're using a custom element matcher the element options + // may contain additional transition overrides + if( typeof elementOptions.delay !== 'undefined' ) options.delay = elementOptions.delay; + if( typeof elementOptions.duration !== 'undefined' ) options.duration = elementOptions.duration; + if( typeof elementOptions.easing !== 'undefined' ) options.easing = elementOptions.easing; + + let fromProps = this.getAutoAnimatableProperties( 'from', from, elementOptions ), + toProps = this.getAutoAnimatableProperties( 'to', to, elementOptions ); + + // Maintain fragment visibility for matching elements when + // we're navigating forwards, this way the viewer won't need + // to step through the same fragments twice + if( to.classList.contains( 'fragment' ) ) { + + // Don't auto-animate the opacity of fragments to avoid + // conflicts with fragment animations + delete toProps.styles['opacity']; + + if( from.classList.contains( 'fragment' ) ) { + + let fromFragmentStyle = ( from.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0]; + let toFragmentStyle = ( to.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0]; + + // Only skip the fragment if the fragment animation style + // remains unchanged + if( fromFragmentStyle === toFragmentStyle && animationOptions.slideDirection === 'forward' ) { + to.classList.add( 'visible', 'disabled' ); + } + + } + + } + + // If translation and/or scaling are enabled, css transform + // the 'to' element so that it matches the position and size + // of the 'from' element + if( elementOptions.translate !== false || elementOptions.scale !== false ) { + + let presentationScale = this.Reveal.getScale(); + + let delta = { + x: ( fromProps.x - toProps.x ) / presentationScale, + y: ( fromProps.y - toProps.y ) / presentationScale, + scaleX: fromProps.width / toProps.width, + scaleY: fromProps.height / toProps.height + }; + + // Limit decimal points to avoid 0.0001px blur and stutter + delta.x = Math.round( delta.x * 1000 ) / 1000; + delta.y = Math.round( delta.y * 1000 ) / 1000; + delta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000; + delta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000; + + let translate = elementOptions.translate !== false && ( delta.x !== 0 || delta.y !== 0 ), + scale = elementOptions.scale !== false && ( delta.scaleX !== 0 || delta.scaleY !== 0 ); + + // No need to transform if nothing's changed + if( translate || scale ) { + + let transform = []; + + if( translate ) transform.push( `translate(${delta.x}px, ${delta.y}px)` ); + if( scale ) transform.push( `scale(${delta.scaleX}, ${delta.scaleY})` ); + + fromProps.styles['transform'] = transform.join( ' ' ); + fromProps.styles['transform-origin'] = 'top left'; + + toProps.styles['transform'] = 'none'; + + } + + } + + // Delete all unchanged 'to' styles + for( let propertyName in toProps.styles ) { + const toValue = toProps.styles[propertyName]; + const fromValue = fromProps.styles[propertyName]; + + if( toValue === fromValue ) { + delete toProps.styles[propertyName]; + } + else { + // If these property values were set via a custom matcher providing + // an explicit 'from' and/or 'to' value, we always inject those values. + if( toValue.explicitValue === true ) { + toProps.styles[propertyName] = toValue.value; + } + + if( fromValue.explicitValue === true ) { + fromProps.styles[propertyName] = fromValue.value; + } + } + } + + let css = ''; + + let toStyleProperties = Object.keys( toProps.styles ); + + // Only create animate this element IF at least one style + // property has changed + if( toStyleProperties.length > 0 ) { + + // Instantly move to the 'from' state + fromProps.styles['transition'] = 'none'; + + // Animate towards the 'to' state + toProps.styles['transition'] = `all ${options.duration}s ${options.easing} ${options.delay}s`; + toProps.styles['transition-property'] = toStyleProperties.join( ', ' ); + toProps.styles['will-change'] = toStyleProperties.join( ', ' ); + + // Build up our custom CSS. We need to override inline styles + // so we need to make our styles vErY IMPORTANT!1!! + let fromCSS = Object.keys( fromProps.styles ).map( propertyName => { + return propertyName + ': ' + fromProps.styles[propertyName] + ' !important;'; + } ).join( '' ); + + let toCSS = Object.keys( toProps.styles ).map( propertyName => { + return propertyName + ': ' + toProps.styles[propertyName] + ' !important;'; + } ).join( '' ); + + css = '[data-auto-animate-target="'+ id +'"] {'+ fromCSS +'}' + + '[data-auto-animate="running"] [data-auto-animate-target="'+ id +'"] {'+ toCSS +'}'; + + } + + return css; + + } + + /** + * Returns the auto-animate options for the given element. + * + * @param {HTMLElement} element Element to pick up options + * from, either a slide or an animation target + * @param {Object} [inheritedOptions] Optional set of existing + * options + */ + getAutoAnimateOptions( element, inheritedOptions ) { + + let options = { + easing: this.Reveal.getConfig().autoAnimateEasing, + duration: this.Reveal.getConfig().autoAnimateDuration, + delay: 0 + }; + + options = extend( options, inheritedOptions ); + + // Inherit options from parent elements + if( element.parentNode ) { + let autoAnimatedParent = closest( element.parentNode, '[data-auto-animate-target]' ); + if( autoAnimatedParent ) { + options = this.getAutoAnimateOptions( autoAnimatedParent, options ); + } + } + + if( element.dataset.autoAnimateEasing ) { + options.easing = element.dataset.autoAnimateEasing; + } + + if( element.dataset.autoAnimateDuration ) { + options.duration = parseFloat( element.dataset.autoAnimateDuration ); + } + + if( element.dataset.autoAnimateDelay ) { + options.delay = parseFloat( element.dataset.autoAnimateDelay ); + } + + return options; + + } + + /** + * Returns an object containing all of the properties + * that can be auto-animated for the given element and + * their current computed values. + * + * @param {String} direction 'from' or 'to' + */ + getAutoAnimatableProperties( direction, element, elementOptions ) { + + let config = this.Reveal.getConfig(); + + let properties = { styles: [] }; + + // Position and size + if( elementOptions.translate !== false || elementOptions.scale !== false ) { + let bounds; + + // Custom auto-animate may optionally return a custom tailored + // measurement function + if( typeof elementOptions.measure === 'function' ) { + bounds = elementOptions.measure( element ); + } + else { + if( config.center ) { + // More precise, but breaks when used in combination + // with zoom for scaling the deck ¯\_(ツ)_/¯ + bounds = element.getBoundingClientRect(); + } + else { + let scale = this.Reveal.getScale(); + bounds = { + x: element.offsetLeft * scale, + y: element.offsetTop * scale, + width: element.offsetWidth * scale, + height: element.offsetHeight * scale + }; + } + } + + properties.x = bounds.x; + properties.y = bounds.y; + properties.width = bounds.width; + properties.height = bounds.height; + } + + const computedStyles = getComputedStyle( element ); + + // CSS styles + ( elementOptions.styles || config.autoAnimateStyles ).forEach( style => { + let value; + + // `style` is either the property name directly, or an object + // definition of a style property + if( typeof style === 'string' ) style = { property: style }; + + if( typeof style.from !== 'undefined' && direction === 'from' ) { + value = { value: style.from, explicitValue: true }; + } + else if( typeof style.to !== 'undefined' && direction === 'to' ) { + value = { value: style.to, explicitValue: true }; + } + else { + // Use a unitless value for line-height so that it inherits properly + if( style.property === 'line-height' ) { + value = parseFloat( computedStyles['line-height'] ) / parseFloat( computedStyles['font-size'] ); + } + + if( isNaN(value) ) { + value = computedStyles[style.property]; + } + } + + if( value !== '' ) { + properties.styles[style.property] = value; + } + } ); + + return properties; + + } + + /** + * Get a list of all element pairs that we can animate + * between the given slides. + * + * @param {HTMLElement} fromSlide + * @param {HTMLElement} toSlide + * + * @return {Array} Each value is an array where [0] is + * the element we're animating from and [1] is the + * element we're animating to + */ + getAutoAnimatableElements( fromSlide, toSlide ) { + + let matcher = typeof this.Reveal.getConfig().autoAnimateMatcher === 'function' ? this.Reveal.getConfig().autoAnimateMatcher : this.getAutoAnimatePairs; + + let pairs = matcher.call( this, fromSlide, toSlide ); + + let reserved = []; + + // Remove duplicate pairs + return pairs.filter( ( pair, index ) => { + if( reserved.indexOf( pair.to ) === -1 ) { + reserved.push( pair.to ); + return true; + } + } ); + + } + + /** + * Identifies matching elements between slides. + * + * You can specify a custom matcher function by using + * the `autoAnimateMatcher` config option. + */ + getAutoAnimatePairs( fromSlide, toSlide ) { + + let pairs = []; + + const codeNodes = 'pre'; + const textNodes = 'h1, h2, h3, h4, h5, h6, p, li'; + const mediaNodes = 'img, video, iframe'; + + // Explicit matches via data-id + this.findAutoAnimateMatches( pairs, fromSlide, toSlide, '[data-id]', node => { + return node.nodeName + ':::' + node.getAttribute( 'data-id' ); + } ); + + // Text + this.findAutoAnimateMatches( pairs, fromSlide, toSlide, textNodes, node => { + return node.nodeName + ':::' + node.innerText; + } ); + + // Media + this.findAutoAnimateMatches( pairs, fromSlide, toSlide, mediaNodes, node => { + return node.nodeName + ':::' + ( node.getAttribute( 'src' ) || node.getAttribute( 'data-src' ) ); + } ); + + // Code + this.findAutoAnimateMatches( pairs, fromSlide, toSlide, codeNodes, node => { + return node.nodeName + ':::' + node.innerText; + } ); + + pairs.forEach( pair => { + // Disable scale transformations on text nodes, we transition + // each individual text property instead + if( matches( pair.from, textNodes ) ) { + pair.options = { scale: false }; + } + // Animate individual lines of code + else if( matches( pair.from, codeNodes ) ) { + + // Transition the code block's width and height instead of scaling + // to prevent its content from being squished + pair.options = { scale: false, styles: [ 'width', 'height' ] }; + + // Lines of code + this.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-code', node => { + return node.textContent; + }, { + scale: false, + styles: [], + measure: this.getLocalBoundingBox.bind( this ) + } ); + + // Line numbers + this.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-numbers[data-line-number]', node => { + return node.getAttribute( 'data-line-number' ); + }, { + scale: false, + styles: [ 'width' ], + measure: this.getLocalBoundingBox.bind( this ) + } ); + + } + + }, this ); + + return pairs; + + } + + /** + * Helper method which returns a bounding box based on + * the given elements offset coordinates. + * + * @param {HTMLElement} element + * @return {Object} x, y, width, height + */ + getLocalBoundingBox( element ) { + + const presentationScale = this.Reveal.getScale(); + + return { + x: Math.round( ( element.offsetLeft * presentationScale ) * 100 ) / 100, + y: Math.round( ( element.offsetTop * presentationScale ) * 100 ) / 100, + width: Math.round( ( element.offsetWidth * presentationScale ) * 100 ) / 100, + height: Math.round( ( element.offsetHeight * presentationScale ) * 100 ) / 100 + }; + + } + + /** + * Finds matching elements between two slides. + * + * @param {Array} pairs List of pairs to push matches to + * @param {HTMLElement} fromScope Scope within the from element exists + * @param {HTMLElement} toScope Scope within the to element exists + * @param {String} selector CSS selector of the element to match + * @param {Function} serializer A function that accepts an element and returns + * a stringified ID based on its contents + * @param {Object} animationOptions Optional config options for this pair + */ + findAutoAnimateMatches( pairs, fromScope, toScope, selector, serializer, animationOptions ) { + + let fromMatches = {}; + let toMatches = {}; + + [].slice.call( fromScope.querySelectorAll( selector ) ).forEach( ( element, i ) => { + const key = serializer( element ); + if( typeof key === 'string' && key.length ) { + fromMatches[key] = fromMatches[key] || []; + fromMatches[key].push( element ); + } + } ); + + [].slice.call( toScope.querySelectorAll( selector ) ).forEach( ( element, i ) => { + const key = serializer( element ); + toMatches[key] = toMatches[key] || []; + toMatches[key].push( element ); + + let fromElement; + + // Retrieve the 'from' element + if( fromMatches[key] ) { + const primaryIndex = toMatches[key].length - 1; + const secondaryIndex = fromMatches[key].length - 1; + + // If there are multiple identical from elements, retrieve + // the one at the same index as our to-element. + if( fromMatches[key][ primaryIndex ] ) { + fromElement = fromMatches[key][ primaryIndex ]; + fromMatches[key][ primaryIndex ] = null; + } + // If there are no matching from-elements at the same index, + // use the last one. + else if( fromMatches[key][ secondaryIndex ] ) { + fromElement = fromMatches[key][ secondaryIndex ]; + fromMatches[key][ secondaryIndex ] = null; + } + } + + // If we've got a matching pair, push it to the list of pairs + if( fromElement ) { + pairs.push({ + from: fromElement, + to: element, + options: animationOptions + }); + } + } ); + + } + + /** + * Returns a all elements within the given scope that should + * be considered unmatched in an auto-animate transition. If + * fading of unmatched elements is turned on, these elements + * will fade when going between auto-animate slides. + * + * Note that parents of auto-animate targets are NOT considered + * unmatched since fading them would break the auto-animation. + * + * @param {HTMLElement} rootElement + * @return {Array} + */ + getUnmatchedAutoAnimateElements( rootElement ) { + + return [].slice.call( rootElement.children ).reduce( ( result, element ) => { + + const containsAnimatedElements = element.querySelector( '[data-auto-animate-target]' ); + + // The element is unmatched if + // - It is not an auto-animate target + // - It does not contain any auto-animate targets + if( !element.hasAttribute( 'data-auto-animate-target' ) && !containsAnimatedElements ) { + result.push( element ); + } + + if( element.querySelector( '[data-auto-animate-target]' ) ) { + result = result.concat( this.getUnmatchedAutoAnimateElements( element ) ); + } + + return result; + + }, [] ); + + } + +} diff --git a/presentation/deck/vendor/reveal/js/controllers/backgrounds.js b/presentation/deck/vendor/reveal/js/controllers/backgrounds.js new file mode 100644 index 00000000..e7ca644f --- /dev/null +++ b/presentation/deck/vendor/reveal/js/controllers/backgrounds.js @@ -0,0 +1,468 @@ +import { queryAll } from '../utils/util.js' +import { colorToRgb, colorBrightness } from '../utils/color.js' + +/** + * Creates and updates slide backgrounds. + */ +export default class Backgrounds { + + constructor( Reveal ) { + + this.Reveal = Reveal; + + } + + render() { + + this.element = document.createElement( 'div' ); + this.element.className = 'backgrounds'; + this.Reveal.getRevealElement().appendChild( this.element ); + + } + + /** + * Creates the slide background elements and appends them + * to the background container. One element is created per + * slide no matter if the given slide has visible background. + */ + create() { + + // Clear prior backgrounds + this.element.innerHTML = ''; + this.element.classList.add( 'no-transition' ); + + // Iterate over all horizontal slides + this.Reveal.getHorizontalSlides().forEach( slideh => { + + let backgroundStack = this.createBackground( slideh, this.element ); + + // Iterate over all vertical slides + queryAll( slideh, 'section' ).forEach( slidev => { + + this.createBackground( slidev, backgroundStack ); + + backgroundStack.classList.add( 'stack' ); + + } ); + + } ); + + // Add parallax background if specified + if( this.Reveal.getConfig().parallaxBackgroundImage ) { + + this.element.style.backgroundImage = 'url("' + this.Reveal.getConfig().parallaxBackgroundImage + '")'; + this.element.style.backgroundSize = this.Reveal.getConfig().parallaxBackgroundSize; + this.element.style.backgroundRepeat = this.Reveal.getConfig().parallaxBackgroundRepeat; + this.element.style.backgroundPosition = this.Reveal.getConfig().parallaxBackgroundPosition; + + // Make sure the below properties are set on the element - these properties are + // needed for proper transitions to be set on the element via CSS. To remove + // annoying background slide-in effect when the presentation starts, apply + // these properties after short time delay + setTimeout( () => { + this.Reveal.getRevealElement().classList.add( 'has-parallax-background' ); + }, 1 ); + + } + else { + + this.element.style.backgroundImage = ''; + this.Reveal.getRevealElement().classList.remove( 'has-parallax-background' ); + + } + + } + + /** + * Creates a background for the given slide. + * + * @param {HTMLElement} slide + * @param {HTMLElement} container The element that the background + * should be appended to + * @return {HTMLElement} New background div + */ + createBackground( slide, container ) { + + // Main slide background element + let element = document.createElement( 'div' ); + element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + + // Inner background element that wraps images/videos/iframes + let contentElement = document.createElement( 'div' ); + contentElement.className = 'slide-background-content'; + + element.appendChild( contentElement ); + container.appendChild( element ); + + slide.slideBackgroundElement = element; + slide.slideBackgroundContentElement = contentElement; + + // Syncs the background to reflect all current background settings + this.sync( slide ); + + return element; + + } + + /** + * Renders all of the visual properties of a slide background + * based on the various background attributes. + * + * @param {HTMLElement} slide + */ + sync( slide ) { + + const element = slide.slideBackgroundElement, + contentElement = slide.slideBackgroundContentElement; + + const data = { + background: slide.getAttribute( 'data-background' ), + backgroundSize: slide.getAttribute( 'data-background-size' ), + backgroundImage: slide.getAttribute( 'data-background-image' ), + backgroundVideo: slide.getAttribute( 'data-background-video' ), + backgroundIframe: slide.getAttribute( 'data-background-iframe' ), + backgroundColor: slide.getAttribute( 'data-background-color' ), + backgroundGradient: slide.getAttribute( 'data-background-gradient' ), + backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), + backgroundPosition: slide.getAttribute( 'data-background-position' ), + backgroundTransition: slide.getAttribute( 'data-background-transition' ), + backgroundOpacity: slide.getAttribute( 'data-background-opacity' ), + }; + + const dataPreload = slide.hasAttribute( 'data-preload' ); + + // Reset the prior background state in case this is not the + // initial sync + slide.classList.remove( 'has-dark-background' ); + slide.classList.remove( 'has-light-background' ); + + element.removeAttribute( 'data-loaded' ); + element.removeAttribute( 'data-background-hash' ); + element.removeAttribute( 'data-background-size' ); + element.removeAttribute( 'data-background-transition' ); + element.style.backgroundColor = ''; + + contentElement.style.backgroundSize = ''; + contentElement.style.backgroundRepeat = ''; + contentElement.style.backgroundPosition = ''; + contentElement.style.backgroundImage = ''; + contentElement.style.opacity = ''; + contentElement.innerHTML = ''; + + if( data.background ) { + // Auto-wrap image urls in url(...) + if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp|webp)([?#\s]|$)/gi.test( data.background ) ) { + slide.setAttribute( 'data-background-image', data.background ); + } + else { + element.style.background = data.background; + } + } + + // Create a hash for this combination of background settings. + // This is used to determine when two slide backgrounds are + // the same. + if( data.background || data.backgroundColor || data.backgroundGradient || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) { + element.setAttribute( 'data-background-hash', data.background + + data.backgroundSize + + data.backgroundImage + + data.backgroundVideo + + data.backgroundIframe + + data.backgroundColor + + data.backgroundGradient + + data.backgroundRepeat + + data.backgroundPosition + + data.backgroundTransition + + data.backgroundOpacity ); + } + + // Additional and optional background properties + if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); + if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; + if( data.backgroundGradient ) element.style.backgroundImage = data.backgroundGradient; + if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); + + if( dataPreload ) element.setAttribute( 'data-preload', '' ); + + // Background image options are set on the content wrapper + if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; + if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity; + + const contrastClass = this.getContrastClass( slide ); + + if( typeof contrastClass === 'string' ) { + slide.classList.add( contrastClass ); + } + + } + + /** + * Returns a class name that can be applied to a slide to indicate + * if it has a light or dark background. + * + * @param {*} slide + * + * @returns {string|null} + */ + getContrastClass( slide ) { + + const element = slide.slideBackgroundElement; + + // If this slide has a background color, we add a class that + // signals if it is light or dark. If the slide has no background + // color, no class will be added + let contrastColor = slide.getAttribute( 'data-background-color' ); + + // If no bg color was found, or it cannot be converted by colorToRgb, check the computed background + if( !contrastColor || !colorToRgb( contrastColor ) ) { + let computedBackgroundStyle = window.getComputedStyle( element ); + if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) { + contrastColor = computedBackgroundStyle.backgroundColor; + } + } + + if( contrastColor ) { + const rgb = colorToRgb( contrastColor ); + + // Ignore fully transparent backgrounds. Some browsers return + // rgba(0,0,0,0) when reading the computed background color of + // an element with no background + if( rgb && rgb.a !== 0 ) { + if( colorBrightness( contrastColor ) < 128 ) { + return 'has-dark-background'; + } + else { + return 'has-light-background'; + } + } + } + + return null; + + } + + /** + * Bubble the 'has-light-background'/'has-dark-background' classes. + */ + bubbleSlideContrastClassToElement( slide, target ) { + + [ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => { + if( slide.classList.contains( classToBubble ) ) { + target.classList.add( classToBubble ); + } + else { + target.classList.remove( classToBubble ); + } + }, this ); + + } + + /** + * Updates the background elements to reflect the current + * slide. + * + * @param {boolean} includeAll If true, the backgrounds of + * all vertical slides (not just the present) will be updated. + */ + update( includeAll = false ) { + + let config = this.Reveal.getConfig(); + let currentSlide = this.Reveal.getCurrentSlide(); + let indices = this.Reveal.getIndices(); + + let currentBackground = null; + + // Reverse past/future classes when in RTL mode + let horizontalPast = config.rtl ? 'future' : 'past', + horizontalFuture = config.rtl ? 'past' : 'future'; + + // Update the classes of all backgrounds to match the + // states of their slides (past/present/future) + Array.from( this.element.childNodes ).forEach( ( backgroundh, h ) => { + + backgroundh.classList.remove( 'past', 'present', 'future' ); + + if( h < indices.h ) { + backgroundh.classList.add( horizontalPast ); + } + else if ( h > indices.h ) { + backgroundh.classList.add( horizontalFuture ); + } + else { + backgroundh.classList.add( 'present' ); + + // Store a reference to the current background element + currentBackground = backgroundh; + } + + if( includeAll || h === indices.h ) { + queryAll( backgroundh, '.slide-background' ).forEach( ( backgroundv, v ) => { + + backgroundv.classList.remove( 'past', 'present', 'future' ); + + const indexv = typeof indices.v === 'number' ? indices.v : 0; + + if( v < indexv ) { + backgroundv.classList.add( 'past' ); + } + else if ( v > indexv ) { + backgroundv.classList.add( 'future' ); + } + else { + backgroundv.classList.add( 'present' ); + + // Only if this is the present horizontal and vertical slide + if( h === indices.h ) currentBackground = backgroundv; + } + + } ); + } + + } ); + + // The previous background may refer to a DOM element that has + // been removed after a presentation is synced & bgs are recreated + if( this.previousBackground && !this.previousBackground.closest( 'body' ) ) { + this.previousBackground = null; + } + + if( currentBackground && this.previousBackground ) { + + // Don't transition between identical backgrounds. This + // prevents unwanted flicker. + let previousBackgroundHash = this.previousBackground.getAttribute( 'data-background-hash' ); + let currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' ); + + if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== this.previousBackground ) { + this.element.classList.add( 'no-transition' ); + + // If multiple slides have the same background video, carry + // the