mirror of
https://github.com/hakimel/reveal.js.git
synced 2025-09-13 16:12:03 +02:00
Compare commits
195 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c513500269 | ||
|
0ef90e0dcf | ||
|
7f9662edab | ||
|
7b53ff0b9d | ||
|
04a2337041 | ||
|
54e59d02d4 | ||
|
dd6c605826 | ||
|
819d2488aa | ||
|
e08d0572bd | ||
|
d5c2d0c03e | ||
|
cd3fc43e77 | ||
|
de2a95d0ac | ||
|
2b85a7de41 | ||
|
7366f607ce | ||
|
5bc5435b61 | ||
|
e6e33f17f8 | ||
|
74d342daf7 | ||
|
54f1fcf381 | ||
|
21983c7b7f | ||
|
aaa03dc2c7 | ||
|
afd10f6886 | ||
|
d9512172e2 | ||
|
f57de200ec | ||
|
e7c5de23d7 | ||
|
32f0d328ff | ||
|
aeec8264ab | ||
|
9ca0ed26db | ||
|
534da10886 | ||
|
0c1a4add3a | ||
|
a6969770bd | ||
|
28ee78e7e4 | ||
|
6433cebdc7 | ||
|
0e86400943 | ||
|
421afe6368 | ||
|
2b9f8e6c0d | ||
|
76a60eea31 | ||
|
acbefa1190 | ||
|
9608ac9d2d | ||
|
e2ba1c1142 | ||
|
eaf525c9a7 | ||
|
2118a6295d | ||
|
bf2c95b546 | ||
|
bdad679715 | ||
|
1053dafc1e | ||
|
fa98bacef6 | ||
|
1f17bdd5c4 | ||
|
4ca99d4264 | ||
|
8980f8accb | ||
|
5fc7fc89f7 | ||
|
939da8834e | ||
|
6d1a78091a | ||
|
677ef9919b | ||
|
0163ffcb43 | ||
|
0a5bf91c9a | ||
|
91b7df3e23 | ||
|
9b00001870 | ||
|
2d44d1e25f | ||
|
a91d401919 | ||
|
681115ea9c | ||
|
8f69e72cf7 | ||
|
dff061f3e6 | ||
|
9de23f022c | ||
|
60d70f483c | ||
|
5fba807843 | ||
|
f6b4561c0e | ||
|
60f2eb9fb3 | ||
|
5d979fdf4a | ||
|
6a03f65e97 | ||
|
2b6c61060b | ||
|
5a782e54f8 | ||
|
01f40cc8d9 | ||
|
beed1ab854 | ||
|
2b38f523f2 | ||
|
ba7aeb36ef | ||
|
182c4abacb | ||
|
eb0a8cfa95 | ||
|
6db42971ea | ||
|
6bcdd8e240 | ||
|
f5574e173b | ||
|
43da46f06b | ||
|
4f08e2aeb0 | ||
|
aa8e27252d | ||
|
6053fe5a97 | ||
|
aaf9da450f | ||
|
31bbfe9398 | ||
|
26108b0694 | ||
|
8554050933 | ||
|
049880dba3 | ||
|
2e024b5b3e | ||
|
eaca6465fa | ||
|
e7b8033a10 | ||
|
445cd12265 | ||
|
eccffbe859 | ||
|
507cab9dc9 | ||
|
ba6bec22c4 | ||
|
b76e42979a | ||
|
affe100075 | ||
|
ac5834a542 | ||
|
4031a21ea8 | ||
|
1013ff7ba5 | ||
|
ec1c111359 | ||
|
d5e1f7e493 | ||
|
19b67aab13 | ||
|
ebb834f4b0 | ||
|
5a6837358b | ||
|
5909c223c6 | ||
|
2caac75c62 | ||
|
0acae19d19 | ||
|
cde5362db3 | ||
|
eee6e5f964 | ||
|
83e4a1f0ac | ||
|
75380e04a9 | ||
|
1675002c7a | ||
|
7379fb3652 | ||
|
27f48f6440 | ||
|
cad7ea0f28 | ||
|
a78a683dda | ||
|
58c1a1e861 | ||
|
e94fc0bdeb | ||
|
6188306066 | ||
|
d138f0fe2b | ||
|
727cbf4b25 | ||
|
f59e64a571 | ||
|
d3e2a95d77 | ||
|
0d9a6419bb | ||
|
8dc7ae85a1 | ||
|
2c78eea0ca | ||
|
831236890c | ||
|
0cd3b8d430 | ||
|
732ed921eb | ||
|
be76bf3d23 | ||
|
87529c4adc | ||
|
b72bee3219 | ||
|
cd66a6574d | ||
|
b6030d5c7a | ||
|
a77842521e | ||
|
81cda2145f | ||
|
7f3bf1409e | ||
|
67add62316 | ||
|
f27db1dc84 | ||
|
ba8fc0bc35 | ||
|
a53b315e24 | ||
|
38b229a22f | ||
|
17854e892b | ||
|
896e0b7010 | ||
|
284610dab0 | ||
|
a30cdaaffc | ||
|
9736509bae | ||
|
510ac8e114 | ||
|
c3bc0f70ef | ||
|
2ae803efb6 | ||
|
401c554c80 | ||
|
4d3c3c8d3f | ||
|
36950c8149 | ||
|
614b8cde66 | ||
|
4805234a41 | ||
|
02f783b68d | ||
|
e8507de1e4 | ||
|
cbd59efef8 | ||
|
9856f57db5 | ||
|
73eb66bf71 | ||
|
3fa01ab107 | ||
|
f7ac5fb159 | ||
|
2c088681ec | ||
|
daecc258d7 | ||
|
5b2b3fa3df | ||
|
bccb6e68c7 | ||
|
2313dfef6d | ||
|
c7c7735e7a | ||
|
1ac6386eef | ||
|
b162a54c2d | ||
|
91ff92e211 | ||
|
c6a75117f6 | ||
|
adc9ad19ce | ||
|
f6dc531298 | ||
|
01000e1478 | ||
|
0c231fec38 | ||
|
9065114ef0 | ||
|
91c3056a62 | ||
|
abbd027124 | ||
|
4a4ffd518e | ||
|
bdff009c74 | ||
|
2026c9645c | ||
|
833e298617 | ||
|
11ee006f9d | ||
|
60bf197890 | ||
|
853efe5043 | ||
|
59c9b3c596 | ||
|
e6f444a307 | ||
|
d2faad4730 | ||
|
f9527d31b4 | ||
|
233160ff23 | ||
|
b36e8d5c4e | ||
|
9edb5f6da0 | ||
|
a6453a0fb0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
.svn
|
||||
log/*.log
|
||||
tmp/**
|
||||
node_modules/
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (C) 2011 Hakim El Hattab, http://hakim.se
|
||||
Copyright (C) 2012 Hakim El Hattab, http://hakim.se
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
250
README.md
250
README.md
@@ -1,11 +1,251 @@
|
||||
# CSS 3D Slideshow
|
||||
# reveal.js
|
||||
|
||||
Requires a browser with support for CSS 3D transforms, such as Chrome or Safari.
|
||||
A CSS 3D slideshow tool for quickly creating good looking HTML presentations. Doesn't _rely_ on any external libraries but [highlight.js](http://softwaremaniacs.org/soft/highlight/en/description/) is included by default for code highlighting.
|
||||
|
||||
Curious about how this looks in action? [Check out the demo page.](http://hakim.se/experiments/css/slideshow/).
|
||||
Note that this requires a browser with support for CSS 3D transforms and ``classList``. If CSS 3D support is not detected, the presentation will degrade to less exciting 2D transitions. A [classList polyfill](http://purl.eligrey.com/github/classList.js/blob/master/classList.js) is incuded to make this work in < iOS 5, < Safari 5.1 and IE.
|
||||
|
||||
# License
|
||||
Curious about how it looks in action? [Check out the demo page](http://lab.hakim.se/reveal-js/).
|
||||
|
||||
## Usage
|
||||
|
||||
### Markup
|
||||
|
||||
Markup heirarchy needs to be ``<div class="reveal"> <div class="slides"> <section>`` where the ``<section>`` represents one slide and can be repeated indefinitely. If you place multiple ``<section>``'s inside of another ``<section>`` they will be shown as vertical slides. For example:
|
||||
|
||||
```html
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
<section>Single Horizontal Slide</section>
|
||||
<section>
|
||||
<section>Vertical Slide 1</section>
|
||||
<section>Vertical Slide 2</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
At the end of your page, after ``<script src="js/reveal.js"></script>``, you need to initialize reveal by running the following code. Note that all config values are optional and will default as specified below.
|
||||
|
||||
```javascript
|
||||
Reveal.initialize({
|
||||
// Display controls in the bottom right corner
|
||||
controls: true,
|
||||
|
||||
// Display a presentation progress bar
|
||||
progress: true,
|
||||
|
||||
// Push each slide change to the browser history
|
||||
history: false,
|
||||
|
||||
// Enable keyboard shortcuts for navigation
|
||||
keyboard: true,
|
||||
|
||||
// Loop the presentation
|
||||
loop: false,
|
||||
|
||||
// Number of milliseconds between automatically proceeding to the
|
||||
// next slide, disabled when set to 0
|
||||
autoSlide: 0,
|
||||
|
||||
// Enable slide navigation via mouse wheel
|
||||
mouseWheel: true,
|
||||
|
||||
// Apply a 3D roll to links on hover
|
||||
rollingLinks: true,
|
||||
|
||||
// UI style
|
||||
theme: 'default', // default/neon/beige
|
||||
|
||||
// Transition style
|
||||
transition: 'default' // default/cube/page/concave/linear(2d)
|
||||
});
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
The Reveal class provides a minimal JavaScript API for controlling its navigation:
|
||||
|
||||
- Reveal.navigateTo( indexh, indexv );
|
||||
- Reveal.navigateLeft();
|
||||
- Reveal.navigateRight();
|
||||
- Reveal.navigateUp();
|
||||
- Reveal.navigateDown();
|
||||
- Reveal.navigatePrev();
|
||||
- Reveal.navigateNext();
|
||||
- Reveal.toggleOverview();
|
||||
|
||||
### States
|
||||
|
||||
If you set ``data-state="somestate"`` on a slide ``<section>``, "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:
|
||||
|
||||
```javascript
|
||||
Reveal.addEventListener( 'somestate', function() {
|
||||
// TODO: Sprinkle magic
|
||||
}, false );
|
||||
```
|
||||
|
||||
### Slide change event
|
||||
|
||||
An '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.
|
||||
|
||||
```javascript
|
||||
Reveal.addEventListener( 'slidechanged', function( event ) {
|
||||
// event.previousSlide, event.currentSlide, event.indexh, event.indexv
|
||||
} );
|
||||
```
|
||||
|
||||
### Fragment events
|
||||
|
||||
When a slide fragment is either shown or hidden reveal.js will dispatch an event.
|
||||
|
||||
```javascript
|
||||
Reveal.addEventListener( 'fragmentshown', function( event ) {
|
||||
// event.fragment = the fragment DOM element
|
||||
} );
|
||||
Reveal.addEventListener( 'fragmenthidden', function( event ) {
|
||||
// event.fragment = the fragment DOM element
|
||||
} );
|
||||
```
|
||||
|
||||
### 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)
|
||||
|
||||
## Speaker Notes
|
||||
|
||||
If you're interested in using speaker notes, reveal.js comes with a Node server that allows you to deliver your presentation in one browser while viewing speaker notes in another.
|
||||
|
||||
To include speaker notes in your presentation, simply add an `<aside class="notes">` element to any slide. These notes will be hidden in the main presentation view.
|
||||
|
||||
You'll also need to [install Node.js](http://nodejs.org/); then, install the server dependencies by running `npm install`.
|
||||
|
||||
Once Node.js and the dependencies are installed, run the following command from the root directory:
|
||||
|
||||
node plugin/speakernotes
|
||||
|
||||
By default, the slides will be served at [localhost:1947](http://localhost:1947).
|
||||
|
||||
You can change the appearance of the speaker notes by editing the file at `plugin/speakernotes/notes.html`.
|
||||
|
||||
### Known Issues
|
||||
|
||||
- The notes page is supposed to show the current slide and the next slide, but when it first starts, it always shows the first slide in both positions.
|
||||
|
||||
## Examples
|
||||
|
||||
* http://lab.hakim.se/reveal-js/ (original)
|
||||
* http://www.ideapolisagency.com/ by [@achrafkassioui](http://twitter.com/achrafkassioui)
|
||||
* http://lucienfrelin.com/ by [@lucienfrelin](http://twitter.com/lucienfrelin)
|
||||
* http://creatorrr.github.com/ThePoet/
|
||||
* http://moduscreate.com/ by [@ModusCreate](https://twitter.com/ModusCreate)
|
||||
* http://idea.diwank.name/ by [Diwank Singh](http://diwank.name/)
|
||||
* [Webapp Development Stack & Tooling](http://dl.dropbox.com/u/39519/talks/jquk-tooling%2Bappstack/index.html) by [Paul Irish](https://github.com/paulirish)
|
||||
* [Lock-free algorithms](http://concurrencykit.org/presentations/lockfree_introduction/) by Samy Al Bahra
|
||||
* [Not Your Average Drag and Drop](http://www.thecssninja.com/talks/not_your_average_dnd/) by [Ryan Seddon](https://github.com/ryanseddon)
|
||||
* [Elasticsearch](http://spinscale.github.com/elasticsearch/2012-03-jugm.html) by [@spinscale](http://twitter.com/spinscale)
|
||||
* [JavaScript Tooling](http://dl.dropbox.com/u/39519/talks/jsconf-tools/index.html) by [Paul Irish](https://github.com/paulirish)
|
||||
* [The Graphical Web: Fostering Creativity](http://vhardy.github.com/presentations/html5-community-meet-up-2012/) by [Vincent Hardy](https://github.com/vhardy)
|
||||
* [Mobile Web Programming is a Bloody Mess](http://westcoastlogic.com/slides/debug-mobile/) by [Brian LeRoux](https://github.com/brianleroux)
|
||||
* [Bio Database Access and Sequence Alignment](http://www.philipbjorge.com/bioinformatics-presentation/) by [Philip Bjorge](https://github.com/philipbjorge)
|
||||
* [Web vs Native](http://prez.mahemoff.com/state-native/) by [Michael Mahemoff](https://github.com/mahemoff)
|
||||
* [Continuously Integrated JS Development](http://trodrigues.net/presentations/buster-ci/) by [Tiago Rodrigues](https://github.com/trodrigues)
|
||||
* [To be Future Friendly is to be Device Agnostic](http://dl.dropbox.com/u/409429/presentations/toster-2012/index.html) by [Joe McCann](https://github.com/joemccann)
|
||||
* [The Web Development Workflow of 2013](http://dl.dropbox.com/u/39519/talks/fluent/index.html) by [Paul Irish](https://github.com/paulirish)
|
||||
* [How To Cope With Graphical Challenges Using Latest Web Technologies](http://alexw.me/playground/slideshows/w3c_netcraft/) by [Alex Wolkov](https://github.com/altryne)
|
||||
* [Going Deeper with jQuery Mobile](http://andymatthews.net/downloads/presentations/going-deeper-with-jquery-mobile/) by [Andy Matthews](https://github.com/commadelimited)
|
||||
* [Studio Nord](http://studionord.org)
|
||||
* [Herrljunga Cider](http://herrljungacider.se/uk/campaign/)
|
||||
|
||||
|
||||
[Send me a link](http://hakim.se/about/contact) if you used reveal.js for a project or presentation.
|
||||
|
||||
|
||||
## History
|
||||
|
||||
#### 1.4 (master/beta)
|
||||
- Main #reveal container is now selected via a class instead of ID
|
||||
- API methods for adding or removing all event listeners
|
||||
- The ```slidechange``` event now includes currentSlide and previousSlide
|
||||
- Fixed bug where 'slidechange' was firing twice when history was enabled
|
||||
- Folder structure updates for scalability (see /lib & /plugin)
|
||||
- Slide notes by [rmurphey](https://github.com/rmurphey)
|
||||
- Bumped up default font-size for code samples
|
||||
- Added beige theme
|
||||
- Added 'autoSlide' config
|
||||
- Bug fix: The 'slidechanged' event is now firing upon 'hashchange'. Thanks [basecode](https://github.com/basecode)
|
||||
- Bug fix: JS error when the 'progress' option was true but there was no progress DOM element
|
||||
- ```keyboard``` config flag for disabling all keyboard navigation
|
||||
|
||||
#### 1.3
|
||||
- Revised keyboard shortcuts, including ESC for overview, N for next, P for previous. Thanks [mahemoff](https://github.com/mahemoff)
|
||||
- Added support for looped presentations via config
|
||||
- Fixed IE9 fallback
|
||||
- Added event binding methods (Reveal.addEventListener, Reveal.removeEventListener)
|
||||
- Added 'slidechanged' event
|
||||
- Added print styles. Thanks [skypanther](https://github.com/skypanther)
|
||||
- The address bar now hides automatically on mobile browsers
|
||||
- Space and return keys can be used to exit the overview mode
|
||||
- Events for fragment states ('fragmentshown'/'fragmenthidden')
|
||||
- Support for swipe navigation on touch devices. Thanks [akiersky](https://github.com/akiersky)
|
||||
- Support for pinch to overview on touch devices
|
||||
|
||||
#### 1.2
|
||||
|
||||
- Big changes to DOM structure:
|
||||
- Previous #main wrapper is now called #reveal
|
||||
- Slides were moved one level deeper, into #reveal .slides
|
||||
- Controls and progress bar were moved into #reveal
|
||||
- CSS is now much more explicit, rooted at #reveal, to prevent conflicts
|
||||
- Config option for disabling updates to URL, defaults to true
|
||||
- Anchors with image children no longer rotate in 3D on hover
|
||||
- Support for mouse wheel navigation ([naugtur](https://github.com/naugtur))
|
||||
- Delayed updates to URL hash to work around a bug in Chrome
|
||||
- Included a classList polyfill for IE9
|
||||
- Support for wireless presenter keys
|
||||
- States can now be applied as classes on the document element by adding data-state on a slide
|
||||
|
||||
#### 1.1
|
||||
|
||||
- Added an optional presentation progress bar
|
||||
- Images wrapped in anchors no longer unexpectedly flip in 3D
|
||||
- Slides that contain other slides are given the 'stack' class
|
||||
- Added 'transition' option for specifying transition styles
|
||||
- Added 'theme' option for specifying UI styles
|
||||
- New transitions: 'box' & 'page'
|
||||
- New theme: 'neon'
|
||||
|
||||
#### 1.0
|
||||
|
||||
- New and improved style
|
||||
- Added controls in bottom right which indicate where you can navigate
|
||||
- Reveal views in iteratively by giving them the .fragment class
|
||||
- Code sample syntax highlighting thanks to [highlight.js](http://softwaremaniacs.org/soft/highlight/en/description/)
|
||||
- Initialization options (toggling controls, toggling rolling links, transition theme)
|
||||
|
||||
#### 0.3
|
||||
|
||||
- Added licensing terms
|
||||
- Fixed broken links on touch devices
|
||||
|
||||
#### 0.2
|
||||
|
||||
- Refactored code and added inline documentation
|
||||
- Slides now have unique URL's
|
||||
- A basic API to invoke navigation was added
|
||||
|
||||
#### 0.1
|
||||
|
||||
- First release
|
||||
- Transitions and a white theme
|
||||
|
||||
## License
|
||||
|
||||
MIT licensed
|
||||
|
||||
Copyright (C) 2011 Hakim El Hattab, http://hakim.se
|
||||
Copyright (C) 2012 Hakim El Hattab, http://hakim.se
|
Binary file not shown.
Before Width: | Height: | Size: 55 KiB |
1051
css/main.css
1051
css/main.css
File diff suppressed because it is too large
Load Diff
167
css/print.css
Normal file
167
css/print.css
Normal file
@@ -0,0 +1,167 @@
|
||||
/* Default Print Stylesheet Template
|
||||
by Rob Glazebrook of CSSnewbie.com
|
||||
Last Updated: June 4, 2008
|
||||
|
||||
Feel free (nay, compelled) to edit, append, and
|
||||
manipulate this file as you see fit. */
|
||||
|
||||
|
||||
/* SECTION 1: Set default width, margin, float, and
|
||||
background. This prevents elements from extending
|
||||
beyond the edge of the printed page, and prevents
|
||||
unnecessary background images from printing */
|
||||
body {
|
||||
background: #fff url(none);
|
||||
font-size: 13pt;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: 0;
|
||||
margin: 0 5%;
|
||||
padding: 0;
|
||||
float: none !important;
|
||||
overflow: visible;
|
||||
}
|
||||
html {
|
||||
background: #fff;
|
||||
width: auto;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* SECTION 2: Remove any elements not needed in print.
|
||||
This would include navigation, ads, sidebars, etc. */
|
||||
.nestedarrow,
|
||||
.controls a,
|
||||
.reveal .progress span,
|
||||
.reveal.overview {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/* SECTION 3: Set body font face, size, and color.
|
||||
Consider using a serif font for readability. */
|
||||
body, p, td, li, div, a {
|
||||
font-size: 13pt;
|
||||
font-family: Georgia, "Times New Roman", Times, serif !important;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* SECTION 4: Set heading font face, sizes, and color.
|
||||
Diffrentiate your headings from your body text.
|
||||
Perhaps use a large sans-serif for distinction. */
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
color: #000!important;
|
||||
height: auto;
|
||||
line-height: normal;
|
||||
font-family: Georgia, "Times New Roman", Times, serif !important;
|
||||
text-shadow: 0 0 0 #000 !important;
|
||||
text-align: left;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
/* Need to reduce the size of the fonts for printing */
|
||||
h1 { font-size: 26pt !important; }
|
||||
h2 { font-size: 22pt !important; }
|
||||
h3 { font-size: 20pt !important; }
|
||||
h4 { font-size: 20pt !important; font-variant: small-caps; }
|
||||
h5 { font-size: 19pt !important; }
|
||||
h6 { font-size: 18pt !important; font-style: italic; }
|
||||
|
||||
/* SECTION 5: Make hyperlinks more usable.
|
||||
Ensure links are underlined, and consider appending
|
||||
the URL to the end of the link for usability. */
|
||||
a:link,
|
||||
a:visited {
|
||||
color: #000 !important;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.reveal a:link:after,
|
||||
.reveal a:visited:after {
|
||||
content: " (" attr(href) ") ";
|
||||
color: #222 !important;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
|
||||
/* SECTION 6: more reveal.js specific additions by @skypanther */
|
||||
ul, ol, div, p {
|
||||
visibility: visible;
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
overflow: visible;
|
||||
margin: auto;
|
||||
text-align: left !important;
|
||||
}
|
||||
.reveal .slides {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
left: auto;
|
||||
top: auto;
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
padding: auto;
|
||||
|
||||
overflow: visible;
|
||||
display: block;
|
||||
|
||||
text-align: center;
|
||||
-webkit-perspective: none;
|
||||
-moz-perspective: none;
|
||||
-ms-perspective: none;
|
||||
perspective: none;
|
||||
|
||||
-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>section, .reveal .slides>section>section,
|
||||
.reveal .slides>section.past, .reveal .slides>section.future,
|
||||
.reveal.linear .slides>section, .reveal.linear .slides>section>section,
|
||||
.reveal.linear .slides>section.past, .reveal.linear .slides>section.future {
|
||||
|
||||
visibility: visible;
|
||||
position: static;
|
||||
width: 90%;
|
||||
height: auto;
|
||||
display: block;
|
||||
overflow: visible;
|
||||
|
||||
left: 0%;
|
||||
top: 0%;
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
padding: 20px 0px;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
-webkit-transform-style: flat;
|
||||
-moz-transform-style: flat;
|
||||
-ms-transform-style: flat;
|
||||
transform-style: flat;
|
||||
|
||||
-webkit-transform: none;
|
||||
-moz-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
.reveal section {
|
||||
page-break-after: always !important;
|
||||
display: block !important;
|
||||
}
|
||||
.reveal section.stack {
|
||||
page-break-after: avoid !important;
|
||||
}
|
||||
.reveal section .fragment {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
.reveal section img {
|
||||
display: block;
|
||||
margin: 15px 0px;
|
||||
background: rgba(255,255,255,1);
|
||||
border: 1px solid #666;
|
||||
box-shadow: none;
|
||||
}
|
382
index.html
382
index.html
File diff suppressed because one or more lines are too long
996
js/reveal.js
Normal file
996
js/reveal.js
Normal file
@@ -0,0 +1,996 @@
|
||||
/*!
|
||||
* reveal.js 1.4
|
||||
* http://lab.hakim.se/reveal-js
|
||||
* MIT licensed
|
||||
*
|
||||
* Copyright (C) 2012 Hakim El Hattab, http://hakim.se
|
||||
*/
|
||||
var Reveal = (function(){
|
||||
|
||||
var HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section',
|
||||
VERTICAL_SLIDES_SELECTOR = '.reveal .slides>section.present>section',
|
||||
|
||||
IS_TOUCH_DEVICE = !!( 'ontouchstart' in window ),
|
||||
|
||||
// The horizontal and verical index of the currently active slide
|
||||
indexh = 0,
|
||||
indexv = 0,
|
||||
|
||||
// Configurations defaults, can be overridden at initialization time
|
||||
config = {
|
||||
// Display controls in the bottom right corner
|
||||
controls: true,
|
||||
|
||||
// Display a presentation progress bar
|
||||
progress: true,
|
||||
|
||||
// Push each slide change to the browser history
|
||||
history: false,
|
||||
|
||||
// Enable keyboard shortcuts for navigation
|
||||
keyboard: true,
|
||||
|
||||
// Loop the presentation
|
||||
loop: false,
|
||||
|
||||
// Number of milliseconds between automatically proceeding to the
|
||||
// next slide, disabled when set to 0
|
||||
autoSlide: 0,
|
||||
|
||||
// Enable slide navigation via mouse wheel
|
||||
mouseWheel: true,
|
||||
|
||||
// Apply a 3D roll to links on hover
|
||||
rollingLinks: true,
|
||||
|
||||
// UI style
|
||||
theme: 'default', // default/neon/beige
|
||||
|
||||
// Transition style
|
||||
transition: 'default' // default/cube/page/concave/linear(2d)
|
||||
},
|
||||
|
||||
// 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.
|
||||
state = [],
|
||||
|
||||
// Cached references to DOM elements
|
||||
dom = {},
|
||||
|
||||
// Detect support for CSS 3D transforms
|
||||
supports3DTransforms = document.body.style['WebkitPerspective'] !== undefined ||
|
||||
document.body.style['MozPerspective'] !== undefined ||
|
||||
document.body.style['msPerspective'] !== undefined ||
|
||||
document.body.style['OPerspective'] !== undefined ||
|
||||
document.body.style['perspective'] !== undefined,
|
||||
|
||||
supports2DTransforms = document.body.style['WebkitTransform'] !== undefined ||
|
||||
document.body.style['MozTransform'] !== undefined ||
|
||||
document.body.style['msTransform'] !== undefined ||
|
||||
document.body.style['OTransform'] !== undefined ||
|
||||
document.body.style['transform'] !== undefined,
|
||||
|
||||
// Detect support for elem.classList
|
||||
supportsClassList = !!document.body.classList;
|
||||
|
||||
// Throttles mouse wheel navigation
|
||||
mouseWheelTimeout = 0,
|
||||
|
||||
// An interval used to automatically move on to the next slide
|
||||
autoSlideTimeout = 0,
|
||||
|
||||
// Delays updates to the URL due to a Chrome thumbnailer bug
|
||||
writeURLTimeout = 0,
|
||||
|
||||
// Holds information about the currently ongoing touch input
|
||||
touch = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
startSpan: 0,
|
||||
startCount: 0,
|
||||
handled: false,
|
||||
threshold: 40
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Starts up the slideshow by applying configuration
|
||||
* options and binding various events.
|
||||
*/
|
||||
function initialize( options ) {
|
||||
|
||||
if( ( !supports2DTransforms && !supports3DTransforms ) || !supportsClassList ) {
|
||||
document.body.setAttribute( 'class', 'no-transforms' );
|
||||
|
||||
// If the browser doesn't support core features we won't be
|
||||
// using JavaScript to control the presentation
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache references to DOM elements
|
||||
dom.wrapper = document.querySelector( '.reveal' );
|
||||
dom.progress = document.querySelector( '.reveal .progress' );
|
||||
dom.progressbar = document.querySelector( '.reveal .progress span' );
|
||||
|
||||
if ( config.controls ) {
|
||||
dom.controls = document.querySelector( '.reveal .controls' );
|
||||
dom.controlsLeft = document.querySelector( '.reveal .controls .left' );
|
||||
dom.controlsRight = document.querySelector( '.reveal .controls .right' );
|
||||
dom.controlsUp = document.querySelector( '.reveal .controls .up' );
|
||||
dom.controlsDown = document.querySelector( '.reveal .controls .down' );
|
||||
}
|
||||
|
||||
addEventListeners();
|
||||
|
||||
// Copy options over to our config object
|
||||
extend( config, options );
|
||||
|
||||
// Updates the presentation to match the current configuration values
|
||||
configure();
|
||||
|
||||
// Read the initial hash
|
||||
readURL();
|
||||
|
||||
// Start auto-sliding if it's enabled
|
||||
cueAutoSlide();
|
||||
|
||||
// Set up hiding of the browser address bar
|
||||
if( navigator.userAgent.match( /(iphone|ipod|android)/i ) ) {
|
||||
// Give the page some scrollable overflow
|
||||
document.documentElement.style.overflow = 'scroll';
|
||||
document.body.style.height = '120%';
|
||||
|
||||
// Events that should trigger the address bar to hide
|
||||
window.addEventListener( 'load', removeAddressBar, false );
|
||||
window.addEventListener( 'orientationchange', removeAddressBar, false );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function configure() {
|
||||
if( supports3DTransforms === false ) {
|
||||
// Fall back on the 2D transform theme 'linear'
|
||||
config.transition = 'linear';
|
||||
}
|
||||
|
||||
if( config.controls && dom.controls ) {
|
||||
dom.controls.style.display = 'block';
|
||||
}
|
||||
|
||||
if( config.progress && dom.progress ) {
|
||||
dom.progress.style.display = 'block';
|
||||
}
|
||||
|
||||
if( config.transition !== 'default' ) {
|
||||
dom.wrapper.classList.add( config.transition );
|
||||
}
|
||||
|
||||
if( config.theme !== 'default' ) {
|
||||
document.documentElement.classList.add( 'theme-' + config.theme );
|
||||
}
|
||||
|
||||
if( config.mouseWheel ) {
|
||||
document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
|
||||
document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );
|
||||
}
|
||||
|
||||
if( config.rollingLinks ) {
|
||||
// Add some 3D magic to our anchors
|
||||
linkify();
|
||||
}
|
||||
}
|
||||
|
||||
function addEventListeners() {
|
||||
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
|
||||
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
|
||||
document.addEventListener( 'touchend', onDocumentTouchEnd, false );
|
||||
window.addEventListener( 'hashchange', onWindowHashChange, false );
|
||||
|
||||
if( config.keyboard ) {
|
||||
document.addEventListener( 'keydown', onDocumentKeyDown, false );
|
||||
}
|
||||
|
||||
if ( config.controls && dom.controls ) {
|
||||
dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
|
||||
dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
|
||||
dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
|
||||
dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
|
||||
}
|
||||
}
|
||||
|
||||
function removeEventListeners() {
|
||||
document.removeEventListener( 'keydown', onDocumentKeyDown, false );
|
||||
document.removeEventListener( 'touchstart', onDocumentTouchStart, false );
|
||||
document.removeEventListener( 'touchmove', onDocumentTouchMove, false );
|
||||
document.removeEventListener( 'touchend', onDocumentTouchEnd, false );
|
||||
window.removeEventListener( 'hashchange', onWindowHashChange, false );
|
||||
|
||||
if ( config.controls && dom.controls ) {
|
||||
dom.controlsLeft.removeEventListener( 'click', preventAndForward( navigateLeft ), false );
|
||||
dom.controlsRight.removeEventListener( 'click', preventAndForward( navigateRight ), false );
|
||||
dom.controlsUp.removeEventListener( 'click', preventAndForward( navigateUp ), false );
|
||||
dom.controlsDown.removeEventListener( 'click', preventAndForward( navigateDown ), false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend object a with the properties of object b.
|
||||
* If there's a conflict, object b takes precedence.
|
||||
*/
|
||||
function extend( a, b ) {
|
||||
for( var i in b ) {
|
||||
a[ i ] = b[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function distanceBetween( a, b ) {
|
||||
var dx = a.x - b.x,
|
||||
dy = a.y - b.y;
|
||||
|
||||
return Math.sqrt( dx*dx + dy*dy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents an events defaults behavior calls the
|
||||
* specified delegate.
|
||||
*
|
||||
* @param {Function} delegate The method to call
|
||||
* after the wrapper has been executed
|
||||
*/
|
||||
function preventAndForward( delegate ) {
|
||||
return function( event ) {
|
||||
event.preventDefault();
|
||||
delegate.call();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the address bar to hide on mobile devices,
|
||||
* more vertical space ftw.
|
||||
*/
|
||||
function removeAddressBar() {
|
||||
setTimeout( function() {
|
||||
window.scrollTo( 0, 1 );
|
||||
}, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'keydown' event.
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function onDocumentKeyDown( event ) {
|
||||
// FFT: Use document.querySelector( ':focus' ) === null
|
||||
// instead of checking contentEditable?
|
||||
|
||||
// Disregard the event if the target is editable or a
|
||||
// modifier is present
|
||||
if ( event.target.contentEditable != 'inherit' || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
|
||||
|
||||
var triggered = false;
|
||||
|
||||
switch( event.keyCode ) {
|
||||
// p, page up
|
||||
case 80: case 33: navigatePrev(); triggered = true; break;
|
||||
// n, page down
|
||||
case 78: case 34: navigateNext(); triggered = true; break;
|
||||
// h, left
|
||||
case 72: case 37: navigateLeft(); triggered = true; break;
|
||||
// l, right
|
||||
case 76: case 39: navigateRight(); triggered = true; break;
|
||||
// k, up
|
||||
case 75: case 38: navigateUp(); triggered = true; break;
|
||||
// j, down
|
||||
case 74: case 40: navigateDown(); triggered = true; break;
|
||||
// home
|
||||
case 36: navigateTo( 0 ); triggered = true; break;
|
||||
// end
|
||||
case 35: navigateTo( Number.MAX_VALUE ); triggered = true; break;
|
||||
// space
|
||||
case 32: overviewIsActive() ? deactivateOverview() : navigateNext(); triggered = true; break;
|
||||
// return
|
||||
case 13: if( overviewIsActive() ) { deactivateOverview(); triggered = true; } break;
|
||||
}
|
||||
|
||||
// If the input resulted in a triggered action we should prevent
|
||||
// the browsers default behavior
|
||||
if( triggered ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
else if ( event.keyCode === 27 && supports3DTransforms ) {
|
||||
toggleOverview();
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// If auto-sliding is enabled we need to cue up
|
||||
// another timeout
|
||||
cueAutoSlide();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'touchstart' event,
|
||||
* enables support for swipe and pinch gestures.
|
||||
*/
|
||||
function onDocumentTouchStart( event ) {
|
||||
touch.startX = event.touches[0].clientX;
|
||||
touch.startY = event.touches[0].clientY;
|
||||
touch.startCount = event.touches.length;
|
||||
|
||||
// If there's two touches we need to memorize the distance
|
||||
// between those two points to detect pinching
|
||||
if( event.touches.length === 2 ) {
|
||||
touch.startSpan = distanceBetween( {
|
||||
x: event.touches[1].clientX,
|
||||
y: event.touches[1].clientY
|
||||
}, {
|
||||
x: touch.startX,
|
||||
y: touch.startY
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'touchmove' event.
|
||||
*/
|
||||
function onDocumentTouchMove( event ) {
|
||||
// Each touch should only trigger one action
|
||||
if( !touch.handled ) {
|
||||
var currentX = event.touches[0].clientX;
|
||||
var currentY = event.touches[0].clientY;
|
||||
|
||||
// If the touch started off with two points and still has
|
||||
// two active touches; test for the pinch gesture
|
||||
if( event.touches.length === 2 && touch.startCount === 2 ) {
|
||||
|
||||
// The current distance in pixels between the two touch points
|
||||
var currentSpan = distanceBetween( {
|
||||
x: event.touches[1].clientX,
|
||||
y: event.touches[1].clientY
|
||||
}, {
|
||||
x: touch.startX,
|
||||
y: touch.startY
|
||||
} );
|
||||
|
||||
// If the span is larger than the desire amount we've got
|
||||
// ourselves a pinch
|
||||
if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
|
||||
touch.handled = true;
|
||||
|
||||
if( currentSpan < touch.startSpan ) {
|
||||
activateOverview();
|
||||
}
|
||||
else {
|
||||
deactivateOverview();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// There was only one touch point, look for a swipe
|
||||
else if( event.touches.length === 1 ) {
|
||||
var deltaX = currentX - touch.startX,
|
||||
deltaY = currentY - touch.startY;
|
||||
|
||||
if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
||||
touch.handled = true;
|
||||
navigateLeft();
|
||||
}
|
||||
else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
||||
touch.handled = true;
|
||||
navigateRight();
|
||||
}
|
||||
else if( deltaY > touch.threshold ) {
|
||||
touch.handled = true;
|
||||
navigateUp();
|
||||
}
|
||||
else if( deltaY < -touch.threshold ) {
|
||||
touch.handled = true;
|
||||
navigateDown();
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'touchend' event.
|
||||
*/
|
||||
function onDocumentTouchEnd( event ) {
|
||||
touch.handled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse wheel scrolling, throttled to avoid
|
||||
* skipping multiple slides.
|
||||
*/
|
||||
function onDocumentMouseScroll( event ){
|
||||
clearTimeout( mouseWheelTimeout );
|
||||
|
||||
mouseWheelTimeout = setTimeout( function() {
|
||||
var delta = event.detail || -event.wheelDelta;
|
||||
if( delta > 0 ) {
|
||||
navigateNext();
|
||||
}
|
||||
else {
|
||||
navigatePrev();
|
||||
}
|
||||
}, 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the window level 'hashchange' event.
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function onWindowHashChange( event ) {
|
||||
readURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap all links in 3D goodness.
|
||||
*/
|
||||
function linkify() {
|
||||
if( supports3DTransforms ) {
|
||||
var nodes = document.querySelectorAll( '.reveal .slides section a:not(.image)' );
|
||||
|
||||
for( var i = 0, len = nodes.length; i < len; i++ ) {
|
||||
var node = nodes[i];
|
||||
|
||||
if( node.textContent && !node.querySelector( 'img' ) && ( !node.className || !node.classList.contains( node, 'roll' ) ) ) {
|
||||
node.classList.add( 'roll' );
|
||||
node.innerHTML = '<span data-title="'+ node.text +'">' + node.innerHTML + '</span>';
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the overview of slides (quick nav) by
|
||||
* scaling down and arranging all slide elements.
|
||||
*
|
||||
* Experimental feature, might be dropped if perf
|
||||
* can't be improved.
|
||||
*/
|
||||
function activateOverview() {
|
||||
|
||||
dom.wrapper.classList.add( 'overview' );
|
||||
|
||||
var horizontalSlides = Array.prototype.slice.call( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
||||
|
||||
for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) {
|
||||
var hslide = horizontalSlides[i],
|
||||
htransform = 'translateZ(-2500px) translate(' + ( ( i - indexh ) * 105 ) + '%, 0%)';
|
||||
|
||||
hslide.setAttribute( 'data-index-h', i );
|
||||
hslide.style.display = 'block';
|
||||
hslide.style.WebkitTransform = htransform;
|
||||
hslide.style.MozTransform = htransform;
|
||||
hslide.style.msTransform = htransform;
|
||||
hslide.style.OTransform = htransform;
|
||||
hslide.style.transform = htransform;
|
||||
|
||||
if( !hslide.classList.contains( 'stack' ) ) {
|
||||
// Navigate to this slide on click
|
||||
hslide.addEventListener( 'click', onOverviewSlideClicked, true );
|
||||
}
|
||||
|
||||
var verticalSlides = Array.prototype.slice.call( hslide.querySelectorAll( 'section' ) );
|
||||
|
||||
for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
|
||||
var vslide = verticalSlides[j],
|
||||
vtransform = 'translate(0%, ' + ( ( j - indexv ) * 105 ) + '%)';
|
||||
|
||||
vslide.setAttribute( 'data-index-h', i );
|
||||
vslide.setAttribute( 'data-index-v', j );
|
||||
vslide.style.display = 'block';
|
||||
vslide.style.WebkitTransform = vtransform;
|
||||
vslide.style.MozTransform = vtransform;
|
||||
vslide.style.msTransform = vtransform;
|
||||
vslide.style.OTransform = vtransform;
|
||||
vslide.style.transform = vtransform;
|
||||
|
||||
// Navigate to this slide on click
|
||||
vslide.addEventListener( 'click', onOverviewSlideClicked, true );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the slide overview and enters the currently
|
||||
* active slide.
|
||||
*/
|
||||
function deactivateOverview() {
|
||||
dom.wrapper.classList.remove( 'overview' );
|
||||
|
||||
var slides = Array.prototype.slice.call( document.querySelectorAll( '.reveal .slides section' ) );
|
||||
|
||||
for( var i = 0, len = slides.length; i < len; i++ ) {
|
||||
var element = slides[i];
|
||||
|
||||
// Resets all transforms to use the external styles
|
||||
element.style.WebkitTransform = '';
|
||||
element.style.MozTransform = '';
|
||||
element.style.msTransform = '';
|
||||
element.style.OTransform = '';
|
||||
element.style.transform = '';
|
||||
|
||||
element.removeEventListener( 'click', onOverviewSlideClicked );
|
||||
}
|
||||
|
||||
slide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the overview is currently active.
|
||||
*
|
||||
* @return {Boolean} true if the overview is active,
|
||||
* false otherwise
|
||||
*/
|
||||
function overviewIsActive() {
|
||||
return dom.wrapper.classList.contains( 'overview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a slide is and we're in the overview.
|
||||
*/
|
||||
function onOverviewSlideClicked( event ) {
|
||||
// TODO There's a bug here where the event listeners are not
|
||||
// removed after deactivating the overview.
|
||||
if( overviewIsActive() ) {
|
||||
event.preventDefault();
|
||||
|
||||
deactivateOverview();
|
||||
|
||||
indexh = this.getAttribute( 'data-index-h' );
|
||||
indexv = this.getAttribute( 'data-index-v' );
|
||||
|
||||
slide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates one dimension of slides by showing the slide
|
||||
* with the specified index.
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*/
|
||||
function updateSlides( selector, index ) {
|
||||
|
||||
// Select all slides and convert the NodeList result to
|
||||
// an array
|
||||
var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) ),
|
||||
slidesLength = slides.length;
|
||||
|
||||
if( slidesLength ) {
|
||||
|
||||
// Should the index loop?
|
||||
if( config.loop ) {
|
||||
index %= slidesLength;
|
||||
|
||||
if( index < 0 ) {
|
||||
index = slidesLength + index;
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce max and minimum index bounds
|
||||
index = Math.max( Math.min( index, slidesLength - 1 ), 0 );
|
||||
|
||||
for( var i = 0; i < slidesLength; i++ ) {
|
||||
var slide = slides[i];
|
||||
|
||||
// Optimization; hide all slides that are three or more steps
|
||||
// away from the present slide
|
||||
if( overviewIsActive() === false ) {
|
||||
// The distance loops so that it measures 1 between the first
|
||||
// and last slides
|
||||
var distance = Math.abs( ( index - i ) % ( slidesLength - 3 ) ) || 0;
|
||||
|
||||
slide.style.display = distance > 3 ? 'none' : 'block';
|
||||
}
|
||||
|
||||
slides[i].classList.remove( 'past' );
|
||||
slides[i].classList.remove( 'present' );
|
||||
slides[i].classList.remove( 'future' );
|
||||
|
||||
if( i < index ) {
|
||||
// Any element previous to index is given the 'past' class
|
||||
slides[i].classList.add( 'past' );
|
||||
}
|
||||
else if( i > index ) {
|
||||
// Any element subsequent to index is given the 'future' class
|
||||
slides[i].classList.add( 'future' );
|
||||
}
|
||||
|
||||
// If this element contains vertical slides
|
||||
if( slide.querySelector( 'section' ) ) {
|
||||
slides[i].classList.add( 'stack' );
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the current slide as present
|
||||
slides[index].classList.add( 'present' );
|
||||
|
||||
// If this slide has a state associated with it, add it
|
||||
// onto the current state of the deck
|
||||
var slideState = slides[index].getAttribute( 'data-state' );
|
||||
if( slideState ) {
|
||||
state = state.concat( slideState.split( ' ' ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Since there are no slides we can't be anywhere beyond the
|
||||
// zeroth index
|
||||
index = 0;
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visual slides to represent the currently
|
||||
* set indices.
|
||||
*/
|
||||
function slide( h, v ) {
|
||||
// Remember the state before this slide
|
||||
var stateBefore = state.concat();
|
||||
|
||||
// Reset the state array
|
||||
state.length = 0;
|
||||
|
||||
var indexhBefore = indexh,
|
||||
indexvBefore = indexv;
|
||||
|
||||
// Activate and transition to the new slide
|
||||
indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );
|
||||
indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );
|
||||
|
||||
// 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
|
||||
// did, we will avoid adding it repeatedly.
|
||||
for( var j = 0; j < stateBefore.length; j++ ) {
|
||||
if( stateBefore[j] === state[i] ) {
|
||||
stateBefore.splice( j, 1 );
|
||||
continue stateLoop;
|
||||
}
|
||||
}
|
||||
|
||||
document.documentElement.classList.add( state[i] );
|
||||
|
||||
// Dispatch custom event matching the state's name
|
||||
dispatchEvent( state[i] );
|
||||
}
|
||||
|
||||
// Clean up the remaints of the previous state
|
||||
while( stateBefore.length ) {
|
||||
document.documentElement.classList.remove( stateBefore.pop() );
|
||||
}
|
||||
|
||||
// Update progress if enabled
|
||||
if( config.progress && dom.progress ) {
|
||||
dom.progressbar.style.width = ( indexh / ( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length - 1 ) ) * window.innerWidth + 'px';
|
||||
}
|
||||
|
||||
// Close the overview if it's active
|
||||
if( overviewIsActive() ) {
|
||||
activateOverview();
|
||||
}
|
||||
|
||||
updateControls();
|
||||
|
||||
clearTimeout( writeURLTimeout );
|
||||
writeURLTimeout = setTimeout( writeURL, 1500 );
|
||||
|
||||
// Only fire if the slide index is different from before
|
||||
if( indexh !== indexhBefore || indexv !== indexvBefore ) {
|
||||
// Query all horizontal slides in the deck
|
||||
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
|
||||
|
||||
// Find the previous and current horizontal slides
|
||||
var previousHorizontalSlide = horizontalSlides[ indexhBefore ],
|
||||
currentHorizontalSlide = horizontalSlides[ indexh ];
|
||||
|
||||
// Query all vertical slides inside of the previous and current horizontal slides
|
||||
var previousVerticalSlides = previousHorizontalSlide.querySelectorAll( 'section' );
|
||||
currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );
|
||||
|
||||
// Dispatch an event notifying observers of the change in slide
|
||||
dispatchEvent( 'slidechanged', {
|
||||
// Include the current indices in the event
|
||||
'indexh': indexh,
|
||||
'indexv': indexv,
|
||||
|
||||
// Passes direct references to the slide HTML elements, attempts to find
|
||||
// a vertical slide and falls back on the horizontal parent
|
||||
'previousSlide': previousVerticalSlides[ indexvBefore ] || previousHorizontalSlide,
|
||||
'currentSlide': currentVerticalSlides[ indexv ] || currentHorizontalSlide
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state and link pointers of the controls.
|
||||
*/
|
||||
function updateControls() {
|
||||
if ( !config.controls || !dom.controls ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var routes = availableRoutes();
|
||||
|
||||
// Remove the 'enabled' class from all directions
|
||||
[ dom.controlsLeft, dom.controlsRight, dom.controlsUp, dom.controlsDown ].forEach( function( node ) {
|
||||
node.classList.remove( 'enabled' );
|
||||
} )
|
||||
|
||||
if( routes.left ) dom.controlsLeft.classList.add( 'enabled' );
|
||||
if( routes.right ) dom.controlsRight.classList.add( 'enabled' );
|
||||
if( routes.up ) dom.controlsUp.classList.add( 'enabled' );
|
||||
if( routes.down ) dom.controlsDown.classList.add( 'enabled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what available routes there are for navigation.
|
||||
*
|
||||
* @return {Object} containing four booleans: left/right/up/down
|
||||
*/
|
||||
function availableRoutes() {
|
||||
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
|
||||
var verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
|
||||
|
||||
return {
|
||||
left: indexh > 0,
|
||||
right: indexh < horizontalSlides.length - 1,
|
||||
up: indexv > 0,
|
||||
down: indexv < verticalSlides.length - 1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the current URL (hash) and navigates accordingly.
|
||||
*/
|
||||
function readURL() {
|
||||
// Break the hash down to separate components
|
||||
var bits = window.location.hash.slice(2).split('/');
|
||||
|
||||
// Read the index components of the hash
|
||||
var h = parseInt( bits[0] ) || 0 ;
|
||||
var v = parseInt( bits[1] ) || 0 ;
|
||||
|
||||
navigateTo( h, v );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the page URL (hash) to reflect the current
|
||||
* state.
|
||||
*/
|
||||
function writeURL() {
|
||||
if( config.history ) {
|
||||
var url = '/';
|
||||
|
||||
// Only include the minimum possible number of components in
|
||||
// the URL
|
||||
if( indexh > 0 || indexv > 0 ) url += indexh;
|
||||
if( indexv > 0 ) url += '/' + indexv;
|
||||
|
||||
window.location.hash = url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event of the specified type from the
|
||||
* reveal DOM element.
|
||||
*/
|
||||
function dispatchEvent( type, properties ) {
|
||||
var event = document.createEvent( "HTMLEvents", 1, 2 );
|
||||
event.initEvent( type, true, true );
|
||||
extend( event, properties );
|
||||
dom.wrapper.dispatchEvent( event );
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the next slide fragment.
|
||||
*
|
||||
* @return {Boolean} true if there was a next fragment,
|
||||
* false otherwise
|
||||
*/
|
||||
function nextFragment() {
|
||||
// Vertical slides:
|
||||
if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) {
|
||||
var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' );
|
||||
if( verticalFragments.length ) {
|
||||
verticalFragments[0].classList.add( 'visible' );
|
||||
|
||||
// Notify subscribers of the change
|
||||
dispatchEvent( 'fragmentshown', { fragment: verticalFragments[0] } );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Horizontal slides:
|
||||
else {
|
||||
var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' );
|
||||
if( horizontalFragments.length ) {
|
||||
horizontalFragments[0].classList.add( 'visible' );
|
||||
|
||||
// Notify subscribers of the change
|
||||
dispatchEvent( 'fragmentshown', { fragment: horizontalFragments[0] } );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the previous slide fragment.
|
||||
*
|
||||
* @return {Boolean} true if there was a previous fragment,
|
||||
* false otherwise
|
||||
*/
|
||||
function previousFragment() {
|
||||
// Vertical slides:
|
||||
if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) {
|
||||
var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment.visible' );
|
||||
if( verticalFragments.length ) {
|
||||
verticalFragments[ verticalFragments.length - 1 ].classList.remove( 'visible' );
|
||||
|
||||
// Notify subscribers of the change
|
||||
dispatchEvent( 'fragmenthidden', { fragment: verticalFragments[ verticalFragments.length - 1 ] } );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Horizontal slides:
|
||||
else {
|
||||
var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment.visible' );
|
||||
if( horizontalFragments.length ) {
|
||||
horizontalFragments[ horizontalFragments.length - 1 ].classList.remove( 'visible' );
|
||||
|
||||
// Notify subscribers of the change
|
||||
dispatchEvent( 'fragmenthidden', { fragment: horizontalFragments[ horizontalFragments.length - 1 ] } );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function cueAutoSlide() {
|
||||
clearTimeout( autoSlideTimeout );
|
||||
|
||||
// Cue the next auto-slide if enabled
|
||||
if( config.autoSlide ) {
|
||||
autoSlideTimeout = setTimeout( navigateNext, config.autoSlide );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a navigation to the specified indices.
|
||||
*
|
||||
* @param {Number} h The horizontal index of the slide to show
|
||||
* @param {Number} v The vertical index of the slide to show
|
||||
*/
|
||||
function navigateTo( h, v ) {
|
||||
slide( h, v );
|
||||
}
|
||||
|
||||
function navigateLeft() {
|
||||
// Prioritize hiding fragments
|
||||
if( overviewIsActive() || previousFragment() === false ) {
|
||||
slide( indexh - 1, 0 );
|
||||
}
|
||||
}
|
||||
function navigateRight() {
|
||||
// Prioritize revealing fragments
|
||||
if( overviewIsActive() || nextFragment() === false ) {
|
||||
slide( indexh + 1, 0 );
|
||||
}
|
||||
}
|
||||
function navigateUp() {
|
||||
// Prioritize hiding fragments
|
||||
if( overviewIsActive() || previousFragment() === false ) {
|
||||
slide( indexh, indexv - 1 );
|
||||
}
|
||||
}
|
||||
function navigateDown() {
|
||||
// Prioritize revealing fragments
|
||||
if( overviewIsActive() || nextFragment() === false ) {
|
||||
slide( indexh, indexv + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates backwards, prioritized in the following order:
|
||||
* 1) Previous fragment
|
||||
* 2) Previous vertical slide
|
||||
* 3) Previous horizontal slide
|
||||
*/
|
||||
function navigatePrev() {
|
||||
// Prioritize revealing fragments
|
||||
if( previousFragment() === false ) {
|
||||
if( availableRoutes().up ) {
|
||||
navigateUp();
|
||||
}
|
||||
else {
|
||||
// Fetch the previous horizontal slide, if there is one
|
||||
var previousSlide = document.querySelector( '.reveal .slides>section.past:nth-child(' + indexh + ')' );
|
||||
|
||||
if( previousSlide ) {
|
||||
indexv = ( previousSlide.querySelectorAll('section').length + 1 ) || 0;
|
||||
indexh --;
|
||||
slide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as #navigatePrev() but navigates forwards.
|
||||
*/
|
||||
function navigateNext() {
|
||||
// Prioritize revealing fragments
|
||||
if( nextFragment() === false ) {
|
||||
availableRoutes().down ? navigateDown() : navigateRight();
|
||||
}
|
||||
|
||||
// If auto-sliding is enabled we need to cue up
|
||||
// another timeout
|
||||
cueAutoSlide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the slide overview mode on and off.
|
||||
*/
|
||||
function toggleOverview() {
|
||||
if( overviewIsActive() ) {
|
||||
deactivateOverview();
|
||||
}
|
||||
else {
|
||||
activateOverview();
|
||||
}
|
||||
}
|
||||
|
||||
// Expose some methods publicly
|
||||
return {
|
||||
initialize: initialize,
|
||||
navigateTo: navigateTo,
|
||||
navigateLeft: navigateLeft,
|
||||
navigateRight: navigateRight,
|
||||
navigateUp: navigateUp,
|
||||
navigateDown: navigateDown,
|
||||
navigatePrev: navigatePrev,
|
||||
navigateNext: navigateNext,
|
||||
toggleOverview: toggleOverview,
|
||||
|
||||
addEventListeners: addEventListeners,
|
||||
removeEventListeners: removeEventListeners,
|
||||
|
||||
// Forward event binding to the reveal DOM element
|
||||
addEventListener: function( type, listener, useCapture ) {
|
||||
( dom.wrapper || document.querySelector( '.reveal' ) ).addEventListener( type, listener, useCapture );
|
||||
},
|
||||
removeEventListener: function( type, listener, useCapture ) {
|
||||
( dom.wrapper || document.querySelector( '.reveal' ) ).removeEventListener( type, listener, useCapture );
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
289
js/slideshow.js
289
js/slideshow.js
@@ -1,289 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Hakim El Hattab, http://hakim.se
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles the very minimal navigation logic involved in the
|
||||
* slideshow. Including keyboard navigation, touch interaction
|
||||
* and URL history behavior.
|
||||
*
|
||||
* Slides are given unique hash based URL's so that they can be
|
||||
* opened directly. I didn't use the HTML5 History API for this
|
||||
* as it would have required the addition of server side rewrite
|
||||
* rules and hence require more effort for anyone to set up.
|
||||
*
|
||||
* This component can be called from other scripts via a tiny API:
|
||||
* - Slideshow.navigateTo( indexh, indexv );
|
||||
* - Slideshow.navigateLeft();
|
||||
* - Slideshow.navigateRight();
|
||||
* - Slideshow.navigateUp();
|
||||
* - Slideshow.navigateDown();
|
||||
*
|
||||
*
|
||||
* version 0.1:
|
||||
* - First release
|
||||
*
|
||||
* version 0.2:
|
||||
* - Refactored code and added inline documentation
|
||||
* - Slides now have unique URL's
|
||||
* - A basic API to invoke navigation was added
|
||||
*
|
||||
* version 0.3:
|
||||
* - Added licensing terms
|
||||
*
|
||||
* version 0.4:
|
||||
* - Fixed broken links on touch devices.
|
||||
*
|
||||
*
|
||||
* @author Hakim El Hattab
|
||||
* @version 0.4
|
||||
*/
|
||||
var Slideshow = (function(){
|
||||
|
||||
var indexh = 0,
|
||||
indexv = 0;
|
||||
|
||||
/**
|
||||
* Activates the main program logic.
|
||||
*/
|
||||
function initialize() {
|
||||
document.addEventListener('keydown', onDocumentKeyDown, false);
|
||||
document.addEventListener('touchstart', onDocumentTouchStart, false);
|
||||
window.addEventListener('hashchange', onWindowHashChange, false);
|
||||
|
||||
// Read the initial state of the URL (hash)
|
||||
readURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'keydown' event.
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function onDocumentKeyDown( event ) {
|
||||
|
||||
if( event.keyCode >= 37 && event.keyCode <= 40 ) {
|
||||
|
||||
switch( event.keyCode ) {
|
||||
case 37: navigateLeft(); break; // left
|
||||
case 39: navigateRight(); break; // right
|
||||
case 38: navigateUp(); break; // up
|
||||
case 40: navigateDown(); break; // down
|
||||
}
|
||||
|
||||
slide();
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the document level 'touchstart' event.
|
||||
*
|
||||
* This enables very basic tap interaction for touch
|
||||
* devices. Added mainly for performance testing of 3D
|
||||
* transforms on iOS but was so happily surprised with
|
||||
* how smoothly it runs so I left it in here. Apple +1
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function onDocumentTouchStart( event ) {
|
||||
// We're only interested in one point taps
|
||||
if (event.touches.length === 1) {
|
||||
// Never prevent taps on anchors and images
|
||||
if( event.target.tagName.toLowerCase() === 'a' || event.target.tagName.toLowerCase() === 'img' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var point = {
|
||||
x: event.touches[0].clientX,
|
||||
y: event.touches[0].clientY
|
||||
};
|
||||
|
||||
// Define the extent of the areas that may be tapped
|
||||
// to navigate
|
||||
var wt = window.innerWidth * 0.3;
|
||||
var ht = window.innerHeight * 0.3;
|
||||
|
||||
if( point.x < wt ) {
|
||||
navigateLeft();
|
||||
}
|
||||
else if( point.x > window.innerWidth - wt ) {
|
||||
navigateRight();
|
||||
}
|
||||
else if( point.y < ht ) {
|
||||
navigateUp();
|
||||
}
|
||||
else if( point.y > window.innerHeight - ht ) {
|
||||
navigateDown();
|
||||
}
|
||||
|
||||
slide();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for the window level 'hashchange' event.
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function onWindowHashChange( event ) {
|
||||
readURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates one dimension of slides by showing the slide
|
||||
* with the specified index.
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*/
|
||||
function updateSlides( selector, index ) {
|
||||
|
||||
// Select all slides and convert the NodeList result to
|
||||
// an array
|
||||
var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) );
|
||||
|
||||
if( slides.length ) {
|
||||
// Enforce max and minimum index bounds
|
||||
index = Math.max(Math.min(index, slides.length - 1), 0);
|
||||
|
||||
slides[index].setAttribute('class', 'present');
|
||||
|
||||
// Any element previous to index is given the 'past' class
|
||||
slides.slice(0, index).map(function(element){
|
||||
element.setAttribute('class', 'past');
|
||||
});
|
||||
|
||||
// Any element subsequent to index is given the 'future' class
|
||||
slides.slice(index + 1).map(function(element){
|
||||
element.setAttribute('class', 'future');
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Since there are no slides we can't be anywhere beyond the
|
||||
// zeroth index
|
||||
index = 0;
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visual slides to represent the currently
|
||||
* set indices.
|
||||
*/
|
||||
function slide() {
|
||||
indexh = updateSlides( '#main>section', indexh );
|
||||
indexv = updateSlides( 'section.present>section', indexv );
|
||||
|
||||
writeURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the current URL (hash) and navigates accordingly.
|
||||
*/
|
||||
function readURL() {
|
||||
// Break the hash down to separate components
|
||||
var bits = window.location.hash.slice(2).split('/');
|
||||
|
||||
// Read the index components of the hash
|
||||
indexh = bits[0] ? parseInt( bits[0] ) : 0;
|
||||
indexv = bits[1] ? parseInt( bits[1] ) : 0;
|
||||
|
||||
navigateTo( indexh, indexv );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the page URL (hash) to reflect the current
|
||||
* navigational state.
|
||||
*/
|
||||
function writeURL() {
|
||||
var url = '/';
|
||||
|
||||
// Only include the minimum possible number of components in
|
||||
// the URL
|
||||
if( indexh > 0 || indexv > 0 ) url += indexh
|
||||
if( indexv > 0 ) url += '/' + indexv
|
||||
|
||||
window.location.hash = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a navigation to the specified indices.
|
||||
*
|
||||
* @param {Number} h The horizontal index of the slide to show
|
||||
* @param {Number} v The vertical index of the slide to show
|
||||
*/
|
||||
function navigateTo( h, v ) {
|
||||
indexh = h === undefined ? indexh : h;
|
||||
indexv = v === undefined ? indexv : v;
|
||||
|
||||
slide();
|
||||
}
|
||||
|
||||
function navigateLeft() {
|
||||
indexh --;
|
||||
indexv = 0;
|
||||
slide();
|
||||
}
|
||||
function navigateRight() {
|
||||
indexh ++;
|
||||
indexv = 0;
|
||||
slide();
|
||||
}
|
||||
function navigateUp() {
|
||||
indexv --;
|
||||
slide();
|
||||
}
|
||||
function navigateDown() {
|
||||
indexv ++;
|
||||
slide();
|
||||
}
|
||||
|
||||
// Initialize the program. Done right before returning to ensure
|
||||
// that any inline variable definitions are available to all
|
||||
// functions
|
||||
initialize();
|
||||
|
||||
// Expose some methods publicly
|
||||
return {
|
||||
navigateTo: navigateTo,
|
||||
navigateLeft: navigateLeft,
|
||||
navigateRight: navigateRight,
|
||||
navigateUp: navigateUp,
|
||||
navigateDown: navigateDown
|
||||
};
|
||||
|
||||
})();
|
||||
|
115
lib/css/zenburn.css
Normal file
115
lib/css/zenburn.css
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
|
||||
Zenburn style from voldmar.ru (c) Vladimir Epifanov <voldmar@voldmar.ru>
|
||||
based on dark.css by Ivan Sagalaev
|
||||
|
||||
*/
|
||||
|
||||
pre code {
|
||||
display: block; padding: 0.5em;
|
||||
background: #3F3F3F;
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
pre .keyword,
|
||||
pre .tag,
|
||||
pre .django .tag,
|
||||
pre .django .keyword,
|
||||
pre .css .class,
|
||||
pre .css .id,
|
||||
pre .lisp .title {
|
||||
color: #E3CEAB;
|
||||
}
|
||||
|
||||
pre .django .template_tag,
|
||||
pre .django .variable,
|
||||
pre .django .filter .argument {
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
pre .number,
|
||||
pre .date {
|
||||
color: #8CD0D3;
|
||||
}
|
||||
|
||||
pre .dos .envvar,
|
||||
pre .dos .stream,
|
||||
pre .variable,
|
||||
pre .apache .sqbracket {
|
||||
color: #EFDCBC;
|
||||
}
|
||||
|
||||
pre .dos .flow,
|
||||
pre .diff .change,
|
||||
pre .python .exception,
|
||||
pre .python .built_in,
|
||||
pre .literal,
|
||||
pre .tex .special {
|
||||
color: #EFEFAF;
|
||||
}
|
||||
|
||||
pre .diff .chunk,
|
||||
pre .ruby .subst {
|
||||
color: #8F8F8F;
|
||||
}
|
||||
|
||||
pre .dos .keyword,
|
||||
pre .python .decorator,
|
||||
pre .class .title,
|
||||
pre .haskell .label,
|
||||
pre .function .title,
|
||||
pre .ini .title,
|
||||
pre .diff .header,
|
||||
pre .ruby .class .parent,
|
||||
pre .apache .tag,
|
||||
pre .nginx .built_in,
|
||||
pre .tex .command,
|
||||
pre .input_number {
|
||||
color: #efef8f;
|
||||
}
|
||||
|
||||
pre .dos .winutils,
|
||||
pre .ruby .symbol,
|
||||
pre .ruby .symbol .string,
|
||||
pre .ruby .symbol .keyword,
|
||||
pre .ruby .symbol .keymethods,
|
||||
pre .ruby .string,
|
||||
pre .ruby .instancevar {
|
||||
color: #DCA3A3;
|
||||
}
|
||||
|
||||
pre .diff .deletion,
|
||||
pre .string,
|
||||
pre .tag .value,
|
||||
pre .preprocessor,
|
||||
pre .built_in,
|
||||
pre .sql .aggregate,
|
||||
pre .javadoc,
|
||||
pre .smalltalk .class,
|
||||
pre .smalltalk .localvars,
|
||||
pre .smalltalk .array,
|
||||
pre .css .rules .value,
|
||||
pre .attr_selector,
|
||||
pre .pseudo,
|
||||
pre .apache .cbracket,
|
||||
pre .tex .formula {
|
||||
color: #CC9393;
|
||||
}
|
||||
|
||||
pre .shebang,
|
||||
pre .diff .addition,
|
||||
pre .comment,
|
||||
pre .java .annotation,
|
||||
pre .template_comment,
|
||||
pre .pi,
|
||||
pre .doctype {
|
||||
color: #7F9F7F;
|
||||
}
|
||||
|
||||
pre .xml .css,
|
||||
pre .xml .javascript,
|
||||
pre .xml .vbscript,
|
||||
pre .tex .formula {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
2
lib/js/classList.js
Normal file
2
lib/js/classList.js
Normal file
@@ -0,0 +1,2 @@
|
||||
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
|
||||
if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p<o;p++){if(p in this&&this[p]===q){return p}}return -1},n=function(o,p){this.name=o;this.code=DOMException[o];this.message=p},g=function(p,o){if(o===""){throw new n("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(o)){throw new n("INVALID_CHARACTER_ERR","String contains an invalid character")}return c.call(p,o)},d=function(s){var r=k.call(s.className),q=r?r.split(/\s+/):[],p=0,o=q.length;for(;p<o;p++){this.push(q[p])}this._updateClassName=function(){s.className=this.toString()}},e=d[f]=[],i=function(){return new d(this)};n[f]=Error[f];e.item=function(o){return this[o]||null};e.contains=function(o){o+="";return g(this,o)!==-1};e.add=function(o){o+="";if(g(this,o)===-1){this.push(o);this._updateClassName()}};e.remove=function(p){p+="";var o=g(this,p);if(o!==-1){this.splice(o,1);this._updateClassName()}};e.toggle=function(o){o+="";if(g(this,o)===-1){this.add(o)}else{this.remove(o)}};e.toString=function(){return this.join(" ")};if(b.defineProperty){var l={get:i,enumerable:true,configurable:true};try{b.defineProperty(m,a,l)}catch(h){if(h.number===-2146823252){l.enumerable=false;b.defineProperty(m,a,l)}}}else{if(b[f].__defineGetter__){m.__defineGetter__(a,i)}}}(self))};
|
8
lib/js/head.min.js
vendored
Normal file
8
lib/js/head.min.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
Head JS The only script in your <HEAD>
|
||||
Copyright Tero Piirainen (tipiirai)
|
||||
License MIT / http://bit.ly/mit-license
|
||||
Version 0.96
|
||||
|
||||
http://headjs.com
|
||||
*/(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c<a.length;c++)b.call(a,a[c],c)}}function r(a){var b;if(typeof a=="object")for(var c in a)a[c]&&(b={name:c,url:a[c]});else b={name:q(a),url:a};var d=h[b.name];if(d&&d.url===b.url)return d;h[b.name]=b;return b}function q(a){var b=a.split("/"),c=b[b.length-1],d=c.indexOf("?");return d!=-1?c.substring(0,d):c}function p(a){a._done||(a(),a._done=1)}var b=a.documentElement,c,d,e=[],f=[],g={},h={},i=a.createElement("script").async===!0||"MozAppearance"in a.documentElement.style||window.opera,j=window.head_conf&&head_conf.head||"head",k=window[j]=window[j]||function(){k.ready.apply(null,arguments)},l=1,m=2,n=3,o=4;i?k.js=function(){var a=arguments,b=a[a.length-1],c={};t(b)||(b=null),s(a,function(d,e){d!=b&&(d=r(d),c[d.name]=d,x(d,b&&e==a.length-2?function(){u(c)&&p(b)}:null))});return k}:k.js=function(){var a=arguments,b=[].slice.call(a,1),d=b[0];if(!c){f.push(function(){k.js.apply(null,a)});return k}d?(s(b,function(a){t(a)||w(r(a))}),x(r(a[0]),t(d)?d:function(){k.js.apply(null,b)})):x(r(a[0]));return k},k.ready=function(b,c){if(b==a){d?p(c):e.push(c);return k}t(b)&&(c=b,b="ALL");if(typeof b!="string"||!t(c))return k;var f=h[b];if(f&&f.state==o||b=="ALL"&&u()&&d){p(c);return k}var i=g[b];i?i.push(c):i=g[b]=[c];return k},k.ready(a,function(){u()&&s(g.ALL,function(a){p(a)}),k.feature&&k.feature("domloaded",!0)});if(window.addEventListener)a.addEventListener("DOMContentLoaded",z,!1),window.addEventListener("load",z,!1);else if(window.attachEvent){a.attachEvent("onreadystatechange",function(){a.readyState==="complete"&&z()});var A=1;try{A=window.frameElement}catch(B){}!A&&b.doScroll&&function(){try{b.doScroll("left"),z()}catch(a){setTimeout(arguments.callee,1);return}}(),window.attachEvent("onload",z)}!a.readyState&&a.addEventListener&&(a.readyState="loading",a.addEventListener("DOMContentLoaded",handler=function(){a.removeEventListener("DOMContentLoaded",handler,!1),a.readyState="complete"},!1)),setTimeout(function(){c=!0,s(f,function(a){a()})},300)})(document)
|
5
lib/js/highlight.js
Normal file
5
lib/js/highlight.js
Normal file
File diff suppressed because one or more lines are too long
20
package.json
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"author": "Hakim El Hattab",
|
||||
"name": "reveal.js",
|
||||
"description": "HTML5 Slideware with Presenter Notes",
|
||||
"version": "1.5.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/hakimel/reveal.js.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "~0.6.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore" : "1.3.3",
|
||||
"express" : "2.5.9",
|
||||
"socket.io" : "0.9.6",
|
||||
"mustache" : "0.4.0"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
35
plugin/speakernotes/client.js
Normal file
35
plugin/speakernotes/client.js
Normal file
@@ -0,0 +1,35 @@
|
||||
(function() {
|
||||
// don't emit events from inside the previews themselves
|
||||
if ( window.location.search.match( /receiver/gi ) ) { return; }
|
||||
|
||||
var socket = io.connect(window.location.origin);
|
||||
var socketId = Math.random().toString().slice(2);
|
||||
|
||||
console.log('View slide notes at ' + window.location.origin + '/notes/' + socketId);
|
||||
|
||||
Reveal.addEventListener( 'slidechanged', function( event ) {
|
||||
var nextindexh;
|
||||
var nextindexv;
|
||||
var slideElement = event.currentSlide;
|
||||
|
||||
if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
|
||||
nextindexh = event.indexh;
|
||||
nextindexv = event.indexv + 1;
|
||||
} else {
|
||||
nextindexh = event.indexh + 1;
|
||||
nextindexv = 0;
|
||||
}
|
||||
|
||||
var notes = slideElement.querySelector('aside.notes');
|
||||
var slideData = {
|
||||
notes : notes ? notes.innerHTML : '',
|
||||
indexh : event.indexh,
|
||||
indexv : event.indexv,
|
||||
nextindexh : nextindexh,
|
||||
nextindexv : nextindexv,
|
||||
socketId : socketId
|
||||
};
|
||||
|
||||
socket.emit('slidechanged', slideData);
|
||||
} );
|
||||
}());
|
55
plugin/speakernotes/index.js
Normal file
55
plugin/speakernotes/index.js
Normal file
@@ -0,0 +1,55 @@
|
||||
var express = require('express');
|
||||
var fs = require('fs');
|
||||
var io = require('socket.io');
|
||||
var _ = require('underscore');
|
||||
var Mustache = require('mustache');
|
||||
|
||||
var app = express.createServer();
|
||||
var staticDir = express.static;
|
||||
|
||||
io = io.listen(app);
|
||||
|
||||
var opts = {
|
||||
port : 1947,
|
||||
baseDir : __dirname + '/../../'
|
||||
};
|
||||
|
||||
io.sockets.on('connection', function(socket) {
|
||||
socket.on('slidechanged', function(slideData) {
|
||||
socket.broadcast.emit('slidedata', slideData);
|
||||
});
|
||||
});
|
||||
|
||||
app.configure(function() {
|
||||
[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
|
||||
app.use('/' + dir, staticDir(opts.baseDir + dir));
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/", function(req, res) {
|
||||
fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
|
||||
});
|
||||
|
||||
app.get("/notes/:socketId", function(req, res) {
|
||||
|
||||
fs.readFile(opts.baseDir + 'plugin/speakernotes/notes.html', function(err, data) {
|
||||
res.send(Mustache.to_html(data.toString(), {
|
||||
socketId : req.params.socketId
|
||||
}));
|
||||
});
|
||||
// fs.createReadStream(opts.baseDir + 'speakernotes/notes.html').pipe(res);
|
||||
});
|
||||
|
||||
// Actually listen
|
||||
app.listen(opts.port || null);
|
||||
|
||||
var brown = '\033[33m',
|
||||
green = '\033[32m',
|
||||
reset = '\033[0m';
|
||||
|
||||
var slidesLocation = "http://localhost" + ( opts.port ? ( ':' + opts.port ) : '' );
|
||||
|
||||
console.log( brown + "reveal.js - Speaker Notes" + reset );
|
||||
console.log( "1. Open the slides at " + green + slidesLocation + reset );
|
||||
console.log( "2. Click on the link your JS console to go to the notes page" );
|
||||
console.log( "3. Advance through your slides and your notes will advance automatically" );
|
109
plugin/speakernotes/notes.html
Normal file
109
plugin/speakernotes/notes.html
Normal file
@@ -0,0 +1,109 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>reveal.js - Slide Notes</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
}
|
||||
|
||||
#notes {
|
||||
font-size: 24px;
|
||||
width: 640px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#wrap-current-slide {
|
||||
width: 640px;
|
||||
height: 512px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#current-slide {
|
||||
width: 1280px;
|
||||
height: 1024px;
|
||||
border: none;
|
||||
-moz-transform: scale(0.5);
|
||||
-moz-transform-origin: 0 0;
|
||||
-o-transform: scale(0.5);
|
||||
-o-transform-origin: 0 0;
|
||||
-webkit-transform: scale(0.5);
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
|
||||
#wrap-next-slide {
|
||||
width: 320px;
|
||||
height: 256px;
|
||||
float: left;
|
||||
margin: 0 0 0 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#next-slide {
|
||||
width: 1280px;
|
||||
height: 1024px;
|
||||
border: none;
|
||||
-moz-transform: scale(0.25);
|
||||
-moz-transform-origin: 0 0;
|
||||
-o-transform: scale(0.25);
|
||||
-o-transform-origin: 0 0;
|
||||
-webkit-transform: scale(0.25);
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.slides {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid black;
|
||||
border-radius: 2px;
|
||||
background: rgb(28, 30, 32);
|
||||
}
|
||||
|
||||
.slides span {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: rgba( 255, 255, 255, 0.9 );
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrap-current-slide" class="slides">
|
||||
<iframe src="/?receiver" width="1280" height="1024" id="current-slide"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="wrap-next-slide" class="slides">
|
||||
<iframe src="/?receiver" width="640" height="512" id="next-slide"></iframe>
|
||||
<span>UPCOMING:</span>
|
||||
</div>
|
||||
<div id="notes"></div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
|
||||
<script>
|
||||
var socketId = '{{socketId}}';
|
||||
var socket = io.connect(window.location.origin);
|
||||
var notes = document.getElementById('notes');
|
||||
var currentSlide = document.getElementById('current-slide');
|
||||
var nextSlide = document.getElementById('next-slide');
|
||||
|
||||
socket.on('slidedata', function(data) {
|
||||
// ignore data from sockets that aren't ours
|
||||
if (data.socketId !== socketId) { return; }
|
||||
|
||||
notes.innerHTML = data.notes;
|
||||
currentSlide.contentWindow.Reveal.navigateTo(data.indexh, data.indexv);
|
||||
nextSlide.contentWindow.Reveal.navigateTo(data.nextindexh, data.nextindexv);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user