update reveal.js

This commit is contained in:
2025-07-07 20:42:39 +02:00
parent 91d4509dac
commit e5beded4a5
119 changed files with 11231 additions and 13895 deletions
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,package-lock.json,*.css,.codespellrc
check-hidden = true
# Ignore super long lines -- must be minimized etc, acronyms
# and some near hit variables
ignore-regex = ^.{120,}|\b(currentY|FOM)\b
# ignore-words-list =
-24
View File
@@ -1,24 +0,0 @@
name: tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build --if-present
- run: npm test
env:
CI: true
+23
View File
@@ -0,0 +1,23 @@
# Codespell configuration is within .codespellrc
---
name: Spellcheck
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Codespell
uses: codespell-project/actions-codespell@v2
+31
View File
@@ -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
-2
View File
@@ -1,7 +1,5 @@
/test
/examples
.github
.gulpfile
.sass-cache
gulpfile.js
CONTRIBUTING.md
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (C) 2011-2022 Hakim El Hattab, http://hakim.se, and reveal.js contributors
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
+3 -39
View File
@@ -4,7 +4,7 @@
</a>
<br><br>
<a href="https://github.com/hakimel/reveal.js/actions"><img src="https://github.com/hakimel/reveal.js/workflows/tests/badge.svg"></a>
<a href="https://slides.com/"><img src="https://s3.amazonaws.com/static.slid.es/images/slides-github-banner-320x40.png?1" alt="Slides" width="160" height="20"></a>
<a href="https://slides.com/"><img src="https://static.slid.es/images/slides-github-banner-320x40.png?1" alt="Slides" width="160" height="20"></a>
</p>
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/).
@@ -13,35 +13,7 @@ The framework comes with a powerful feature set including [nested slides](https:
---
### Sponsors
Hakim's open source work is supported by <a href="https://github.com/sponsors/hakimel">GitHub sponsors</a>. Special thanks to:
<div align="center">
<table>
<td align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=revealjs&utm_source=github">
<div>
<img src="https://user-images.githubusercontent.com/629429/151508669-efb4c3b3-8fe3-45eb-8e47-e9510b5f0af1.svg" width="290" alt="WorkOS">
</div>
<b>Your app, enterprise-ready.</b>
<div>
<sub>Start selling to enterprise customers with just a few lines of code. Add Single Sign-On (and more) in minutes instead of months.</sup>
</div>
</a>
</td>
<td align="center">
<a href="https://www.doppler.com/?utm_cam![Uploading workos-logo-white-bg.svg…]()
paign=github_repo&utm_medium=referral&utm_content=revealjs&utm_source=github">
<div>
<img src="https://user-images.githubusercontent.com/629429/151510865-9fd454f1-fd8c-4df4-b227-a54b87313db4.png" width="290" alt="Doppler">
</div>
<b>All your environment variables, in one place</b>
<div>
<sub>Stop struggling with scattered API keys, hacking together home-brewed tools, and avoiding access controls. Keep your team and servers in sync with Doppler.</sup>
</div>
</a>
</td>
</table>
</div>
Want to create reveal.js presentation in a graphical editor? Try <https://slides.com>. It's made by the same people behind reveal.js.
---
@@ -52,15 +24,7 @@ Hakim's open source work is supported by <a href="https://github.com/sponsors/ha
- 🖌 [Try the visual editor for reveal.js at Slides.com](https://slides.com/)
- 🎬 [Watch the reveal.js video course (paid)](https://revealjs.com/course)
---
### Online Editor
Want to create your presentation using a visual editor? Try the official reveal.js presentation platform for free at [Slides.com](https://slides.com). It's made by the same people behind reveal.js.
<br>
<br>
---
<div align="center">
MIT licensed | Copyright © 2011-2022 Hakim El Hattab, https://hakim.se
MIT licensed | Copyright © 2011-2024 Hakim El Hattab, https://hakim.se
</div>
+1
View File
@@ -25,6 +25,7 @@
// Stack multiple elements on top of each other
.reveal .r-stack {
display: grid;
grid-template-rows: 100%;
}
.reveal .r-stack > * {
+31 -38
View File
@@ -1,42 +1,30 @@
/* Default Print Stylesheet Template
by Rob Glazebrook of CSSnewbie.com
Last Updated: June 4, 2008
Feel free (nay, compelled) to edit, append, and
manipulate this file as you see fit. */
@media print {
html:not(.print-pdf) {
background: #fff;
overflow: visible;
width: auto;
height: auto;
overflow: visible;
body {
background: #fff;
font-size: 20pt;
width: auto;
height: auto;
border: 0;
margin: 0 5%;
margin: 0;
padding: 0;
overflow: visible;
float: none !important;
}
}
html:not(.print-pdf) .reveal {
background: #fff;
font-size: 20pt;
.nestedarrow,
.controls,
.fork-reveal,
.share-reveal,
.state-background,
.reveal .progress,
.reveal .backgrounds,
.reveal .slide-number {
.progress,
.backgrounds,
.slide-number {
display: none !important;
}
body, p, td, li {
p, td, li {
font-size: 20pt!important;
color: #000;
}
@@ -49,7 +37,6 @@
letter-spacing: normal;
}
/* Need to reduce the size of the fonts for printing */
h1 { font-size: 28pt !important; }
h2 { font-size: 24pt !important; }
h3 { font-size: 22pt !important; }
@@ -74,18 +61,19 @@
margin: 0;
text-align: left !important;
}
.reveal pre,
.reveal table {
pre,
table {
margin-left: 0;
margin-right: 0;
}
.reveal pre code {
pre code {
padding: 20px;
}
.reveal blockquote {
blockquote {
margin: 20px 0;
}
.reveal .slides {
.slides {
position: static !important;
width: auto !important;
height: auto !important;
@@ -106,7 +94,7 @@
perspective-origin: 50% 50%;
}
.reveal .slides section {
.slides section {
visibility: visible !important;
position: static !important;
width: auto !important;
@@ -129,19 +117,24 @@
transform: none !important;
transition: none !important;
}
.reveal .slides section.stack {
.slides section.stack {
padding: 0 !important;
}
.reveal section:last-of-type {
.slides section:last-of-type {
page-break-after: avoid !important;
}
.reveal section .fragment {
.slides section .fragment {
opacity: 1 !important;
visibility: visible !important;
transform: none !important;
}
.reveal section img {
.r-fit-text {
white-space: normal !important;
}
section img {
display: block;
margin: 15px 0px;
background: rgba(255,255,255,1);
@@ -149,11 +142,11 @@
box-shadow: none;
}
.reveal section small {
section small {
font-size: 0.8em;
}
.reveal .hljs {
.hljs {
max-height: 100%;
white-space: pre-wrap;
word-wrap: break-word;
@@ -161,11 +154,11 @@
font-size: 15pt;
}
.reveal .hljs .hljs-ln-numbers {
.hljs .hljs-ln-numbers {
white-space: nowrap;
}
.reveal .hljs td {
.hljs td {
font-size: inherit !important;
color: inherit !important;
}
+6 -3
View File
@@ -5,7 +5,7 @@
* https://revealjs.com/pdf-export/
*/
html.print-pdf {
html.reveal-print {
* {
-webkit-print-color-adjust: exact;
}
@@ -36,7 +36,6 @@ html.print-pdf {
.reveal pre code {
overflow: hidden !important;
font-family: Courier, 'Courier New', monospace !important;
}
.reveal {
@@ -71,6 +70,10 @@ html.print-pdf {
page-break-after: always;
}
.reveal .slides .pdf-page:last-of-type {
page-break-after: avoid;
}
.reveal .slides section {
visibility: visible !important;
display: block !important;
@@ -100,7 +103,6 @@ html.print-pdf {
box-shadow: none;
}
/* Slide backgrounds are placed inside of their slide when exporting to PDF */
.reveal .backgrounds {
display: none;
@@ -147,6 +149,7 @@ html.print-pdf {
display: block;
position: absolute;
font-size: 14px;
visibility: visible;
}
/* This accessibility tool is not useful in PDF and breaks it visually */
+545 -185
View File
@@ -1,5 +1,3 @@
@use "sass:math";
/**
* reveal.js
* http://revealjs.com
@@ -8,6 +6,7 @@
* Copyright (C) Hakim El Hattab, https://hakim.se
*/
@use "sass:math";
@import 'layout';
/*********************************************
@@ -19,6 +18,7 @@ html.reveal-full-page {
height: 100%;
height: 100vh;
height: calc( var(--vh, 1vh) * 100 );
height: 100dvh;
overflow: hidden;
}
@@ -31,6 +31,18 @@ html.reveal-full-page {
background-color: #fff;
color: #000;
--r-controls-spacing: 12px;
--r-overlay-header-height: 40px;
--r-overlay-margin: 0px;
--r-overlay-padding: 6px;
--r-overlay-gap: 5px;
}
@media screen and (max-width: 1024px), (max-height: 768px) {
.reveal-viewport {
--r-overlay-header-height: 26px;
}
}
// Force the presentation to cover the full viewport when we
@@ -48,11 +60,14 @@ html.reveal-full-page {
* VIEW FRAGMENTS
*********************************************/
.reveal .slides section .fragment {
opacity: 0;
visibility: hidden;
.reveal .fragment {
transition: all .2s ease;
will-change: opacity;
&:not(.custom) {
opacity: 0;
visibility: hidden;
will-change: opacity;
}
&.visible {
opacity: 1;
@@ -64,7 +79,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.grow {
.reveal .fragment.grow {
opacity: 1;
visibility: inherit;
@@ -73,7 +88,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.shrink {
.reveal .fragment.shrink {
opacity: 1;
visibility: inherit;
@@ -82,7 +97,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.zoom-in {
.reveal .fragment.zoom-in {
transform: scale( 0.1 );
&.visible {
@@ -90,7 +105,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-out {
.reveal .fragment.fade-out {
opacity: 1;
visibility: inherit;
@@ -100,7 +115,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.semi-fade-out {
.reveal .fragment.semi-fade-out {
opacity: 1;
visibility: inherit;
@@ -110,7 +125,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.strike {
.reveal .fragment.strike {
opacity: 1;
visibility: inherit;
@@ -119,7 +134,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-up {
.reveal .fragment.fade-up {
transform: translate(0, 40px);
&.visible {
@@ -127,7 +142,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-down {
.reveal .fragment.fade-down {
transform: translate(0, -40px);
&.visible {
@@ -135,7 +150,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-right {
.reveal .fragment.fade-right {
transform: translate(-40px, 0);
&.visible {
@@ -143,7 +158,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-left {
.reveal .fragment.fade-left {
transform: translate(40px, 0);
&.visible {
@@ -151,8 +166,8 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-in-then-out,
.reveal .slides section .fragment.current-visible {
.reveal .fragment.fade-in-then-out,
.reveal .fragment.current-visible {
opacity: 0;
visibility: hidden;
@@ -162,7 +177,7 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.fade-in-then-semi-out {
.reveal .fragment.fade-in-then-semi-out {
opacity: 0;
visibility: hidden;
@@ -177,32 +192,32 @@ html.reveal-full-page {
}
}
.reveal .slides section .fragment.highlight-red,
.reveal .slides section .fragment.highlight-current-red,
.reveal .slides section .fragment.highlight-green,
.reveal .slides section .fragment.highlight-current-green,
.reveal .slides section .fragment.highlight-blue,
.reveal .slides section .fragment.highlight-current-blue {
.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 .slides section .fragment.highlight-red.visible {
.reveal .fragment.highlight-red.visible {
color: #ff2c2d
}
.reveal .slides section .fragment.highlight-green.visible {
.reveal .fragment.highlight-green.visible {
color: #17ff2e;
}
.reveal .slides section .fragment.highlight-blue.visible {
.reveal .fragment.highlight-blue.visible {
color: #1b91ff;
}
.reveal .slides section .fragment.highlight-current-red.current-fragment {
.reveal .fragment.highlight-current-red.current-fragment {
color: #ff2c2d
}
.reveal .slides section .fragment.highlight-current-green.current-fragment {
.reveal .fragment.highlight-current-green.current-fragment {
color: #17ff2e;
}
.reveal .slides section .fragment.highlight-current-blue.current-fragment {
.reveal .fragment.highlight-current-blue.current-fragment {
color: #1b91ff;
}
@@ -268,13 +283,11 @@ $controlsArrowAngleActive: 36deg;
}
.reveal .controls {
$spacing: 12px;
display: none;
position: absolute;
top: auto;
bottom: $spacing;
right: $spacing;
bottom: var(--r-controls-spacing);
right: var(--r-controls-spacing);
left: auto;
z-index: 11;
color: #000;
@@ -506,7 +519,9 @@ $controlsArrowAngleActive: 36deg;
// Edge aligned controls layout
@media screen and (min-width: 500px) {
$spacing: 0.8em;
.reveal-viewport {
--r-controls-spacing: 0.8em;
}
.reveal .controls[data-controls-layout="edges"] {
& {
@@ -526,24 +541,24 @@ $controlsArrowAngleActive: 36deg;
.navigate-left {
top: 50%;
left: $spacing;
left: var(--r-controls-spacing);
margin-top: -$controlArrowSize*0.5;
}
.navigate-right {
top: 50%;
right: $spacing;
right: var(--r-controls-spacing);
margin-top: -$controlArrowSize*0.5;
}
.navigate-up {
top: $spacing;
top: var(--r-controls-spacing);
left: 50%;
margin-left: -$controlArrowSize*0.5;
}
.navigate-down {
bottom: $spacing - $controlArrowSpacing + 0.3em;
bottom: calc(var(--r-controls-spacing) - #{$controlArrowSpacing} + 0.3em);
left: 50%;
margin-left: -$controlArrowSize*0.5;
}
@@ -625,11 +640,16 @@ $controlsArrowAngleActive: 36deg;
touch-action: pinch-zoom;
}
// Swiping on an embedded deck should not block page scrolling
// 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%;
@@ -723,6 +743,8 @@ $controlsArrowAngleActive: 36deg;
.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;
@@ -987,7 +1009,7 @@ $controlsArrowAngleActive: 36deg;
border-radius: 4px;
box-shadow: 0px 95px 25px rgba(0,0,0,0.2);
-webkit-transform: translateZ(-90px) rotateX( 65deg );
transform: translateZ(-90px) rotateX( 65deg );
}
.reveal.page .slides>section.stack {
@@ -1306,12 +1328,6 @@ $controlsArrowAngleActive: 36deg;
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;
@@ -1344,10 +1360,6 @@ $controlsArrowAngleActive: 36deg;
.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 {
@@ -1427,160 +1439,234 @@ $controlsArrowAngleActive: 36deg;
* OVERLAY FOR LINK PREVIEWS AND HELP
*********************************************/
$overlayHeaderHeight: 40px;
$overlayHeaderPadding: 5px;
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.reveal > .overlay {
@keyframes scale-up {
from { transform: scale( 0.95 ); }
to { transform: scale( 1 ); }
}
.reveal [data-preview-image],
.reveal [data-preview-video],
.reveal [data-preview-link]:not(a):not([data-preview-link=false]) {
cursor: zoom-in;
}
.r-overlay {
position: absolute;
top: 0;
left: 0;
top: var(--r-overlay-margin);
right: var(--r-overlay-margin);
bottom: var(--r-overlay-margin);
left: var(--r-overlay-margin);
border-radius: min( var(--r-overlay-margin), 6px );
z-index: 99;
background: rgba( 0, 0, 0, 0.95 );
backdrop-filter: blur( 10px );
transition: all 0.3s ease;
color: #fff;
animation: fade-in 0.3s ease;
font-family: ui-sans-serif, system-ui, -apple-system, Helvetica, sans-serif;
}
.r-overlay-viewport {
position: absolute;
top: var(--r-overlay-padding);
right: var(--r-overlay-padding);
bottom: var(--r-overlay-padding);
left: var(--r-overlay-padding);
gap: var(--r-overlay-gap);
display: flex;
flex-direction: column;
}
.r-overlay-header {
display: flex;
z-index: 2;
box-sizing: border-box;
align-items: center;
justify-content: flex-end;
height: var(--r-overlay-header-height);
gap: 6px;
}
.r-overlay-header .r-overlay-button {
all: unset;
display: flex;
align-items: center;
justify-content: center;
min-width: var(--r-overlay-header-height);
min-height: var(--r-overlay-header-height);
padding: 0 calc(var(--r-overlay-header-height) / 4);
opacity: 1;
border-radius: 6px;
font-size: 18px;
gap: 8px;
cursor: pointer;
box-sizing: border-box;
}
.r-overlay-header .r-overlay-button:hover {
opacity: 1;
background-color: rgba( 255, 255, 255, 0.15 );
}
.r-overlay-header .icon {
display: inline-block;
width: 20px;
height: 20px;
background-position: 50% 50%;
background-size: 100%;
background-repeat: no-repeat;
}
.r-overlay-close .icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTIuODU0IDIuODU0YS41LjUgMCAwIDAtLjcwOC0uNzA4TDcuNSA2Ljc5MyAyLjg1NCAyLjE0NmEuNS41IDAgMSAwLS43MDguNzA4TDYuNzkzIDcuNWwtNC42NDcgNC42NDZhLjUuNSAwIDAgMCAuNzA4LjcwOEw3LjUgOC4yMDdsNC42NDYgNC42NDdhLjUuNSAwIDAgMCAuNzA4LS43MDhMOC4yMDcgNy41bDQuNjQ3LTQuNjQ2WiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+);
}
.r-overlay-external .icon {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMyAyYTEgMSAwIDAgMC0xIDF2OWExIDEgMCAwIDAgMSAxaDlhMSAxIDAgMCAwIDEtMVY4LjVhLjUuNSAwIDAgMC0xIDBWMTJIM1YzaDMuNWEuNS41IDAgMCAwIDAtMUgzWm05Ljg1NC4xNDZhLjUuNSAwIDAgMSAuMTQ2LjM1MVY1LjVhLjUuNSAwIDAgMS0xIDBWMy43MDdMNi44NTQgOC44NTRhLjUuNSAwIDEgMS0uNzA4LS43MDhMMTEuMjkzIDNIOS41YS41LjUgMCAwIDEgMC0xaDNhLjQ5OS40OTkgMCAwIDEgLjM1NC4xNDZaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=);
}
.r-overlay-content {
position: relative;
display: grid;
place-items: center;
border-radius: 6px;
overflow: hidden;
flex-grow: 1;
background-color: rgba(20, 20, 20, 0.8);
animation: scale-up 0.5s cubic-bezier(0.260, 0.860, 0.440, 0.985);
}
.r-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: hidden;
opacity: 0;
}
// Preview overlay
.r-overlay-preview .r-overlay-content iframe {
width: 100%;
height: 100%;
z-index: 1000;
background: rgba( 0, 0, 0, 0.9 );
max-width: 100%;
max-height: 100%;
border: 0;
opacity: 0;
visibility: hidden;
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);
.r-overlay-preview[data-state="loaded"] iframe {
opacity: 1;
visibility: visible;
}
visibility: visible;
opacity: 0.6;
transition: all 0.3s ease;
}
.r-overlay-preview .r-overlay-content img,
.r-overlay-preview .r-overlay-content video {
position: absolute;
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
margin: 0;
object-fit: scale-down;
}
.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;
.r-overlay-preview[data-preview-fit="none"] img,
.r-overlay-preview[data-preview-fit="none"] video {
object-fit: none;
}
box-sizing: border-box;
}
.reveal > .overlay header a:hover {
opacity: 1;
}
.reveal > .overlay header a .icon {
display: inline-block;
width: 20px;
height: 20px;
.r-overlay-preview[data-preview-fit="scale-down"] img,
.r-overlay-preview[data-preview-fit="scale-down"] video {
object-fit: scale-down;
}
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==);
}
.r-overlay-preview[data-preview-fit="contain"] img,
.r-overlay-preview[data-preview-fit="contain"] video {
object-fit: contain;
}
.reveal > .overlay .viewport {
position: absolute;
display: flex;
top: $overlayHeaderHeight + $overlayHeaderPadding*2;
right: 0;
bottom: 0;
left: 0;
}
.r-overlay-preview[data-preview-fit="cover"] img,
.r-overlay-preview[data-preview-fit="cover"] video {
object-fit: cover;
}
.reveal > .overlay.overlay-preview .viewport iframe {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
border: 0;
.r-overlay-preview[data-state="loaded"] .r-overlay-content-inner {
position: absolute;
z-index: -1;
left: 0;
top: 45%;
width: 100%;
text-align: center;
letter-spacing: normal;
}
.r-overlay-preview .r-overlay-error {
font-size: 18px;
color: orange;
}
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.r-overlay-preview .x-frame-error {
opacity: 0;
transition: opacity 0.3s ease 0.3s;
}
.r-overlay-preview[data-state="loaded"] .x-frame-error {
opacity: 1;
}
.reveal > .overlay.overlay-preview.loaded .viewport iframe {
opacity: 1;
visibility: visible;
}
.r-overlay-preview[data-state="loading"] .r-overlay-spinner {
opacity: 0.6;
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;
}
// Help overlay
.r-overlay-help .r-overlay-content {
overflow: auto;
}
.reveal > .overlay.overlay-preview.loaded .spinner {
opacity: 0;
visibility: hidden;
transform: scale(0.2);
}
.r-overlay-help-content {
max-width: 560px;
padding: 20px 0;
margin: auto;
text-align: center;
letter-spacing: normal;
}
.reveal > .overlay.overlay-help .viewport {
overflow: auto;
color: #fff;
}
.r-overlay-help-content .title {
font-size: 20px;
margin-top: 0;
}
.reveal > .overlay.overlay-help .viewport .viewport-inner {
width: 600px;
margin: auto;
padding: 20px 20px 80px 20px;
text-align: center;
letter-spacing: normal;
}
/* Specificity battle with reveal.js theme table styles */
.r-overlay-help .r-overlay-help-content table {
border: 1px solid #fff;
border-collapse: collapse;
font-size: 16px;
text-align: left;
}
.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;
}
.r-overlay-help .r-overlay-help-content table th,
.r-overlay-help .r-overlay-help-content table td {
width: 240px;
padding: 14px;
border: 1px solid #fff;
vertical-align: middle;
}
.r-overlay-help .r-overlay-help-content table th {
padding-top: 20px;
padding-bottom: 20px;
}
/*********************************************
* PLAYBACK COMPONENT
@@ -1631,6 +1717,10 @@ $overlayHeaderPadding: 5px;
opacity: 0.4;
}
.reveal .hljs.has-highlights.fragment {
transition: all .2s ease;
}
.reveal .hljs:not(:first-child).fragment {
position: absolute;
top: 0;
@@ -1794,6 +1884,43 @@ $notesWidthPercent: 25%;
}
/*********************************************
* 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
*********************************************/
@@ -1818,6 +1945,239 @@ $notesWidthPercent: 25%;
}
/*********************************************
* 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;
}
.r-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
*********************************************/
@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -23,10 +24,13 @@ $headingColor: #333;
$headingTextShadow: none;
$backgroundColor: #f7f3de;
$linkColor: #8b743d;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 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) );
@@ -0,0 +1,50 @@
/**
* 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 -----------------
@use "sass:color";
@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: color.scale( $linkColor, $lightness: 15% );
$selectionBackgroundColor: color.scale( $linkColor, $lightness: 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";
// ---------------------------------------------
@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -29,8 +30,8 @@ $headingLetterSpacing: normal;
$headingTextTransform: uppercase;
$headingFontWeight: 600;
$linkColor: #42affa;
$linkColorHover: lighten( $linkColor, 15% );
$selectionBackgroundColor: lighten( $linkColor, 25% );
$linkColorHover: color.scale( $linkColor, $lightness: 15% );
$selectionBackgroundColor: rgba( $linkColor, 0.75 );
$heading1Size: 2.5em;
$heading2Size: 1.6em;
@@ -10,7 +10,8 @@
*
*/
// Default mixins and settings -----------------
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -40,7 +41,7 @@ $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b
// Links
$linkColor: $blood;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
// Text selection
$selectionBackgroundColor: $blood;
@@ -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);
}
}
}
+2 -5
View File
@@ -5,6 +5,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -18,10 +19,6 @@
/**
* Solarized colors by Ethan Schoonover
*/
html * {
color-profile: sRGB;
rendering-intent: auto;
}
// Solarized colors
$base03: #002b36;
@@ -47,7 +44,7 @@ $headingColor: $base2;
$headingTextShadow: none;
$backgroundColor: $base03;
$linkColor: $blue;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
$selectionBackgroundColor: $magenta;
// Change text colors against light slide backgrounds
@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -21,7 +22,7 @@ $backgroundColor: #111;
$mainFont: 'Open Sans', sans-serif;
$linkColor: #e7ad52;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
$headingFont: 'Montserrat', Impact, sans-serif;
$headingTextShadow: none;
$headingLetterSpacing: -0.03em;
@@ -7,6 +7,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -22,9 +23,12 @@ $headingTextShadow: none;
$headingTextTransform: none;
$backgroundColor: #F0F1EB;
$linkColor: #51483D;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
$selectionBackgroundColor: #26351C;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
.reveal a {
line-height: 1.3em;
}
@@ -8,6 +8,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -28,9 +29,12 @@ $headingTextShadow: none;
$headingTextTransform: none;
$backgroundColor: #fff;
$linkColor: #00008B;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 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);
+5 -1
View File
@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -26,9 +27,12 @@ $headingLetterSpacing: -0.08em;
$headingTextShadow: none;
$backgroundColor: #f7fbfc;
$linkColor: #3b759e;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 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;
@@ -5,6 +5,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -48,9 +49,12 @@ $headingColor: $base01;
$headingTextShadow: none;
$backgroundColor: $base3;
$linkColor: $blue;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 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) );
@@ -0,0 +1,53 @@
/**
* 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 -----------------
@use "sass:color";
@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: color.scale( $linkColor, $lightness: 15% );
$selectionBackgroundColor: color.scale( $linkColor, $lightness: 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";
// ---------------------------------------------
@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -29,14 +30,17 @@ $headingLetterSpacing: normal;
$headingTextTransform: uppercase;
$headingFontWeight: 600;
$linkColor: #2a76dd;
$linkColorHover: lighten( $linkColor, 15% );
$selectionBackgroundColor: lighten( $linkColor, 25% );
$linkColorHover: color.scale( $linkColor, $lightness: 15% );
$selectionBackgroundColor: color.scale( $linkColor, $lightness: 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);
@@ -1,4 +1,6 @@
// Exposes theme's variables for easy re-use in CSS for plugin authors
// Exposes theme's variables for easy reuse in CSS for plugin authors
@use "sass:color";
:root {
--r-background-color: #{$backgroundColor};
@@ -21,8 +23,10 @@
--r-heading4-size: #{$heading4Size};
--r-code-font: #{$codeFont};
--r-link-color: #{$linkColor};
--r-link-color-dark: #{darken($linkColor , 15% )};
--r-link-color-dark: #{color.scale( $linkColor, $lightness: -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};
}
@@ -1,3 +1,5 @@
@use "sass:color";
// Base settings for all themes that can optionally be
// overridden by the super-theme
@@ -32,12 +34,17 @@ $codeFont: monospace;
// Links and actions
$linkColor: #13DAEC;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 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() {
@@ -278,8 +278,7 @@
.reveal .roll span:after {
color: #fff;
// background: darken( var(--r-link-color), 15% );
background: var(--r-link-color-dark);
background: var(--r-link-color-dark);
}
+43 -19
View File
@@ -86,7 +86,7 @@
<section data-auto-animate>
<h2 data-id="code-title">Pretty Code</h2>
<pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers>
<pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers>
import React, { useState } from 'react';
function Example() {
@@ -101,8 +101,8 @@
</section>
<section data-auto-animate>
<h2 data-id="code-title">With animations</h2>
<pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template">
<h2 data-id="code-title">With Animations</h2>
<pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template">
import React, { useState } from 'react';
function Example() {
@@ -165,9 +165,9 @@
</section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<div class="r-stack">
<div data-id="box1" style="background: cyan; width: 300px; height: 300px; border-radius: 200px;"></div>
<div data-id="box2" style="background: magenta; width: 200px; height: 200px; border-radius: 200px;"></div>
<div data-id="box3" style="background: yellow; width: 100px; height: 100px; border-radius: 200px;"></div>
<div data-id="box1" style="background: cyan; width: 300px; height: 300px;"></div>
<div data-id="box2" style="background: magenta; width: 200px; height: 200px;"></div>
<div data-id="box3" style="background: yellow; width: 100px; height: 100px;"></div>
</div>
<h2 style="margin-top: 20px;">Auto-Animate</h2>
</section>
@@ -181,14 +181,14 @@
<section data-markdown>
<script type="text/template">
## Markdown support
## Markdown Support
Write content using inline or external Markdown.
Instructions and more info available in the [docs](https://revealjs.com/markdown/).
```html []
<section data-markdown>
## Markdown support
## Markdown Support
Write content using inline or external Markdown.
Instructions and more info available in the [docs](https://revealjs.com/markdown/).
@@ -197,6 +197,25 @@
</script>
</section>
<section>
<h2>Lightbox</h2>
Turn any element into a <a href="https://revealjs.com/lightbox/">lightbox</a> using <strong>datapreviewimage</strong> & <strong>datapreviewvideo</strong>.
<div class="r-hstack" style="gap: 2rem;">
<div>
<pre style="font-size: 12px; width: 100%"><code class="html" data-trim>
&lt;img src="image.png" data-preview-image="image.png"&gt;
</code></pre>
<img src="https://static.slid.es/logo/v2/slides-symbol-1024x1024.png" height="100" data-preview-image>
</div>
<div>
<pre style="font-size: 12px; width: 100%"><code class="html" data-trim>
&lt;img src="video.png" data-preview-video="video.mp4"&gt;
</code></pre>
<img src="https://static.slid.es/site/homepage/v1/homepage-video-editor.png" height="100" data-preview-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4">
</div>
</div>
</section>
<section>
<p>Add the <code>r-fit-text</code> class to auto-size text</p>
<h2 class="r-fit-text">FIT TEXT</h2>
@@ -249,17 +268,17 @@
<p>
reveal.js comes with a few themes built in: <br>
<!-- Hacks to swap themes after the page has loaded. Not flexible and only intended for the reveal.js demo deck. -->
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/black.css'); return false;">Black (default)</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/white.css'); return false;">White</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/league.css'); return false;">League</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/sky.css'); return false;">Sky</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/beige.css'); return false;">Beige</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/simple.css'); return false;">Simple</a> <br>
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/serif.css'); return false;">Serif</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/blood.css'); return false;">Blood</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/night.css'); return false;">Night</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/moon.css'); return false;">Moon</a> -
<a href="#" onclick="document.getElementById('theme').setAttribute('href','dist/theme/solarized.css'); return false;">Solarized</a>
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/black.css'); return false;">Black (default)</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/white.css'); return false;">White</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/league.css'); return false;">League</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/sky.css'); return false;">Sky</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/beige.css'); return false;">Beige</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/simple.css'); return false;">Simple</a> <br>
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/serif.css'); return false;">Serif</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/blood.css'); return false;">Blood</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/night.css'); return false;">Night</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/moon.css'); return false;">Moon</a> -
<a href="#/themes" onclick="document.getElementById('theme').setAttribute('href','dist/theme/solarized.css'); return false;">Solarized</a>
</p>
</section>
@@ -273,6 +292,11 @@
<img class="r-frame" style="background: rgba(255,255,255,0.1);" width="178" height="238" data-src="https://static.slid.es/reveal/arrow.png" alt="Down arrow">
</a>
</section>
<section data-background-gradient="linear-gradient(to bottom, #283b95, #17b2c3)">
<h2>Gradient Backgrounds</h2>
<pre><code class="hljs html wrap">&lt;section data-background-gradient=
"linear-gradient(to bottom, #ddd, #191919)"&gt;</code></pre>
</section>
<section data-background="https://static.slid.es/reveal/image-placeholder.png">
<h2>Image Backgrounds</h2>
<pre><code class="hljs html">&lt;section data-background="image.png"&gt;</code></pre>
+9 -8
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7 -7
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+11 -9
View File
@@ -33,20 +33,22 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #8b743d;
--r-link-color-dark: #564826;
--r-link-color-hover: #c0a86e;
--r-link-color-dark: rgb(118.15, 98.6, 51.85);
--r-link-color-hover: rgb(179.36, 150.84, 82.64);
--r-selection-background-color: rgba(79, 64, 28, 0.99);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
background: #f7f2d3;
background: -moz-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, white), color-stop(100%, #f7f2d3));
background: -webkit-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -o-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -ms-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: rgb(247, 242, 211);
background: -moz-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, rgb(255, 255, 255)), color-stop(100%, rgb(247, 242, 211)));
background: -webkit-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -o-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -ms-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background-color: var(--r-background-color);
}
+362
View File
@@ -0,0 +1,362 @@
/**
* 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).
*/
@import url(./fonts/source-sans-pro/source-sans-pro.css);
section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
color: #000;
}
/*********************************************
* GLOBAL STYLES
*********************************************/
:root {
--r-background-color: #000000;
--r-main-font: Source Sans Pro, Helvetica, sans-serif;
--r-main-font-size: 42px;
--r-main-color: #fff;
--r-block-margin: 20px;
--r-heading-margin: 0 0 20px 0;
--r-heading-font: Source Sans Pro, Helvetica, sans-serif;
--r-heading-color: #fff;
--r-heading-line-height: 1.2;
--r-heading-letter-spacing: normal;
--r-heading-text-transform: uppercase;
--r-heading-text-shadow: none;
--r-heading-font-weight: 600;
--r-heading1-text-shadow: none;
--r-heading1-size: 2.5em;
--r-heading2-size: 1.6em;
--r-heading3-size: 1.3em;
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #42affa;
--r-link-color-dark: rgb(19.8216494845, 155.4536082474, 248.7783505155);
--r-link-color-hover: rgb(94.35, 187, 250.75);
--r-selection-background-color: rgb(113.25, 195, 251.25);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
background: #000000;
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 0.15s ease;
}
.reveal a:hover {
color: var(--r-link-color-hover);
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
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 0.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);
}
}
+5 -3
View File
@@ -32,10 +32,12 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #42affa;
--r-link-color-dark: #068de9;
--r-link-color-hover: #8dcffc;
--r-selection-background-color: #bee4fd;
--r-link-color-dark: rgb(19.8216494845, 155.4536082474, 248.7783505155);
--r-link-color-hover: rgb(94.35, 187, 250.75);
--r-selection-background-color: rgba(66, 175, 250, 0.75);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
+4 -2
View File
@@ -38,10 +38,12 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #a23;
--r-link-color-dark: #6a1520;
--r-link-color-hover: #dd5566;
--r-link-color-dark: rgb(144.5, 28.9, 43.35);
--r-link-color-hover: rgb(214.2, 51, 71.4);
--r-selection-background-color: #a23;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
+385
View File
@@ -0,0 +1,385 @@
/**
* Dracula Dark theme for reveal.js.
* Based on https://draculatheme.com
*/
/**
* Dracula colors by Zeno Rocha
* https://draculatheme.com/contribute
*/
html * {
color-profile: sRGB;
rendering-intent: auto;
}
section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
color: #282A36;
}
/*********************************************
* GLOBAL STYLES
*********************************************/
:root {
--r-background-color: #282A36;
--r-main-font: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
--r-main-font-size: 40px;
--r-main-color: #F8F8F2;
--r-block-margin: 20px;
--r-heading-margin: 0 0 20px 0;
--r-heading-font: League Gothic, Impact, sans-serif;
--r-heading-color: #BD93F9;
--r-heading-line-height: 1.2;
--r-heading-letter-spacing: normal;
--r-heading-text-transform: none;
--r-heading-text-shadow: none;
--r-heading-font-weight: normal;
--r-heading1-text-shadow: none;
--r-heading1-size: 3.77em;
--r-heading2-size: 2.11em;
--r-heading3-size: 1.55em;
--r-heading4-size: 1em;
--r-code-font: Fira Code, Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
--r-link-color: #FF79C6;
--r-link-color-dark: rgb(255, 64.6, 174.0089552239);
--r-link-color-hover: #8BE9FD;
--r-selection-background-color: #44475A;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
background: #282A36;
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 0.15s ease;
}
.reveal a:hover {
color: var(--r-link-color-hover);
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
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 0.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);
}
}
:root {
--r-bold-color: #FFB86C;
--r-italic-color: #F1FA8C;
--r-inline-code-color: #50FA7B;
--r-list-bullet-color: #8BE9FD;
}
.reveal strong, .reveal b {
color: var(--r-bold-color);
}
.reveal em, .reveal i, .reveal blockquote {
color: var(--r-italic-color);
}
.reveal code {
color: var(--r-inline-code-color);
}
.reveal ul li::marker, .reveal ol li::marker {
color: var(--r-list-bullet-color);
}
+11 -9
View File
@@ -35,20 +35,22 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #13DAEC;
--r-link-color-dark: #0d99a5;
--r-link-color-hover: #71e9f4;
--r-link-color-dark: rgb(16.15, 185.3, 200.6);
--r-link-color-hover: rgb(66.2, 225.4, 239.8);
--r-selection-background-color: #FF5E99;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
background: #1c1e20;
background: -moz-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #555a5f), color-stop(100%, #1c1e20));
background: -webkit-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -o-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -ms-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: rgb(28, 30, 32);
background: -moz-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, rgb(85, 90, 95)), color-stop(100%, rgb(28, 30, 32)));
background: -webkit-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -o-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -ms-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background-color: var(--r-background-color);
}
+4 -7
View File
@@ -7,11 +7,6 @@
/**
* Solarized colors by Ethan Schoonover
*/
html * {
color-profile: sRGB;
rendering-intent: auto;
}
section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
color: #222;
}
@@ -40,10 +35,12 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #268bd2;
--r-link-color-dark: #1a6091;
--r-link-color-hover: #78b9e6;
--r-link-color-dark: rgb(32.3, 118.15, 178.5);
--r-link-color-hover: rgb(77.5161290323, 162.8774193548, 222.8838709677);
--r-selection-background-color: #d33682;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
+4 -2
View File
@@ -33,10 +33,12 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #e7ad52;
--r-link-color-dark: #d08a1d;
--r-link-color-hover: #f3d7ac;
--r-link-color-dark: rgb(225.2802030457, 153.4573604061, 40.7697969543);
--r-link-color-hover: rgb(235.8, 189.4, 116.6);
--r-selection-background-color: #e7ad52;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
+4 -2
View File
@@ -36,10 +36,12 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #51483D;
--r-link-color-dark: #25211c;
--r-link-color-hover: #8b7c69;
--r-link-color-dark: rgb(68.85, 61.2, 51.85);
--r-link-color-hover: rgb(122.9830985915, 109.3183098592, 92.6169014085);
--r-selection-background-color: #26351C;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
+4 -2
View File
@@ -35,10 +35,12 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #00008B;
--r-link-color-dark: #00003f;
--r-link-color-hover: #0000f1;
--r-link-color-dark: rgb(0, 0, 118.15);
--r-link-color-hover: rgb(0, 0, 213.2);
--r-selection-background-color: rgba(0, 0, 0, 0.99);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
+4 -2
View File
@@ -37,10 +37,12 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #3b759e;
--r-link-color-dark: #264c66;
--r-link-color-hover: #74a7cb;
--r-link-color-dark: rgb(50.15, 99.45, 134.3);
--r-link-color-hover: rgb(84.330875576, 146.9815668203, 191.269124424);
--r-selection-background-color: #134674;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
+4 -2
View File
@@ -36,10 +36,12 @@ html * {
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #268bd2;
--r-link-color-dark: #1a6091;
--r-link-color-hover: #78b9e6;
--r-link-color-dark: rgb(32.3, 118.15, 178.5);
--r-link-color-hover: rgb(77.5161290323, 162.8774193548, 222.8838709677);
--r-selection-background-color: #d33682;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
+362
View File
@@ -0,0 +1,362 @@
/**
* 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).
*/
@import url(./fonts/source-sans-pro/source-sans-pro.css);
section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 {
color: #fff;
}
/*********************************************
* GLOBAL STYLES
*********************************************/
:root {
--r-background-color: #fff;
--r-main-font: Source Sans Pro, Helvetica, sans-serif;
--r-main-font-size: 42px;
--r-main-color: #000;
--r-block-margin: 20px;
--r-heading-margin: 0 0 20px 0;
--r-heading-font: Source Sans Pro, Helvetica, sans-serif;
--r-heading-color: #000;
--r-heading-line-height: 1.2;
--r-heading-letter-spacing: normal;
--r-heading-text-transform: uppercase;
--r-heading-text-shadow: none;
--r-heading-font-weight: 600;
--r-heading1-text-shadow: none;
--r-heading1-size: 2.5em;
--r-heading2-size: 1.6em;
--r-heading3-size: 1.3em;
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #2a76dd;
--r-link-color-dark: rgb(30.7720647773, 99.5566801619, 192.7779352227);
--r-link-color-hover: rgb(73.95, 138.55, 226.1);
--r-selection-background-color: rgb(95.25, 152.25, 229.5);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
background: #fff;
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 0.15s ease;
}
.reveal a:hover {
color: var(--r-link-color-hover);
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
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 0.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);
}
}
+5 -3
View File
@@ -32,10 +32,12 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #2a76dd;
--r-link-color-dark: #1a53a1;
--r-link-color-hover: #6ca0e8;
--r-selection-background-color: #98bdef;
--r-link-color-dark: rgb(30.7720647773, 99.5566801619, 192.7779352227);
--r-link-color-hover: rgb(73.95, 138.55, 226.1);
--r-selection-background-color: rgb(95.25, 152.25, 229.5);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
@@ -0,0 +1,360 @@
/**
* 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).
*/
@import url(./fonts/source-sans-pro/source-sans-pro.css);
section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 {
color: #fff;
}
/*********************************************
* GLOBAL STYLES
*********************************************/
:root {
--r-background-color: #fff;
--r-main-font: Source Sans Pro, Helvetica, sans-serif;
--r-main-font-size: 25px;
--r-main-color: #000;
--r-block-margin: 20px;
--r-heading-margin: 0 0 20px 0;
--r-heading-font: Source Sans Pro, Helvetica, sans-serif;
--r-heading-color: #000;
--r-heading-line-height: 1.2;
--r-heading-letter-spacing: normal;
--r-heading-text-transform: none;
--r-heading-text-shadow: none;
--r-heading-font-weight: 450;
--r-heading1-text-shadow: none;
--r-heading1-size: 2.5em;
--r-heading2-size: 1.6em;
--r-heading3-size: 1.3em;
--r-heading4-size: 1em;
--r-code-font: monospace;
--r-link-color: #2a76dd;
--r-link-color-dark: #1a53a1;
--r-link-color-hover: #6ca0e8;
--r-selection-background-color: #98bdef;
--r-selection-color: #fff;
}
.reveal-viewport {
background: #fff;
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 0.15s ease;
}
.reveal a:hover {
color: var(--r-link-color-hover);
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
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 0.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);
}
}
+526
View File
@@ -0,0 +1,526 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - 500 slides</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="../dist/reveal.css">
<link rel="stylesheet" href="../dist/theme/black.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section><h1>1</h1></section>
<section><h1>2</h1></section>
<section><h1>3</h1></section>
<section><h1>4</h1></section>
<section><h1>5</h1></section>
<section><h1>6</h1></section>
<section><h1>7</h1></section>
<section><h1>8</h1></section>
<section><h1>9</h1></section>
<section><h1>10</h1></section>
<section><h1>11</h1></section>
<section><h1>12</h1></section>
<section><h1>13</h1></section>
<section><h1>14</h1></section>
<section><h1>15</h1></section>
<section><h1>16</h1></section>
<section><h1>17</h1></section>
<section><h1>18</h1></section>
<section><h1>19</h1></section>
<section><h1>20</h1></section>
<section><h1>21</h1></section>
<section><h1>22</h1></section>
<section><h1>23</h1></section>
<section><h1>24</h1></section>
<section><h1>25</h1></section>
<section><h1>26</h1></section>
<section><h1>27</h1></section>
<section><h1>28</h1></section>
<section><h1>29</h1></section>
<section><h1>30</h1></section>
<section><h1>31</h1></section>
<section><h1>32</h1></section>
<section><h1>33</h1></section>
<section><h1>34</h1></section>
<section><h1>35</h1></section>
<section><h1>36</h1></section>
<section><h1>37</h1></section>
<section><h1>38</h1></section>
<section><h1>39</h1></section>
<section><h1>40</h1></section>
<section><h1>41</h1></section>
<section><h1>42</h1></section>
<section><h1>43</h1></section>
<section><h1>44</h1></section>
<section><h1>45</h1></section>
<section><h1>46</h1></section>
<section><h1>47</h1></section>
<section><h1>48</h1></section>
<section><h1>49</h1></section>
<section><h1>50</h1></section>
<section><h1>51</h1></section>
<section><h1>52</h1></section>
<section><h1>53</h1></section>
<section><h1>54</h1></section>
<section><h1>55</h1></section>
<section><h1>56</h1></section>
<section><h1>57</h1></section>
<section><h1>58</h1></section>
<section><h1>59</h1></section>
<section><h1>60</h1></section>
<section><h1>61</h1></section>
<section><h1>62</h1></section>
<section><h1>63</h1></section>
<section><h1>64</h1></section>
<section><h1>65</h1></section>
<section><h1>66</h1></section>
<section><h1>67</h1></section>
<section><h1>68</h1></section>
<section><h1>69</h1></section>
<section><h1>70</h1></section>
<section><h1>71</h1></section>
<section><h1>72</h1></section>
<section><h1>73</h1></section>
<section><h1>74</h1></section>
<section><h1>75</h1></section>
<section><h1>76</h1></section>
<section><h1>77</h1></section>
<section><h1>78</h1></section>
<section><h1>79</h1></section>
<section><h1>80</h1></section>
<section><h1>81</h1></section>
<section><h1>82</h1></section>
<section><h1>83</h1></section>
<section><h1>84</h1></section>
<section><h1>85</h1></section>
<section><h1>86</h1></section>
<section><h1>87</h1></section>
<section><h1>88</h1></section>
<section><h1>89</h1></section>
<section><h1>90</h1></section>
<section><h1>91</h1></section>
<section><h1>92</h1></section>
<section><h1>93</h1></section>
<section><h1>94</h1></section>
<section><h1>95</h1></section>
<section><h1>96</h1></section>
<section><h1>97</h1></section>
<section><h1>98</h1></section>
<section><h1>99</h1></section>
<section><h1>100</h1></section>
<section><h1>101</h1></section>
<section><h1>102</h1></section>
<section><h1>103</h1></section>
<section><h1>104</h1></section>
<section><h1>105</h1></section>
<section><h1>106</h1></section>
<section><h1>107</h1></section>
<section><h1>108</h1></section>
<section><h1>109</h1></section>
<section><h1>110</h1></section>
<section><h1>111</h1></section>
<section><h1>112</h1></section>
<section><h1>113</h1></section>
<section><h1>114</h1></section>
<section><h1>115</h1></section>
<section><h1>116</h1></section>
<section><h1>117</h1></section>
<section><h1>118</h1></section>
<section><h1>119</h1></section>
<section><h1>120</h1></section>
<section><h1>121</h1></section>
<section><h1>122</h1></section>
<section><h1>123</h1></section>
<section><h1>124</h1></section>
<section><h1>125</h1></section>
<section><h1>126</h1></section>
<section><h1>127</h1></section>
<section><h1>128</h1></section>
<section><h1>129</h1></section>
<section><h1>130</h1></section>
<section><h1>131</h1></section>
<section><h1>132</h1></section>
<section><h1>133</h1></section>
<section><h1>134</h1></section>
<section><h1>135</h1></section>
<section><h1>136</h1></section>
<section><h1>137</h1></section>
<section><h1>138</h1></section>
<section><h1>139</h1></section>
<section><h1>140</h1></section>
<section><h1>141</h1></section>
<section><h1>142</h1></section>
<section><h1>143</h1></section>
<section><h1>144</h1></section>
<section><h1>145</h1></section>
<section><h1>146</h1></section>
<section><h1>147</h1></section>
<section><h1>148</h1></section>
<section><h1>149</h1></section>
<section><h1>150</h1></section>
<section><h1>151</h1></section>
<section><h1>152</h1></section>
<section><h1>153</h1></section>
<section><h1>154</h1></section>
<section><h1>155</h1></section>
<section><h1>156</h1></section>
<section><h1>157</h1></section>
<section><h1>158</h1></section>
<section><h1>159</h1></section>
<section><h1>160</h1></section>
<section><h1>161</h1></section>
<section><h1>162</h1></section>
<section><h1>163</h1></section>
<section><h1>164</h1></section>
<section><h1>165</h1></section>
<section><h1>166</h1></section>
<section><h1>167</h1></section>
<section><h1>168</h1></section>
<section><h1>169</h1></section>
<section><h1>170</h1></section>
<section><h1>171</h1></section>
<section><h1>172</h1></section>
<section><h1>173</h1></section>
<section><h1>174</h1></section>
<section><h1>175</h1></section>
<section><h1>176</h1></section>
<section><h1>177</h1></section>
<section><h1>178</h1></section>
<section><h1>179</h1></section>
<section><h1>180</h1></section>
<section><h1>181</h1></section>
<section><h1>182</h1></section>
<section><h1>183</h1></section>
<section><h1>184</h1></section>
<section><h1>185</h1></section>
<section><h1>186</h1></section>
<section><h1>187</h1></section>
<section><h1>188</h1></section>
<section><h1>189</h1></section>
<section><h1>190</h1></section>
<section><h1>191</h1></section>
<section><h1>192</h1></section>
<section><h1>193</h1></section>
<section><h1>194</h1></section>
<section><h1>195</h1></section>
<section><h1>196</h1></section>
<section><h1>197</h1></section>
<section><h1>198</h1></section>
<section><h1>199</h1></section>
<section><h1>200</h1></section>
<section><h1>201</h1></section>
<section><h1>202</h1></section>
<section><h1>203</h1></section>
<section><h1>204</h1></section>
<section><h1>205</h1></section>
<section><h1>206</h1></section>
<section><h1>207</h1></section>
<section><h1>208</h1></section>
<section><h1>209</h1></section>
<section><h1>210</h1></section>
<section><h1>211</h1></section>
<section><h1>212</h1></section>
<section><h1>213</h1></section>
<section><h1>214</h1></section>
<section><h1>215</h1></section>
<section><h1>216</h1></section>
<section><h1>217</h1></section>
<section><h1>218</h1></section>
<section><h1>219</h1></section>
<section><h1>220</h1></section>
<section><h1>221</h1></section>
<section><h1>222</h1></section>
<section><h1>223</h1></section>
<section><h1>224</h1></section>
<section><h1>225</h1></section>
<section><h1>226</h1></section>
<section><h1>227</h1></section>
<section><h1>228</h1></section>
<section><h1>229</h1></section>
<section><h1>230</h1></section>
<section><h1>231</h1></section>
<section><h1>232</h1></section>
<section><h1>233</h1></section>
<section><h1>234</h1></section>
<section><h1>235</h1></section>
<section><h1>236</h1></section>
<section><h1>237</h1></section>
<section><h1>238</h1></section>
<section><h1>239</h1></section>
<section><h1>240</h1></section>
<section><h1>241</h1></section>
<section><h1>242</h1></section>
<section><h1>243</h1></section>
<section><h1>244</h1></section>
<section><h1>245</h1></section>
<section><h1>246</h1></section>
<section><h1>247</h1></section>
<section><h1>248</h1></section>
<section><h1>249</h1></section>
<section><h1>250</h1></section>
<section><h1>251</h1></section>
<section><h1>252</h1></section>
<section><h1>253</h1></section>
<section><h1>254</h1></section>
<section><h1>255</h1></section>
<section><h1>256</h1></section>
<section><h1>257</h1></section>
<section><h1>258</h1></section>
<section><h1>259</h1></section>
<section><h1>260</h1></section>
<section><h1>261</h1></section>
<section><h1>262</h1></section>
<section><h1>263</h1></section>
<section><h1>264</h1></section>
<section><h1>265</h1></section>
<section><h1>266</h1></section>
<section><h1>267</h1></section>
<section><h1>268</h1></section>
<section><h1>269</h1></section>
<section><h1>270</h1></section>
<section><h1>271</h1></section>
<section><h1>272</h1></section>
<section><h1>273</h1></section>
<section><h1>274</h1></section>
<section><h1>275</h1></section>
<section><h1>276</h1></section>
<section><h1>277</h1></section>
<section><h1>278</h1></section>
<section><h1>279</h1></section>
<section><h1>280</h1></section>
<section><h1>281</h1></section>
<section><h1>282</h1></section>
<section><h1>283</h1></section>
<section><h1>284</h1></section>
<section><h1>285</h1></section>
<section><h1>286</h1></section>
<section><h1>287</h1></section>
<section><h1>288</h1></section>
<section><h1>289</h1></section>
<section><h1>290</h1></section>
<section><h1>291</h1></section>
<section><h1>292</h1></section>
<section><h1>293</h1></section>
<section><h1>294</h1></section>
<section><h1>295</h1></section>
<section><h1>296</h1></section>
<section><h1>297</h1></section>
<section><h1>298</h1></section>
<section><h1>299</h1></section>
<section><h1>300</h1></section>
<section><h1>301</h1></section>
<section><h1>302</h1></section>
<section><h1>303</h1></section>
<section><h1>304</h1></section>
<section><h1>305</h1></section>
<section><h1>306</h1></section>
<section><h1>307</h1></section>
<section><h1>308</h1></section>
<section><h1>309</h1></section>
<section><h1>310</h1></section>
<section><h1>311</h1></section>
<section><h1>312</h1></section>
<section><h1>313</h1></section>
<section><h1>314</h1></section>
<section><h1>315</h1></section>
<section><h1>316</h1></section>
<section><h1>317</h1></section>
<section><h1>318</h1></section>
<section><h1>319</h1></section>
<section><h1>320</h1></section>
<section><h1>321</h1></section>
<section><h1>322</h1></section>
<section><h1>323</h1></section>
<section><h1>324</h1></section>
<section><h1>325</h1></section>
<section><h1>326</h1></section>
<section><h1>327</h1></section>
<section><h1>328</h1></section>
<section><h1>329</h1></section>
<section><h1>330</h1></section>
<section><h1>331</h1></section>
<section><h1>332</h1></section>
<section><h1>333</h1></section>
<section><h1>334</h1></section>
<section><h1>335</h1></section>
<section><h1>336</h1></section>
<section><h1>337</h1></section>
<section><h1>338</h1></section>
<section><h1>339</h1></section>
<section><h1>340</h1></section>
<section><h1>341</h1></section>
<section><h1>342</h1></section>
<section><h1>343</h1></section>
<section><h1>344</h1></section>
<section><h1>345</h1></section>
<section><h1>346</h1></section>
<section><h1>347</h1></section>
<section><h1>348</h1></section>
<section><h1>349</h1></section>
<section><h1>350</h1></section>
<section><h1>351</h1></section>
<section><h1>352</h1></section>
<section><h1>353</h1></section>
<section><h1>354</h1></section>
<section><h1>355</h1></section>
<section><h1>356</h1></section>
<section><h1>357</h1></section>
<section><h1>358</h1></section>
<section><h1>359</h1></section>
<section><h1>360</h1></section>
<section><h1>361</h1></section>
<section><h1>362</h1></section>
<section><h1>363</h1></section>
<section><h1>364</h1></section>
<section><h1>365</h1></section>
<section><h1>366</h1></section>
<section><h1>367</h1></section>
<section><h1>368</h1></section>
<section><h1>369</h1></section>
<section><h1>370</h1></section>
<section><h1>371</h1></section>
<section><h1>372</h1></section>
<section><h1>373</h1></section>
<section><h1>374</h1></section>
<section><h1>375</h1></section>
<section><h1>376</h1></section>
<section><h1>377</h1></section>
<section><h1>378</h1></section>
<section><h1>379</h1></section>
<section><h1>380</h1></section>
<section><h1>381</h1></section>
<section><h1>382</h1></section>
<section><h1>383</h1></section>
<section><h1>384</h1></section>
<section><h1>385</h1></section>
<section><h1>386</h1></section>
<section><h1>387</h1></section>
<section><h1>388</h1></section>
<section><h1>389</h1></section>
<section><h1>390</h1></section>
<section><h1>391</h1></section>
<section><h1>392</h1></section>
<section><h1>393</h1></section>
<section><h1>394</h1></section>
<section><h1>395</h1></section>
<section><h1>396</h1></section>
<section><h1>397</h1></section>
<section><h1>398</h1></section>
<section><h1>399</h1></section>
<section><h1>400</h1></section>
<section><h1>401</h1></section>
<section><h1>402</h1></section>
<section><h1>403</h1></section>
<section><h1>404</h1></section>
<section><h1>405</h1></section>
<section><h1>406</h1></section>
<section><h1>407</h1></section>
<section><h1>408</h1></section>
<section><h1>409</h1></section>
<section><h1>410</h1></section>
<section><h1>411</h1></section>
<section><h1>412</h1></section>
<section><h1>413</h1></section>
<section><h1>414</h1></section>
<section><h1>415</h1></section>
<section><h1>416</h1></section>
<section><h1>417</h1></section>
<section><h1>418</h1></section>
<section><h1>419</h1></section>
<section><h1>420</h1></section>
<section><h1>421</h1></section>
<section><h1>422</h1></section>
<section><h1>423</h1></section>
<section><h1>424</h1></section>
<section><h1>425</h1></section>
<section><h1>426</h1></section>
<section><h1>427</h1></section>
<section><h1>428</h1></section>
<section><h1>429</h1></section>
<section><h1>430</h1></section>
<section><h1>431</h1></section>
<section><h1>432</h1></section>
<section><h1>433</h1></section>
<section><h1>434</h1></section>
<section><h1>435</h1></section>
<section><h1>436</h1></section>
<section><h1>437</h1></section>
<section><h1>438</h1></section>
<section><h1>439</h1></section>
<section><h1>440</h1></section>
<section><h1>441</h1></section>
<section><h1>442</h1></section>
<section><h1>443</h1></section>
<section><h1>444</h1></section>
<section><h1>445</h1></section>
<section><h1>446</h1></section>
<section><h1>447</h1></section>
<section><h1>448</h1></section>
<section><h1>449</h1></section>
<section><h1>450</h1></section>
<section><h1>451</h1></section>
<section><h1>452</h1></section>
<section><h1>453</h1></section>
<section><h1>454</h1></section>
<section><h1>455</h1></section>
<section><h1>456</h1></section>
<section><h1>457</h1></section>
<section><h1>458</h1></section>
<section><h1>459</h1></section>
<section><h1>460</h1></section>
<section><h1>461</h1></section>
<section><h1>462</h1></section>
<section><h1>463</h1></section>
<section><h1>464</h1></section>
<section><h1>465</h1></section>
<section><h1>466</h1></section>
<section><h1>467</h1></section>
<section><h1>468</h1></section>
<section><h1>469</h1></section>
<section><h1>470</h1></section>
<section><h1>471</h1></section>
<section><h1>472</h1></section>
<section><h1>473</h1></section>
<section><h1>474</h1></section>
<section><h1>475</h1></section>
<section><h1>476</h1></section>
<section><h1>477</h1></section>
<section><h1>478</h1></section>
<section><h1>479</h1></section>
<section><h1>480</h1></section>
<section><h1>481</h1></section>
<section><h1>482</h1></section>
<section><h1>483</h1></section>
<section><h1>484</h1></section>
<section><h1>485</h1></section>
<section><h1>486</h1></section>
<section><h1>487</h1></section>
<section><h1>488</h1></section>
<section><h1>489</h1></section>
<section><h1>490</h1></section>
<section><h1>491</h1></section>
<section><h1>492</h1></section>
<section><h1>493</h1></section>
<section><h1>494</h1></section>
<section><h1>495</h1></section>
<section><h1>496</h1></section>
<section><h1>497</h1></section>
<section><h1>498</h1></section>
<section><h1>499</h1></section>
</div>
</div>
<script src="../dist/reveal.js"></script>
<script>
Reveal.initialize({
transition: 'linear'
});
</script>
</body>
</html>
@@ -176,6 +176,37 @@
<h3>B2</h3>
</section>
<section>
<section id="stacked-slide-1" data-auto-animate>
<a href="#/stacked-slide-1">Slide 1</a><br>
<a href="#/stacked-slide-2">Slide 2</a><br>
<a href="#/stacked-slide-3">Slide 3</a><br>
<a href="#/stacked-slide-4">Slide 4</a><br>
<div data-id="anim" style="background: indigo; padding: 8px; width: 50px; height: 50px; position: absolute; left: 0px;">A</div>
</section>
<section id="stacked-slide-2" data-auto-animate>
<a href="#/stacked-slide-1">Slide 1</a><br>
<a href="#/stacked-slide-2">Slide 2</a><br>
<a href="#/stacked-slide-3">Slide 3</a><br>
<a href="#/stacked-slide-4">Slide 4</a><br>
<div data-id="anim" style="background: indigo; padding: 8px; width: 50px; height: 50px; position: absolute; left: 25%;">A</div>
</section>
<section id="stacked-slide-3" data-auto-animate>
<a href="#/stacked-slide-1">Slide 1</a><br>
<a href="#/stacked-slide-2">Slide 2</a><br>
<a href="#/stacked-slide-3">Slide 3</a><br>
<a href="#/stacked-slide-4">Slide 4</a><br>
<div data-id="anim" style="background: indigo; padding: 8px; width: 50px; height: 50px; position: absolute; left: 50%;">A</div>
</section>
<section id="stacked-slide-4" data-auto-animate>
<a href="#/stacked-slide-1">Slide 1</a><br>
<a href="#/stacked-slide-2">Slide 2</a><br>
<a href="#/stacked-slide-3">Slide 3</a><br>
<a href="#/stacked-slide-4">Slide 4</a><br>
<div data-id="anim" style="background: indigo; padding: 8px; width: 50px; height: 50px; position: absolute; left: 75%;">A</div>
</section>
</section>
</div>
</div>
+1 -1
View File
@@ -89,7 +89,7 @@
<h2>Same background twice (2/2)</h2>
</section>
<section data-background-video="https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.webm">
<section data-background-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://static.slid.es/site/homepage/v1/homepage-video-editor.webm">
<h2>Video background</h2>
</section>
+144
View File
@@ -0,0 +1,144 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Ligthbox</title>
<link rel="stylesheet" href="../dist/reveal.css">
<link rel="stylesheet" href="../dist/theme/black.css" id="theme">
<style>
.reveal {
font-size: 24px;
}
.reveal figure {
margin: 0 0 1rem 0;
text-align: left;
}
.reveal figure img,
.reveal figure video {
margin: 0.25rem 0 0 0;
}
figcaption, a {
font-size: 16px;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h2>Preview Overlays</h2>
<div class="r-hstack items-start">
<div class="r-vstack items-start">
<h5>Images</h5>
<figure>
<figcaption>Preview with default settings:</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/a.png" data-preview-image>
</figure>
<figure>
<figcaption>Preview with data-preview-fit="contain"</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/a.png" data-preview-image data-preview-fit="contain">
</figure>
<figure>
<figcaption>Preview with data-preview-fit="cover"</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/a.png" data-preview-image data-preview-fit="cover">
</figure>
<figure>
<figcaption>Preview another image (c)</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/b.png" data-preview-image="https://static.slid.es/images/alphabet/v1/c.png">
</figure>
<a href="#" data-preview-image="https://static.slid.es/images/alphabet/v1/x.png">
Preview image from a link.
</a>
</div>
<div style="width: 1px; height: 30vh; margin: 0 3rem;background-color: #999;"></div>
<div class="r-vstack items-start">
<h5>Videos</h5>
<figure>
<figcaption>Preview video</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/x.png" data-preview-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4">
</figure>
<figure>
<figcaption>Preview video</figcaption>
<video height="50" src="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4" data-preview-video></video>
</figure>
<a href="#" data-preview-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4">
Preview video from a link.
</a>
</div>
<div style="width: 1px; height: 30vh; margin: 0 3rem;background-color: #999;"></div>
<div class="r-vstack items-start">
<h5>Iframes</h5>
<a href="https://hakim.se">https://hakim.se | data-preview-link</a>
<a data-preview-link href="https://hakim.se">https://hakim.se | data-preview-link</a>
<br />
<a data-preview-link="false" href="https://hakim.se">https://hakim.se | data-preview-link=false</a>
<br />
<figure>
<figcaption>Preview link from an image</figcaption>
<img height="50" src="https://static.slid.es/images/alphabet/v1/a.png" data-preview-link="https://hakim.se">
</figure>
</div>
</div>
</section>
<section style="text-align: left;">
<h2>Lightbox</h2>
<div class="r-hstack items-start justify-start">
<div class="r-vstack items-start">
<h5>Images</h5>
<figure>
<img height="100" src="https://static.slid.es/images/alphabet/v1/a.png" data-preview-image data-preview-fit="contain">
</figure>
</div>
<div style="width: 1px; height: 20vh; margin: 0 3rem;background-color: #222;"></div>
<div class="r-vstack items-start">
<h5>Videos</h5>
<figure>
<video height="100" src="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4" data-preview-video></video>
</figure>
</div>
<div style="width: 1px; height: 20vh; margin: 0 3rem;background-color: #222;"></div>
<div class="r-vstack items-start">
<h5>Iframes</h5>
<a style="font-size: 28px;" data-preview-link href="https://hakim.se">https://hakim.se</a>
</div>
</div>
</section>
</div>
</div>
<script src="../dist/reveal.js"></script>
<script>
Reveal.initialize({
previewLinks: false,
width: 1280,
height: 720
});
</script>
</body>
</html>
+20 -1
View File
@@ -99,10 +99,29 @@
</script>
</section>
<!-- add optional line count offset, in this case 287 -->
<section data-markdown>
<script type="text/template">
## echo.c
```c [287: 2|4,6]
/* All of the options in this arg are valid, so handle them. */
p = arg + 1;
do {
if (*p == 'n')
nflag = 0;
if (*p == 'e')
eflag = '\\';
} while (*++p);
```
[source](https://git.busybox.net/busybox/tree/coreutils/echo.c?h=1_36_stable#n287)
</script>
</section>
<!-- Images -->
<section data-markdown>
<script type="text/template">
![Sample image](https://s3.amazonaws.com/static.slid.es/logo/v2/slides-symbol-512x512.png)
![Sample image](https://static.slid.es/logo/v2/slides-symbol-512x512.png)
</script>
</section>
+1 -1
View File
@@ -33,7 +33,7 @@ Content 3.2
## External 3.3 (Image)
![External Image](https://s3.amazonaws.com/static.slid.es/logo/v2/slides-symbol-512x512.png)
![External Image](https://static.slid.es/logo/v2/slides-symbol-512x512.png)
## External 3.4 (Math)
+120
View File
@@ -0,0 +1,120 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Scroll View</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="../dist/reset.css">
<link rel="stylesheet" href="../dist/reveal.css">
<link rel="stylesheet" href="../dist/theme/black.css" id="theme">
<link rel="stylesheet" href="../plugin/highlight/monokai.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section><h1>Scroll View</h1></section>
<section data-background="indigo">
<h2>Scroll triggered fragments</h2>
<ul>
<li class="fragment fade-left">Step one</li>
<li class="fragment fade-left">Step two</li>
<li class="fragment fade-left">Step three</li>
</ul>
</section>
<section data-background-color="#fff"><h2>Scrollbar inverts<br>based on slide bg</h2></section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<h2>Auto-Animate</h2>
<p>Scroll triggered auto-animations 😍</p>
<div class="r-hstack justify-center">
<div data-id="box1" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
<div data-id="box2" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
<div data-id="box3" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
</div>
</section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<div class="r-hstack justify-center">
<div data-id="box1" data-auto-animate-delay="0" style="background: cyan; width: 150px; height: 100px; margin: 10px;"></div>
<div data-id="box2" data-auto-animate-delay="0.1" style="background: magenta; width: 150px; height: 100px; margin: 10px;"></div>
<div data-id="box3" data-auto-animate-delay="0.2" style="background: yellow; width: 150px; height: 100px; margin: 10px;"></div>
</div>
<h2 style="margin-top: 20px;">Auto-Animate</h2>
</section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<div class="r-stack">
<div data-id="box1" style="background: cyan; width: 300px; height: 300px; border-radius: 200px;"></div>
<div data-id="box2" style="background: magenta; width: 200px; height: 200px; border-radius: 200px;"></div>
<div data-id="box3" style="background: yellow; width: 100px; height: 100px; border-radius: 200px;"></div>
</div>
<h2 style="margin-top: 20px;">Auto-Animate</h2>
</section>
<section data-background-gradient="linear-gradient(to bottom, #283b95, #17b2c3)" id="gradient-bg">
<h2 data-id="code-title">Code highlights,<br />meet scroll triggers</h2>
<pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template">
import React, { useState } from 'react';
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 SecondExample() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
</script></code></pre>
</section>
<section class="stack">
<section data-background="https://static.slid.es/reveal/image-placeholder.png" id="image-bg">
<h2>Image Backgrounds</h2>
</section>
<section data-background-video-muted data-background-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://static.slid.es/site/homepage/v1/homepage-video-editor.webm">
<div style="background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px;">
<h2>Video background</h2>
</div>
</section>
</section>
<section><h2>The end</h2></section>
</div>
</div>
<script src="../dist/reveal.js"></script>
<script src="../plugin/notes/notes.js"></script>
<script src="../plugin/markdown/markdown.js"></script>
<script src="../plugin/highlight/highlight.js"></script>
<script>
Reveal.initialize({
view: 'scroll',
hash: true,
plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
});
</script>
</body>
</html>
+48 -36
View File
@@ -1,22 +1,20 @@
const fs = require('fs');
const pkg = require('./package.json')
const path = require('path')
const glob = require('glob')
const yargs = require('yargs')
const colors = require('colors')
const through = require('through2');
const qunit = require('node-qunit-puppeteer')
const {rollup} = require('rollup')
const {terser} = require('rollup-plugin-terser')
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 header = require('gulp-header-comment')
const eslint = require('gulp-eslint')
const minify = require('gulp-clean-css')
const connect = require('gulp-connect')
@@ -26,13 +24,21 @@ 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-2022 Hakim El Hattab, https://hakim.se
*/\n`
const cssLicense = `
reveal.js ${pkg.version}
${pkg.homepage}
MIT licensed
Copyright (C) 2011-2024 Hakim El Hattab, https://hakim.se
`;
const jsLicense = `/*!
* 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);
@@ -88,7 +94,7 @@ gulp.task('js-es5', () => {
name: 'Reveal',
file: './dist/reveal.js',
format: 'umd',
banner: banner,
banner: jsLicense,
sourcemap: true
});
});
@@ -110,7 +116,7 @@ gulp.task('js-es6', () => {
return bundle.write({
file: './dist/reveal.esm.js',
format: 'es',
banner: banner,
banner: jsLicense,
sourcemap: true
});
});
@@ -163,12 +169,12 @@ function compileSass() {
const transformedFile = vinylFile.clone();
sass.render({
silenceDeprecations: ['legacy-js-api'],
data: transformedFile.contents.toString(),
includePaths: ['css/', 'css/theme/template']
file: transformedFile.path,
}, ( err, result ) => {
if( err ) {
console.log( vinylFile.path );
console.log( err.formatted );
callback(err);
}
else {
transformedFile.extname = '.css';
@@ -187,7 +193,7 @@ gulp.task('css-core', () => gulp.src(['css/reveal.scss'])
.pipe(compileSass())
.pipe(autoprefixer())
.pipe(minify({compatibility: 'ie9'}))
.pipe(header(banner))
.pipe(header(cssLicense))
.pipe(gulp.dest('./dist')))
gulp.task('css', gulp.parallel('css-themes', 'css-core'))
@@ -214,7 +220,7 @@ gulp.task('qunit', () => {
targetUrl: `http://${serverConfig.host}:${serverConfig.port}/${filename}`,
timeout: 20000,
redirectConsole: false,
puppeteerArgs: ['--allow-file-access-from-files']
puppeteerArgs: ['--allow-file-access-from-files', '--no-sandbox']
})
.then(result => {
if( result.stats.failed > 0 ) {
@@ -269,24 +275,25 @@ 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.task('package', gulp.series(async () => {
gulp.src(
[
'./index.html',
'./dist/**',
'./lib/**',
'./images/**',
'./plugin/**',
'./**.md'
],
{ base: './' }
)
let dirs = [
'./index.html',
'./dist/**',
'./plugin/**',
'./*/*.md'
];
if (fs.existsSync('./lib')) dirs.push('./lib/**');
if (fs.existsSync('./images')) dirs.push('./images/**');
if (fs.existsSync('./slides')) dirs.push('./slides/**');
return gulp.src( dirs, { base: './', encoding: false } )
.pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./'))
))
}))
gulp.task('reload', () => gulp.src(['*.html', '*.md'])
gulp.task('reload', () => gulp.src(['index.html'])
.pipe(connect.reload()));
gulp.task('serve', () => {
@@ -298,14 +305,19 @@ gulp.task('serve', () => {
livereload: true
})
gulp.watch(['*.html', '*.md'], gulp.series('reload'))
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/source/**/*.{sass,scss}',
'css/theme/template/*.{sass,scss}',
], gulp.series('css-themes', 'reload'))
@@ -316,4 +328,4 @@ gulp.task('serve', () => {
gulp.watch(['test/*.html'], gulp.series('test'))
})
})
+1 -1
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+42 -2
View File
@@ -15,7 +15,10 @@ export default {
minScale: 0.2,
maxScale: 2.0,
// Display presentation control arrows
// Display presentation control arrows.
// - true: Display controls on all screens
// - false: Hide controls on all screens
// - "speaker-only": Only display controls in the speaker view
controls: true,
// Help the user learn the controls by providing hints, for example by
@@ -65,13 +68,16 @@ export default {
// 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
// Optional function that blocks keyboard events when returning false
//
// If you set this to 'focused', we will only capture keyboard events
// for embedded decks when they are in focus
@@ -253,6 +259,36 @@ export default {
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,
@@ -284,6 +320,10 @@ export default {
// 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: [],
+32 -31
View File
@@ -31,10 +31,13 @@ export default class AutoAnimate {
let toSlideIndex = allSlides.indexOf( toSlide );
let fromSlideIndex = allSlides.indexOf( fromSlide );
// Ensure that both slides are auto-animate targets with the same data-auto-animate-id value
// (including null if absent on both) and that data-auto-animate-restart isn't set on the
// physically latter slide (independent of slide direction)
if( fromSlide.hasAttribute( 'data-auto-animate' ) && toSlide.hasAttribute( 'data-auto-animate' )
// 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' ) ) {
@@ -50,11 +53,19 @@ export default class AutoAnimate {
// 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 ) {
@@ -167,28 +178,12 @@ export default class AutoAnimate {
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
@@ -391,7 +386,14 @@ export default class AutoAnimate {
value = { value: style.to, explicitValue: true };
}
else {
value = computedStyles[style.property];
// 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 !== '' ) {
@@ -446,14 +448,14 @@ export default class AutoAnimate {
const textNodes = 'h1, h2, h3, h4, h5, h6, p, li';
const mediaNodes = 'img, video, iframe';
// Eplicit matches via data-id
// 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;
return node.nodeName + ':::' + node.textContent.trim();
} );
// Media
@@ -463,11 +465,10 @@ export default class AutoAnimate {
// Code
this.findAutoAnimateMatches( pairs, fromSlide, toSlide, codeNodes, node => {
return node.nodeName + ':::' + node.innerText;
return node.nodeName + ':::' + node.textContent.trim();
} );
pairs.forEach( pair => {
// Disable scale transformations on text nodes, we transition
// each individual text property instead
if( matches( pair.from, textNodes ) ) {
@@ -490,7 +491,7 @@ export default class AutoAnimate {
} );
// Line numbers
this.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-line[data-line-number]', node => {
this.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-numbers[data-line-number]', node => {
return node.getAttribute( 'data-line-number' );
}, {
scale: false,
@@ -559,14 +560,14 @@ export default class AutoAnimate {
// Retrieve the 'from' element
if( fromMatches[key] ) {
const pimaryIndex = toMatches[key].length - 1;
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][ pimaryIndex ] ) {
fromElement = fromMatches[key][ pimaryIndex ];
fromMatches[key][ pimaryIndex ] = null;
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.
@@ -594,7 +595,7 @@ export default class AutoAnimate {
* 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 considerd
* Note that parents of auto-animate targets are NOT considered
* unmatched since fading them would break the auto-animation.
*
* @param {HTMLElement} rootElement
+95 -28
View File
@@ -122,6 +122,7 @@ export default class Backgrounds {
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' ),
@@ -150,7 +151,7 @@ export default class Backgrounds {
if( data.background ) {
// Auto-wrap image urls in url(...)
if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) {
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 {
@@ -161,13 +162,14 @@ export default class Backgrounds {
// 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.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {
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 +
@@ -177,6 +179,7 @@ export default class Backgrounds {
// 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', '' );
@@ -187,10 +190,30 @@ export default class Backgrounds {
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 = data.backgroundColor;
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 ) ) {
@@ -208,14 +231,32 @@ export default class Backgrounds {
// an element with no background
if( rgb && rgb.a !== 0 ) {
if( colorBrightness( contrastColor ) < 128 ) {
slide.classList.add( 'has-dark-background' );
return 'has-dark-background';
}
else {
slide.classList.add( 'has-light-background' );
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 );
}
/**
@@ -227,14 +268,15 @@ export default class Backgrounds {
*/
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 = this.Reveal.getConfig().rtl ? 'future' : 'past',
horizontalFuture = this.Reveal.getConfig().rtl ? 'past' : 'future';
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)
@@ -260,10 +302,12 @@ export default class Backgrounds {
backgroundv.classList.remove( 'past', 'present', 'future' );
if( v < indices.v ) {
const indexv = typeof indices.v === 'number' ? indices.v : 0;
if( v < indexv ) {
backgroundv.classList.add( 'past' );
}
else if ( v > indices.v ) {
else if ( v > indexv ) {
backgroundv.classList.add( 'future' );
}
else {
@@ -278,15 +322,53 @@ export default class Backgrounds {
} );
// 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 <video> element forward so that it plays continuously
// across multiple slides
const currentVideo = currentBackground.querySelector( 'video' );
const previousVideo = this.previousBackground.querySelector( 'video' );
if( currentVideo && previousVideo ) {
const currentVideoParent = currentVideo.parentNode;
const previousVideoParent = previousVideo.parentNode;
// Swap the two videos
previousVideoParent.appendChild( currentVideo );
currentVideoParent.appendChild( previousVideo );
}
}
}
const backgroundChanged = currentBackground !== this.previousBackground;
// Stop content inside of previous backgrounds
if( this.previousBackground ) {
if( backgroundChanged && this.previousBackground ) {
this.Reveal.slideContent.stopEmbeddedContent( this.previousBackground, { unloadIframes: !this.Reveal.slideContent.shouldPreload( this.previousBackground ) } );
}
// Start content in the current background
if( currentBackground ) {
if( backgroundChanged && currentBackground ) {
this.Reveal.slideContent.startEmbeddedContent( currentBackground );
@@ -304,14 +386,6 @@ export default class Backgrounds {
}
// Don't transition between identical backgrounds. This
// prevents unwanted flicker.
let previousBackgroundHash = this.previousBackground ? this.previousBackground.getAttribute( 'data-background-hash' ) : null;
let currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );
if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== this.previousBackground ) {
this.element.classList.add( 'no-transition' );
}
this.previousBackground = currentBackground;
}
@@ -319,20 +393,13 @@ export default class Backgrounds {
// If there's a background brightness flag for this slide,
// bubble it to the .reveal container
if( currentSlide ) {
[ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => {
if( currentSlide.classList.contains( classToBubble ) ) {
this.Reveal.getRevealElement().classList.add( classToBubble );
}
else {
this.Reveal.getRevealElement().classList.remove( classToBubble );
}
}, this );
this.bubbleSlideContrastClassToElement( currentSlide, this.Reveal.getRevealElement() );
}
// Allow the first background to apply without transition
setTimeout( () => {
this.element.classList.remove( 'no-transition' );
}, 1 );
}, 10 );
}
+24 -3
View File
@@ -1,4 +1,4 @@
import { queryAll } from '../utils/util.js'
import { queryAll, enterFullscreen } from '../utils/util.js'
import { isAndroid } from '../utils/device.js'
/**
@@ -12,6 +12,7 @@ import { isAndroid } from '../utils/device.js'
* - .navigate-left
* - .navigate-next
* - .navigate-prev
* - .enter-fullscreen
*/
export default class Controls {
@@ -25,6 +26,7 @@ export default class Controls {
this.onNavigateDownClicked = this.onNavigateDownClicked.bind( this );
this.onNavigatePrevClicked = this.onNavigatePrevClicked.bind( this );
this.onNavigateNextClicked = this.onNavigateNextClicked.bind( this );
this.onEnterFullscreen = this.onEnterFullscreen.bind( this );
}
@@ -50,6 +52,7 @@ export default class Controls {
this.controlsDown = queryAll( revealElement, '.navigate-down' );
this.controlsPrev = queryAll( revealElement, '.navigate-prev' );
this.controlsNext = queryAll( revealElement, '.navigate-next' );
this.controlsFullscreen = queryAll( revealElement, '.enter-fullscreen' );
// The left, right and down arrows in the standard reveal.js controls
this.controlsRightArrow = this.element.querySelector( '.navigate-right' );
@@ -63,7 +66,10 @@ export default class Controls {
*/
configure( config, oldConfig ) {
this.element.style.display = config.controls ? 'block' : 'none';
this.element.style.display = (
config.controls &&
(config.controls !== 'speaker-only' || this.Reveal.isSpeakerNotes())
) ? 'block' : 'none';
this.element.setAttribute( 'data-controls-layout', config.controlsLayout );
this.element.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows );
@@ -89,6 +95,7 @@ export default class Controls {
this.controlsDown.forEach( el => el.addEventListener( eventName, this.onNavigateDownClicked, false ) );
this.controlsPrev.forEach( el => el.addEventListener( eventName, this.onNavigatePrevClicked, false ) );
this.controlsNext.forEach( el => el.addEventListener( eventName, this.onNavigateNextClicked, false ) );
this.controlsFullscreen.forEach( el => el.addEventListener( eventName, this.onEnterFullscreen, false ) );
} );
}
@@ -102,6 +109,7 @@ export default class Controls {
this.controlsDown.forEach( el => el.removeEventListener( eventName, this.onNavigateDownClicked, false ) );
this.controlsPrev.forEach( el => el.removeEventListener( eventName, this.onNavigatePrevClicked, false ) );
this.controlsNext.forEach( el => el.removeEventListener( eventName, this.onNavigateNextClicked, false ) );
this.controlsFullscreen.forEach( el => el.removeEventListener( eventName, this.onEnterFullscreen, false ) );
} );
}
@@ -141,9 +149,14 @@ export default class Controls {
if( fragmentsRoutes.prev ) this.controlsPrev.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );
if( fragmentsRoutes.next ) this.controlsNext.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );
const isVerticalStack = this.Reveal.isVerticalSlide( currentSlide );
const hasVerticalSiblings = isVerticalStack &&
currentSlide.parentElement &&
currentSlide.parentElement.querySelectorAll( ':scope > section' ).length > 1;
// Apply fragment decorators to directional buttons based on
// what slide axis they are in
if( this.Reveal.isVerticalSlide( currentSlide ) ) {
if( isVerticalStack && hasVerticalSiblings ) {
if( fragmentsRoutes.prev ) this.controlsUp.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );
if( fragmentsRoutes.next ) this.controlsDown.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );
}
@@ -262,5 +275,13 @@ export default class Controls {
}
onEnterFullscreen( event ) {
const config = this.Reveal.getConfig();
const viewport = this.Reveal.getViewportElement();
enterFullscreen( config.embedded ? viewport : viewport.parentElement );
}
}
+25 -26
View File
@@ -174,24 +174,23 @@ export default class Fragments {
*
* @return {{shown: array, hidden: array}}
*/
update( index, fragments ) {
update( index, fragments, slide = this.Reveal.getCurrentSlide() ) {
let changedFragments = {
shown: [],
hidden: []
};
let currentSlide = this.Reveal.getCurrentSlide();
if( currentSlide && this.Reveal.getConfig().fragments ) {
if( slide && this.Reveal.getConfig().fragments ) {
fragments = fragments || this.sort( currentSlide.querySelectorAll( '.fragment' ) );
fragments = fragments || this.sort( slide.querySelectorAll( '.fragment' ) );
if( fragments.length ) {
let maxIndex = 0;
if( typeof index !== 'number' ) {
let currentFragment = this.sort( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
let currentFragment = this.sort( slide.querySelectorAll( '.fragment.visible' ) ).pop();
if( currentFragment ) {
index = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );
}
@@ -252,12 +251,32 @@ export default class Fragments {
// the current fragment index.
index = typeof index === 'number' ? index : -1;
index = Math.max( Math.min( index, maxIndex ), -1 );
currentSlide.setAttribute( 'data-fragment', index );
slide.setAttribute( 'data-fragment', index );
}
}
if( changedFragments.hidden.length ) {
this.Reveal.dispatchEvent({
type: 'fragmenthidden',
data: {
fragment: changedFragments.hidden[0],
fragments: changedFragments.hidden
}
});
}
if( changedFragments.shown.length ) {
this.Reveal.dispatchEvent({
type: 'fragmentshown',
data: {
fragment: changedFragments.shown[0],
fragments: changedFragments.shown
}
});
}
return changedFragments;
}
@@ -312,26 +331,6 @@ export default class Fragments {
let changedFragments = this.update( index, fragments );
if( changedFragments.hidden.length ) {
this.Reveal.dispatchEvent({
type: 'fragmenthidden',
data: {
fragment: changedFragments.hidden[0],
fragments: changedFragments.hidden
}
});
}
if( changedFragments.shown.length ) {
this.Reveal.dispatchEvent({
type: 'fragmentshown',
data: {
fragment: changedFragments.shown[0],
fragments: changedFragments.shown
}
});
}
this.Reveal.controls.update();
this.Reveal.progress.update();
@@ -0,0 +1,197 @@
import {
SLIDE_NUMBER_FORMAT_CURRENT,
SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL
} from "../utils/constants";
/**
* Makes it possible to jump to a slide by entering its
* slide number or id.
*/
export default class JumpToSlide {
constructor( Reveal ) {
this.Reveal = Reveal;
this.onInput = this.onInput.bind( this );
this.onBlur = this.onBlur.bind( this );
this.onKeyDown = this.onKeyDown.bind( this );
}
render() {
this.element = document.createElement( 'div' );
this.element.className = 'jump-to-slide';
this.jumpInput = document.createElement( 'input' );
this.jumpInput.type = 'text';
this.jumpInput.className = 'jump-to-slide-input';
this.jumpInput.placeholder = 'Jump to slide';
this.jumpInput.addEventListener( 'input', this.onInput );
this.jumpInput.addEventListener( 'keydown', this.onKeyDown );
this.jumpInput.addEventListener( 'blur', this.onBlur );
this.element.appendChild( this.jumpInput );
}
show() {
this.indicesOnShow = this.Reveal.getIndices();
this.Reveal.getRevealElement().appendChild( this.element );
this.jumpInput.focus();
}
hide() {
if( this.isVisible() ) {
this.element.remove();
this.jumpInput.value = '';
clearTimeout( this.jumpTimeout );
delete this.jumpTimeout;
}
}
isVisible() {
return !!this.element.parentNode;
}
/**
* Parses the current input and jumps to the given slide.
*/
jump() {
clearTimeout( this.jumpTimeout );
delete this.jumpTimeout;
let query = this.jumpInput.value.trim( '' );
let indices;
// When slide numbers are formatted to be a single linear number
// (instead of showing a separate horizontal/vertical index) we
// use the same format for slide jumps
if( /^\d+$/.test( query ) ) {
const slideNumberFormat = this.Reveal.getConfig().slideNumber;
if( slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT || slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL ) {
const slide = this.Reveal.getSlides()[ parseInt( query, 10 ) - 1 ];
if( slide ) {
indices = this.Reveal.getIndices( slide );
}
}
}
if( !indices ) {
// If the query uses "horizontal.vertical" format, convert to
// "horizontal/vertical" so that our URL parser can understand
if( /^\d+\.\d+$/.test( query ) ) {
query = query.replace( '.', '/' );
}
indices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } );
}
// Still no valid index? Fall back on a text search
if( !indices && /\S+/i.test( query ) && query.length > 1 ) {
indices = this.search( query );
}
if( indices && query !== '' ) {
this.Reveal.slide( indices.h, indices.v, indices.f );
return true;
}
else {
this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
return false;
}
}
jumpAfter( delay ) {
clearTimeout( this.jumpTimeout );
this.jumpTimeout = setTimeout( () => this.jump(), delay );
}
/**
* A lofi search that looks for the given query in all
* of our slides and returns the first match.
*/
search( query ) {
const regex = new RegExp( '\\b' + query.trim() + '\\b', 'i' );
const slide = this.Reveal.getSlides().find( ( slide ) => {
return regex.test( slide.innerText );
} );
if( slide ) {
return this.Reveal.getIndices( slide );
}
else {
return null;
}
}
/**
* Reverts back to the slide we were on when jump to slide was
* invoked.
*/
cancel() {
this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
this.hide();
}
confirm() {
this.jump();
this.hide();
}
destroy() {
this.jumpInput.removeEventListener( 'input', this.onInput );
this.jumpInput.removeEventListener( 'keydown', this.onKeyDown );
this.jumpInput.removeEventListener( 'blur', this.onBlur );
this.element.remove();
}
onKeyDown( event ) {
if( event.keyCode === 13 ) {
this.confirm();
}
else if( event.keyCode === 27 ) {
this.cancel();
event.stopImmediatePropagation();
}
}
onInput( event ) {
this.jumpAfter( 200 );
}
onBlur() {
setTimeout( () => this.hide(), 1 );
}
}
+41 -25
View File
@@ -17,7 +17,6 @@ export default class Keyboard {
this.bindings = {};
this.onDocumentKeyDown = this.onDocumentKeyDown.bind( this );
this.onDocumentKeyPress = this.onDocumentKeyPress.bind( this );
}
@@ -43,6 +42,7 @@ export default class Keyboard {
this.shortcuts['Shift + &#8592;/&#8593/&#8594;/&#8595;'] = 'Jump to first/last slide';
this.shortcuts['B , .'] = 'Pause';
this.shortcuts['F'] = 'Fullscreen';
this.shortcuts['G'] = 'Jump to slide';
this.shortcuts['ESC, O'] = 'Slide overview';
}
@@ -53,7 +53,6 @@ export default class Keyboard {
bind() {
document.addEventListener( 'keydown', this.onDocumentKeyDown, false );
document.addEventListener( 'keypress', this.onDocumentKeyPress, false );
}
@@ -63,7 +62,6 @@ export default class Keyboard {
unbind() {
document.removeEventListener( 'keydown', this.onDocumentKeyDown, false );
document.removeEventListener( 'keypress', this.onDocumentKeyPress, false );
}
@@ -134,20 +132,6 @@ export default class Keyboard {
}
/**
* Handler for the document level 'keypress' event.
*
* @param {object} event
*/
onDocumentKeyPress( event ) {
// Check if the pressed key is question mark
if( event.shiftKey && event.charCode === 63 ) {
this.Reveal.toggleHelp();
}
}
/**
* Handler for the document level 'keydown' event.
*
@@ -183,10 +167,10 @@ export default class Keyboard {
let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
// Whitelist certain modifiers for slide navigation shortcuts
let isNavigationKey = [32, 37, 38, 39, 40, 78, 80].indexOf( event.keyCode ) !== -1;
let keyCodeUsesModifier = [32, 37, 38, 39, 40, 63, 78, 80, 191].indexOf( event.keyCode ) !== -1;
// Prevent all other events when a modifier is pressed
let unusedModifier = !( isNavigationKey && event.shiftKey || event.altKey ) &&
let unusedModifier = !( keyCodeUsesModifier && event.shiftKey || event.altKey ) &&
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
// Disregard the event if there's a focused element or a
@@ -194,7 +178,7 @@ export default class Keyboard {
if( activeElementIsCE || activeElementIsInput || activeElementIsNotes || unusedModifier ) return;
// While paused only allow resume keyboard events; 'b', 'v', '.'
let resumeKeyCodes = [66,86,190,191];
let resumeKeyCodes = [66,86,190,191,112];
let key;
// Custom key bindings for togglePause should be able to resume
@@ -206,6 +190,10 @@ export default class Keyboard {
}
}
if( this.Reveal.isOverlayOpen() && !["Escape", "f", "c", "b", "."].includes(event.key) ) {
return false;
}
if( this.Reveal.isPaused() && resumeKeyCodes.indexOf( keyCode ) === -1 ) {
return false;
}
@@ -287,7 +275,12 @@ export default class Keyboard {
this.Reveal.slide( 0 );
}
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.prev({skipFragments: event.altKey});
if( config.rtl ) {
this.Reveal.next({skipFragments: event.altKey});
}
else {
this.Reveal.prev({skipFragments: event.altKey});
}
}
else {
this.Reveal.left({skipFragments: event.altKey});
@@ -299,7 +292,12 @@ export default class Keyboard {
this.Reveal.slide( this.Reveal.getHorizontalSlides().length - 1 );
}
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.next({skipFragments: event.altKey});
if( config.rtl ) {
this.Reveal.prev({skipFragments: event.altKey});
}
else {
this.Reveal.next({skipFragments: event.altKey});
}
}
else {
this.Reveal.right({skipFragments: event.altKey});
@@ -350,7 +348,7 @@ export default class Keyboard {
}
}
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
else if( keyCode === 58 || keyCode === 59 || keyCode === 66 || keyCode === 86 || keyCode === 190 || keyCode === 191 ) {
else if( [58, 59, 66, 86, 190].includes( keyCode ) || ( keyCode === 191 && !event.shiftKey ) ) {
this.Reveal.togglePause();
}
// F
@@ -359,10 +357,28 @@ export default class Keyboard {
}
// A
else if( keyCode === 65 ) {
if ( config.autoSlideStoppable ) {
if( config.autoSlideStoppable ) {
this.Reveal.toggleAutoSlide( autoSlideWasPaused );
}
}
// G
else if( keyCode === 71 ) {
if( config.jumpToSlide ) {
this.Reveal.toggleJumpToSlide();
}
}
// C
else if( keyCode === 67 && this.Reveal.isOverlayOpen() ) {
this.Reveal.closeOverlay();
}
// ?
else if( ( keyCode === 63 || keyCode === 191 ) && event.shiftKey ) {
this.Reveal.toggleHelp();
}
// F1
else if( keyCode === 112 ) {
this.Reveal.toggleHelp();
}
else {
triggered = false;
}
@@ -389,4 +405,4 @@ export default class Keyboard {
}
}
}
+9 -7
View File
@@ -40,7 +40,7 @@ export default class Location {
*
* @returns slide indices or null
*/
getIndicesFromHash( hash=window.location.hash ) {
getIndicesFromHash( hash=window.location.hash, options={} ) {
// Attempt to parse the hash as either an index or name
let name = hash.replace( /^#\/?/, '' );
@@ -49,7 +49,7 @@ export default class Location {
// If the first bit is not fully numeric and there is a name we
// can assume that this is a named link
if( !/^[0-9]*$/.test( bits[0] ) && name.length ) {
let element;
let slide;
let f;
@@ -62,17 +62,19 @@ export default class Location {
// Ensure the named link is a valid HTML ID attribute
try {
element = document.getElementById( decodeURIComponent( name ) );
slide = document
.getElementById( decodeURIComponent( name ) )
.closest('.slides section');
}
catch ( error ) { }
if( element ) {
return { ...this.Reveal.getIndices( element ), f };
if( slide ) {
return { ...this.Reveal.getIndices( slide ), f };
}
}
else {
const config = this.Reveal.getConfig();
let hashIndexBase = config.hashOneBasedIndex ? 1 : 0;
let hashIndexBase = config.hashOneBasedIndex || options.oneBasedIndex ? 1 : 0;
// Read the index components of the hash
let h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0,
@@ -139,7 +141,7 @@ export default class Location {
let hash = this.getHash();
// If we're configured to push to history OR the history
// API is not avaialble.
// API is not available.
if( config.history ) {
window.location.hash = hash;
}
+16 -10
View File
@@ -1,5 +1,5 @@
/**
* Handles the showing and
* Handles the showing of speaker notes
*/
export default class Notes {
@@ -38,10 +38,12 @@ export default class Notes {
*/
update() {
if( this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.print.isPrintingPDF() ) {
if( this.Reveal.getConfig().showNotes &&
this.element && this.Reveal.getCurrentSlide() &&
!this.Reveal.isScrollView() &&
!this.Reveal.isPrintView()
) {
this.element.innerHTML = this.getSlideNotes() || '<span class="notes-placeholder">No notes on this slide.</span>';
}
}
@@ -54,7 +56,11 @@ export default class Notes {
*/
updateVisibility() {
if( this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.print.isPrintingPDF() ) {
if( this.Reveal.getConfig().showNotes &&
this.hasNotes() &&
!this.Reveal.isScrollView() &&
!this.Reveal.isPrintView()
) {
this.Reveal.getRevealElement().classList.add( 'show-notes' );
}
else {
@@ -89,7 +95,7 @@ export default class Notes {
* Retrieves the speaker notes from a slide. Notes can be
* defined in two ways:
* 1. As a data-notes attribute on the slide <section>
* 2. As an <aside class="notes"> inside of the slide
* 2. With <aside class="notes"> elements inside the slide
*
* @param {HTMLElement} [slide=currentSlide]
* @return {(string|null)}
@@ -101,10 +107,10 @@ export default class Notes {
return slide.getAttribute( 'data-notes' );
}
// ... or using an <aside class="notes"> element
let notesElement = slide.querySelector( 'aside.notes' );
if( notesElement ) {
return notesElement.innerHTML;
// ... or using <aside class="notes"> elements
let notesElements = slide.querySelectorAll( 'aside.notes' );
if( notesElements ) {
return Array.from(notesElements).map( notesElement => notesElement.innerHTML ).join( '\n' );
}
return null;
+389
View File
@@ -0,0 +1,389 @@
/**
* Handles the display of reveal.js' overlay elements used
* to preview iframes, images & videos.
*/
export default class Overlay {
constructor( Reveal ) {
this.Reveal = Reveal;
this.onSlidesClicked = this.onSlidesClicked.bind( this );
this.iframeTriggerSelector = null;
this.mediaTriggerSelector = '[data-preview-image], [data-preview-video]';
this.stateProps = ['previewIframe', 'previewImage', 'previewVideo', 'previewFit'];
this.state = {};
}
update() {
// Enable link previews globally
if( this.Reveal.getConfig().previewLinks ) {
this.iframeTriggerSelector = 'a[href]:not([data-preview-link=false]), [data-preview-link]:not(a):not([data-preview-link=false])';
}
// Enable link previews for individual elements
else {
this.iframeTriggerSelector = '[data-preview-link]:not([data-preview-link=false])';
}
const hasLinkPreviews = this.Reveal.getSlidesElement().querySelectorAll( this.iframeTriggerSelector ).length > 0;
const hasMediaPreviews = this.Reveal.getSlidesElement().querySelectorAll( this.mediaTriggerSelector ).length > 0;
// Only add the listener when there are previewable elements in the slides
if( hasLinkPreviews || hasMediaPreviews ) {
this.Reveal.getSlidesElement().addEventListener( 'click', this.onSlidesClicked, false );
}
else {
this.Reveal.getSlidesElement().removeEventListener( 'click', this.onSlidesClicked, false );
}
}
createOverlay( className ) {
this.dom = document.createElement( 'div' );
this.dom.classList.add( 'r-overlay' );
this.dom.classList.add( className );
this.viewport = document.createElement( 'div' );
this.viewport.classList.add( 'r-overlay-viewport' );
this.dom.appendChild( this.viewport );
this.Reveal.getRevealElement().appendChild( this.dom );
}
/**
* Opens a lightbox that previews the target URL.
*
* @param {string} url - url for lightbox iframe src
*/
previewIframe( url ) {
this.close();
this.state = { previewIframe: url };
this.createOverlay( 'r-overlay-preview' );
this.dom.dataset.state = 'loading';
this.viewport.innerHTML =
`<header class="r-overlay-header">
<a class="r-overlay-button r-overlay-external" href="${url}" target="_blank"><span class="icon"></span></a>
<button class="r-overlay-button r-overlay-close"><span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content">
<iframe src="${url}"></iframe>
<small class="r-overlay-content-inner">
<span class="r-overlay-error x-frame-error">Unable to load iframe. This is likely due to the site's policy (x-frame-options).</span>
</small>
</div>`;
this.dom.querySelector( 'iframe' ).addEventListener( 'load', event => {
this.dom.dataset.state = 'loaded';
}, false );
this.dom.querySelector( '.r-overlay-close' ).addEventListener( 'click', event => {
this.close();
event.preventDefault();
}, false );
this.dom.querySelector( '.r-overlay-external' ).addEventListener( 'click', event => {
this.close();
}, false );
this.Reveal.dispatchEvent({ type: 'previewiframe', data: { url } });
}
/**
* Opens a lightbox window that provides a larger view of the
* given image/video.
*
* @param {string} url - url to the image/video to preview
* @param {image|video} mediaType
* @param {string} [fitMode] - the fit mode to use for the preview
*/
previewMedia( url, mediaType, fitMode ) {
if( mediaType !== 'image' && mediaType !== 'video' ) {
console.warn( 'Please specify a valid media type to preview (image|video)' );
return;
}
this.close();
fitMode = fitMode || 'scale-down';
this.createOverlay( 'r-overlay-preview' );
this.dom.dataset.state = 'loading';
this.dom.dataset.previewFit = fitMode;
this.viewport.innerHTML =
`<header class="r-overlay-header">
<button class="r-overlay-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-spinner"></div>
<div class="r-overlay-content"></div>`;
const contentElement = this.dom.querySelector( '.r-overlay-content' );
if( mediaType === 'image' ) {
this.state = { previewImage: url, previewFit: fitMode }
const img = document.createElement( 'img', {} );
img.src = url;
contentElement.appendChild( img );
img.addEventListener( 'load', () => {
this.dom.dataset.state = 'loaded';
}, false );
img.addEventListener( 'error', () => {
this.dom.dataset.state = 'error';
contentElement.innerHTML =
`<span class="r-overlay-error">Unable to load image.</span>`
}, false );
// Hide image overlays when clicking outside the overlay
this.dom.style.cursor = 'zoom-out';
this.dom.addEventListener( 'click', ( event ) => {
this.close();
}, false );
this.Reveal.dispatchEvent({ type: 'previewimage', data: { url } });
}
else if( mediaType === 'video' ) {
this.state = { previewVideo: url, previewFit: fitMode }
const video = document.createElement( 'video' );
video.autoplay = this.dom.dataset.previewAutoplay === 'false' ? false : true;
video.controls = this.dom.dataset.previewControls === 'false' ? false : true;
video.loop = this.dom.dataset.previewLoop === 'true' ? true : false;
video.muted = this.dom.dataset.previewMuted === 'true' ? true : false;
video.playsInline = true;
video.src = url;
contentElement.appendChild( video );
video.addEventListener( 'loadeddata', () => {
this.dom.dataset.state = 'loaded';
}, false );
video.addEventListener( 'error', () => {
this.dom.dataset.state = 'error';
contentElement.innerHTML =
`<span class="r-overlay-error">Unable to load video.</span>`;
}, false );
this.Reveal.dispatchEvent({ type: 'previewvideo', data: { url } });
}
else {
throw new Error( 'Please specify a valid media type to preview' );
}
this.dom.querySelector( '.r-overlay-close' ).addEventListener( 'click', ( event ) => {
this.close();
event.preventDefault();
}, false );
}
previewImage( url, fitMode ) {
this.previewMedia( url, 'image', fitMode );
}
previewVideo( url, fitMode ) {
this.previewMedia( url, 'video', fitMode );
}
/**
* Open or close help overlay window.
*
* @param {Boolean} [override] Flag which overrides the
* toggle logic and forcibly sets the desired state. True means
* help is open, false means it's closed.
*/
toggleHelp( override ) {
if( typeof override === 'boolean' ) {
override ? this.showHelp() : this.close();
}
else {
if( this.dom ) {
this.close();
}
else {
this.showHelp();
}
}
}
/**
* Opens an overlay window with help material.
*/
showHelp() {
if( this.Reveal.getConfig().help ) {
this.close();
this.createOverlay( 'r-overlay-help' );
let html = '<p class="title">Keyboard Shortcuts</p>';
let shortcuts = this.Reveal.keyboard.getShortcuts(),
bindings = this.Reveal.keyboard.getBindings();
html += '<table><th>KEY</th><th>ACTION</th>';
for( let key in shortcuts ) {
html += `<tr><td>${key}</td><td>${shortcuts[ key ]}</td></tr>`;
}
// Add custom key bindings that have associated descriptions
for( let binding in bindings ) {
if( bindings[binding].key && bindings[binding].description ) {
html += `<tr><td>${bindings[binding].key}</td><td>${bindings[binding].description}</td></tr>`;
}
}
html += '</table>';
this.viewport.innerHTML = `
<header class="r-overlay-header">
<button class="r-overlay-button r-overlay-close">Esc <span class="icon"></span></button>
</header>
<div class="r-overlay-content">
<div class="r-overlay-help-content">${html}</div>
</div>
`;
this.dom.querySelector( '.r-overlay-close' ).addEventListener( 'click', event => {
this.close();
event.preventDefault();
}, false );
this.Reveal.dispatchEvent({ type: 'showhelp' });
}
}
isOpen() {
return !!this.dom;
}
/**
* Closes any currently open overlay.
*/
close() {
if( this.dom ) {
this.dom.remove();
this.dom = null;
this.state = {};
this.Reveal.dispatchEvent({ type: 'closeoverlay' });
return true;
}
return false;
}
getState() {
return this.state;
}
setState( state ) {
// Ignore the incoming state if none of the preview related
// props have changed
if( this.stateProps.every( key => this.state[ key ] === state[ key ] ) ) {
return;
}
if( state.previewIframe ) {
this.previewIframe( state.previewIframe );
}
else if( state.previewImage ) {
this.previewImage( state.previewImage, state.previewFit );
}
else if( state.previewVideo ) {
this.previewVideo( state.previewVideo, state.previewFit );
}
else {
this.close();
}
}
onSlidesClicked( event ) {
const target = event.target;
const linkTarget = target.closest( this.iframeTriggerSelector );
const mediaTarget = target.closest( this.mediaTriggerSelector );
// Was an iframe lightbox trigger clicked?
if( linkTarget ) {
if( event.metaKey || event.shiftKey || event.altKey ) {
// Let the browser handle meta keys naturally so users can cmd+click
return;
}
let url = linkTarget.getAttribute( 'href' ) || linkTarget.getAttribute( 'data-preview-link' );
if( url ) {
this.previewIframe( url );
event.preventDefault();
}
}
// Was a media lightbox trigger clicked?
else if( mediaTarget ) {
if( mediaTarget.hasAttribute( 'data-preview-image' ) ) {
let url = mediaTarget.dataset.previewImage || mediaTarget.getAttribute( 'src' );
if( url ) {
this.previewImage( url, mediaTarget.dataset.previewFit );
event.preventDefault();
}
}
else if( mediaTarget.hasAttribute( 'data-preview-video' ) ) {
let url = mediaTarget.dataset.previewVideo || mediaTarget.getAttribute( 'src' );
if( !url ) {
let source = mediaTarget.querySelector( 'source' );
if( source ) {
url = source.getAttribute( 'src' );
}
}
if( url ) {
this.previewVideo( url, mediaTarget.dataset.previewFit );
event.preventDefault();
}
}
}
}
destroy() {
this.close();
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ export default class Overview {
activate() {
// Only proceed if enabled in config
if( this.Reveal.getConfig().overview && !this.isActive() ) {
if( this.Reveal.getConfig().overview && !this.Reveal.isScrollView() && !this.isActive() ) {
this.active = true;
+2 -2
View File
@@ -12,7 +12,7 @@ export default class Plugins {
// Flags our current state (idle -> loading -> loaded)
this.state = 'idle';
// An id:instance map of currently registed plugins
// An id:instance map of currently registered plugins
this.registeredPlugins = {};
this.asyncDependencies = [];
@@ -171,7 +171,7 @@ export default class Plugins {
/**
* Registers a new plugin with this reveal.js instance.
*
* reveal.js waits for all regisered plugins to initialize
* reveal.js waits for all registered plugins to initialize
* before considering itself ready, as long as the plugin
* is registered before calling `Reveal.initialize()`.
*/
+4 -7
View File
@@ -27,12 +27,10 @@ export default class Pointer {
configure( config, oldConfig ) {
if( config.mouseWheel ) {
document.addEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF
document.addEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.addEventListener( 'wheel', this.onDocumentMouseScroll, false );
}
else {
document.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF
document.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'wheel', this.onDocumentMouseScroll, false );
}
// Auto-hide the mouse pointer when its inactive
@@ -79,8 +77,7 @@ export default class Pointer {
this.showCursor();
document.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false );
document.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'wheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'mousemove', this.onDocumentCursorActive, false );
document.removeEventListener( 'mousedown', this.onDocumentCursorActive, false );
@@ -126,4 +123,4 @@ export default class Pointer {
}
}
}
@@ -4,7 +4,7 @@ import { queryAll, createStyleSheet } from '../utils/util.js'
/**
* Setups up our presentation for printing/exporting to PDF.
*/
export default class Print {
export default class PrintView {
constructor( Reveal ) {
@@ -16,13 +16,13 @@ export default class Print {
* Configures the presentation for printing to a static
* PDF.
*/
async setupPDF() {
async activate() {
const config = this.Reveal.getConfig();
const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )
// Compute slide numbers now, before we start duplicating slides
const doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
const injectPageNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
@@ -42,11 +42,11 @@ export default class Print {
// Limit the size of certain elements to the dimensions of the slide
createStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
document.documentElement.classList.add( 'print-pdf' );
document.documentElement.classList.add( 'reveal-print', 'print-pdf' );
document.body.style.width = pageWidth + 'px';
document.body.style.height = pageHeight + 'px';
const viewportElement = document.querySelector( '.reveal-viewport' );
const viewportElement = this.Reveal.getViewportElement();
let presentationBackground;
if( viewportElement ) {
const viewportStyles = window.getComputedStyle( viewportElement );
@@ -66,6 +66,7 @@ export default class Print {
const pages = [];
const pageContainer = slides[0].parentNode;
let slideNumber = 1;
// Slide and slide background layout
slides.forEach( function( slide, index ) {
@@ -109,9 +110,7 @@ export default class Print {
slide.style.top = top + 'px';
slide.style.width = slideWidth + 'px';
// Re-run the slide layout so that r-fit-text is applied based on
// the printed slide size
this.Reveal.slideContent.layout( slide )
this.Reveal.slideContent.layout( slide );
if( slide.slideBackgroundElement ) {
page.insertBefore( slide.slideBackgroundElement, slide );
@@ -146,13 +145,12 @@ export default class Print {
}
// Inject slide numbers if `slideNumbers` are enabled
if( doingSlideNumbers ) {
const slideNumber = index + 1;
// Inject page numbers if `slideNumbers` are enabled
if( injectPageNumbers ) {
const numberElement = document.createElement( 'div' );
numberElement.classList.add( 'slide-number' );
numberElement.classList.add( 'slide-number-pdf' );
numberElement.innerHTML = slideNumber;
numberElement.innerHTML = slideNumber++;
page.appendChild( numberElement );
}
@@ -166,7 +164,7 @@ export default class Print {
let previousFragmentStep;
fragmentGroups.forEach( function( fragments ) {
fragmentGroups.forEach( function( fragments, index ) {
// Remove 'current-fragment' from the previous group
if( previousFragmentStep ) {
@@ -182,6 +180,14 @@ export default class Print {
// Create a separate page for the current fragment state
const clonedPage = page.cloneNode( true );
// Inject unique page numbers for fragments
if( injectPageNumbers ) {
const numberElement = clonedPage.querySelector( '.slide-number-pdf' );
const fragmentNumber = index + 1;
numberElement.innerHTML += '.' + fragmentNumber;
}
pages.push( clonedPage );
previousFragmentStep = fragments;
@@ -211,18 +217,23 @@ export default class Print {
pages.forEach( page => pageContainer.appendChild( page ) );
// Re-run JS-based content layout after the slide is added to page DOM
this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
// Notify subscribers that the PDF layout is good to go
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
viewportElement.classList.remove( 'loading-scroll-mode' );
}
/**
* Checks if this instance is being used to print a PDF.
* Checks if the print mode is/should be activated.
*/
isPrintingPDF() {
isActive() {
return ( /print-pdf/gi ).test( window.location.search );
return this.Reveal.getConfig().view === 'print';
}
}
}
@@ -0,0 +1,923 @@
import { HORIZONTAL_SLIDES_SELECTOR, HORIZONTAL_BACKGROUNDS_SELECTOR } from '../utils/constants.js'
import { queryAll } from '../utils/util.js'
const HIDE_SCROLLBAR_TIMEOUT = 500;
const MAX_PROGRESS_SPACING = 4;
const MIN_PROGRESS_SEGMENT_HEIGHT = 6;
const MIN_PLAYHEAD_HEIGHT = 8;
/**
* The scroll view lets you read a reveal.js presentation
* as a linear scrollable page.
*/
export default class ScrollView {
constructor( Reveal ) {
this.Reveal = Reveal;
this.active = false;
this.activatedCallbacks = [];
this.onScroll = this.onScroll.bind( this );
}
/**
* Activates the scroll view. This rearranges the presentation DOM
* byamong other thingswrapping each slide in a page element.
*/
activate() {
if( this.active ) return;
const stateBeforeActivation = this.Reveal.getState();
this.active = true;
// Store the full presentation HTML so that we can restore it
// when/if the scroll view is deactivated
this.slideHTMLBeforeActivation = this.Reveal.getSlidesElement().innerHTML;
const horizontalSlides = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_SLIDES_SELECTOR );
const horizontalBackgrounds = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_BACKGROUNDS_SELECTOR );
this.viewportElement.classList.add( 'loading-scroll-mode', 'reveal-scroll' );
let presentationBackground;
const viewportStyles = window.getComputedStyle( this.viewportElement );
if( viewportStyles && viewportStyles.background ) {
presentationBackground = viewportStyles.background;
}
const pageElements = [];
const pageContainer = horizontalSlides[0].parentNode;
let previousSlide;
// Creates a new page element and appends the given slide/bg
// to it.
const createPageElement = ( slide, h, v, isVertical ) => {
let contentContainer;
// If this slide is part of an auto-animation sequence, we
// group it under the same page element as the previous slide
if( previousSlide && this.Reveal.shouldAutoAnimateBetween( previousSlide, slide ) ) {
contentContainer = document.createElement( 'div' );
contentContainer.className = 'scroll-page-content scroll-auto-animate-page';
contentContainer.style.display = 'none';
previousSlide.closest( '.scroll-page-content' ).parentNode.appendChild( contentContainer );
}
else {
// Wrap the slide in a page element and hide its overflow
// so that no page ever flows onto another
const page = document.createElement( 'div' );
page.className = 'scroll-page';
pageElements.push( page );
// This transfers over the background of the vertical stack containing
// the slide if it exists. Otherwise, it uses the presentation-wide
// background.
if( isVertical && horizontalBackgrounds.length > h ) {
const slideBackground = horizontalBackgrounds[h];
const pageBackground = window.getComputedStyle( slideBackground );
if( pageBackground && pageBackground.background ) {
page.style.background = pageBackground.background;
}
else if( presentationBackground ) {
page.style.background = presentationBackground;
}
} else if( presentationBackground ) {
page.style.background = presentationBackground;
}
const stickyContainer = document.createElement( 'div' );
stickyContainer.className = 'scroll-page-sticky';
page.appendChild( stickyContainer );
contentContainer = document.createElement( 'div' );
contentContainer.className = 'scroll-page-content';
stickyContainer.appendChild( contentContainer );
}
contentContainer.appendChild( slide );
slide.classList.remove( 'past', 'future' );
slide.setAttribute( 'data-index-h', h );
slide.setAttribute( 'data-index-v', v );
if( slide.slideBackgroundElement ) {
slide.slideBackgroundElement.remove( 'past', 'future' );
contentContainer.insertBefore( slide.slideBackgroundElement, slide );
}
previousSlide = slide;
}
// Slide and slide background layout
horizontalSlides.forEach( ( horizontalSlide, h ) => {
if( this.Reveal.isVerticalStack( horizontalSlide ) ) {
horizontalSlide.querySelectorAll( 'section' ).forEach( ( verticalSlide, v ) => {
createPageElement( verticalSlide, h, v, true );
});
}
else {
createPageElement( horizontalSlide, h, 0 );
}
}, this );
this.createProgressBar();
// Remove leftover stacks
queryAll( this.Reveal.getRevealElement(), '.stack' ).forEach( stack => stack.remove() );
// Add our newly created pages to the DOM
pageElements.forEach( page => pageContainer.appendChild( page ) );
// Re-run JS-based content layout after the slide is added to page DOM
this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
this.Reveal.layout();
this.Reveal.setState( stateBeforeActivation );
this.activatedCallbacks.forEach( callback => callback() );
this.activatedCallbacks = [];
this.restoreScrollPosition();
this.viewportElement.classList.remove( 'loading-scroll-mode' );
this.viewportElement.addEventListener( 'scroll', this.onScroll, { passive: true } );
}
/**
* Deactivates the scroll view and restores the standard slide-based
* presentation.
*/
deactivate() {
if( !this.active ) return;
const stateBeforeDeactivation = this.Reveal.getState();
this.active = false;
this.viewportElement.removeEventListener( 'scroll', this.onScroll );
this.viewportElement.classList.remove( 'reveal-scroll' );
this.removeProgressBar();
this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation;
this.Reveal.sync();
this.Reveal.setState( stateBeforeDeactivation );
this.slideHTMLBeforeActivation = null;
}
toggle( override ) {
if( typeof override === 'boolean' ) {
override ? this.activate() : this.deactivate();
}
else {
this.isActive() ? this.deactivate() : this.activate();
}
}
/**
* Checks if the scroll view is currently active.
*/
isActive() {
return this.active;
}
/**
* Renders the progress bar component.
*/
createProgressBar() {
this.progressBar = document.createElement( 'div' );
this.progressBar.className = 'scrollbar';
this.progressBarInner = document.createElement( 'div' );
this.progressBarInner.className = 'scrollbar-inner';
this.progressBar.appendChild( this.progressBarInner );
this.progressBarPlayhead = document.createElement( 'div' );
this.progressBarPlayhead.className = 'scrollbar-playhead';
this.progressBarInner.appendChild( this.progressBarPlayhead );
this.viewportElement.insertBefore( this.progressBar, this.viewportElement.firstChild );
const handleDocumentMouseMove = ( event ) => {
let progress = ( event.clientY - this.progressBarInner.getBoundingClientRect().top ) / this.progressBarHeight;
progress = Math.max( Math.min( progress, 1 ), 0 );
this.viewportElement.scrollTop = progress * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
};
const handleDocumentMouseUp = ( event ) => {
this.draggingProgressBar = false;
this.showProgressBar();
document.removeEventListener( 'mousemove', handleDocumentMouseMove );
document.removeEventListener( 'mouseup', handleDocumentMouseUp );
};
const handleMouseDown = ( event ) => {
event.preventDefault();
this.draggingProgressBar = true;
document.addEventListener( 'mousemove', handleDocumentMouseMove );
document.addEventListener( 'mouseup', handleDocumentMouseUp );
handleDocumentMouseMove( event );
};
this.progressBarInner.addEventListener( 'mousedown', handleMouseDown );
}
removeProgressBar() {
if( this.progressBar ) {
this.progressBar.remove();
this.progressBar = null;
}
}
layout() {
if( this.isActive() ) {
this.syncPages();
this.syncScrollPosition();
}
}
/**
* Updates our pages to match the latest configuration and
* presentation size.
*/
syncPages() {
const config = this.Reveal.getConfig();
const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
const scale = this.Reveal.getScale();
const useCompactLayout = config.scrollLayout === 'compact';
const viewportHeight = this.viewportElement.offsetHeight;
const compactHeight = slideSize.height * scale;
const pageHeight = useCompactLayout ? compactHeight : viewportHeight;
// The height that needs to be scrolled between scroll triggers
this.scrollTriggerHeight = useCompactLayout ? compactHeight : viewportHeight;
this.viewportElement.style.setProperty( '--page-height', pageHeight + 'px' );
this.viewportElement.style.scrollSnapType = typeof config.scrollSnap === 'string' ? `y ${config.scrollSnap}` : '';
// This will hold all scroll triggers used to show/hide slides
this.slideTriggers = [];
const pageElements = Array.from( this.Reveal.getRevealElement().querySelectorAll( '.scroll-page' ) );
this.pages = pageElements.map( pageElement => {
const page = this.createPage({
pageElement,
slideElement: pageElement.querySelector( 'section' ),
stickyElement: pageElement.querySelector( '.scroll-page-sticky' ),
contentElement: pageElement.querySelector( '.scroll-page-content' ),
backgroundElement: pageElement.querySelector( '.slide-background' ),
autoAnimateElements: pageElement.querySelectorAll( '.scroll-auto-animate-page' ),
autoAnimatePages: []
});
page.pageElement.style.setProperty( '--slide-height', config.center === true ? 'auto' : slideSize.height + 'px' );
this.slideTriggers.push({
page: page,
activate: () => this.activatePage( page ),
deactivate: () => this.deactivatePage( page )
});
// Create scroll triggers that show/hide fragments
this.createFragmentTriggersForPage( page );
// Create scroll triggers for triggering auto-animate steps
if( page.autoAnimateElements.length > 0 ) {
this.createAutoAnimateTriggersForPage( page );
}
let totalScrollTriggerCount = Math.max( page.scrollTriggers.length - 1, 0 );
// Each auto-animate step may include its own scroll triggers
// for fragments, ensure we count those as well
totalScrollTriggerCount += page.autoAnimatePages.reduce( ( total, page ) => {
return total + Math.max( page.scrollTriggers.length - 1, 0 );
}, page.autoAnimatePages.length );
// Clean up from previous renders
page.pageElement.querySelectorAll( '.scroll-snap-point' ).forEach( el => el.remove() );
// Create snap points for all scroll triggers
// - Can't be absolute in FF
// - Can't be 0-height in Safari
// - Can't use snap-align on parent in Safari because then
// inner triggers won't work
for( let i = 0; i < totalScrollTriggerCount + 1; i++ ) {
const triggerStick = document.createElement( 'div' );
triggerStick.className = 'scroll-snap-point';
triggerStick.style.height = this.scrollTriggerHeight + 'px';
triggerStick.style.scrollSnapAlign = useCompactLayout ? 'center' : 'start';
page.pageElement.appendChild( triggerStick );
if( i === 0 ) {
triggerStick.style.marginTop = -this.scrollTriggerHeight + 'px';
}
}
// In the compact layout, only slides with scroll triggers cover the
// full viewport height. This helps avoid empty gaps before or after
// a sticky slide.
if( useCompactLayout && page.scrollTriggers.length > 0 ) {
page.pageHeight = viewportHeight;
page.pageElement.style.setProperty( '--page-height', viewportHeight + 'px' );
}
else {
page.pageHeight = pageHeight;
page.pageElement.style.removeProperty( '--page-height' );
}
// Add scroll padding based on how many scroll triggers we have
page.scrollPadding = this.scrollTriggerHeight * totalScrollTriggerCount;
// The total height including scrollable space
page.totalHeight = page.pageHeight + page.scrollPadding;
// This is used to pad the height of our page in CSS
page.pageElement.style.setProperty( '--page-scroll-padding', page.scrollPadding + 'px' );
// If this is a sticky page, stick it to the vertical center
if( totalScrollTriggerCount > 0 ) {
page.stickyElement.style.position = 'sticky';
page.stickyElement.style.top = Math.max( ( viewportHeight - page.pageHeight ) / 2, 0 ) + 'px';
}
else {
page.stickyElement.style.position = 'relative';
page.pageElement.style.scrollSnapAlign = page.pageHeight < viewportHeight ? 'center' : 'start';
}
return page;
} );
this.setTriggerRanges();
/*
console.log(this.slideTriggers.map( t => {
return {
range: `${t.range[0].toFixed(2)}-${t.range[1].toFixed(2)}`,
triggers: t.page.scrollTriggers.map( t => {
return `${t.range[0].toFixed(2)}-${t.range[1].toFixed(2)}`
}).join( ', ' ),
}
}))
*/
this.viewportElement.setAttribute( 'data-scrollbar', config.scrollProgress );
if( config.scrollProgress && this.totalScrollTriggerCount > 1 ) {
// Create the progress bar if it doesn't already exist
if( !this.progressBar ) this.createProgressBar();
this.syncProgressBar();
}
else {
this.removeProgressBar();
}
}
/**
* Calculates and sets the scroll range for all of our scroll
* triggers.
*/
setTriggerRanges() {
// Calculate the total number of scroll triggers
this.totalScrollTriggerCount = this.slideTriggers.reduce( ( total, trigger ) => {
return total + Math.max( trigger.page.scrollTriggers.length, 1 );
}, 0 );
let rangeStart = 0;
// Calculate the scroll range of each scroll trigger on a scale
// of 0-1
this.slideTriggers.forEach( ( trigger, i ) => {
trigger.range = [
rangeStart,
rangeStart + Math.max( trigger.page.scrollTriggers.length, 1 ) / this.totalScrollTriggerCount
];
const scrollTriggerSegmentSize = ( trigger.range[1] - trigger.range[0] ) / trigger.page.scrollTriggers.length;
// Set the range for each inner scroll trigger
trigger.page.scrollTriggers.forEach( ( scrollTrigger, i ) => {
scrollTrigger.range = [
rangeStart + i * scrollTriggerSegmentSize,
rangeStart + ( i + 1 ) * scrollTriggerSegmentSize
];
} );
rangeStart = trigger.range[1];
} );
// Ensure the last trigger extends to the end of the page, otherwise
// rounding errors can cause the last trigger to end at 0.999999...
this.slideTriggers[this.slideTriggers.length - 1].range[1] = 1;
}
/**
* Creates one scroll trigger for each fragments in the given page.
*
* @param {*} page
*/
createFragmentTriggersForPage( page, slideElement ) {
slideElement = slideElement || page.slideElement;
// Each fragment 'group' is an array containing one or more
// fragments. Multiple fragments that appear at the same time
// are part of the same group.
const fragmentGroups = this.Reveal.fragments.sort( slideElement.querySelectorAll( '.fragment' ), true );
// Create scroll triggers that show/hide fragments
if( fragmentGroups.length ) {
page.fragments = this.Reveal.fragments.sort( slideElement.querySelectorAll( '.fragment:not(.disabled)' ) );
page.scrollTriggers.push(
// Trigger for the initial state with no fragments visible
{
activate: () => {
this.Reveal.fragments.update( -1, page.fragments, slideElement );
}
}
);
// Triggers for each fragment group
fragmentGroups.forEach( ( fragments, i ) => {
page.scrollTriggers.push({
activate: () => {
this.Reveal.fragments.update( i, page.fragments, slideElement );
}
});
} );
}
return page.scrollTriggers.length;
}
/**
* Creates scroll triggers for the auto-animate steps in the
* given page.
*
* @param {*} page
*/
createAutoAnimateTriggersForPage( page ) {
if( page.autoAnimateElements.length > 0 ) {
// Triggers for each subsequent auto-animate slide
this.slideTriggers.push( ...Array.from( page.autoAnimateElements ).map( ( autoAnimateElement, i ) => {
let autoAnimatePage = this.createPage({
slideElement: autoAnimateElement.querySelector( 'section' ),
contentElement: autoAnimateElement,
backgroundElement: autoAnimateElement.querySelector( '.slide-background' )
});
// Create fragment scroll triggers for the auto-animate slide
this.createFragmentTriggersForPage( autoAnimatePage, autoAnimatePage.slideElement );
page.autoAnimatePages.push( autoAnimatePage );
// Return our slide trigger
return {
page: autoAnimatePage,
activate: () => this.activatePage( autoAnimatePage ),
deactivate: () => this.deactivatePage( autoAnimatePage )
};
}));
}
}
/**
* Helper method for creating a page definition and adding
* required fields. A "page" is a slide or auto-animate step.
*/
createPage( page ) {
page.scrollTriggers = [];
page.indexh = parseInt( page.slideElement.getAttribute( 'data-index-h' ), 10 );
page.indexv = parseInt( page.slideElement.getAttribute( 'data-index-v' ), 10 );
return page;
}
/**
* Rerenders progress bar segments so that they match the current
* reveal.js config and size.
*/
syncProgressBar() {
this.progressBarInner.querySelectorAll( '.scrollbar-slide' ).forEach( slide => slide.remove() );
const scrollHeight = this.viewportElement.scrollHeight;
const viewportHeight = this.viewportElement.offsetHeight;
const viewportHeightFactor = viewportHeight / scrollHeight;
this.progressBarHeight = this.progressBarInner.offsetHeight;
this.playheadHeight = Math.max( viewportHeightFactor * this.progressBarHeight, MIN_PLAYHEAD_HEIGHT );
this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight;
const progressSegmentHeight = viewportHeight / scrollHeight * this.progressBarHeight;
const spacing = Math.min( progressSegmentHeight / 8, MAX_PROGRESS_SPACING );
this.progressBarPlayhead.style.height = this.playheadHeight - spacing + 'px';
// Don't show individual segments if they're too small
if( progressSegmentHeight > MIN_PROGRESS_SEGMENT_HEIGHT ) {
this.slideTriggers.forEach( slideTrigger => {
const { page } = slideTrigger;
// Visual representation of a slide
page.progressBarSlide = document.createElement( 'div' );
page.progressBarSlide.className = 'scrollbar-slide';
page.progressBarSlide.style.top = slideTrigger.range[0] * this.progressBarHeight + 'px';
page.progressBarSlide.style.height = ( slideTrigger.range[1] - slideTrigger.range[0] ) * this.progressBarHeight - spacing + 'px';
page.progressBarSlide.classList.toggle( 'has-triggers', page.scrollTriggers.length > 0 );
this.progressBarInner.appendChild( page.progressBarSlide );
// Visual representations of each scroll trigger
page.scrollTriggerElements = page.scrollTriggers.map( ( trigger, i ) => {
const triggerElement = document.createElement( 'div' );
triggerElement.className = 'scrollbar-trigger';
triggerElement.style.top = ( trigger.range[0] - slideTrigger.range[0] ) * this.progressBarHeight + 'px';
triggerElement.style.height = ( trigger.range[1] - trigger.range[0] ) * this.progressBarHeight - spacing + 'px';
page.progressBarSlide.appendChild( triggerElement );
if( i === 0 ) triggerElement.style.display = 'none';
return triggerElement;
} );
} );
}
else {
this.pages.forEach( page => page.progressBarSlide = null );
}
}
/**
* Reads the current scroll position and updates our active
* trigger states accordingly.
*/
syncScrollPosition() {
const viewportHeight = this.viewportElement.offsetHeight;
const viewportHeightFactor = viewportHeight / this.viewportElement.scrollHeight;
const scrollTop = this.viewportElement.scrollTop;
const scrollHeight = this.viewportElement.scrollHeight - viewportHeight
const scrollProgress = Math.max( Math.min( scrollTop / scrollHeight, 1 ), 0 );
const scrollProgressMid = Math.max( Math.min( ( scrollTop + viewportHeight / 2 ) / this.viewportElement.scrollHeight, 1 ), 0 );
let activePage;
this.slideTriggers.forEach( ( trigger ) => {
const { page } = trigger;
const shouldPreload = scrollProgress >= trigger.range[0] - viewportHeightFactor*2 &&
scrollProgress <= trigger.range[1] + viewportHeightFactor*2;
// Load slides that are within the preload range
if( shouldPreload && !page.loaded ) {
page.loaded = true;
this.Reveal.slideContent.load( page.slideElement );
}
else if( page.loaded ) {
page.loaded = false;
this.Reveal.slideContent.unload( page.slideElement );
}
// If we're within this trigger range, activate it
if( scrollProgress >= trigger.range[0] && scrollProgress <= trigger.range[1] ) {
this.activateTrigger( trigger );
activePage = trigger.page;
}
// .. otherwise deactivate
else if( trigger.active ) {
this.deactivateTrigger( trigger );
}
} );
// Each page can have its own scroll triggers, check if any of those
// need to be activated/deactivated
if( activePage ) {
activePage.scrollTriggers.forEach( ( trigger ) => {
if( scrollProgressMid >= trigger.range[0] && scrollProgressMid <= trigger.range[1] ) {
this.activateTrigger( trigger );
}
else if( trigger.active ) {
this.deactivateTrigger( trigger );
}
} );
}
// Update our visual progress indication
this.setProgressBarValue( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) );
}
/**
* Moves the progress bar playhead to the specified position.
*
* @param {number} progress 0-1
*/
setProgressBarValue( progress ) {
if( this.progressBar ) {
this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`;
this.getAllPages()
.filter( page => page.progressBarSlide )
.forEach( ( page ) => {
page.progressBarSlide.classList.toggle( 'active', page.active === true );
page.scrollTriggers.forEach( ( trigger, i ) => {
page.scrollTriggerElements[i].classList.toggle( 'active', page.active === true && trigger.active === true );
} );
} );
this.showProgressBar();
}
}
/**
* Show the progress bar and, if configured, automatically hide
* it after a delay.
*/
showProgressBar() {
this.progressBar.classList.add( 'visible' );
clearTimeout( this.hideProgressBarTimeout );
if( this.Reveal.getConfig().scrollProgress === 'auto' && !this.draggingProgressBar ) {
this.hideProgressBarTimeout = setTimeout( () => {
if( this.progressBar ) {
this.progressBar.classList.remove( 'visible' );
}
}, HIDE_SCROLLBAR_TIMEOUT );
}
}
/**
* Scroll to the previous page.
*/
prev() {
this.viewportElement.scrollTop -= this.scrollTriggerHeight;
}
/**
* Scroll to the next page.
*/
next() {
this.viewportElement.scrollTop += this.scrollTriggerHeight;
}
/**
* Scrolls the given slide element into view.
*
* @param {HTMLElement} slideElement
*/
scrollToSlide( slideElement ) {
// If the scroll view isn't active yet, queue this action
if( !this.active ) {
this.activatedCallbacks.push( () => this.scrollToSlide( slideElement ) );
}
else {
// Find the trigger for this slide
const trigger = this.getScrollTriggerBySlide( slideElement );
if( trigger ) {
// Use the trigger's range to calculate the scroll position
this.viewportElement.scrollTop = trigger.range[0] * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
}
}
}
/**
* Persists the current scroll position to session storage
* so that it can be restored.
*/
storeScrollPosition() {
clearTimeout( this.storeScrollPositionTimeout );
this.storeScrollPositionTimeout = setTimeout( () => {
sessionStorage.setItem( 'reveal-scroll-top', this.viewportElement.scrollTop );
sessionStorage.setItem( 'reveal-scroll-origin', location.origin + location.pathname );
this.storeScrollPositionTimeout = null;
}, 50 );
}
/**
* Restores the scroll position when a deck is reloader.
*/
restoreScrollPosition() {
const scrollPosition = sessionStorage.getItem( 'reveal-scroll-top' );
const scrollOrigin = sessionStorage.getItem( 'reveal-scroll-origin' );
if( scrollPosition && scrollOrigin === location.origin + location.pathname ) {
this.viewportElement.scrollTop = parseInt( scrollPosition, 10 );
}
}
/**
* Activates the given page and starts its embedded content
* if there is any.
*
* @param {object} page
*/
activatePage( page ) {
if( !page.active ) {
page.active = true;
const { slideElement, backgroundElement, contentElement, indexh, indexv } = page;
contentElement.style.display = 'block';
slideElement.classList.add( 'present' );
if( backgroundElement ) {
backgroundElement.classList.add( 'present' );
}
this.Reveal.setCurrentScrollPage( slideElement, indexh, indexv );
this.Reveal.backgrounds.bubbleSlideContrastClassToElement( slideElement, this.viewportElement );
// If this page is part of an auto-animation there will be one
// content element per auto-animated page. We need to show the
// current page and hide all others.
Array.from( contentElement.parentNode.querySelectorAll( '.scroll-page-content' ) ).forEach( sibling => {
if( sibling !== contentElement ) {
sibling.style.display = 'none';
}
});
}
}
/**
* Deactivates the page after it has been visible.
*
* @param {object} page
*/
deactivatePage( page ) {
if( page.active ) {
page.active = false;
if( page.slideElement ) page.slideElement.classList.remove( 'present' );
if( page.backgroundElement ) page.backgroundElement.classList.remove( 'present' );
}
}
activateTrigger( trigger ) {
if( !trigger.active ) {
trigger.active = true;
trigger.activate();
}
}
deactivateTrigger( trigger ) {
if( trigger.active ) {
trigger.active = false;
if( trigger.deactivate ) {
trigger.deactivate();
}
}
}
/**
* Retrieve a slide by its original h/v index (i.e. the indices the
* slide had before being linearized).
*
* @param {number} h
* @param {number} v
* @returns {HTMLElement}
*/
getSlideByIndices( h, v ) {
const page = this.getAllPages().find( page => {
return page.indexh === h && page.indexv === v;
} );
return page ? page.slideElement : null;
}
/**
* Retrieve a list of all scroll triggers for the given slide
* DOM element.
*
* @param {HTMLElement} slide
* @returns {Array}
*/
getScrollTriggerBySlide( slide ) {
return this.slideTriggers.find( trigger => trigger.page.slideElement === slide );
}
/**
* Get a list of all pages in the scroll view. This includes
* both top-level slides and auto-animate steps.
*
* @returns {Array}
*/
getAllPages() {
return this.pages.flatMap( page => [page, ...(page.autoAnimatePages || [])] );
}
onScroll() {
this.syncScrollPosition();
this.storeScrollPosition();
}
get viewportElement() {
return this.Reveal.getViewportElement();
}
}
@@ -1,4 +1,4 @@
import { extend, queryAll, closest, getMimeTypeFromFile } from '../utils/util.js'
import { extend, queryAll, closest, getMimeTypeFromFile, encodeRFC3986URI } from '../utils/util.js'
import { isMobile } from '../utils/device.js'
import fitty from 'fitty';
@@ -25,6 +25,10 @@ export default class SlideContent {
*/
shouldPreload( element ) {
if( this.Reveal.isScrollView() ) {
return true;
}
// Prefer an explicit global preload setting
let preload = this.Reveal.getConfig().preloadIframes;
@@ -108,19 +112,21 @@ export default class SlideContent {
// URL(s)
else {
backgroundContent.style.backgroundImage = backgroundImage.split( ',' ).map( background => {
return `url(${encodeURI(background.trim())})`;
// Decode URL(s) that are already encoded first
let decoded = decodeURI(background.trim());
return `url(${encodeRFC3986URI(decoded)})`;
}).join( ',' );
}
}
// Videos
else if ( backgroundVideo && !this.Reveal.isSpeakerNotes() ) {
else if ( backgroundVideo ) {
let video = document.createElement( 'video' );
if( backgroundVideoLoop ) {
video.setAttribute( 'loop', '' );
}
if( backgroundVideoMuted ) {
if( backgroundVideoMuted || this.Reveal.isSpeakerNotes() ) {
video.muted = true;
}
@@ -136,13 +142,15 @@ export default class SlideContent {
// Support comma separated lists of video sources
backgroundVideo.split( ',' ).forEach( source => {
const sourceElement = document.createElement( 'source' );
sourceElement.setAttribute( 'src', source );
let type = getMimeTypeFromFile( source );
if( type ) {
video.innerHTML += `<source src="${source}" type="${type}">`;
}
else {
video.innerHTML += `<source src="${source}">`;
sourceElement.setAttribute( 'type', type );
}
video.appendChild( sourceElement );
} );
backgroundContent.appendChild( video );
@@ -186,15 +194,14 @@ export default class SlideContent {
}
/**
* Applies JS-dependent layout helpers for the given slide,
* if there are any.
* Applies JS-dependent layout helpers for the scope.
*/
layout( slide ) {
layout( scopeElement ) {
// Autosize text with the r-fit-text class based on the
// size of its container. This needs to happen after the
// slide is visible in order to measure the text.
Array.from( slide.querySelectorAll( '.r-fit-text' ) ).forEach( element => {
Array.from( scopeElement.querySelectorAll( '.r-fit-text' ) ).forEach( element => {
fitty( element, {
minSize: 24,
maxSize: this.Reveal.getConfig().height * 0.8,
@@ -273,7 +280,9 @@ export default class SlideContent {
*/
startEmbeddedContent( element ) {
if( element && !this.Reveal.isSpeakerNotes() ) {
if( element ) {
const isSpeakerNotesWindow = this.Reveal.isSpeakerNotes();
// Restart GIFs
queryAll( element, 'img[src$=".gif"]' ).forEach( el => {
@@ -299,6 +308,9 @@ export default class SlideContent {
if( autoplay && typeof el.play === 'function' ) {
// In the speaker view we only auto-play muted media
if( isSpeakerNotesWindow && !el.muted ) return;
// If the media is ready, start playback
if( el.readyState > 1 ) {
this.startEmbeddedMedia( { target: el } );
@@ -330,27 +342,33 @@ export default class SlideContent {
}
} );
// Normal iframes
queryAll( element, 'iframe[src]' ).forEach( el => {
if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return;
}
// Don't play iframe content in the speaker view since we can't
// guarantee that it's muted
if( !isSpeakerNotesWindow ) {
this.startEmbeddedIframe( { target: el } );
} );
// Normal iframes
queryAll( element, 'iframe[src]' ).forEach( el => {
if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return;
}
// Lazy loading iframes
queryAll( element, 'iframe[data-src]' ).forEach( el => {
if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return;
}
this.startEmbeddedIframe( { target: el } );
} );
if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {
el.removeEventListener( 'load', this.startEmbeddedIframe ); // remove first to avoid dupes
el.addEventListener( 'load', this.startEmbeddedIframe );
el.setAttribute( 'src', el.getAttribute( 'data-src' ) );
}
} );
// Lazy loading iframes
queryAll( element, 'iframe[data-src]' ).forEach( el => {
if( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {
return;
}
if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {
el.removeEventListener( 'load', this.startEmbeddedIframe ); // remove first to avoid dupes
el.addEventListener( 'load', this.startEmbeddedIframe );
el.setAttribute( 'src', el.getAttribute( 'data-src' ) );
}
} );
}
}
@@ -368,8 +386,11 @@ export default class SlideContent {
isVisible = !!closest( event.target, '.present' );
if( isAttachedToDOM && isVisible ) {
event.target.currentTime = 0;
event.target.play();
// Don't restart if media is already playing
if( event.target.paused || event.target.ended ) {
event.target.currentTime = 0;
event.target.play();
}
}
event.target.removeEventListener( 'loadeddata', this.startEmbeddedMedia );
@@ -1,3 +1,10 @@
import {
SLIDE_NUMBER_FORMAT_CURRENT,
SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL,
SLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL,
SLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL
} from "../utils/constants";
/**
* Handles the display of reveal.js' optional slide number.
*/
@@ -23,7 +30,7 @@ export default class SlideNumber {
configure( config, oldConfig ) {
let slideNumberDisplay = 'none';
if( config.slideNumber && !this.Reveal.isPrintingPDF() ) {
if( config.slideNumber && !this.Reveal.isPrintView() ) {
if( config.showSlideNumber === 'all' ) {
slideNumberDisplay = 'block';
}
@@ -56,7 +63,7 @@ export default class SlideNumber {
let config = this.Reveal.getConfig();
let value;
let format = 'h.v';
let format = SLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL;
if ( typeof config.slideNumber === 'function' ) {
value = config.slideNumber( slide );
@@ -69,7 +76,7 @@ export default class SlideNumber {
// If there are ONLY vertical slides in this deck, always use
// a flattened slide number
if( !/c/.test( format ) && this.Reveal.getHorizontalSlides().length === 1 ) {
format = 'c';
format = SLIDE_NUMBER_FORMAT_CURRENT;
}
// Offset the current slide number by 1 to make it 1-indexed
@@ -77,16 +84,16 @@ export default class SlideNumber {
value = [];
switch( format ) {
case 'c':
case SLIDE_NUMBER_FORMAT_CURRENT:
value.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset );
break;
case 'c/t':
case SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL:
value.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset, '/', this.Reveal.getTotalSlides() );
break;
default:
let indices = this.Reveal.getIndices( slide );
value.push( indices.h + horizontalOffset );
let sep = format === 'h/v' ? '/' : '.';
let sep = format === SLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL ? '/' : '.';
if( this.Reveal.isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );
}
}
+3 -1
View File
@@ -84,7 +84,7 @@ export default class Touch {
isSwipePrevented( target ) {
// Prevent accidental swipes when scrubbing timelines
if( matches( target, 'video, audio' ) ) return true;
if( matches( target, 'video[controls], audio[controls]' ) ) return true;
while( target && typeof target.hasAttribute === 'function' ) {
if( target.hasAttribute( 'data-prevent-swipe' ) ) return true;
@@ -103,6 +103,8 @@ export default class Touch {
*/
onTouchStart( event ) {
this.touchCaptured = false;
if( this.isSwipePrevented( event.target ) ) return true;
this.touchStartX = event.touches[0].clientX;
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -44,7 +44,7 @@ export const colorToRgb = ( color ) => {
};
}
let rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i );
let rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i );
if( rgba ) {
return {
r: parseInt( rgba[1], 10 ),
+9 -2
View File
@@ -2,9 +2,16 @@
export const SLIDES_SELECTOR = '.slides section';
export const HORIZONTAL_SLIDES_SELECTOR = '.slides>section';
export const VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section';
export const HORIZONTAL_BACKGROUNDS_SELECTOR = '.backgrounds>.slide-background';
// Methods that may not be invoked via the postMessage API
export const POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/;
export const POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener|showPreview/;
// Regex for retrieving the fragment style from a class attribute
export const FRAGMENT_STYLE_REGEX = /fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;
export const FRAGMENT_STYLE_REGEX = /fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;
// Slide number formats
export const SLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL = 'h.v';
export const SLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL = 'h/v';
export const SLIDE_NUMBER_FORMAT_CURRENT = 'c';
export const SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL = 'c/t';
+1 -8
View File
@@ -1,15 +1,8 @@
const UA = navigator.userAgent;
const testElement = document.createElement( 'div' );
export const isMobile = /(iphone|ipod|ipad|android)/gi.test( UA ) ||
( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS
export const isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );
export const isAndroid = /android/gi.test( UA );
// Flags if we should use zoom instead of transform to scale
// up slides. Zoom produces crisper results but has a lot of
// xbrowser quirks so we only use it in whitelisted browsers.
export const supportsZoom = 'zoom' in testElement.style && !isMobile &&
( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) );
export const isAndroid = /android/gi.test( UA );
+16
View File
@@ -294,4 +294,20 @@ const fileExtensionToMimeMap = {
*/
export const getMimeTypeFromFile = ( filename='' ) => {
return fileExtensionToMimeMap[filename.split('.').pop()]
}
/**
* Encodes a string for RFC3986-compliant URL format.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#encoding_for_rfc3986
*
* @param {string} url
*/
export const encodeRFC3986URI = ( url='' ) => {
return encodeURI(url)
.replace(/%5B/g, "[")
.replace(/%5D/g, "]")
.replace(
/[!'()*]/g,
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
);
}
+4819 -12692
View File
File diff suppressed because it is too large Load Diff
+34 -26
View File
@@ -1,6 +1,6 @@
{
"name": "reveal.js",
"version": "4.3.1",
"version": "5.2.1",
"description": "The HTML Presentation Framework",
"homepage": "https://revealjs.com",
"subdomain": "revealjs",
@@ -22,7 +22,7 @@
"url": "git://github.com/hakimel/reveal.js.git"
},
"engines": {
"node": ">=10.0.0"
"node": ">=18.0.0"
},
"keywords": [
"reveal",
@@ -30,35 +30,43 @@
"presentation"
],
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/eslint-parser": "^7.14.3",
"@babel/preset-env": "^7.14.2",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"babel-plugin-transform-html-import-to-string": "0.0.1",
"@babel/core": "^7.23.2",
"@babel/eslint-parser": "^7.22.15",
"@babel/preset-env": "^7.23.2",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"babel-plugin-transform-html-import-to-string": "2.0.0",
"colors": "^1.4.0",
"core-js": "^3.12.1",
"fitty": "^2.3.0",
"glob": "^7.1.7",
"gulp": "^4.0.2",
"core-js": "^3.33.1",
"fitty": "^2.3.7",
"glob": "^10.3.10",
"gulp": "^5.0.0",
"gulp-autoprefixer": "^8.0.0",
"gulp-clean-css": "^4.2.0",
"gulp-clean-css": "^4.3.0",
"gulp-connect": "^5.7.0",
"gulp-eslint": "^6.0.0",
"gulp-header": "^2.0.9",
"gulp-tap": "^2.0.0",
"gulp-zip": "^4.2.0",
"highlight.js": "^10.0.3",
"marked": "^4.0.12",
"node-qunit-puppeteer": "^2.1.0",
"qunit": "^2.17.2",
"rollup": "^2.48.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.39.2",
"yargs": "^15.1.0"
"gulp-header-comment": "^0.10.0",
"gulp-zip": "^5.1.0",
"highlight.js": "^11.9.0",
"marked": "^4.3.0",
"node-qunit-puppeteer": "^2.2.0",
"through2": "^4.0.2",
"qunit": "^2.22.0",
"rollup": "^4.1.5",
"sass": "^1.79.4",
"yargs": "^17.7.2"
},
"browserslist": "> 0.5%, IE 11, not dead",
"overrides": {
"gulp-connect": {
"send": "0.19.0"
},
"gulp-header-comment": {
"moment": "2.30.1"
}
},
"browserslist": "> 2%, not dead",
"eslintConfig": {
"env": {
"browser": true,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -19,7 +19,7 @@ const Plugin = {
hljs,
/**
* Highlights code blocks withing the given deck.
* Highlights code blocks within the given deck.
*
* Note that this can be called multiple times if
* there are multiple presentations on one page.
@@ -52,7 +52,7 @@ const Plugin = {
block.innerHTML = betterTrim( block );
}
// Escape HTML tags unless the "data-noescape" attrbute is present
// Escape HTML tags unless the "data-noescape" attribute is present
if( config.escapeHTML && !block.hasAttribute( 'data-noescape' )) {
block.innerHTML = block.innerHTML.replace( /</g,"&lt;").replace(/>/g, '&gt;' );
}
@@ -138,7 +138,7 @@ const Plugin = {
// Scroll highlights into view as we step through them
fragmentBlock.addEventListener( 'visible', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock, scrollState ) );
fragmentBlock.addEventListener( 'hidden', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousSibling, scrollState ) );
fragmentBlock.addEventListener( 'hidden', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousElementSibling, scrollState ) );
} );
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+64 -48
View File
@@ -7,13 +7,16 @@
import { marked } from 'marked';
const DEFAULT_SLIDE_SEPARATOR = '\r?\n---\r?\n',
DEFAULT_NOTES_SEPARATOR = 'notes?:',
DEFAULT_VERTICAL_SEPARATOR = null,
DEFAULT_NOTES_SEPARATOR = '^\s*notes?:',
DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
const SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';
const CODE_LINE_NUMBER_REGEX = /\[([\s\d,|-]*)\]/;
// match an optional line number offset and highlight line numbers
// [<line numbers>] or [<offset>: <line numbers>]
const CODE_LINE_NUMBER_REGEX = /\[\s*((\d*):)?\s*([\s\d,|-]*)\]/;
const HTML_ESCAPE_MAP = {
'&': '&amp;',
@@ -35,22 +38,22 @@ const Plugin = () => {
function getMarkdownFromSlide( section ) {
// look for a <script> or <textarea data-template> wrapper
var template = section.querySelector( '[data-template]' ) || section.querySelector( 'script' );
const template = section.querySelector( '[data-template]' ) || section.querySelector( 'script' );
// strip leading whitespace so it isn't evaluated as code
var text = ( template || section ).textContent;
let text = ( template || section ).textContent;
// restore script end tags
text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );
var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
const leadingWs = text.match( /^\n?(\s*)/ )[1].length,
leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
if( leadingTabs > 0 ) {
text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}(.*)','g'), function(m, p1) { return '\n' + p1 ; } );
}
else if( leadingWs > 1 ) {
text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
text = text.replace( new RegExp('\\n? {' + leadingWs + '}(.*)', 'g'), function(m, p1) { return '\n' + p1 ; } );
}
return text;
@@ -65,11 +68,11 @@ const Plugin = () => {
*/
function getForwardedAttributes( section ) {
var attributes = section.attributes;
var result = [];
const attributes = section.attributes;
const result = [];
for( var i = 0, len = attributes.length; i < len; i++ ) {
var name = attributes[i].name,
for( let i = 0, len = attributes.length; i < len; i++ ) {
const name = attributes[i].name,
value = attributes[i].value;
// disregard attributes that are used for markdown loading/parsing
@@ -92,10 +95,12 @@ const Plugin = () => {
* values for what's not defined.
*/
function getSlidifyOptions( options ) {
const markdownConfig = deck?.getConfig?.().markdown;
options = options || {};
options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
options.separator = options.separator || markdownConfig?.separator || DEFAULT_SLIDE_SEPARATOR;
options.verticalSeparator = options.verticalSeparator || markdownConfig?.verticalSeparator || DEFAULT_VERTICAL_SEPARATOR;
options.notesSeparator = options.notesSeparator || markdownConfig?.notesSeparator || DEFAULT_NOTES_SEPARATOR;
options.attributes = options.attributes || '';
return options;
@@ -109,7 +114,7 @@ const Plugin = () => {
options = getSlidifyOptions( options );
var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
const notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
if( notesMatch.length === 2 ) {
content = notesMatch[0] + '<aside class="notes">' + marked(notesMatch[1].trim()) + '</aside>';
@@ -131,10 +136,10 @@ const Plugin = () => {
options = getSlidifyOptions( options );
var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
const separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
horizontalSeparatorRegex = new RegExp( options.separator );
var matches,
let matches,
lastIndex = 0,
isHorizontal,
wasHorizontal = true,
@@ -143,7 +148,7 @@ const Plugin = () => {
// iterate until all blocks between separators are stacked up
while( matches = separatorRegex.exec( markdown ) ) {
var notes = null;
const notes = null;
// determine direction (horizontal by default)
isHorizontal = horizontalSeparatorRegex.test( matches[0] );
@@ -172,10 +177,10 @@ const Plugin = () => {
// add the remaining slide
( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
var markdownSections = '';
let markdownSections = '';
// flatten the hierarchical stack, and insert <section data-markdown> tags
for( var i = 0, len = sectionStack.length; i < len; i++ ) {
for( let i = 0, len = sectionStack.length; i < len; i++ ) {
// vertical
if( sectionStack[i] instanceof Array ) {
markdownSections += '<section '+ options.attributes +'>';
@@ -204,7 +209,7 @@ const Plugin = () => {
return new Promise( function( resolve ) {
var externalPromises = [];
const externalPromises = [];
[].slice.call( scope.querySelectorAll( 'section[data-markdown]:not([data-markdown-parsed])') ).forEach( function( section, i ) {
@@ -257,13 +262,13 @@ const Plugin = () => {
return new Promise( function( resolve, reject ) {
var xhr = new XMLHttpRequest(),
const xhr = new XMLHttpRequest(),
url = section.getAttribute( 'data-markdown' );
var datacharset = section.getAttribute( 'data-charset' );
const datacharset = section.getAttribute( 'data-charset' );
// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
if( datacharset != null && datacharset != '' ) {
if( datacharset !== null && datacharset !== '' ) {
xhr.overrideMimeType( 'text/html; charset=' + datacharset );
}
@@ -308,17 +313,17 @@ const Plugin = () => {
*/
function addAttributeInElement( node, elementTarget, separator ) {
var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"]+?)\"|(data-[^\"= ]+?)(?=[\" ])", 'mg' );
var nodeValue = node.nodeValue;
var matches,
const markdownClassesInElementsRegex = new RegExp( separator, 'mg' );
const markdownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"]+?)\"|(data-[^\"= ]+?)(?=[\" ])", 'mg' );
let nodeValue = node.nodeValue;
let matches,
matchesClass;
if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
if( matches = markdownClassesInElementsRegex.exec( nodeValue ) ) {
var classes = matches[1];
nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
const classes = matches[1];
nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( markdownClassesInElementsRegex.lastIndex );
node.nodeValue = nodeValue;
while( matchesClass = mardownClassRegex.exec( classes ) ) {
while( matchesClass = markdownClassRegex.exec( classes ) ) {
if( matchesClass[2] ) {
elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
} else {
@@ -336,34 +341,34 @@ const Plugin = () => {
*/
function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
var previousParentElement = element;
for( var i = 0; i < element.childNodes.length; i++ ) {
var childElement = element.childNodes[i];
if ( element !== null && element.childNodes !== undefined && element.childNodes.length > 0 ) {
let previousParentElement = element;
for( let i = 0; i < element.childNodes.length; i++ ) {
const childElement = element.childNodes[i];
if ( i > 0 ) {
var j = i - 1;
let j = i - 1;
while ( j >= 0 ) {
var aPreviousChildElement = element.childNodes[j];
if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
const aPreviousChildElement = element.childNodes[j];
if ( typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== "BR" ) {
previousParentElement = aPreviousChildElement;
break;
}
j = j - 1;
}
}
var parentSection = section;
if( childElement.nodeName == "section" ) {
let parentSection = section;
if( childElement.nodeName === "section" ) {
parentSection = childElement ;
previousParentElement = childElement ;
}
if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
if ( typeof childElement.setAttribute === 'function' || childElement.nodeType === Node.COMMENT_NODE ) {
addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
}
}
}
if ( element.nodeType == Node.COMMENT_NODE ) {
if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
if ( element.nodeType === Node.COMMENT_NODE ) {
if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) === false ) {
addAttributeInElement( element, section, separatorSectionAttributes );
}
}
@@ -375,14 +380,14 @@ const Plugin = () => {
*/
function convertSlides() {
var sections = deck.getRevealElement().querySelectorAll( '[data-markdown]:not([data-markdown-parsed])');
const sections = deck.getRevealElement().querySelectorAll( '[data-markdown]:not([data-markdown-parsed])');
[].slice.call( sections ).forEach( function( section ) {
section.setAttribute( 'data-markdown-parsed', true )
var notes = section.querySelector( 'aside.notes' );
var markdown = getMarkdownFromSlide( section );
const notes = section.querySelector( 'aside.notes' );
const markdown = getMarkdownFromSlide( section );
section.innerHTML = marked( markdown );
addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||
@@ -429,14 +434,23 @@ const Plugin = () => {
renderer.code = ( code, language ) => {
// Off by default
let lineNumberOffset = '';
let lineNumbers = '';
// Users can opt in to show line numbers and highlight
// specific lines.
// ```javascript [] show line numbers
// ```javascript [1,4-8] highlights lines 1 and 4-8
// optional line number offset:
// ```javascript [25: 1,4-8] start line numbering at 25,
// highlights lines 1 (numbered as 25) and 4-8 (numbered as 28-32)
if( CODE_LINE_NUMBER_REGEX.test( language ) ) {
lineNumbers = language.match( CODE_LINE_NUMBER_REGEX )[1].trim();
let lineNumberOffsetMatch = language.match( CODE_LINE_NUMBER_REGEX )[2];
if (lineNumberOffsetMatch){
lineNumberOffset = `data-ln-start-from="${lineNumberOffsetMatch.trim()}"`;
}
lineNumbers = language.match( CODE_LINE_NUMBER_REGEX )[3].trim();
lineNumbers = `data-line-numbers="${lineNumbers}"`;
language = language.replace( CODE_LINE_NUMBER_REGEX, '' ).trim();
}
@@ -446,7 +460,9 @@ const Plugin = () => {
// highlight.js is able to read it
code = escapeForHTML( code );
return `<pre><code ${lineNumbers} class="${language}">${code}</code></pre>`;
// return `<pre><code ${lineNumbers} class="${language}">${code}</code></pre>`;
return `<pre><code ${lineNumbers} ${lineNumberOffset} class="${language}">${code}</code></pre>`;
};
}
+1 -1
View File
@@ -16,7 +16,7 @@ export const KaTeX = () => {
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre']
ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
}
const loadCss = src => {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -13,7 +13,7 @@ export const MathJax2 = () => {
messageStyle: 'none',
tex2jax: {
inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ],
skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre', 'code' ]
},
skipStartupTypeset: true
};
+3 -3
View File
@@ -15,13 +15,13 @@ export const MathJax3 = () => {
inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ]
},
options: {
skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre', 'code' ]
},
startup: {
ready: () => {
MathJax.startup.defaultReady();
MathJax.startup.promise.then(() => {
Reveal.layout();
deck.layout();
});
}
}
@@ -66,7 +66,7 @@ export const MathJax3 = () => {
loadScript( url, function() {
// Reprocess equations in slides when they turn visible
Reveal.addEventListener( 'slidechanged', function( event ) {
deck.addEventListener( 'slidechanged', function( event ) {
MathJax.typeset();
} );
} );
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+50 -15
View File
@@ -1,4 +1,4 @@
import speakerViewHTML from './speaker-view.html';
import speakerViewHTML from './speaker-view.html'
import { marked } from 'marked';
@@ -108,7 +108,7 @@ const Plugin = () => {
function post( event ) {
let slideElement = deck.getCurrentSlide(),
notesElement = slideElement.querySelector( 'aside.notes' ),
notesElements = slideElement.querySelectorAll( 'aside.notes' ),
fragmentElement = slideElement.querySelector( '.current-fragment' );
let messageData = {
@@ -130,36 +130,67 @@ const Plugin = () => {
if( fragmentElement ) {
let fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
if( fragmentNotes ) {
notesElement = fragmentNotes;
messageData.notes = fragmentNotes.innerHTML;
messageData.markdown = typeof fragmentNotes.getAttribute( 'data-markdown' ) === 'string';
// Ignore other slide notes
notesElements = null;
}
else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
messageData.notes = fragmentElement.getAttribute( 'data-notes' );
messageData.whitespace = 'pre-wrap';
// In case there are slide notes
notesElement = null;
notesElements = null;
}
}
// Look for notes defined in an aside element
if( notesElement ) {
messageData.notes = notesElement.innerHTML;
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
if( notesElements && notesElements.length ) {
// Ignore notes inside of fragments since those are shown
// individually when stepping through fragments
notesElements = Array.from( notesElements ).filter( notesElement => notesElement.closest( '.fragment' ) === null );
messageData.notes = notesElements.map( notesElement => notesElement.innerHTML ).join( '\n' );
messageData.markdown = notesElements[0] && typeof notesElements[0].getAttribute( 'data-markdown' ) === 'string';
}
speakerWindow.postMessage( JSON.stringify( messageData ), '*' );
}
/**
* Check if the given event is from the same origin as the
* current window.
*/
function isSameOriginEvent( event ) {
try {
return window.location.origin === event.source.location.origin;
}
catch ( error ) {
return false;
}
}
function onPostMessage( event ) {
let data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
callRevealApi( data.methodName, data.arguments, data.callId );
// Only allow same-origin messages
// (added 12/5/22 as a XSS safeguard)
if( isSameOriginEvent( event ) ) {
try {
let data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
callRevealApi( data.methodName, data.arguments, data.callId );
}
} catch (e) {}
}
}
@@ -178,6 +209,10 @@ const Plugin = () => {
deck.on( 'overviewshown', post );
deck.on( 'paused', post );
deck.on( 'resumed', post );
deck.on( 'previewiframe', post );
deck.on( 'previewimage', post );
deck.on( 'previewvideo', post );
deck.on( 'closeoverlay', post );
// Post the initial state
post();
@@ -198,7 +233,7 @@ const Plugin = () => {
openSpeakerWindow();
}
else {
// Keep listening for speaker view hearbeats. If we receive a
// Keep listening for speaker view heartbeats. If we receive a
// heartbeat from an orphaned window, reconnect it. This ensures
// that we remain connected to the notes even if the presentation
// is reloaded.
@@ -350,8 +350,9 @@
layoutDropdown,
pendingCalls = {},
lastRevealApiCallId = 0,
connected = false,
whitelistedWindows = [window.opener];
connected = false
var connectionStatus = document.querySelector( '#connection-status' );
var SPEAKER_LAYOUTS = {
'default': 'Default',
@@ -362,16 +363,31 @@
setupLayout();
var connectionStatus = document.querySelector( '#connection-status' );
let openerOrigin;
try {
openerOrigin = window.opener.location.origin;
}
catch ( error ) { console.warn( error ) }
// In order to prevent XSS, the speaker view will only run if its
// opener has the same origin as itself
if( window.location.origin !== openerOrigin ) {
connectionStatus.innerHTML = 'Cross origin error.<br>The speaker window can only be opened from the same origin.';
return;
}
var connectionTimeout = setTimeout( function() {
connectionStatus.innerHTML = 'Error connecting to main window.<br>Please try closing and reopening the speaker view.';
}, 5000 );
;
window.addEventListener( 'message', function( event ) {
// Validate the origin of this message to prevent XSS
if( window.location.origin !== event.origin && whitelistedWindows.indexOf( event.source ) === -1 ) {
return;
// Validate the origin of all messages to avoid parsing messages
// that aren't meant for us. Ignore when running off file:// so
// that the speaker view continues to work without a web server.
if( window.location.origin !== event.origin && window.location.origin !== 'file://' ) {
return
}
clearTimeout( connectionTimeout );
@@ -398,14 +414,24 @@
}
// Messages sent by the reveal.js inside of the current slide preview
else if( data && data.namespace === 'reveal' ) {
const supportedEvents = [
'slidechanged',
'fragmentshown',
'fragmenthidden',
'paused',
'resumed',
'previewiframe',
'previewimage',
'previewvideo',
'closeoverlay'
];
if( /ready/.test( data.eventName ) ) {
// Send a message back to notify that the handshake is complete
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );
}
else if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
else if( supportedEvents.includes( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
dispatchStateToMainWindow( data.state );
}
}
@@ -478,9 +504,12 @@
notes.classList.add( 'hidden' );
}
// Don't show lightboxes in the upcoming slide
const { previewVideo, previewImage, previewIframe, ...upcomingState } = data.state;
// Update the note slides
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ upcomingState ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
}
@@ -524,8 +553,8 @@
var urlSeparator = /\?/.test(data.url) ? '&' : '?';
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;
var currentURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&controls=false' + hash;
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );
@@ -539,8 +568,6 @@
upcomingSlide.setAttribute( 'src', upcomingURL );
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
whitelistedWindows.push( currentSlide.contentWindow, upcomingSlide.contentWindow );
}
/**
+4 -2
View File
@@ -137,7 +137,7 @@ const Plugin = () => {
this.setRegex = function(input)
{
input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
input = input.trim();
matchRegex = new RegExp("(" + input + ")","i");
}
@@ -235,7 +235,9 @@ const Plugin = () => {
},
open: openSearch
open: openSearch,
close: closeSearch,
toggle: toggleSearch
}
};
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More