From d2ededfefb2d39283b6cf581082b394127b96132 Mon Sep 17 00:00:00 2001 From: jcraitz Date: Fri, 22 Jan 2021 12:38:55 -0500 Subject: [PATCH] bumping to reveal.js v3.9.2 --- _layouts/slide.html | 4 +- reveal.js/.gitignore | 13 + reveal.js/.travis.yml | 5 + reveal.js/CONTRIBUTING.md | 4 +- reveal.js/Gruntfile.js | 123 +- reveal.js/LICENSE | 2 +- reveal.js/README.md | 849 ++- reveal.js/bower.json | 7 +- reveal.js/css/print/paper.css | 5 +- reveal.js/css/print/pdf.css | 70 +- reveal.js/css/reset.css | 30 + reveal.js/css/reveal.css | 832 ++- reveal.js/css/reveal.scss | 762 ++- reveal.js/css/theme/README.md | 4 +- reveal.js/css/theme/beige.css | 83 +- reveal.js/css/theme/black.css | 87 +- reveal.js/css/theme/blood.css | 83 +- reveal.js/css/theme/league.css | 83 +- reveal.js/css/theme/moon.css | 81 +- reveal.js/css/theme/night.css | 83 +- reveal.js/css/theme/serif.css | 83 +- reveal.js/css/theme/simple.css | 95 +- reveal.js/css/theme/sky.css | 83 +- reveal.js/css/theme/solarized.css | 81 +- reveal.js/css/theme/source/beige.scss | 2 +- reveal.js/css/theme/source/black.scss | 6 +- reveal.js/css/theme/source/blood.scss | 3 +- reveal.js/css/theme/source/league.scss | 2 +- reveal.js/css/theme/source/night.scss | 3 +- reveal.js/css/theme/source/serif.scss | 2 +- reveal.js/css/theme/source/simple.scss | 7 +- reveal.js/css/theme/source/sky.scss | 2 +- reveal.js/css/theme/source/white.scss | 4 +- reveal.js/css/theme/template/settings.scss | 6 +- reveal.js/css/theme/template/theme.scss | 78 +- reveal.js/css/theme/white.css | 83 +- reveal.js/demo.html | 425 ++ reveal.js/index.html | 393 +- reveal.js/js/reveal.js | 3108 ++++++--- reveal.js/lib/css/monokai.css | 71 + reveal.js/lib/css/zenburn.css | 95 +- reveal.js/lib/font/league-gothic/LICENSE | 2 +- reveal.js/lib/font/source-sans-pro/LICENSE | 4 +- reveal.js/lib/js/classList.js | 2 - reveal.js/lib/js/head.min.js | 8 - reveal.js/lib/js/promise.js | 2 + reveal.js/package-lock.json | 5703 +++++++++++++++++ reveal.js/package.json | 43 +- reveal.js/plugin/highlight/highlight.js | 321 +- reveal.js/plugin/markdown/example.html | 11 +- reveal.js/plugin/markdown/example.md | 5 + reveal.js/plugin/markdown/markdown.js | 230 +- reveal.js/plugin/markdown/marked.js | 6 +- reveal.js/plugin/math/math.js | 71 +- reveal.js/plugin/multiplex/index.js | 10 +- reveal.js/plugin/multiplex/master.js | 5 +- reveal.js/plugin/multiplex/package.json | 19 + reveal.js/plugin/notes-server/index.js | 7 +- reveal.js/plugin/notes-server/notes.html | 218 +- reveal.js/plugin/notes/notes.html | 523 +- reveal.js/plugin/notes/notes.js | 99 +- reveal.js/plugin/print-pdf/print-pdf.js | 75 +- reveal.js/plugin/search/search.js | 228 +- reveal.js/plugin/zoom-js/zoom.js | 47 +- reveal.js/test/assets/external-script-a.js | 1 + reveal.js/test/assets/external-script-b.js | 1 + reveal.js/test/assets/external-script-c.js | 1 + reveal.js/test/assets/external-script-d.js | 1 + reveal.js/test/examples/assets/beeping.txt | 2 + reveal.js/test/examples/assets/beeping.wav | Bin 0 -> 422472 bytes reveal.js/test/examples/embedded-media.html | 8 +- reveal.js/test/examples/math.html | 28 +- .../test/examples/slide-backgrounds.html | 3 +- .../test/examples/slide-transitions.html | 1 - reveal.js/test/qunit-1.12.0.css | 244 - reveal.js/test/qunit-1.12.0.js | 2212 ------- reveal.js/test/qunit-2.5.0.css | 436 ++ reveal.js/test/qunit-2.5.0.js | 5188 +++++++++++++++ reveal.js/test/simple.md | 12 + reveal.js/test/test-dependencies-async.html | 78 + reveal.js/test/test-dependencies.html | 54 + reveal.js/test/test-grid-navigation.html | 74 + reveal.js/test/test-iframe-backgrounds.html | 104 + reveal.js/test/test-iframes.html | 108 + .../test-markdown-element-attributes.html | 8 +- .../test/test-markdown-element-attributes.js | 48 +- reveal.js/test/test-markdown-external.html | 37 + reveal.js/test/test-markdown-external.js | 20 + reveal.js/test/test-markdown-options.html | 40 + reveal.js/test/test-markdown-options.js | 27 + .../test/test-markdown-slide-attributes.html | 5 +- .../test/test-markdown-slide-attributes.js | 49 +- reveal.js/test/test-markdown.html | 7 +- reveal.js/test/test-markdown.js | 8 +- reveal.js/test/test-pdf.html | 7 +- reveal.js/test/test-pdf.js | 7 +- reveal.js/test/test-plugins.html | 105 + reveal.js/test/test-state.html | 139 + reveal.js/test/test.html | 13 +- reveal.js/test/test.js | 393 +- 100 files changed, 19179 insertions(+), 5771 deletions(-) create mode 100644 reveal.js/.gitignore create mode 100644 reveal.js/.travis.yml create mode 100644 reveal.js/css/reset.css create mode 100644 reveal.js/demo.html create mode 100644 reveal.js/lib/css/monokai.css delete mode 100644 reveal.js/lib/js/classList.js delete mode 100644 reveal.js/lib/js/head.min.js create mode 100644 reveal.js/lib/js/promise.js create mode 100644 reveal.js/package-lock.json create mode 100644 reveal.js/plugin/multiplex/package.json create mode 100644 reveal.js/test/assets/external-script-a.js create mode 100644 reveal.js/test/assets/external-script-b.js create mode 100644 reveal.js/test/assets/external-script-c.js create mode 100644 reveal.js/test/assets/external-script-d.js create mode 100644 reveal.js/test/examples/assets/beeping.txt create mode 100644 reveal.js/test/examples/assets/beeping.wav delete mode 100644 reveal.js/test/qunit-1.12.0.css delete mode 100644 reveal.js/test/qunit-1.12.0.js create mode 100644 reveal.js/test/qunit-2.5.0.css create mode 100644 reveal.js/test/qunit-2.5.0.js create mode 100644 reveal.js/test/simple.md create mode 100644 reveal.js/test/test-dependencies-async.html create mode 100644 reveal.js/test/test-dependencies.html create mode 100644 reveal.js/test/test-grid-navigation.html create mode 100644 reveal.js/test/test-iframe-backgrounds.html create mode 100644 reveal.js/test/test-iframes.html create mode 100644 reveal.js/test/test-markdown-external.html create mode 100644 reveal.js/test/test-markdown-external.js create mode 100644 reveal.js/test/test-markdown-options.html create mode 100644 reveal.js/test/test-markdown-options.js create mode 100644 reveal.js/test/test-plugins.html create mode 100644 reveal.js/test/test-state.html diff --git a/_layouts/slide.html b/_layouts/slide.html index 33e485b..564e3c2 100755 --- a/_layouts/slide.html +++ b/_layouts/slide.html @@ -33,7 +33,7 @@ {% endif %} - + + A paragraph with some text and a [link](http://hakim.se). + ``` #### External Markdown -You can write your content as a separate file and have reveal.js load it at runtime. Note the separator arguments which determine how slides are delimited in the external file. The ```data-charset``` attribute is optional and specifies which charset to use when loading the external file. +You can write your content as a separate file and have reveal.js load it at runtime. Note the separator arguments which determine how slides are delimited in the external file: the `data-separator` attribute defines a regular expression for horizontal slides (defaults to `^\r?\n---\r?\n$`, a newline-bounded horizontal rule) and `data-separator-vertical` defines vertical slides (disabled by default). The `data-separator-notes` attribute is a regular expression for specifying the beginning of the current slide's speaker notes (defaults to `notes?:`, so it will match both "note:" and "notes:"). The `data-charset` attribute is optional and specifies which charset to use when loading the external file. -When used locally, this feature requires that reveal.js [runs from a local web server](#full-setup). +When used locally, this feature requires that reveal.js [runs from a local web server](#full-setup). The following example customises all available options: ```html -
+
``` #### Element Attributes -Special syntax (in html comment) is available for adding attributes to Markdown elements. This is useful for fragments, amongst other things. +Special syntax (through HTML comments) is available for adding attributes to Markdown elements. This is useful for fragments, amongst other things. ```html
@@ -103,7 +214,7 @@ Special syntax (in html comment) is available for adding attributes to Markdown #### Slide Attributes -Special syntax (in html comment) is available for adding attributes to the slide `
` elements generated by your Markdown. +Special syntax (through HTML comments) is available for adding attributes to the slide `
` elements generated by your Markdown. ```html
@@ -114,24 +225,52 @@ Special syntax (in html comment) is available for adding attributes to the slide
``` +#### Configuring *marked* + +We use [marked](https://github.com/chjj/marked) to parse Markdown. To customise marked's rendering, you can pass in options when [configuring Reveal](#configuration): + +```javascript +Reveal.initialize({ + // Options which are passed into marked + // See https://marked.js.org/#/USING_ADVANCED.md#options + markdown: { + smartypants: true + } +}); +``` ### Configuration -At the end of your page you need to initialize reveal by running the following code. Note that all config values are optional and will default as specified below. +At the end of your page you need to initialize reveal by running the following code. Note that all configuration values are optional and will default to the values specified below. ```javascript Reveal.initialize({ - // Display controls in the bottom right corner + // Display presentation control arrows controls: true, + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + // Display a presentation progress bar progress: true, // Display the page number of the current slide slideNumber: false, - // Push each slide change to the browser history + // Add the current slide number to the URL hash so that reloading the + // page/copying the URL will return you to the same slide + hash: false, + + // Push each slide change to the browser history. Implies `hash: true` history: false, // Enable keyboard shortcuts for navigation @@ -152,9 +291,19 @@ Reveal.initialize({ // Change the presentation direction to be RTL rtl: false, + // See https://github.com/hakimel/reveal.js/#navigation-mode + navigationMode: 'default', + + // Randomizes the order of slides each time the presentation loads + shuffle: false, + // Turns fragments on and off globally fragments: true, + // Flags whether to include the current fragment in the URL, + // so that reloading brings you to the same fragment position + fragmentInURL: false, + // Flags if the presentation is running in an embedded mode, // i.e. contained within a limited portion of the screen embedded: false, @@ -166,6 +315,19 @@ Reveal.initialize({ // Flags if speaker notes should be visible to all viewers showNotes: false, + // Global override for autoplaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Global override for preloading lazy-loaded iframes + // - null: Iframes with data-src AND data-preload will be loaded when within + // the viewDistance, iframes with only data-src will be loaded when visible + // - true: All iframes with data-src will be loaded when within the viewDistance + // - false: All iframes with data-src will be loaded only when visible + preloadIframes: null, + // Number of milliseconds between automatically proceeding to the // next slide, disabled when set to 0, this value can be overwritten // by using a data-autoslide attribute on your slides @@ -174,27 +336,61 @@ Reveal.initialize({ // Stop auto-sliding after user input autoSlideStoppable: true, + // Use this method for navigation when auto-sliding + autoSlideMethod: Reveal.navigateNext, + + // Specify the average time in seconds that you think you will spend + // presenting each slide. This is used to show a pacing timer in the + // speaker view + defaultTiming: 120, + + // Specify the total time in seconds that is available to + // present. If this is set to a nonzero value, the pacing + // timer will work out the time available for each slide, + // instead of using the defaultTiming value + totalTime: 0, + + // Specify the minimum amount of time you want to allot to + // each slide, if using the totalTime calculation method. If + // the automated time allocation causes slide pacing to fall + // below this threshold, then you will see an alert in the + // speaker notes window + minimumTimePerSlide: 0; + // Enable slide navigation via mouse wheel mouseWheel: false, + // Hide cursor if inactive + hideInactiveCursor: true, + + // Time before the cursor is hidden (in ms) + hideCursorTime: 5000, + // Hides the address bar on mobile devices hideAddressBar: true, // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually previewLinks: false, // Transition style - transition: 'default', // none/fade/slide/convex/concave/zoom + transition: 'slide', // none/fade/slide/convex/concave/zoom // Transition speed transitionSpeed: 'default', // default/fast/slow // Transition style for full page slide backgrounds - backgroundTransition: 'default', // none/fade/slide/convex/concave/zoom + backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom // Number of slides away from the current that are visible viewDistance: 3, + // Number of slides away from the current that are visible on mobile + // devices. It is advisable to set this to a lower number than + // viewDistance in order to save resources. + mobileViewDistance: 2, + // Parallax background image parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'" @@ -205,13 +401,15 @@ Reveal.initialize({ // - Calculated automatically unless specified // - Set to 0 to disable movement along an axis parallaxBackgroundHorizontal: null, - parallaxBackgroundVertical: null + parallaxBackgroundVertical: null, + + // The display mode that will be used to show slides + display: 'block' }); ``` - -The configuration can be updated after initialization using the ```configure``` method: +The configuration can be updated after initialization using the `configure` method: ```javascript // Turn autoSlide off @@ -221,17 +419,16 @@ Reveal.configure({ autoSlide: 0 }); Reveal.configure({ autoSlide: 5000 }); ``` - ### Presentation Size -All presentations have a normal size, that is the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport. +All presentations have a normal size, that is, the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport. See below for a list of configuration options related to sizing, including default values: ```javascript Reveal.initialize({ - ... + // ... // The "normal" size of the presentation, aspect ratio will be preserved // when the presentation is scaled to fit different resolutions. Can be @@ -249,6 +446,20 @@ Reveal.initialize({ }); ``` +If you wish to disable this behavior and do your own scaling (e.g. using media queries), try these settings: + +```javascript +Reveal.initialize({ + + // ... + + width: "100%", + height: "100%", + margin: 0, + minScale: 1, + maxScale: 1 +}); +``` ### Dependencies @@ -257,15 +468,12 @@ Reveal.js doesn't _rely_ on any third party scripts to work but a few optional l ```javascript Reveal.initialize({ dependencies: [ - // Cross-browser shim that fully implements classList - https://github.com/eligrey/classList.js/ - { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, - // Interpret Markdown in
elements { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, // Syntax highlight for elements - { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, + { src: 'plugin/highlight/highlight.js', async: true }, // Zoom in and out with Alt+click { src: 'plugin/zoom-js/zoom.js', async: true }, @@ -285,10 +493,9 @@ You can add your own extensions using the same syntax. The following properties - **callback**: [optional] Function to execute when the script has loaded - **condition**: [optional] Function which must return true for the script to be loaded - ### Ready Event -A 'ready' event is fired when reveal.js has loaded all non-async dependencies and is ready to start navigating. To check if reveal.js is already 'ready' you can call `Reveal.isReady()`. +A `ready` event is fired when reveal.js has loaded all non-async dependencies and is ready to start navigating. To check if reveal.js is already 'ready' you can call `Reveal.isReady()`. ```javascript Reveal.addEventListener( 'ready', function( event ) { @@ -296,6 +503,7 @@ Reveal.addEventListener( 'ready', function( event ) { } ); ``` +Note that we also add a `.ready` class to the `.reveal` element so that you can hook into this with CSS. ### Auto-sliding @@ -307,9 +515,10 @@ Reveal.configure({ autoSlide: 5000 }); ``` -When this is turned on a control element will appear that enables users to pause and resume auto-sliding. Alternatively, sliding can be paused or resumed by pressing »a« on the keyboard. Sliding is paused automatically as soon as the user starts navigating. You can disable these controls by specifying ```autoSlideStoppable: false``` in your reveal.js config. -You can also override the slide duration for individual slides and fragments by using the ```data-autoslide``` attribute: +When this is turned on a control element will appear that enables users to pause and resume auto-sliding. Alternatively, sliding can be paused or resumed by pressing »A« on the keyboard. Sliding is paused automatically as soon as the user starts navigating. You can disable these controls by specifying `autoSlideStoppable: false` in your reveal.js config. + +You can also override the slide duration for individual slides and fragments by using the `data-autoslide` attribute: ```html
@@ -319,12 +528,13 @@ You can also override the slide duration for individual slides and fragments by
``` -Whenever the auto-slide mode is resumed or paused the ```autoslideresumed``` and ```autoslidepaused``` events are fired. +To override the method used for navigation when auto-sliding, you can specify the `autoSlideMethod` setting. To only navigate along the top layer and ignore vertical slides, set this to `Reveal.navigateRight`. +Whenever the auto-slide mode is resumed or paused the `autoslideresumed` and `autoslidepaused` events are fired. ### Keyboard Bindings -If you're unhappy with any of the default keyboard bindings you can override them using the ```keyboard``` config option: +If you're unhappy with any of the default keyboard bindings you can override them using the `keyboard` config option: ```javascript Reveal.configure({ @@ -336,23 +546,37 @@ Reveal.configure({ }); ``` +### Vertical Slide Navigation + +Slides can be nested within other slides to create vertical stacks (see [Markup](#markup)). When presenting, you use the left/right arrows to step through the main (horizontal) slides. When you arrive at a vertical stack you can optionally press the up/down arrows to view the vertical slides or skip past them by pressing the right arrow. Here's an example showing a bird's-eye view of what this looks like in action: + + + +#### Navigation Mode +You can fine tune the reveal.js navigation behavior by using the `navigationMode` config option. Note that these options are only useful for presentations that use a mix of horizontal and vertical slides. The following navigation modes are available: + +| Value | Description | +| :--------------------------- | :---------- | +| default | Left/right arrow keys step between horizontal slides. Up/down arrow keys step between vertical slides. Space key steps through all slides (both horizontal and vertical). | +| linear | Removes the up/down arrows. Left/right arrows step through all slides (both horizontal and vertical). | +| grid | When this is enabled, stepping left/right from a vertical stack to an adjacent vertical stack will land you at the same vertical index.

Consider a deck with six slides ordered in two vertical stacks:
`1.1`    `2.1`
`1.2`    `2.2`
`1.3`    `2.3`

If you're on slide 1.3 and navigate right, you will normally move from 1.3 -> 2.1. With navigationMode set to "grid" the same navigation takes you from 1.3 -> 2.3. | + ### Touch Navigation You can swipe to navigate through a presentation on any touch-enabled device. Horizontal swipes change between horizontal slides, vertical swipes change between vertical slides. If you wish to disable this you can set the `touch` config option to false when initializing reveal.js. If there's some part of your content that needs to remain accessible to touch events you'll need to highlight this by adding a `data-prevent-swipe` attribute to the element. One common example where this is useful is elements that need to be scrolled. - ### Lazy Loading When working on presentation with a lot of media or iframe content it's important to load lazily. Lazy loading means that reveal.js will only load content for the few slides nearest to the current slide. The number of slides that are preloaded is determined by the `viewDistance` configuration option. -To enable lazy loading all you need to do is change your "src" attributes to "data-src" as shown below. This is supported for image, video, audio and iframe elements. Lazy loaded iframes will also unload when the containing slide is no longer visible. +To enable lazy loading all you need to do is change your `src` attributes to `data-src` as shown below. This is supported for image, video, audio and iframe elements. ```html
- +
``` +#### Lazy Loading Iframes + +Note that lazy loaded iframes ignore the `viewDistance` configuration and will only load when their containing slide becomes visible. Iframes are also unloaded as soon as the slide is hidden. + +When we lazy load a video or audio element, reveal.js won't start playing that content until the slide becomes visible. However there is no way to control this for an iframe since that could contain any kind of content. That means if we loaded an iframe before the slide is visible on screen it could begin playing media and sound in the background. + +You can override this behavior with the `data-preload` attribute. The iframe below will be loaded +according to the `viewDistance`. + +```html +
+ +
+``` + +You can also change the default globally with the `preloadIframes` configuration option. If set to +`true` ALL iframes with a `data-src` attribute will be preloaded when within the `viewDistance` +regardless of individual `data-preload` attributes. If set to `false`, all iframes will only be +loaded when they become visible. ### API -The ``Reveal`` object exposes a JavaScript API for controlling navigation and reading state: +The `Reveal` object exposes a JavaScript API for controlling navigation and reading state: ```javascript // Navigation @@ -377,11 +620,18 @@ Reveal.next(); Reveal.prevFragment(); Reveal.nextFragment(); +// Randomize the order of slides +Reveal.shuffle(); + // Toggle presentation states, optionally pass true/false to force on/off Reveal.toggleOverview(); Reveal.togglePause(); Reveal.toggleAutoSlide(); +// Shows a help overlay with keyboard shortcuts, optionally pass true/false +// to force on/off +Reveal.toggleHelp(); + // Change a config value at runtime Reveal.configure({ controls: true }); @@ -395,9 +645,20 @@ Reveal.getScale(); Reveal.getPreviousSlide(); Reveal.getCurrentSlide(); -Reveal.getIndices(); // { h: 0, v: 0 } } -Reveal.getProgress(); // 0-1 -Reveal.getTotalSlides(); +Reveal.getIndices(); // { h: 0, v: 0, f: 0 } +Reveal.getSlidePastCount(); +Reveal.getProgress(); // (0 == first slide, 1 == last slide) +Reveal.getSlides(); // Array of all slides +Reveal.getTotalSlides(); // Total number of slides + +// Returns an array with all horizontal/vertical slides in the deck +Reveal.getHorizontalSlides(); +Reveal.getVerticalSlides(); + +// Checks if the presentation contains two or more +// horizontal/vertical slides +Reveal.hasHorizontalSlides(); +Reveal.hasVerticalSlides(); // Returns the speaker notes for the current slide Reveal.getSlideNotes(); @@ -408,11 +669,45 @@ Reveal.isLastSlide(); Reveal.isOverview(); Reveal.isPaused(); Reveal.isAutoSliding(); + +// Returns the top-level DOM element +Reveal.getRevealElement(); //
...
``` +### Custom Key Bindings + +Custom key bindings can be added and removed using the following Javascript API. Custom key bindings will override the default keyboard bindings, but will in turn be overridden by the user defined bindings in the ``keyboard`` config option. + +```javascript +Reveal.addKeyBinding( binding, callback ); +Reveal.removeKeyBinding( keyCode ); +``` + +For example + +```javascript +// The binding parameter provides the following properties +// keyCode: the keycode for binding to the callback +// key: the key label to show in the help overlay +// description: the description of the action to show in the help overlay +Reveal.addKeyBinding( { keyCode: 84, key: 'T', description: 'Start timer' }, function() { + // start timer +} ) + +// The binding parameter can also be a direct keycode without providing the help description +Reveal.addKeyBinding( 82, function() { + // reset timer +} ) +``` + +This allows plugins to add key bindings directly to Reveal so they can + +* make use of Reveal's pre-processing logic for key handling (for example, ignoring key presses when paused); and +* be included in the help overlay (optional) + ### Slide Changed Event -A 'slidechanged' event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes. +A `slidechanged` event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes. Some libraries, like MathJax (see [#226](https://github.com/hakimel/reveal.js/issues/226#issuecomment-10261609)), get confused by the transforms and display states of slides. Often times, this can be fixed by calling their update or render function from this callback. @@ -441,7 +736,7 @@ Reveal.setState( state ); ### Slide States -If you set ``data-state="somestate"`` on a slide ``
``, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide. +If you set `data-state="somestate"` on a slide `
`, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide. Furthermore you can also listen to these changes in state via JavaScript: @@ -453,32 +748,77 @@ Reveal.addEventListener( 'somestate', function() { ### Slide Backgrounds -Slides are contained within a limited portion of the screen by default to allow them to fit any display and scale uniformly. You can apply full page backgrounds outside of the slide area by adding a ```data-background``` attribute to your ```
``` elements. Four different types of backgrounds are supported: color, image, video and iframe. Below are a few examples. +Slides are contained within a limited portion of the screen by default to allow them to fit any display and scale uniformly. You can apply full page backgrounds outside of the slide area by adding a `data-background` attribute to your `
` elements. Four different types of backgrounds are supported: color, image, video and iframe. + +#### Color Backgrounds + +All CSS color formats are supported, including hex values, keywords, `rgba()` or `hsl()`. ```html -
-

All CSS color formats are supported, like rgba() or hsl().

-
-
-

This slide will have a full-size background image.

-
-
-

This background image will be sized to 100px and repeated.

-
-
-

Video. Multiple sources can be defined using a comma separated list. Video will loop when the data-background-video-loop attribute is provided.

-
-
-

Embeds a web page as a background. Note that the page won't be interactive.

+
+

Color

``` -Backgrounds transition using a fade animation by default. This can be changed to a linear sliding transition by passing ```backgroundTransition: 'slide'``` to the ```Reveal.initialize()``` call. Alternatively you can set ```data-background-transition``` on any section with a background to override that specific transition. +#### Image Backgrounds + +By default, background images are resized to cover the full page. Available options: + +| Attribute | Default | Description | +| :------------------------------- | :--------- | :---------- | +| data-background-image | | URL of the image to show. GIFs restart when the slide opens. | +| data-background-size | cover | See [background-size](https://developer.mozilla.org/docs/Web/CSS/background-size) on MDN. | +| data-background-position | center | See [background-position](https://developer.mozilla.org/docs/Web/CSS/background-position) on MDN. | +| data-background-repeat | no-repeat | See [background-repeat](https://developer.mozilla.org/docs/Web/CSS/background-repeat) on MDN. | +| data-background-opacity | 1 | Opacity of the background image on a 0-1 scale. 0 is transparent and 1 is fully opaque. | + +```html +
+

Image

+
+
+

This background image will be sized to 100px and repeated

+
+``` + +#### Video Backgrounds + +Automatically plays a full size video behind the slide. + +| Attribute | Default | Description | +| :--------------------------- | :------ | :---------- | +| data-background-video | | A single video source, or a comma separated list of video sources. | +| data-background-video-loop | false | Flags if the video should play repeatedly. | +| data-background-video-muted | false | Flags if the audio should be muted. | +| data-background-size | cover | Use `cover` for full screen and some cropping or `contain` for letterboxing. | +| data-background-opacity | 1 | Opacity of the background video on a 0-1 scale. 0 is transparent and 1 is fully opaque. | + +```html +
+

Video

+
+``` + +#### Iframe Backgrounds + +Embeds a web page as a slide background that covers 100% of the reveal.js width and height. The iframe is in the background layer, behind your slides, and as such it's not possible to interact with it by default. To make your background interactive, you can add the `data-background-interactive` attribute. + +```html +
+

Iframe

+
+``` + +Iframes are lazy-loaded when they become visible. If you'd like to preload iframes ahead of time, you can append a `data-preload` attribute to the slide `
`. You can also enable preloading globally for all iframes using the `preloadIframes` configuration option. + +#### Background Transitions + +Backgrounds transition using a fade animation by default. This can be changed to a linear sliding transition by passing `backgroundTransition: 'slide'` to the `Reveal.initialize()` call. Alternatively you can set `data-background-transition` on any section with a background to override that specific transition. ### Parallax Background -If you want to use a parallax scrolling background, set the first two config properties below when initializing reveal.js (the other two are optional). +If you want to use a parallax scrolling background, set the first two properties below when initializing reveal.js (the other two are optional). ```javascript Reveal.initialize({ @@ -498,12 +838,11 @@ Reveal.initialize({ }); ``` -Make sure that the background size is much bigger than screen size to allow for some scrolling. [View example](https://lab.hakim.se/reveal-js/?parallaxBackgroundImage=https%3A%2F%2Fs3.amazonaws.com%2Fhakim-static%2Freveal-js%2Freveal-parallax-1.jpg¶llaxBackgroundSize=2100px%20900px). - - +Make sure that the background size is much bigger than screen size to allow for some scrolling. [View example](http://revealjs.com/?parallaxBackgroundImage=https%3A%2F%2Fs3.amazonaws.com%2Fhakim-static%2Freveal-js%2Freveal-parallax-1.jpg¶llaxBackgroundSize=2100px%20900px). ### Slide Transitions -The global presentation transition is set using the ```transition``` config value. You can override the global transition for a specific slide by using the ```data-transition``` attribute: + +The global presentation transition is set using the `transition` config value. You can override the global transition for a specific slide by using the `data-transition` attribute: ```html
@@ -519,33 +858,32 @@ You can also use different in and out transitions for the same slide: ```html
- The train goes on … + The train goes on …
-
- and on … +
+ and on …
-
+
and stops.
-
+
(Passengers entering and leaving)
And it starts again.
``` - - +You can choose from `none`, `fade`, `slide`, `convex`, `concave` and `zoom`. ### Internal links -It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (```
```): +It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (`
`): ```html Link Link ``` -You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an ```enabled``` class when it's a valid navigation route based on the current slide. +You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an `enabled` class when it's a valid navigation route based on the current slide. ```html @@ -556,9 +894,9 @@ You can also add relative navigation links, similar to the built in reveal.js co ``` - ### Fragments -Fragments are used to highlight individual elements on a slide. Every element with the class ```fragment``` will be stepped through before moving on to the next slide. Here's an example: https://lab.hakim.se/reveal-js/#/fragments + +Fragments are used to highlight individual elements on a slide. Every element with the class `fragment` will be stepped through before moving on to the next slide. Here's an example: http://revealjs.com/#/fragments The default fragment style is to start out invisible and fade in. This style can be changed by appending a different class to the fragment: @@ -567,7 +905,9 @@ The default fragment style is to start out invisible and fade in. This style can

grow

shrink

fade-out

-

visible only once

+

fade-up (also down, left and right!)

+

fades in, then out when we move to the next step

+

fades in, then obfuscate when we move to the next step

blue only once

highlight-red

highlight-green

@@ -585,7 +925,7 @@ Multiple fragments can be applied to the same element sequentially by wrapping i
``` -The display order of fragments can be controlled using the ```data-fragment-index``` attribute. +The display order of fragments can be controlled using the `data-fragment-index` attribute. ```html
@@ -610,9 +950,20 @@ Reveal.addEventListener( 'fragmenthidden', function( event ) { } ); ``` -### Code syntax highlighting +### Code Syntax Highlighting -By default, Reveal is configured with [highlight.js](https://highlightjs.org/) for code syntax highlighting. Below is an example with clojure code that will be syntax highlighted. When the `data-trim` attribute is present, surrounding whitespace is automatically removed. HTML will be escaped by default. To avoid this, for example if you are using `` to call out a line of code, add the `data-noescape` attribute to the `` element. +By default, Reveal is configured with [highlight.js](https://highlightjs.org/) for code syntax highlighting. To enable syntax highlighting, you'll have to load the highlight plugin ([plugin/highlight/highlight.js](plugin/highlight/highlight.js)) and a highlight.js CSS theme (Reveal comes packaged with the Monokai themes: [lib/css/monokai.css](lib/css/monokai.css)). + +```javascript +Reveal.initialize({ + // More info https://github.com/hakimel/reveal.js#dependencies + dependencies: [ + { src: 'plugin/highlight/highlight.js', async: true }, + ] +}); +``` + +Below is an example with clojure code that will be syntax highlighted. When the `data-trim` attribute is present, surrounding whitespace is automatically removed. HTML will be escaped by default. To avoid this, for example if you are using `` to call out a line of code, add the `data-noescape` attribute to the `` element. ```html
@@ -626,8 +977,40 @@ By default, Reveal is configured with [highlight.js](https://highlightjs.org/) f
``` +#### Line Numbers & Highlights + +To enable line numbers, add `data-line-numbers` to your `` tags. If you want to highlight specific lines you can provide a comma separated list of line numbers using the same attribute. For example, in the following example lines 4 and 8-11 are highlighted: + +```html +

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

You clicked {count} times

+ +
+ ); +} +
+``` + +line-numbers + +#### Step-by-step Highlights + +You can step through multiple code highlights on the same code block. Delimit each of your highlight steps with the `|` character. For example `data-line-numbers="1|2-3|4,6-10"` will produce three steps. It will start by highlighting line 1, next step is lines 2-3, and finally line 4 and 6 through 10. + + + ### Slide number -If you would like to display the page number of the current slide you can do so using the ```slideNumber``` configuration value. + +If you would like to display the page number of the current slide you can do so using the `slideNumber` and `showSlideNumber` configuration values. ```javascript // Shows the slide number using default formatting @@ -640,12 +1023,22 @@ Reveal.configure({ slideNumber: true }); // "c/t": flattened slide number / total slides Reveal.configure({ slideNumber: 'c/t' }); -``` +// You can provide a function to fully customize the number: +Reveal.configure({ slideNumber: function( slide ) { + // Ignore numbering of vertical slides + return [ Reveal.getIndices( slide ).h ]; +}}); +// Control which views the slide number displays on using the "showSlideNumber" value: +// "all": show on all views (default) +// "speaker": only show slide numbers on speaker notes view +// "print": only show slide numbers when printing to PDF +Reveal.configure({ showSlideNumber: 'speaker' }); +``` ### Overview mode -Press "Esc" or "o" keys to toggle the overview mode on and off. While you're in this mode, you can still navigate between slides, +Press »ESC« or »O« keys to toggle the overview mode on and off. While you're in this mode, you can still navigate between slides, as if you were at 1,000 feet above your presentation. The overview mode comes with a few API hooks: ```javascript @@ -657,28 +1050,33 @@ Reveal.toggleOverview(); ``` ### Fullscreen mode + Just press »F« on your keyboard to show your presentation in fullscreen mode. Press the »ESC« key to exit fullscreen mode. - ### Embedded media -Embedded HTML5 `
``` @@ -796,9 +1253,20 @@ This will only display in the notes window. #### Share and Print Speaker Notes -Notes are only visible to the speaker inside of the speaker view. If you wish to share your notes with others you can initialize reveal.js with the `showNotes` config value set to `true`. Notes will appear along the bottom of the presentations. +Notes are only visible to the speaker inside of the speaker view. If you wish to share your notes with others you can initialize reveal.js with the `showNotes` configuration value set to `true`. Notes will appear along the bottom of the presentations. + +When `showNotes` is enabled notes are also included when you [export to PDF](https://github.com/hakimel/reveal.js#pdf-export). By default, notes are printed in a box on top of the slide. If you'd rather print them on a separate page, after the slide, set `showNotes: "separate-page"`. + +#### Speaker notes clock and timers + +The speaker notes window will also show: + +- Time elapsed since the beginning of the presentation. If you hover the mouse above this section, a timer reset button will appear. +- Current wall-clock time +- (Optionally) a pacing timer which indicates whether the current pace of the presentation is on track for the right timing (shown in green), and if not, whether the presenter should speed up (shown in red) or has the luxury of slowing down (blue). + +The pacing timer can be enabled by configuring the `defaultTiming` parameter in the `Reveal` configuration block, which specifies the number of seconds per slide. 120 can be a reasonable rule of thumb. Alternatively, you can enable the timer by setting `totalTime`, which sets the total length of your presentation (also in seconds). If both values are specified, `totalTime` wins and `defaultTiming` is ignored. Regardless of the baseline timing method, timings can also be given per slide `
` by setting the `data-timing` attribute (again, in seconds). -When `showNotes` is enabled notes are also included when you [export to PDF](https://github.com/hakimel/reveal.js#pdf-export). ## Server Side Speaker Notes @@ -806,7 +1274,7 @@ In some cases it can be desirable to run notes on a separate device from the one ```javascript Reveal.initialize({ - ... + // ... dependencies: [ { src: 'socket.io/socket.io.js', async: true }, @@ -817,14 +1285,36 @@ Reveal.initialize({ Then: -1. Install [Node.js](https://nodejs.org/) -2. Run ```npm install``` -3. Run ```node plugin/notes-server``` +1. Install [Node.js](http://nodejs.org/) (9.0.0 or later) +2. Run `npm install` +3. Run `node plugin/notes-server` + + +## Plugins + +Plugins should register themselves with reveal.js by calling `Reveal.registerPlugin( 'myPluginID', MyPlugin )`. Registered plugin instances can optionally expose an "init" function that reveal.js will call to initialize them. + +When reveal.js is booted up via `Reveal.initialize()`, it will go through all registered plugins and invoke their "init" methods. If the "init" method returns a Promise, reveal.js will wait for that promise to be fulfilled before finishing the startup sequence and firing the [ready](#ready-event) event. Here's an example of a plugin that does some asynchronous work before reveal.js can proceed: + +```javascript +let MyPlugin = { + init: () => new Promise( resolve => setTimeout( resolve, 3000 ) ) +}; +Reveal.registerPlugin( 'myPlugin', MyPlugin ); +Reveal.addEventListener( 'ready', () => console.log( 'Three seconds later...' ) ); +Reveal.initialize(); +``` + +Note that reveal.js will *not* wait for init Promise fulfillment if the plugin is loaded as an [async dependency](#dependencies). If the plugin's init method does _not_ return a Promise, the plugin is considered ready right away and will not hold up the reveal.js startup sequence. + +### Retrieving Plugins + +If you want to check if a specific plugin is registered you can use the `Reveal.hasPlugin` method and pass in a plugin ID, for example: `Reveal.hasPlugin( 'myPlugin' )`. If you want to retrieve a plugin instance you can use `Reveal.getPlugin( 'myPlugin' )`. ## Multiplexing -The multiplex plugin allows your audience to view the slides of the presentation you are controlling on their own phone, tablet or laptop. As the master presentation navigates the slides, all client presentations will update in real time. See a demo at [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/). +The multiplex plugin allows your audience to view the slides of the presentation you are controlling on their own phone, tablet or laptop. As the master presentation navigates the slides, all client presentations will update in real time. See a demo at [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/). The multiplex plugin needs the following 3 things to operate: @@ -832,19 +1322,19 @@ The multiplex plugin needs the following 3 things to operate: 2. Client presentations that follow the master 3. Socket.io server to broadcast events from the master to the clients -More details: - #### Master presentation -Served from a static file server accessible (preferably) only to the presenter. This need only be on your (the presenter's) computer. (It's safer to run the master presentation from your own computer, so if the venue's Internet goes down it doesn't stop the show.) An example would be to execute the following commands in the directory of your master presentation: -1. ```npm install node-static``` -2. ```static``` +Served from a static file server accessible (preferably) only to the presenter. This need only be on your (the presenter's) computer. (It's safer to run the master presentation from your own computer, so if the venue's Internet goes down it doesn't stop the show.) An example would be to execute the following commands in the directory of your master presentation: -If you want to use the speaker notes plugin with your master presentation then make sure you have the speaker notes plugin configured correctly along with the configuration shown below, then execute ```node plugin/notes-server``` in the directory of your master presentation. The configuration below will cause it to connect to the socket.io server as a master, as well as launch your speaker-notes/static-file server. +1. `npm install node-static` +2. `static` -You can then access your master presentation at ```https://localhost:1947``` +If you want to use the speaker notes plugin with your master presentation then make sure you have the speaker notes plugin configured correctly along with the configuration shown below, then execute `node plugin/notes-server` in the directory of your master presentation. The configuration below will cause it to connect to the socket.io server as a master, as well as launch your speaker-notes/static-file server. + +You can then access your master presentation at `http://localhost:1947` Example configuration: + ```javascript Reveal.initialize({ // other options... @@ -853,12 +1343,12 @@ Reveal.initialize({ // Example values. To generate your own, see the socket.io server instructions. secret: '13652805320794272084', // Obtained from the socket.io server. Gives this (the master) control of the presentation id: '1ea875674b17ca76', // Obtained from socket.io server - url: 'revealjs-51546.onmodulus.net:80' // Location of socket.io server + url: 'https://reveal-js-multiplex-ccjbegmaii.now.sh' // Location of socket.io server }, // Don't forget to add the dependencies dependencies: [ - { src: '//cdn.socket.io/socket.io-1.3.5.js', async: true }, + { src: '//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js', async: true }, { src: 'plugin/multiplex/master.js', async: true }, // and if you want speaker notes @@ -870,9 +1360,11 @@ Reveal.initialize({ ``` #### Client presentation -Served from a publicly accessible static file server. Examples include: GitHub Pages, Amazon S3, Dreamhost, Akamai, etc. The more reliable, the better. Your audience can then access the client presentation via ```https://example.com/path/to/presentation/client/index.html```, with the configuration below causing them to connect to the socket.io server as clients. + +Served from a publicly accessible static file server. Examples include: GitHub Pages, Amazon S3, Dreamhost, Akamai, etc. The more reliable, the better. Your audience can then access the client presentation via `https://example.com/path/to/presentation/client/index.html`, with the configuration below causing them to connect to the socket.io server as clients. Example configuration: + ```javascript Reveal.initialize({ // other options... @@ -881,12 +1373,12 @@ Reveal.initialize({ // Example values. To generate your own, see the socket.io server instructions. secret: null, // null so the clients do not have control of the master presentation id: '1ea875674b17ca76', // id, obtained from socket.io server - url: 'revealjs-51546.onmodulus.net:80' // Location of socket.io server + url: 'https://reveal-js-multiplex-ccjbegmaii.now.sh' // Location of socket.io server }, // Don't forget to add the dependencies dependencies: [ - { src: '//cdn.socket.io/socket.io-1.3.5.js', async: true }, + { src: '//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js', async: true }, { src: 'plugin/multiplex/client.js', async: true } // other dependencies... @@ -895,22 +1387,26 @@ Reveal.initialize({ ``` #### Socket.io server -Server that receives the slideChanged events from the master presentation and broadcasts them out to the connected client presentations. This needs to be publicly accessible. You can run your own socket.io server with the commands: -1. ```npm install``` -2. ```node plugin/multiplex``` +Server that receives the `slideChanged` events from the master presentation and broadcasts them out to the connected client presentations. This needs to be publicly accessible. You can run your own socket.io server with the commands: -Or you use the socket.io server at [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/). +1. `npm install` +2. `node plugin/multiplex` -You'll need to generate a unique secret and token pair for your master and client presentations. To do so, visit ```https://example.com/token```, where ```https://example.com``` is the location of your socket.io server. Or if you're going to use the socket.io server at [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/), visit [https://revealjs-51546.onmodulus.net/token](https://revealjs-51546.onmodulus.net/token). +Or you can use the socket.io server at [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/). -You are very welcome to point your presentations at the Socket.io server running at [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/), but availability and stability are not guaranteed. For anything mission critical I recommend you run your own server. It is simple to deploy to nodejitsu, heroku, your own environment, etc. +You'll need to generate a unique secret and token pair for your master and client presentations. To do so, visit `https://example.com/token`, where `https://example.com` is the location of your socket.io server. Or if you're going to use the socket.io server at [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/), visit [https://reveal-js-multiplex-ccjbegmaii.now.sh/token](https://reveal-js-multiplex-ccjbegmaii.now.sh/token). + +You are very welcome to point your presentations at the Socket.io server running at [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/), but availability and stability are not guaranteed. + +For anything mission critical I recommend you run your own server. The easiest way to do this is by installing [now](https://zeit.co/now). With that installed, deploying your own Multiplex server is as easy running the following command from the reveal.js folder: `now plugin/multiplex`. ##### socket.io server as file static server -The socket.io server can play the role of static file server for your client presentation, as in the example at [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/). (Open [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/) in two browsers. Navigate through the slides on one, and the other will update to match.) +The socket.io server can play the role of static file server for your client presentation, as in the example at [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/). (Open [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/) in two browsers. Navigate through the slides on one, and the other will update to match.) Example configuration: + ```javascript Reveal.initialize({ // other options... @@ -924,16 +1420,17 @@ Reveal.initialize({ // Don't forget to add the dependencies dependencies: [ - { src: '//cdn.socket.io/socket.io-1.3.5.js', async: true }, + { src: '//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js', async: true }, { src: 'plugin/multiplex/client.js', async: true } // other dependencies... ] ``` -It can also play the role of static file server for your master presentation and client presentations at the same time (as long as you don't want to use speaker notes). (Open [https://revealjs-51546.onmodulus.net/](https://revealjs-51546.onmodulus.net/) in two browsers. Navigate through the slides on one, and the other will update to match. Navigate through the slides on the second, and the first will update to match.) This is probably not desirable, because you don't want your audience to mess with your slides while you're presenting. ;) +It can also play the role of static file server for your master presentation and client presentations at the same time (as long as you don't want to use speaker notes). (Open [https://reveal-js-multiplex-ccjbegmaii.now.sh/](https://reveal-js-multiplex-ccjbegmaii.now.sh/) in two browsers. Navigate through the slides on one, and the other will update to match. Navigate through the slides on the second, and the first will update to match.) This is probably not desirable, because you don't want your audience to mess with your slides while you're presenting. ;) Example configuration: + ```javascript Reveal.initialize({ // other options... @@ -947,7 +1444,7 @@ Reveal.initialize({ // Don't forget to add the dependencies dependencies: [ - { src: '//cdn.socket.io/socket.io-1.3.5.js', async: true }, + { src: '//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js', async: true }, { src: 'plugin/multiplex/master.js', async: true }, { src: 'plugin/multiplex/client.js', async: true } @@ -956,91 +1453,43 @@ Reveal.initialize({ }); ``` + ## MathJax -If you want to display math equations in your presentation you can easily do so by including this plugin. The plugin is a very thin wrapper around the [MathJax](https://www.mathjax.org/) library. To use it you'll need to include it as a reveal.js dependency, [find our more about dependencies here](#dependencies). +If you want to display math equations in your presentation you can easily do so by including this plugin. The plugin is a very thin wrapper around the [MathJax](http://www.mathjax.org/) library. To use it you'll need to include it as a reveal.js dependency, [find our more about dependencies here](#dependencies). -The plugin defaults to using [LaTeX](https://en.wikipedia.org/wiki/LaTeX) but that can be adjusted through the ```math``` configuration object. Note that MathJax is loaded from a remote server. If you want to use it offline you'll need to download a copy of the library and adjust the ```mathjax``` configuration value. +The plugin defaults to using [LaTeX](https://en.wikipedia.org/wiki/LaTeX) but that can be adjusted through the `math` configuration object. Note that MathJax is loaded from a remote server. If you want to use it offline you'll need to download a copy of the library and adjust the `mathjax` configuration value. -Below is an example of how the plugin can be configured. If you don't intend to change these values you do not need to include the ```math``` config object at all. +Below is an example of how the plugin can be configured. If you don't intend to change these values you do not need to include the `math` config object at all. ```js Reveal.initialize({ - // other options ... math: { - mathjax: 'https://cdn.mathjax.org/mathjax/latest/MathJax.js', - config: 'TeX-AMS_HTML-full' // See https://docs.mathjax.org/en/latest/config-files.html + mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js', + config: 'TeX-AMS_HTML-full', // See http://docs.mathjax.org/en/latest/config-files.html + // pass other options into `MathJax.Hub.Config()` + TeX: { Macros: { RR: "{\\bf R}" } } }, - + dependencies: [ { src: 'plugin/math/math.js', async: true } ] - }); ``` -Read MathJax's documentation if you need [HTTPS delivery](https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn) or serving of [specific versions](https://docs.mathjax.org/en/latest/configuration.html#loading-mathjax-from-the-cdn) for stability. +Read MathJax's documentation if you need [HTTPS delivery](http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn) or serving of [specific versions](http://docs.mathjax.org/en/latest/configuration.html#loading-mathjax-from-the-cdn) for stability. +#### MathJax in Markdown +If you want to include math inside of a presentation written in Markdown you need to wrap the formula in backticks. This prevents syntax conflicts between LaTeX and Markdown. For example: -## Installation - -The **basic setup** is for authoring presentations only. The **full setup** gives you access to all reveal.js features and plugins such as speaker notes as well as the development tasks needed to make changes to the source. - -### Basic setup - -The core of reveal.js is very easy to install. You'll simply need to download a copy of this repository and open the index.html file directly in your browser. - -1. Download the latest version of reveal.js from - -2. Unzip and replace the example contents in index.html with your own - -3. Open index.html in a browser to view it - - -### Full setup - -Some reveal.js features, like external Markdown and speaker notes, require that presentations run from a local web server. The following instructions will set up such a server as well as all of the development tasks needed to make edits to the reveal.js source code. - -1. Install [Node.js](https://nodejs.org/) - -2. Install [Grunt](https://gruntjs.com/getting-started#installing-the-cli) - -4. Clone the reveal.js repository - ```sh - $ git clone https://github.com/hakimel/reveal.js.git - ``` - -5. Navigate to the reveal.js folder - ```sh - $ cd reveal.js - ``` - -6. Install dependencies - ```sh - $ npm install - ``` - -7. Serve the presentation and monitor source files for changes - ```sh - $ grunt serve - ``` - -8. Open to view your presentation - - You can change the port by using `grunt serve --port 8001`. - - -### Folder Structure -- **css/** Core styles without which the project does not function -- **js/** Like above but for JavaScript -- **plugin/** Components that have been developed as extensions to reveal.js -- **lib/** All other third party assets (JavaScript, CSS, fonts) - +``` +`$$ J(\theta_0,\theta_1) = \sum_{i=0} $$` +``` ## License MIT licensed -Copyright (C) 2016 Hakim El Hattab, https://hakim.se +Copyright (C) 2020 Hakim El Hattab, http://hakim.se diff --git a/reveal.js/bower.json b/reveal.js/bower.json index 808fdf6..bc825ab 100644 --- a/reveal.js/bower.json +++ b/reveal.js/bower.json @@ -1,19 +1,16 @@ { "name": "reveal.js", - "version": "3.2.0", + "version": "3.9.2", "main": [ "js/reveal.js", "css/reveal.css" ], - "homepage": "https://lab.hakim.se/reveal-js/", + "homepage": "http://revealjs.com", "license": "MIT", "description": "The HTML Presentation Framework", "authors": [ "Hakim El Hattab " ], - "dependencies": { - "headjs": "~1.0.3" - }, "repository": { "type": "git", "url": "git://github.com/hakimel/reveal.js.git" diff --git a/reveal.js/css/print/paper.css b/reveal.js/css/print/paper.css index 6588f48..27d19dd 100644 --- a/reveal.js/css/print/paper.css +++ b/reveal.js/css/print/paper.css @@ -38,7 +38,8 @@ .share-reveal, .state-background, .reveal .progress, - .reveal .backgrounds { + .reveal .backgrounds, + .reveal .slide-number { display: none !important; } @@ -199,4 +200,4 @@ font-size: 0.8em; } -} \ No newline at end of file +} diff --git a/reveal.js/css/print/pdf.css b/reveal.js/css/print/pdf.css index 9ed90d6..752d955 100644 --- a/reveal.js/css/print/pdf.css +++ b/reveal.js/css/print/pdf.css @@ -60,8 +60,9 @@ ul, ol, div, p { } .reveal .slides { position: static; - width: 100%; - height: auto; + width: 100% !important; + height: auto !important; + zoom: 1 !important; left: auto; top: auto; @@ -71,24 +72,22 @@ ul, ol, div, p { overflow: visible; display: block; - -webkit-perspective: none; - -moz-perspective: none; - -ms-perspective: none; - perspective: none; + perspective: none; + perspective-origin: 50% 50%; +} - -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */ - -moz-perspective-origin: 50% 50%; - -ms-perspective-origin: 50% 50%; - perspective-origin: 50% 50%; +.reveal .slides .pdf-page { + position: relative; + overflow: hidden; + z-index: 1; + + page-break-after: always; } .reveal .slides section { - page-break-after: always !important; - visibility: visible !important; - position: relative !important; display: block !important; - position: relative !important; + position: absolute !important; margin: 0 !important; padding: 0 !important; @@ -97,18 +96,12 @@ ul, ol, div, p { opacity: 1 !important; - -webkit-transform-style: flat !important; - -moz-transform-style: flat !important; - -ms-transform-style: flat !important; - transform-style: flat !important; - - -webkit-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - transform: none !important; + transform-style: flat !important; + transform: none !important; } .reveal section.stack { + position: relative !important; margin: 0 !important; padding: 0 !important; page-break-after: avoid !important; @@ -126,35 +119,46 @@ ul, ol, div, p { } /* Slide backgrounds are placed inside of their slide when exporting to PDF */ -.reveal section .slide-background { +.reveal .slide-background { display: block !important; position: absolute; top: 0; left: 0; width: 100%; - z-index: -1; -} - -/* All elements should be above the slide-background */ -.reveal section>* { - position: relative; - z-index: 1; + height: 100%; + z-index: auto !important; } /* Display slide speaker notes when 'showNotes' is enabled */ +.reveal.show-notes { + max-width: none; + max-height: none; +} .reveal .speaker-notes-pdf { display: block; width: 100%; + height: auto; max-height: none; - left: auto; top: auto; + right: auto; + bottom: auto; + left: auto; z-index: 100; } +/* Layout option which makes notes appear on a separate page */ +.reveal .speaker-notes-pdf[data-layout="separate-page"] { + position: relative; + color: inherit; + background-color: transparent; + padding: 20px; + page-break-after: always; + border: 0; +} + /* Display slide numbers when 'slideNumber' is enabled */ .reveal .slide-number-pdf { display: block; position: absolute; font-size: 14px; } - diff --git a/reveal.js/css/reset.css b/reveal.js/css/reset.css new file mode 100644 index 0000000..e238539 --- /dev/null +++ b/reveal.js/css/reset.css @@ -0,0 +1,30 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v4.0 | 20180602 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +main, menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, main, menu, nav, section { + display: block; +} \ No newline at end of file diff --git a/reveal.js/css/reveal.css b/reveal.js/css/reveal.css index d3edf61..0ca77e7 100644 --- a/reveal.js/css/reveal.css +++ b/reveal.js/css/reveal.css @@ -1,121 +1,127 @@ /*! * reveal.js - * https://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2016 Hakim El Hattab, https://hakim.se + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se */ -/********************************************* - * RESET STYLES - *********************************************/ -html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, -.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, -.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, -.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, -.reveal b, .reveal u, .reveal center, -.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, -.reveal fieldset, .reveal form, .reveal label, .reveal legend, -.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, -.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, -.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, -.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, -.reveal time, .reveal mark, .reveal audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; } - -.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, -.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { - display: block; } - /********************************************* * GLOBAL STYLES *********************************************/ -html, -body { +html { width: 100%; height: 100%; + height: 100vh; + height: calc( var(--vh, 1vh) * 100); overflow: hidden; } body { + height: 100%; + overflow: hidden; position: relative; line-height: 1; + margin: 0; background-color: #fff; color: #000; } -html:-webkit-full-screen-ancestor { - background-color: inherit; } - -html:-moz-full-screen-ancestor { - background-color: inherit; } - /********************************************* * VIEW FRAGMENTS *********************************************/ .reveal .slides section .fragment { opacity: 0; visibility: hidden; - -webkit-transition: all 0.2s ease; - transition: all 0.2s ease; } + transition: all .2s ease; } .reveal .slides section .fragment.visible { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.grow { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.grow.visible { -webkit-transform: scale(1.3); - -ms-transform: scale(1.3); transform: scale(1.3); } .reveal .slides section .fragment.shrink { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.shrink.visible { -webkit-transform: scale(0.7); - -ms-transform: scale(0.7); transform: scale(0.7); } .reveal .slides section .fragment.zoom-in { -webkit-transform: scale(0.1); - -ms-transform: scale(0.1); transform: scale(0.1); } .reveal .slides section .fragment.zoom-in.visible { -webkit-transform: none; - -ms-transform: none; transform: none; } .reveal .slides section .fragment.fade-out { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.fade-out.visible { opacity: 0; visibility: hidden; } .reveal .slides section .fragment.semi-fade-out { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.semi-fade-out.visible { opacity: 0.5; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.strike { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.strike.visible { text-decoration: line-through; } +.reveal .slides section .fragment.fade-up { + -webkit-transform: translate(0, 40px); + transform: translate(0, 40px); } + .reveal .slides section .fragment.fade-up.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-down { + -webkit-transform: translate(0, -40px); + transform: translate(0, -40px); } + .reveal .slides section .fragment.fade-down.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-right { + -webkit-transform: translate(-40px, 0); + transform: translate(-40px, 0); } + .reveal .slides section .fragment.fade-right.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-left { + -webkit-transform: translate(40px, 0); + transform: translate(40px, 0); } + .reveal .slides section .fragment.fade-left.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-in-then-out, .reveal .slides section .fragment.current-visible { opacity: 0; visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-out.current-fragment, .reveal .slides section .fragment.current-visible.current-fragment { opacity: 1; - visibility: visible; } + visibility: inherit; } + +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-semi-out.visible { + opacity: 0.5; + visibility: inherit; } + .reveal .slides section .fragment.fade-in-then-semi-out.current-fragment { + opacity: 1; + visibility: inherit; } .reveal .slides section .fragment.highlight-red, .reveal .slides section .fragment.highlight-current-red, @@ -124,7 +130,7 @@ html:-moz-full-screen-ancestor { .reveal .slides section .fragment.highlight-blue, .reveal .slides section .fragment.highlight-current-blue { opacity: 1; - visibility: visible; } + visibility: inherit; } .reveal .slides section .fragment.highlight-red.visible { color: #ff2c2d; } @@ -171,107 +177,274 @@ html:-moz-full-screen-ancestor { /********************************************* * CONTROLS *********************************************/ +@-webkit-keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } + +@-webkit-keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + .reveal .controls { display: none; - position: fixed; - width: 110px; - height: 110px; - z-index: 30; - right: 10px; - bottom: 10px; - -webkit-user-select: none; } - -.reveal .controls button { - padding: 0; position: absolute; - opacity: 0.05; - width: 0; - height: 0; - background-color: transparent; - border: 12px solid transparent; - -webkit-transform: scale(0.9999); - -ms-transform: scale(0.9999); - transform: scale(0.9999); - -webkit-transition: all 0.2s ease; - transition: all 0.2s ease; - -webkit-appearance: none; - -webkit-tap-highlight-color: transparent; } + top: auto; + bottom: 12px; + right: 12px; + left: auto; + z-index: 11; + color: #000; + pointer-events: none; + font-size: 10px; } + .reveal .controls button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + -webkit-transform: scale(0.9999); + transform: scale(0.9999); + transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease; + transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease; + z-index: 2; + pointer-events: auto; + font-size: inherit; + visibility: hidden; + opacity: 0; + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + .reveal .controls .controls-arrow:before, + .reveal .controls .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 2.6em; + height: 0.5em; + border-radius: 0.25em; + background-color: currentColor; + transition: all 0.15s ease, background-color 0.8s ease; + -webkit-transform-origin: 0.2em 50%; + transform-origin: 0.2em 50%; + will-change: transform; } + .reveal .controls .controls-arrow { + position: relative; + width: 3.6em; + height: 3.6em; } + .reveal .controls .controls-arrow:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + .reveal .controls .controls-arrow:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + .reveal .controls .controls-arrow:hover:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(40deg); } + .reveal .controls .controls-arrow:hover:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); } + .reveal .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(36deg); } + .reveal .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); } + .reveal .controls .navigate-left { + right: 6.4em; + bottom: 3.2em; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); } + .reveal .controls .navigate-right { + right: 0; + bottom: 3.2em; + -webkit-transform: translateX(10px); + transform: translateX(10px); } + .reveal .controls .navigate-right .controls-arrow { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + .reveal .controls .navigate-right.highlight { + -webkit-animation: bounce-right 2s 50 both ease-out; + animation: bounce-right 2s 50 both ease-out; } + .reveal .controls .navigate-up { + right: 3.2em; + bottom: 6.4em; + -webkit-transform: translateY(-10px); + transform: translateY(-10px); } + .reveal .controls .navigate-up .controls-arrow { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + .reveal .controls .navigate-down { + right: 3.2em; + bottom: -1.4em; + padding-bottom: 1.4em; + -webkit-transform: translateY(10px); + transform: translateY(10px); } + .reveal .controls .navigate-down .controls-arrow { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); } + .reveal .controls .navigate-down.highlight { + -webkit-animation: bounce-down 2s 50 both ease-out; + animation: bounce-down 2s 50 both ease-out; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover { + opacity: 1; } + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; } + .reveal .controls .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + -webkit-transform: none; + transform: none; } + .reveal .controls .enabled.fragmented { + opacity: 0.5; } + .reveal .controls .enabled:hover, + .reveal .controls .enabled.fragmented:hover { + opacity: 1; } -.reveal .controls .enabled { - opacity: 0.7; - cursor: pointer; } +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-up, +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-down { + display: none; } -.reveal .controls .enabled:active { - margin-top: 1px; } +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-left, +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: 1.4em; + right: 5.5em; } -.reveal .controls .navigate-left { - top: 42px; - border-right-width: 22px; - border-right-color: #000; } +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-right, +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: 1.4em; + right: 0.5em; } -.reveal .controls .navigate-left.fragmented { - opacity: 0.3; } +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: 1.4em; + bottom: 5em; } -.reveal .controls .navigate-right { - left: 74px; - top: 42px; - border-left-width: 22px; - border-left-color: #000; } +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: 1.4em; + bottom: 0.5em; } -.reveal .controls .navigate-right.fragmented { - opacity: 0.3; } +.reveal.has-dark-background .controls { + color: #fff; } -.reveal .controls .navigate-up { - left: 42px; - border-bottom-width: 22px; - border-bottom-color: #000; } +.reveal.has-light-background .controls { + color: #000; } -.reveal .controls .navigate-up.fragmented { - opacity: 0.3; } +.reveal.no-hover .controls .controls-arrow:hover:before, +.reveal.no-hover .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } -.reveal .controls .navigate-down { - left: 42px; - top: 74px; - border-top-width: 22px; - border-top-color: #000; } +.reveal.no-hover .controls .controls-arrow:hover:after, +.reveal.no-hover .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } -.reveal .controls .navigate-down.fragmented { - opacity: 0.3; } +@media screen and (min-width: 500px) { + .reveal .controls[data-controls-layout="edges"] { + top: 0; + right: 0; + bottom: 0; + left: 0; } + .reveal .controls[data-controls-layout="edges"] .navigate-left, + .reveal .controls[data-controls-layout="edges"] .navigate-right, + .reveal .controls[data-controls-layout="edges"] .navigate-up, + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: auto; + right: auto; } + .reveal .controls[data-controls-layout="edges"] .navigate-left { + top: 50%; + left: 0.8em; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-right { + top: 50%; + right: 0.8em; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-up { + top: 0.8em; + left: 50%; + margin-left: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: -0.3em; + left: 50%; + margin-left: -1.8em; } } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - position: fixed; + position: absolute; display: none; height: 3px; width: 100%; bottom: 0; left: 0; z-index: 10; - background-color: rgba(0, 0, 0, 0.2); } + background-color: rgba(0, 0, 0, 0.2); + color: #fff; } .reveal .progress:after { content: ''; display: block; position: absolute; - height: 20px; + height: 10px; width: 100%; - top: -20px; } + top: -10px; } .reveal .progress span { display: block; height: 100%; width: 0px; - background-color: #000; - -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + background-color: currentColor; + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /********************************************* * SLIDE NUMBER *********************************************/ .reveal .slide-number { - position: fixed; + position: absolute; display: block; right: 8px; bottom: 8px; @@ -283,6 +456,9 @@ html:-moz-full-screen-ancestor { background-color: rgba(0, 0, 0, 0.4); padding: 5px; } +.reveal .slide-number a { + color: currentColor; } + .reveal .slide-number-delimiter { margin: 0 3px; } @@ -294,8 +470,8 @@ html:-moz-full-screen-ancestor { width: 100%; height: 100%; overflow: hidden; - -ms-touch-action: none; - touch-action: none; } + -ms-touch-action: pinch-zoom; + touch-action: pinch-zoom; } .reveal .slides { position: absolute; @@ -306,6 +482,7 @@ html:-moz-full-screen-ancestor { bottom: 0; left: 0; margin: auto; + pointer-events: none; overflow: visible; z-index: 1; text-align: center; @@ -315,7 +492,8 @@ html:-moz-full-screen-ancestor { perspective-origin: 50% 40%; } .reveal .slides > section { - -ms-perspective: 600px; } + -webkit-perspective: 600px; + perspective: 600px; } .reveal .slides > section, .reveal .slides > section > section { @@ -323,34 +501,32 @@ html:-moz-full-screen-ancestor { position: absolute; width: 100%; padding: 20px 0px; + pointer-events: auto; z-index: 10; - -webkit-transform-style: preserve-3d; - transform-style: preserve-3d; - -webkit-transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: -ms-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + -webkit-transform-style: flat; + transform-style: flat; + transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } /* Global transition speed settings */ .reveal[data-transition-speed="fast"] .slides section { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal[data-transition-speed="slow"] .slides section { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /* Slide-specific transition speed overrides */ .reveal .slides section[data-transition-speed="fast"] { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal .slides section[data-transition-speed="slow"] { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } .reveal .slides > section.stack { padding-top: 0; - padding-bottom: 0; } + padding-bottom: 0; + pointer-events: none; + height: 100%; } .reveal .slides > section.present, .reveal .slides > section > section.present { @@ -358,6 +534,12 @@ html:-moz-full-screen-ancestor { z-index: 11; opacity: 1; } +.reveal .slides > section:empty, +.reveal .slides > section > section:empty, +.reveal .slides > section[data-background-interactive], +.reveal .slides > section > section[data-background-interactive] { + pointer-events: none; } + .reveal.center, .reveal.center .slides, .reveal.center .slides section { @@ -395,28 +577,24 @@ html:-moz-full-screen-ancestor { .reveal .slides > section[data-transition~=slide-out].past, .reveal.slide .slides > section:not([data-transition]).past { -webkit-transform: translate(-150%, 0); - -ms-transform: translate(-150%, 0); transform: translate(-150%, 0); } .reveal .slides > section[data-transition=slide].future, .reveal .slides > section[data-transition~=slide-in].future, .reveal.slide .slides > section:not([data-transition]).future { -webkit-transform: translate(150%, 0); - -ms-transform: translate(150%, 0); transform: translate(150%, 0); } .reveal .slides > section > section[data-transition=slide].past, .reveal .slides > section > section[data-transition~=slide-out].past, .reveal.slide .slides > section > section:not([data-transition]).past { -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); transform: translate(0, -150%); } .reveal .slides > section > section[data-transition=slide].future, .reveal .slides > section > section[data-transition~=slide-in].future, .reveal.slide .slides > section > section:not([data-transition]).future { -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); transform: translate(0, 150%); } .reveal.linear section { @@ -427,34 +605,35 @@ html:-moz-full-screen-ancestor { .reveal .slides > section[data-transition~=linear-out].past, .reveal.linear .slides > section:not([data-transition]).past { -webkit-transform: translate(-150%, 0); - -ms-transform: translate(-150%, 0); transform: translate(-150%, 0); } .reveal .slides > section[data-transition=linear].future, .reveal .slides > section[data-transition~=linear-in].future, .reveal.linear .slides > section:not([data-transition]).future { -webkit-transform: translate(150%, 0); - -ms-transform: translate(150%, 0); transform: translate(150%, 0); } .reveal .slides > section > section[data-transition=linear].past, .reveal .slides > section > section[data-transition~=linear-out].past, .reveal.linear .slides > section > section:not([data-transition]).past { -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); transform: translate(0, -150%); } .reveal .slides > section > section[data-transition=linear].future, .reveal .slides > section > section[data-transition~=linear-in].future, .reveal.linear .slides > section > section:not([data-transition]).future { -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); transform: translate(0, 150%); } /********************************************* * CONVEX TRANSITION * Aliased 'default' for backwards compatibility *********************************************/ +.reveal .slides section[data-transition=default].stack, +.reveal.default .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + .reveal .slides > section[data-transition=default].past, .reveal .slides > section[data-transition~=default-out].past, .reveal.default .slides > section:not([data-transition]).past { @@ -479,6 +658,11 @@ html:-moz-full-screen-ancestor { -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } +.reveal .slides section[data-transition=convex].stack, +.reveal.convex .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + .reveal .slides > section[data-transition=convex].past, .reveal .slides > section[data-transition~=convex-out].past, .reveal.convex .slides > section:not([data-transition]).past { @@ -506,6 +690,11 @@ html:-moz-full-screen-ancestor { /********************************************* * CONCAVE TRANSITION *********************************************/ +.reveal .slides section[data-transition=concave].stack, +.reveal.concave .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + .reveal .slides > section[data-transition=concave].past, .reveal .slides > section[data-transition~=concave-out].past, .reveal.concave .slides > section:not([data-transition]).past { @@ -535,15 +724,13 @@ html:-moz-full-screen-ancestor { *********************************************/ .reveal .slides section[data-transition=zoom], .reveal.zoom .slides section:not([data-transition]) { - -webkit-transition-timing-function: ease; - transition-timing-function: ease; } + transition-timing-function: ease; } .reveal .slides > section[data-transition=zoom].past, .reveal .slides > section[data-transition~=zoom-out].past, .reveal.zoom .slides > section:not([data-transition]).past { visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } .reveal .slides > section[data-transition=zoom].future, @@ -551,25 +738,26 @@ html:-moz-full-screen-ancestor { .reveal.zoom .slides > section:not([data-transition]).future { visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } .reveal .slides > section > section[data-transition=zoom].past, .reveal .slides > section > section[data-transition~=zoom-out].past, .reveal.zoom .slides > section > section:not([data-transition]).past { - -webkit-transform: translate(0, -150%); - -ms-transform: translate(0, -150%); - transform: translate(0, -150%); } + -webkit-transform: scale(16); + transform: scale(16); } .reveal .slides > section > section[data-transition=zoom].future, .reveal .slides > section > section[data-transition~=zoom-in].future, .reveal.zoom .slides > section > section:not([data-transition]).future { - -webkit-transform: translate(0, 150%); - -ms-transform: translate(0, 150%); - transform: translate(0, 150%); } + -webkit-transform: scale(0.2); + transform: scale(0.2); } /********************************************* * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.cube .slides { -webkit-perspective: 1300px; @@ -580,7 +768,9 @@ html:-moz-full-screen-ancestor { min-height: 700px; -webkit-backface-visibility: hidden; backface-visibility: hidden; - box-sizing: border-box; } + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } .reveal.center.cube .slides section { min-height: 0; } @@ -619,34 +809,34 @@ html:-moz-full-screen-ancestor { .reveal.cube .slides > section.past { -webkit-transform-origin: 100% 0%; - -ms-transform-origin: 100% 0%; transform-origin: 100% 0%; -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg); transform: translate3d(-100%, 0, 0) rotateY(-90deg); } .reveal.cube .slides > section.future { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg); transform: translate3d(100%, 0, 0) rotateY(90deg); } .reveal.cube .slides > section > section.past { -webkit-transform-origin: 0% 100%; - -ms-transform-origin: 0% 100%; transform-origin: 0% 100%; -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg); transform: translate3d(0, -100%, 0) rotateX(90deg); } .reveal.cube .slides > section > section.future { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg); transform: translate3d(0, 100%, 0) rotateX(-90deg); } /********************************************* * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.page .slides { -webkit-perspective-origin: 0% 50%; @@ -657,7 +847,9 @@ html:-moz-full-screen-ancestor { .reveal.page .slides section { padding: 30px; min-height: 700px; - box-sizing: border-box; } + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } .reveal.page .slides section.past { z-index: 12; } @@ -694,28 +886,24 @@ html:-moz-full-screen-ancestor { .reveal.page .slides > section.past { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg); transform: translate3d(-40%, 0, 0) rotateY(-80deg); } .reveal.page .slides > section.future { -webkit-transform-origin: 100% 0%; - -ms-transform-origin: 100% 0%; transform-origin: 100% 0%; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .reveal.page .slides > section > section.past { -webkit-transform-origin: 0% 0%; - -ms-transform-origin: 0% 0%; transform-origin: 0% 0%; -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg); transform: translate3d(0, -40%, 0) rotateX(80deg); } .reveal.page .slides > section > section.future { -webkit-transform-origin: 0% 100%; - -ms-transform-origin: 0% 100%; transform-origin: 0% 100%; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -727,15 +915,12 @@ html:-moz-full-screen-ancestor { .reveal.fade .slides section:not([data-transition]), .reveal.fade .slides > section > section:not([data-transition]) { -webkit-transform: none; - -ms-transform: none; transform: none; - -webkit-transition: opacity 0.5s; - transition: opacity 0.5s; } + transition: opacity 0.5s; } .reveal.fade.overview .slides section, .reveal.fade.overview .slides > section > section { - -webkit-transition: none; - transition: none; } + transition: none; } /********************************************* * NO TRANSITION @@ -743,10 +928,8 @@ html:-moz-full-screen-ancestor { .reveal .slides section[data-transition=none], .reveal.none .slides section:not([data-transition]) { -webkit-transform: none; - -ms-transform: none; transform: none; - -webkit-transition: none; - transition: none; } + transition: none; } /********************************************* * PAUSED MODE @@ -761,8 +944,22 @@ html:-moz-full-screen-ancestor { visibility: hidden; opacity: 0; z-index: 100; - -webkit-transition: all 1s ease; - transition: all 1s ease; } + transition: all 1s ease; } + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; } + .reveal .pause-overlay .resume-button:hover { + color: #fff; + border-color: #fff; } .reveal.paused .pause-overlay { visibility: visible; @@ -774,39 +971,38 @@ html:-moz-full-screen-ancestor { .no-transforms { overflow-y: auto; } +.no-transforms .reveal { + overflow: visible; } + .no-transforms .reveal .slides { position: relative; width: 80%; - height: auto !important; + max-width: 1280px; + height: auto; top: 0; - left: 50%; - margin: 0; + margin: 0 auto; text-align: center; } .no-transforms .reveal .controls, .no-transforms .reveal .progress { - display: none !important; } + display: none; } .no-transforms .reveal .slides section { - display: block !important; - opacity: 1 !important; - position: relative !important; + display: block; + opacity: 1; + position: relative; height: auto; min-height: 0; top: 0; - left: -50%; + left: 0; + margin: 10vh 0; margin: 70px 0; -webkit-transform: none; - -ms-transform: none; transform: none; } -.no-transforms .reveal .slides section section { - left: 0; } - .reveal .no-transition, .reveal .no-transition * { - -webkit-transition: none !important; - transition: none !important; } + transition: none !important; } /********************************************* * PER-SLIDE BACKGROUNDS @@ -827,19 +1023,25 @@ html:-moz-full-screen-ancestor { height: 100%; opacity: 0; visibility: hidden; - background-color: transparent; + overflow: hidden; + background-color: rgba(0, 0, 0, 0); + transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; background-position: 50% 50%; background-repeat: no-repeat; - background-size: cover; - -webkit-transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); - transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + background-size: cover; } .reveal .slide-background.stack { display: block; } .reveal .slide-background.present { opacity: 1; - visibility: visible; } + visibility: visible; + z-index: 2; } .print-pdf .reveal .slide-background { opacity: 1 !important; @@ -853,13 +1055,18 @@ html:-moz-full-screen-ancestor { max-width: none; max-height: none; top: 0; - left: 0; } + left: 0; + -o-object-fit: cover; + object-fit: cover; } + +.reveal .slide-background[data-background-size="contain"] video { + -o-object-fit: contain; + object-fit: contain; } /* Immediate transition style */ .reveal[data-background-transition=none] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=none] { - -webkit-transition: none; - transition: none; } + transition: none; } /* Slide */ .reveal[data-background-transition=slide] > .backgrounds .slide-background, @@ -871,25 +1078,21 @@ html:-moz-full-screen-ancestor { .reveal[data-background-transition=slide] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=slide] { -webkit-transform: translate(-100%, 0); - -ms-transform: translate(-100%, 0); transform: translate(-100%, 0); } .reveal[data-background-transition=slide] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=slide] { -webkit-transform: translate(100%, 0); - -ms-transform: translate(100%, 0); transform: translate(100%, 0); } .reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] { -webkit-transform: translate(0, -100%); - -ms-transform: translate(0, -100%); transform: translate(0, -100%); } .reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] { -webkit-transform: translate(0, 100%); - -ms-transform: translate(0, 100%); transform: translate(0, 100%); } /* Convex */ @@ -945,15 +1148,13 @@ html:-moz-full-screen-ancestor { /* Zoom */ .reveal[data-background-transition=zoom] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=zoom] { - -webkit-transition-timing-function: ease; - transition-timing-function: ease; } + transition-timing-function: ease; } .reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=zoom] { opacity: 0; visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } .reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, @@ -961,7 +1162,6 @@ html:-moz-full-screen-ancestor { opacity: 0; visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } .reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, @@ -969,7 +1169,6 @@ html:-moz-full-screen-ancestor { opacity: 0; visibility: hidden; -webkit-transform: scale(16); - -ms-transform: scale(16); transform: scale(16); } .reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, @@ -977,17 +1176,14 @@ html:-moz-full-screen-ancestor { opacity: 0; visibility: hidden; -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); transform: scale(0.2); } /* Global transition speed settings */ .reveal[data-transition-speed="fast"] > .backgrounds .slide-background { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal[data-transition-speed="slow"] > .backgrounds .slide-background { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /********************************************* * OVERVIEW @@ -997,8 +1193,11 @@ html:-moz-full-screen-ancestor { perspective-origin: 50% 50%; -webkit-perspective: 700px; perspective: 700px; } + .reveal.overview .slides { + -moz-transform-style: preserve-3d; } .reveal.overview .slides section { - height: 700px; + height: 100%; + top: 0 !important; opacity: 1 !important; overflow: hidden; visibility: visible !important; @@ -1010,8 +1209,7 @@ html:-moz-full-screen-ancestor { outline-offset: 10px; } .reveal.overview .slides section .fragment { opacity: 1; - -webkit-transition: none; - transition: none; } + transition: none; } .reveal.overview .slides section:after, .reveal.overview .slides section:before { display: none !important; } @@ -1023,26 +1221,23 @@ html:-moz-full-screen-ancestor { overflow: visible; } .reveal.overview .backgrounds { -webkit-perspective: inherit; - perspective: inherit; } + perspective: inherit; + -moz-transform-style: preserve-3d; } .reveal.overview .backgrounds .slide-background { opacity: 1; visibility: visible; outline: 10px solid rgba(150, 150, 150, 0.1); outline-offset: 10px; } + .reveal.overview .backgrounds .slide-background.stack { + overflow: visible; } .reveal.overview .slides section, .reveal.overview-deactivating .slides section { - -webkit-transition: none; - transition: none; } + transition: none; } .reveal.overview .backgrounds .slide-background, .reveal.overview-deactivating .backgrounds .slide-background { - -webkit-transition: none; - transition: none; } - -.reveal.overview-animated .slides { - -webkit-transition: -webkit-transform 0.4s ease; - transition: transform 0.4s ease; } + transition: none; } /********************************************* * RTL SUPPORT @@ -1072,22 +1267,19 @@ html:-moz-full-screen-ancestor { * PARALLAX BACKGROUND *********************************************/ .reveal.has-parallax-background .backgrounds { - -webkit-transition: all 0.8s ease; - transition: all 0.8s ease; } + transition: all 0.8s ease; } /* Global transition speed settings */ .reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { - -webkit-transition-duration: 400ms; - transition-duration: 400ms; } + transition-duration: 400ms; } .reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { - -webkit-transition-duration: 1200ms; - transition-duration: 1200ms; } + transition-duration: 1200ms; } /********************************************* - * LINK PREVIEW OVERLAY + * OVERLAY FOR LINK PREVIEWS AND HELP *********************************************/ -.reveal .overlay { +.reveal > .overlay { position: absolute; top: 0; left: 0; @@ -1097,14 +1289,13 @@ html:-moz-full-screen-ancestor { background: rgba(0, 0, 0, 0.9); opacity: 0; visibility: hidden; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } -.reveal .overlay.visible { +.reveal > .overlay.visible { opacity: 1; visibility: visible; } -.reveal .overlay .spinner { +.reveal > .overlay .spinner { position: absolute; display: block; top: 50%; @@ -1116,10 +1307,9 @@ html:-moz-full-screen-ancestor { background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); visibility: visible; opacity: 0.6; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } -.reveal .overlay header { +.reveal > .overlay header { position: absolute; left: 0; top: 0; @@ -1128,19 +1318,20 @@ html:-moz-full-screen-ancestor { z-index: 2; border-bottom: 1px solid #222; } -.reveal .overlay header a { +.reveal > .overlay header a { display: inline-block; width: 40px; height: 40px; + line-height: 36px; padding: 0 10px; float: right; opacity: 0.6; box-sizing: border-box; } -.reveal .overlay header a:hover { +.reveal > .overlay header a:hover { opacity: 1; } -.reveal .overlay header a .icon { +.reveal > .overlay header a .icon { display: inline-block; width: 20px; height: 20px; @@ -1148,20 +1339,23 @@ html:-moz-full-screen-ancestor { background-size: 100%; background-repeat: no-repeat; } -.reveal .overlay header a.close .icon { +.reveal > .overlay header a.close .icon { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); } -.reveal .overlay header a.external .icon { +.reveal > .overlay header a.external .icon { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); } -.reveal .overlay .viewport { +.reveal > .overlay .viewport { position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; top: 40px; right: 0; bottom: 0; left: 0; } -.reveal .overlay.overlay-preview .viewport iframe { +.reveal > .overlay.overlay-preview .viewport iframe { width: 100%; height: 100%; max-width: 100%; @@ -1169,47 +1363,61 @@ html:-moz-full-screen-ancestor { border: 0; opacity: 0; visibility: hidden; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } + transition: all 0.3s ease; } -.reveal .overlay.overlay-preview.loaded .viewport iframe { +.reveal > .overlay.overlay-preview.loaded .viewport iframe { opacity: 1; visibility: visible; } -.reveal .overlay.overlay-preview.loaded .spinner { - opacity: 0; - visibility: hidden; - -webkit-transform: scale(0.2); - -ms-transform: scale(0.2); - transform: scale(0.2); } - -.reveal .overlay.overlay-help .viewport { - overflow: auto; - color: #fff; } - -.reveal .overlay.overlay-help .viewport .viewport-inner { - width: 600px; - margin: 0 auto; - padding: 60px; +.reveal > .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; text-align: center; letter-spacing: normal; } -.reveal .overlay.overlay-help .viewport .viewport-inner .title { +.reveal > .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; } + +.reveal > .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; } + +.reveal > .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal > .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner .title { font-size: 20px; } -.reveal .overlay.overlay-help .viewport .viewport-inner table { +.reveal > .overlay.overlay-help .viewport .viewport-inner table { border: 1px solid #fff; border-collapse: collapse; - font-size: 14px; } + font-size: 16px; } -.reveal .overlay.overlay-help .viewport .viewport-inner table th, -.reveal .overlay.overlay-help .viewport .viewport-inner table td { +.reveal > .overlay.overlay-help .viewport .viewport-inner table th, +.reveal > .overlay.overlay-help .viewport .viewport-inner table td { width: 200px; - padding: 10px; + padding: 14px; border: 1px solid #fff; vertical-align: middle; } -.reveal .overlay.overlay-help .viewport .viewport-inner table th { +.reveal > .overlay.overlay-help .viewport .viewport-inner table th { padding-top: 20px; padding-bottom: 20px; } @@ -1217,18 +1425,45 @@ html:-moz-full-screen-ancestor { * PLAYBACK COMPONENT *********************************************/ .reveal .playback { - position: fixed; + position: absolute; left: 15px; bottom: 20px; z-index: 30; cursor: pointer; - -webkit-transition: all 400ms ease; - transition: all 400ms ease; } + transition: all 400ms ease; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .reveal.overview .playback { opacity: 0; visibility: hidden; } +/********************************************* + * CODE HIGHLGIHTING + *********************************************/ +.reveal .hljs table { + margin: initial; } + +.reveal .hljs-ln-code, +.reveal .hljs-ln-numbers { + padding: 0; + border: 0; } + +.reveal .hljs-ln-numbers { + opacity: 0.6; + padding-right: 0.75em; + text-align: right; + vertical-align: top; } + +.reveal .hljs.has-highlights tr:not(.highlight-line) { + opacity: 0.4; } + +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; } + /********************************************* * ROLLING LINKS *********************************************/ @@ -1251,10 +1486,8 @@ html:-moz-full-screen-ancestor { position: relative; padding: 0 2px; pointer-events: none; - -webkit-transition: all 400ms ease; - transition: all 400ms ease; + transition: all 400ms ease; -webkit-transform-origin: 50% 0%; - -ms-transform-origin: 50% 0%; transform-origin: 50% 0%; -webkit-transform-style: preserve-3d; transform-style: preserve-3d; @@ -1276,7 +1509,6 @@ html:-moz-full-screen-ancestor { -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-transform-origin: 50% 0%; - -ms-transform-origin: 50% 0%; transform-origin: 50% 0%; -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg); transform: translate3d(0px, 110%, 0px) rotateX(-90deg); } @@ -1290,33 +1522,69 @@ html:-moz-full-screen-ancestor { .reveal .speaker-notes { display: none; position: absolute; - width: 70%; - max-height: 15%; - left: 15%; - bottom: 26px; - padding: 10px; + width: 33.3333333333%; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; z-index: 1; font-size: 18px; line-height: 1.4; - color: #fff; - background-color: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(0, 0, 0, 0.05); + color: #222; + background-color: #f5f5f5; overflow: auto; box-sizing: border-box; text-align: left; font-family: Helvetica, sans-serif; -webkit-overflow-scrolling: touch; } + .reveal .speaker-notes .notes-placeholder { + color: #ccc; + font-style: italic; } + .reveal .speaker-notes:focus { + outline: none; } + .reveal .speaker-notes:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; } -.reveal .speaker-notes.visible:not(:empty) { +.reveal.show-notes { + max-width: 75%; + overflow: visible; } + +.reveal.show-notes .speaker-notes { display: block; } -@media screen and (max-width: 1024px) { +@media screen and (min-width: 1600px) { .reveal .speaker-notes { - font-size: 14px; } } + font-size: 20px; } } + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + max-height: 70vh; + overflow: visible; } + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: 42.8571428571%; + height: 30vh; + border: 0; } } @media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + max-height: 60vh; } + .reveal.show-notes .speaker-notes { + top: 100%; + height: 66.6666666667%; + height: 40vh; } .reveal .speaker-notes { - width: 90%; - left: 5%; } } + font-size: 14px; } } /********************************************* * ZOOM PLUGIN diff --git a/reveal.js/css/reveal.scss b/reveal.js/css/reveal.scss index ee478b1..ab11f32 100644 --- a/reveal.js/css/reveal.scss +++ b/reveal.js/css/reveal.scss @@ -1,71 +1,35 @@ /*! * reveal.js - * https://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2016 Hakim El Hattab, https://hakim.se + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se */ -/********************************************* - * RESET STYLES - *********************************************/ - -html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, -.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, -.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, -.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, -.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, -.reveal b, .reveal u, .reveal center, -.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, -.reveal fieldset, .reveal form, .reveal label, .reveal legend, -.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, -.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, -.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, -.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, -.reveal time, .reveal mark, .reveal audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} - -.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, -.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { - display: block; -} - - /********************************************* * GLOBAL STYLES *********************************************/ -html, -body { +html { width: 100%; height: 100%; + height: 100vh; + height: calc( var(--vh, 1vh) * 100 ); overflow: hidden; } body { + height: 100%; + overflow: hidden; position: relative; line-height: 1; + margin: 0; background-color: #fff; color: #000; } -// Ensures that the main background color matches the -// theme in fullscreen mode -html:-webkit-full-screen-ancestor { - background-color: inherit; -} -html:-moz-full-screen-ancestor { - background-color: inherit; -} - /********************************************* * VIEW FRAGMENTS @@ -78,13 +42,13 @@ html:-moz-full-screen-ancestor { &.visible { opacity: 1; - visibility: visible; + visibility: inherit; } } .reveal .slides section .fragment.grow { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { transform: scale( 1.3 ); @@ -93,7 +57,7 @@ html:-moz-full-screen-ancestor { .reveal .slides section .fragment.shrink { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { transform: scale( 0.7 ); @@ -110,7 +74,7 @@ html:-moz-full-screen-ancestor { .reveal .slides section .fragment.fade-out { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { opacity: 0; @@ -120,30 +84,78 @@ html:-moz-full-screen-ancestor { .reveal .slides section .fragment.semi-fade-out { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { opacity: 0.5; - visibility: visible; + visibility: inherit; } } .reveal .slides section .fragment.strike { opacity: 1; - visibility: visible; + visibility: inherit; &.visible { text-decoration: line-through; } } +.reveal .slides section .fragment.fade-up { + transform: translate(0, 40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-down { + transform: translate(0, -40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-right { + transform: translate(-40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-left { + transform: translate(40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-in-then-out, .reveal .slides section .fragment.current-visible { opacity: 0; visibility: hidden; &.current-fragment { opacity: 1; - visibility: visible; + visibility: inherit; + } +} + +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; + + &.visible { + opacity: 0.5; + visibility: inherit; + } + + &.current-fragment { + opacity: 1; + visibility: inherit; } } @@ -154,7 +166,7 @@ html:-moz-full-screen-ancestor { .reveal .slides section .fragment.highlight-blue, .reveal .slides section .fragment.highlight-current-blue { opacity: 1; - visibility: visible; + visibility: inherit; } .reveal .slides section .fragment.highlight-red.visible { color: #ff2c2d @@ -212,90 +224,287 @@ html:-moz-full-screen-ancestor { * CONTROLS *********************************************/ +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(10px);} + 30% {transform: translateX(-5px);} +} + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% {transform: translateY(0);} + 20% {transform: translateY(10px);} + 30% {transform: translateY(-5px);} +} + +$controlArrowSize: 3.6em; +$controlArrowSpacing: 1.4em; +$controlArrowLength: 2.6em; +$controlArrowThickness: 0.5em; +$controlsArrowAngle: 45deg; +$controlsArrowAngleHover: 40deg; +$controlsArrowAngleActive: 36deg; + +@mixin controlsArrowTransform( $angle ) { + &:before { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( $angle ); + } + + &:after { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( -$angle ); + } +} + .reveal .controls { + $spacing: 12px; + display: none; - position: fixed; - width: 110px; - height: 110px; - z-index: 30; - right: 10px; - bottom: 10px; - - -webkit-user-select: none; -} - -.reveal .controls button { - padding: 0; position: absolute; - opacity: 0.05; - width: 0; - height: 0; - background-color: transparent; - border: 12px solid transparent; - transform: scale(.9999); - transition: all 0.2s ease; - -webkit-appearance: none; - -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + top: auto; + bottom: $spacing; + right: $spacing; + left: auto; + z-index: 11; + color: #000; + pointer-events: none; + font-size: 10px; + + button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + transform: scale(.9999); + transition: color 0.2s ease, + opacity 0.2s ease, + transform 0.2s ease; + z-index: 2; // above slides + pointer-events: auto; + font-size: inherit; + + visibility: hidden; + opacity: 0; + + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + } + + .controls-arrow:before, + .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: $controlArrowLength; + height: $controlArrowThickness; + border-radius: $controlArrowThickness/2; + background-color: currentColor; + + transition: all 0.15s ease, background-color 0.8s ease; + transform-origin: floor(($controlArrowThickness/2)*10)/10 50%; + will-change: transform; + } + + .controls-arrow { + position: relative; + width: $controlArrowSize; + height: $controlArrowSize; + + @include controlsArrowTransform( $controlsArrowAngle ); + + &:hover { + @include controlsArrowTransform( $controlsArrowAngleHover ); + } + + &:active { + @include controlsArrowTransform( $controlsArrowAngleActive ); + } + } + + .navigate-left { + right: $controlArrowSize + $controlArrowSpacing*2; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( -10px ); + } + + .navigate-right { + right: 0; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( 10px ); + + .controls-arrow { + transform: rotate( 180deg ); + } + + &.highlight { + animation: bounce-right 2s 50 both ease-out; + } + } + + .navigate-up { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: $controlArrowSpacing*2 + $controlArrowSize; + transform: translateY( -10px ); + + .controls-arrow { + transform: rotate( 90deg ); + } + } + + .navigate-down { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: -$controlArrowSpacing; + padding-bottom: $controlArrowSpacing; + transform: translateY( 10px ); + + .controls-arrow { + transform: rotate( -90deg ); + } + + &.highlight { + animation: bounce-down 2s 50 both ease-out; + } + } + + // Back arrow style: "faded": + // Deemphasize backwards navigation arrows in favor of drawing + // attention to forwards navigation + &[data-controls-back-arrows="faded"] .navigate-left.enabled, + &[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show arrows for backwards navigation + &[data-controls-back-arrows="hidden"] .navigate-left.enabled, + &[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; + } + + // Any control button that can be clicked is "enabled" + .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + transform: none; + } + + // Any control button that leads to showing or hiding + // a fragment + .enabled.fragmented { + opacity: 0.5; + } + + .enabled:hover, + .enabled.fragmented:hover { + opacity: 1; + } } -.reveal .controls .enabled { - opacity: 0.7; - cursor: pointer; +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-up, +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-down { + display: none; } -.reveal .controls .enabled:active { - margin-top: 1px; +// Adjust the layout when there are no vertical slides +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-left, +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: $controlArrowSpacing; + right: 0.5em + $controlArrowSpacing + $controlArrowSize; } - .reveal .controls .navigate-left { - top: 42px; +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-right, +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: $controlArrowSpacing; + right: 0.5em; +} - border-right-width: 22px; - border-right-color: #000; - } - .reveal .controls .navigate-left.fragmented { - opacity: 0.3; +// Adjust the layout when there are no horizontal slides +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: $controlArrowSpacing; + bottom: $controlArrowSpacing + $controlArrowSize; +} +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: $controlArrowSpacing; + bottom: 0.5em; +} + +// Invert arrows based on background color +.reveal.has-dark-background .controls { + color: #fff; +} +.reveal.has-light-background .controls { + color: #000; +} + +// Disable active states on touch devices +.reveal.no-hover .controls .controls-arrow:hover, +.reveal.no-hover .controls .controls-arrow:active { + @include controlsArrowTransform( $controlsArrowAngle ); +} + +// Edge aligned controls layout +@media screen and (min-width: 500px) { + + $spacing: 0.8em; + + .reveal .controls[data-controls-layout="edges"] { + & { + top: 0; + right: 0; + bottom: 0; + left: 0; } - .reveal .controls .navigate-right { - left: 74px; - top: 42px; - - border-left-width: 22px; - border-left-color: #000; - } - .reveal .controls .navigate-right.fragmented { - opacity: 0.3; + .navigate-left, + .navigate-right, + .navigate-up, + .navigate-down { + bottom: auto; + right: auto; } - .reveal .controls .navigate-up { - left: 42px; - - border-bottom-width: 22px; - border-bottom-color: #000; - } - .reveal .controls .navigate-up.fragmented { - opacity: 0.3; + .navigate-left { + top: 50%; + left: $spacing; + margin-top: -$controlArrowSize/2; } - .reveal .controls .navigate-down { - left: 42px; - top: 74px; - - border-top-width: 22px; - border-top-color: #000; - } - .reveal .controls .navigate-down.fragmented { - opacity: 0.3; + .navigate-right { + top: 50%; + right: $spacing; + margin-top: -$controlArrowSize/2; } + .navigate-up { + top: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; + } + + .navigate-down { + bottom: $spacing - $controlArrowSpacing + 0.3em; + left: 50%; + margin-left: -$controlArrowSize/2; + } + } + +} + /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - position: fixed; + position: absolute; display: none; height: 3px; width: 100%; @@ -304,21 +513,22 @@ html:-moz-full-screen-ancestor { z-index: 10; background-color: rgba( 0, 0, 0, 0.2 ); + color: #fff; } .reveal .progress:after { content: ''; display: block; position: absolute; - height: 20px; + height: 10px; width: 100%; - top: -20px; + top: -10px; } .reveal .progress span { display: block; height: 100%; width: 0px; - background-color: #000; + background-color: currentColor; transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } @@ -327,7 +537,7 @@ html:-moz-full-screen-ancestor { *********************************************/ .reveal .slide-number { - position: fixed; + position: absolute; display: block; right: 8px; bottom: 8px; @@ -340,6 +550,10 @@ html:-moz-full-screen-ancestor { padding: 5px; } +.reveal .slide-number a { + color: currentColor; +} + .reveal .slide-number-delimiter { margin: 0 3px; } @@ -353,7 +567,7 @@ html:-moz-full-screen-ancestor { width: 100%; height: 100%; overflow: hidden; - touch-action: none; + touch-action: pinch-zoom; } .reveal .slides { @@ -365,6 +579,7 @@ html:-moz-full-screen-ancestor { bottom: 0; left: 0; margin: auto; + pointer-events: none; overflow: visible; z-index: 1; @@ -374,7 +589,7 @@ html:-moz-full-screen-ancestor { } .reveal .slides>section { - -ms-perspective: 600px; + perspective: 600px; } .reveal .slides>section, @@ -383,9 +598,10 @@ html:-moz-full-screen-ancestor { position: absolute; width: 100%; padding: 20px 0px; + pointer-events: auto; z-index: 10; - transform-style: preserve-3d; + transform-style: flat; transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), @@ -411,6 +627,8 @@ html:-moz-full-screen-ancestor { .reveal .slides>section.stack { padding-top: 0; padding-bottom: 0; + pointer-events: none; + height: 100%; } .reveal .slides>section.present, @@ -420,6 +638,13 @@ html:-moz-full-screen-ancestor { opacity: 1; } +.reveal .slides>section:empty, +.reveal .slides>section>section:empty, +.reveal .slides>section[data-background-interactive], +.reveal .slides>section>section[data-background-interactive] { + pointer-events: none; +} + .reveal.center, .reveal.center .slides, .reveal.center .slides section { @@ -457,6 +682,12 @@ html:-moz-full-screen-ancestor { @content; } } +@mixin transition-stack($style) { + .reveal .slides section[data-transition=#{$style}].stack, + .reveal.#{$style} .slides section.stack { + @content; + } +} @mixin transition-horizontal-past($style) { .reveal .slides>section[data-transition=#{$style}].past, .reveal .slides>section[data-transition~=#{$style}-out].past, @@ -516,6 +747,10 @@ html:-moz-full-screen-ancestor { *********************************************/ @each $stylename in default, convex { + @include transition-stack(#{$stylename}) { + transform-style: preserve-3d; + } + @include transition-horizontal-past(#{$stylename}) { transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } @@ -534,6 +769,10 @@ html:-moz-full-screen-ancestor { * CONCAVE TRANSITION *********************************************/ +@include transition-stack(concave) { + transform-style: preserve-3d; +} + @include transition-horizontal-past(concave) { transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } @@ -564,15 +803,19 @@ html:-moz-full-screen-ancestor { transform: scale(0.2); } @include transition-vertical-past(zoom) { - transform: translate(0, -150%); + transform: scale(16); } @include transition-vertical-future(zoom) { - transform: translate(0, 150%); + transform: scale(0.2); } /********************************************* * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.cube .slides { @@ -584,6 +827,7 @@ html:-moz-full-screen-ancestor { min-height: 700px; backface-visibility: hidden; box-sizing: border-box; + transform-style: preserve-3d; } .reveal.center.cube .slides section { min-height: 0; @@ -644,6 +888,10 @@ html:-moz-full-screen-ancestor { /********************************************* * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. *********************************************/ .reveal.page .slides { @@ -655,6 +903,7 @@ html:-moz-full-screen-ancestor { padding: 30px; min-height: 700px; box-sizing: border-box; + transform-style: preserve-3d; } .reveal.page .slides section.past { z-index: 12; @@ -757,6 +1006,25 @@ html:-moz-full-screen-ancestor { z-index: 100; transition: all 1s ease; } + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; + + &:hover { + color: #fff; + border-color: #fff; + } +} + .reveal.paused .pause-overlay { visibility: visible; opacity: 1; @@ -771,37 +1039,38 @@ html:-moz-full-screen-ancestor { overflow-y: auto; } +.no-transforms .reveal { + overflow: visible; +} + .no-transforms .reveal .slides { position: relative; width: 80%; - height: auto !important; + max-width: 1280px; + height: auto; top: 0; - left: 50%; - margin: 0; + margin: 0 auto; text-align: center; } .no-transforms .reveal .controls, .no-transforms .reveal .progress { - display: none !important; + display: none; } .no-transforms .reveal .slides section { - display: block !important; - opacity: 1 !important; - position: relative !important; + display: block; + opacity: 1; + position: relative; height: auto; min-height: 0; top: 0; - left: -50%; + left: 0; + margin: 10vh 0; margin: 70px 0; transform: none; } -.no-transforms .reveal .slides section section { - left: 0; -} - .reveal .no-transition, .reveal .no-transition * { transition: none !important; @@ -827,13 +1096,21 @@ html:-moz-full-screen-ancestor { height: 100%; opacity: 0; visibility: hidden; + overflow: hidden; background-color: rgba( 0, 0, 0, 0 ); + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + background-position: 50% 50%; background-repeat: no-repeat; background-size: cover; - - transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } .reveal .slide-background.stack { @@ -843,6 +1120,7 @@ html:-moz-full-screen-ancestor { .reveal .slide-background.present { opacity: 1; visibility: visible; + z-index: 2; } .print-pdf .reveal .slide-background { @@ -859,7 +1137,11 @@ html:-moz-full-screen-ancestor { max-height: none; top: 0; left: 0; + object-fit: cover; } + .reveal .slide-background[data-background-size="contain"] video { + object-fit: contain; + } /* Immediate transition style */ .reveal[data-background-transition=none]>.backgrounds .slide-background, @@ -989,8 +1271,15 @@ html:-moz-full-screen-ancestor { perspective-origin: 50% 50%; perspective: 700px; + .slides { + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + .slides section { - height: 700px; + height: 100%; + top: 0 !important; opacity: 1 !important; overflow: hidden; visibility: visible !important; @@ -1020,6 +1309,10 @@ html:-moz-full-screen-ancestor { .backgrounds { perspective: inherit; + + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; } .backgrounds .slide-background { @@ -1030,6 +1323,10 @@ html:-moz-full-screen-ancestor { outline: 10px solid rgba(150,150,150,0.1); outline-offset: 10px; } + + .backgrounds .slide-background.stack { + overflow: visible; + } } // Disable transitions transitions while we're activating @@ -1044,10 +1341,6 @@ html:-moz-full-screen-ancestor { transition: none; } -.reveal.overview-animated .slides { - transition: transform 0.4s ease; -} - /********************************************* * RTL SUPPORT @@ -1096,10 +1389,10 @@ html:-moz-full-screen-ancestor { /********************************************* - * LINK PREVIEW OVERLAY + * OVERLAY FOR LINK PREVIEWS AND HELP *********************************************/ -.reveal .overlay { +.reveal > .overlay { position: absolute; top: 0; left: 0; @@ -1111,12 +1404,12 @@ html:-moz-full-screen-ancestor { visibility: hidden; transition: all 0.3s ease; } - .reveal .overlay.visible { + .reveal > .overlay.visible { opacity: 1; visibility: visible; } - .reveal .overlay .spinner { + .reveal > .overlay .spinner { position: absolute; display: block; top: 50%; @@ -1132,7 +1425,7 @@ html:-moz-full-screen-ancestor { transition: all 0.3s ease; } - .reveal .overlay header { + .reveal > .overlay header { position: absolute; left: 0; top: 0; @@ -1141,20 +1434,21 @@ html:-moz-full-screen-ancestor { z-index: 2; border-bottom: 1px solid #222; } - .reveal .overlay header a { + .reveal > .overlay header a { display: inline-block; width: 40px; height: 40px; + line-height: 36px; padding: 0 10px; float: right; opacity: 0.6; box-sizing: border-box; } - .reveal .overlay header a:hover { + .reveal > .overlay header a:hover { opacity: 1; } - .reveal .overlay header a .icon { + .reveal > .overlay header a .icon { display: inline-block; width: 20px; height: 20px; @@ -1163,22 +1457,23 @@ html:-moz-full-screen-ancestor { background-size: 100%; background-repeat: no-repeat; } - .reveal .overlay header a.close .icon { + .reveal > .overlay header a.close .icon { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); } - .reveal .overlay header a.external .icon { + .reveal > .overlay header a.external .icon { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); } - .reveal .overlay .viewport { + .reveal > .overlay .viewport { position: absolute; + display: flex; top: 40px; right: 0; bottom: 0; left: 0; } - .reveal .overlay.overlay-preview .viewport iframe { + .reveal > .overlay.overlay-preview .viewport iframe { width: 100%; height: 100%; max-width: 100%; @@ -1190,66 +1485,83 @@ html:-moz-full-screen-ancestor { transition: all 0.3s ease; } - .reveal .overlay.overlay-preview.loaded .viewport iframe { + .reveal > .overlay.overlay-preview.loaded .viewport iframe { opacity: 1; visibility: visible; } - .reveal .overlay.overlay-preview.loaded .spinner { + .reveal > .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; + } + .reveal > .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; + } + .reveal > .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; + } + + .reveal > .overlay.overlay-preview.loaded .spinner { opacity: 0; visibility: hidden; transform: scale(0.2); } - .reveal .overlay.overlay-help .viewport { + .reveal > .overlay.overlay-help .viewport { overflow: auto; color: #fff; } - .reveal .overlay.overlay-help .viewport .viewport-inner { + .reveal > .overlay.overlay-help .viewport .viewport-inner { width: 600px; - margin: 0 auto; - padding: 60px; + margin: auto; + padding: 20px 20px 80px 20px; text-align: center; letter-spacing: normal; } - .reveal .overlay.overlay-help .viewport .viewport-inner .title { + .reveal > .overlay.overlay-help .viewport .viewport-inner .title { font-size: 20px; } - .reveal .overlay.overlay-help .viewport .viewport-inner table { + .reveal > .overlay.overlay-help .viewport .viewport-inner table { border: 1px solid #fff; border-collapse: collapse; - font-size: 14px; + font-size: 16px; } - .reveal .overlay.overlay-help .viewport .viewport-inner table th, - .reveal .overlay.overlay-help .viewport .viewport-inner table td { + .reveal > .overlay.overlay-help .viewport .viewport-inner table th, + .reveal > .overlay.overlay-help .viewport .viewport-inner table td { width: 200px; - padding: 10px; + padding: 14px; border: 1px solid #fff; vertical-align: middle; } - .reveal .overlay.overlay-help .viewport .viewport-inner table th { + .reveal > .overlay.overlay-help .viewport .viewport-inner table th { padding-top: 20px; padding-bottom: 20px; } - /********************************************* * PLAYBACK COMPONENT *********************************************/ .reveal .playback { - position: fixed; + position: absolute; left: 15px; bottom: 20px; z-index: 30; cursor: pointer; transition: all 400ms ease; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); } .reveal.overview .playback { @@ -1258,6 +1570,40 @@ html:-moz-full-screen-ancestor { } +/********************************************* + * CODE HIGHLGIHTING + *********************************************/ + +.reveal .hljs table { + margin: initial; +} + +.reveal .hljs-ln-code, +.reveal .hljs-ln-numbers { + padding: 0; + border: 0; +} + +.reveal .hljs-ln-numbers { + opacity: 0.6; + padding-right: 0.75em; + text-align: right; + vertical-align: top; +} + +.reveal .hljs.has-highlights tr:not(.highlight-line) { + opacity: 0.4; +} + +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; +} + + /********************************************* * ROLLING LINKS *********************************************/ @@ -1308,6 +1654,8 @@ html:-moz-full-screen-ancestor { * SPEAKER NOTES *********************************************/ +$notesWidthPercent: 25%; + // Hide on-page notes .reveal aside.notes { display: none; @@ -1318,37 +1666,89 @@ html:-moz-full-screen-ancestor { .reveal .speaker-notes { display: none; position: absolute; - width: 70%; - max-height: 15%; - left: 15%; - bottom: 26px; - padding: 10px; + width: $notesWidthPercent / (1-$notesWidthPercent/100) * 1%; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; z-index: 1; font-size: 18px; line-height: 1.4; - color: #fff; - background-color: rgba(0,0,0,0.5); + border: 1px solid rgba( 0, 0, 0, 0.05 ); + color: #222; + background-color: #f5f5f5; overflow: auto; box-sizing: border-box; text-align: left; font-family: Helvetica, sans-serif; -webkit-overflow-scrolling: touch; + + .notes-placeholder { + color: #ccc; + font-style: italic; + } + + &:focus { + outline: none; + } + + &:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; + } } -.reveal .speaker-notes.visible:not(:empty) { + +.reveal.show-notes { + max-width: 100% - $notesWidthPercent; + overflow: visible; +} + +.reveal.show-notes .speaker-notes { display: block; } -@media screen and (max-width: 1024px) { +@media screen and (min-width: 1600px) { .reveal .speaker-notes { - font-size: 14px; + font-size: 20px; + } +} + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + max-height: 70vh; + overflow: visible; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: (30/0.7)*1%; + height: 30vh; + border: 0; } } @media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + max-height: 60vh; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + height: (40/0.6)*1%; + height: 40vh; + } + .reveal .speaker-notes { - width: 90%; - left: 5%; + font-size: 14px; } } @@ -1375,5 +1775,3 @@ html:-moz-full-screen-ancestor { .zoomed .reveal .roll span:after { visibility: hidden; } - - diff --git a/reveal.js/css/theme/README.md b/reveal.js/css/theme/README.md index 5a6c8fa..5ebe72a 100644 --- a/reveal.js/css/theme/README.md +++ b/reveal.js/css/theme/README.md @@ -1,10 +1,10 @@ ## Dependencies -Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceding: https://github.com/hakimel/reveal.js#full-setup +Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceeding: https://github.com/hakimel/reveal.js#full-setup ## Creating a Theme -To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js)) when you run `grunt css-themes`. +To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/gruntfile.js)) when you run `npm run build -- css-themes`. Each theme file does four things in the following order: diff --git a/reveal.js/css/theme/beige.css b/reveal.js/css/theme/beige.css index a497f04..615dd6d 100644 --- a/reveal.js/css/theme/beige.css +++ b/reveal.js/css/theme/beige.css @@ -1,7 +1,7 @@ /** * Beige theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @import url(../../lib/font/league-gothic/league-gothic.css); @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); @@ -20,7 +20,7 @@ body { .reveal { font-family: "Lato", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #333; } @@ -29,8 +29,13 @@ body { background: rgba(79, 64, 28, 0.99); text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -121,10 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -152,10 +153,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -186,14 +188,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -210,9 +215,9 @@ body { .reveal a { color: #8b743d; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #c0a86e; @@ -237,9 +242,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -249,42 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #8b743d; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #8b743d; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #8b743d; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #8b743d; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #c0a86e; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #c0a86e; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #c0a86e; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #c0a86e; } +.reveal .controls { + color: #8b743d; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #8b743d; } .reveal .progress span { - background: #8b743d; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #f7f3de; } } diff --git a/reveal.js/css/theme/black.css b/reveal.js/css/theme/black.css index 8857385..7dd88c2 100644 --- a/reveal.js/css/theme/black.css +++ b/reveal.js/css/theme/black.css @@ -1,7 +1,7 @@ /** * Black theme for reveal.js. This is the opposite of the 'white' theme. * - * By Hakim El Hattab, https://hakim.se + * By Hakim El Hattab, http://hakim.se */ @import url(../../lib/font/source-sans-pro/source-sans-pro.css); section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { @@ -11,12 +11,12 @@ section.has-light-background, section.has-light-background h1, section.has-light * GLOBAL STYLES *********************************************/ body { - background: #222; - background-color: #222; } + background: #191919; + background-color: #191919; } .reveal { font-family: "Source Sans Pro", Helvetica, sans-serif; - font-size: 38px; + font-size: 42px; font-weight: normal; color: #fff; } @@ -25,8 +25,13 @@ body { background: #bee4fd; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -117,10 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -148,10 +149,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -182,14 +184,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -206,9 +211,9 @@ body { .reveal a { color: #42affa; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #8dcffc; @@ -233,9 +238,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -245,42 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #42affa; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #42affa; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #42affa; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #42affa; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #8dcffc; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #8dcffc; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #8dcffc; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #8dcffc; } +.reveal .controls { + color: #42affa; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #42affa; } .reveal .progress span { - background: #42affa; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #191919; } } diff --git a/reveal.js/css/theme/blood.css b/reveal.js/css/theme/blood.css index 79ac20e..5cbd488 100644 --- a/reveal.js/css/theme/blood.css +++ b/reveal.js/css/theme/blood.css @@ -1,6 +1,6 @@ /** * Blood theme for reveal.js - * Author: Walther https://github.com/Walther + * Author: Walther http://github.com/Walther * * Designed to be used with highlight.js theme * "monokai_sublime.css" available from @@ -19,7 +19,7 @@ body { .reveal { font-family: Ubuntu, "sans-serif"; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #eee; } @@ -28,8 +28,13 @@ body { background: #a23; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #a23; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -120,10 +125,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -151,10 +152,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -185,14 +187,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -209,9 +214,9 @@ body { .reveal a { color: #a23; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #dd5566; @@ -236,9 +241,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -248,46 +253,28 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #a23; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #a23; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #a23; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #a23; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #dd5566; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #dd5566; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #dd5566; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #dd5566; } +.reveal .controls { + color: #a23; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #a23; } .reveal .progress span { - background: #a23; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #222; } } + .reveal p { font-weight: 300; text-shadow: 1px 1px #222; } diff --git a/reveal.js/css/theme/league.css b/reveal.js/css/theme/league.css index 54423d7..f8fba4d 100644 --- a/reveal.js/css/theme/league.css +++ b/reveal.js/css/theme/league.css @@ -3,7 +3,7 @@ * * This was the default theme pre-3.0.0. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @import url(../../lib/font/league-gothic/league-gothic.css); @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); @@ -22,7 +22,7 @@ body { .reveal { font-family: "Lato", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #eee; } @@ -31,8 +31,13 @@ body { background: #FF5E99; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -123,10 +128,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -154,10 +155,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -188,14 +190,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -212,9 +217,9 @@ body { .reveal a { color: #13DAEC; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #71e9f4; @@ -239,9 +244,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -251,42 +256,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #13DAEC; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #13DAEC; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #13DAEC; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #13DAEC; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #71e9f4; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #71e9f4; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #71e9f4; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #71e9f4; } +.reveal .controls { + color: #13DAEC; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #13DAEC; } .reveal .progress span { - background: #13DAEC; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #2b2b2b; } } diff --git a/reveal.js/css/theme/moon.css b/reveal.js/css/theme/moon.css index b119576..d18f526 100644 --- a/reveal.js/css/theme/moon.css +++ b/reveal.js/css/theme/moon.css @@ -20,7 +20,7 @@ body { .reveal { font-family: "Lato", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #93a1a1; } @@ -29,8 +29,13 @@ body { background: #d33682; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -121,10 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -152,10 +153,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -186,14 +188,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -210,9 +215,9 @@ body { .reveal a { color: #268bd2; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #78b9e6; @@ -237,9 +242,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -249,42 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #268bd2; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #268bd2; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #268bd2; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #268bd2; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #78b9e6; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #78b9e6; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #78b9e6; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #78b9e6; } +.reveal .controls { + color: #268bd2; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } .reveal .progress span { - background: #268bd2; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #002b36; } } diff --git a/reveal.js/css/theme/night.css b/reveal.js/css/theme/night.css index 8f337ec..f5ccb52 100644 --- a/reveal.js/css/theme/night.css +++ b/reveal.js/css/theme/night.css @@ -1,7 +1,7 @@ /** * Black theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @import url(https://fonts.googleapis.com/css?family=Montserrat:700); @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); @@ -14,7 +14,7 @@ body { .reveal { font-family: "Open Sans", sans-serif; - font-size: 30px; + font-size: 40px; font-weight: normal; color: #eee; } @@ -23,8 +23,13 @@ body { background: #e7ad52; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -115,10 +120,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -146,10 +147,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -180,14 +182,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -204,9 +209,9 @@ body { .reveal a { color: #e7ad52; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #f3d7ac; @@ -231,9 +236,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -243,42 +248,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #e7ad52; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #e7ad52; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #e7ad52; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #e7ad52; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #f3d7ac; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #f3d7ac; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #f3d7ac; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #f3d7ac; } +.reveal .controls { + color: #e7ad52; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #e7ad52; } .reveal .progress span { - background: #e7ad52; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #111; } } diff --git a/reveal.js/css/theme/serif.css b/reveal.js/css/theme/serif.css index 4936cfc..6514a6f 100644 --- a/reveal.js/css/theme/serif.css +++ b/reveal.js/css/theme/serif.css @@ -2,7 +2,7 @@ * A simple theme for reveal.js presentations, similar * to the default theme. The accent color is brown. * - * This theme is Copyright (C) 2012-2013 Owen Versteeg, https://owenversteeg.com - it is MIT licensed. + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. */ .reveal a { line-height: 1.3em; } @@ -16,7 +16,7 @@ body { .reveal { font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #000; } @@ -25,8 +25,13 @@ body { background: #26351C; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -117,10 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -148,10 +149,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -182,14 +184,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -206,9 +211,9 @@ body { .reveal a { color: #51483D; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #8b7c69; @@ -233,9 +238,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -245,42 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #51483D; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #51483D; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #51483D; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #51483D; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #8b7c69; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #8b7c69; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #8b7c69; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #8b7c69; } +.reveal .controls { + color: #51483D; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #51483D; } .reveal .progress span { - background: #51483D; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #F0F1EB; } } diff --git a/reveal.js/css/theme/simple.css b/reveal.js/css/theme/simple.css index eac5325..a7a29a6 100644 --- a/reveal.js/css/theme/simple.css +++ b/reveal.js/css/theme/simple.css @@ -3,10 +3,13 @@ * to the default theme. The accent color is darkblue. * * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. - * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + /********************************************* * GLOBAL STYLES *********************************************/ @@ -16,7 +19,7 @@ body { .reveal { font-family: "Lato", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #000; } @@ -25,8 +28,13 @@ body { background: rgba(0, 0, 0, 0.99); text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -117,10 +125,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -148,10 +152,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -182,14 +187,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -204,14 +212,14 @@ body { * LINKS *********************************************/ .reveal a { - color: #CC0000; + color: #00008B; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { - color: #990000; + color: #0000f1; text-shadow: none; border: none; } @@ -225,61 +233,44 @@ body { .reveal section img { margin: 15px 0px; background: rgba(255, 255, 255, 0.12); - } + border: 4px solid #000; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } .reveal section img.plain { border: 0; box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); - border-color: #CC0000; + border-color: #00008B; box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #CC0000; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #CC0000; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #CC0000; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #CC0000; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #990000; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #990000; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #990000; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #990000; } +.reveal .controls { + color: #00008B; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #00008B; } .reveal .progress span { - background: #CC0000; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/reveal.js/css/theme/sky.css b/reveal.js/css/theme/sky.css index 454828c..d8734c9 100644 --- a/reveal.js/css/theme/sky.css +++ b/reveal.js/css/theme/sky.css @@ -1,7 +1,7 @@ /** * Sky theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); @@ -23,7 +23,7 @@ body { .reveal { font-family: "Open Sans", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #333; } @@ -32,8 +32,13 @@ body { background: #134674; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #134674; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -124,10 +129,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -155,10 +156,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -189,14 +191,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -213,9 +218,9 @@ body { .reveal a { color: #3b759e; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #74a7cb; @@ -240,9 +245,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -252,42 +257,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #3b759e; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #3b759e; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #3b759e; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #3b759e; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #74a7cb; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #74a7cb; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #74a7cb; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #74a7cb; } +.reveal .controls { + color: #3b759e; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #3b759e; } .reveal .progress span { - background: #3b759e; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #f7fbfc; } } diff --git a/reveal.js/css/theme/solarized.css b/reveal.js/css/theme/solarized.css index bf2f651..f1a2b9e 100644 --- a/reveal.js/css/theme/solarized.css +++ b/reveal.js/css/theme/solarized.css @@ -20,7 +20,7 @@ body { .reveal { font-family: "Lato", sans-serif; - font-size: 36px; + font-size: 40px; font-weight: normal; color: #657b83; } @@ -29,8 +29,13 @@ body { background: #d33682; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -121,10 +126,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -152,10 +153,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -186,14 +188,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -210,9 +215,9 @@ body { .reveal a { color: #268bd2; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #78b9e6; @@ -237,9 +242,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -249,42 +254,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #268bd2; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #268bd2; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #268bd2; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #268bd2; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #78b9e6; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #78b9e6; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #78b9e6; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #78b9e6; } +.reveal .controls { + color: #268bd2; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } .reveal .progress span { - background: #268bd2; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fdf6e3; } } diff --git a/reveal.js/css/theme/source/beige.scss b/reveal.js/css/theme/source/beige.scss index 0dcf788..5564f53 100644 --- a/reveal.js/css/theme/source/beige.scss +++ b/reveal.js/css/theme/source/beige.scss @@ -1,7 +1,7 @@ /** * Beige theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ diff --git a/reveal.js/css/theme/source/black.scss b/reveal.js/css/theme/source/black.scss index 26b54a9..4720c8a 100644 --- a/reveal.js/css/theme/source/black.scss +++ b/reveal.js/css/theme/source/black.scss @@ -1,7 +1,7 @@ /** * Black theme for reveal.js. This is the opposite of the 'white' theme. * - * By Hakim El Hattab, https://hakim.se + * By Hakim El Hattab, http://hakim.se */ @@ -16,12 +16,12 @@ // Override theme settings (see ../template/settings.scss) -$backgroundColor: #222; +$backgroundColor: #191919; $mainColor: #fff; $headingColor: #fff; -$mainFontSize: 38px; +$mainFontSize: 42px; $mainFont: 'Source Sans Pro', Helvetica, sans-serif; $headingFont: 'Source Sans Pro', Helvetica, sans-serif; $headingTextShadow: none; diff --git a/reveal.js/css/theme/source/blood.scss b/reveal.js/css/theme/source/blood.scss index 8fa237c..4533fc0 100644 --- a/reveal.js/css/theme/source/blood.scss +++ b/reveal.js/css/theme/source/blood.scss @@ -1,6 +1,6 @@ /** * Blood theme for reveal.js - * Author: Walther https://github.com/Walther + * Author: Walther http://github.com/Walther * * Designed to be used with highlight.js theme * "monokai_sublime.css" available from @@ -28,7 +28,6 @@ $backgroundColor: $coal; // Main text $mainFont: Ubuntu, 'sans-serif'; -$mainFontSize: 36px; $mainColor: #eee; // Headings diff --git a/reveal.js/css/theme/source/league.scss b/reveal.js/css/theme/source/league.scss index 830c659..46ea04a 100644 --- a/reveal.js/css/theme/source/league.scss +++ b/reveal.js/css/theme/source/league.scss @@ -3,7 +3,7 @@ * * This was the default theme pre-3.0.0. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ diff --git a/reveal.js/css/theme/source/night.scss b/reveal.js/css/theme/source/night.scss index b283a8e..d49a282 100644 --- a/reveal.js/css/theme/source/night.scss +++ b/reveal.js/css/theme/source/night.scss @@ -1,7 +1,7 @@ /** * Black theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @@ -27,7 +27,6 @@ $headingTextShadow: none; $headingLetterSpacing: -0.03em; $headingTextTransform: none; $selectionBackgroundColor: #e7ad52; -$mainFontSize: 30px; // Theme template ------------------------------ diff --git a/reveal.js/css/theme/source/serif.scss b/reveal.js/css/theme/source/serif.scss index 32f6d19..ec3fcb3 100644 --- a/reveal.js/css/theme/source/serif.scss +++ b/reveal.js/css/theme/source/serif.scss @@ -2,7 +2,7 @@ * A simple theme for reveal.js presentations, similar * to the default theme. The accent color is brown. * - * This theme is Copyright (C) 2012-2013 Owen Versteeg, https://owenversteeg.com - it is MIT licensed. + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. */ diff --git a/reveal.js/css/theme/source/simple.scss b/reveal.js/css/theme/source/simple.scss index c4dc491..394c9cd 100644 --- a/reveal.js/css/theme/source/simple.scss +++ b/reveal.js/css/theme/source/simple.scss @@ -3,7 +3,7 @@ * to the default theme. The accent color is darkblue. * * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. - * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ @@ -31,6 +31,11 @@ $linkColor: #00008B; $linkColorHover: lighten( $linkColor, 20% ); $selectionBackgroundColor: rgba(0, 0, 0, 0.99); +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} // Theme template ------------------------------ diff --git a/reveal.js/css/theme/source/sky.scss b/reveal.js/css/theme/source/sky.scss index 32707ad..3fee67c 100644 --- a/reveal.js/css/theme/source/sky.scss +++ b/reveal.js/css/theme/source/sky.scss @@ -1,7 +1,7 @@ /** * Sky theme for reveal.js. * - * Copyright (C) 2011-2012 Hakim El Hattab, https://hakim.se + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se */ diff --git a/reveal.js/css/theme/source/white.scss b/reveal.js/css/theme/source/white.scss index ebc1934..7f06ffd 100644 --- a/reveal.js/css/theme/source/white.scss +++ b/reveal.js/css/theme/source/white.scss @@ -1,7 +1,7 @@ /** * White theme for reveal.js. This is the opposite of the 'black' theme. * - * By Hakim El Hattab, https://hakim.se + * By Hakim El Hattab, http://hakim.se */ @@ -21,7 +21,7 @@ $backgroundColor: #fff; $mainColor: #222; $headingColor: #222; -$mainFontSize: 38px; +$mainFontSize: 42px; $mainFont: 'Source Sans Pro', Helvetica, sans-serif; $headingFont: 'Source Sans Pro', Helvetica, sans-serif; $headingTextShadow: none; diff --git a/reveal.js/css/theme/template/settings.scss b/reveal.js/css/theme/template/settings.scss index ffaac23..5a917f8 100644 --- a/reveal.js/css/theme/template/settings.scss +++ b/reveal.js/css/theme/template/settings.scss @@ -6,7 +6,7 @@ $backgroundColor: #2b2b2b; // Primary/body text $mainFont: 'Lato', sans-serif; -$mainFontSize: 36px; +$mainFontSize: 40px; $mainColor: #eee; // Vertical spacing between blocks of text @@ -28,6 +28,8 @@ $heading2Size: 2.11em; $heading3Size: 1.55em; $heading4Size: 1.00em; +$codeFont: monospace; + // Links and actions $linkColor: #13DAEC; $linkColorHover: lighten( $linkColor, 20% ); @@ -40,4 +42,4 @@ $selectionColor: #fff; // to return a background image or gradient @mixin bodyBackground() { background: $backgroundColor; -} \ No newline at end of file +} diff --git a/reveal.js/css/theme/template/theme.scss b/reveal.js/css/theme/template/theme.scss index 9bb416a..9ccfaf5 100644 --- a/reveal.js/css/theme/template/theme.scss +++ b/reveal.js/css/theme/template/theme.scss @@ -22,8 +22,14 @@ body { text-shadow: none; } -.reveal .slides>section, -.reveal .slides>section>section { +::-moz-selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +.reveal .slides section, +.reveal .slides section>section { line-height: 1.3; font-weight: inherit; } @@ -128,11 +134,6 @@ body { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; -} - .reveal blockquote { display: block; position: relative; @@ -161,15 +162,17 @@ body { text-align: left; font-size: 0.55em; - font-family: monospace; + font-family: $codeFont; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0,0,0,0.3); + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + .reveal code { - font-family: monospace; + font-family: $codeFont; + text-transform: none; } .reveal pre code { @@ -207,15 +210,18 @@ body { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { vertical-align: super; + font-size: smaller; } .reveal sub { vertical-align: sub; + font-size: smaller; } .reveal small { @@ -290,40 +296,8 @@ body { * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: $linkColor; -} - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: $linkColor; -} - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: $linkColor; -} - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: $linkColor; -} - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: $linkColorHover; -} - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: $linkColorHover; -} - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: $linkColorHover; -} - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: $linkColorHover; +.reveal .controls { + color: $linkColor; } @@ -333,13 +307,19 @@ body { .reveal .progress { background: rgba(0,0,0,0.2); + color: $linkColor; } .reveal .progress span { - background: $linkColor; - -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); - transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } - +/********************************************* + * PRINT BACKGROUND + *********************************************/ + @media print { + .backgrounds { + background-color: $backgroundColor; + } +} diff --git a/reveal.js/css/theme/white.css b/reveal.js/css/theme/white.css index 8d65b74..43ef2c7 100644 --- a/reveal.js/css/theme/white.css +++ b/reveal.js/css/theme/white.css @@ -1,7 +1,7 @@ /** * White theme for reveal.js. This is the opposite of the 'black' theme. * - * By Hakim El Hattab, https://hakim.se + * By Hakim El Hattab, http://hakim.se */ @import url(../../lib/font/source-sans-pro/source-sans-pro.css); section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { @@ -16,7 +16,7 @@ body { .reveal { font-family: "Source Sans Pro", Helvetica, sans-serif; - font-size: 38px; + font-size: 42px; font-weight: normal; color: #222; } @@ -25,8 +25,13 @@ body { background: #98bdef; text-shadow: none; } -.reveal .slides > section, -.reveal .slides > section > section { +::-moz-selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { line-height: 1.3; font-weight: inherit; } @@ -117,10 +122,6 @@ body { .reveal dd { margin-left: 40px; } -.reveal q, -.reveal blockquote { - quotes: none; } - .reveal blockquote { display: block; position: relative; @@ -148,10 +149,11 @@ body { font-family: monospace; line-height: 1.2em; word-wrap: break-word; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } .reveal code { - font-family: monospace; } + font-family: monospace; + text-transform: none; } .reveal pre code { display: block; @@ -182,14 +184,17 @@ body { .reveal table td[align="right"] { text-align: right; } -.reveal table tr:last-child td { +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { border-bottom: none; } .reveal sup { - vertical-align: super; } + vertical-align: super; + font-size: smaller; } .reveal sub { - vertical-align: sub; } + vertical-align: sub; + font-size: smaller; } .reveal small { display: inline-block; @@ -206,9 +211,9 @@ body { .reveal a { color: #2a76dd; text-decoration: none; - -webkit-transition: color 0.15s ease; - -moz-transition: color 0.15s ease; - transition: color 0.15s ease; } + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } .reveal a:hover { color: #6ca0e8; @@ -233,9 +238,9 @@ body { box-shadow: none; } .reveal a img { - -webkit-transition: all 0.15s linear; - -moz-transition: all 0.15s linear; - transition: all 0.15s linear; } + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } .reveal a:hover img { background: rgba(255, 255, 255, 0.2); @@ -245,42 +250,24 @@ body { /********************************************* * NAVIGATION CONTROLS *********************************************/ -.reveal .controls .navigate-left, -.reveal .controls .navigate-left.enabled { - border-right-color: #2a76dd; } - -.reveal .controls .navigate-right, -.reveal .controls .navigate-right.enabled { - border-left-color: #2a76dd; } - -.reveal .controls .navigate-up, -.reveal .controls .navigate-up.enabled { - border-bottom-color: #2a76dd; } - -.reveal .controls .navigate-down, -.reveal .controls .navigate-down.enabled { - border-top-color: #2a76dd; } - -.reveal .controls .navigate-left.enabled:hover { - border-right-color: #6ca0e8; } - -.reveal .controls .navigate-right.enabled:hover { - border-left-color: #6ca0e8; } - -.reveal .controls .navigate-up.enabled:hover { - border-bottom-color: #6ca0e8; } - -.reveal .controls .navigate-down.enabled:hover { - border-top-color: #6ca0e8; } +.reveal .controls { + color: #2a76dd; } /********************************************* * PROGRESS BAR *********************************************/ .reveal .progress { - background: rgba(0, 0, 0, 0.2); } + background: rgba(0, 0, 0, 0.2); + color: #2a76dd; } .reveal .progress span { - background: #2a76dd; -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/reveal.js/demo.html b/reveal.js/demo.html new file mode 100644 index 0000000..cf05e88 --- /dev/null +++ b/reveal.js/demo.html @@ -0,0 +1,425 @@ + + + + + + + reveal.js – The HTML Presentation Framework + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+

Reveal.js

+

The HTML Presentation Framework

+

+ Created by Hakim El Hattab and contributors +

+
+ +
+

Hello There

+

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

+
+ + +
+
+

Vertical Slides

+

Slides can be nested inside of each other.

+

Use the Space key to navigate through all slides.

+
+ + Down arrow + +
+
+

Basement Level 1

+

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

+
+
+

Basement Level 2

+

That's it, time to go back up.

+
+ + Up arrow + +
+
+ +
+

Slides

+

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

+
+ +
+

Point of View

+

+ Press ESC to enter the slide overview. +

+

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

+

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

+
+ +
+

Touch Optimized

+

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

+
+ +
+ +
+ +
+
+

Fragments

+

Hit the next arrow...

+

... to step through ...

+

... a fragmented slide.

+ + +
+
+

Fragment Styles

+

There's different types of fragments, like:

+

grow

+

shrink

+

fade-out

+

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

+

fade-in-then-out

+

fade-in-then-semi-out

+

Highlight red blue green

+
+
+ +
+

Transition Styles

+

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

+
+ +
+

Themes

+

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

+
+ +
+
+

Slide Backgrounds

+

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

+ + Down arrow + +
+
+

Image Backgrounds

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

Tiled Backgrounds

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

Video Backgrounds

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

... and GIFs!

+
+
+ +
+

Background Transitions

+

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

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

Background Transitions

+

+ You can override background transitions per-slide. +

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

Iframe Backgrounds

+

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

+
+
+ +
+

Pretty Code

+

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

Code syntax highlighting courtesy of highlight.js.

+
+ +
+

Marvelous List

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

Fantastic Ordered List

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

Tabular Tables

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

Clever Quotes

+

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

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

Intergalactic Interconnections

+

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

+
+ +
+

Speaker View

+

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

+

Press the S key to try it out.

+ + +
+ +
+

Export to PDF

+

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

+ +
+ +
+

Global State

+

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

+
+ +
+

State Events

+

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

+

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

Take a Moment

+

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

+
+ +
+

Much more

+ +
+ +
+

THE END

+

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

+
+ +
+ +
+ + + + + + + diff --git a/reveal.js/index.html b/reveal.js/index.html index 0589843..571d152 100644 --- a/reveal.js/index.html +++ b/reveal.js/index.html @@ -1,24 +1,17 @@ - - + + - reveal.js – The HTML Presentation Framework - - - - - - - - + reveal.js + - + - - + + - - - -
- -
-
-

Reveal.js

-

The HTML Presentation Framework

-

- Created by Hakim El Hattab / @hakimel -

-
- -
-

Hello There

-

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

-
- - -
-
-

Vertical Slides

-

Slides can be nested inside of each other.

-

Use the Space key to navigate through all slides.

-
- - Down arrow - -
-
-

Basement Level 1

-

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

-
-
-

Basement Level 2

-

That's it, time to go back up.

-
- - Up arrow - -
-
- -
-

Slides

-

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

-
- -
-

Point of View

-

- Press ESC to enter the slide overview. -

-

- Hold down alt and click on any element to zoom in on it using zoom.js. Alt + click anywhere to zoom back out. -

-
- -
-

Touch Optimized

-

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

-
- -
- -
- -
-
-

Fragments

-

Hit the next arrow...

-

... to step through ...

-

... a fragmented slide.

- - -
-
-

Fragment Styles

-

There's different types of fragments, like:

-

grow

-

shrink

-

fade-out

-

current-visible

-

highlight-red

-

highlight-blue

-
-
- -
-

Transition Styles

-

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

-
- -
-

Themes

-

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

-
- -
-
-

Slide Backgrounds

-

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

- - Down arrow - -
-
-

Image Backgrounds

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

Tiled Backgrounds

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

Video Backgrounds

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

... and GIFs!

-
-
- -
-

Background Transitions

-

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

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

Background Transitions

-

- You can override background transitions per-slide. -

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

Pretty Code

-

-function linkify( selector ) {
-  if( supports3DTransforms ) {
-
-    var nodes = document.querySelectorAll( selector );
-
-    for( var i = 0, len = nodes.length; i < len; i++ ) {
-      var node = nodes[i];
-
-      if( !node.className ) {
-        node.className += ' roll';
-      }
-    }
-  }
-}
-					
-

Code syntax highlighting courtesy of highlight.js.

-
- -
-

Marvelous List

-
    -
  • No order here
  • -
  • Or here
  • -
  • Or here
  • -
  • Or here
  • -
-
- -
-

Fantastic Ordered List

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

Tabular Tables

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

Clever Quotes

-

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

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

Intergalactic Interconnections

-

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

-
- -
-

Speaker View

-

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

-

Press the S key to try it out.

- - -
- -
-

Export to PDF

-

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

- -
- -
-

Global State

-

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

-
- -
-

State Events

-

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

-

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

Take a Moment

-

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

-
- -
-

Much more

- -
- -
-

THE END

-

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

-
- +
Slide 1
+
Slide 2
-
- - diff --git a/reveal.js/js/reveal.js b/reveal.js/js/reveal.js index 054c175..a1357a6 100644 --- a/reveal.js/js/reveal.js +++ b/reveal.js/js/reveal.js @@ -1,9 +1,9 @@ /*! * reveal.js - * https://lab.hakim.se/reveal-js + * http://revealjs.com * MIT licensed * - * Copyright (C) 2016 Hakim El Hattab, https://hakim.se + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se */ (function( root, factory ) { if( typeof define === 'function' && define.amd ) { @@ -25,11 +25,19 @@ var Reveal; + // The reveal.js version + var VERSION = '3.9.2'; + var SLIDES_SELECTOR = '.slides section', HORIZONTAL_SLIDES_SELECTOR = '.slides>section', VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section', HOME_SLIDE_SELECTOR = '.slides>section:first-of-type', + UA = navigator.userAgent, + + // Methods that may not be invoked via the postMessage API + POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/, + // Configuration defaults, can be overridden at initialization time config = { @@ -39,22 +47,60 @@ height: 700, // Factor of the display size that should remain empty around the content - margin: 0.1, + margin: 0.04, // Bounds for smallest/largest possible scale to apply to content minScale: 0.2, - maxScale: 1.5, + maxScale: 2.0, - // Display controls in the bottom right corner + // Display presentation control arrows controls: true, + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + // Display a presentation progress bar progress: true, // Display the page number of the current slide + // - true: Show slide number + // - false: Hide slide number + // + // Can optionally be set as a string that specifies the number formatting: + // - "h.v": Horizontal . vertical slide number (default) + // - "h/v": Horizontal / vertical slide number + // - "c": Flattened slide number + // - "c/t": Flattened slide number / total slides + // + // Alternatively, you can provide a function that returns the slide + // number for the current slide. The function should take in a slide + // object and return an array with one string [slideNumber] or + // three strings [n1,delimiter,n2]. See #formatSlideNumber(). slideNumber: false, - // Push each slide change to the browser history + // Can be used to limit the contexts in which the slide number appears + // - "all": Always show the slide number + // - "print": Only when printing to PDF + // - "speaker": Only in the speaker view + showSlideNumber: 'all', + + // Use 1 based indexing for # links to match slide number (default is zero + // based) + hashOneBasedIndex: false, + + // Add the current slide number to the URL hash so that reloading the + // page/copying the URL will return you to the same slide + hash: false, + + // Push each slide change to the browser history. Implies `hash: true` history: false, // Enable keyboard shortcuts for navigation @@ -66,6 +112,10 @@ // Enable the slide overview mode overview: true, + // Disables the default reveal.js slide layout so that you can use + // custom CSS layout + disableLayout: false, + // Vertical centering of slides center: true, @@ -78,14 +128,47 @@ // Change the presentation direction to be RTL rtl: false, + // Changes the behavior of our navigation directions. + // + // "default" + // Left/right arrow keys step between horizontal slides, up/down + // arrow keys step between vertical slides. Space key steps through + // all slides (both horizontal and vertical). + // + // "linear" + // Removes the up/down arrows. Left/right arrows step through all + // slides (both horizontal and vertical). + // + // "grid" + // When this is enabled, stepping left/right from a vertical stack + // to an adjacent vertical stack will land you at the same vertical + // index. + // + // Consider a deck with six slides ordered in two vertical stacks: + // 1.1 2.1 + // 1.2 2.2 + // 1.3 2.3 + // + // If you're on slide 1.3 and navigate right, you will normally move + // from 1.3 -> 2.1. If "grid" is used, the same navigation takes you + // from 1.3 -> 2.3. + navigationMode: 'default', + + // Randomizes the order of slides each time the presentation loads + shuffle: false, + // Turns fragments on and off globally fragments: true, + // Flags whether to include the current fragment in the URL, + // so that reloading brings you to the same fragment position + fragmentInURL: false, + // Flags if the presentation is running in an embedded mode, // i.e. contained within a limited portion of the screen embedded: false, - // Flags if we should show a help overlay when the questionmark + // Flags if we should show a help overlay when the question-mark // key is pressed help: true, @@ -95,14 +178,37 @@ // Flags if speaker notes should be visible to all viewers showNotes: false, - // Number of milliseconds between automatically proceeding to the - // next slide, disabled when set to 0, this value can be overwritten - // by using a data-autoslide attribute on your slides + // Global override for autolaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Global override for preloading lazy-loaded iframes + // - null: Iframes with data-src AND data-preload will be loaded when within + // the viewDistance, iframes with only data-src will be loaded when visible + // - true: All iframes with data-src will be loaded when within the viewDistance + // - false: All iframes with data-src will be loaded only when visible + preloadIframes: null, + + // Controls automatic progression to the next slide + // - 0: Auto-sliding only happens if the data-autoslide HTML attribute + // is present on the current slide or fragment + // - 1+: All slides will progress automatically at the given interval + // - false: No auto-sliding, even if data-autoslide is present autoSlide: 0, // Stop auto-sliding after user input autoSlideStoppable: true, + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + + // Specify the average time in seconds that you think you will spend + // presenting each slide. This is used to show a pacing timer in the + // speaker view + defaultTiming: null, + // Enable slide navigation via mouse wheel mouseWheel: false, @@ -113,6 +219,8 @@ hideAddressBar: true, // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually previewLinks: false, // Exposes the reveal.js API through window.postMessage @@ -121,7 +229,7 @@ // Dispatches all reveal.js events to the parent window through postMessage postMessageEvents: false, - // Focuses body when page changes visiblity to ensure keyboard shortcuts work + // Focuses body when page changes visibility to ensure keyboard shortcuts work focusBodyOnPageVisibilityChange: true, // Transition style @@ -139,24 +247,65 @@ // Parallax background size parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px" + // Parallax background repeat + parallaxBackgroundRepeat: '', // repeat/repeat-x/repeat-y/no-repeat/initial/inherit + + // Parallax background position + parallaxBackgroundPosition: '', // CSS syntax, e.g. "top left" + // Amount of pixels to move the parallax background per slide step parallaxBackgroundHorizontal: null, parallaxBackgroundVertical: null, + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + + // Prints each fragment on a separate slide + pdfSeparateFragments: true, + + // Offset used to reduce the height of content within exported PDF pages. + // This exists to account for environment differences based on how you + // print to PDF. CLI printing options, like phantomjs and wkpdf, can end + // on precisely the total height of the document whereas in-browser + // printing has to end one pixel before. + pdfPageHeightOffset: -1, + // Number of slides away from the current that are visible viewDistance: 3, + // Number of slides away from the current that are visible on mobile + // devices. It is advisable to set this to a lower number than + // viewDistance in order to save resources. + mobileViewDistance: 2, + + // The display mode that will be used to show slides + display: 'block', + + // Hide cursor if inactive + hideInactiveCursor: true, + + // Time before the cursor is hidden (in ms) + hideCursorTime: 5000, + // Script dependencies to load dependencies: [] }, + // Flags if Reveal.initialize() has been called + initialized = false, + // Flags if reveal.js is loaded (has dispatched the 'ready' event) loaded = false, // Flags if the overview mode is currently active overview = false, + // Holds the dimensions of our overview slides, including margins + overviewSlideWidth = null, + overviewSlideHeight = null, + // The horizontal and vertical index of the currently active slide indexh, indexv, @@ -167,6 +316,10 @@ previousBackground, + // Remember which directions that the user has navigated towards + hasNavigatedRight = false, + hasNavigatedDown = false, + // Slides may hold a data-state attribute which we pick up and apply // as a class to the body. This list contains the combined state of // all current slides. @@ -182,18 +335,33 @@ // Cached references to DOM elements dom = {}, + // A list of registered reveal.js plugins + plugins = {}, + + // List of asynchronously loaded reveal.js dependencies + asyncDependencies = [], + // Features supported by the browser, see #checkCapabilities() features = {}, // Client is a mobile device, see #checkCapabilities() isMobileDevice, + // Client is a desktop Chrome, see #checkCapabilities() + isChrome, + // Throttles mouse wheel navigation lastMouseWheelStep = 0, // Delays updates to the URL due to a Chrome thumbnailer bug writeURLTimeout = 0, + // Is the mouse pointer currently hidden from view + cursorHidden = false, + + // Timeout used to determine when the cursor is inactive + cursorInactiveTimeout = 0, + // Flags if the interaction event listeners are bound eventsAreBound = false, @@ -210,32 +378,28 @@ touch = { startX: 0, startY: 0, - startSpan: 0, startCount: 0, captured: false, threshold: 40 }, - // Holds information about the keyboard shortcuts - keyboardShortcuts = { - 'N , SPACE': 'Next slide', - 'P': 'Previous slide', - '← , H': 'Navigate left', - '→ , L': 'Navigate right', - '↑ , K': 'Navigate up', - '↓ , J': 'Navigate down', - 'Home': 'First slide', - 'End': 'Last slide', - 'B , .': 'Pause', - 'F': 'Fullscreen', - 'ESC, O': 'Slide overview' - }; + // A key:value map of shortcut keyboard keys and descriptions of + // the actions they trigger, generated in #configure() + keyboardShortcuts = {}, + + // Holds custom key code mappings + registeredKeyBindings = {}; /** * Starts up the presentation if the client is capable. */ function initialize( options ) { + // Make sure we only initialize once + if( initialized === true ) return; + + initialized = true; + checkCapabilities(); if( !features.transforms2d && !features.transforms3d ) { @@ -281,7 +445,7 @@ // Hide the address bar in mobile browsers hideAddressBar(); - // Loads the dependencies and continues to #start() once done + // Loads dependencies and continues to #start() once done load(); } @@ -292,111 +456,212 @@ */ function checkCapabilities() { - features.transforms3d = 'WebkitPerspective' in document.body.style || - 'MozPerspective' in document.body.style || - 'msPerspective' in document.body.style || - 'OPerspective' in document.body.style || - 'perspective' in document.body.style; + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) || + ( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS + isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); - features.transforms2d = 'WebkitTransform' in document.body.style || - 'MozTransform' in document.body.style || - 'msTransform' in document.body.style || - 'OTransform' in document.body.style || - 'transform' in document.body.style; + var testElement = document.createElement( 'div' ); + + features.transforms3d = 'WebkitPerspective' in testElement.style || + 'MozPerspective' in testElement.style || + 'msPerspective' in testElement.style || + 'OPerspective' in testElement.style || + 'perspective' in testElement.style; + + features.transforms2d = 'WebkitTransform' in testElement.style || + 'MozTransform' in testElement.style || + 'msTransform' in testElement.style || + 'OTransform' in testElement.style || + 'transform' in testElement.style; features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function'; features.canvas = !!document.createElement( 'canvas' ).getContext; - features.touch = !!( 'ontouchstart' in window ); - // Transitions in the overview are disabled in desktop and - // mobile Safari due to lag - features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( navigator.userAgent ); + // Safari due to lag + features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA ); - isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( navigator.userAgent ); + // Flags if we should use zoom instead of transform to scale + // up slides. Zoom produces crisper results but has a lot of + // xbrowser quirks so we only use it in whitelsited browsers. + features.zoom = 'zoom' in testElement.style && !isMobileDevice && + ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); } - /** - * Loads the dependencies of reveal.js. Dependencies are - * defined via the configuration option 'dependencies' - * and will be loaded prior to starting/binding reveal.js. - * Some dependencies may have an 'async' flag, if so they - * will load after reveal.js has been started up. - */ + /** + * Loads the dependencies of reveal.js. Dependencies are + * defined via the configuration option 'dependencies' + * and will be loaded prior to starting/binding reveal.js. + * Some dependencies may have an 'async' flag, if so they + * will load after reveal.js has been started up. + */ function load() { var scripts = [], - scriptsAsync = [], - scriptsToPreload = 0; - - // Called once synchronous scripts finish loading - function proceed() { - if( scriptsAsync.length ) { - // Load asynchronous scripts - head.js.apply( null, scriptsAsync ); - } - - start(); - } - - function loadScript( s ) { - head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], function() { - // Extension may contain callback functions - if( typeof s.callback === 'function' ) { - s.callback.apply( this ); - } - - if( --scriptsToPreload === 0 ) { - proceed(); - } - }); - } - - for( var i = 0, len = config.dependencies.length; i < len; i++ ) { - var s = config.dependencies[i]; + scriptsToLoad = 0; + config.dependencies.forEach( function( s ) { // Load if there's no condition or the condition is truthy if( !s.condition || s.condition() ) { if( s.async ) { - scriptsAsync.push( s.src ); + asyncDependencies.push( s ); } else { - scripts.push( s.src ); + scripts.push( s ); } - - loadScript( s ); } - } + } ); if( scripts.length ) { - scriptsToPreload = scripts.length; + scriptsToLoad = scripts.length; // Load synchronous scripts - head.js.apply( null, scripts ); + scripts.forEach( function( s ) { + loadScript( s.src, function() { + + if( typeof s.callback === 'function' ) s.callback(); + + if( --scriptsToLoad === 0 ) { + initPlugins(); + } + + } ); + } ); } else { - proceed(); + initPlugins(); } } + /** + * Initializes our plugins and waits for them to be ready + * before proceeding. + */ + function initPlugins() { + + var pluginsToInitialize = Object.keys( plugins ).length; + + // If there are no plugins, skip this step + if( pluginsToInitialize === 0 ) { + loadAsyncDependencies(); + } + // ... otherwise initialize plugins + else { + + var afterPlugInitialized = function() { + if( --pluginsToInitialize === 0 ) { + loadAsyncDependencies(); + } + }; + + for( var i in plugins ) { + + var plugin = plugins[i]; + + // If the plugin has an 'init' method, invoke it + if( typeof plugin.init === 'function' ) { + var callback = plugin.init(); + + // If the plugin returned a Promise, wait for it + if( callback && typeof callback.then === 'function' ) { + callback.then( afterPlugInitialized ); + } + else { + afterPlugInitialized(); + } + } + else { + afterPlugInitialized(); + } + + } + + } + + } + + /** + * Loads all async reveal.js dependencies. + */ + function loadAsyncDependencies() { + + if( asyncDependencies.length ) { + asyncDependencies.forEach( function( s ) { + loadScript( s.src, s.callback ); + } ); + } + + start(); + + } + + /** + * Loads a JavaScript file from the given URL and executes it. + * + * @param {string} url Address of the .js file to load + * @param {function} callback Method to invoke when the script + * has loaded and executed + */ + function loadScript( url, callback ) { + + var script = document.createElement( 'script' ); + script.type = 'text/javascript'; + script.async = false; + script.defer = false; + script.src = url; + + if( callback ) { + + // Success callback + script.onload = script.onreadystatechange = function( event ) { + if( event.type === "load" || (/loaded|complete/.test( script.readyState ) ) ) { + + // Kill event listeners + script.onload = script.onreadystatechange = script.onerror = null; + + callback(); + + } + }; + + // Error callback + script.onerror = function( err ) { + + // Kill event listeners + script.onload = script.onreadystatechange = script.onerror = null; + + callback( new Error( 'Failed loading script: ' + script.src + '\n' + err) ); + + }; + + } + + // Append the script at the end of + var head = document.querySelector( 'head' ); + head.insertBefore( script, head.lastChild ); + + } + /** * Starts up reveal.js by binding input events and navigating * to the current URL deeplink if there is one. */ function start() { + loaded = true; + // Make sure we've got all the DOM elements we need setupDOM(); // Listen to messages posted to this window setupPostMessage(); - // Prevent iframes from scrolling the slides out of view - setupIframeScrollPrevention(); + // Prevent the slides from being scrolled out of view + setupScrollPrevention(); // Resets all vertical slides so that only the first is visible resetVerticalSlides(); @@ -416,7 +681,7 @@ // Enable transitions now that we're loaded dom.slides.classList.remove( 'no-transition' ); - loaded = true; + dom.wrapper.classList.add( 'ready' ); dispatchEvent( 'ready', { 'indexh': indexh, @@ -451,6 +716,20 @@ // Prevent transitions while we're loading dom.slides.classList.add( 'no-transition' ); + if( isMobileDevice ) { + dom.wrapper.classList.add( 'no-hover' ); + } + else { + dom.wrapper.classList.remove( 'no-hover' ); + } + + if( /iphone/gi.test( UA ) ) { + dom.wrapper.classList.add( 'ua-iphone' ); + } + else { + dom.wrapper.classList.remove( 'ua-iphone' ); + } + // Background element dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); @@ -459,11 +738,11 @@ dom.progressbar = dom.progress.querySelector( 'span' ); // Arrow controls - createSingletonNode( dom.wrapper, 'aside', 'controls', - '' + - '' + - '' + - '' ); + dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls', + '' + + '' + + '' + + '' ); // Slide number dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' ); @@ -471,13 +750,10 @@ // Element containing notes that are visible to the audience dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null ); dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' ); + dom.speakerNotes.setAttribute( 'tabindex', '0' ); // Overlay graphic which is displayed during the paused mode - createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null ); - - // Cache references to elements - dom.controls = document.querySelector( '.reveal .controls' ); - dom.theme = document.querySelector( '#theme' ); + dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', config.controls ? '' : null ); dom.wrapper.setAttribute( 'role', 'application' ); @@ -489,6 +765,10 @@ dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); + // The right and down arrows in the standard reveal.js controls + dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' ); + dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' ); + dom.statusDiv = createStatusDiv(); } @@ -496,6 +776,8 @@ * Creates a hidden div with role aria-live to announce the * current slide content. Hide the div off-screen to make it * available only to Assistive Technologies. + * + * @return {HTMLElement} */ function createStatusDiv() { @@ -505,7 +787,7 @@ statusDiv.style.position = 'absolute'; statusDiv.style.height = '1px'; statusDiv.style.width = '1px'; - statusDiv.style.overflow ='hidden'; + statusDiv.style.overflow = 'hidden'; statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )'; statusDiv.setAttribute( 'id', 'aria-status-div' ); statusDiv.setAttribute( 'aria-live', 'polite' ); @@ -516,6 +798,38 @@ } + /** + * Converts the given HTML element into a string of text + * that can be announced to a screen reader. Hidden + * elements are excluded. + */ + function getStatusText( node ) { + + var text = ''; + + // Text node + if( node.nodeType === 3 ) { + text += node.textContent; + } + // Element node + else if( node.nodeType === 1 ) { + + var isAriaHidden = node.getAttribute( 'aria-hidden' ); + var isDisplayHidden = window.getComputedStyle( node )['display'] === 'none'; + if( isAriaHidden !== 'true' && !isDisplayHidden ) { + + toArray( node.childNodes ).forEach( function( child ) { + text += getStatusText( child ); + } ); + + } + + } + + return text; + + } + /** * Configures the presentation for printing to a static * PDF. @@ -526,14 +840,14 @@ // Dimensions of the PDF pages var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), - pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); + pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); // Dimensions of slides within the pages var slideWidth = slideSize.width, slideHeight = slideSize.height; // Let the browser know what page size we want to print - injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0;}' ); + injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' ); // Limit the size of certain elements to the dimensions of the slide injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); @@ -542,17 +856,13 @@ document.body.style.width = pageWidth + 'px'; document.body.style.height = pageHeight + 'px'; - // Add each slide's index as attributes on itself, we need these - // indices to generate slide numbers below - toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { - hslide.setAttribute( 'data-index-h', h ); + // Make sure stretch elements fit on slide + layoutSlideContents( slideWidth, slideHeight ); - if( hslide.classList.contains( 'stack' ) ) { - toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { - vslide.setAttribute( 'data-index-h', h ); - vslide.setAttribute( 'data-index-v', v ); - } ); - } + // Compute slide numbers now, before we start duplicating slides + var doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber ); + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + slide.setAttribute( 'data-slide-number', getSlideNumber( slide ) ); } ); // Slide and slide background layout @@ -565,84 +875,147 @@ var left = ( pageWidth - slideWidth ) / 2, top = ( pageHeight - slideHeight ) / 2; - var contentHeight = getAbsoluteHeight( slide ); + var contentHeight = slide.scrollHeight; var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + // Center slides vertically if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); } + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + var page = document.createElement( 'div' ); + page.className = 'pdf-page'; + page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; + slide.parentNode.insertBefore( page, slide ); + page.appendChild( slide ); + // Position the slide inside of the page slide.style.left = left + 'px'; slide.style.top = top + 'px'; slide.style.width = slideWidth + 'px'; - // TODO Backgrounds need to be multiplied when the slide - // stretches over multiple pages - var background = slide.querySelector( '.slide-background' ); - if( background ) { - background.style.width = pageWidth + 'px'; - background.style.height = ( pageHeight * numberOfPages ) + 'px'; - background.style.top = -top + 'px'; - background.style.left = -left + 'px'; + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); } // Inject notes if `showNotes` is enabled if( config.showNotes ) { + + // Are there notes for this slide? var notes = getSlideNotes( slide ); if( notes ) { + var notesSpacing = 8; + var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; var notesElement = document.createElement( 'div' ); notesElement.classList.add( 'speaker-notes' ); notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); notesElement.innerHTML = notes; - notesElement.style.left = ( notesSpacing - left ) + 'px'; - notesElement.style.bottom = ( notesSpacing - top ) + 'px'; - notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; - slide.appendChild( notesElement ); + + if( notesLayout === 'separate-page' ) { + page.parentNode.insertBefore( notesElement, page.nextSibling ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + } + } // Inject slide numbers if `slideNumbers` are enabled - if( config.slideNumber ) { - var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1, - slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1; - + if( doingSlideNumbers ) { var numberElement = document.createElement( 'div' ); numberElement.classList.add( 'slide-number' ); numberElement.classList.add( 'slide-number-pdf' ); - numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV ); - background.appendChild( numberElement ); + numberElement.innerHTML = slide.getAttribute( 'data-slide-number' ); + page.appendChild( numberElement ); } + + // Copy page and show fragments one after another + if( config.pdfSeparateFragments ) { + + // 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. + var fragmentGroups = sortFragments( page.querySelectorAll( '.fragment' ), true ); + + var previousFragmentStep; + var previousPage; + + fragmentGroups.forEach( function( fragments ) { + + // Remove 'current-fragment' from the previous group + if( previousFragmentStep ) { + previousFragmentStep.forEach( function( fragment ) { + fragment.classList.remove( 'current-fragment' ); + } ); + } + + // Show the fragments for the current index + fragments.forEach( function( fragment ) { + fragment.classList.add( 'visible', 'current-fragment' ); + } ); + + // Create a separate page for the current fragment state + var clonedPage = page.cloneNode( true ); + page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling ); + + previousFragmentStep = fragments; + previousPage = clonedPage; + + } ); + + // Reset the first/original page so that all fragments are hidden + fragmentGroups.forEach( function( fragments ) { + fragments.forEach( function( fragment ) { + fragment.classList.remove( 'visible', 'current-fragment' ); + } ); + } ); + + } + // Show all fragments + else { + toArray( page.querySelectorAll( '.fragment:not(.fade-out)' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + } ); + } + } } ); - // Show all fragments - toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' .fragment' ) ).forEach( function( fragment ) { - fragment.classList.add( 'visible' ); - } ); + // Notify subscribers that the PDF layout is good to go + dispatchEvent( 'pdf-ready' ); } /** - * This is an unfortunate necessity. Iframes can trigger the - * parent window to scroll, for example by focusing an input. + * This is an unfortunate necessity. Some actions – such as + * an input field being focused in an iframe or using the + * keyboard to expand text selection beyond the bounds of + * a slide – can trigger our content to be pushed out of view. * This scrolling can not be prevented by hiding overflow in - * CSS so we have to resort to repeatedly checking if the - * browser has decided to offset our slides :( + * CSS (we already do) so we have to resort to repeatedly + * checking if the slides have been offset :( */ - function setupIframeScrollPrevention() { + function setupScrollPrevention() { - if( dom.slides.querySelector( 'iframe' ) ) { - setInterval( function() { - if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { - dom.wrapper.scrollTop = 0; - dom.wrapper.scrollLeft = 0; - } - }, 500 ); - } + setInterval( function() { + if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { + dom.wrapper.scrollTop = 0; + dom.wrapper.scrollLeft = 0; + } + }, 1000 ); } @@ -650,6 +1023,13 @@ * Creates an HTML element and returns a reference to it. * If the element already exists the existing instance will * be returned. + * + * @param {HTMLElement} container + * @param {string} tagname + * @param {string} classname + * @param {string} innerHTML + * + * @return {HTMLElement} */ function createSingletonNode( container, tagname, classname, innerHTML ) { @@ -667,7 +1047,7 @@ // If no node was found, create it now var node = document.createElement( tagname ); - node.classList.add( classname ); + node.className = classname; if( typeof innerHTML === 'string' ) { node.innerHTML = innerHTML; } @@ -693,24 +1073,12 @@ // Iterate over all horizontal slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { - var backgroundStack; - - if( printMode ) { - backgroundStack = createBackground( slideh, slideh ); - } - else { - backgroundStack = createBackground( slideh, dom.background ); - } + var backgroundStack = createBackground( slideh, dom.background ); // Iterate over all vertical slides toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { - if( printMode ) { - createBackground( slidev, slidev ); - } - else { - createBackground( slidev, backgroundStack ); - } + createBackground( slidev, backgroundStack ); backgroundStack.classList.add( 'stack' ); @@ -723,6 +1091,8 @@ dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")'; dom.background.style.backgroundSize = config.parallaxBackgroundSize; + dom.background.style.backgroundRepeat = config.parallaxBackgroundRepeat; + dom.background.style.backgroundPosition = config.parallaxBackgroundPosition; // Make sure the below properties are set on the element - these properties are // needed for proper transitions to be set on the element via CSS. To remove @@ -748,9 +1118,61 @@ * @param {HTMLElement} slide * @param {HTMLElement} container The element that the background * should be appended to + * @return {HTMLElement} New background div */ function createBackground( slide, container ) { + + // Main slide background element + var element = document.createElement( 'div' ); + element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + + // Inner background element that wraps images/videos/iframes + var contentElement = document.createElement( 'div' ); + contentElement.className = 'slide-background-content'; + + element.appendChild( contentElement ); + container.appendChild( element ); + + slide.slideBackgroundElement = element; + slide.slideBackgroundContentElement = contentElement; + + // Syncs the background to reflect all current background settings + syncBackground( slide ); + + return element; + + } + + /** + * Renders all of the visual properties of a slide background + * based on the various background attributes. + * + * @param {HTMLElement} slide + */ + function syncBackground( slide ) { + + var element = slide.slideBackgroundElement, + contentElement = slide.slideBackgroundContentElement; + + // Reset the prior background state in case this is not the + // initial sync + slide.classList.remove( 'has-dark-background' ); + slide.classList.remove( 'has-light-background' ); + + element.removeAttribute( 'data-loaded' ); + element.removeAttribute( 'data-background-hash' ); + element.removeAttribute( 'data-background-size' ); + element.removeAttribute( 'data-background-transition' ); + element.style.backgroundColor = ''; + + contentElement.style.backgroundSize = ''; + contentElement.style.backgroundRepeat = ''; + contentElement.style.backgroundPosition = ''; + contentElement.style.backgroundImage = ''; + contentElement.style.opacity = ''; + contentElement.innerHTML = ''; + var data = { background: slide.getAttribute( 'data-background' ), backgroundSize: slide.getAttribute( 'data-background-size' ), @@ -760,17 +1182,13 @@ backgroundColor: slide.getAttribute( 'data-background-color' ), backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), backgroundPosition: slide.getAttribute( 'data-background-position' ), - backgroundTransition: slide.getAttribute( 'data-background-transition' ) + backgroundTransition: slide.getAttribute( 'data-background-transition' ), + backgroundOpacity: slide.getAttribute( 'data-background-opacity' ) }; - var element = document.createElement( 'div' ); - - // Carry over custom classes from the slide to the background - element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); - if( data.background ) { // Auto-wrap image urls in url(...) - if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) { + if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) { slide.setAttribute( 'data-background-image', data.background ); } else { @@ -790,34 +1208,44 @@ data.backgroundColor + data.backgroundRepeat + data.backgroundPosition + - data.backgroundTransition ); + data.backgroundTransition + + data.backgroundOpacity ); } // Additional and optional background properties - if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize; + if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; - if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat; - if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition; if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); - container.appendChild( element ); + if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' ); - // If backgrounds are being recreated, clear old classes - slide.classList.remove( 'has-dark-background' ); - slide.classList.remove( 'has-light-background' ); + // Background image options are set on the content wrapper + if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; + if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity; - // If this slide has a background color, add a class that + // 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 set - var computedBackgroundColor = window.getComputedStyle( element ).backgroundColor; - if( computedBackgroundColor ) { - var rgb = colorToRgb( computedBackgroundColor ); + // color, no class will be added + var contrastColor = data.backgroundColor; + + // If no bg color was found, check the computed background + if( !contrastColor ) { + var computedBackgroundStyle = window.getComputedStyle( element ); + if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) { + contrastColor = computedBackgroundStyle.backgroundColor; + } + } + + if( contrastColor ) { + var rgb = colorToRgb( contrastColor ); // Ignore fully transparent backgrounds. Some browsers return // rgba(0,0,0,0) when reading the computed background color of // an element with no background if( rgb && rgb.a !== 0 ) { - if( colorBrightness( computedBackgroundColor ) < 128 ) { + if( colorBrightness( contrastColor ) < 128 ) { slide.classList.add( 'has-dark-background' ); } else { @@ -826,8 +1254,6 @@ } } - return element; - } /** @@ -852,7 +1278,20 @@ // Check if the requested method can be found if( data.method && typeof Reveal[data.method] === 'function' ) { - Reveal[data.method].apply( Reveal, data.args ); + + if( POST_MESSAGE_METHOD_BLACKLIST.test( data.method ) === false ) { + + var result = Reveal[data.method].apply( Reveal, data.args ); + + // Dispatch a postMessage event with the returned value from + // our method invocation for getter functions + dispatchPostMessage( 'callback', { method: data.method, result: result } ); + + } + else { + console.warn( 'reveal.js: "'+ data.method +'" is is blacklisted from the postMessage API' ); + } + } } }, false ); @@ -863,17 +1302,27 @@ /** * Applies the configuration settings from the config * object. May be called multiple times. + * + * @param {object} options */ function configure( options ) { - var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; - - dom.wrapper.classList.remove( config.transition ); + var oldTransition = config.transition; // New config options may be passed when this method // is invoked through the API after initialization if( typeof options === 'object' ) extend( config, options ); + // Abort if reveal.js hasn't finished loading, config + // changes will be applied automatically once loading + // finishes + if( loaded === false ) return; + + var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; + + // Remove the previously configured transition class + dom.wrapper.classList.remove( oldTransition ); + // Force linear transition based on browser capabilities if( features.transforms3d === false ) config.transition = 'linear'; @@ -884,7 +1333,13 @@ dom.controls.style.display = config.controls ? 'block' : 'none'; dom.progress.style.display = config.progress ? 'block' : 'none'; - dom.slideNumber.style.display = config.slideNumber && !isPrintingPDF() ? 'block' : 'none'; + + dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout ); + dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows ); + + if( config.shuffle ) { + shuffle(); + } if( config.rtl ) { dom.wrapper.classList.add( 'rtl' ); @@ -906,10 +1361,7 @@ } if( config.showNotes ) { - dom.speakerNotes.classList.add( 'visible' ); - } - else { - dom.speakerNotes.classList.remove( 'visible' ); + dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' ); } if( config.mouseWheel ) { @@ -929,13 +1381,26 @@ disableRollingLinks(); } + // Auto-hide the mouse pointer when its inactive + if( config.hideInactiveCursor ) { + document.addEventListener( 'mousemove', onDocumentCursorActive, false ); + document.addEventListener( 'mousedown', onDocumentCursorActive, false ); + } + else { + showCursor(); + + document.removeEventListener( 'mousemove', onDocumentCursorActive, false ); + document.removeEventListener( 'mousedown', onDocumentCursorActive, false ); + } + // Iframe link previews if( config.previewLinks ) { enablePreviewLinks(); + disablePreviewLinks( '[data-preview-link=false]' ); } else { disablePreviewLinks(); - enablePreviewLinks( '[data-preview-link]' ); + enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' ); } // Remove existing auto-slide controls @@ -962,6 +1427,47 @@ } ); } + // Slide numbers + var slideNumberDisplay = 'none'; + if( config.slideNumber && !isPrintingPDF() ) { + if( config.showSlideNumber === 'all' ) { + slideNumberDisplay = 'block'; + } + else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) { + slideNumberDisplay = 'block'; + } + } + + dom.slideNumber.style.display = slideNumberDisplay; + + // Add the navigation mode to the DOM so we can adjust styling + if( config.navigationMode !== 'default' ) { + dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode ); + } + else { + dom.wrapper.removeAttribute( 'data-navigation-mode' ); + } + + // Define our contextual list of keyboard shortcuts + if( config.navigationMode === 'linear' ) { + keyboardShortcuts['→ , ↓ , SPACE , N , L , J'] = 'Next slide'; + keyboardShortcuts['← , ↑ , P , H , K'] = 'Previous slide'; + } + else { + keyboardShortcuts['N , SPACE'] = 'Next slide'; + keyboardShortcuts['P'] = 'Previous slide'; + keyboardShortcuts['← , H'] = 'Navigate left'; + keyboardShortcuts['→ , L'] = 'Navigate right'; + keyboardShortcuts['↑ , K'] = 'Navigate up'; + keyboardShortcuts['↓ , J'] = 'Navigate down'; + } + + keyboardShortcuts['Home , Shift ←'] = 'First slide'; + keyboardShortcuts['End , Shift →'] = 'Last slide'; + keyboardShortcuts['B , .'] = 'Pause'; + keyboardShortcuts['F'] = 'Fullscreen'; + keyboardShortcuts['ESC, O'] = 'Slide overview'; + sync(); } @@ -977,13 +1483,8 @@ window.addEventListener( 'resize', onWindowResize, false ); if( config.touch ) { - dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); - dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); - dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); - - // Support pointer-style touch interaction as well - if( window.navigator.pointerEnabled ) { - // IE 11 uses un-prefixed version of pointer events + if( 'onpointerdown' in window ) { + // Use W3C pointer events dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false ); dom.wrapper.addEventListener( 'pointermove', onPointerMove, false ); dom.wrapper.addEventListener( 'pointerup', onPointerUp, false ); @@ -994,6 +1495,12 @@ dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false ); dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false ); } + else { + // Fall back to touch events + dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); + } } if( config.keyboard ) { @@ -1005,6 +1512,8 @@ dom.progress.addEventListener( 'click', onProgressClicked, false ); } + dom.pauseOverlay.addEventListener( 'click', resume, false ); + if( config.focusBodyOnPageVisibilityChange ) { var visibilityChange; @@ -1029,7 +1538,7 @@ // Only support touch for Android, fixes double navigations in // stock browser - if( navigator.userAgent.match( /android/gi ) ) { + if( UA.match( /android/gi ) ) { pointerEvents = [ 'touchstart' ]; } @@ -1056,22 +1565,19 @@ window.removeEventListener( 'hashchange', onWindowHashChange, false ); window.removeEventListener( 'resize', onWindowResize, false ); + dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); + + dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); + dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false ); dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false ); dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false ); - // IE11 - if( window.navigator.pointerEnabled ) { - dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); - dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); - dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); - } - // IE10 - else if( window.navigator.msPointerEnabled ) { - dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); - dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); - dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); - } + dom.pauseOverlay.removeEventListener( 'click', resume, false ); if ( config.progress && dom.progress ) { dom.progress.removeEventListener( 'click', onProgressClicked, false ); @@ -1088,9 +1594,91 @@ } + /** + * Registers a new plugin with this reveal.js instance. + * + * reveal.js waits for all regisered plugins to initialize + * before considering itself ready, as long as the plugin + * is registered before calling `Reveal.initialize()`. + */ + function registerPlugin( id, plugin ) { + + if( plugins[id] === undefined ) { + plugins[id] = plugin; + + // If a plugin is registered after reveal.js is loaded, + // initialize it right away + if( loaded && typeof plugin.init === 'function' ) { + plugin.init(); + } + } + else { + console.warn( 'reveal.js: "'+ id +'" plugin has already been registered' ); + } + + } + + /** + * Checks if a specific plugin has been registered. + * + * @param {String} id Unique plugin identifier + */ + function hasPlugin( id ) { + + return !!plugins[id]; + + } + + /** + * Returns the specific plugin instance, if a plugin + * with the given ID has been registered. + * + * @param {String} id Unique plugin identifier + */ + function getPlugin( id ) { + + return plugins[id]; + + } + + /** + * Add a custom key binding with optional description to + * be added to the help screen. + */ + function addKeyBinding( binding, callback ) { + + if( typeof binding === 'object' && binding.keyCode ) { + registeredKeyBindings[binding.keyCode] = { + callback: callback, + key: binding.key, + description: binding.description + }; + } + else { + registeredKeyBindings[binding] = { + callback: callback, + key: null, + description: null + }; + } + + } + + /** + * Removes the specified custom key binding. + */ + function removeKeyBinding( keyCode ) { + + delete registeredKeyBindings[keyCode]; + + } + /** * Extend object a with the properties of object b. * If there's a conflict, object b takes precedence. + * + * @param {object} a + * @param {object} b */ function extend( a, b ) { @@ -1098,10 +1686,15 @@ a[ i ] = b[ i ]; } + return a; + } /** * Converts the target object to an array. + * + * @param {object} o + * @return {object[]} */ function toArray( o ) { @@ -1111,6 +1704,9 @@ /** * Utility for deserializing a value. + * + * @param {*} value + * @return {*} */ function deserialize( value ) { @@ -1118,7 +1714,7 @@ if( value === 'null' ) return null; else if( value === 'true' ) return true; else if( value === 'false' ) return false; - else if( value.match( /^\d+$/ ) ) return parseFloat( value ); + else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value ); } return value; @@ -1129,8 +1725,10 @@ * Measures the distance in pixels between point a * and point b. * - * @param {Object} a point with x/y properties - * @param {Object} b point with x/y properties + * @param {object} a point with x/y properties + * @param {object} b point with x/y properties + * + * @return {number} */ function distanceBetween( a, b ) { @@ -1143,6 +1741,9 @@ /** * Applies a CSS transform to the target element. + * + * @param {HTMLElement} element + * @param {string} transform */ function transformElement( element, transform ) { @@ -1157,6 +1758,8 @@ * Applies CSS transforms to the slides container. The container * is transformed from two separate sources: layout and the overview * mode. + * + * @param {object} transforms */ function transformSlides( transforms ) { @@ -1176,6 +1779,8 @@ /** * Injects the given CSS styles into the DOM. + * + * @param {string} value */ function injectStyleSheet( value ) { @@ -1191,14 +1796,56 @@ } + /** + * Find the closest parent that matches the given + * selector. + * + * @param {HTMLElement} target The child element + * @param {String} selector The CSS selector to match + * the parents against + * + * @return {HTMLElement} The matched parent or null + * if no matching parent was found + */ + function closestParent( target, selector ) { + + var parent = target.parentNode; + + while( parent ) { + + // There's some overhead doing this each time, we don't + // want to rewrite the element prototype but should still + // be enough to feature detect once at startup... + var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector; + + // If we find a match, we're all set + if( matchesMethod && matchesMethod.call( parent, selector ) ) { + return parent; + } + + // Keep searching + parent = parent.parentNode; + + } + + return null; + + } + /** * Converts various color input formats to an {r:0,g:0,b:0} object. * - * @param {String} color The string representation of a color, - * the following formats are supported: - * - #000 - * - #000000 - * - rgb(0,0,0) + * @param {string} color The string representation of a color + * @example + * colorToRgb('#000'); + * @example + * colorToRgb('#000000'); + * @example + * colorToRgb('rgb(0,0,0)'); + * @example + * colorToRgb('rgba(0,0,0)'); + * + * @return {{r: number, g: number, b: number, [a]: number}|null} */ function colorToRgb( color ) { @@ -1248,7 +1895,8 @@ /** * Calculates brightness on a scale of 0-255. * - * @param color See colorStringToRgb for supported formats. + * @param {string} color See colorToRgb for supported formats. + * @see {@link colorToRgb} */ function colorBrightness( color ) { @@ -1262,46 +1910,14 @@ } - /** - * Retrieves the height of the given element by looking - * at the position and height of its immediate children. - */ - function getAbsoluteHeight( element ) { - - var height = 0; - - if( element ) { - var absoluteChildren = 0; - - toArray( element.childNodes ).forEach( function( child ) { - - if( typeof child.offsetTop === 'number' && child.style ) { - // Count # of abs children - if( window.getComputedStyle( child ).position === 'absolute' ) { - absoluteChildren += 1; - } - - height = Math.max( height, child.offsetTop + child.offsetHeight ); - } - - } ); - - // If there are no absolute children, use offsetHeight - if( absoluteChildren === 0 ) { - height = element.offsetHeight; - } - - } - - return height; - - } - /** * Returns the remaining height within the parent of the * target element. * * remaining height = [ configured parent height ] - [ current parent height ] + * + * @param {HTMLElement} element + * @param {number} [height] */ function getRemainingHeight( element, height ) { @@ -1313,11 +1929,19 @@ // Change the .stretch element height to 0 in order find the height of all // the other elements element.style.height = '0px'; + + // In Overview mode, the parent (.slide) height is set of 700px. + // Restore it temporarily to its natural height. + element.parentNode.style.height = 'auto'; + newHeight = height - element.parentNode.offsetHeight; // Restore the old height, just in case element.style.height = oldHeight + 'px'; + // Clear the parent (.slide) height. .removeProperty works in IE9+ + element.parentNode.style.removeProperty('height'); + return newHeight; } @@ -1372,8 +1996,25 @@ // If we're in an iframe, post each reveal.js event to the // parent window. Used by the notes plugin + dispatchPostMessage( type ); + + } + + /** + * Dispatched a postMessage of the given type from our window. + */ + function dispatchPostMessage( type, data ) { + if( config.postMessageEvents && window.parent !== window.self ) { - window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' ); + var message = { + namespace: 'reveal', + eventName: type, + state: getState() + }; + + extend( message, data ); + + window.parent.postMessage( JSON.stringify( message ), '*' ); } } @@ -1424,6 +2065,8 @@ /** * Bind preview frame links. + * + * @param {string} [selector=a] - selector for anchors */ function enablePreviewLinks( selector ) { @@ -1440,9 +2083,9 @@ /** * Unbind preview frame links. */ - function disablePreviewLinks() { + function disablePreviewLinks( selector ) { - var anchors = toArray( document.querySelectorAll( 'a' ) ); + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); anchors.forEach( function( element ) { if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { @@ -1454,6 +2097,8 @@ /** * Opens a preview window for the target URL. + * + * @param {string} url - url for preview iframe src */ function showPreview( url ) { @@ -1472,6 +2117,9 @@ '
', '
', '', + '', + 'Unable to load iframe. This is likely due to the site\'s policy (x-frame-options).', + '', '
' ].join(''); @@ -1495,7 +2143,29 @@ } /** - * Opens a overlay window with help material. + * Open or close help overlay window. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * help is open, false means it's closed. + */ + function toggleHelp( override ){ + + if( typeof override === 'boolean' ) { + override ? showHelp() : closeOverlay(); + } + else { + if( dom.overlay ) { + closeOverlay(); + } + else { + showHelp(); + } + } + } + + /** + * Opens an overlay window with help material. */ function showHelp() { @@ -1515,6 +2185,13 @@ html += '' + key + '' + keyboardShortcuts[ key ] + ''; } + // Add custom key bindings that have associated descriptions + for( var binding in registeredKeyBindings ) { + if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) { + html += '' + registeredKeyBindings[binding].key + '' + registeredKeyBindings[binding].description + ''; + } + } + html += ''; dom.overlay.innerHTML = [ @@ -1559,85 +2236,115 @@ if( dom.wrapper && !isPrintingPDF() ) { - var size = getComputedSlideSize(); + if( !config.disableLayout ) { - var slidePadding = 20; // TODO Dig this out of DOM + // On some mobile devices '100vh' is taller than the visible + // viewport which leads to part of the presentation being + // cut off. To work around this we define our own '--vh' custom + // property where 100x adds up to the correct height. + // + // https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ + if( isMobileDevice ) { + document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' ); + } - // Layout the contents of the slides - layoutSlideContents( config.width, config.height, slidePadding ); + var size = getComputedSlideSize(); - dom.slides.style.width = size.width + 'px'; - dom.slides.style.height = size.height + 'px'; + var oldScale = scale; - // Determine scale of content to fit within available space - scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); + // Layout the contents of the slides + layoutSlideContents( config.width, config.height ); - // Respect max/min scale settings - scale = Math.max( scale, config.minScale ); - scale = Math.min( scale, config.maxScale ); + dom.slides.style.width = size.width + 'px'; + dom.slides.style.height = size.height + 'px'; - // Don't apply any scaling styles if scale is 1 - if( scale === 1 ) { - dom.slides.style.zoom = ''; - dom.slides.style.left = ''; - dom.slides.style.top = ''; - dom.slides.style.bottom = ''; - dom.slides.style.right = ''; - transformSlides( { layout: '' } ); - } - else { - // Use zoom to scale up in desktop Chrome so that content - // remains crisp. We don't use zoom to scale down since that - // can lead to shifts in text layout/line breaks. - if( scale > 1 && !isMobileDevice && /chrome/i.test( navigator.userAgent ) && typeof dom.slides.style.zoom !== 'undefined' ) { - dom.slides.style.zoom = scale; + // Determine scale of content to fit within available space + scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); + + // Respect max/min scale settings + 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 ) { + dom.slides.style.zoom = ''; dom.slides.style.left = ''; dom.slides.style.top = ''; dom.slides.style.bottom = ''; dom.slides.style.right = ''; transformSlides( { layout: '' } ); } - // Apply scale transform as a fallback else { - dom.slides.style.zoom = ''; - dom.slides.style.left = '50%'; - dom.slides.style.top = '50%'; - dom.slides.style.bottom = 'auto'; - dom.slides.style.right = 'auto'; - transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); - } - } - - // Select all slides, vertical and horizontal - var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); - - for( var i = 0, len = slides.length; i < len; i++ ) { - var slide = slides[ i ]; - - // Don't bother updating invisible slides - if( slide.style.display === 'none' ) { - continue; + // Zoom Scaling + // Content remains crisp no matter how much we scale. Side + // effects are minor differences in text layout and iframe + // viewports changing size. A 200x200 iframe viewport in a + // 2x zoomed presentation ends up having a 400x400 viewport. + if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) { + dom.slides.style.zoom = scale; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + // Transform Scaling + // Content layout remains the exact same when scaled up. + // Side effect is content becoming blurred, especially with + // high scale values on ldpi screens. + else { + dom.slides.style.zoom = ''; + dom.slides.style.left = '50%'; + dom.slides.style.top = '50%'; + dom.slides.style.bottom = 'auto'; + dom.slides.style.right = 'auto'; + transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); + } } - if( config.center || slide.classList.contains( 'center' ) ) { - // Vertical stacks are not centred since their section - // children will be - if( slide.classList.contains( 'stack' ) ) { - slide.style.top = 0; + // Select all slides, vertical and horizontal + var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); + + for( var i = 0, len = slides.length; i < len; i++ ) { + var slide = slides[ i ]; + + // Don't bother updating invisible slides + if( slide.style.display === 'none' ) { + continue; + } + + if( config.center || slide.classList.contains( 'center' ) ) { + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) ) { + slide.style.top = 0; + } + else { + slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px'; + } } else { - slide.style.top = Math.max( ( ( size.height - getAbsoluteHeight( slide ) ) / 2 ) - slidePadding, 0 ) + 'px'; + slide.style.top = ''; } - } - else { - slide.style.top = ''; + } + if( oldScale !== scale ) { + dispatchEvent( 'resize', { + 'oldScale': oldScale, + 'scale': scale, + 'size': size + } ); + } } updateProgress(); updateParallax(); + if( isOverview() ) { + updateOverview(); + } + } } @@ -1645,8 +2352,11 @@ /** * Applies layout logic to the contents of all slides in * the presentation. + * + * @param {string|number} width + * @param {string|number} height */ - function layoutSlideContents( width, height, padding ) { + function layoutSlideContents( width, height ) { // Handle sizing of elements with the 'stretch' class toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { @@ -1678,6 +2388,9 @@ * Calculates the computed pixel size of our slides. These * values are based on the width and height configuration * options. + * + * @param {number} [presentationWidth=dom.wrapper.offsetWidth] + * @param {number} [presentationHeight=dom.wrapper.offsetHeight] */ function getComputedSlideSize( presentationWidth, presentationHeight ) { @@ -1715,7 +2428,7 @@ * from the stack. * * @param {HTMLElement} stack The vertical stack element - * @param {int} v Index to memorize + * @param {string|number} [v=0] Index to memorize */ function setPreviousVerticalIndex( stack, v ) { @@ -1779,6 +2492,17 @@ } } ); + // Calculate slide sizes + var margin = 70; + var slideSize = getComputedSlideSize(); + overviewSlideWidth = slideSize.width + margin; + overviewSlideHeight = slideSize.height + margin; + + // Reverse in RTL mode + if( config.rtl ) { + overviewSlideWidth = -overviewSlideWidth; + } + updateSlidesVisibility(); layoutOverview(); updateOverview(); @@ -1802,19 +2526,10 @@ */ function layoutOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } - // Layout slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { hslide.setAttribute( 'data-index-h', h ); - transformElement( hslide, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); if( hslide.classList.contains( 'stack' ) ) { @@ -1822,7 +2537,7 @@ vslide.setAttribute( 'data-index-h', h ); vslide.setAttribute( 'data-index-v', v ); - transformElement( vslide, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } @@ -1830,10 +2545,10 @@ // Layout slide backgrounds toArray( dom.background.childNodes ).forEach( function( hbackground, h ) { - transformElement( hbackground, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) { - transformElement( vbackground, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } ); @@ -1845,20 +2560,14 @@ */ function updateOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } + var vmin = Math.min( window.innerWidth, window.innerHeight ); + var scale = Math.max( vmin / 5, 150 ) / vmin; transformSlides( { overview: [ - 'translateX('+ ( -indexh * slideWidth ) +'px)', - 'translateY('+ ( -indexv * slideHeight ) +'px)', - 'translateZ('+ ( window.innerWidth < 400 ? -1000 : -2500 ) +'px)' + 'scale('+ scale +')', + 'translateX('+ ( -indexh * overviewSlideWidth ) +'px)', + 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)' ].join( ' ' ) } ); @@ -1923,7 +2632,7 @@ /** * Toggles the slide overview mode on and off. * - * @param {Boolean} override Optional flag which overrides the + * @param {Boolean} [override] Flag which overrides the * toggle logic and forcibly sets the desired state. True means * overview is open, false means it's closed. */ @@ -1950,12 +2659,51 @@ } + /** + * Return a hash URL that will resolve to the given slide location. + * + * @param {HTMLElement} [slide=currentSlide] The slide to link to + */ + function locationHash( slide ) { + + var url = '/'; + + // Attempt to create a named link based on the slide's ID + var s = slide || currentSlide; + var id = s ? s.getAttribute( 'id' ) : null; + if( id ) { + id = encodeURIComponent( id ); + } + + var index = getIndices( slide ); + if( !config.fragmentInURL ) { + index.f = undefined; + } + + // If the current slide has an ID, use that as a named link, + // but we don't support named links with a fragment index + if( typeof id === 'string' && id.length && index.f === undefined ) { + url = '/' + id; + } + // Otherwise use the /h/v index + else { + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; + if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase; + if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase ); + if( index.f !== undefined ) url += '/' + index.f; + } + + return url; + + } + /** * Checks if the current or specified slide is vertical * (nested within another slide). * - * @param {HTMLElement} slide [optional] The slide to check + * @param {HTMLElement} [slide=currentSlide] The slide to check * orientation of + * @return {Boolean} */ function isVerticalSlide( slide ) { @@ -1969,15 +2717,15 @@ /** * Handling the fullscreen functionality via the fullscreen API * - * @see https://fullscreen.spec.whatwg.org/ + * @see http://fullscreen.spec.whatwg.org/ * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode */ function enterFullscreen() { - var element = document.body; + var element = document.documentElement; // Check which implementation is available - var requestMethod = element.requestFullScreen || + var requestMethod = element.requestFullscreen || element.webkitRequestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || @@ -1989,6 +2737,32 @@ } + /** + * Shows the mouse pointer after it has been hidden with + * #hideCursor. + */ + function showCursor() { + + if( cursorHidden ) { + cursorHidden = false; + dom.wrapper.style.cursor = ''; + } + + } + + /** + * Hides the mouse pointer when it's on top of the .reveal + * container. + */ + function hideCursor() { + + if( cursorHidden === false ) { + cursorHidden = true; + dom.wrapper.style.cursor = 'none'; + } + + } + /** * Enters the paused mode which fades everything on screen to * black. @@ -2040,6 +2814,8 @@ /** * Checks if we are currently in the paused mode. + * + * @return {Boolean} */ function isPaused() { @@ -2050,7 +2826,7 @@ /** * Toggles the auto slide mode on and off. * - * @param {Boolean} override Optional flag which sets the desired state. + * @param {Boolean} [override] Flag which sets the desired state. * True means autoplay starts, false means it stops. */ @@ -2068,6 +2844,8 @@ /** * Checks if the auto slide mode is currently on. + * + * @return {Boolean} */ function isAutoSliding() { @@ -2080,11 +2858,11 @@ * slide which matches the specified horizontal and vertical * indices. * - * @param {int} h Horizontal index of the target slide - * @param {int} v Vertical index of the target slide - * @param {int} f Optional index of a fragment within the + * @param {number} [h=indexh] Horizontal index of the target slide + * @param {number} [v=indexv] Vertical index of the target slide + * @param {number} [f] Index of a fragment within the * target slide to activate - * @param {int} o Optional origin for use in multimaster environments + * @param {number} [o] Origin for use in multimaster environments */ function slide( h, v, f, o ) { @@ -2094,6 +2872,9 @@ // Query all horizontal slides in the deck var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + // Abort if there are no slides + if( horizontalSlides.length === 0 ) return; + // If no vertical index is specified and the upcoming slide is a // stack, resume at its previous vertical index if( v === undefined && !isOverview() ) { @@ -2124,6 +2905,54 @@ layout(); + // Update the overview if it's currently active + if( isOverview() ) { + updateOverview(); + } + + // Find the current horizontal slide and any possible vertical slides + // within it + var currentHorizontalSlide = horizontalSlides[ indexh ], + currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); + + // Store references to the previous and current slides + currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; + + // Show fragment, if specified + if( typeof f !== 'undefined' ) { + navigateFragment( f ); + } + + // Dispatch an event if the slide changed + var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); + if (!slideChanged) { + // Ensure that the previous slide is never the same as the current + previousSlide = null; + } + + // Solves an edge case where the previous slide maintains the + // 'present' class when navigating between adjacent vertical + // stacks + if( previousSlide && previousSlide !== currentSlide ) { + previousSlide.classList.remove( 'present' ); + previousSlide.setAttribute( 'aria-hidden', 'true' ); + + // Reset all slides upon navigate to home + // Issue: #285 + if ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { + // Launch async task + setTimeout( function () { + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; + for( i in slides ) { + if( slides[i] ) { + // Reset stack + setPreviousVerticalIndex( slides[i], 0 ); + } + } + }, 0 ); + } + } + // Apply the new state stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { // Check if this state existed on the previous slide. If it @@ -2146,26 +2975,6 @@ document.documentElement.classList.remove( stateBefore.pop() ); } - // Update the overview if it's currently active - if( isOverview() ) { - updateOverview(); - } - - // Find the current horizontal slide and any possible vertical slides - // within it - var currentHorizontalSlide = horizontalSlides[ indexh ], - currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); - - // Store references to the previous and current slides - currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; - - // Show fragment, if specified - if( typeof f !== 'undefined' ) { - navigateFragment( f ); - } - - // Dispatch an event if the slide changed - var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); if( slideChanged ) { dispatchEvent( 'slidechanged', { 'indexh': indexh, @@ -2175,33 +2984,6 @@ 'origin': o } ); } - else { - // Ensure that the previous slide is never the same as the current - previousSlide = null; - } - - // Solves an edge case where the previous slide maintains the - // 'present' class when navigating between adjacent vertical - // stacks - if( previousSlide ) { - previousSlide.classList.remove( 'present' ); - previousSlide.setAttribute( 'aria-hidden', 'true' ); - - // Reset all slides upon navigate to home - // Issue: #285 - if ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { - // Launch async task - setTimeout( function () { - var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; - for( i in slides ) { - if( slides[i] ) { - // Reset stack - setPreviousVerticalIndex( slides[i], 0 ); - } - } - }, 0 ); - } - } // Handle embedded content if( slideChanged || !previousSlide ) { @@ -2210,7 +2992,7 @@ } // Announce the current slide contents, for screen readers - dom.statusDiv.textContent = currentSlide.textContent; + dom.statusDiv.textContent = getStatusText( currentSlide ); updateControls(); updateProgress(); @@ -2218,6 +3000,7 @@ updateParallax(); updateSlideNumber(); updateNotes(); + updateFragments(); // Update the URL hash writeURL(); @@ -2256,13 +3039,21 @@ updateControls(); updateProgress(); - updateBackground( true ); updateSlideNumber(); updateSlidesVisibility(); + updateBackground( true ); + updateNotesVisibility(); updateNotes(); formatEmbeddedContent(); - startEmbeddedContent( currentSlide ); + + // Start or stop embedded content depending on global config + if( config.autoPlayMedia === false ) { + stopEmbeddedContent( currentSlide, { unloadIframes: false } ); + } + else { + startEmbeddedContent( currentSlide ); + } if( isOverview() ) { layoutOverview(); @@ -2270,6 +3061,48 @@ } + /** + * Updates reveal.js to keep in sync with new slide attributes. For + * example, if you add a new `data-background-image` you can call + * this to have reveal.js render the new background image. + * + * Similar to #sync() but more efficient when you only need to + * refresh a specific slide. + * + * @param {HTMLElement} slide + */ + function syncSlide( slide ) { + + // Default to the current slide + slide = slide || currentSlide; + + syncBackground( slide ); + syncFragments( slide ); + + loadSlide( slide ); + + updateBackground(); + updateNotes(); + + } + + /** + * Formats the fragments on the given slide so that they have + * valid indices. Call this if fragments are changed in the DOM + * after reveal.js has already initialized. + * + * @param {HTMLElement} slide + * @return {Array} a list of the HTML fragments that were synced + */ + function syncFragments( slide ) { + + // Default to the current slide + slide = slide || currentSlide; + + return sortFragments( slide.querySelectorAll( '.fragment' ) ); + + } + /** * Resets all vertical slides so that only the first * is visible. @@ -2317,16 +3150,33 @@ } + /** + * Randomly shuffles all slides in the deck. + */ + function shuffle() { + + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + slides.forEach( function( slide ) { + + // Insert this slide next to another random slide. This may + // cause the slide to insert before itself but that's fine. + dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] ); + + } ); + + } + /** * Updates one dimension of slides by showing the slide * with the specified index. * - * @param {String} selector A CSS selector that will fetch + * @param {string} selector A CSS selector that will fetch * the group of slides we are working with - * @param {Number} index The index of the slide that should be + * @param {number} index The index of the slide that should be * shown * - * @return {Number} The index of the slide that is now shown, + * @return {number} The index of the slide that is now shown, * might differ from the passed in index if it was out of * bounds. */ @@ -2362,7 +3212,7 @@ element.classList.remove( 'present' ); element.classList.remove( 'future' ); - // https://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute + // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute element.setAttribute( 'hidden', '' ); element.setAttribute( 'aria-hidden', 'true' ); @@ -2382,14 +3232,11 @@ element.classList.add( reverse ? 'future' : 'past' ); if( config.fragments ) { - var pastFragments = toArray( element.querySelectorAll( '.fragment' ) ); - - // Show all fragments on prior slides - while( pastFragments.length ) { - var pastFragment = pastFragments.pop(); - pastFragment.classList.add( 'visible' ); - pastFragment.classList.remove( 'current-fragment' ); - } + // Show all fragments in prior slides + toArray( element.querySelectorAll( '.fragment' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + fragment.classList.remove( 'current-fragment' ); + } ); } } else if( i > index ) { @@ -2397,14 +3244,11 @@ element.classList.add( reverse ? 'past' : 'future' ); if( config.fragments ) { - var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) ); - - // No fragments in future slides should be visible ahead of time - while( futureFragments.length ) { - var futureFragment = futureFragments.pop(); - futureFragment.classList.remove( 'visible' ); - futureFragment.classList.remove( 'current-fragment' ); - } + // Hide all fragments in future slides + toArray( element.querySelectorAll( '.fragment.visible' ) ).forEach( function( fragment ) { + fragment.classList.remove( 'visible' ); + fragment.classList.remove( 'current-fragment' ); + } ); } } } @@ -2451,9 +3295,10 @@ // be visible var viewDistance = isOverview() ? 10 : config.viewDistance; - // Limit view distance on weaker devices + // Shorten the view distance on devices that typically have + // less resources if( isMobileDevice ) { - viewDistance = isOverview() ? 6 : 2; + viewDistance = isOverview() ? 6 : config.mobileViewDistance; } // All slides need to be visible when exporting to PDF @@ -2478,10 +3323,10 @@ // Show the horizontal slide if it's within the view distance if( distanceX < viewDistance ) { - showSlide( horizontalSlide ); + loadSlide( horizontalSlide ); } else { - hideSlide( horizontalSlide ); + unloadSlide( horizontalSlide ); } if( verticalSlidesLength ) { @@ -2494,36 +3339,79 @@ distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy ); if( distanceX + distanceY < viewDistance ) { - showSlide( verticalSlide ); + loadSlide( verticalSlide ); } else { - hideSlide( verticalSlide ); + unloadSlide( verticalSlide ); } } } } + // Flag if there are ANY vertical slides, anywhere in the deck + if( hasVerticalSlides() ) { + dom.wrapper.classList.add( 'has-vertical-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-vertical-slides' ); + } + + // Flag if there are ANY horizontal slides, anywhere in the deck + if( hasHorizontalSlides() ) { + dom.wrapper.classList.add( 'has-horizontal-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-horizontal-slides' ); + } + } } /** - * Pick up notes from the current slide and display tham + * Pick up notes from the current slide and display them * to the viewer. * - * @see `showNotes` config value + * @see {@link config.showNotes} */ function updateNotes() { if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) { - dom.speakerNotes.innerHTML = getSlideNotes() || ''; + dom.speakerNotes.innerHTML = getSlideNotes() || 'No notes on this slide.'; } } + /** + * Updates the visibility of the speaker notes sidebar that + * is used to share annotated slides. The notes sidebar is + * only visible if showNotes is true and there are notes on + * one or more slides in the deck. + */ + function updateNotesVisibility() { + + if( config.showNotes && hasNotes() ) { + dom.wrapper.classList.add( 'show-notes' ); + } + else { + dom.wrapper.classList.remove( 'show-notes' ); + } + + } + + /** + * Checks if there are speaker notes for ANY slide in the + * presentation. + */ + function hasNotes() { + + return dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0; + + } + /** * Updates the progress bar to reflect the current slide. */ @@ -2538,62 +3426,92 @@ } + /** - * Updates the slide number div to reflect the current slide. - * - * The following slide number formats are available: - * "h.v": horizontal . vertical slide number (default) - * "h/v": horizontal / vertical slide number - * "c": flattened slide number - * "c/t": flattened slide number / total slides + * Updates the slide number to match the current slide. */ function updateSlideNumber() { // Update slide number if enabled if( config.slideNumber && dom.slideNumber ) { + dom.slideNumber.innerHTML = getSlideNumber(); + } - var value = []; - var format = 'h.v'; + } + /** + * Returns the HTML string corresponding to the current slide number, + * including formatting. + */ + function getSlideNumber( slide ) { + + var value; + var format = 'h.v'; + if( slide === undefined ) { + slide = currentSlide; + } + + if ( typeof config.slideNumber === 'function' ) { + value = config.slideNumber( slide ); + } else { // Check if a custom number format is available if( typeof config.slideNumber === 'string' ) { format = config.slideNumber; } - switch( format ) { - case 'c': - value.push( getSlidePastCount() + 1 ); - break; - case 'c/t': - value.push( getSlidePastCount() + 1, '/', getTotalSlides() ); - break; - case 'h/v': - value.push( indexh + 1 ); - if( isVerticalSlide() ) value.push( '/', indexv + 1 ); - break; - default: - value.push( indexh + 1 ); - if( isVerticalSlide() ) value.push( '.', indexv + 1 ); + // If there are ONLY vertical slides in this deck, always use + // a flattened slide number + if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) { + format = 'c'; } - dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] ); + value = []; + switch( format ) { + case 'c': + value.push( getSlidePastCount( slide ) + 1 ); + break; + case 'c/t': + value.push( getSlidePastCount( slide ) + 1, '/', getTotalSlides() ); + break; + default: + var indices = getIndices( slide ); + value.push( indices.h + 1 ); + var sep = format === 'h/v' ? '/' : '.'; + if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 ); + } } + var url = '#' + locationHash( slide ); + return formatSlideNumber( value[0], value[1], value[2], url ); + } /** * Applies HTML formatting to a slide number before it's * written to the DOM. + * + * @param {number} a Current slide + * @param {string} delimiter Character to separate slide numbers + * @param {(number|*)} b Total slides + * @param {HTMLElement} [url='#'+locationHash()] The url to link to + * @return {string} HTML string fragment */ - function formatSlideNumber( a, delimiter, b ) { + function formatSlideNumber( a, delimiter, b, url ) { + if( url === undefined ) { + url = '#' + locationHash(); + } if( typeof b === 'number' && !isNaN( b ) ) { - return ''+ a +'' + + return '' + + ''+ a +'' + ''+ delimiter +'' + - ''+ b +''; + ''+ b +'' + + ''; } else { - return ''+ a +''; + return '' + + ''+ a +'' + + ''; } } @@ -2614,34 +3532,57 @@ .concat( dom.controlsNext ).forEach( function( node ) { node.classList.remove( 'enabled' ); node.classList.remove( 'fragmented' ); + + // Set 'disabled' attribute on all directions + node.setAttribute( 'disabled', 'disabled' ); } ); - // Add the 'enabled' class to the available routes - if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + // Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons + if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Prev/next buttons - if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Highlight fragment directions if( currentSlide ) { // Always apply fragment decorator to prev/next buttons - if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Apply fragment decorators to directional buttons based on // what slide axis they are in if( isVerticalSlide( currentSlide ) ) { - if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); } else { - if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + + } + + if( config.controlsTutorial ) { + + // Highlight control arrows with an animation to ensure + // that the viewer knows how to navigate + if( !hasNavigatedDown && routes.down ) { + dom.controlsDownArrow.classList.add( 'highlight' ); + } + else { + dom.controlsDownArrow.classList.remove( 'highlight' ); + + if( !hasNavigatedRight && routes.right && indexv === 0 ) { + dom.controlsRightArrow.classList.add( 'highlight' ); + } + else { + dom.controlsRightArrow.classList.remove( 'highlight' ); + } } } @@ -2652,7 +3593,7 @@ * Updates the background elements to reflect the current * slide. * - * @param {Boolean} includeAll If true, the backgrounds of + * @param {boolean} includeAll If true, the backgrounds of * all vertical slides (not just the present) will be updated. */ function updateBackground( includeAll ) { @@ -2709,30 +3650,30 @@ } ); - // Stop any currently playing video background + // Stop content inside of previous backgrounds if( previousBackground ) { - var previousVideo = previousBackground.querySelector( 'video' ); - if( previousVideo ) previousVideo.pause(); + stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } ); } + // Start content in the current background if( currentBackground ) { - // Start video playback - var currentVideo = currentBackground.querySelector( 'video' ); - if( currentVideo ) { - if( currentVideo.currentTime > 0 ) currentVideo.currentTime = 0; - currentVideo.play(); - } + startEmbeddedContent( currentBackground ); - var backgroundImageURL = currentBackground.style.backgroundImage || ''; + var currentBackgroundContent = currentBackground.querySelector( '.slide-background-content' ); + if( currentBackgroundContent ) { + + var backgroundImageURL = currentBackgroundContent.style.backgroundImage || ''; + + // Restart GIFs (doesn't work in Firefox) + if( /\.gif/i.test( backgroundImageURL ) ) { + currentBackgroundContent.style.backgroundImage = ''; + window.getComputedStyle( currentBackgroundContent ).opacity; + currentBackgroundContent.style.backgroundImage = backgroundImageURL; + } - // Restart GIFs (doesn't work in Firefox) - if( /\.gif/i.test( backgroundImageURL ) ) { - currentBackground.style.backgroundImage = ''; - window.getComputedStyle( currentBackground ).opacity; - currentBackground.style.backgroundImage = backgroundImageURL; } // Don't transition between identical backgrounds. This @@ -2815,7 +3756,7 @@ verticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ); } - verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv * 1 : 0; + verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv : 0; dom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px'; @@ -2823,20 +3764,47 @@ } + /** + * Should the given element be preloaded? + * Decides based on local element attributes and global config. + * + * @param {HTMLElement} element + */ + function shouldPreload( element ) { + + // Prefer an explicit global preload setting + var preload = config.preloadIframes; + + // If no global setting is available, fall back on the element's + // own preload setting + if( typeof preload !== 'boolean' ) { + preload = element.hasAttribute( 'data-preload' ); + } + + return preload; + } + /** * Called when the given slide is within the configured view * distance. Shows the slide element and loads any content * that is set to load lazily (data-src). + * + * @param {HTMLElement} slide Slide to show */ - function showSlide( slide ) { + function loadSlide( slide, options ) { + + options = options || {}; // Show the slide element - slide.style.display = 'block'; + slide.style.display = config.display; // Media elements with data-src attributes - toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) { - element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); - element.removeAttribute( 'data-src' ); + toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) { + if( element.tagName !== 'IFRAME' || shouldPreload( element ) ) { + element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.setAttribute( 'data-lazy-loaded', '' ); + element.removeAttribute( 'data-src' ); + } } ); // Media elements with children @@ -2846,6 +3814,7 @@ toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) { source.setAttribute( 'src', source.getAttribute( 'data-src' ) ); source.removeAttribute( 'data-src' ); + source.setAttribute( 'data-lazy-loaded', '' ); sources += 1; } ); @@ -2858,11 +3827,13 @@ // Show the corresponding background element - var indices = getIndices( slide ); - var background = getSlideBackground( indices.h, indices.v ); + var background = slide.slideBackgroundElement; if( background ) { background.style.display = 'block'; + var backgroundContent = slide.slideBackgroundContentElement; + var backgroundIframe = slide.getAttribute( 'data-background-iframe' ); + // If the background contains media, load it if( background.hasAttribute( 'data-loaded' ) === false ) { background.setAttribute( 'data-loaded', 'true' ); @@ -2870,11 +3841,11 @@ var backgroundImage = slide.getAttribute( 'data-background-image' ), backgroundVideo = slide.getAttribute( 'data-background-video' ), backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), - backgroundIframe = slide.getAttribute( 'data-background-iframe' ); + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ); // Images if( backgroundImage ) { - background.style.backgroundImage = 'url('+ backgroundImage +')'; + backgroundContent.style.backgroundImage = 'url('+ encodeURI( backgroundImage ) +')'; } // Videos else if ( backgroundVideo && !isSpeakerNotes() ) { @@ -2884,51 +3855,102 @@ video.setAttribute( 'loop', '' ); } + if( backgroundVideoMuted ) { + video.muted = true; + } + + // Inline video playback works (at least in Mobile Safari) as + // long as the video is muted and the `playsinline` attribute is + // present + if( isMobileDevice ) { + video.muted = true; + video.autoplay = true; + video.setAttribute( 'playsinline', '' ); + } + // Support comma separated lists of video sources backgroundVideo.split( ',' ).forEach( function( source ) { video.innerHTML += ''; } ); - background.appendChild( video ); + backgroundContent.appendChild( video ); } // Iframes - else if( backgroundIframe ) { + else if( backgroundIframe && options.excludeIframes !== true ) { var iframe = document.createElement( 'iframe' ); - iframe.setAttribute( 'src', backgroundIframe ); - iframe.style.width = '100%'; - iframe.style.height = '100%'; - iframe.style.maxHeight = '100%'; - iframe.style.maxWidth = '100%'; + iframe.setAttribute( 'allowfullscreen', '' ); + iframe.setAttribute( 'mozallowfullscreen', '' ); + iframe.setAttribute( 'webkitallowfullscreen', '' ); + iframe.setAttribute( 'allow', 'autoplay' ); - background.appendChild( iframe ); + iframe.setAttribute( 'data-src', backgroundIframe ); + + iframe.style.width = '100%'; + iframe.style.height = '100%'; + iframe.style.maxHeight = '100%'; + iframe.style.maxWidth = '100%'; + + backgroundContent.appendChild( iframe ); } } + + // Start loading preloadable iframes + var backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' ); + if( backgroundIframeElement ) { + + // Check if this iframe is eligible to be preloaded + if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { + if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) { + backgroundIframeElement.setAttribute( 'src', backgroundIframe ); + } + } + + } + } } /** - * Called when the given slide is moved outside of the - * configured view distance. + * Unloads and hides the given slide. This is called when the + * slide is moved outside of the configured view distance. + * + * @param {HTMLElement} slide */ - function hideSlide( slide ) { + function unloadSlide( slide ) { // Hide the slide element slide.style.display = 'none'; // Hide the corresponding background element - var indices = getIndices( slide ); - var background = getSlideBackground( indices.h, indices.v ); + var background = getSlideBackground( slide ); if( background ) { background.style.display = 'none'; + + // Unload any background iframes + toArray( background.querySelectorAll( 'iframe[src]' ) ).forEach( function( element ) { + element.removeAttribute( 'src' ); + } ); } + // Reset lazy-loaded media elements with src attributes + toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ) ).forEach( function( element ) { + element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); + element.removeAttribute( 'src' ); + } ); + + // Reset lazy-loaded media elements with children + toArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) { + source.setAttribute( 'data-src', source.getAttribute( 'src' ) ); + source.removeAttribute( 'src' ); + } ); + } /** * Determine what available routes there are for navigation. * - * @return {Object} containing four booleans: left/right/up/down + * @return {{left: boolean, right: boolean, up: boolean, down: boolean}} */ function availableRoutes() { @@ -2936,13 +3958,27 @@ verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); var routes = { - left: indexh > 0 || config.loop, - right: indexh < horizontalSlides.length - 1 || config.loop, + left: indexh > 0, + right: indexh < horizontalSlides.length - 1, up: indexv > 0, down: indexv < verticalSlides.length - 1 }; - // reverse horizontal controls for rtl + // Looped presentations can always be navigated as long as + // there are slides available + if( config.loop ) { + if( horizontalSlides.length > 1 ) { + routes.left = true; + routes.right = true; + } + + if( verticalSlides.length > 1 ) { + routes.up = true; + routes.down = true; + } + } + + // Reverse horizontal controls for rtl if( config.rtl ) { var left = routes.left; routes.left = routes.right; @@ -2957,7 +3993,7 @@ * Returns an object describing the available fragment * directions. * - * @return {Object} two boolean properties: prev/next + * @return {{prev: boolean, next: boolean}} */ function availableFragments() { @@ -3002,61 +4038,156 @@ /** * Start playback of any embedded content inside of - * the targeted slide. + * the given element. + * + * @param {HTMLElement} element */ - function startEmbeddedContent( slide ) { + function startEmbeddedContent( element ) { + + if( element && !isSpeakerNotes() ) { - if( slide && !isSpeakerNotes() ) { // Restart GIFs - toArray( slide.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { // Setting the same unchanged source like this was confirmed // to work in Chrome, FF & Safari el.setAttribute( 'src', el.getAttribute( 'src' ) ); } ); // HTML5 media elements - toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { - if( el.hasAttribute( 'data-autoplay' ) && typeof el.play === 'function' ) { - el.play(); + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' ); + } + + if( autoplay && typeof el.play === 'function' ) { + + // If the media is ready, start playback + if( el.readyState > 1 ) { + startEmbeddedMedia( { target: el } ); + } + // Mobile devices never fire a loaded event so instead + // of waiting, we initiate playback + else if( isMobileDevice ) { + var promise = el.play(); + + // If autoplay does not work, ensure that the controls are visible so + // that the viewer can start the media on their own + if( promise && typeof promise.catch === 'function' && el.controls === false ) { + promise.catch( function() { + el.controls = true; + + // Once the video does start playing, hide the controls again + el.addEventListener( 'play', function() { + el.controls = false; + } ); + } ); + } + } + // If the media isn't loaded, wait before playing + else { + el.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes + el.addEventListener( 'loadeddata', startEmbeddedMedia ); + } + } } ); // Normal iframes - toArray( slide.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + startEmbeddedIframe( { target: el } ); } ); // Lazy loading iframes - toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) { el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes el.addEventListener( 'load', startEmbeddedIframe ); el.setAttribute( 'src', el.getAttribute( 'data-src' ) ); } } ); + } } + /** + * Starts playing an embedded video/audio element after + * it has finished loading. + * + * @param {object} event + */ + function startEmbeddedMedia( event ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + event.target.currentTime = 0; + event.target.play(); + } + + event.target.removeEventListener( 'loadeddata', startEmbeddedMedia ); + + } + /** * "Starts" the content of an embedded iframe using the - * postmessage API. + * postMessage API. + * + * @param {object} event */ function startEmbeddedIframe( event ) { var iframe = event.target; - // YouTube postMessage API - if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) { - iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); - } - // Vimeo postMessage API - else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && iframe.hasAttribute( 'data-autoplay' ) ) { - iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); - } - // Generic postMessage API - else { - iframe.contentWindow.postMessage( 'slide:start', '*' ); + if( iframe && iframe.contentWindow ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' ); + } + + // YouTube postMessage API + if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); + } + // Vimeo postMessage API + else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); + } + // Generic postMessage API + else { + iframe.contentWindow.postMessage( 'slide:start', '*' ); + } + + } + } } @@ -3064,44 +4195,54 @@ /** * Stop playback of any embedded content inside of * the targeted slide. + * + * @param {HTMLElement} element */ - function stopEmbeddedContent( slide ) { + function stopEmbeddedContent( element, options ) { - if( slide && slide.parentNode ) { + options = extend( { + // Defaults + unloadIframes: true + }, options || {} ); + + if( element && element.parentNode ) { // HTML5 media elements - toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) { + el.setAttribute('data-paused-by-reveal', ''); el.pause(); } } ); // Generic postMessage API for non-lazy loaded iframes - toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) { - el.contentWindow.postMessage( 'slide:stop', '*' ); + toArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) { + if( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' ); el.removeEventListener( 'load', startEmbeddedIframe ); }); // YouTube postMessage API - toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { - if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) { + toArray( element.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' ); } }); // Vimeo postMessage API - toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { - if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) { + toArray( element.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { el.contentWindow.postMessage( '{"method":"pause"}', '*' ); } }); - // Lazy loading iframes - toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { - // Only removing the src doesn't actually unload the frame - // in all browsers (Firefox) so we set it to blank first - el.setAttribute( 'src', 'about:blank' ); - el.removeAttribute( 'src' ); - } ); + if( options.unloadIframes === true ) { + // Unload lazy-loaded iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + // Only removing the src doesn't actually unload the frame + // in all browsers (Firefox) so we set it to blank first + el.setAttribute( 'src', 'about:blank' ); + el.removeAttribute( 'src' ); + } ); + } } } @@ -3109,8 +4250,16 @@ /** * Returns the number of past slides. This can be used as a global * flattened index for slides. + * + * @param {HTMLElement} [slide=currentSlide] The slide we're counting before + * + * @return {number} Past slide count */ - function getSlidePastCount() { + function getSlidePastCount( slide ) { + + if( slide === undefined ) { + slide = currentSlide; + } var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); @@ -3126,7 +4275,7 @@ for( var j = 0; j < verticalSlides.length; j++ ) { // Stop as soon as we arrive at the present - if( verticalSlides[j].classList.contains( 'present' ) ) { + if( verticalSlides[j] === slide ) { break mainLoop; } @@ -3135,7 +4284,7 @@ } // Stop as soon as we arrive at the present - if( horizontalSlide.classList.contains( 'present' ) ) { + if( horizontalSlide === slide ) { break; } @@ -3153,6 +4302,8 @@ /** * Returns a value ranging from 0-1 that represents * how far into the presentation we have navigated. + * + * @return {number} */ function getProgress() { @@ -3179,13 +4330,15 @@ } - return pastCount / ( totalCount - 1 ); + return Math.min( pastCount / ( totalCount - 1 ), 1 ); } /** * Checks if this presentation is running inside of the * speaker notes window. + * + * @return {boolean} */ function isSpeakerNotes() { @@ -3204,21 +4357,27 @@ var bits = hash.slice( 2 ).split( '/' ), name = hash.replace( /#|\//gi, '' ); - // If the first bit is invalid and there is a name we can - // assume that this is a named link - if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) { + // If the first bit is not fully numeric and there is a name we + // can assume that this is a named link + if( !/^[0-9]*$/.test( bits[0] ) && name.length ) { var element; // Ensure the named link is a valid HTML ID attribute - if( /^[a-zA-Z][\w:.-]*$/.test( name ) ) { - // Find the slide with the specified ID - element = document.getElementById( name ); + try { + element = document.getElementById( decodeURIComponent( name ) ); } + catch ( error ) { } + + // Ensure that we're not already on a slide with the same name + var isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false; if( element ) { - // Find the position of the named slide and navigate to it - var indices = Reveal.getIndices( element ); - slide( indices.h, indices.v ); + // If the slide exists and is not the current slide... + if ( !isSameNameAsCurrentSlide ) { + // ...find the position of the named slide and navigate to it + var indices = Reveal.getIndices(element); + slide(indices.h, indices.v); + } } // If the slide doesn't exist, navigate to the current slide else { @@ -3226,12 +4385,22 @@ } } else { - // Read the index components of the hash - var h = parseInt( bits[0], 10 ) || 0, - v = parseInt( bits[1], 10 ) || 0; + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; - if( h !== indexh || v !== indexv ) { - slide( h, v ); + // Read the index components of the hash + var h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0, + v = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0, + f; + + if( config.fragmentInURL ) { + f = parseInt( bits[2], 10 ); + if( isNaN( f ) ) { + f = undefined; + } + } + + if( h !== indexh || v !== indexv || f !== undefined ) { + slide( h, v, f ); } } @@ -3241,54 +4410,47 @@ * Updates the page URL (hash) to reflect the current * state. * - * @param {Number} delay The time in ms to wait before + * @param {number} delay The time in ms to wait before * writing the hash */ function writeURL( delay ) { - if( config.history ) { + // Make sure there's never more than one timeout running + clearTimeout( writeURLTimeout ); - // Make sure there's never more than one timeout running - clearTimeout( writeURLTimeout ); - - // If a delay is specified, timeout this call - if( typeof delay === 'number' ) { - writeURLTimeout = setTimeout( writeURL, delay ); + // If a delay is specified, timeout this call + if( typeof delay === 'number' ) { + writeURLTimeout = setTimeout( writeURL, delay ); + } + else if( currentSlide ) { + // If we're configured to push to history OR the history + // API is not avaialble. + if( config.history || !window.history ) { + window.location.hash = locationHash(); } - else if( currentSlide ) { - var url = '/'; - - // Attempt to create a named link based on the slide's ID - var id = currentSlide.getAttribute( 'id' ); - if( id ) { - id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' ); - } - - // If the current slide has an ID, use that as a named link - if( typeof id === 'string' && id.length ) { - url = '/' + id; - } - // Otherwise use the /h/v index - else { - if( indexh > 0 || indexv > 0 ) url += indexh; - if( indexv > 0 ) url += '/' + indexv; - } - - window.location.hash = url; + // If we're configured to reflect the current slide in the + // URL without pushing to history. + else if( config.hash ) { + window.history.replaceState( null, null, '#' + locationHash() ); + } + // If history and hash are both disabled, a hash may still + // be added to the URL by clicking on a href with a hash + // target. Counter this by always removing the hash. + else { + window.history.replaceState( null, null, window.location.pathname + window.location.search ); } } } - /** - * Retrieves the h/v location of the current, or specified, - * slide. + * Retrieves the h/v location and fragment of the current, + * or specified, slide. * - * @param {HTMLElement} slide If specified, the returned + * @param {HTMLElement} [slide] If specified, the returned * index will be for this slide rather than the currently * active one * - * @return {Object} { h: , v: , f: } + * @return {{h: number, v: number, f: number}} */ function getIndices( slide ) { @@ -3334,17 +4496,86 @@ } + /** + * Retrieves all slides in this presentation. + */ + function getSlides() { + + return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ) ); + + } + + /** + * Returns a list of all horizontal slides in the deck. Each + * vertical stack is included as one horizontal slide in the + * resulting array. + */ + function getHorizontalSlides() { + + return toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + } + + /** + * Returns all vertical slides that exist within this deck. + */ + function getVerticalSlides() { + + return toArray( dom.wrapper.querySelectorAll( '.slides>section>section' ) ); + + } + + /** + * Returns true if there are at least two horizontal slides. + */ + function hasHorizontalSlides() { + + return getHorizontalSlides().length > 1; + } + + /** + * Returns true if there are at least two vertical slides. + */ + function hasVerticalSlides() { + + return getVerticalSlides().length > 1; + + } + + /** + * Returns an array of objects where each object represents the + * attributes on its respective slide. + */ + function getSlidesAttributes() { + + return getSlides().map( function( slide ) { + + var attributes = {}; + for( var i = 0; i < slide.attributes.length; i++ ) { + var attribute = slide.attributes[ i ]; + attributes[ attribute.name ] = attribute.value; + } + return attributes; + + } ); + + } + /** * Retrieves the total number of slides in this presentation. + * + * @return {number} */ function getTotalSlides() { - return dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; + return getSlides().length; } /** * Returns the slide element matching the specified index. + * + * @return {HTMLElement} */ function getSlide( x, y ) { @@ -3364,31 +4595,20 @@ * All slides, even the ones with no background properties * defined, have a background element so as long as the * index is valid an element will be returned. + * + * @param {mixed} x Horizontal background index OR a slide + * HTML element + * @param {number} y Vertical background index + * @return {(HTMLElement[]|*)} */ function getSlideBackground( x, y ) { - // When printing to PDF the slide backgrounds are nested - // inside of the slides - if( isPrintingPDF() ) { - var slide = getSlide( x, y ); - if( slide ) { - var background = slide.querySelector( '.slide-background' ); - if( background && background.parentNode === slide ) { - return background; - } - } - - return undefined; + var slide = typeof x === 'number' ? getSlide( x, y ) : x; + if( slide ) { + return slide.slideBackgroundElement; } - var horizontalBackground = dom.wrapper.querySelectorAll( '.backgrounds>.slide-background' )[ x ]; - var verticalBackgrounds = horizontalBackground && horizontalBackground.querySelectorAll( '.slide-background' ); - - if( verticalBackgrounds && verticalBackgrounds.length && typeof y === 'number' ) { - return verticalBackgrounds ? verticalBackgrounds[ y ] : undefined; - } - - return horizontalBackground; + return undefined; } @@ -3397,6 +4617,9 @@ * defined in two ways: * 1. As a data-notes attribute on the slide
* 2. As an