1
0
mirror of https://github.com/hakimel/reveal.js.git synced 2025-09-20 03:21:29 +02:00

Compare commits

..

78 Commits
4.6.0 ... 5.0.2

Author SHA1 Message Date
Hakim El Hattab
bddeb70f4e 5.0.2 2023-11-09 09:05:58 +01:00
Hakim El Hattab
1e1e228680 fix issue where background of a future vertical slide is briefly visible ahead of time #3520 2023-11-09 09:02:29 +01:00
Hakim El Hattab
11680561e9 nil check for deck in md plugin #3517 2023-11-06 18:10:53 +01:00
Hakim El Hattab
9d1c7e21b6 md plugin api works even if deck isn't available #3517 2023-11-06 10:59:12 +01:00
Hakim El Hattab
010f06c339 fix speaker view bug, bump version to 5.0.1 #3512 2023-10-30 07:37:05 +01:00
Hakim El Hattab
eee0a4ff24 scroll example deck tweaks 2023-10-29 21:31:25 +01:00
Hakim El Hattab
d14084d4a4 rebuild after deps update 2023-10-29 19:53:52 +01:00
Hakim El Hattab
30eddd95ed Merge pull request #3507 from Mister-Hope/deps
chore: bump deps
2023-10-29 19:52:27 +01:00
Hakim El Hattab
b6a3ea82e1 Merge pull request #3506 from Mister-Hope/typos
chore: fix typos
2023-10-29 19:43:38 +01:00
Hakim El Hattab
b2d0a3f70c Merge pull request #3505 from Mister-Hope/moon
chore: remove deprecated css declarations
2023-10-29 19:43:18 +01:00
Mr.Hope
73d3f3432f chore: bump deps 2023-10-28 18:49:41 +08:00
Mr.Hope
09b22cee6c chore: remove deprecated css declarations 2023-10-28 18:42:08 +08:00
Mr.Hope
97d73bd3dc chore: fix typos 2023-10-28 18:40:24 +08:00
Hakim El Hattab
790fd8c29a mute video in scroll demo 2023-10-27 14:57:55 +02:00
Hakim El Hattab
2518301d3e 5.0 2023-10-27 14:50:14 +02:00
Hakim El Hattab
89bf44ba92 remove legacy mousewheel listeners #3489 2023-10-27 10:35:37 +02:00
Hakim El Hattab
adfa3462cc Merge pull request #3489 from quochuy/mousewheelevents
Support to 'wheel' event listener
2023-10-27 09:43:13 +02:00
Hakim El Hattab
68efdf6b03 Merge branch 'master' into mousewheelevents 2023-10-27 09:42:32 +02:00
Hakim El Hattab
cc640a21d3 not important 2023-10-25 21:33:36 +02:00
Hakim El Hattab
c594f9c6ec not so important 2023-10-25 21:30:53 +02:00
Hakim El Hattab
942be4ee42 fix scroll view activation in tests 2023-10-25 14:13:51 +02:00
Hakim El Hattab
c23964274c reader mode -> scroll view, auto-enable below 435px width 2023-10-25 13:58:06 +02:00
Hakim El Hattab
e46bad392a fix scroll snapping in reader mode compact layout 2023-10-25 13:08:41 +02:00
Hakim El Hattab
ff252c984f reader mode now works for embedded decks 2023-10-25 11:32:40 +02:00
Hakim El Hattab
0072845828 ? keyboard shortcut should not trigger when focus is on an editable element fixes #2645 2023-10-25 11:04:53 +02:00
Hakim El Hattab
5b537aa8f8 fix slide numbers not visible in pdf exports 2023-10-24 10:19:59 +02:00
Hakim El Hattab
aa5c03c234 reader mode remembers scroll position when reloading 2023-10-23 13:08:17 +02:00
Hakim El Hattab
ff3244af7a reader mode refactoring 2023-10-23 11:24:50 +02:00
Hakim El Hattab
49c0030392 improved reader progress bar visuals in high density 2023-10-20 21:02:04 +02:00
Hakim El Hattab
51acc830f9 major cleanup of reader mode code 2023-10-20 20:23:31 +02:00
Hakim El Hattab
cc9a36dc25 massive reader mode refactor; adds support for auto-animate + snapping for fragments 2023-10-20 10:18:34 +02:00
Hakim El Hattab
a9031821ef add scroll snap points for reader mode scroll triggers 2023-10-18 11:16:06 +02:00
Hakim El Hattab
c1d64ad8d0 audit fix 2023-10-17 14:03:27 +02:00
Hakim El Hattab
57ce5a5e3d === 2023-10-17 14:03:00 +02:00
Hakim El Hattab
c4e322ce79 don't show reader scroll bar when there is no overflow, reader style tweaks 2023-10-17 13:59:11 +02:00
Hakim El Hattab
836967d8ab prevent extra page at end when printing to pdf, reader mode styling tweaks 2023-10-13 16:02:55 +02:00
Hakim El Hattab
28ef437a89 rebuild assets 2023-10-12 14:05:42 +02:00
Hakim El Hattab
c80b685a88 Merge pull request #3482 from hakimel/feature/reader-mode
Add reader mode
2023-10-12 14:04:44 +02:00
Hakim El Hattab
7108476911 Merge branch 'master' into feature/reader-mode 2023-10-12 14:04:36 +02:00
Hakim El Hattab
b8b55b8d4c readerScrollBar -> readerScrollbar 2023-10-12 14:03:28 +02:00
Hakim El Hattab
a7d0916f28 reader mode accessibility, bug fixes 2023-10-12 13:58:10 +02:00
Hakim El Hattab
198cbc4ace reader mode tweaks 2023-10-12 13:39:44 +02:00
Hakim El Hattab
d802789c4d more accurate scroll trigger positioning in progress bar 2023-10-12 11:09:47 +02:00
Hakim El Hattab
c1b1745200 audit fix 2023-10-11 10:43:15 +02:00
Hakim El Hattab
980b902a9d disable overview while in reader mode 2023-10-11 10:42:47 +02:00
Hakim El Hattab
be5d811914 convert sass controls spacing to css var, full height reader progress bar 2023-10-11 10:01:32 +02:00
Hakim El Hattab
122642fdea reader progress theming, automatically invert based on slide bg 2023-10-11 09:51:03 +02:00
Hakim El Hattab
09f36adc70 mobile tweaks 2023-10-11 08:24:06 +02:00
Hakim El Hattab
2c5a83c945 refactoring 2023-10-10 14:47:08 +02:00
Hakim El Hattab
a6abd0423e finishing touches on reader mode progress bar 2023-10-10 13:34:33 +02:00
Hakim El Hattab
234799114a reader mode progress bar can be dragged to scroll 2023-10-10 11:16:31 +02:00
Hakim El Hattab
f80ee3b917 reader mode progress bar 2023-10-10 10:24:02 +02:00
Hakim El Hattab
1871824fae reader mode; named deeplink support, stay on same slide when reader mode is turned on/off 2023-10-06 11:37:58 +02:00
Hakim El Hattab
1f1ca3a887 refactoring 2023-10-06 10:07:19 +02:00
Hakim El Hattab
d84aa3472e reader mode tests 2023-10-06 09:52:21 +02:00
Hakim El Hattab
ab52d334df add support for responsively activating reader mode via 2023-10-06 09:14:23 +02:00
NGUYEN DINH Quoc-Huy
42a1844d27 Support to 'wheel' event listener 2023-10-06 15:03:32 +11:00
Hakim El Hattab
899a45dff6 update api method name 2023-10-05 14:24:13 +02:00
Hakim El Hattab
3db2340df3 fix issues with active slide logic in reader mode, foundational work for auto-animate support 2023-10-05 14:06:06 +02:00
Hakim El Hattab
88fbfc5751 fix incorrect unit for slide-width/height css variable #1263 2023-09-26 10:20:29 -04:00
Hakim El Hattab
c856fa9db1 dispatch slidechange events in reader mode 2023-09-25 12:32:46 +02:00
Hakim El Hattab
4c9cc89566 refactoring, remove unused layout 2023-09-22 10:31:34 +02:00
Hakim El Hattab
97f2e184c1 fix preload bug 2023-09-21 14:15:46 +02:00
Hakim El Hattab
e49e89a557 reader mode supports scroll snapping, sticky pages with scroll triggers are always full height 2023-09-21 13:35:49 +02:00
Hakim El Hattab
f0950ba9ae rename 'mode' config value to 'view' 2023-09-20 16:11:31 +02:00
Hakim El Hattab
0861b07618 revamped reader mode sticky logic, add option for fullscreen pages 2023-09-20 15:00:15 +02:00
Hakim El Hattab
5de7da7692 reader mode can be turned off without reload, add Reveal.toggleReader() 2023-09-19 11:52:54 +02:00
Hakim El Hattab
db2523db27 add support for aside element notes inside of fragments (fixes #3478) 2023-09-19 09:29:53 +02:00
Hakim El Hattab
eb01f8f3a5 Merge pull request #3477 from skyboyer/3476-data-notes-does-not-work-on-slide-level
Notes plugin: notes from data-notes attribute were not shown
2023-09-19 09:20:19 +02:00
Yevhen Kozlov
cd948d4136 Notes plugin: notes from data-notes attribute were not shown 2023-09-17 23:29:31 +02:00
Hakim El Hattab
07a6cf1249 fix empty slide bug when all slides in a stack are hidden via data-visibility 2023-09-15 13:48:26 +02:00
Hakim El Hattab
4da6f6b30f refactoring, fix preload distance 2023-09-14 15:17:28 +02:00
Hakim El Hattab
eaf5f61318 reader mode; deeplink support, presentation scaling, scroll trigger fixes 2023-09-14 15:03:23 +02:00
Hakim El Hattab
0f27ef40fb revert demo changes to index.html 2023-09-14 13:19:33 +02:00
Hakim El Hattab
f26d31570e separate reader mode into individual controller, add scroll triggers for fragments 2023-09-14 13:00:31 +02:00
Hakim El Hattab
a4b7f9dff7 4.6.1, remove log 2023-09-13 12:23:36 +02:00
Hakim El Hattab
6aa1eae796 foundation for reader mode, activate via 'mode=reader/print' config param 2023-09-12 17:00:56 +02:00
Hakim El Hattab
487cc860f8 fix alpha overlap during scrolled code highlight transitions 2023-09-12 10:35:37 +02:00
63 changed files with 5629 additions and 10824 deletions

View File

@@ -1,27 +1,31 @@
name: tests
on: [push]
on:
- push
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
node-version:
- 18
- 20
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build --if-present
- run: npm test
env:
CI: true
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build --if-present
- run: npm test
env:
CI: true

View File

@@ -1,7 +1,5 @@
/test
/examples
.github
.gulpfile
.sass-cache
gulpfile.js
CONTRIBUTING.md

View File

@@ -5,7 +5,7 @@
* https://revealjs.com/pdf-export/
*/
html.print-pdf {
html.reveal-print {
* {
-webkit-print-color-adjust: exact;
}
@@ -70,6 +70,10 @@ html.print-pdf {
page-break-after: always;
}
.reveal .slides .pdf-page:last-of-type {
page-break-after: avoid;
}
.reveal .slides section {
visibility: visible !important;
display: block !important;
@@ -145,6 +149,7 @@ html.print-pdf {
display: block;
position: absolute;
font-size: 14px;
visibility: visible;
}
/* This accessibility tool is not useful in PDF and breaks it visually */

View File

@@ -19,6 +19,7 @@ html.reveal-full-page {
height: 100%;
height: 100vh;
height: calc( var(--vh, 1vh) * 100 );
height: 100svh;
overflow: hidden;
}
@@ -31,6 +32,8 @@ html.reveal-full-page {
background-color: #fff;
color: #000;
--r-controls-spacing: 12px;
}
// Force the presentation to cover the full viewport when we
@@ -271,13 +274,11 @@ $controlsArrowAngleActive: 36deg;
}
.reveal .controls {
$spacing: 12px;
display: none;
position: absolute;
top: auto;
bottom: $spacing;
right: $spacing;
bottom: var(--r-controls-spacing);
right: var(--r-controls-spacing);
left: auto;
z-index: 11;
color: #000;
@@ -509,7 +510,9 @@ $controlsArrowAngleActive: 36deg;
// Edge aligned controls layout
@media screen and (min-width: 500px) {
$spacing: 0.8em;
.reveal-viewport {
--r-controls-spacing: 0.8em;
}
.reveal .controls[data-controls-layout="edges"] {
& {
@@ -529,24 +532,24 @@ $controlsArrowAngleActive: 36deg;
.navigate-left {
top: 50%;
left: $spacing;
left: var(--r-controls-spacing);
margin-top: -$controlArrowSize*0.5;
}
.navigate-right {
top: 50%;
right: $spacing;
right: var(--r-controls-spacing);
margin-top: -$controlArrowSize*0.5;
}
.navigate-up {
top: $spacing;
top: var(--r-controls-spacing);
left: 50%;
margin-left: -$controlArrowSize*0.5;
}
.navigate-down {
bottom: $spacing - $controlArrowSpacing + 0.3em;
bottom: calc(var(--r-controls-spacing) - #{$controlArrowSpacing} + 0.3em);
left: 50%;
margin-left: -$controlArrowSize*0.5;
}
@@ -1636,6 +1639,10 @@ $overlayHeaderPadding: 5px;
opacity: 0.4;
}
.reveal .hljs.has-highlights.fragment {
transition: all .2s ease;
}
.reveal .hljs:not(:first-child).fragment {
position: absolute;
top: 0;
@@ -1860,6 +1867,234 @@ $notesWidthPercent: 25%;
}
/*********************************************
* SCROLL VIEW
*********************************************/
.reveal-viewport.loading-scroll-mode {
visibility: hidden;
}
.reveal-viewport.reveal-scroll {
& {
margin: 0 auto;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
z-index: 1;
--r-scrollbar-width: 7px;
--r-scrollbar-trigger-size: 5px;
--r-controls-spacing: 8px;
}
@media screen and (max-width: 500px) {
--r-scrollbar-width: 3px;
--r-scrollbar-trigger-size: 3px;
}
.controls,
.progress,
.playback,
.backgrounds,
.slide-number,
.speaker-notes {
display: none !important;
}
.reveal {
overflow: visible;
touch-action: manipulation;
}
.slides {
position: static;
pointer-events: initial;
left: auto;
top: auto;
width: 100% !important;
margin: 0;
padding: 0;
overflow: visible;
display: block;
perspective: none;
perspective-origin: 50% 50%;
}
.scroll-page {
position: relative;
width: 100%;
height: calc(var(--page-height) + var(--page-scroll-padding));
z-index: 1;
overflow: visible;
}
.scroll-page-sticky {
position: sticky;
height: var(--page-height);
top: 0px;
}
.scroll-page-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.scroll-page section {
visibility: visible !important;
display: block !important;
position: absolute !important;
width: var(--slide-width) !important;
height: var(--slide-height) !important;
top: 50% !important;
left: 50% !important;
opacity: 1 !important;
transform: scale(var(--slide-scale)) translate(-50%, -50%) !important;
transform-style: flat !important;
transform-origin: 0 0 !important;
}
.slide-background {
display: block !important;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: auto !important;
visibility: visible;
opacity: 1;
touch-action: manipulation;
}
}
// Chromium
.reveal-viewport.reveal-scroll[data-scrollbar="true"]::-webkit-scrollbar,
.reveal-viewport.reveal-scroll[data-scrollbar="auto"]::-webkit-scrollbar {
display: none;
}
// Firefox
.reveal-viewport.reveal-scroll[data-scrollbar="true"],
.reveal-viewport.reveal-scroll[data-scrollbar="auto"] {
scrollbar-width: none;
}
.reveal.has-dark-background,
.reveal-viewport.has-dark-background {
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal.has-light-background,
.reveal-viewport.has-light-background {
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport.reveal-scroll .scrollbar {
position: sticky;
top: 50%;
z-index: 20;
opacity: 0;
transition: all 0.3s ease;
&.visible,
&:hover {
opacity: 1;
}
.scrollbar-inner {
position: absolute;
width: var(--r-scrollbar-width);
height: calc(var(--viewport-height) - var(--r-controls-spacing) * 2);
right: var(--r-controls-spacing);
top: 0;
transform: translateY(-50%);
border-radius: var(--r-scrollbar-width);
z-index: 10;
}
.scrollbar-playhead {
position: absolute;
width: var(--r-scrollbar-width);
height: var(--r-scrollbar-width);
top: 0;
left: 0;
border-radius: var(--r-scrollbar-width);
background-color: rgba(var(--r-overlay-element-bg-color), 1);
z-index: 11;
transition: background-color 0.2s ease;
}
.scrollbar-slide {
position: absolute;
width: 100%;
background-color: rgba(var(--r-overlay-element-bg-color), 0.2);
box-shadow: 0 0 0px 1px rgba(var(--r-overlay-element-fg-color), 0.1);
border-radius: var(--r-scrollbar-width);
transition: background-color 0.2s ease;
}
// Hit area
.scrollbar-slide:after {
content: '';
position: absolute;
width: 200%;
height: 100%;
top: 0;
left: -50%;
background: rgba( 0, 0, 0, 0 );
z-index: -1;
}
.scrollbar-slide:hover,
.scrollbar-slide.active {
background-color: rgba(var(--r-overlay-element-bg-color), 0.4);
}
.scrollbar-trigger {
position: absolute;
width: 100%;
transition: background-color 0.2s ease;
}
.scrollbar-slide.active.has-triggers {
background-color: rgba(var(--r-overlay-element-bg-color), 0.4);
z-index: 10;
}
.scrollbar-slide.active .scrollbar-trigger:after {
content: '';
position: absolute;
width: var(--r-scrollbar-trigger-size);
height: var(--r-scrollbar-trigger-size);
border-radius: 20px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(var(--r-overlay-element-bg-color), 1);
transition: transform 0.2s ease, opacity 0.2s ease;
opacity: 0.4;
}
.scrollbar-slide.active .scrollbar-trigger.active:after,
.scrollbar-slide.active .scrollbar-trigger.active ~ .scrollbar-trigger:after {
opacity: 1;
}
.scrollbar-slide.active .scrollbar-trigger ~ .scrollbar-trigger.active:after {
transform: translate(calc( var(--r-scrollbar-width) * -2), 0);
background-color: rgba(var(--r-overlay-element-bg-color), 1);
}
}
/*********************************************
* PRINT STYLES
*********************************************/

View File

@@ -27,6 +27,9 @@ $linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: rgba(79, 64, 28, 0.99);
$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Background generator
@mixin bodyBackground() {
@include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) );

View File

@@ -18,10 +18,6 @@
/**
* Solarized colors by Ethan Schoonover
*/
html * {
color-profile: sRGB;
rendering-intent: auto;
}
// Solarized colors
$base03: #002b36;

View File

@@ -25,6 +25,9 @@ $linkColor: #51483D;
$linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: #26351C;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
.reveal a {
line-height: 1.3em;
}

View File

@@ -31,6 +31,9 @@ $linkColor: #00008B;
$linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: rgba(0, 0, 0, 0.99);
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Change text colors against dark slide backgrounds
@include dark-bg-text-color(#fff);

View File

@@ -29,6 +29,9 @@ $linkColor: #3b759e;
$linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: #134674;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Fix links so they are not cut off
.reveal a {
line-height: 1.3em;

View File

@@ -51,6 +51,9 @@ $linkColor: $blue;
$linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: $magenta;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Background generator
// @mixin bodyBackground() {
// @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) );

View File

@@ -40,6 +40,9 @@ $heading2Size: 1.6em;
$heading3Size: 1.3em;
$heading4Size: 1.0em;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Change text colors against dark slide backgrounds
@include dark-bg-text-color(#fff);

View File

@@ -37,6 +37,9 @@ $heading2Size: 1.6em;
$heading3Size: 1.3em;
$heading4Size: 1.0em;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
// Change text colors against dark slide backgrounds
@include dark-bg-text-color(#fff);

View File

@@ -25,4 +25,6 @@
--r-link-color-hover: #{$linkColorHover};
--r-selection-background-color: #{$selectionBackgroundColor};
--r-selection-color: #{$selectionColor};
--r-overlay-element-bg-color: #{$overlayElementBgColor};
--r-overlay-element-fg-color: #{$overlayElementFgColor};
}

View File

@@ -38,6 +38,11 @@ $linkColorHover: lighten( $linkColor, 20% );
$selectionBackgroundColor: #FF5E99;
$selectionColor: #fff;
// Colors used for UI elements that are overlaid on top of
// the presentation
$overlayElementBgColor: 240, 240, 240;
$overlayElementFgColor: 0, 0, 0;
// Generates the presentation background, can be overridden
// to return a background image or gradient
@mixin bodyBackground() {

4
dist/reveal.css vendored

File diff suppressed because one or more lines are too long

4
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

4
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

16
dist/theme/beige.css vendored
View File

@@ -37,16 +37,18 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #c0a86e;
--r-selection-background-color: rgba(79, 64, 28, 0.99);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {
background: #f7f2d3;
background: -moz-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, white), color-stop(100%, #f7f2d3));
background: -webkit-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -o-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: -ms-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: radial-gradient(center, circle cover, white 0%, #f7f2d3 100%);
background: rgb(247, 242, 211);
background: -moz-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, rgb(255, 255, 255)), color-stop(100%, rgb(247, 242, 211)));
background: -webkit-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -o-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: -ms-radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background: radial-gradient(center, circle cover, rgb(255, 255, 255) 0%, rgb(247, 242, 211) 100%);
background-color: var(--r-background-color);
}

View File

@@ -39,6 +39,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #8dcffc;
--r-selection-background-color: #bee4fd;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

View File

@@ -36,6 +36,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #8dcffc;
--r-selection-background-color: rgba(66, 175, 250, 0.75);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

View File

@@ -42,6 +42,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #dd5566;
--r-selection-background-color: #a23;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

View File

@@ -43,6 +43,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #8BE9FD;
--r-selection-background-color: #44475A;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

16
dist/theme/league.css vendored
View File

@@ -39,16 +39,18 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #71e9f4;
--r-selection-background-color: #FF5E99;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {
background: #1c1e20;
background: -moz-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #555a5f), color-stop(100%, #1c1e20));
background: -webkit-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -o-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: -ms-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
background: rgb(28, 30, 32);
background: -moz-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, rgb(85, 90, 95)), color-stop(100%, rgb(28, 30, 32)));
background: -webkit-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -o-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: -ms-radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background: radial-gradient(center, circle cover, rgb(85, 90, 95) 0%, rgb(28, 30, 32) 100%);
background-color: var(--r-background-color);
}

7
dist/theme/moon.css vendored
View File

@@ -7,11 +7,6 @@
/**
* Solarized colors by Ethan Schoonover
*/
html * {
color-profile: sRGB;
rendering-intent: auto;
}
section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
color: #222;
}
@@ -44,6 +39,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #78b9e6;
--r-selection-background-color: #d33682;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

View File

@@ -37,6 +37,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
--r-link-color-hover: #f3d7ac;
--r-selection-background-color: #e7ad52;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 240, 240, 240;
--r-overlay-element-fg-color: 0, 0, 0;
}
.reveal-viewport {

View File

@@ -40,6 +40,8 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #8b7c69;
--r-selection-background-color: #26351C;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

View File

@@ -39,6 +39,8 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #0000f1;
--r-selection-background-color: rgba(0, 0, 0, 0.99);
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

2
dist/theme/sky.css vendored
View File

@@ -41,6 +41,8 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #74a7cb;
--r-selection-background-color: #134674;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

View File

@@ -40,6 +40,8 @@ html * {
--r-link-color-hover: #78b9e6;
--r-selection-background-color: #d33682;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

View File

@@ -39,6 +39,8 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #6ca0e8;
--r-selection-background-color: #98bdef;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

View File

@@ -36,6 +36,8 @@ section.has-dark-background, section.has-dark-background h1, section.has-dark-ba
--r-link-color-hover: #6ca0e8;
--r-selection-background-color: #98bdef;
--r-selection-color: #fff;
--r-overlay-element-bg-color: 0, 0, 0;
--r-overlay-element-fg-color: 240, 240, 240;
}
.reveal-viewport {

118
examples/scroll.html Normal file
View File

@@ -0,0 +1,118 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Scroll View</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="../dist/reset.css">
<link rel="stylesheet" href="../dist/reveal.css">
<link rel="stylesheet" href="../dist/theme/black.css" id="theme">
<link rel="stylesheet" href="../plugin/highlight/monokai.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section><h1>Scroll View</h1></section>
<section data-background="indigo">
<h2>Scroll triggered fragments</h2>
<ul>
<li class="fragment fade-left">Step one</li>
<li class="fragment fade-left">Step two</li>
<li class="fragment fade-left">Step three</li>
</ul>
</section>
<section data-background-color="#fff"><h2>Scrollbar inverts<br>based on slide bg</h2></section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<h2>Auto-Animate</h2>
<p>Scroll triggered auto-animations 😍</p>
<div class="r-hstack justify-center">
<div data-id="box1" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
<div data-id="box2" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
<div data-id="box3" style="background: #999; width: 50px; height: 50px; margin: 10px; border-radius: 5px;"></div>
</div>
</section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<div class="r-hstack justify-center">
<div data-id="box1" data-auto-animate-delay="0" style="background: cyan; width: 150px; height: 100px; margin: 10px;"></div>
<div data-id="box2" data-auto-animate-delay="0.1" style="background: magenta; width: 150px; height: 100px; margin: 10px;"></div>
<div data-id="box3" data-auto-animate-delay="0.2" style="background: yellow; width: 150px; height: 100px; margin: 10px;"></div>
</div>
<h2 style="margin-top: 20px;">Auto-Animate</h2>
</section>
<section data-auto-animate data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)">
<div class="r-stack">
<div data-id="box1" style="background: cyan; width: 300px; height: 300px; border-radius: 200px;"></div>
<div data-id="box2" style="background: magenta; width: 200px; height: 200px; border-radius: 200px;"></div>
<div data-id="box3" style="background: yellow; width: 100px; height: 100px; border-radius: 200px;"></div>
</div>
<h2 style="margin-top: 20px;">Auto-Animate</h2>
</section>
<section data-background-gradient="linear-gradient(to bottom, #283b95, #17b2c3)" id="gradient-bg">
<h2 data-id="code-title">Code highlights,<br />meet scroll triggers</h2>
<pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template">
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
function SecondExample() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
</script></code></pre>
</section>
<section class="stack">
<section data-background="https://static.slid.es/reveal/image-placeholder.png" id="image-bg">
<h2>Image Backgrounds</h2>
</section>
<section data-background-video-muted data-background-video="https://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>
</section>
<section><h2>The end</h2></section>
</div>
</div>
<script src="../dist/reveal.js"></script>
<script src="../plugin/notes/notes.js"></script>
<script src="../plugin/markdown/markdown.js"></script>
<script src="../plugin/highlight/highlight.js"></script>
<script>
Reveal.initialize({
view: 'scroll',
hash: true,
plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
});
</script>
</body>
</html>

View File

@@ -1,13 +1,11 @@
const pkg = require('./package.json')
const path = require('path')
const glob = require('glob')
const yargs = require('yargs')
const colors = require('colors')
const through = require('through2');
const qunit = require('node-qunit-puppeteer')
const {rollup} = require('rollup')
const {terser} = require('rollup-plugin-terser')
const terser = require('@rollup/plugin-terser')
const babel = require('@rollup/plugin-babel').default
const commonjs = require('@rollup/plugin-commonjs')
const resolve = require('@rollup/plugin-node-resolve').default

View File

@@ -256,6 +256,36 @@ export default {
parallaxBackgroundHorizontal: null,
parallaxBackgroundVertical: null,
// Can be used to initialize reveal.js in one of the following views:
// - print: Render the presentation so that it can be printed to PDF
// - scroll: Show the presentation as a tall scrollable page with scroll
// triggered animations
view: null,
// Adjusts the height of each slide in the scroll view.
// - full: Each slide is as tall as the viewport
// - compact: Slides are as small as possible, allowing multiple slides
// to be visible in parallel on tall devices
scrollLayout: 'full',
// Control how scroll snapping works in the scroll view.
// - false: No snapping, scrolling is continuous
// - proximity: Snap when close to a slide
// - mandatory: Always snap to the closest slide
//
// Only applies to presentations in scroll view.
scrollSnap: 'mandatory',
// Enables and configure the scroll view progress bar.
// - 'auto': Show the scrollbar while scrolling, hide while idle
// - true: Always show the scrollbar
// - false: Never show the scrollbar
scrollProgress: 'auto',
// Automatically activate the scroll view when we the viewport falls
// below the given width.
scrollActivationWidth: 435,
// The maximum number of pages a single slide can expand onto when printing
// to PDF, unlimited by default
pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY,
@@ -287,7 +317,7 @@ export default {
// Time before the cursor is hidden (in ms)
hideCursorTime: 5000,
// Should we automatmically sort and set indices for fragments
// Should we automatically sort and set indices for fragments
// at each sync? (See Reveal.sync)
sortFragmentsOnSync: true,

View File

@@ -190,10 +190,30 @@ export default class Backgrounds {
if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition;
if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity;
const contrastClass = this.getContrastClass( slide );
if( typeof contrastClass === 'string' ) {
slide.classList.add( contrastClass );
}
}
/**
* Returns a class name that can be applied to a slide to indicate
* if it has a light or dark background.
*
* @param {*} slide
*
* @returns {string|null}
*/
getContrastClass( slide ) {
const element = slide.slideBackgroundElement;
// If this slide has a background color, we add a class that
// signals if it is light or dark. If the slide has no background
// color, no class will be added
let contrastColor = data.backgroundColor;
let contrastColor = slide.getAttribute( 'data-background-color' );
// If no bg color was found, or it cannot be converted by colorToRgb, check the computed background
if( !contrastColor || !colorToRgb( contrastColor ) ) {
@@ -211,14 +231,32 @@ export default class Backgrounds {
// an element with no background
if( rgb && rgb.a !== 0 ) {
if( colorBrightness( contrastColor ) < 128 ) {
slide.classList.add( 'has-dark-background' );
return 'has-dark-background';
}
else {
slide.classList.add( 'has-light-background' );
return 'has-light-background';
}
}
}
return null;
}
/**
* Bubble the 'has-light-background'/'has-dark-background' classes.
*/
bubbleSlideContrastClassToElement( slide, target ) {
[ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => {
if( slide.classList.contains( classToBubble ) ) {
target.classList.add( classToBubble );
}
else {
target.classList.remove( classToBubble );
}
}, this );
}
/**
@@ -263,10 +301,12 @@ export default class Backgrounds {
backgroundv.classList.remove( 'past', 'present', 'future' );
if( v < indices.v ) {
const indexv = typeof indices.v === 'number' ? indices.v : 0;
if( v < indexv ) {
backgroundv.classList.add( 'past' );
}
else if ( v > indices.v ) {
else if ( v > indexv ) {
backgroundv.classList.add( 'future' );
}
else {
@@ -322,14 +362,7 @@ export default class Backgrounds {
// If there's a background brightness flag for this slide,
// bubble it to the .reveal container
if( currentSlide ) {
[ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => {
if( currentSlide.classList.contains( classToBubble ) ) {
this.Reveal.getRevealElement().classList.add( classToBubble );
}
else {
this.Reveal.getRevealElement().classList.remove( classToBubble );
}
}, this );
this.bubbleSlideContrastClassToElement( currentSlide, this.Reveal.getRevealElement() );
}
// Allow the first background to apply without transition

View File

@@ -174,24 +174,23 @@ export default class Fragments {
*
* @return {{shown: array, hidden: array}}
*/
update( index, fragments ) {
update( index, fragments, slide = this.Reveal.getCurrentSlide() ) {
let changedFragments = {
shown: [],
hidden: []
};
let currentSlide = this.Reveal.getCurrentSlide();
if( currentSlide && this.Reveal.getConfig().fragments ) {
if( slide && this.Reveal.getConfig().fragments ) {
fragments = fragments || this.sort( currentSlide.querySelectorAll( '.fragment' ) );
fragments = fragments || this.sort( slide.querySelectorAll( '.fragment' ) );
if( fragments.length ) {
let maxIndex = 0;
if( typeof index !== 'number' ) {
let currentFragment = this.sort( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
let currentFragment = this.sort( slide.querySelectorAll( '.fragment.visible' ) ).pop();
if( currentFragment ) {
index = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );
}
@@ -252,7 +251,7 @@ export default class Fragments {
// the current fragment index.
index = typeof index === 'number' ? index : -1;
index = Math.max( Math.min( index, maxIndex ), -1 );
currentSlide.setAttribute( 'data-fragment', index );
slide.setAttribute( 'data-fragment', index );
}

View File

@@ -17,7 +17,6 @@ export default class Keyboard {
this.bindings = {};
this.onDocumentKeyDown = this.onDocumentKeyDown.bind( this );
this.onDocumentKeyPress = this.onDocumentKeyPress.bind( this );
}
@@ -54,7 +53,6 @@ export default class Keyboard {
bind() {
document.addEventListener( 'keydown', this.onDocumentKeyDown, false );
document.addEventListener( 'keypress', this.onDocumentKeyPress, false );
}
@@ -64,7 +62,6 @@ export default class Keyboard {
unbind() {
document.removeEventListener( 'keydown', this.onDocumentKeyDown, false );
document.removeEventListener( 'keypress', this.onDocumentKeyPress, false );
}
@@ -135,20 +132,6 @@ export default class Keyboard {
}
/**
* Handler for the document level 'keypress' event.
*
* @param {object} event
*/
onDocumentKeyPress( event ) {
// Check if the pressed key is question mark
if( event.shiftKey && event.charCode === 63 ) {
this.Reveal.toggleHelp();
}
}
/**
* Handler for the document level 'keydown' event.
*
@@ -184,10 +167,10 @@ export default class Keyboard {
let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
// Whitelist certain modifiers for slide navigation shortcuts
let isNavigationKey = [32, 37, 38, 39, 40, 78, 80].indexOf( event.keyCode ) !== -1;
let keyCodeUsesModifier = [32, 37, 38, 39, 40, 78, 80, 191].indexOf( event.keyCode ) !== -1;
// Prevent all other events when a modifier is pressed
let unusedModifier = !( isNavigationKey && event.shiftKey || event.altKey ) &&
let unusedModifier = !( keyCodeUsesModifier && event.shiftKey || event.altKey ) &&
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
// Disregard the event if there's a focused element or a
@@ -351,7 +334,7 @@ export default class Keyboard {
}
}
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
else if( keyCode === 58 || keyCode === 59 || keyCode === 66 || keyCode === 86 || keyCode === 190 || keyCode === 191 ) {
else if( [58, 59, 66, 86, 190].includes( keyCode ) || ( keyCode === 191 && !event.shiftKey ) ) {
this.Reveal.togglePause();
}
// F
@@ -360,16 +343,20 @@ export default class Keyboard {
}
// A
else if( keyCode === 65 ) {
if ( config.autoSlideStoppable ) {
if( config.autoSlideStoppable ) {
this.Reveal.toggleAutoSlide( autoSlideWasPaused );
}
}
// G
else if( keyCode === 71 ) {
if ( config.jumpToSlide ) {
if( config.jumpToSlide ) {
this.Reveal.toggleJumpToSlide();
}
}
// ?
else if( keyCode === 191 && event.shiftKey ) {
this.Reveal.toggleHelp();
}
else {
triggered = false;
}

View File

@@ -64,7 +64,7 @@ export default class Location {
try {
slide = document
.getElementById( decodeURIComponent( name ) )
.closest('.slides>section, .slides>section>section');
.closest('.slides section');
}
catch ( error ) { }

View File

@@ -38,10 +38,12 @@ export default class Notes {
*/
update() {
if( this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.print.isPrintingPDF() ) {
if( this.Reveal.getConfig().showNotes &&
this.element && this.Reveal.getCurrentSlide() &&
!this.Reveal.isScrollView() &&
!this.Reveal.isPrintView()
) {
this.element.innerHTML = this.getSlideNotes() || '<span class="notes-placeholder">No notes on this slide.</span>';
}
}
@@ -54,7 +56,11 @@ export default class Notes {
*/
updateVisibility() {
if( this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.print.isPrintingPDF() ) {
if( this.Reveal.getConfig().showNotes &&
this.hasNotes() &&
!this.Reveal.isScrollView() &&
!this.Reveal.isPrintView()
) {
this.Reveal.getRevealElement().classList.add( 'show-notes' );
}
else {

View File

@@ -24,7 +24,7 @@ export default class Overview {
activate() {
// Only proceed if enabled in config
if( this.Reveal.getConfig().overview && !this.isActive() ) {
if( this.Reveal.getConfig().overview && !this.Reveal.isScrollView() && !this.isActive() ) {
this.active = true;

View File

@@ -27,12 +27,10 @@ export default class Pointer {
configure( config, oldConfig ) {
if( config.mouseWheel ) {
document.addEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF
document.addEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.addEventListener( 'wheel', this.onDocumentMouseScroll, false );
}
else {
document.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF
document.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'wheel', this.onDocumentMouseScroll, false );
}
// Auto-hide the mouse pointer when its inactive
@@ -79,8 +77,7 @@ export default class Pointer {
this.showCursor();
document.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false );
document.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'wheel', this.onDocumentMouseScroll, false );
document.removeEventListener( 'mousemove', this.onDocumentCursorActive, false );
document.removeEventListener( 'mousedown', this.onDocumentCursorActive, false );
@@ -126,4 +123,4 @@ export default class Pointer {
}
}
}

View File

@@ -4,7 +4,7 @@ import { queryAll, createStyleSheet } from '../utils/util.js'
/**
* Setups up our presentation for printing/exporting to PDF.
*/
export default class Print {
export default class PrintView {
constructor( Reveal ) {
@@ -16,7 +16,7 @@ export default class Print {
* Configures the presentation for printing to a static
* PDF.
*/
async setupPDF() {
async activate() {
const config = this.Reveal.getConfig();
const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )
@@ -42,11 +42,11 @@ export default class Print {
// Limit the size of certain elements to the dimensions of the slide
createStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
document.documentElement.classList.add( 'print-pdf' );
document.documentElement.classList.add( 'reveal-print', 'print-pdf' );
document.body.style.width = pageWidth + 'px';
document.body.style.height = pageHeight + 'px';
const viewportElement = document.querySelector( '.reveal-viewport' );
const viewportElement = this.Reveal.getViewportElement();
let presentationBackground;
if( viewportElement ) {
const viewportStyles = window.getComputedStyle( viewportElement );
@@ -226,12 +226,12 @@ export default class Print {
}
/**
* Checks if this instance is being used to print a PDF.
* Checks if the print mode is/should be activated.
*/
isPrintingPDF() {
isActive() {
return ( /print-pdf/gi ).test( window.location.search );
return this.Reveal.getConfig().view === 'print';
}
}
}

View File

@@ -0,0 +1,888 @@
import { HORIZONTAL_SLIDES_SELECTOR } from '../utils/constants.js'
import { queryAll } from '../utils/util.js'
const HIDE_SCROLLBAR_TIMEOUT = 500;
const MAX_PROGRESS_SPACING = 4;
const MIN_PROGRESS_SEGMENT_HEIGHT = 6;
const MIN_PLAYHEAD_HEIGHT = 8;
/**
* The scroll view lets you read a reveal.js presentation
* as a linear scrollable page.
*/
export default class ScrollView {
constructor( Reveal ) {
this.Reveal = Reveal;
this.active = false;
this.activatedCallbacks = [];
this.onScroll = this.onScroll.bind( this );
}
/**
* Activates the scroll view. This rearranges the presentation DOM
* by—among other things—wrapping each slide in a page element.
*/
activate() {
if( this.active ) return;
const stateBeforeActivation = this.Reveal.getState();
this.active = true;
// Store the full presentation HTML so that we can restore it
// when/if the scroll view is deactivated
this.slideHTMLBeforeActivation = this.Reveal.getSlidesElement().innerHTML;
const horizontalSlides = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_SLIDES_SELECTOR );
this.viewportElement.classList.add( 'loading-scroll-mode', 'reveal-scroll' );
let presentationBackground;
const viewportStyles = window.getComputedStyle( this.viewportElement );
if( viewportStyles && viewportStyles.background ) {
presentationBackground = viewportStyles.background;
}
const pageElements = [];
const pageContainer = horizontalSlides[0].parentNode;
let previousSlide;
// Creates a new page element and appends the given slide/bg
// to it.
const createPageElement = ( slide, h, v ) => {
let contentContainer;
// If this slide is part of an auto-animation sequence, we
// group it under the same page element as the previous slide
if( previousSlide && this.Reveal.shouldAutoAnimateBetween( previousSlide, slide ) ) {
contentContainer = document.createElement( 'div' );
contentContainer.className = 'scroll-page-content scroll-auto-animate-page';
contentContainer.style.display = 'none';
previousSlide.closest( '.scroll-page-content' ).parentNode.appendChild( contentContainer );
}
else {
// Wrap the slide in a page element and hide its overflow
// so that no page ever flows onto another
const page = document.createElement( 'div' );
page.className = 'scroll-page';
pageElements.push( page );
// Copy the presentation-wide background to each page
if( presentationBackground ) {
page.style.background = presentationBackground;
}
const stickyContainer = document.createElement( 'div' );
stickyContainer.className = 'scroll-page-sticky';
page.appendChild( stickyContainer );
contentContainer = document.createElement( 'div' );
contentContainer.className = 'scroll-page-content';
stickyContainer.appendChild( contentContainer );
}
contentContainer.appendChild( slide );
slide.classList.remove( 'past', 'future' );
slide.setAttribute( 'data-index-h', h );
slide.setAttribute( 'data-index-v', v );
if( slide.slideBackgroundElement ) {
slide.slideBackgroundElement.remove( 'past', 'future' );
contentContainer.insertBefore( slide.slideBackgroundElement, slide );
}
previousSlide = slide;
}
// Slide and slide background layout
horizontalSlides.forEach( ( horizontalSlide, h ) => {
if( this.Reveal.isVerticalStack( horizontalSlide ) ) {
horizontalSlide.querySelectorAll( 'section' ).forEach( ( verticalSlide, v ) => {
createPageElement( verticalSlide, h, v );
});
}
else {
createPageElement( horizontalSlide, h, 0 );
}
}, this );
this.createProgressBar();
// Remove leftover stacks
queryAll( this.Reveal.getRevealElement(), '.stack' ).forEach( stack => stack.remove() );
// Add our newly created pages to the DOM
pageElements.forEach( page => pageContainer.appendChild( page ) );
// Re-run JS-based content layout after the slide is added to page DOM
this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
this.Reveal.layout();
this.Reveal.setState( stateBeforeActivation );
this.activatedCallbacks.forEach( callback => callback() );
this.activatedCallbacks = [];
this.restoreScrollPosition();
this.viewportElement.classList.remove( 'loading-scroll-mode' );
this.viewportElement.addEventListener( 'scroll', this.onScroll, { passive: true } );
}
/**
* Deactivates the scroll view and restores the standard slide-based
* presentation.
*/
deactivate() {
if( !this.active ) return;
const stateBeforeDeactivation = this.Reveal.getState();
this.active = false;
this.viewportElement.removeEventListener( 'scroll', this.onScroll );
this.viewportElement.classList.remove( 'reveal-scroll' );
this.removeProgressBar();
this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation;
this.Reveal.sync();
this.Reveal.setState( stateBeforeDeactivation );
this.slideHTMLBeforeActivation = null;
}
toggle( override ) {
if( typeof override === 'boolean' ) {
override ? this.activate() : this.deactivate();
}
else {
this.isActive() ? this.deactivate() : this.activate();
}
}
/**
* Checks if the scroll view is currently active.
*/
isActive() {
return this.active;
}
/**
* Renders the progress bar component.
*/
createProgressBar() {
this.progressBar = document.createElement( 'div' );
this.progressBar.className = 'scrollbar';
this.progressBarInner = document.createElement( 'div' );
this.progressBarInner.className = 'scrollbar-inner';
this.progressBar.appendChild( this.progressBarInner );
this.progressBarPlayhead = document.createElement( 'div' );
this.progressBarPlayhead.className = 'scrollbar-playhead';
this.progressBarInner.appendChild( this.progressBarPlayhead );
this.viewportElement.insertBefore( this.progressBar, this.viewportElement.firstChild );
const handleDocumentMouseMove = ( event ) => {
let progress = ( event.clientY - this.progressBarInner.getBoundingClientRect().top ) / this.progressBarHeight;
progress = Math.max( Math.min( progress, 1 ), 0 );
this.viewportElement.scrollTop = progress * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
};
const handleDocumentMouseUp = ( event ) => {
this.draggingProgressBar = false;
this.showProgressBar();
document.removeEventListener( 'mousemove', handleDocumentMouseMove );
document.removeEventListener( 'mouseup', handleDocumentMouseUp );
};
const handleMouseDown = ( event ) => {
event.preventDefault();
this.draggingProgressBar = true;
document.addEventListener( 'mousemove', handleDocumentMouseMove );
document.addEventListener( 'mouseup', handleDocumentMouseUp );
handleDocumentMouseMove( event );
};
this.progressBarInner.addEventListener( 'mousedown', handleMouseDown );
}
removeProgressBar() {
if( this.progressBar ) {
this.progressBar.remove();
this.progressBar = null;
}
}
layout() {
if( this.isActive() ) {
this.syncPages();
this.syncScrollPosition();
}
}
/**
* Updates our pages to match the latest configuration and
* presentation size.
*/
syncPages() {
const config = this.Reveal.getConfig();
const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
const scale = this.Reveal.getScale();
const useCompactLayout = config.scrollLayout === 'compact';
const viewportHeight = this.viewportElement.offsetHeight;
const compactHeight = slideSize.height * scale;
const pageHeight = useCompactLayout ? compactHeight : viewportHeight;
// The height that needs to be scrolled between scroll triggers
const scrollTriggerHeight = useCompactLayout ? compactHeight : viewportHeight;
this.viewportElement.style.setProperty( '--page-height', pageHeight + 'px' );
this.viewportElement.style.scrollSnapType = typeof config.scrollSnap === 'string' ? `y ${config.scrollSnap}` : '';
// This will hold all scroll triggers used to show/hide slides
this.slideTriggers = [];
const pageElements = Array.from( this.Reveal.getRevealElement().querySelectorAll( '.scroll-page' ) );
this.pages = pageElements.map( pageElement => {
const page = this.createPage({
pageElement,
slideElement: pageElement.querySelector( 'section' ),
stickyElement: pageElement.querySelector( '.scroll-page-sticky' ),
contentElement: pageElement.querySelector( '.scroll-page-content' ),
backgroundElement: pageElement.querySelector( '.slide-background' ),
autoAnimateElements: pageElement.querySelectorAll( '.scroll-auto-animate-page' ),
autoAnimatePages: []
});
page.pageElement.style.setProperty( '--slide-height', config.center === true ? 'auto' : slideSize.height + 'px' );
this.slideTriggers.push({
page: page,
activate: () => this.activatePage( page ),
deactivate: () => this.deactivatePage( page )
});
// Create scroll triggers that show/hide fragments
this.createFragmentTriggersForPage( page );
// Create scroll triggers for triggering auto-animate steps
if( page.autoAnimateElements.length > 0 ) {
this.createAutoAnimateTriggersForPage( page );
}
let totalScrollTriggerCount = Math.max( page.scrollTriggers.length - 1, 0 );
// Each auto-animate step may include its own scroll triggers
// for fragments, ensure we count those as well
totalScrollTriggerCount += page.autoAnimatePages.reduce( ( total, page ) => {
return total + Math.max( page.scrollTriggers.length - 1, 0 );
}, page.autoAnimatePages.length );
// Clean up from previous renders
page.pageElement.querySelectorAll( '.scroll-snap-point' ).forEach( el => el.remove() );
// Create snap points for all scroll triggers
// - Can't be absolute in FF
// - Can't be 0-height in Safari
// - Can't use snap-align on parent in Safari because then
// inner triggers won't work
for( let i = 0; i < totalScrollTriggerCount + 1; i++ ) {
const triggerStick = document.createElement( 'div' );
triggerStick.className = 'scroll-snap-point';
triggerStick.style.height = scrollTriggerHeight + 'px';
triggerStick.style.scrollSnapAlign = useCompactLayout ? 'center' : 'start';
page.pageElement.appendChild( triggerStick );
if( i === 0 ) {
triggerStick.style.marginTop = -scrollTriggerHeight + 'px';
}
}
// In the compact layout, only slides with scroll triggers cover the
// full viewport height. This helps avoid empty gaps before or after
// a sticky slide.
if( useCompactLayout && page.scrollTriggers.length > 0 ) {
page.pageHeight = viewportHeight;
page.pageElement.style.setProperty( '--page-height', viewportHeight + 'px' );
}
else {
page.pageHeight = pageHeight;
page.pageElement.style.removeProperty( '--page-height' );
}
// Add scroll padding based on how many scroll triggers we have
page.scrollPadding = scrollTriggerHeight * totalScrollTriggerCount;
// The total height including scrollable space
page.totalHeight = page.pageHeight + page.scrollPadding;
// This is used to pad the height of our page in CSS
page.pageElement.style.setProperty( '--page-scroll-padding', page.scrollPadding + 'px' );
// If this is a sticky page, stick it to the vertical center
if( totalScrollTriggerCount > 0 ) {
page.stickyElement.style.position = 'sticky';
page.stickyElement.style.top = Math.max( ( viewportHeight - page.pageHeight ) / 2, 0 ) + 'px';
}
else {
page.stickyElement.style.position = 'relative';
page.pageElement.style.scrollSnapAlign = page.pageHeight < viewportHeight ? 'center' : 'start';
}
return page;
} );
this.setTriggerRanges();
/*
console.log(this.slideTriggers.map( t => {
return {
range: `${t.range[0].toFixed(2)}-${t.range[1].toFixed(2)}`,
triggers: t.page.scrollTriggers.map( t => {
return `${t.range[0].toFixed(2)}-${t.range[1].toFixed(2)}`
}).join( ', ' ),
}
}))
*/
this.viewportElement.setAttribute( 'data-scrollbar', config.scrollProgress );
if( config.scrollProgress && this.totalScrollTriggerCount > 1 ) {
// Create the progress bar if it doesn't already exist
if( !this.progressBar ) this.createProgressBar();
this.syncProgressBar();
}
else {
this.removeProgressBar();
}
}
/**
* Calculates and sets the scroll range for all of our scroll
* triggers.
*/
setTriggerRanges() {
// Calculate the total number of scroll triggers
this.totalScrollTriggerCount = this.slideTriggers.reduce( ( total, trigger ) => {
return total + Math.max( trigger.page.scrollTriggers.length, 1 );
}, 0 );
let rangeStart = 0;
// Calculate the scroll range of each scroll trigger on a scale
// of 0-1
this.slideTriggers.forEach( ( trigger, i ) => {
trigger.range = [
rangeStart,
rangeStart + Math.max( trigger.page.scrollTriggers.length, 1 ) / this.totalScrollTriggerCount
];
const scrollTriggerSegmentSize = ( trigger.range[1] - trigger.range[0] ) / trigger.page.scrollTriggers.length;
// Set the range for each inner scroll trigger
trigger.page.scrollTriggers.forEach( ( scrollTrigger, i ) => {
scrollTrigger.range = [
rangeStart + i * scrollTriggerSegmentSize,
rangeStart + ( i + 1 ) * scrollTriggerSegmentSize
];
} );
rangeStart = trigger.range[1];
} );
}
/**
* Creates one scroll trigger for each fragments in the given page.
*
* @param {*} page
*/
createFragmentTriggersForPage( page, slideElement ) {
slideElement = slideElement || page.slideElement;
// Each fragment 'group' is an array containing one or more
// fragments. Multiple fragments that appear at the same time
// are part of the same group.
const fragmentGroups = this.Reveal.fragments.sort( slideElement.querySelectorAll( '.fragment' ), true );
// Create scroll triggers that show/hide fragments
if( fragmentGroups.length ) {
page.fragments = this.Reveal.fragments.sort( slideElement.querySelectorAll( '.fragment:not(.disabled)' ) );
page.scrollTriggers.push(
// Trigger for the initial state with no fragments visible
{
activate: () => {
this.Reveal.fragments.update( -1, page.fragments, slideElement );
}
},
// Triggers for each fragment group
...fragmentGroups.map( ( fragments, i ) => ({
activate: () => {
this.Reveal.fragments.update( i, page.fragments, slideElement );
}
})
)
);
}
return page.scrollTriggers.length;
}
/**
* Creates scroll triggers for the auto-animate steps in the
* given page.
*
* @param {*} page
*/
createAutoAnimateTriggersForPage( page ) {
if( page.autoAnimateElements.length > 0 ) {
// Triggers for each subsequent auto-animate slide
this.slideTriggers.push( ...Array.from( page.autoAnimateElements ).map( ( autoAnimateElement, i ) => {
let autoAnimatePage = this.createPage({
slideElement: autoAnimateElement.querySelector( 'section' ),
contentElement: autoAnimateElement,
backgroundElement: autoAnimateElement.querySelector( '.slide-background' )
});
// Create fragment scroll triggers for the auto-animate slide
this.createFragmentTriggersForPage( autoAnimatePage, autoAnimatePage.slideElement );
page.autoAnimatePages.push( autoAnimatePage );
// Return our slide trigger
return {
page: autoAnimatePage,
activate: () => this.activatePage( autoAnimatePage ),
deactivate: () => this.deactivatePage( autoAnimatePage )
};
}));
}
}
/**
* Helper method for creating a page definition and adding
* required fields. A "page" is a slide or auto-animate step.
*/
createPage( page ) {
page.scrollTriggers = [];
page.indexh = parseInt( page.slideElement.getAttribute( 'data-index-h' ), 10 );
page.indexv = parseInt( page.slideElement.getAttribute( 'data-index-v' ), 10 );
return page;
}
/**
* Rerenders progress bar segments so that they match the current
* reveal.js config and size.
*/
syncProgressBar() {
this.progressBarInner.querySelectorAll( '.scrollbar-slide' ).forEach( slide => slide.remove() );
const scrollHeight = this.viewportElement.scrollHeight;
const viewportHeight = this.viewportElement.offsetHeight;
const viewportHeightFactor = viewportHeight / scrollHeight;
this.progressBarHeight = this.progressBarInner.offsetHeight;
this.playheadHeight = Math.max( viewportHeightFactor * this.progressBarHeight, MIN_PLAYHEAD_HEIGHT );
this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight;
const progressSegmentHeight = viewportHeight / scrollHeight * this.progressBarHeight;
const spacing = Math.min( progressSegmentHeight / 8, MAX_PROGRESS_SPACING );
this.progressBarPlayhead.style.height = this.playheadHeight - spacing + 'px';
// Don't show individual segments if they're too small
if( progressSegmentHeight > MIN_PROGRESS_SEGMENT_HEIGHT ) {
this.slideTriggers.forEach( slideTrigger => {
const { page } = slideTrigger;
// Visual representation of a slide
page.progressBarSlide = document.createElement( 'div' );
page.progressBarSlide.className = 'scrollbar-slide';
page.progressBarSlide.style.top = slideTrigger.range[0] * this.progressBarHeight + 'px';
page.progressBarSlide.style.height = ( slideTrigger.range[1] - slideTrigger.range[0] ) * this.progressBarHeight - spacing + 'px';
page.progressBarSlide.classList.toggle( 'has-triggers', page.scrollTriggers.length > 0 );
this.progressBarInner.appendChild( page.progressBarSlide );
// Visual representations of each scroll trigger
page.scrollTriggerElements = page.scrollTriggers.map( ( trigger, i ) => {
const triggerElement = document.createElement( 'div' );
triggerElement.className = 'scrollbar-trigger';
triggerElement.style.top = ( trigger.range[0] - slideTrigger.range[0] ) * this.progressBarHeight + 'px';
triggerElement.style.height = ( trigger.range[1] - trigger.range[0] ) * this.progressBarHeight - spacing + 'px';
page.progressBarSlide.appendChild( triggerElement );
if( i === 0 ) triggerElement.style.display = 'none';
return triggerElement;
} );
} );
}
else {
this.pages.forEach( page => page.progressBarSlide = null );
}
}
/**
* Reads the current scroll position and updates our active
* trigger states accordingly.
*/
syncScrollPosition() {
const viewportHeight = this.viewportElement.offsetHeight;
const viewportHeightFactor = viewportHeight / this.viewportElement.scrollHeight;
const scrollTop = this.viewportElement.scrollTop;
const scrollHeight = this.viewportElement.scrollHeight - viewportHeight
const scrollProgress = Math.max( Math.min( scrollTop / scrollHeight, 1 ), 0 );
const scrollProgressMid = Math.max( Math.min( ( scrollTop + viewportHeight / 2 ) / this.viewportElement.scrollHeight, 1 ), 0 );
let activePage;
this.slideTriggers.forEach( ( trigger ) => {
const { page } = trigger;
const shouldPreload = scrollProgress >= trigger.range[0] - viewportHeightFactor*2 &&
scrollProgress <= trigger.range[1] + viewportHeightFactor*2;
// Load slides that are within the preload range
if( shouldPreload && !page.loaded ) {
page.loaded = true;
this.Reveal.slideContent.load( page.slideElement );
}
else if( page.loaded ) {
page.loaded = false;
this.Reveal.slideContent.unload( page.slideElement );
}
// If we're within this trigger range, activate it
if( scrollProgress >= trigger.range[0] && scrollProgress <= trigger.range[1] ) {
this.activateTrigger( trigger );
activePage = trigger.page;
}
// .. otherwise deactivate
else if( trigger.active ) {
this.deactivateTrigger( trigger );
}
} );
// Each page can have its own scroll triggers, check if any of those
// need to be activated/deactivated
if( activePage ) {
activePage.scrollTriggers.forEach( ( trigger ) => {
if( scrollProgressMid >= trigger.range[0] && scrollProgressMid <= trigger.range[1] ) {
this.activateTrigger( trigger );
}
else if( trigger.active ) {
this.deactivateTrigger( trigger );
}
} );
}
// Update our visual progress indication
this.setProgressBarValue( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) );
}
/**
* Moves the progress bar playhead to the specified position.
*
* @param {number} progress 0-1
*/
setProgressBarValue( progress ) {
if( this.progressBar ) {
this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`;
this.getAllPages()
.filter( page => page.progressBarSlide )
.forEach( ( page ) => {
page.progressBarSlide.classList.toggle( 'active', page.active === true );
page.scrollTriggers.forEach( ( trigger, i ) => {
page.scrollTriggerElements[i].classList.toggle( 'active', page.active === true && trigger.active === true );
} );
} );
this.showProgressBar();
}
}
/**
* Show the progress bar and, if configured, automatically hide
* it after a delay.
*/
showProgressBar() {
this.progressBar.classList.add( 'visible' );
clearTimeout( this.hideProgressBarTimeout );
if( this.Reveal.getConfig().scrollProgress === 'auto' && !this.draggingProgressBar ) {
this.hideProgressBarTimeout = setTimeout( () => {
if( this.progressBar ) {
this.progressBar.classList.remove( 'visible' );
}
}, HIDE_SCROLLBAR_TIMEOUT );
}
}
/**
* Scrolls the given slide element into view.
*
* @param {HTMLElement} slideElement
*/
scrollToSlide( slideElement ) {
// If the scroll view isn't active yet, queue this action
if( !this.active ) {
this.activatedCallbacks.push( () => this.scrollToSlide( slideElement ) );
}
else {
// Find the trigger for this slide
const trigger = this.getScrollTriggerBySlide( slideElement );
if( trigger ) {
// Use the trigger's range to calculate the scroll position
this.viewportElement.scrollTop = trigger.range[0] * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
}
}
}
/**
* Persists the current scroll position to session storage
* so that it can be restored.
*/
storeScrollPosition() {
clearTimeout( this.storeScrollPositionTimeout );
this.storeScrollPositionTimeout = setTimeout( () => {
sessionStorage.setItem( 'reveal-scroll-top', this.viewportElement.scrollTop );
sessionStorage.setItem( 'reveal-scroll-origin', location.origin + location.pathname );
this.storeScrollPositionTimeout = null;
}, 50 );
}
/**
* Restores the scroll position when a deck is reloader.
*/
restoreScrollPosition() {
const scrollPosition = sessionStorage.getItem( 'reveal-scroll-top' );
const scrollOrigin = sessionStorage.getItem( 'reveal-scroll-origin' );
if( scrollPosition && scrollOrigin === location.origin + location.pathname ) {
this.viewportElement.scrollTop = parseInt( scrollPosition, 10 );
}
}
/**
* Activates the given page and starts its embedded content
* if there is any.
*
* @param {object} page
*/
activatePage( page ) {
if( !page.active ) {
page.active = true;
const { slideElement, backgroundElement, contentElement, indexh, indexv } = page;
contentElement.style.display = 'block';
slideElement.classList.add( 'present' );
if( backgroundElement ) {
backgroundElement.classList.add( 'present' );
}
this.Reveal.setCurrentScrollPage( slideElement, indexh, indexv );
this.Reveal.backgrounds.bubbleSlideContrastClassToElement( slideElement, this.viewportElement );
// If this page is part of an auto-animation there will be one
// content element per auto-animated page. We need to show the
// current page and hide all others.
Array.from( contentElement.parentNode.querySelectorAll( '.scroll-page-content' ) ).forEach( sibling => {
if( sibling !== contentElement ) {
sibling.style.display = 'none';
}
});
}
}
/**
* Deactivates the page after it has been visible.
*
* @param {object} page
*/
deactivatePage( page ) {
if( page.active ) {
page.active = false;
page.slideElement.classList.remove( 'present' );
page.backgroundElement.classList.remove( 'present' );
}
}
activateTrigger( trigger ) {
if( !trigger.active ) {
trigger.active = true;
trigger.activate();
}
}
deactivateTrigger( trigger ) {
if( trigger.active ) {
trigger.active = false;
if( trigger.deactivate ) {
trigger.deactivate();
}
}
}
/**
* Retrieve a slide by its original h/v index (i.e. the indices the
* slide had before being linearized).
*
* @param {number} h
* @param {number} v
* @returns {HTMLElement}
*/
getSlideByIndices( h, v ) {
const page = this.getAllPages().find( page => {
return page.indexh === h && page.indexv === v;
} );
return page ? page.slideElement : null;
}
/**
* Retrieve a list of all scroll triggers for the given slide
* DOM element.
*
* @param {HTMLElement} slide
* @returns {Array}
*/
getScrollTriggerBySlide( slide ) {
return this.slideTriggers.find( trigger => trigger.page.slideElement === slide );
}
/**
* Get a list of all pages in the scroll view. This includes
* both top-level slides and auto-animate steps.
*
* @returns {Array}
*/
getAllPages() {
return this.pages.flatMap( page => [page, ...(page.autoAnimatePages || [])] );
}
onScroll() {
this.syncScrollPosition();
this.storeScrollPosition();
}
get viewportElement() {
return this.Reveal.getViewportElement();
}
}

View File

@@ -25,6 +25,10 @@ export default class SlideContent {
*/
shouldPreload( element ) {
if( this.Reveal.isScrollView() ) {
return true;
}
// Prefer an explicit global preload setting
let preload = this.Reveal.getConfig().preloadIframes;

View File

@@ -23,7 +23,7 @@ export default class SlideNumber {
configure( config, oldConfig ) {
let slideNumberDisplay = 'none';
if( config.slideNumber && !this.Reveal.isPrintingPDF() ) {
if( config.slideNumber && !this.Reveal.isPrintView() ) {
if( config.showSlideNumber === 'all' ) {
slideNumberDisplay = 'block';
}

View File

@@ -3,6 +3,8 @@ import SlideNumber from './controllers/slidenumber.js'
import JumpToSlide from './controllers/jumptoslide.js'
import Backgrounds from './controllers/backgrounds.js'
import AutoAnimate from './controllers/autoanimate.js'
import ScrollView from './controllers/scrollview.js'
import PrintView from './controllers/printview.js'
import Fragments from './controllers/fragments.js'
import Overview from './controllers/overview.js'
import Keyboard from './controllers/keyboard.js'
@@ -11,7 +13,6 @@ 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 Print from './controllers/print.js'
import Touch from './controllers/touch.js'
import Focus from './controllers/focus.js'
import Notes from './controllers/notes.js'
@@ -27,7 +28,7 @@ import {
} from './utils/constants.js'
// The reveal.js version
export const VERSION = '4.6.0';
export const VERSION = '5.0.1';
/**
* reveal.js
@@ -105,6 +106,8 @@ export default function( revealElement, options ) {
jumpToSlide = new JumpToSlide( Reveal ),
autoAnimate = new AutoAnimate( Reveal ),
backgrounds = new Backgrounds( Reveal ),
scrollView = new ScrollView( Reveal ),
printView = new PrintView( Reveal ),
fragments = new Fragments( Reveal ),
overview = new Overview( Reveal ),
keyboard = new Keyboard( Reveal ),
@@ -113,7 +116,6 @@ export default function( revealElement, options ) {
progress = new Progress( Reveal ),
pointer = new Pointer( Reveal ),
plugins = new Plugins( Reveal ),
print = new Print( Reveal ),
focus = new Focus( Reveal ),
touch = new Touch( Reveal ),
notes = new Notes( Reveal );
@@ -140,6 +142,11 @@ export default function( revealElement, options ) {
// 5. Query params
config = { ...defaultConfig, ...config, ...options, ...initOptions, ...Util.getQueryHash() };
// Legacy support for the ?print-pdf query
if( /print-pdf/gi.test( window.location.search ) ) {
config.view = 'print';
}
setViewport();
// Force a layout when the whole page, incl fonts, has loaded
@@ -201,12 +208,15 @@ export default function( revealElement, options ) {
// Updates the presentation to match the current configuration values
configure();
// Read the initial hash
location.readURL();
// Create slide backgrounds
backgrounds.update( true );
// Activate the print/scroll view if configured
activateInitialView();
// Read the initial hash
location.readURL();
// Notify listeners that the presentation is ready but use a 1ms
// timeout to ensure it's not fired synchronously after #initialize()
setTimeout( () => {
@@ -225,19 +235,41 @@ export default function( revealElement, options ) {
});
}, 1 );
// Special setup and config is required when printing to PDF
if( print.isPrintingPDF() ) {
removeEventListeners();
}
// The document needs to have loaded for the PDF layout
// measurements to be accurate
if( document.readyState === 'complete' ) {
print.setupPDF();
/**
* Activates the correct reveal.js view based on our config.
* This is only invoked once during initialization.
*/
function activateInitialView() {
const activatePrintView = config.view === 'print';
const activateScrollView = config.view === 'scroll' || config.view === 'reader';
if( activatePrintView || activateScrollView ) {
if( activatePrintView ) {
removeEventListeners();
}
else {
window.addEventListener( 'load', () => {
print.setupPDF();
} );
touch.unbind();
}
// Avoid content flickering during layout
dom.viewport.classList.add( 'loading-scroll-mode' );
if( activatePrintView ) {
// The document needs to have loaded for the PDF layout
// measurements to be accurate
if( document.readyState === 'complete' ) {
printView.activate();
}
else {
window.addEventListener( 'load', () => printView.activate() );
}
}
else {
scrollView.activate();
}
}
@@ -255,7 +287,18 @@ export default function( revealElement, options ) {
if( !config.showHiddenSlides ) {
Util.queryAll( dom.wrapper, 'section[data-visibility="hidden"]' ).forEach( slide => {
slide.parentNode.removeChild( slide );
const parent = slide.parentNode;
// If this slide is part of a stack and that stack will be
// empty after removing the hidden slide, remove the entire
// stack
if( parent.childElementCount === 1 && /section/i.test( parent.nodeName ) ) {
parent.remove();
}
else {
slide.remove();
}
} );
}
@@ -374,7 +417,7 @@ export default function( revealElement, options ) {
function setupScrollPrevention() {
setInterval( () => {
if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) {
if( !scrollView.isActive() && dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) {
dom.wrapper.scrollTop = 0;
dom.wrapper.scrollLeft = 0;
}
@@ -441,8 +484,8 @@ export default function( revealElement, options ) {
dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
// Expose our configured slide dimensions as custom props
dom.viewport.style.setProperty( '--slide-width', config.width + 'px' );
dom.viewport.style.setProperty( '--slide-height', config.height + 'px' );
dom.viewport.style.setProperty( '--slide-width', typeof config.width === 'string' ? config.width : config.width + 'px' );
dom.viewport.style.setProperty( '--slide-height', typeof config.height === 'string' ? config.height : config.height + 'px' );
if( config.shuffle ) {
shuffle();
@@ -678,6 +721,26 @@ export default function( revealElement, options ) {
}
/**
* Dispatches a slidechanged event.
*
* @param {string} origin Used to identify multiplex clients
*/
function dispatchSlideChanged( origin ) {
dispatchEvent({
type: 'slidechanged',
data: {
indexh,
indexv,
previousSlide,
currentSlide,
origin
}
});
}
/**
* Dispatched a postMessage of the given type from our window.
*/
@@ -861,7 +924,10 @@ export default function( revealElement, options ) {
*/
function layout() {
if( dom.wrapper && !print.isPrintingPDF() ) {
if( dom.wrapper && !printView.isActive() ) {
const viewportWidth = dom.viewport.offsetWidth;
const viewportHeight = dom.viewport.offsetHeight;
if( !config.disableLayout ) {
@@ -875,7 +941,9 @@ export default function( revealElement, options ) {
document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' );
}
const size = getComputedSlideSize();
const size = scrollView.isActive() ?
getComputedSlideSize( viewportWidth, viewportHeight ) :
getComputedSlideSize();
const oldScale = scale;
@@ -892,8 +960,9 @@ export default function( revealElement, options ) {
scale = Math.max( scale, config.minScale );
scale = Math.min( scale, config.maxScale );
// Don't apply any scaling styles if scale is 1
if( scale === 1 ) {
// Don't apply any scaling styles if scale is 1 or we're
// in the scroll view
if( scale === 1 || scrollView.isActive() ) {
dom.slides.style.zoom = '';
dom.slides.style.left = '';
dom.slides.style.top = '';
@@ -921,7 +990,7 @@ export default function( revealElement, options ) {
continue;
}
if( config.center || slide.classList.contains( 'center' ) ) {
if( ( config.center || slide.classList.contains( 'center' ) ) ) {
// Vertical stacks are not centred since their section
// children will be
if( slide.classList.contains( 'stack' ) ) {
@@ -947,9 +1016,25 @@ export default function( revealElement, options ) {
}
});
}
// Responsively turn on the scroll mode if there is an activation
// width configured. Ignore if we're configured to always be in
// scroll mode.
if( typeof config.scrollActivationWidth === 'number' && config.view !== 'scroll' ) {
if( size.presentationWidth > 0 && size.presentationWidth <= config.scrollActivationWidth ) {
if( !scrollView.isActive() ) scrollView.activate();
}
else {
if( scrollView.isActive() ) scrollView.deactivate();
}
}
}
dom.viewport.style.setProperty( '--slide-scale', scale );
dom.viewport.style.setProperty( '--viewport-width', viewportWidth + 'px' );
dom.viewport.style.setProperty( '--viewport-height', viewportHeight + 'px' );
scrollView.layout();
progress.update();
backgrounds.updateParallax();
@@ -970,7 +1055,6 @@ export default function( revealElement, options ) {
* @param {string|number} height
*/
function layoutSlideContents( width, height ) {
// Handle sizing of elements with the 'r-stretch' class
Util.queryAll( dom.slides, 'section > .stretch, section > .r-stretch' ).forEach( element => {
@@ -1006,6 +1090,7 @@ export default function( revealElement, options ) {
* @param {number} [presentationHeight=dom.wrapper.offsetHeight]
*/
function getComputedSlideSize( presentationWidth, presentationHeight ) {
let width = config.width;
let height = config.height;
@@ -1092,6 +1177,19 @@ export default function( revealElement, options ) {
}
/**
* Checks if the current or specified slide is a stack containing
* vertical slides.
*
* @param {HTMLElement} [slide=currentSlide]
* @return {Boolean}
*/
function isVerticalStack( slide = currentSlide ) {
return slide.classList.contains( '.stack' ) || slide.querySelector( 'section' ) !== null;
}
/**
* Returns true if we're on the last slide in the current
* vertical stack.
@@ -1277,6 +1375,14 @@ export default function( revealElement, options ) {
// Query all horizontal slides in the deck
const horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
// If we're in scroll mode, we scroll the target slide into view
// instead of running our standard slide transition
if( scrollView.isActive() ) {
const scrollToSlide = scrollView.getSlideByIndices( h, v );
if( scrollToSlide ) scrollView.scrollToSlide( scrollToSlide );
return;
}
// Abort if there are no slides
if( horizontalSlides.length === 0 ) return;
@@ -1323,6 +1429,9 @@ export default function( revealElement, options ) {
// Detect if we're moving between two auto-animated slides
if( slideChanged && previousSlide && currentSlide && !overview.isActive() ) {
transition = 'running';
autoAnimateTransition = shouldAutoAnimateBetween( previousSlide, currentSlide, indexhBefore, indexvBefore );
// If this is an auto-animated transition, we disable the
// regular slide transition
@@ -1330,16 +1439,9 @@ export default function( revealElement, options ) {
// Note 20-03-2020:
// This needs to happen before we update slide visibility,
// otherwise transitions will still run in Safari.
if( previousSlide.hasAttribute( 'data-auto-animate' ) && currentSlide.hasAttribute( 'data-auto-animate' )
&& previousSlide.getAttribute( 'data-auto-animate-id' ) === currentSlide.getAttribute( 'data-auto-animate-id' )
&& !( ( indexh > indexhBefore || indexv > indexvBefore ) ? currentSlide : previousSlide ).hasAttribute( 'data-auto-animate-restart' ) ) {
autoAnimateTransition = true;
dom.slides.classList.add( 'disable-slide-transitions' );
if( autoAnimateTransition ) {
dom.slides.classList.add( 'disable-slide-transitions' )
}
transition = 'running';
}
// Update the visibility of slides now that the indices have changed
@@ -1398,16 +1500,7 @@ export default function( revealElement, options ) {
}
if( slideChanged ) {
dispatchEvent({
type: 'slidechanged',
data: {
indexh,
indexv,
previousSlide,
currentSlide,
origin
}
});
dispatchSlideChanged( origin );
}
// Handle embedded content
@@ -1452,6 +1545,71 @@ export default function( revealElement, options ) {
}
/**
* Checks whether or not an auto-animation should take place between
* the two given slides.
*
* @param {HTMLElement} fromSlide
* @param {HTMLElement} toSlide
* @param {number} indexhBefore
* @param {number} indexvBefore
*
* @returns {boolean}
*/
function shouldAutoAnimateBetween( fromSlide, toSlide, indexhBefore, indexvBefore ) {
return fromSlide.hasAttribute( 'data-auto-animate' ) && toSlide.hasAttribute( 'data-auto-animate' ) &&
fromSlide.getAttribute( 'data-auto-animate-id' ) === toSlide.getAttribute( 'data-auto-animate-id' ) &&
!( ( indexh > indexhBefore || indexv > indexvBefore ) ? toSlide : fromSlide ).hasAttribute( 'data-auto-animate-restart' );
}
/**
* Called anytime a new slide should be activated while in the scroll
* view. The active slide is the page that occupies the most space in
* the scrollable viewport.
*
* @param {number} pageIndex
* @param {HTMLElement} slideElement
*/
function setCurrentScrollPage( slideElement, h, v ) {
let indexhBefore = indexh || 0;
indexh = h;
indexv = v;
const slideChanged = currentSlide !== slideElement;
previousSlide = currentSlide;
currentSlide = slideElement;
if( currentSlide && previousSlide ) {
if( config.autoAnimate && shouldAutoAnimateBetween( previousSlide, currentSlide, indexhBefore, indexv ) ) {
// Run the auto-animation between our slides
autoAnimate.run( previousSlide, currentSlide );
}
}
// Start or stop embedded content like videos and iframes
if( slideChanged ) {
if( previousSlide ) {
slideContent.stopEmbeddedContent( previousSlide );
slideContent.stopEmbeddedContent( previousSlide.slideBackgroundElement );
}
slideContent.startEmbeddedContent( currentSlide );
slideContent.startEmbeddedContent( currentSlide.slideBackgroundElement );
}
requestAnimationFrame( () => {
announceStatus( getStatusText( currentSlide ) );
});
dispatchSlideChanged();
}
/**
* Syncs the presentation with the current DOM. Useful
* when new slides or control elements are added or when
@@ -1597,7 +1755,7 @@ export default function( revealElement, options ) {
let slides = Util.queryAll( dom.wrapper, selector ),
slidesLength = slides.length;
let printMode = print.isPrintingPDF();
let printMode = scrollView.isActive() || printView.isActive();
let loopedForwards = false;
let loopedBackwards = false;
@@ -1709,7 +1867,7 @@ export default function( revealElement, options ) {
}
/**
* Shows all fragment elements within the given contaienr.
* Shows all fragment elements within the given container.
*/
function showFragmentsIn( container ) {
@@ -1721,7 +1879,7 @@ export default function( revealElement, options ) {
}
/**
* Hides all fragment elements within the given contaienr.
* Hides all fragment elements within the given container.
*/
function hideFragmentsIn( container ) {
@@ -1757,7 +1915,7 @@ export default function( revealElement, options ) {
}
// All slides need to be visible when exporting to PDF
if( print.isPrintingPDF() ) {
if( printView.isActive() ) {
viewDistance = Number.MAX_VALUE;
}
@@ -1988,21 +2146,31 @@ export default function( revealElement, options ) {
// If a slide is specified, return the indices of that slide
if( slide ) {
let isVertical = isVerticalSlide( slide );
let slideh = isVertical ? slide.parentNode : slide;
// In scroll mode the original h/x index is stored on the slide
if( scrollView.isActive() ) {
h = parseInt( slide.getAttribute( 'data-index-h' ), 10 );
// Select all horizontal slides
let horizontalSlides = getHorizontalSlides();
if( slide.getAttribute( 'data-index-v' ) ) {
v = parseInt( slide.getAttribute( 'data-index-v' ), 10 );
}
}
else {
let isVertical = isVerticalSlide( slide );
let slideh = isVertical ? slide.parentNode : slide;
// Now that we know which the horizontal slide is, get its index
h = Math.max( horizontalSlides.indexOf( slideh ), 0 );
// Select all horizontal slides
let horizontalSlides = getHorizontalSlides();
// Assume we're not vertical
v = undefined;
// Now that we know which the horizontal slide is, get its index
h = Math.max( horizontalSlides.indexOf( slideh ), 0 );
// If this is a vertical slide, grab the vertical index
if( isVertical ) {
v = Math.max( Util.queryAll( slide.parentNode, 'section' ).indexOf( slide ), 0 );
// Assume we're not vertical
v = undefined;
// If this is a vertical slide, grab the vertical index
if( isVertical ) {
v = Math.max( Util.queryAll( slide.parentNode, 'section' ).indexOf( slide ), 0 );
}
}
}
@@ -2675,6 +2843,9 @@ export default function( revealElement, options ) {
// Toggles the overview mode on/off
toggleOverview: overview.toggle.bind( overview ),
// Toggles the scroll view on/off
toggleScrollView: scrollView.toggle.bind( scrollView ),
// Toggles the "black screen" mode on/off
togglePause,
@@ -2689,6 +2860,7 @@ export default function( revealElement, options ) {
isLastSlide,
isLastVerticalSlide,
isVerticalSlide,
isVerticalStack,
// State checks
isPaused,
@@ -2696,7 +2868,9 @@ export default function( revealElement, options ) {
isSpeakerNotes: notes.isSpeakerNotesWindow.bind( notes ),
isOverview: overview.isActive.bind( overview ),
isFocused: focus.isFocused.bind( focus ),
isPrintingPDF: print.isPrintingPDF.bind( print ),
isScrollView: scrollView.isActive.bind( scrollView ),
isPrintView: printView.isActive.bind( printView ),
// Checks if reveal.js has been loaded and is ready for use
isReady: () => ready,
@@ -2769,6 +2943,8 @@ export default function( revealElement, options ) {
hasNavigatedHorizontally: () => navigationHistory.hasNavigatedHorizontally,
hasNavigatedVertically: () => navigationHistory.hasNavigatedVertically,
shouldAutoAnimateBetween,
// Adds/removes a custom key binding
addKeyBinding: keyboard.addKeyBinding.bind( keyboard ),
removeKeyBinding: keyboard.removeKeyBinding.bind( keyboard ),
@@ -2780,6 +2956,7 @@ export default function( revealElement, options ) {
registerKeyboardShortcut: keyboard.registerKeyboardShortcut.bind( keyboard ),
getComputedSlideSize,
setCurrentScrollPage,
// Returns the current scale of the presentation content
getScale: () => scale,
@@ -2816,13 +2993,14 @@ export default function( revealElement, options ) {
getStatusText,
// Controllers
print,
focus,
scroll: scrollView,
progress,
controls,
location,
overview,
fragments,
backgrounds,
slideContent,
slideNumber,

14353
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "reveal.js",
"version": "4.6.0",
"version": "5.0.2",
"description": "The HTML Presentation Framework",
"homepage": "https://revealjs.com",
"subdomain": "revealjs",
@@ -22,7 +22,7 @@
"url": "git://github.com/hakimel/reveal.js.git"
},
"engines": {
"node": ">=10.0.0"
"node": ">=18.0.0"
},
"keywords": [
"reveal",
@@ -30,33 +30,33 @@
"presentation"
],
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/eslint-parser": "^7.14.3",
"@babel/preset-env": "^7.14.2",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"babel-plugin-transform-html-import-to-string": "0.0.1",
"@babel/core": "^7.23.2",
"@babel/eslint-parser": "^7.22.15",
"@babel/preset-env": "^7.23.2",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"babel-plugin-transform-html-import-to-string": "2.0.0",
"colors": "^1.4.0",
"core-js": "^3.12.1",
"fitty": "^2.3.0",
"glob": "^7.1.7",
"core-js": "^3.33.1",
"fitty": "^2.3.7",
"glob": "^10.3.10",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^8.0.0",
"gulp-clean-css": "^4.2.0",
"gulp-clean-css": "^4.3.0",
"gulp-connect": "^5.7.0",
"gulp-eslint": "^6.0.0",
"gulp-header": "^2.0.9",
"gulp-tap": "^2.0.0",
"gulp-zip": "^4.2.0",
"highlight.js": "^11.7.0",
"marked": "^4.0.12",
"gulp-zip": "^5.1.0",
"highlight.js": "^11.9.0",
"marked": "^4.3.0",
"node-qunit-puppeteer": "^2.1.2",
"qunit": "^2.19.3",
"rollup": "^2.48.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.39.2",
"yargs": "^15.1.0"
"qunit": "^2.20.0",
"rollup": "^4.1.5",
"sass": "^1.69.5",
"yargs": "^17.7.2"
},
"overrides": {
"chokidar": "3.5.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -95,7 +95,7 @@ const Plugin = () => {
* values for what's not defined.
*/
function getSlidifyOptions( options ) {
const markdownConfig = deck.getConfig().markdown;
const markdownConfig = deck?.getConfig?.().markdown;
options = options || {};
options.separator = options.separator || markdownConfig?.separator || DEFAULT_SLIDE_SEPARATOR;

View File

@@ -3,4 +3,4 @@ const t=()=>{let t,e={messageStyle:"none",tex2jax:{inlineMath:[["$","$"],["\\(",
* 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 default a;
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};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -146,8 +146,12 @@ const Plugin = () => {
}
// Look for notes defined in an aside element
if( notesElements ) {
messageData.notes = Array.from(notesElements).map( notesElement => notesElement.innerHTML ).join( '\n' );
if( notesElements && notesElements.length ) {
// Ignore notes inside of fragments since those are shown
// individually when stepping through fragments
notesElements = Array.from( notesElements ).filter( notesElement => notesElement.closest( '.fragment' ) === null );
messageData.notes = notesElements.map( notesElement => notesElement.innerHTML ).join( '\n' );
messageData.markdown = notesElements[0] && typeof notesElements[0].getAttribute( 'data-markdown' ) === 'string';
}

View File

@@ -533,8 +533,8 @@
var urlSeparator = /\?/.test(data.url) ? '&' : '?';
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;
var currentURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&controls=false' + hash;
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );

View File

@@ -4,4 +4,4 @@
*
* @author Jon Snyder <snyder.jon@gmail.com>, February 2013
*/
export default()=>{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.replace(/^[^\w]+|[^\w]+$/g,"").replace(/[^\w'-]+/g,"|"),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}};
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 c("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,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.replace(/^[^\w]+|[^\w]+$/g,"").replace(/[^\w'-]+/g,"|"),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 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(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: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};

View File

@@ -1,11 +1,11 @@
/*!
* reveal.js Zoom plugin
*/
const e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(o){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;o[i]&&!e.isOverview()&&(o.preventDefault(),t.to({x:o.clientX,y:o.clientY,scale:d,pan:!1}))}))},destroy:()=>{t.reset()}};var t=function(){var e=1,o=0,n=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,o){var n=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,l)if(1===o)document.body.style.transform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();n<i?window.scroll(d.x,d.y-14/e*(1-n/i)):n>window.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),o<t?window.scroll(d.x-14/e*(1-o/t),d.y):o>window.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-o)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(o){1!==e&&27===o.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(o=t.clientX,n=t.clientY)})),{to:function(o){if(1!==e)t.out();else{if(o.x=o.x||0,o.y=o.y||0,o.element){var n=o.element.getBoundingClientRect();o.x=n.left-20,o.y=n.top-20,o.width=n.width+40,o.height=n.height+40}void 0!==o.width&&void 0!==o.height&&(o.scale=Math.max(Math.min(window.innerWidth/o.width,window.innerHeight/o.height),1)),o.scale>1&&(o.x*=o.scale,o.y*=o.scale,s(o,o.scale),!1!==o.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();
const e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(t){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;t[i]&&!e.isOverview()&&(t.preventDefault(),o.to({x:t.clientX,y:t.clientY,scale:d,pan:!1}))}))},destroy:()=>{o.reset()}};var t=()=>e,o=function(){var e=1,t=0,n=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,o){var n=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,l)if(1===o)document.body.style.transform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var o=.12*window.innerWidth,i=.12*window.innerHeight,d=r();n<i?window.scroll(d.x,d.y-14/e*(1-n/i)):n>window.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),t<o?window.scroll(d.x-14/e*(1-t/o),d.y):t>window.innerWidth-o&&window.scroll(d.x+(1-(window.innerWidth-t)/o)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(t){1!==e&&27===t.keyCode&&o.out()})),document.addEventListener("mousemove",(function(o){1!==e&&(t=o.clientX,n=o.clientY)})),{to:function(t){if(1!==e)o.out();else{if(t.x=t.x||0,t.y=t.y||0,t.element){var n=t.element.getBoundingClientRect();t.x=n.left-20,t.y=n.top-20,t.width=n.width+40,t.height=n.height+40}void 0!==t.width&&void 0!==t.height&&(t.scale=Math.max(Math.min(window.innerWidth/t.width,window.innerHeight/t.height),1)),t.scale>1&&(t.x*=t.scale,t.y*=t.scale,s(t,t.scale),!1!==t.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();
/*!
* zoom.js 0.3 (modified for use with reveal.js)
* http://lab.hakim.se/zoom-js
* MIT licensed
*
* Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
*/export default()=>e;
*/export{t as default};

115
test/test-scroll.html Normal file
View File

@@ -0,0 +1,115 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Test Scroll View</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" style="opacity: 0; pointer-events: none;">
<div class="slides">
<section>
<h1>slide 1</h1>
</section>
<section>
<h1>slide 2</h1>
</section>
<section>
<h1>slide 3</h1>
<p class="fragment">fragment 1</p>
<p class="fragment">fragment 2</p>
<p class="fragment">fragment 3</p>
</section>
<section>
<h1>slide 4</h1>
</section>
</div>
</div>
<script src="../dist/reveal.js"></script>
<script>
QUnit.config.testTimeout = 30000;
QUnit.config.reorder = false;
function getScrollHeight() {
return Reveal.getViewportElement().scrollHeight;
}
function getViewportHeight() {
return Reveal.getViewportElement().offsetHeight;
}
Reveal.initialize({ view: 'scroll' }).then( async () => {
QUnit.module( 'Scroll View' );
QUnit.test( 'Activates', assert => {
assert.ok( getScrollHeight() > getViewportHeight(), 'Is overflowing' );
});
QUnit.test( 'Can be toggled via API', assert => {
Reveal.toggleScrollView( false );
assert.ok( getScrollHeight() <= getViewportHeight(), 'Is not overflowing' );
Reveal.toggleScrollView( true );
assert.ok( getScrollHeight() > getViewportHeight(), 'Is overflowing' );
});
QUnit.test( 'Changes present slide when scrolling', assert => {
assert.timeout( 200 );
assert.expect( 2 );
const slides = document.querySelectorAll( '.reveal .slides section' );
assert.ok( slides[0].classList.contains( 'present' ), 'First slide is present' );
Reveal.getViewportElement().scrollTop = getViewportHeight() * 1;
return new Promise( resolve => {
setTimeout(() => {
assert.ok( slides[1].classList.contains( 'present' ), 'Second slide is present' );
resolve();
}, 100);
} );
});
QUnit.test( 'Fires slideschanged event when scrolling', assert => {
assert.timeout( 200 );
assert.expect( 2 );
const slides = document.querySelectorAll( '.reveal .slides section' );
return new Promise( resolve => {
let callback = ( event ) => {
Reveal.off( 'slidechanged', callback );
assert.ok( true, 'slidechanged event fired' );
assert.ok( event.currentSlide.classList.contains( 'present' ), 'slidechanged provides reference to currentSlide' );
resolve();
}
Reveal.on( 'slidechanged', callback );
Reveal.getViewportElement().scrollTop = getViewportHeight() * 2;
});
});
} );
</script>
</body>
</html>