1
0
mirror of https://github.com/hakimel/reveal.js.git synced 2025-09-08 13:40:48 +02:00

Compare commits

...

103 Commits

Author SHA1 Message Date
Hakim El Hattab
8bb6674303 fix videos not autoplaying when using control arrows in android 2025-09-04 10:15:17 +02:00
Hakim El Hattab
1427059378 fix initial video autoplay not working in android 2025-09-03 10:34:22 +02:00
Hakim El Hattab
4cf184924d Merge pull request #3810 from phwebi/update-display-config
Update `display` config to support `!important` flag.
2025-08-28 12:01:20 +02:00
Hakim El Hattab
e8cd04da83 fix bug where multiple videos started simultaneously sometimes failed to render in mobile safari 2025-08-26 19:49:24 +02:00
Hakim El Hattab
5412639a54 accessibility improvements; announce img/video alt tags, punctuate screen reader text content (#3757, #3772) 2025-08-05 10:24:45 +02:00
Phoebe Gao
8003efe030 feat: support important property 2025-08-04 11:51:57 -07:00
Hakim El Hattab
eb95b14531 overview mode: fix missing thumbs in adjacent stacks with over 10 vertical slides (closes #3754) 2025-04-28 13:24:53 +02:00
Hakim El Hattab
dfc5690c6d add missing font to dracula theme, fixes #3781 2025-03-28 14:15:33 +01:00
Hakim El Hattab
b03a94c6fc distinct active/hover state difference in overview mode #3780 2025-03-28 14:14:59 +01:00
Hakim El Hattab
23926edb87 Merge pull request #3778 from tobi-or-not-tobi/feature/open-active-slide-on-enter
feat: Open the active slide from the overview screen on enter
2025-03-28 14:04:02 +01:00
Hakim El Hattab
70daf8fce6 Merge branch 'master' into feature/open-active-slide-on-enter 2025-03-28 14:03:49 +01:00
Hakim El Hattab
25e52e26af release 5.2.1 2025-03-28 13:51:23 +01:00
Hakim El Hattab
7e96e9ce0a dont show lightboxes over upcoming slide in speaker view 2025-03-28 13:38:58 +01:00
Hakim El Hattab
2f76a34897 add lightbox example to demo deck 2025-03-26 15:08:44 +01:00
tobi-or-not-tobi
9612f7bde0 open the active slide from the overview screen on enter key event 2025-03-25 20:55:36 +01:00
Hakim El Hattab
722b14b89f reduce lightbox header in small windows 2025-03-25 11:58:07 +01:00
Hakim El Hattab
1923a5c7a4 lightbox refactor 2025-03-25 11:14:46 +01:00
Hakim El Hattab
aa9dfc7eb8 sync lightbox between speaker/main window (fixes #3771) 2025-03-25 10:42:41 +01:00
Hakim El Hattab
ac4064b64d Merge pull request #3776 from tobi-or-not-tobi/feature/shift-click-preview-link
Allow users to use meta keys when navigating to preview links
2025-03-25 10:11:34 +01:00
tobi-or-not-tobi
9f7256fe78 Allow users to use meta keys when navigating to preview links 2025-03-25 08:29:03 +01:00
Hakim El Hattab
5c77e86301 Merge pull request #3768 from tobi-or-not-tobi/fix/move-pause-overlay-over-other-overlay
fix: move pause overlay over other overlay
2025-03-24 15:38:27 +01:00
tobi-or-not-tobi
0121173213 fix z-index 2025-03-24 08:56:11 +01:00
tobi-or-not-tobi
7a62643c6a z-index 2025-03-21 15:26:38 +01:00
tobi-or-not-tobi
198e206ad0 build files 2025-03-21 07:17:08 +01:00
tobi-or-not-tobi
1e0a2a7d4a fix pause overlay 2025-03-21 07:09:21 +01:00
Hakim El Hattab
47ee25dd19 build assets 2025-03-20 13:58:22 +01:00
Hakim El Hattab
94716f9e51 add support for data-preview-link on any element type (prev only a) 2025-03-20 13:52:28 +01:00
Hakim El Hattab
b3fd27d071 prevent default keyboard shortcuts while overlay is open #3766 #3767 2025-03-20 11:38:20 +01:00
Hakim El Hattab
edb69c840c update version to 5.2.0 2025-03-19 12:03:33 +01:00
Hakim El Hattab
f11812e0a2 fix theme css conflict 2025-03-19 11:59:39 +01:00
Hakim El Hattab
d5d292f70a config tweak for lightbox demo 2025-03-19 11:29:36 +01:00
Hakim El Hattab
a7b4bb4946 fix css help overlay css conflict 2025-03-19 11:11:35 +01:00
Hakim El Hattab
659472f8a4 rebuild math plugin 2025-03-19 10:53:35 +01:00
Hakim El Hattab
2ac0566941 dont restart background video when it hasn't changed (fixes #3633) 2025-03-19 10:33:13 +01:00
Hakim El Hattab
657543ac96 rename actions 2025-03-19 10:16:05 +01:00
Hakim El Hattab
e7456847c6 spelling fix 2025-03-19 10:14:10 +01:00
Hakim El Hattab
255d55ab8f Merge pull request #3602 from yarikoptic/enh-codespell
codespell: add config + workflow and make it fix some typos it finds
2025-03-19 10:11:48 +01:00
Hakim El Hattab
60401a2740 npm audit fix 2025-03-19 10:02:56 +01:00
Hakim El Hattab
9c6bd42939 Merge pull request #3744 from KlavierCat/fix-security-vulnerability
fix: bump 2 deps to patch security vulnerabilities
2025-03-19 10:02:18 +01:00
Hakim El Hattab
36daed927e update readme 2025-03-14 08:46:56 +01:00
Hakim El Hattab
2059d388f7 dispatch event when showing lightbox or help overlay 2025-02-20 10:30:31 +01:00
Hakim El Hattab
fe67bad092 .overlay -> .r-overlay to reduce risk of conflict, move overlay up to viewport level 2025-02-17 19:59:40 +01:00
Hakim El Hattab
9af726b606 brief transition for overlay 2025-02-17 19:08:43 +01:00
Hakim El Hattab
0950590300 more flexible styling system for overlays 2025-02-05 15:57:14 +01:00
Hakim El Hattab
087eed8dc3 overlay mode refactoring 2025-02-05 14:38:53 +01:00
KlavierCat
8f19e13ab1 fix: bump 2 deps to patch security vulnerabilities 2025-01-31 15:55:28 +00:00
Hakim El Hattab
1b2c39a86e add support image/video lightbox via data-preview-image/video, move overlay into standalone controller 2025-01-31 16:10:15 +01:00
Hakim El Hattab
0524ae855d further refinements to overlay styles 2025-01-29 15:19:47 +01:00
Hakim El Hattab
3ceac9402d remove outdated ff bug fix 2025-01-29 14:34:29 +01:00
Hakim El Hattab
6cebb771ee switch to dvh for reveal.js height, fixes issue with presentations not covering fullviewport in ios 2025-01-24 09:16:36 +01:00
Hakim El Hattab
31ba65ce86 refactored overlays; new icons and reduced selector specificity 2025-01-24 09:15:47 +01:00
Hakim El Hattab
983c6248f7 update controls comment to match other prop descriptions 2025-01-13 13:16:02 +01:00
Hakim El Hattab
6527f4d912 Merge pull request #3716 from gpotter2/controls-for-speaker
Add 'controlsOnlyForSpeaker' option
2025-01-13 13:13:30 +01:00
gpotter2
3751715414 Add controls: 'speaker-only' option 2025-01-11 16:41:06 +01:00
Hakim El Hattab
e15cf92ccd update specs for 6dea2a5094 2024-11-28 14:53:25 +01:00
Hakim El Hattab
6dea2a5094 auto-animate no longer skips matching fragments on adjacent slides 2024-11-28 14:44:01 +01:00
Hakim El Hattab
0d02d8a303 default mathjax config now ignores code tags 2024-11-18 08:26:23 +01:00
Hakim El Hattab
fe4a6e82b0 fix last slide not triggering slidechange in scroll view (closes #3715) 2024-11-11 16:05:34 +01:00
Hakim El Hattab
5a275f223b don't parse math equations in code blocks 2024-11-11 15:35:16 +01:00
Hakim El Hattab
f979ff68e9 fix bug where hiding all except 1 slide in a stack caused controls to break 2024-10-29 15:39:27 +01:00
Hakim El Hattab
a6417ae747 Merge pull request #3701 from dennybiasiolli/fixing-gulp-package
gulp package: fixing encoding before piping to zip
2024-10-29 13:21:51 +01:00
Denny Biasiolli
c9ad332057 gulp package: fixing encoding after updating to gulp 5
Took inspiration from this issue: https://github.com/sindresorhus/gulp-zip/issues/123
2024-10-26 14:00:00 +02:00
Hakim El Hattab
16ac4b0067 prevent double-initialization #3696 2024-10-25 10:27:54 +02:00
Hakim El Hattab
96ca819770 fix gulp package #3693 2024-10-25 09:57:14 +02:00
Hakim El Hattab
95946b4ec6 autoplay muted background videos in speaker view #1037 2024-10-21 14:27:51 +02:00
Hakim El Hattab
8d7b03c886 fix two npm audit warnings 2024-10-11 14:58:40 +02:00
Hakim El Hattab
b8bb94f788 upgrade to gulp 5.0, latest sass, & node-qunit-puppeteer #3608 2024-10-11 14:50:22 +02:00
Hakim El Hattab
8e58d1b7db scroll demo tweak 2024-10-11 13:40:54 +02:00
Hakim El Hattab
669cc25e55 Merge pull request #3685 from lechten/extend-search-API
Extend search API
2024-09-30 10:41:34 +02:00
Jens Lechtenbörger
2dcbf2745e Extend search API
Previously, only openSearch() was exported.  For symmetry, add
closeSearch().  For convenience, also add toggleSearch().
2024-09-29 12:48:39 +02:00
Hakim El Hattab
472535065c only destroy if reveal instance is ready, don't proceed with initialization after destroy is called, tests #3593 2024-05-15 11:15:31 +02:00
Hakim El Hattab
7cab93baab Merge pull request #3620 from wainuiomata/typos
Fix typo in jsdoc: presentation
2024-05-15 10:30:39 +02:00
Hakim El Hattab
15d9b650a5 Merge pull request #3618 from wainuiomata/regex-redundant-escape
Fix escape before comma in regex is redundant
2024-05-15 10:30:23 +02:00
Rob van der Linde
ab760babb7 Fix typo in jsdoc: presentation 2024-04-30 19:41:47 +12:00
Rob van der Linde
2d273bf06c Fix escape before comma in regex is redundant
There are various other commas in the same regex that aren't escaped.

This one doesn't need escaping either.
2024-04-30 19:35:39 +12:00
Hakim El Hattab
6b8c64ffa8 5.1.0 2024-04-11 08:45:27 +02:00
Hakim El Hattab
dab6ef6b38 Merge pull request #3603 from lechten/fix-help
Re-add question mark for help
2024-04-09 09:19:20 +02:00
Jens Lechtenbörger
092a34bf0c Re-add question mark for help 2024-04-01 16:12:15 +02:00
Yaroslav Halchenko
e0bc3f764d [DATALAD RUNCMD] run codespell throughout fixing typos automagically
=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^
2024-03-29 14:49:42 -04:00
Yaroslav Halchenko
091fede288 [DATALAD RUNCMD] Do interactive fixing of leftover ambigous typos
=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w -i 3 -C 2 ./plugin/zoom/plugin.js plugin/notes/plugin.js",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^
2024-03-29 14:49:40 -04:00
Yaroslav Halchenko
e8ddb101de Adjust codespell config to minimize false positives etc 2024-03-29 14:48:02 -04:00
Yaroslav Halchenko
f9cf4f5cfe Add rudimentary codespell config 2024-03-29 14:44:08 -04:00
Yaroslav Halchenko
1cc02565b0 Add github action to codespell master on push and PRs 2024-03-29 14:44:08 -04:00
Hakim El Hattab
6410c756ea auto-animate demo tweak 2024-03-25 09:10:26 +01:00
Hakim El Hattab
62297e6259 nil check slides before running auto-animate transition #3592 2024-03-25 09:01:53 +01:00
Hakim El Hattab
ade53094b9 Merge pull request #3598 from alifeee/fix/r-stack-chrome
Fix `r-stack` overflow behaviour on Chromium browsers
2024-03-25 08:52:32 +01:00
alifeee
164254655b update build 2024-03-24 23:13:36 +00:00
alifeee
e2344787c4 fix r-stack with grid-template-rows: 100%; 2024-03-24 23:11:22 +00:00
Hakim El Hattab
334abff10f Merge pull request #3595 from jokester/mathjax3-fix-for-multiple-instances 2024-03-23 12:27:59 +01:00
Wang Guan
19c1bca1e4 MathJax3: allow non-singleton Reveal instance 2024-03-23 17:22:23 +09:00
Hakim El Hattab
0799c8f674 fix exception when destroying uninitialized reveal instance (closes #3593) 2024-03-22 14:29:35 +01:00
Hakim El Hattab
924bdb6305 fix vertical swipe navigation not blocking page scrolling in embedded decks 2024-03-19 09:09:44 +01:00
Hakim El Hattab
d4e5c39fe4 Merge pull request #3588 from NatKarmios/notes-error-catch
Fix error when the notes plugin receives a non-string message
2024-03-15 09:02:32 +01:00
Nat Karmios
2fb4b46307 Notes: don't error on non-string message 2024-03-14 18:18:33 +00:00
Hakim El Hattab
488c5c8f94 fix rtl prev/next navigation on slides with fragments 2024-03-13 15:15:59 +01:00
Hakim El Hattab
421da63750 fix previous bg video playing in background 2024-03-12 10:54:28 +01:00
Hakim El Hattab
62b1ea302c don't start video bgs if autoPlayMedia config is set to false 2024-03-11 14:20:01 +01:00
Hakim El Hattab
76ec60a137 fix issue when disabling autoPlay config flag at runtime 2024-03-11 13:24:27 +01:00
Hakim El Hattab
1748a55ece don't restart media when it's already playing #2882 2024-03-11 11:24:00 +01:00
Hakim El Hattab
a29a9c71ae allow same background video to continue playing across multiple slides #3189 #2882
Co-authored-by: Chi Vong <chivongv@gmail.com>
2024-03-08 14:02:26 +01:00
Hakim El Hattab
6ef138b61f dont prevent swipe navigation on video backgrounds #3584 2024-03-07 10:26:01 +01:00
Hakim El Hattab
63e0a37a88 fix broken backwards navigation in rtl mode 2024-03-06 10:55:09 +01:00
Hakim El Hattab
2927be34d8 new .enter-fullscreen class lets you add shortcuts to fullscreen mode 2024-02-28 11:08:56 +01:00
82 changed files with 3139 additions and 3672 deletions

8
.codespellrc Normal file
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 =

23
.github/workflows/spellcheck.yml vendored Normal file
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

View File

@@ -1,4 +1,4 @@
name: tests
name: Tests
on:
- push

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/).
@@ -17,26 +17,6 @@ Want to create reveal.js presentation in a graphical editor? Try <https://slides
---
### 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>
</table>
</div>
---
### Getting started
- 🚀 [Install reveal.js](https://revealjs.com/installation)
- 👀 [View the demo presentation](https://revealjs.com/demo)

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 > * {

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,7 +18,7 @@ html.reveal-full-page {
height: 100%;
height: 100vh;
height: calc( var(--vh, 1vh) * 100 );
height: 100svh;
height: 100dvh;
overflow: hidden;
}
@@ -34,6 +33,16 @@ html.reveal-full-page {
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
@@ -631,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%;
@@ -995,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 {
@@ -1314,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;
@@ -1331,9 +1339,12 @@ $controlsArrowAngleActive: 36deg;
}
.slides section:hover,
.slides section.present {
outline: 10px solid rgba(150,150,150,0.4);
outline: 10px solid rgba(150,150,150,0.6);
outline-offset: 10px;
}
.slides section.present {
outline: 10px solid var(--r-link-color);
}
.slides section .fragment {
opacity: 1;
transition: none;
@@ -1352,10 +1363,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 {
@@ -1435,160 +1442,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.95 );
backdrop-filter: blur( 6px );
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;
}
.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;
}
.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 {
padding-top: 20px;
padding-bottom: 20px;
}
/*********************************************
* PLAYBACK COMPONENT
@@ -1901,7 +1982,7 @@ $notesWidthPercent: 25%;
display: none !important;
}
.overlay,
.r-overlay,
.pause-overlay {
position: fixed;
}

View File

@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -23,7 +24,7 @@ $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);

View File

@@ -9,6 +9,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -32,8 +33,8 @@ $headingLetterSpacing: normal;
$headingTextTransform: uppercase;
$headingFontWeight: 600;
$linkColor: #42affa;
$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;

View File

@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -29,7 +30,7 @@ $headingLetterSpacing: normal;
$headingTextTransform: uppercase;
$headingFontWeight: 600;
$linkColor: #42affa;
$linkColorHover: lighten( $linkColor, 15% );
$linkColorHover: color.scale( $linkColor, $lightness: 15% );
$selectionBackgroundColor: rgba( $linkColor, 0.75 );
$heading1Size: 2.5em;

View File

@@ -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;

View File

@@ -9,9 +9,10 @@
@import "../template/settings";
// ---------------------------------------------
// Include theme-specific fonts
@import url(./fonts/league-gothic/league-gothic.css);
@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
$systemFontsSansSerif: -apple-system,
BlinkMacSystemFont,
avenir next,

View File

@@ -5,6 +5,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -43,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

View File

@@ -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;

View File

@@ -7,6 +7,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -22,7 +23,7 @@ $headingTextShadow: none;
$headingTextTransform: none;
$backgroundColor: #F0F1EB;
$linkColor: #51483D;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
$selectionBackgroundColor: #26351C;
$overlayElementBgColor: 0, 0, 0;

View File

@@ -8,6 +8,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -28,7 +29,7 @@ $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;

View File

@@ -6,6 +6,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -26,7 +27,7 @@ $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;

View File

@@ -5,6 +5,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -48,7 +49,7 @@ $headingColor: $base01;
$headingTextShadow: none;
$backgroundColor: $base3;
$linkColor: $blue;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
$selectionBackgroundColor: $magenta;
$overlayElementBgColor: 0, 0, 0;

View File

@@ -9,6 +9,7 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "../template/mixins";
@import "../template/settings";
// ---------------------------------------------
@@ -32,8 +33,8 @@ $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;

View File

@@ -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: #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;

View File

@@ -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,7 +23,7 @@
--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};

View File

@@ -1,3 +1,5 @@
@use "sass:color";
// Base settings for all themes that can optionally be
// overridden by the super-theme
@@ -32,7 +34,7 @@ $codeFont: monospace;
// Links and actions
$linkColor: #13DAEC;
$linkColorHover: lighten( $linkColor, 20% );
$linkColorHover: color.scale( $linkColor, $lightness: 20% );
// Text selection
$selectionBackgroundColor: #FF5E99;

View File

@@ -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);
}

View File

@@ -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>
@@ -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>

17
dist/reveal.css vendored

File diff suppressed because one or more lines are too long

14
dist/reveal.esm.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
dist/reveal.js vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -33,8 +33,8 @@ 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;

View File

@@ -35,9 +35,9 @@ 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: 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;

View File

@@ -32,8 +32,8 @@ 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-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;

View File

@@ -38,8 +38,8 @@ 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;

View File

@@ -2,6 +2,8 @@
* Dracula Dark theme for reveal.js.
* Based on https://draculatheme.com
*/
@import url(./fonts/league-gothic/league-gothic.css);
@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
/**
* Dracula colors by Zeno Rocha
* https://draculatheme.com/contribute
@@ -39,7 +41,7 @@ section.has-light-background, section.has-light-background h1, section.has-light
--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: #ff2da5;
--r-link-color-dark: rgb(255, 64.6, 174.0089552239);
--r-link-color-hover: #8BE9FD;
--r-selection-background-color: #44475A;
--r-selection-color: #fff;

View File

@@ -35,8 +35,8 @@ 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;

4
dist/theme/moon.css vendored
View File

@@ -35,8 +35,8 @@ 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;

View File

@@ -33,8 +33,8 @@ 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;

View File

@@ -36,8 +36,8 @@ 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;

View File

@@ -35,8 +35,8 @@ 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;

4
dist/theme/sky.css vendored
View File

@@ -37,8 +37,8 @@ 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;

View File

@@ -36,8 +36,8 @@ 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;

View File

@@ -35,9 +35,9 @@ 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;

View File

@@ -32,9 +32,9 @@ 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;

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
examples/lightbox.html Normal file
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>

View File

@@ -121,7 +121,7 @@
<!-- 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>

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)

View File

@@ -33,10 +33,10 @@
<section>
<h2>Video</h2>
<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" data-autoplay></video>
<video src="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4" data-autoplay></video>
</section>
<section data-background-video="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">
<section data-background-video="https://static.slid.es/site/homepage/v1/homepage-video-editor.mp4">
<h2>Background Video</h2>
</section>

View File

@@ -91,8 +91,10 @@
<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://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">
<h2>Video background</h2>
<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>

View File

@@ -1,3 +1,4 @@
const fs = require('fs');
const pkg = require('./package.json')
const glob = require('glob')
const yargs = require('yargs')
@@ -12,9 +13,8 @@ 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')
@@ -24,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-2024 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);
@@ -86,7 +94,7 @@ gulp.task('js-es5', () => {
name: 'Reveal',
file: './dist/reveal.js',
format: 'umd',
banner: banner,
banner: jsLicense,
sourcemap: true
});
});
@@ -108,7 +116,7 @@ gulp.task('js-es6', () => {
return bundle.write({
file: './dist/reveal.esm.js',
format: 'es',
banner: banner,
banner: jsLicense,
sourcemap: true
});
});
@@ -161,6 +169,7 @@ function compileSass() {
const transformedFile = vinylFile.clone();
sass.render({
silenceDeprecations: ['legacy-js-api'],
data: transformedFile.contents.toString(),
file: transformedFile.path,
}, ( err, result ) => {
@@ -184,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'))
@@ -211,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 ) {
@@ -266,22 +275,23 @@ 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(['index.html'])
.pipe(connect.reload()));

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
@@ -74,7 +77,7 @@ export default {
// 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

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' ) ) {
@@ -175,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
@@ -468,7 +455,7 @@ export default class AutoAnimate {
// Text
this.findAutoAnimateMatches( pairs, fromSlide, toSlide, textNodes, node => {
return node.nodeName + ':::' + node.innerText;
return node.nodeName + ':::' + node.textContent.trim();
} );
// Media
@@ -478,7 +465,7 @@ 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 => {

View File

@@ -268,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)
@@ -321,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 );
@@ -347,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;
}
@@ -368,7 +399,7 @@ export default class Backgrounds {
// Allow the first background to apply without transition
setTimeout( () => {
this.element.classList.remove( 'no-transition' );
}, 1 );
}, 10 );
}

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 );
@@ -77,9 +83,10 @@ export default class Controls {
let pointerEvents = [ 'touchstart', 'click' ];
// Only support touch for Android, fixes double navigations in
// stock browser
// stock browser. Use touchend for it to be considered a valid
// user interaction (so we're allowed to autoplay media).
if( isAndroid ) {
pointerEvents = [ 'touchstart' ];
pointerEvents = [ 'touchend' ];
}
pointerEvents.forEach( eventName => {
@@ -89,19 +96,21 @@ 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 ) );
} );
}
unbind() {
[ 'touchstart', 'click' ].forEach( eventName => {
[ 'touchstart', 'touchend', 'click' ].forEach( eventName => {
this.controlsLeft.forEach( el => el.removeEventListener( eventName, this.onNavigateLeftClicked, false ) );
this.controlsRight.forEach( el => el.removeEventListener( eventName, this.onNavigateRightClicked, false ) );
this.controlsUp.forEach( el => el.removeEventListener( eventName, this.onNavigateUpClicked, false ) );
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 +150,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 +276,13 @@ export default class Controls {
}
onEnterFullscreen( event ) {
const config = this.Reveal.getConfig();
const viewport = this.Reveal.getViewportElement();
enterFullscreen( config.embedded ? viewport : viewport.parentElement );
}
}

View File

@@ -74,7 +74,7 @@ export default class JumpToSlide {
let query = this.jumpInput.value.trim( '' );
let indices;
// When slide numbers are formatted to be a single linear mumber
// 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 ) ) {

View File

@@ -167,7 +167,7 @@ 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 keyCodeUsesModifier = [32, 37, 38, 39, 40, 78, 80, 191].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 = !( keyCodeUsesModifier && event.shiftKey || event.altKey ) &&
@@ -190,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;
}
@@ -271,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});
@@ -283,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});
@@ -353,8 +367,12 @@ export default class Keyboard {
this.Reveal.toggleJumpToSlide();
}
}
// C
else if( keyCode === 67 && this.Reveal.isOverlayOpen() ) {
this.Reveal.closeOverlay();
}
// ?
else if( keyCode === 191 && event.shiftKey ) {
else if( ( keyCode === 63 || keyCode === 191 ) && event.shiftKey ) {
this.Reveal.toggleHelp();
}
// F1
@@ -380,6 +398,12 @@ export default class Keyboard {
event.preventDefault && event.preventDefault();
}
// Enter to exit overview mode
else if (keyCode === 13 && this.Reveal.overview.isActive()) {
this.Reveal.overview.deactivate();
event.preventDefault && event.preventDefault();
}
// If auto-sliding is enabled we need to cue up
// another timeout

389
js/controllers/overlay.js Normal file
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();
}
}

View File

@@ -449,6 +449,10 @@ export default class ScrollView {
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;
}
/**

View File

@@ -9,11 +9,14 @@ import fitty from 'fitty';
*/
export default class SlideContent {
allowedToPlay = true;
constructor( Reveal ) {
this.Reveal = Reveal;
this.startEmbeddedIframe = this.startEmbeddedIframe.bind( this );
this.ensureMobileMediaPlaying = this.ensureMobileMediaPlaying.bind( this );
}
@@ -51,7 +54,13 @@ export default class SlideContent {
load( slide, options = {} ) {
// Show the slide element
slide.style.display = this.Reveal.getConfig().display;
const displayValue = this.Reveal.getConfig().display;
if( displayValue.includes('!important') ) {
const value = displayValue.replace(/\s*!important\s*$/, '').trim();
slide.style.setProperty('display', value, 'important');
} else {
slide.style.display = displayValue;
}
// Media elements with data-src attributes
queryAll( slide, 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ).forEach( element => {
@@ -119,14 +128,14 @@ export default class SlideContent {
}
}
// 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;
}
@@ -280,7 +289,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 => {
@@ -306,6 +317,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 } );
@@ -315,10 +329,16 @@ export default class SlideContent {
else if( isMobile ) {
let promise = el.play();
el.addEventListener( 'canplay', this.ensureMobileMediaPlaying );
// If autoplay does not work, ensure that the controls are visible so
// that the viewer can start the media on their own
if( promise && typeof promise.catch === 'function' && el.controls === false ) {
promise.catch( () => {
promise
.then( () => {
this.allowedToPlay = true;
})
.catch( () => {
el.controls = true;
// Once the video does start playing, hide the controls again
@@ -337,32 +357,72 @@ 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' ) );
}
} );
}
}
}
/**
* Ensure that an HTMLMediaElement is playing on mobile devices.
*
* This is a workaround for a bug in mobile Safari where
* the media fails to display if many videos are started
* at the same moment. When this happens, Mobile Safari
* reports the video is playing, and the current time
* advances, but nothing is visible.
*
* @param {Event} event
*/
ensureMobileMediaPlaying( event ) {
const el = event.target;
// Ignore this check incompatible browsers
if( typeof el.getVideoPlaybackQuality !== 'function' ) {
return;
}
setTimeout( () => {
const playing = el.paused === false;
const totalFrames = el.getVideoPlaybackQuality().totalVideoFrames;
if( playing && totalFrames === 0 ) {
el.load();
el.play();
}
}, 1000 );
}
/**
* Starts playing an embedded video/audio element after
* it has finished loading.
@@ -375,8 +435,23 @@ 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;
const promise = event.target.play();
if( promise && typeof promise.catch === 'function' ) {
promise
.then( () => {
this.allowedToPlay = true;
} )
.catch( ( error ) => {
if( error.name === 'NotAllowedError' ) {
this.allowedToPlay = false;
}
} );
}
}
}
event.target.removeEventListener( 'loadeddata', this.startEmbeddedMedia );
@@ -447,6 +522,10 @@ export default class SlideContent {
if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {
el.setAttribute('data-paused-by-reveal', '');
el.pause();
if( isMobile ) {
el.removeEventListener( 'canplay', this.ensureMobileMediaPlaying );
}
}
} );
@@ -483,4 +562,15 @@ export default class SlideContent {
}
/**
* Checks whether media playback is blocked by the browser. This
* typically happens when media playback is initiated without a
* direct user interaction.
*/
isNotAllowedToPlay() {
return !this.allowedToPlay;
}
}

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;
@@ -214,6 +216,14 @@ export default class Touch {
*/
onTouchEnd( event ) {
// Media playback is only allowed as a direct result of a
// user interaction. Some mobile devices do not consider a
// 'touchmove' to be a direct user action. If this is the
// case, we fall back to starting playback here instead.
if( this.touchCaptured && this.Reveal.slideContent.isNotAllowedToPlay() ) {
this.Reveal.startEmbeddedContent( this.Reveal.getCurrentSlide() );
}
this.touchCaptured = false;
}

View File

@@ -13,6 +13,7 @@ import Controls from './controllers/controls.js'
import Progress from './controllers/progress.js'
import Pointer from './controllers/pointer.js'
import Plugins from './controllers/plugins.js'
import Overlay from './controllers/overlay.js'
import Touch from './controllers/touch.js'
import Focus from './controllers/focus.js'
import Notes from './controllers/notes.js'
@@ -28,7 +29,7 @@ import {
} from './utils/constants.js'
// The reveal.js version
export const VERSION = '5.0.5';
export const VERSION = '5.2.1';
/**
* reveal.js
@@ -51,6 +52,9 @@ export default function( revealElement, options ) {
// Configuration defaults, can be overridden at initialization time
let config = {},
// Flags if initialize() has been invoked for this reveal instance
initialized = false,
// Flags if reveal.js is loaded (has dispatched the 'ready' event)
ready = false,
@@ -116,6 +120,7 @@ export default function( revealElement, options ) {
progress = new Progress( Reveal ),
pointer = new Pointer( Reveal ),
plugins = new Plugins( Reveal ),
overlay = new Overlay( Reveal ),
focus = new Focus( Reveal ),
touch = new Touch( Reveal ),
notes = new Notes( Reveal );
@@ -127,6 +132,10 @@ export default function( revealElement, options ) {
if( !revealElement ) throw 'Unable to find presentation root (<div class="reveal">).';
if( initialized ) throw 'Reveal.js has already been initialized.';
initialized = true;
// Cache references to key DOM elements
dom.wrapper = revealElement;
dom.slides = revealElement.querySelector( '.slides' );
@@ -185,6 +194,9 @@ export default function( revealElement, options ) {
*/
function start() {
// Don't proceed if this instance has been destroyed
if( initialized === false ) return;
ready = true;
// Remove slides hidden with data-visibility
@@ -382,7 +394,7 @@ export default function( revealElement, options ) {
// Text node
if( node.nodeType === 3 ) {
text += node.textContent;
text += node.textContent.trim();
}
// Element node
else if( node.nodeType === 1 ) {
@@ -391,10 +403,25 @@ export default function( revealElement, options ) {
let isDisplayHidden = window.getComputedStyle( node )['display'] === 'none';
if( isAriaHidden !== 'true' && !isDisplayHidden ) {
// Capture alt text from img and video elements
if( node.tagName === 'IMG' || node.tagName === 'VIDEO' ) {
let altText = node.getAttribute( 'alt' );
if( altText ) {
text += ensurePunctuation( altText );
}
}
Array.from( node.childNodes ).forEach( child => {
text += getStatusText( child );
} );
// Add period after block-level text elements to improve
// screen reader experience
const textElements = ['P', 'DIV', 'UL', 'OL', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE'];
if( textElements.includes( node.tagName ) && text.trim() !== '' ) {
text = ensurePunctuation( text );
}
}
}
@@ -405,6 +432,22 @@ export default function( revealElement, options ) {
}
/**
* Ensures text ends with proper punctuation by adding a period
* if it doesn't already end with punctuation.
*/
function ensurePunctuation( text ) {
const trimmedText = text.trim();
if( trimmedText === '' ) {
return text;
}
return !/[.!?]$/.test(trimmedText) ? trimmedText + '.' : trimmedText;
}
/**
* This is an unfortunate necessity. Some actions such as
* an input field being focused in an iframe or using the
@@ -500,16 +543,6 @@ export default function( revealElement, options ) {
resume();
}
// Iframe link previews
if( config.previewLinks ) {
enablePreviewLinks();
disablePreviewLinks( '[data-preview-link=false]' );
}
else {
disablePreviewLinks();
enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' );
}
// Reset all changes made by auto-animations
autoAnimate.reset();
@@ -604,13 +637,19 @@ export default function( revealElement, options ) {
*/
function destroy() {
initialized = false;
// There's nothing to destroy if this instance hasn't finished
// initializing
if( ready === false ) return;
removeEventListeners();
cancelAutoSlide();
disablePreviewLinks();
// Destroy controllers
notes.destroy();
focus.destroy();
overlay.destroy();
plugins.destroy();
pointer.destroy();
controls.destroy();
@@ -760,164 +799,6 @@ export default function( revealElement, options ) {
}
/**
* Bind preview frame links.
*
* @param {string} [selector=a] - selector for anchors
*/
function enablePreviewLinks( selector = 'a' ) {
Array.from( dom.wrapper.querySelectorAll( selector ) ).forEach( element => {
if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
element.addEventListener( 'click', onPreviewLinkClicked, false );
}
} );
}
/**
* Unbind preview frame links.
*/
function disablePreviewLinks( selector = 'a' ) {
Array.from( dom.wrapper.querySelectorAll( selector ) ).forEach( element => {
if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
element.removeEventListener( 'click', onPreviewLinkClicked, false );
}
} );
}
/**
* Opens a preview window for the target URL.
*
* @param {string} url - url for preview iframe src
*/
function showPreview( url ) {
closeOverlay();
dom.overlay = document.createElement( 'div' );
dom.overlay.classList.add( 'overlay' );
dom.overlay.classList.add( 'overlay-preview' );
dom.wrapper.appendChild( dom.overlay );
dom.overlay.innerHTML =
`<header>
<a class="close" href="#"><span class="icon"></span></a>
<a class="external" href="${url}" target="_blank"><span class="icon"></span></a>
</header>
<div class="spinner"></div>
<div class="viewport">
<iframe src="${url}"></iframe>
<small class="viewport-inner">
<span class="x-frame-error">Unable to load iframe. This is likely due to the site's policy (x-frame-options).</span>
</small>
</div>`;
dom.overlay.querySelector( 'iframe' ).addEventListener( 'load', event => {
dom.overlay.classList.add( 'loaded' );
}, false );
dom.overlay.querySelector( '.close' ).addEventListener( 'click', event => {
closeOverlay();
event.preventDefault();
}, false );
dom.overlay.querySelector( '.external' ).addEventListener( 'click', event => {
closeOverlay();
}, false );
}
/**
* 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.
*/
function toggleHelp( override ){
if( typeof override === 'boolean' ) {
override ? showHelp() : closeOverlay();
}
else {
if( dom.overlay ) {
closeOverlay();
}
else {
showHelp();
}
}
}
/**
* Opens an overlay window with help material.
*/
function showHelp() {
if( config.help ) {
closeOverlay();
dom.overlay = document.createElement( 'div' );
dom.overlay.classList.add( 'overlay' );
dom.overlay.classList.add( 'overlay-help' );
dom.wrapper.appendChild( dom.overlay );
let html = '<p class="title">Keyboard Shortcuts</p><br/>';
let shortcuts = keyboard.getShortcuts(),
bindings = 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>';
dom.overlay.innerHTML = `
<header>
<a class="close" href="#"><span class="icon"></span></a>
</header>
<div class="viewport">
<div class="viewport-inner">${html}</div>
</div>
`;
dom.overlay.querySelector( '.close' ).addEventListener( 'click', event => {
closeOverlay();
event.preventDefault();
}, false );
}
}
/**
* Closes any currently open overlay.
*/
function closeOverlay() {
if( dom.overlay ) {
dom.overlay.parentNode.removeChild( dom.overlay );
dom.overlay = null;
return true;
}
return false;
}
/**
* Applies JavaScript-controlled layout rules to the
* presentation.
@@ -1243,7 +1124,7 @@ export default function( revealElement, options ) {
/**
* Returns true if we're currently on the last slide in
* the presenation. If the last slide is a stack, we only
* the presentation. If the last slide is a stack, we only
* consider this the last slide if it's at the end of the
* stack.
*/
@@ -1446,6 +1327,9 @@ export default function( revealElement, options ) {
let currentHorizontalSlide = horizontalSlides[ indexh ],
currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );
// Indicate when we're on a vertical slide
revealElement.classList.toggle( 'is-vertical-slide', currentVerticalSlides.length > 1 );
// Store references to the previous and current slides
currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide;
@@ -1671,6 +1555,7 @@ export default function( revealElement, options ) {
notes.update();
notes.updateVisibility();
overlay.update();
backgrounds.update( true );
slideNumber.update();
slideContent.formatEmbeddedContent();
@@ -1928,14 +1813,16 @@ export default function( revealElement, options ) {
if( horizontalSlidesLength && typeof indexh !== 'undefined' ) {
const isOverview = overview.isActive();
// The number of steps away from the present slide that will
// be visible
let viewDistance = overview.isActive() ? 10 : config.viewDistance;
let viewDistance = isOverview ? 10 : config.viewDistance;
// Shorten the view distance on devices that typically have
// less resources
if( Device.isMobile ) {
viewDistance = overview.isActive() ? 6 : config.mobileViewDistance;
viewDistance = isOverview ? 6 : config.mobileViewDistance;
}
// All slides need to be visible when exporting to PDF
@@ -1968,7 +1855,7 @@ export default function( revealElement, options ) {
if( verticalSlidesLength ) {
let oy = getPreviousVerticalIndex( horizontalSlide );
let oy = isOverview ? 0 : getPreviousVerticalIndex( horizontalSlide );
for( let y = 0; y < verticalSlidesLength; y++ ) {
let verticalSlide = verticalSlides[y];
@@ -2356,7 +2243,8 @@ export default function( revealElement, options ) {
indexv: indices.v,
indexf: indices.f,
paused: isPaused(),
overview: overview.isActive()
overview: overview.isActive(),
...overlay.getState()
};
}
@@ -2382,6 +2270,8 @@ export default function( revealElement, options ) {
if( typeof overviewFlag === 'boolean' && overviewFlag !== overview.isActive() ) {
overview.toggle( overviewFlag );
}
overlay.setState( state );
}
}
@@ -2595,6 +2485,9 @@ export default function( revealElement, options ) {
let h = indexh - 1;
slide( h, v );
}
else if( config.rtl ) {
navigateRight({skipFragments});
}
else {
navigateLeft({skipFragments});
}
@@ -2783,24 +2676,6 @@ export default function( revealElement, options ) {
}
/**
* Handles clicks on links that are set to preview in the
* iframe overlay.
*
* @param {object} event
*/
function onPreviewLinkClicked( event ) {
if( event.currentTarget && event.currentTarget.hasAttribute( 'href' ) ) {
let url = event.currentTarget.getAttribute( 'href' );
if( url ) {
showPreview( url );
event.preventDefault();
}
}
}
/**
* Handles click on the auto-sliding controls element.
*
@@ -2879,7 +2754,7 @@ export default function( revealElement, options ) {
availableFragments: fragments.availableRoutes.bind( fragments ),
// Toggles a help overlay with keyboard shortcuts
toggleHelp,
toggleHelp: overlay.toggleHelp.bind( overlay ),
// Toggles the overview mode on/off
toggleOverview: overview.toggle.bind( overview ),
@@ -2909,7 +2784,7 @@ export default function( revealElement, options ) {
isSpeakerNotes: notes.isSpeakerNotesWindow.bind( notes ),
isOverview: overview.isActive.bind( overview ),
isFocused: focus.isFocused.bind( focus ),
isOverlayOpen: overlay.isOpen.bind( overlay ),
isScrollView: scrollView.isActive.bind( scrollView ),
isPrintView: printView.isActive.bind( printView ),
@@ -2920,13 +2795,17 @@ export default function( revealElement, options ) {
loadSlide: slideContent.load.bind( slideContent ),
unloadSlide: slideContent.unload.bind( slideContent ),
// Media playback
// Start/stop all media inside of the current slide
startEmbeddedContent: () => slideContent.startEmbeddedContent( currentSlide ),
stopEmbeddedContent: () => slideContent.stopEmbeddedContent( currentSlide, { unloadIframes: false } ),
// Preview management
showPreview,
hidePreview: closeOverlay,
// Lightbox previews
previewIframe: overlay.previewIframe.bind( overlay ),
previewImage: overlay.previewImage.bind( overlay ),
previewVideo: overlay.previewVideo.bind( overlay ),
showPreview: overlay.previewIframe.bind( overlay ), // deprecated in favor of showIframeLightbox
hidePreview: overlay.close.bind( overlay ),
// Adds or removes all internal event listeners
addEventListeners,
@@ -3040,13 +2919,14 @@ export default function( revealElement, options ) {
controls,
location,
overview,
keyboard,
fragments,
backgrounds,
slideContent,
slideNumber,
onUserInput,
closeOverlay,
closeOverlay: overlay.close.bind( overlay ),
updateSlidesVisibility,
layoutSlideContents,
transformSlides,

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 ),

4669
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "reveal.js",
"version": "5.0.5",
"version": "5.2.1",
"description": "The HTML Presentation Framework",
"homepage": "https://revealjs.com",
"subdomain": "revealjs",
@@ -42,25 +42,29 @@
"core-js": "^3.33.1",
"fitty": "^2.3.7",
"glob": "^10.3.10",
"gulp": "^4.0.2",
"gulp": "^5.0.0",
"gulp-autoprefixer": "^8.0.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-header-comment": "^0.10.0",
"gulp-zip": "^5.1.0",
"highlight.js": "^11.9.0",
"marked": "^4.3.0",
"node-qunit-puppeteer": "^2.1.2",
"qunit": "^2.20.0",
"node-qunit-puppeteer": "^2.2.0",
"through2": "^4.0.2",
"qunit": "^2.22.0",
"rollup": "^4.1.5",
"sass": "^1.69.5",
"sass": "^1.79.4",
"yargs": "^17.7.2"
},
"overrides": {
"chokidar": "3.5.3",
"glob-parent": "6.0.2"
"gulp-connect": {
"send": "0.19.0"
},
"gulp-header-comment": {
"moment": "2.30.1"
}
},
"browserslist": "> 2%, not dead",
"eslintConfig": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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;' );
}

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 => {

View File

@@ -1,6 +1,6 @@
const t=()=>{let t,e={messageStyle:"none",tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]],skipTags:["script","noscript","style","textarea","pre"]},skipStartupTypeset:!0};return{id:"mathjax2",init:function(a){t=a;let n=t.getConfig().mathjax2||t.getConfig().math||{},i={...e,...n},s=(i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js")+"?config="+(i.config||"TeX-AMS_HTML-full");i.tex2jax={...e.tex2jax,...n.tex2jax},i.mathjax=i.config=null,function(t,e){let a=document.querySelector("head"),n=document.createElement("script");n.type="text/javascript",n.src=t;let i=()=>{"function"==typeof e&&(e.call(),e=null)};n.onload=i,n.onreadystatechange=()=>{"loaded"===this.readyState&&i()},a.appendChild(n)}(s,(function(){MathJax.Hub.Config(i),MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.getRevealElement()]),MathJax.Hub.Queue(t.layout),t.on("slidechanged",(function(t){MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.currentSlide])}))}))}}},e=t;
const t=()=>{let t,e={messageStyle:"none",tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]],skipTags:["script","noscript","style","textarea","pre","code"]},skipStartupTypeset:!0};return{id:"mathjax2",init:function(a){t=a;let n=t.getConfig().mathjax2||t.getConfig().math||{},i={...e,...n},s=(i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js")+"?config="+(i.config||"TeX-AMS_HTML-full");i.tex2jax={...e.tex2jax,...n.tex2jax},i.mathjax=i.config=null,function(t,e){let a=document.querySelector("head"),n=document.createElement("script");n.type="text/javascript",n.src=t;let i=()=>{"function"==typeof e&&(e.call(),e=null)};n.onload=i,n.onreadystatechange=()=>{"loaded"===this.readyState&&i()},a.appendChild(n)}(s,(function(){MathJax.Hub.Config(i),MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.getRevealElement()]),MathJax.Hub.Queue(t.layout),t.on("slidechanged",(function(t){MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.currentSlide])}))}))}}},e=t;
/*!
* This plugin is a wrapper for the MathJax2,
* MathJax3 and KaTeX typesetter plugins.
*/
var a=Plugin=Object.assign(e(),{KaTeX:()=>{let t,e={version:"latest",delimiters:[{left:"$$",right:"$$",display:!0},{left:"$",right:"$",display:!1},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],ignoredTags:["script","noscript","style","textarea","pre"]};const a=t=>new Promise(((e,a)=>{const n=document.createElement("script");n.type="text/javascript",n.onload=e,n.onerror=a,n.src=t,document.head.append(n)}));return{id:"katex",init:function(n){t=n;let i=t.getConfig().katex||{},s={...e,...i};const{local:l,version:o,extensions:r,...c}=s;let d=s.local||"https://cdn.jsdelivr.net/npm/katex",p=s.local?"":"@"+s.version,u=d+p+"/dist/katex.min.css",h=d+p+"/dist/contrib/mhchem.min.js",x=d+p+"/dist/contrib/auto-render.min.js",m=[d+p+"/dist/katex.min.js"];s.extensions&&s.extensions.includes("mhchem")&&m.push(h),m.push(x);const f=()=>{renderMathInElement(n.getSlidesElement(),c),t.layout()};(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href=t,document.head.appendChild(e)})(u),async function(t){for(const e of t)await a(e)}(m).then((()=>{t.isReady()?f():t.on("ready",f.bind(this))}))}}},MathJax2:t,MathJax3:()=>{let t,e={tex:{inlineMath:[["$","$"],["\\(","\\)"]]},options:{skipHtmlTags:["script","noscript","style","textarea","pre"]},startup:{ready:()=>{MathJax.startup.defaultReady(),MathJax.startup.promise.then((()=>{Reveal.layout()}))}}};return{id:"mathjax3",init:function(a){t=a;let n=t.getConfig().mathjax3||{},i={...e,...n};i.tex={...e.tex,...n.tex},i.options={...e.options,...n.options},i.startup={...e.startup,...n.startup};let s=i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";i.mathjax=null,window.MathJax=i,function(t,e){let a=document.createElement("script");a.type="text/javascript",a.id="MathJax-script",a.src=t,a.async=!0,a.onload=()=>{"function"==typeof e&&(e.call(),e=null)},document.head.appendChild(a)}(s,(function(){Reveal.addEventListener("slidechanged",(function(t){MathJax.typeset()}))}))}}}});export{a as default};
var a=Plugin=Object.assign(e(),{KaTeX:()=>{let t,e={version:"latest",delimiters:[{left:"$$",right:"$$",display:!0},{left:"$",right:"$",display:!1},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],ignoredTags:["script","noscript","style","textarea","pre","code"]};const a=t=>new Promise(((e,a)=>{const n=document.createElement("script");n.type="text/javascript",n.onload=e,n.onerror=a,n.src=t,document.head.append(n)}));return{id:"katex",init:function(n){t=n;let i=t.getConfig().katex||{},s={...e,...i};const{local:o,version:l,extensions:r,...c}=s;let d=s.local||"https://cdn.jsdelivr.net/npm/katex",p=s.local?"":"@"+s.version,u=d+p+"/dist/katex.min.css",h=d+p+"/dist/contrib/mhchem.min.js",x=d+p+"/dist/contrib/auto-render.min.js",m=[d+p+"/dist/katex.min.js"];s.extensions&&s.extensions.includes("mhchem")&&m.push(h),m.push(x);const f=()=>{renderMathInElement(n.getSlidesElement(),c),t.layout()};(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href=t,document.head.appendChild(e)})(u),async function(t){for(const e of t)await a(e)}(m).then((()=>{t.isReady()?f():t.on("ready",f.bind(this))}))}}},MathJax2:t,MathJax3:()=>{let t,e={tex:{inlineMath:[["$","$"],["\\(","\\)"]]},options:{skipHtmlTags:["script","noscript","style","textarea","pre","code"]},startup:{ready:()=>{MathJax.startup.defaultReady(),MathJax.startup.promise.then((()=>{t.layout()}))}}};return{id:"mathjax3",init:function(a){t=a;let n=t.getConfig().mathjax3||{},i={...e,...n};i.tex={...e.tex,...n.tex},i.options={...e.options,...n.options},i.startup={...e.startup,...n.startup};let s=i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";i.mathjax=null,window.MathJax=i,function(t,e){let a=document.createElement("script");a.type="text/javascript",a.id="MathJax-script",a.src=t,a.async=!0,a.onload=()=>{"function"==typeof e&&(e.call(),e=null)},document.head.appendChild(a)}(s,(function(){t.addEventListener("slidechanged",(function(t){MathJax.typeset()}))}))}}}});export{a as default};

View File

@@ -1 +1 @@
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).RevealMath=e()}(this,(function(){"use strict";const t=()=>{let t,e={messageStyle:"none",tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]],skipTags:["script","noscript","style","textarea","pre"]},skipStartupTypeset:!0};return{id:"mathjax2",init:function(n){t=n;let a=t.getConfig().mathjax2||t.getConfig().math||{},i={...e,...a},s=(i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js")+"?config="+(i.config||"TeX-AMS_HTML-full");i.tex2jax={...e.tex2jax,...a.tex2jax},i.mathjax=i.config=null,function(t,e){let n=document.querySelector("head"),a=document.createElement("script");a.type="text/javascript",a.src=t;let i=()=>{"function"==typeof e&&(e.call(),e=null)};a.onload=i,a.onreadystatechange=()=>{"loaded"===this.readyState&&i()},n.appendChild(a)}(s,(function(){MathJax.Hub.Config(i),MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.getRevealElement()]),MathJax.Hub.Queue(t.layout),t.on("slidechanged",(function(t){MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.currentSlide])}))}))}}},e=t;return Plugin=Object.assign(e(),{KaTeX:()=>{let t,e={version:"latest",delimiters:[{left:"$$",right:"$$",display:!0},{left:"$",right:"$",display:!1},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],ignoredTags:["script","noscript","style","textarea","pre"]};const n=t=>new Promise(((e,n)=>{const a=document.createElement("script");a.type="text/javascript",a.onload=e,a.onerror=n,a.src=t,document.head.append(a)}));return{id:"katex",init:function(a){t=a;let i=t.getConfig().katex||{},s={...e,...i};const{local:o,version:l,extensions:r,...c}=s;let d=s.local||"https://cdn.jsdelivr.net/npm/katex",u=s.local?"":"@"+s.version,p=d+u+"/dist/katex.min.css",h=d+u+"/dist/contrib/mhchem.min.js",x=d+u+"/dist/contrib/auto-render.min.js",m=[d+u+"/dist/katex.min.js"];s.extensions&&s.extensions.includes("mhchem")&&m.push(h),m.push(x);const f=()=>{renderMathInElement(a.getSlidesElement(),c),t.layout()};(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href=t,document.head.appendChild(e)})(p),async function(t){for(const e of t)await n(e)}(m).then((()=>{t.isReady()?f():t.on("ready",f.bind(this))}))}}},MathJax2:t,MathJax3:()=>{let t,e={tex:{inlineMath:[["$","$"],["\\(","\\)"]]},options:{skipHtmlTags:["script","noscript","style","textarea","pre"]},startup:{ready:()=>{MathJax.startup.defaultReady(),MathJax.startup.promise.then((()=>{Reveal.layout()}))}}};return{id:"mathjax3",init:function(n){t=n;let a=t.getConfig().mathjax3||{},i={...e,...a};i.tex={...e.tex,...a.tex},i.options={...e.options,...a.options},i.startup={...e.startup,...a.startup};let s=i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";i.mathjax=null,window.MathJax=i,function(t,e){let n=document.createElement("script");n.type="text/javascript",n.id="MathJax-script",n.src=t,n.async=!0,n.onload=()=>{"function"==typeof e&&(e.call(),e=null)},document.head.appendChild(n)}(s,(function(){Reveal.addEventListener("slidechanged",(function(t){MathJax.typeset()}))}))}}}})}));
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).RevealMath=e()}(this,(function(){"use strict";const t=()=>{let t,e={messageStyle:"none",tex2jax:{inlineMath:[["$","$"],["\\(","\\)"]],skipTags:["script","noscript","style","textarea","pre","code"]},skipStartupTypeset:!0};return{id:"mathjax2",init:function(n){t=n;let a=t.getConfig().mathjax2||t.getConfig().math||{},i={...e,...a},s=(i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js")+"?config="+(i.config||"TeX-AMS_HTML-full");i.tex2jax={...e.tex2jax,...a.tex2jax},i.mathjax=i.config=null,function(t,e){let n=document.querySelector("head"),a=document.createElement("script");a.type="text/javascript",a.src=t;let i=()=>{"function"==typeof e&&(e.call(),e=null)};a.onload=i,a.onreadystatechange=()=>{"loaded"===this.readyState&&i()},n.appendChild(a)}(s,(function(){MathJax.Hub.Config(i),MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.getRevealElement()]),MathJax.Hub.Queue(t.layout),t.on("slidechanged",(function(t){MathJax.Hub.Queue(["Typeset",MathJax.Hub,t.currentSlide])}))}))}}},e=t;return Plugin=Object.assign(e(),{KaTeX:()=>{let t,e={version:"latest",delimiters:[{left:"$$",right:"$$",display:!0},{left:"$",right:"$",display:!1},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],ignoredTags:["script","noscript","style","textarea","pre","code"]};const n=t=>new Promise(((e,n)=>{const a=document.createElement("script");a.type="text/javascript",a.onload=e,a.onerror=n,a.src=t,document.head.append(a)}));return{id:"katex",init:function(a){t=a;let i=t.getConfig().katex||{},s={...e,...i};const{local:o,version:l,extensions:c,...r}=s;let d=s.local||"https://cdn.jsdelivr.net/npm/katex",u=s.local?"":"@"+s.version,p=d+u+"/dist/katex.min.css",h=d+u+"/dist/contrib/mhchem.min.js",x=d+u+"/dist/contrib/auto-render.min.js",m=[d+u+"/dist/katex.min.js"];s.extensions&&s.extensions.includes("mhchem")&&m.push(h),m.push(x);const f=()=>{renderMathInElement(a.getSlidesElement(),r),t.layout()};(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href=t,document.head.appendChild(e)})(p),async function(t){for(const e of t)await n(e)}(m).then((()=>{t.isReady()?f():t.on("ready",f.bind(this))}))}}},MathJax2:t,MathJax3:()=>{let t,e={tex:{inlineMath:[["$","$"],["\\(","\\)"]]},options:{skipHtmlTags:["script","noscript","style","textarea","pre","code"]},startup:{ready:()=>{MathJax.startup.defaultReady(),MathJax.startup.promise.then((()=>{t.layout()}))}}};return{id:"mathjax3",init:function(n){t=n;let a=t.getConfig().mathjax3||{},i={...e,...a};i.tex={...e.tex,...a.tex},i.options={...e.options,...a.options},i.startup={...e.startup,...a.startup};let s=i.mathjax||"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";i.mathjax=null,window.MathJax=i,function(t,e){let n=document.createElement("script");n.type="text/javascript",n.id="MathJax-script",n.src=t,n.async=!0,n.onload=()=>{"function"==typeof e&&(e.call(),e=null)},document.head.appendChild(n)}(s,(function(){t.addEventListener("slidechanged",(function(t){MathJax.typeset()}))}))}}}})}));

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
};

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

View File

@@ -180,14 +180,16 @@ const Plugin = () => {
// (added 12/5/22 as a XSS safeguard)
if( isSameOriginEvent( 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 );
}
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) {}
}
@@ -207,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();
@@ -227,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.

View File

@@ -414,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 );
}
}
@@ -494,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' }), '*' );
}

View File

@@ -235,7 +235,9 @@ const Plugin = () => {
},
open: openSearch
open: openSearch,
close: closeSearch,
toggle: toggleSearch
}
};

View File

@@ -4,4 +4,4 @@
*
* @author Jon Snyder <snyder.jon@gmail.com>, February 2013
*/
const e=()=>{let e,t,n,l,i,o,r;function s(){t=document.createElement("div"),t.classList.add("searchbox"),t.style.position="absolute",t.style.top="10px",t.style.right="10px",t.style.zIndex=10,t.innerHTML='<input type="search" class="searchinput" placeholder="Search..." style="vertical-align: top;"/>\n\t\t</span>',n=t.querySelector(".searchinput"),n.style.width="240px",n.style.fontSize="14px",n.style.padding="4px 6px",n.style.color="#000",n.style.background="#fff",n.style.borderRadius="2px",n.style.border="0",n.style.outline="0",n.style.boxShadow="0 2px 18px rgba(0, 0, 0, 0.2)",n.style["-webkit-appearance"]="none",e.getRevealElement().appendChild(t),n.addEventListener("keyup",(function(t){if(13===t.keyCode)t.preventDefault(),function(){if(o){var t=n.value;""===t?(r&&r.remove(),l=null):(r=new c("slidecontent"),l=r.apply(t),i=0)}l&&(l.length&&l.length<=i&&(i=0),l.length>i&&(e.slide(l[i].h,l[i].v),i++))}(),o=!1;else o=!0}),!1),d()}function a(){t||s(),t.style.display="inline",n.focus(),n.select()}function d(){t||s(),t.style.display="none",r&&r.remove()}function c(t,n){var l=document.getElementById(t)||document.body,i=n||"EM",o=new RegExp("^(?:"+i+"|SCRIPT|FORM)$"),r=["#ff6","#a0ffff","#9f9","#f99","#f6f"],s=[],a=0,d="",c=[];this.setRegex=function(e){e=e.trim(),d=new RegExp("("+e+")","i")},this.getRegex=function(){return d.toString().replace(/^\/\\b\(|\)\\b\/i$/g,"").replace(/\|/g," ")},this.hiliteWords=function(t){if(null!=t&&t&&d&&!o.test(t.nodeName)){if(t.hasChildNodes())for(var n=0;n<t.childNodes.length;n++)this.hiliteWords(t.childNodes[n]);var l,p;if(3==t.nodeType)if((l=t.nodeValue)&&(p=d.exec(l))){for(var u=t;null!=u&&"SECTION"!=u.nodeName;)u=u.parentNode;var h=e.getIndices(u),f=c.length,y=!1;for(n=0;n<f;n++)c[n].h===h.h&&c[n].v===h.v&&(y=!0);y||c.push(h),s[p[0].toLowerCase()]||(s[p[0].toLowerCase()]=r[a++%r.length]);var g=document.createElement(i);g.appendChild(document.createTextNode(p[0])),g.style.backgroundColor=s[p[0].toLowerCase()],g.style.fontStyle="inherit",g.style.color="#000";var v=t.splitText(p.index);v.nodeValue=v.nodeValue.substring(p[0].length),t.parentNode.insertBefore(g,v)}}},this.remove=function(){for(var e,t=document.getElementsByTagName(i);t.length&&(e=t[0]);)e.parentNode.replaceChild(e.firstChild,e)},this.apply=function(e){if(null!=e&&e)return this.remove(),this.setRegex(e),this.hiliteWords(l),c}}return{id:"search",init:n=>{e=n,e.registerKeyboardShortcut("CTRL + Shift + F","Search"),document.addEventListener("keydown",(function(e){"F"==e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),t||s(),"inline"!==t.style.display?a():d())}),!1)},open:a}};export{e as default};
const e=()=>{let e,t,n,l,o,i,r;function s(){t=document.createElement("div"),t.classList.add("searchbox"),t.style.position="absolute",t.style.top="10px",t.style.right="10px",t.style.zIndex=10,t.innerHTML='<input type="search" class="searchinput" placeholder="Search..." style="vertical-align: top;"/>\n\t\t</span>',n=t.querySelector(".searchinput"),n.style.width="240px",n.style.fontSize="14px",n.style.padding="4px 6px",n.style.color="#000",n.style.background="#fff",n.style.borderRadius="2px",n.style.border="0",n.style.outline="0",n.style.boxShadow="0 2px 18px rgba(0, 0, 0, 0.2)",n.style["-webkit-appearance"]="none",e.getRevealElement().appendChild(t),n.addEventListener("keyup",(function(t){if(13===t.keyCode)t.preventDefault(),function(){if(i){var t=n.value;""===t?(r&&r.remove(),l=null):(r=new p("slidecontent"),l=r.apply(t),o=0)}l&&(l.length&&l.length<=o&&(o=0),l.length>o&&(e.slide(l[o].h,l[o].v),o++))}(),i=!1;else i=!0}),!1),d()}function a(){t||s(),t.style.display="inline",n.focus(),n.select()}function d(){t||s(),t.style.display="none",r&&r.remove()}function c(){t||s(),"inline"!==t.style.display?a():d()}function p(t,n){var l=document.getElementById(t)||document.body,o=n||"EM",i=new RegExp("^(?:"+o+"|SCRIPT|FORM)$"),r=["#ff6","#a0ffff","#9f9","#f99","#f6f"],s=[],a=0,d="",c=[];this.setRegex=function(e){e=e.trim(),d=new RegExp("("+e+")","i")},this.getRegex=function(){return d.toString().replace(/^\/\\b\(|\)\\b\/i$/g,"").replace(/\|/g," ")},this.hiliteWords=function(t){if(null!=t&&t&&d&&!i.test(t.nodeName)){if(t.hasChildNodes())for(var n=0;n<t.childNodes.length;n++)this.hiliteWords(t.childNodes[n]);var l,p;if(3==t.nodeType)if((l=t.nodeValue)&&(p=d.exec(l))){for(var u=t;null!=u&&"SECTION"!=u.nodeName;)u=u.parentNode;var f=e.getIndices(u),h=c.length,y=!1;for(n=0;n<h;n++)c[n].h===f.h&&c[n].v===f.v&&(y=!0);y||c.push(f),s[p[0].toLowerCase()]||(s[p[0].toLowerCase()]=r[a++%r.length]);var g=document.createElement(o);g.appendChild(document.createTextNode(p[0])),g.style.backgroundColor=s[p[0].toLowerCase()],g.style.fontStyle="inherit",g.style.color="#000";var v=t.splitText(p.index);v.nodeValue=v.nodeValue.substring(p[0].length),t.parentNode.insertBefore(g,v)}}},this.remove=function(){for(var e,t=document.getElementsByTagName(o);t.length&&(e=t[0]);)e.parentNode.replaceChild(e.firstChild,e)},this.apply=function(e){if(null!=e&&e)return this.remove(),this.setRegex(e),this.hiliteWords(l),c}}return{id:"search",init:t=>{e=t,e.registerKeyboardShortcut("CTRL + Shift + F","Search"),document.addEventListener("keydown",(function(e){"F"==e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),c())}),!1)},open:a,close:d,toggle:c}};export{e as default};

View File

@@ -4,4 +4,4 @@
* by navigatating to that slide and highlighting it.
*
* @author Jon Snyder <snyder.jon@gmail.com>, February 2013
*/return()=>{let e,t,n,i,o,l,r;function s(){t=document.createElement("div"),t.classList.add("searchbox"),t.style.position="absolute",t.style.top="10px",t.style.right="10px",t.style.zIndex=10,t.innerHTML='<input type="search" class="searchinput" placeholder="Search..." style="vertical-align: top;"/>\n\t\t</span>',n=t.querySelector(".searchinput"),n.style.width="240px",n.style.fontSize="14px",n.style.padding="4px 6px",n.style.color="#000",n.style.background="#fff",n.style.borderRadius="2px",n.style.border="0",n.style.outline="0",n.style.boxShadow="0 2px 18px rgba(0, 0, 0, 0.2)",n.style["-webkit-appearance"]="none",e.getRevealElement().appendChild(t),n.addEventListener("keyup",(function(t){if(13===t.keyCode)t.preventDefault(),function(){if(l){var t=n.value;""===t?(r&&r.remove(),i=null):(r=new c("slidecontent"),i=r.apply(t),o=0)}i&&(i.length&&i.length<=o&&(o=0),i.length>o&&(e.slide(i[o].h,i[o].v),o++))}(),l=!1;else l=!0}),!1),d()}function a(){t||s(),t.style.display="inline",n.focus(),n.select()}function d(){t||s(),t.style.display="none",r&&r.remove()}function c(t,n){var i=document.getElementById(t)||document.body,o=n||"EM",l=new RegExp("^(?:"+o+"|SCRIPT|FORM)$"),r=["#ff6","#a0ffff","#9f9","#f99","#f6f"],s=[],a=0,d="",c=[];this.setRegex=function(e){e=e.trim(),d=new RegExp("("+e+")","i")},this.getRegex=function(){return d.toString().replace(/^\/\\b\(|\)\\b\/i$/g,"").replace(/\|/g," ")},this.hiliteWords=function(t){if(null!=t&&t&&d&&!l.test(t.nodeName)){if(t.hasChildNodes())for(var n=0;n<t.childNodes.length;n++)this.hiliteWords(t.childNodes[n]);var i,f;if(3==t.nodeType)if((i=t.nodeValue)&&(f=d.exec(i))){for(var u=t;null!=u&&"SECTION"!=u.nodeName;)u=u.parentNode;var p=e.getIndices(u),h=c.length,y=!1;for(n=0;n<h;n++)c[n].h===p.h&&c[n].v===p.v&&(y=!0);y||c.push(p),s[f[0].toLowerCase()]||(s[f[0].toLowerCase()]=r[a++%r.length]);var g=document.createElement(o);g.appendChild(document.createTextNode(f[0])),g.style.backgroundColor=s[f[0].toLowerCase()],g.style.fontStyle="inherit",g.style.color="#000";var v=t.splitText(f.index);v.nodeValue=v.nodeValue.substring(f[0].length),t.parentNode.insertBefore(g,v)}}},this.remove=function(){for(var e,t=document.getElementsByTagName(o);t.length&&(e=t[0]);)e.parentNode.replaceChild(e.firstChild,e)},this.apply=function(e){if(null!=e&&e)return this.remove(),this.setRegex(e),this.hiliteWords(i),c}}return{id:"search",init:n=>{e=n,e.registerKeyboardShortcut("CTRL + Shift + F","Search"),document.addEventListener("keydown",(function(e){"F"==e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),t||s(),"inline"!==t.style.display?a():d())}),!1)},open:a}}}));
*/return()=>{let e,t,n,o,i,l,s;function r(){t=document.createElement("div"),t.classList.add("searchbox"),t.style.position="absolute",t.style.top="10px",t.style.right="10px",t.style.zIndex=10,t.innerHTML='<input type="search" class="searchinput" placeholder="Search..." style="vertical-align: top;"/>\n\t\t</span>',n=t.querySelector(".searchinput"),n.style.width="240px",n.style.fontSize="14px",n.style.padding="4px 6px",n.style.color="#000",n.style.background="#fff",n.style.borderRadius="2px",n.style.border="0",n.style.outline="0",n.style.boxShadow="0 2px 18px rgba(0, 0, 0, 0.2)",n.style["-webkit-appearance"]="none",e.getRevealElement().appendChild(t),n.addEventListener("keyup",(function(t){if(13===t.keyCode)t.preventDefault(),function(){if(l){var t=n.value;""===t?(s&&s.remove(),o=null):(s=new f("slidecontent"),o=s.apply(t),i=0)}o&&(o.length&&o.length<=i&&(i=0),o.length>i&&(e.slide(o[i].h,o[i].v),i++))}(),l=!1;else l=!0}),!1),d()}function a(){t||r(),t.style.display="inline",n.focus(),n.select()}function d(){t||r(),t.style.display="none",s&&s.remove()}function c(){t||r(),"inline"!==t.style.display?a():d()}function f(t,n){var o=document.getElementById(t)||document.body,i=n||"EM",l=new RegExp("^(?:"+i+"|SCRIPT|FORM)$"),s=["#ff6","#a0ffff","#9f9","#f99","#f6f"],r=[],a=0,d="",c=[];this.setRegex=function(e){e=e.trim(),d=new RegExp("("+e+")","i")},this.getRegex=function(){return d.toString().replace(/^\/\\b\(|\)\\b\/i$/g,"").replace(/\|/g," ")},this.hiliteWords=function(t){if(null!=t&&t&&d&&!l.test(t.nodeName)){if(t.hasChildNodes())for(var n=0;n<t.childNodes.length;n++)this.hiliteWords(t.childNodes[n]);var o,f;if(3==t.nodeType)if((o=t.nodeValue)&&(f=d.exec(o))){for(var u=t;null!=u&&"SECTION"!=u.nodeName;)u=u.parentNode;var p=e.getIndices(u),h=c.length,y=!1;for(n=0;n<h;n++)c[n].h===p.h&&c[n].v===p.v&&(y=!0);y||c.push(p),r[f[0].toLowerCase()]||(r[f[0].toLowerCase()]=s[a++%s.length]);var g=document.createElement(i);g.appendChild(document.createTextNode(f[0])),g.style.backgroundColor=r[f[0].toLowerCase()],g.style.fontStyle="inherit",g.style.color="#000";var v=t.splitText(f.index);v.nodeValue=v.nodeValue.substring(f[0].length),t.parentNode.insertBefore(g,v)}}},this.remove=function(){for(var e,t=document.getElementsByTagName(i);t.length&&(e=t[0]);)e.parentNode.replaceChild(e.firstChild,e)},this.apply=function(e){if(null!=e&&e)return this.remove(),this.setRegex(e),this.hiliteWords(o),c}}return{id:"search",init:t=>{e=t,e.registerKeyboardShortcut("CTRL + Shift + F","Search"),document.addEventListener("keydown",(function(e){"F"==e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),c())}),!1)},open:a,close:d,toggle:c}}}));

View File

@@ -147,7 +147,7 @@ var zoom = (function(){
}
/**
* Pan the document when the mosue cursor approaches the edges
* Pan the document when the mouse cursor approaches the edges
* of the window.
*/
function pan() {

View File

@@ -110,21 +110,6 @@
Reveal.configure({ autoAnimate: true });
});
QUnit.test( 'Carries forward matching fragment visibility', assert => {
Reveal.slide(4);
assert.ok( !slides[5].h1.classList.contains( 'visible' ) )
Reveal.next();
Reveal.next();
Reveal.next();
assert.ok( slides[5].h1.classList.contains( 'visible' ) )
assert.ok( slides[5].h2.classList.contains( 'visible' ) )
assert.ok( !slides[5].h3.classList.contains( 'visible' ) )
Reveal.next();
assert.ok( slides[5].h3.classList.contains( 'visible' ) )
Reveal.next();
assert.ok( slides[6].slide === Reveal.getCurrentSlide() )
});
QUnit.test( 'Slide specific data-auto-animate-duration', assert => {
assert.timeout( 400 );
assert.expect( 1 );

97
test/test-destroy.html Normal file
View File

@@ -0,0 +1,97 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Test Dependencies</title>
<link rel="stylesheet" href="../dist/reveal.css">
<link rel="stylesheet" href="../node_modules/qunit/qunit/qunit.css">
<script src="../node_modules/qunit/qunit/qunit.js"></script>
</head>
<body style="overflow: auto;">
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<div class="reveal deck1" style="display: none;">
<div class="slides">
<section>Slide content</section>
</div>
</div>
<div class="reveal deck2" style="display: none;">
<div class="slides">
<section>Slide content</section>
</div>
</div>
<script type="module">
import Reveal from '../dist/reveal.esm.js'
import Markdown from '../plugin/markdown/markdown.esm.js'
QUnit.module( 'Destroy' );
QUnit.test( 'Destruction during initialization', function( assert ) {
let deck = new Reveal( document.querySelector( '.deck1' ) );
deck.initialize({ plugins: [ Markdown ] });
let firstAttemptSuccess = false;
let repeatedAttemptSuccess = false;
try {
deck.destroy();
firstAttemptSuccess = true;
}
catch( error ) {
console.error( error );
}
assert.ok( firstAttemptSuccess, 'was successful' );
// should be able to destroy twice with no side effect
try {
deck.destroy();
repeatedAttemptSuccess = true;
}
catch( error ) {
console.error( error );
}
assert.ok( repeatedAttemptSuccess, 'destroyed twice with no exceptions' );
} );
QUnit.test( 'Destruction after initialization', function( assert ) {
assert.expect( 1 );
let done = assert.async( 1 );
let deck = new Reveal( document.querySelector( '.deck2' ) );
deck.initialize({ plugins: [ Markdown ] }).then(() => {
let wasSuccessful = false;
try {
deck.destroy();
wasSuccessful = true;
}
catch( error ) {
console.error( error );
}
if( wasSuccessful ) {
assert.ok( true, 'was successful' );
}
done();
});
} );
</script>
</body>
</html>

View File

@@ -36,68 +36,66 @@
QUnit.config.testTimeout = 30000;
Reveal.initialize({ viewDistance: 2 }).then( () => {
Reveal.initialize({ viewDistance: 2 });
var defaultIframe = document.querySelector( '.default-iframe' ),
preloadIframe = document.querySelector( '.preload-iframe' );
var defaultIframe = document.querySelector( '.default-iframe' ),
preloadIframe = document.querySelector( '.preload-iframe' );
QUnit.module( 'Iframe' );
QUnit.module( 'Iframe' );
QUnit.test( 'Using default settings', function( assert ) {
QUnit.test( 'Using default settings', function( assert ) {
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'not preloaded when within viewDistance' );
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'not preloaded when within viewDistance' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'unloaded when slide becomes invisible' );
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'unloaded when slide becomes invisible' );
});
});
QUnit.test( 'Using data-preload', function( assert ) {
QUnit.test( 'Using data-preload', function( assert ) {
Reveal.slide(1);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
Reveal.slide(1);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
Reveal.slide(2);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becoems visible' );
Reveal.slide(2);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
Reveal.slide(0);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), false, 'unloads outside of viewDistance' );
Reveal.slide(0);
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), false, 'unloads outside of viewDistance' );
});
});
QUnit.test( 'Using preloadIframes: true', function( assert ) {
QUnit.test( 'Using preloadIframes: true', function( assert ) {
Reveal.configure({ preloadIframes: true });
Reveal.configure({ preloadIframes: true });
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
Reveal.slide(1);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'preloaded within viewDistance' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
});
});
QUnit.test( 'Using preloadIframes: false', function( assert ) {
QUnit.test( 'Using preloadIframes: false', function( assert ) {
Reveal.configure({ preloadIframes: false });
Reveal.configure({ preloadIframes: false });
Reveal.slide(0);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'not preloaded within viewDistance' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), false, 'not preloaded within viewDistance' );
Reveal.slide(0);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), false, 'not preloaded within viewDistance' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), false, 'not preloaded within viewDistance' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
Reveal.slide(2);
assert.strictEqual( defaultIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
assert.strictEqual( preloadIframe.hasAttribute( 'src' ), true, 'loaded when slide becomes visible' );
});
} );
});
</script>
</body>