Slides from Community Day added to docs

Signed-off-by: Dominic Schabel <dominic.schabel@bosch-si.com>
This commit is contained in:
Dominic Schabel
2018-10-22 21:06:42 +02:00
parent e1f28ab5bf
commit 56ce992764
26 changed files with 10315 additions and 1 deletions

View File

@@ -0,0 +1,249 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Eclipse hawkBit</title>
<meta name="description" content="Eclipse hawkBit - Community Day 2018">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/hawkBit.css" id="theme">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU"
crossorigin="anonymous">
<body>
<div class="reveal">
<div class="header">
<img src="resources/images/hawkbit_logo.png" width="120px" align="right" />
</div>
<div class="slides">
<section>
<div class="intro">
<h1>Community Day - Eclipse hawkBit</h1>
<h6>EclipseCon Europe 2018</h6>
</div>
</section>
<section>
<h1>Welcome...</h1>
<div style="font-size: 1.5em; position:absolute; left:2em; width: 100%;">
<div style="display: inline-block;">
<a href="https://github.com/laverman"><img src="https://avatars3.githubusercontent.com/u/9153035?s=120&v=4"
style="border-radius: 50%;" align="left"></a>
<p style="margin-left: 5em; margin-top: 0.5em;">Kai Zimmermann</p>
<i style="margin-left: 4em; margin-top: 0.5em; font-size: 0.5em;">Project Lead - Microsoft</i>
</div>
<br>
<div style="display: inline-block;">
<a href="https://github.com/schabdo"><img src="https://avatars1.githubusercontent.com/u/18215905?s=120&v=4"
style="border-radius: 50%;" align="left"></a>
<p style="margin-left: 5em; margin-top: 0.5em;">Dominic Schabel</p>
<i style="margin-left: 4em; margin-top: 0.5em; font-size: 0.5em;">Committer - Bosch</i>
</div>
</div>
</section>
<section>
<h1>About</h1>
<p>
Eclipse hawkBit aims to create a <b>domain independent</b> back end solution for rolling out <b>software
updates</b>
to constrained edge devices as well as more powerful controllers and gateways connected to IP based
networking infrastructure
</p>
</section>
<section>
<h1>Big picture</h1>
<img src="resources/images/hawkBit_overview.jpg">
</section>
<section>
<h1>Project update...</h1>
</section>
<section>
<h1>New team members</h1>
<div style="font-size: 1.5em; position:absolute; left:2em; width: 100%;">
<div style="display: inline-block;">
<a href="https://github.com/laverman"><img src="https://avatars3.githubusercontent.com/u/19288577?s=120&v=4"
style="border-radius: 50%;" align="left"></a>
<p style="margin-left: 5em; margin-top: 0.5em;">Jeroen Laverman</p>
<i style="margin-left: 4em; margin-top: 0.5em; font-size: 0.5em;">Project Lead - Bosch</i>
</div>
<br>
<div style="display: inline-block;">
<a href="https://github.com/stefbehl"><img src="https://avatars2.githubusercontent.com/u/16321153?s=120&v=4"
style="border-radius: 50%;" align="left"></a>
<p style="margin-left: 5em; margin-top: 0.5em;">Stefan Behl</p>
<i style="margin-left: 4em; margin-top: 0.5em; font-size: 0.5em;">Committer - Bosch</i>
</div>
<br>
<div style="display: inline-block;">
<a href="https://github.com/schabdo"><img src="https://avatars1.githubusercontent.com/u/18215905?s=120&v=4"
style="border-radius: 50%;" align="left"></a>
<p style="margin-left: 5em; margin-top: 0.5em;">Dominic Schabel</p>
<i style="margin-left: 4em; margin-top: 0.5em; font-size: 0.5em;">Committer - Bosch</i>
</div>
</div>
</section>
<section>
<h1>Statistics (10/2018)</h1>
<ul style="position:absolute; left:2em;">
<li style="margin-bottom: 0.5em"><a href="https://github.com/eclipse/hawkbit">hawkbit Core</a>: 149
stars, 622 PRs, 134 issues</li>
<li style="margin-bottom: 0.5em"><a href="https://github.com/eclipse/hawkbit-extensions">hawkbit
Extensions</a>: 30 PRs</li>
<li style="margin-bottom: 0.5em"><a href="https://github.com/eclipse/hawkbit-examples">hawkbit
Examples</a>: 25 PRs</li>
<li style="margin-bottom: 0.5em"><a href="https://hub.docker.com/u/hawkbit/">Docker Hub</a>: 3.4K
PULLS</li>
</ul>
</section>
<section>
<h1>We have been busy</h1>
<ul style="position:absolute; left:2em;">
<li style="margin-bottom: 0.5em"><a href="https://search.maven.org/search?q=g:org.eclipse.hawkbit%20AND%20v:0.2.0">First
Release<a></li>
<li style="margin-bottom: 0.5em"><a href="https://hawkbit.eclipse.org">New sandbox</a></li>
<li style="margin-bottom: 0.5em"><a href="https://www.eclipse.org/hawkbit">New homepage</a></li>
<li style="margin-bottom: 0.5em">Circle CI 2.0 (incl. <a href="https://github.com/eclipse/hawkbit-examples">examples</a>
& <a href="https://github.com/eclipse/hawkbit-extensions">extensions</a>)</li>
<li style="margin-bottom: 0.5em"><a href="https://hub.docker.com/u/hawkbit">Docker Hub setup</a></li>
<li style="margin-bottom: 0.5em"><a href="https://github.com/eclipse/hawkbit/pull/688">Open sourced
REST docs</a></li>
</ul>
</section>
<section>
<h1>Roadmap...</h1>
</section>
<section>
<h1>Upgrades, upgrades</h1>
<ul style="position:absolute; left:2em;">
<li style="margin-bottom: 0.5em">Support JDK 11</li>
<li style="margin-bottom: 0.5em">Vaadin 8</li>
<li style="margin-bottom: 0.5em">JUnit 5, Java &gt;= 9 </li>
<li style="margin-bottom: 0.5em"><a href="https://github.com/eclipse/hawkbit/pull/721">Spring Boot
2</a></li>
</ul>
</section>
<section>
<h1>Version overview</h1>
<div style="display: grid; grid-template-columns: 8em 5em 8em; grid-template-rows: 1.5em auto 1.5em;
grid-gap: 15px 0px; font-size: 1.5em; margin-left: 1.0em; margin-top: 1em;">
<div style="place-self: center; font-weight:bold;">hawkBit</div>
<div>&nbsp;</div>
<div style="place-self: center; font-weight:bold;">Spring Boot</div>
<div style="place-self: center;">0.2.x</div>
<div style="place-self: center;"><i class="fas fa-long-arrow-alt-right"></i></div>
<div style="place-self: center;">1.4</div>
<div style="place-self: center;">0.3.0</div>
<div style="place-self: center;"><i class="fas fa-long-arrow-alt-right"></i></div>
<div style="place-self: center;">2.0</div>
</div>
</section>
<section>
<h1>A few features as well</h1>
<ul style="position:absolute; left:2em;">
<li style="margin-bottom: 0.5em">Custom key/value pairs for targets (<a href="https://github.com/eclipse/hawkbit/issues/737">#737</a>)</li>
<li style="margin-bottom: 0.5em">Small improvements of rollout management</li>
</ul>
</section>
<section>
<h1>Cloud PaaS extensions...</h1>
<ul style="position:absolute; left:2em;">
<li style="margin-bottom: 0.5em">Inner cluster messaging (<a href="https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-about">Azure
Event Hubs</a>)</li>
<li style="margin-bottom: 0.5em">Multi-user support and role based access with OAuth2 (<a href="https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis">Azure
Active Directory</a>)</li>
<li style="margin-bottom: 0.5em">More storage providers (e.g. <a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction">Azure
Blob Storage</a>)</li>
<li style="margin-bottom: 0.5em">Example k8s deployments (potentially <a href="https://github.com/helm/helm">helm</a>
templates)</li>
<li style="margin-bottom: 0.5em">IoT platform integration (e.g. <a href="https://docs.microsoft.com/en-us/azure/iot-hub/about-iot-hub">Azure
IoT Hub</a>)
</li>
</ul>
</section>
<section>
<h1>Deprecations</h1>
<div style="position:absolute; left:2em;">We'll drop UAA extension support</div>
</section>
<section>
<h1>Thank you</h1>
<div style="font-size: 1.5em; position:absolute; left:2em;">
<p><i class="fa fa-bullhorn fa-fw"></i>&nbsp;<a href="https://www.eclipsecon.org/europe2018/sessions/eclipse-hawkbit-101">101
tutorial</a></p>
<p><i class="fab fa-docker fa-fw"></i>&nbsp;<a href="https://hub.docker.com/u/hawkbit">hawkbit</a></p>
<p><i class="fab fa-gitter fa-fw"></i>&nbsp;<a href="https://gitter.im/eclipse/hawkbit">eclipse/hawkbit</a></p>
<p><i class="fab fa-github fa-fw"></i>&nbsp;<a href="https://github.com/eclipse/hawkbit">eclipse/hawkbit</a></p>
<p><i class="fa fa-book-open fa-fw"></i>&nbsp;<a href="http://www.eclipse.org/hawkbit">eclipse.org/hawkbit</a></p>
</div>
</section>
</div>
</div>
<div class="footer">
<a href="http://www.eclipse.org" target="_blank">Eclipse Foundation</a> |
<a href="http://www.eclipse.org/legal/privacy.php" target="_blank">Privacy Policy</a> |
<a href="http://www.eclipse.org/legal/termsofuse.php" target="_blank">Terms of Use</a> |
<a href="http://www.eclipse.org/legal/copyright.php" target="_blank">Copyright Agent</a> |
<a href="http://www.eclipse.org/legal" target="_blank">Legal</a>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// Full list of configuration options available at:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: false,
progress: true,
history: true,
center: true,
transition: 'convex', // none/fade/slide/convex/concave/zoom
// Optional reveal.js plugins
dependencies: [{
src: 'lib/js/classList.js',
condition: function () {
return !document.body.classList;
}
}, {
src: 'plugin/markdown/marked.js',
condition: function () {
return !!document.querySelector('[data-markdown]');
}
}, {
src: 'plugin/markdown/markdown.js',
condition: function () {
return !!document.querySelector('[data-markdown]');
}
}, {
src: 'plugin/highlight/highlight.js',
async: true,
condition: function () {
return !!document.querySelector('pre code');
},
callback: function () {
hljs.initHighlightingOnLoad();
}
}, {
src: 'plugin/zoom-js/zoom.js',
async: true
}, {
src: 'plugin/notes/notes.js',
async: true
}]
});
Reveal.configure({
slideNumber: true
});
</script>
</body>
</html>

1555
docs/static/slides/css/reveal.css vendored Executable file

File diff suppressed because it is too large Load Diff

381
docs/static/slides/css/theme/hawkBit.css vendored Normal file
View File

@@ -0,0 +1,381 @@
/*********************************************
* GLOBAL STYLES
*********************************************/
body {
border-left: 0px solid #ffffff;
overflow: auto;
background-color: white;
background-position: center bottom;
background-repeat: no-repeat;
background-size: 100% 17px;
font-family: Helvetica, Arial, sans-serif;
font-weight: normal;
color: #2e3032
}
.header {
position: relative;
top: 5px;
right: 20px;
}
.footer {
font-size: 10px !Important;
font-family: Helvetica, Arial, sans-serif;
color: #687175;
position: absolute;
bottom: 20px;
left: 15px;
margin-bottom: 10px
}
.footer a {
color: #003b6a;
text-decoration: none;
}
.reveal {
font-family: Helvetica, Arial, sans-serif;
font-size: 25px;
font-weight: normal;
color: #551f62; d
}
.reveal .controls {
margin-bottom: 10px
}
.reveal .slide-number {
margin-bottom: 10px
}
::selection {
color: #fff;
background: #98bdef;
text-shadow: none;
}
.reveal .slides>section, .reveal .slides>section>section {
line-height: 1.1;
font-weight: inherit;
text-align: left;
/* padding-bottom: 25px; */
}
/*********************************************
* HEADERS
*********************************************/
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
margin: 0 0 20px 0;
font-family: Helvetica, Arial, sans-serif;
font-weight: 100;
line-height: 1.0;
padding-bottom: 15px;
letter-spacing: normal;
text-shadow: none;
word-wrap: break-word;
}
.reveal h1 {
font-size: 3.1em;
text-shadow: none;
}
.reveal h2 {
margin-top: 15px;
font-size: 1.6em;
}
.reveal h3 {
font-size: 1.2em;
}
.reveal h4 {
font-size: 1.0em;
color: #000;
}
/*********************************************
* OTHER
*********************************************/
.reveal .intro {
color: #551f62;
}
.noborder img {
border: none !Important;
box-shadow: none !Important;
background: none !Important;
}
.intro {
color: #551f62;
position: relative;
top: 250px;
left: 0px;
}
.reveal p {
margin: 20px 0;
line-height: 1.3;
}
/* Ensure certain elements are never larger than the slide itself */
.reveal img, .reveal video, .reveal iframe {
max-width: 80%;
max-height: 80%
}
.reveal strong, .reveal b {
font-weight: bold;
}
.reveal em {
font-style: italic;
}
.reveal ol, .reveal dl, .reveal ul {
display: inline-block;
text-align: left;
margin: 0 0 0 1em;
}
.reveal ol {
list-style-type: decimal;
}
.reveal ul {
list-style-type: disc;
/* margin-top: -15px;
margin-bottom: 20px; */
}
.reveal ul ul {
list-style-type: disc;
margin-top: 0px;
}
.reveal ul ul ul {
list-style-type: disc;
margin-top: 0px;
}
.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
display: block;
margin-left: 40px;
}
.reveal dt {
font-weight: bold;
}
.reveal dd {
margin-left: 40px;
}
.reveal q, .reveal blockquote {
quotes: none;
}
.reveal blockquote {
display: block;
position: relative;
width: 70%;
margin: 20px auto;
padding: 5px;
font-style: italic;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2);
}
.reveal blockquote p:first-child, .reveal blockquote p:last-child {
display: inline-block;
}
.reveal q {
font-style: italic;
}
.reveal pre {
display: block;
position: relative;
width: 90%;
margin: 20px auto;
text-align: left;
font-size: 0.55em;
font-family: monospace;
line-height: 1.2em;
word-wrap: break-word;
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);
}
.reveal code {
font-family: monospace;
}
.reveal pre code {
display: block;
padding: 5px;
overflow: auto;
max-height: 400px;
word-wrap: normal;
background: #3F3F3F;
color: #DCDCDC;
}
.reveal table {
margin: auto;
border-collapse: collapse;
border-spacing: 0;
}
.reveal table th {
font-weight: bold;
}
.reveal table th, .reveal table td {
text-align: left;
padding: 0.2em 0.5em 0.2em 0.5em;
border-bottom: 1px solid;
}
.reveal table th[align="center"], .reveal table td[align="center"] {
text-align: center;
}
.reveal table th[align="right"], .reveal table td[align="right"] {
text-align: right;
}
.reveal table tr:last-child td {
border-bottom: none;
}
.reveal sup {
vertical-align: super;
}
.reveal sub {
vertical-align: sub;
}
.reveal small {
display: inline-block;
font-size: 0.6em;
line-height: 1.2em;
vertical-align: top;
}
.reveal small * {
vertical-align: top;
}
/*********************************************
* LINKS
*********************************************/
.reveal a {
color: #6eb553;
text-decoration: none;
-webkit-transition: color 0.15s ease;
-moz-transition: color 0.15s ease;
transition: color 0.15s ease;
}
.reveal a:hover {
color: #6eb553;
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
background: #1a54a1;
}
/*********************************************
* IMAGES
*********************************************/
.reveal section img {
margin: 15px 0px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
display: block;
margin-left: auto;
margin-right: auto;
}
.reveal a img {
-webkit-transition: all 0.15s linear;
-moz-transition: all 0.15s linear;
transition: all 0.15s linear;
}
.reveal a:hover img {
background: rgba(255, 255, 255, 0.2);
border-color: #6eb553;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55);
}
/*********************************************
* NAVIGATION CONTROLS
*********************************************/
.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled
{
border-right-color: #6eb553;
}
.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled
{
border-left-color: #6eb553;
}
.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled
{
border-bottom-color: #6eb553;
}
.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled
{
border-top-color: #6eb553;
}
.reveal .controls div.navigate-left.enabled:hover {
border-right-color: #6eb553;
}
.reveal .controls div.navigate-right.enabled:hover {
border-left-color: #6eb553;
}
.reveal .controls div.navigate-up.enabled:hover {
border-bottom-color: #6eb553;
}
.reveal .controls div.navigate-down.enabled:hover {
border-top-color: #6eb553;
}
/*********************************************
* PROGRESS BAR
*********************************************/
.reveal .progress {
background: rgba(0, 0, 0, 0.2);
}
.reveal .progress span {
background: #003b6a;
-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);
}
/*********************************************
* SLIDE NUMBER
*********************************************/
.reveal .slide-number {
color: #003b6a;
}

5239
docs/static/slides/js/reveal.js vendored Executable file

File diff suppressed because it is too large Load Diff

2
docs/static/slides/lib/js/classList.js vendored Executable file
View 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))};

9
docs/static/slides/lib/js/head.min.js vendored Executable file
View File

@@ -0,0 +1,9 @@
/*! head.core - v1.0.2 */
(function(n,t){"use strict";function r(n){a[a.length]=n}function k(n){var t=new RegExp(" ?\\b"+n+"\\b");c.className=c.className.replace(t,"")}function p(n,t){for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}function tt(){var t,e,f,o;c.className=c.className.replace(/ (w-|eq-|gt-|gte-|lt-|lte-|portrait|no-portrait|landscape|no-landscape)\d+/g,"");t=n.innerWidth||c.clientWidth;e=n.outerWidth||n.screen.width;u.screen.innerWidth=t;u.screen.outerWidth=e;r("w-"+t);p(i.screens,function(n){t>n?(i.screensCss.gt&&r("gt-"+n),i.screensCss.gte&&r("gte-"+n)):t<n?(i.screensCss.lt&&r("lt-"+n),i.screensCss.lte&&r("lte-"+n)):t===n&&(i.screensCss.lte&&r("lte-"+n),i.screensCss.eq&&r("e-q"+n),i.screensCss.gte&&r("gte-"+n))});f=n.innerHeight||c.clientHeight;o=n.outerHeight||n.screen.height;u.screen.innerHeight=f;u.screen.outerHeight=o;u.feature("portrait",f>t);u.feature("landscape",f<t)}function it(){n.clearTimeout(b);b=n.setTimeout(tt,50)}var y=n.document,rt=n.navigator,ut=n.location,c=y.documentElement,a=[],i={screens:[240,320,480,640,768,800,1024,1280,1440,1680,1920],screensCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!1},browsers:[{ie:{min:6,max:11}}],browserCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!0},html5:!0,page:"-page",section:"-section",head:"head"},v,u,s,w,o,h,l,d,f,g,nt,e,b;if(n.head_conf)for(v in n.head_conf)n.head_conf[v]!==t&&(i[v]=n.head_conf[v]);u=n[i.head]=function(){u.ready.apply(null,arguments)};u.feature=function(n,t,i){return n?(Object.prototype.toString.call(t)==="[object Function]"&&(t=t.call()),r((t?"":"no-")+n),u[n]=!!t,i||(k("no-"+n),k(n),u.feature()),u):(c.className+=" "+a.join(" "),a=[],u)};u.feature("js",!0);s=rt.userAgent.toLowerCase();w=/mobile|android|kindle|silk|midp|phone|(windows .+arm|touch)/.test(s);u.feature("mobile",w,!0);u.feature("desktop",!w,!0);s=/(chrome|firefox)[ \/]([\w.]+)/.exec(s)||/(iphone|ipad|ipod)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(android)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(webkit|opera)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(msie) ([\w.]+)/.exec(s)||/(trident).+rv:(\w.)+/.exec(s)||[];o=s[1];h=parseFloat(s[2]);switch(o){case"msie":case"trident":o="ie";h=y.documentMode||h;break;case"firefox":o="ff";break;case"ipod":case"ipad":case"iphone":o="ios";break;case"webkit":o="safari"}for(u.browser={name:o,version:h},u.browser[o]=!0,l=0,d=i.browsers.length;l<d;l++)for(f in i.browsers[l])if(o===f)for(r(f),g=i.browsers[l][f].min,nt=i.browsers[l][f].max,e=g;e<=nt;e++)h>e?(i.browserCss.gt&&r("gt-"+f+e),i.browserCss.gte&&r("gte-"+f+e)):h<e?(i.browserCss.lt&&r("lt-"+f+e),i.browserCss.lte&&r("lte-"+f+e)):h===e&&(i.browserCss.lte&&r("lte-"+f+e),i.browserCss.eq&&r("eq-"+f+e),i.browserCss.gte&&r("gte-"+f+e));else r("no-"+f);r(o);r(o+parseInt(h,10));i.html5&&o==="ie"&&h<9&&p("abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|progress|section|summary|time|video".split("|"),function(n){y.createElement(n)});p(ut.pathname.split("/"),function(n,u){if(this.length>2&&this[u+1]!==t)u&&r(this.slice(u,u+1).join("-").toLowerCase()+i.section);else{var f=n||"index",e=f.indexOf(".");e>0&&(f=f.substring(0,e));c.id=f.toLowerCase()+i.page;u||r("root"+i.section)}});u.screen={height:n.screen.height,width:n.screen.width};tt();b=0;n.addEventListener?n.addEventListener("resize",it,!1):n.attachEvent("onresize",it)})(window);
/*! head.css3 - v1.0.0 */
(function(n,t){"use strict";function a(n){for(var r in n)if(i[n[r]]!==t)return!0;return!1}function r(n){var t=n.charAt(0).toUpperCase()+n.substr(1),i=(n+" "+c.join(t+" ")+t).split(" ");return!!a(i)}var h=n.document,o=h.createElement("i"),i=o.style,s=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),c="Webkit Moz O ms Khtml".split(" "),l=n.head_conf&&n.head_conf.head||"head",u=n[l],f={gradient:function(){var n="background-image:";return i.cssText=(n+s.join("gradient(linear,left top,right bottom,from(#9f9),to(#fff));"+n)+s.join("linear-gradient(left top,#eee,#fff);"+n)).slice(0,-n.length),!!i.backgroundImage},rgba:function(){return i.cssText="background-color:rgba(0,0,0,0.5)",!!i.backgroundColor},opacity:function(){return o.style.opacity===""},textshadow:function(){return i.textShadow===""},multiplebgs:function(){i.cssText="background:url(https://),url(https://),red url(https://)";var n=(i.background||"").match(/url/g);return Object.prototype.toString.call(n)==="[object Array]"&&n.length===3},boxshadow:function(){return r("boxShadow")},borderimage:function(){return r("borderImage")},borderradius:function(){return r("borderRadius")},cssreflections:function(){return r("boxReflect")},csstransforms:function(){return r("transform")},csstransitions:function(){return r("transition")},touch:function(){return"ontouchstart"in n},retina:function(){return n.devicePixelRatio>1},fontface:function(){var t=u.browser.name,n=u.browser.version;switch(t){case"ie":return n>=9;case"chrome":return n>=13;case"ff":return n>=6;case"ios":return n>=5;case"android":return!1;case"webkit":return n>=5.1;case"opera":return n>=10;default:return!1}}};for(var e in f)f[e]&&u.feature(e,f[e].call(),!0);u.feature()})(window);
/*! head.load - v1.0.3 */
(function(n,t){"use strict";function w(){}function u(n,t){if(n){typeof n=="object"&&(n=[].slice.call(n));for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}}function it(n,i){var r=Object.prototype.toString.call(i).slice(8,-1);return i!==t&&i!==null&&r===n}function s(n){return it("Function",n)}function a(n){return it("Array",n)}function et(n){var i=n.split("/"),t=i[i.length-1],r=t.indexOf("?");return r!==-1?t.substring(0,r):t}function f(n){(n=n||w,n._done)||(n(),n._done=1)}function ot(n,t,r,u){var f=typeof n=="object"?n:{test:n,success:!t?!1:a(t)?t:[t],failure:!r?!1:a(r)?r:[r],callback:u||w},e=!!f.test;return e&&!!f.success?(f.success.push(f.callback),i.load.apply(null,f.success)):e||!f.failure?u():(f.failure.push(f.callback),i.load.apply(null,f.failure)),i}function v(n){var t={},i,r;if(typeof n=="object")for(i in n)!n[i]||(t={name:i,url:n[i]});else t={name:et(n),url:n};return(r=c[t.name],r&&r.url===t.url)?r:(c[t.name]=t,t)}function y(n){n=n||c;for(var t in n)if(n.hasOwnProperty(t)&&n[t].state!==l)return!1;return!0}function st(n){n.state=ft;u(n.onpreload,function(n){n.call()})}function ht(n){n.state===t&&(n.state=nt,n.onpreload=[],rt({url:n.url,type:"cache"},function(){st(n)}))}function ct(){var n=arguments,t=n[n.length-1],r=[].slice.call(n,1),f=r[0];return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(f?(u(r,function(n){s(n)||!n||ht(v(n))}),b(v(n[0]),s(f)?f:function(){i.load.apply(null,r)})):b(v(n[0])),i)}function lt(){var n=arguments,t=n[n.length-1],r={};return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(u(n,function(n){n!==t&&(n=v(n),r[n.name]=n)}),u(n,function(n){n!==t&&(n=v(n),b(n,function(){y(r)&&f(t)}))}),i)}function b(n,t){if(t=t||w,n.state===l){t();return}if(n.state===tt){i.ready(n.name,t);return}if(n.state===nt){n.onpreload.push(function(){b(n,t)});return}n.state=tt;rt(n,function(){n.state=l;t();u(h[n.name],function(n){f(n)});o&&y()&&u(h.ALL,function(n){f(n)})})}function at(n){n=n||"";var t=n.split("?")[0].split(".");return t[t.length-1].toLowerCase()}function rt(t,i){function e(t){t=t||n.event;u.onload=u.onreadystatechange=u.onerror=null;i()}function o(f){f=f||n.event;(f.type==="load"||/loaded|complete/.test(u.readyState)&&(!r.documentMode||r.documentMode<9))&&(n.clearTimeout(t.errorTimeout),n.clearTimeout(t.cssTimeout),u.onload=u.onreadystatechange=u.onerror=null,i())}function s(){if(t.state!==l&&t.cssRetries<=20){for(var i=0,f=r.styleSheets.length;i<f;i++)if(r.styleSheets[i].href===u.href){o({type:"load"});return}t.cssRetries++;t.cssTimeout=n.setTimeout(s,250)}}var u,h,f;i=i||w;h=at(t.url);h==="css"?(u=r.createElement("link"),u.type="text/"+(t.type||"css"),u.rel="stylesheet",u.href=t.url,t.cssRetries=0,t.cssTimeout=n.setTimeout(s,500)):(u=r.createElement("script"),u.type="text/"+(t.type||"javascript"),u.src=t.url);u.onload=u.onreadystatechange=o;u.onerror=e;u.async=!1;u.defer=!1;t.errorTimeout=n.setTimeout(function(){e({type:"timeout"})},7e3);f=r.head||r.getElementsByTagName("head")[0];f.insertBefore(u,f.lastChild)}function vt(){for(var t,u=r.getElementsByTagName("script"),n=0,f=u.length;n<f;n++)if(t=u[n].getAttribute("data-headjs-load"),!!t){i.load(t);return}}function yt(n,t){var v,p,e;return n===r?(o?f(t):d.push(t),i):(s(n)&&(t=n,n="ALL"),a(n))?(v={},u(n,function(n){v[n]=c[n];i.ready(n,function(){y(v)&&f(t)})}),i):typeof n!="string"||!s(t)?i:(p=c[n],p&&p.state===l||n==="ALL"&&y()&&o)?(f(t),i):(e=h[n],e?e.push(t):e=h[n]=[t],i)}function e(){if(!r.body){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(e,50);return}o||(o=!0,vt(),u(d,function(n){f(n)}))}function k(){r.addEventListener?(r.removeEventListener("DOMContentLoaded",k,!1),e()):r.readyState==="complete"&&(r.detachEvent("onreadystatechange",k),e())}var r=n.document,d=[],h={},c={},ut="async"in r.createElement("script")||"MozAppearance"in r.documentElement.style||n.opera,o,g=n.head_conf&&n.head_conf.head||"head",i=n[g]=n[g]||function(){i.ready.apply(null,arguments)},nt=1,ft=2,tt=3,l=4,p;if(r.readyState==="complete")e();else if(r.addEventListener)r.addEventListener("DOMContentLoaded",k,!1),n.addEventListener("load",e,!1);else{r.attachEvent("onreadystatechange",k);n.attachEvent("onload",e);p=!1;try{p=!n.frameElement&&r.documentElement}catch(wt){}p&&p.doScroll&&function pt(){if(!o){try{p.doScroll("left")}catch(t){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(pt,50);return}e()}}()}i.load=i.js=ut?lt:ct;i.test=ot;i.ready=yt;i.ready(r,function(){y()&&u(h.ALL,function(n){f(n)});i.feature&&i.feature("domloaded",!0)})})(window);
/*
//# sourceMappingURL=head.min.js.map
*/

File diff suppressed because one or more lines are too long

412
docs/static/slides/plugin/markdown/markdown.js vendored Executable file
View File

@@ -0,0 +1,412 @@
/**
* The reveal.js markdown plugin. Handles parsing of
* markdown inside of presentations as well as loading
* of external markdown documents.
*/
(function( root, factory ) {
if (typeof define === 'function' && define.amd) {
root.marked = require( './marked' );
root.RevealMarkdown = factory( root.marked );
root.RevealMarkdown.initialize();
} else if( typeof exports === 'object' ) {
module.exports = factory( require( './marked' ) );
} else {
// Browser globals (root is window)
root.RevealMarkdown = factory( root.marked );
root.RevealMarkdown.initialize();
}
}( this, function( marked ) {
var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$',
DEFAULT_NOTES_SEPARATOR = 'notes?:',
DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';
/**
* Retrieves the markdown contents of a slide section
* element. Normalizes leading tabs/whitespace.
*/
function getMarkdownFromSlide( section ) {
// look for a <script> or <textarea data-template> wrapper
var template = section.querySelector( '[data-template]' ) || section.querySelector( 'script' );
// strip leading whitespace so it isn't evaluated as code
var text = ( template || section ).textContent;
// restore script end tags
text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );
var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
if( leadingTabs > 0 ) {
text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
}
else if( leadingWs > 1 ) {
text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
}
return text;
}
/**
* Given a markdown slide section element, this will
* return all arguments that aren't related to markdown
* parsing. Used to forward any other user-defined arguments
* to the output markdown slide.
*/
function getForwardedAttributes( section ) {
var attributes = section.attributes;
var result = [];
for( var i = 0, len = attributes.length; i < len; i++ ) {
var name = attributes[i].name,
value = attributes[i].value;
// disregard attributes that are used for markdown loading/parsing
if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
if( value ) {
result.push( name + '="' + value + '"' );
}
else {
result.push( name );
}
}
return result.join( ' ' );
}
/**
* Inspects the given options and fills out default
* values for what's not defined.
*/
function getSlidifyOptions( options ) {
options = options || {};
options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
options.attributes = options.attributes || '';
return options;
}
/**
* Helper function for constructing a markdown slide.
*/
function createMarkdownSlide( content, options ) {
options = getSlidifyOptions( options );
var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
if( notesMatch.length === 2 ) {
content = notesMatch[0] + '<aside class="notes">' + marked(notesMatch[1].trim()) + '</aside>';
}
// prevent script end tags in the content from interfering
// with parsing
content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER );
return '<script type="text/template">' + content + '</script>';
}
/**
* Parses a data string into multiple slides based
* on the passed in separator arguments.
*/
function slidify( markdown, options ) {
options = getSlidifyOptions( options );
var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
horizontalSeparatorRegex = new RegExp( options.separator );
var matches,
lastIndex = 0,
isHorizontal,
wasHorizontal = true,
content,
sectionStack = [];
// iterate until all blocks between separators are stacked up
while( matches = separatorRegex.exec( markdown ) ) {
notes = null;
// determine direction (horizontal by default)
isHorizontal = horizontalSeparatorRegex.test( matches[0] );
if( !isHorizontal && wasHorizontal ) {
// create vertical stack
sectionStack.push( [] );
}
// pluck slide content from markdown input
content = markdown.substring( lastIndex, matches.index );
if( isHorizontal && wasHorizontal ) {
// add to horizontal stack
sectionStack.push( content );
}
else {
// add to vertical stack
sectionStack[sectionStack.length-1].push( content );
}
lastIndex = separatorRegex.lastIndex;
wasHorizontal = isHorizontal;
}
// add the remaining slide
( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
var markdownSections = '';
// flatten the hierarchical stack, and insert <section data-markdown> tags
for( var i = 0, len = sectionStack.length; i < len; i++ ) {
// vertical
if( sectionStack[i] instanceof Array ) {
markdownSections += '<section '+ options.attributes +'>';
sectionStack[i].forEach( function( child ) {
markdownSections += '<section data-markdown>' + createMarkdownSlide( child, options ) + '</section>';
} );
markdownSections += '</section>';
}
else {
markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';
}
}
return markdownSections;
}
/**
* Parses any current data-markdown slides, splits
* multi-slide markdown into separate sections and
* handles loading of external markdown.
*/
function processSlides() {
var sections = document.querySelectorAll( '[data-markdown]'),
section;
for( var i = 0, len = sections.length; i < len; i++ ) {
section = sections[i];
if( section.getAttribute( 'data-markdown' ).length ) {
var xhr = new XMLHttpRequest(),
url = section.getAttribute( 'data-markdown' );
datacharset = section.getAttribute( 'data-charset' );
// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
if( datacharset != null && datacharset != '' ) {
xhr.overrideMimeType( 'text/html; charset=' + datacharset );
}
xhr.onreadystatechange = function() {
if( xhr.readyState === 4 ) {
// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
section.outerHTML = slidify( xhr.responseText, {
separator: section.getAttribute( 'data-separator' ),
verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
notesSeparator: section.getAttribute( 'data-separator-notes' ),
attributes: getForwardedAttributes( section )
});
}
else {
section.outerHTML = '<section data-state="alert">' +
'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
'Check your browser\'s JavaScript console for more details.' +
'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
'</section>';
}
}
};
xhr.open( 'GET', url, false );
try {
xhr.send();
}
catch ( e ) {
alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
}
}
else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
section.outerHTML = slidify( getMarkdownFromSlide( section ), {
separator: section.getAttribute( 'data-separator' ),
verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
notesSeparator: section.getAttribute( 'data-separator-notes' ),
attributes: getForwardedAttributes( section )
});
}
else {
section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
}
}
}
/**
* Check if a node value has the attributes pattern.
* If yes, extract it and add that value as one or several attributes
* the the terget element.
*
* You need Cache Killer on Chrome to see the effect on any FOM transformation
* directly on refresh (F5)
* http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
*/
function addAttributeInElement( node, elementTarget, separator ) {
var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
var nodeValue = node.nodeValue;
if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
var classes = matches[1];
nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
node.nodeValue = nodeValue;
while( matchesClass = mardownClassRegex.exec( classes ) ) {
elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
}
return true;
}
return false;
}
/**
* Add attributes to the parent element of a text node,
* or the element of an attribute node.
*/
function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
previousParentElement = element;
for( var i = 0; i < element.childNodes.length; i++ ) {
childElement = element.childNodes[i];
if ( i > 0 ) {
j = i - 1;
while ( j >= 0 ) {
aPreviousChildElement = element.childNodes[j];
if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
previousParentElement = aPreviousChildElement;
break;
}
j = j - 1;
}
}
parentSection = section;
if( childElement.nodeName == "section" ) {
parentSection = childElement ;
previousParentElement = childElement ;
}
if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
}
}
}
if ( element.nodeType == Node.COMMENT_NODE ) {
if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
addAttributeInElement( element, section, separatorSectionAttributes );
}
}
}
/**
* Converts any current data-markdown slides in the
* DOM to HTML.
*/
function convertSlides() {
var sections = document.querySelectorAll( '[data-markdown]');
for( var i = 0, len = sections.length; i < len; i++ ) {
var section = sections[i];
// Only parse the same slide once
if( !section.getAttribute( 'data-markdown-parsed' ) ) {
section.setAttribute( 'data-markdown-parsed', true )
var notes = section.querySelector( 'aside.notes' );
var markdown = getMarkdownFromSlide( section );
section.innerHTML = marked( markdown );
addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||
section.parentNode.getAttribute( 'data-element-attributes' ) ||
DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
section.getAttribute( 'data-attributes' ) ||
section.parentNode.getAttribute( 'data-attributes' ) ||
DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
// If there were notes, we need to re-add them after
// having overwritten the section's HTML
if( notes ) {
section.appendChild( notes );
}
}
}
}
// API
return {
initialize: function() {
if( typeof marked === 'undefined' ) {
throw 'The reveal.js Markdown plugin requires marked to be loaded';
}
if( typeof hljs !== 'undefined' ) {
marked.setOptions({
highlight: function( code, lang ) {
return hljs.highlightAuto( code, [lang] ).value;
}
});
}
var options = Reveal.getConfig().markdown;
if ( options ) {
marked.setOptions( options );
}
processSlides();
convertSlides();
},
// TODO: Do these belong in the API?
processSlides: processSlides,
convertSlides: convertSlides,
slidify: slidify
};
}));

File diff suppressed because one or more lines are too long

67
docs/static/slides/plugin/math/math.js vendored Executable file
View File

@@ -0,0 +1,67 @@
/**
* A plugin which enables rendering of math equations inside
* of reveal.js slides. Essentially a thin wrapper for MathJax.
*
* @author Hakim El Hattab
*/
var RevealMath = window.RevealMath || (function(){
var options = Reveal.getConfig().math || {};
options.mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
options.config = options.config || 'TeX-AMS_HTML-full';
loadScript( options.mathjax + '?config=' + options.config, function() {
MathJax.Hub.Config({
messageStyle: 'none',
tex2jax: {
inlineMath: [['$','$'],['\\(','\\)']] ,
skipTags: ['script','noscript','style','textarea','pre']
},
skipStartupTypeset: true
});
// Typeset followed by an immediate reveal.js layout since
// the typesetting process could affect slide height
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
MathJax.Hub.Queue( Reveal.layout );
// Reprocess equations in slides when they turn visible
Reveal.addEventListener( 'slidechanged', function( event ) {
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
} );
} );
function loadScript( url, callback ) {
var head = document.querySelector( 'head' );
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
// Wrapper for callback to make sure it only fires once
var finish = function() {
if( typeof callback === 'function' ) {
callback.call();
callback = null;
}
}
script.onload = finish;
// IE
script.onreadystatechange = function() {
if ( this.readyState === 'loaded' ) {
finish();
}
}
// Normal browsers
head.appendChild( script );
}
})();

13
docs/static/slides/plugin/multiplex/client.js vendored Executable file
View File

@@ -0,0 +1,13 @@
(function() {
var multiplex = Reveal.getConfig().multiplex;
var socketId = multiplex.id;
var socket = io.connect(multiplex.url);
socket.on(multiplex.id, function(data) {
// ignore data from sockets that aren't ours
if (data.socketId !== socketId) { return; }
if( window.location.host === 'localhost:1947' ) return;
Reveal.setState(data.state);
});
}());

64
docs/static/slides/plugin/multiplex/index.js vendored Executable file
View File

@@ -0,0 +1,64 @@
var http = require('http');
var express = require('express');
var fs = require('fs');
var io = require('socket.io');
var crypto = require('crypto');
var app = express();
var staticDir = express.static;
var server = http.createServer(app);
io = io(server);
var opts = {
port: process.env.PORT || 1948,
baseDir : __dirname + '/../../'
};
io.on( 'connection', function( socket ) {
socket.on('multiplex-statechanged', function(data) {
if (typeof data.secret == 'undefined' || data.secret == null || data.secret === '') return;
if (createHash(data.secret) === data.socketId) {
data.secret = null;
socket.broadcast.emit(data.socketId, data);
};
});
});
[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
app.use('/' + dir, staticDir(opts.baseDir + dir));
});
app.get("/", function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var stream = fs.createReadStream(opts.baseDir + '/index.html');
stream.on('error', function( error ) {
res.write('<style>body{font-family: sans-serif;}</style><h2>reveal.js multiplex server.</h2><a href="/token">Generate token</a>');
res.end();
});
stream.on('readable', function() {
stream.pipe(res);
});
});
app.get("/token", function(req,res) {
var ts = new Date().getTime();
var rand = Math.floor(Math.random()*9999999);
var secret = ts.toString() + rand.toString();
res.send({secret: secret, socketId: createHash(secret)});
});
var createHash = function(secret) {
var cipher = crypto.createCipher('blowfish', secret);
return(cipher.final('hex'));
};
// Actually listen
server.listen( opts.port || null );
var brown = '\033[33m',
green = '\033[32m',
reset = '\033[0m';
console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );

34
docs/static/slides/plugin/multiplex/master.js vendored Executable file
View File

@@ -0,0 +1,34 @@
(function() {
// Don't emit events from inside of notes windows
if ( window.location.search.match( /receiver/gi ) ) { return; }
var multiplex = Reveal.getConfig().multiplex;
var socket = io.connect( multiplex.url );
function post() {
var messageData = {
state: Reveal.getState(),
secret: multiplex.secret,
socketId: multiplex.id
};
socket.emit( 'multiplex-statechanged', messageData );
};
// post once the page is loaded, so the client follows also on "open URL".
window.addEventListener( 'load', post );
// Monitor events that trigger a change in state
Reveal.addEventListener( 'slidechanged', post );
Reveal.addEventListener( 'fragmentshown', post );
Reveal.addEventListener( 'fragmenthidden', post );
Reveal.addEventListener( 'overviewhidden', post );
Reveal.addEventListener( 'overviewshown', post );
Reveal.addEventListener( 'paused', post );
Reveal.addEventListener( 'resumed', post );
}());

View File

@@ -0,0 +1,19 @@
{
"name": "reveal-js-multiplex",
"version": "1.0.0",
"description": "reveal.js multiplex server",
"homepage": "http://revealjs.com",
"scripts": {
"start": "node index.js"
},
"engines": {
"node": "~4.1.1"
},
"dependencies": {
"express": "~4.13.3",
"grunt-cli": "~0.1.13",
"mustache": "~2.2.1",
"socket.io": "~1.3.7"
},
"license": "MIT"
}

View File

@@ -0,0 +1,65 @@
(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 ),
socketId = Math.random().toString().slice( 2 );
console.log( 'View slide notes at ' + window.location.origin + '/notes/' + socketId );
window.open( window.location.origin + '/notes/' + socketId, 'notes-' + socketId );
/**
* Posts the current slide data to the notes window
*/
function post() {
var slideElement = Reveal.getCurrentSlide(),
notesElement = slideElement.querySelector( 'aside.notes' );
var messageData = {
notes: '',
markdown: false,
socketId: socketId,
state: Reveal.getState()
};
// Look for notes defined in a slide attribute
if( slideElement.hasAttribute( 'data-notes' ) ) {
messageData.notes = slideElement.getAttribute( 'data-notes' );
}
// Look for notes defined in an aside element
if( notesElement ) {
messageData.notes = notesElement.innerHTML;
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
}
socket.emit( 'statechanged', messageData );
}
// When a new notes window connects, post our current state
socket.on( 'new-subscriber', function( data ) {
post();
} );
// When the state changes from inside of the speaker view
socket.on( 'statechanged-speaker', function( data ) {
Reveal.setState( data.state );
} );
// Monitor events that trigger a change in state
Reveal.addEventListener( 'slidechanged', post );
Reveal.addEventListener( 'fragmentshown', post );
Reveal.addEventListener( 'fragmenthidden', post );
Reveal.addEventListener( 'overviewhidden', post );
Reveal.addEventListener( 'overviewshown', post );
Reveal.addEventListener( 'paused', post );
Reveal.addEventListener( 'resumed', post );
// Post the initial state
post();
}());

View File

@@ -0,0 +1,69 @@
var http = require('http');
var express = require('express');
var fs = require('fs');
var io = require('socket.io');
var Mustache = require('mustache');
var app = express();
var staticDir = express.static;
var server = http.createServer(app);
io = io(server);
var opts = {
port : 1947,
baseDir : __dirname + '/../../'
};
io.on( 'connection', function( socket ) {
socket.on( 'new-subscriber', function( data ) {
socket.broadcast.emit( 'new-subscriber', data );
});
socket.on( 'statechanged', function( data ) {
delete data.state.overview;
socket.broadcast.emit( 'statechanged', data );
});
socket.on( 'statechanged-speaker', function( data ) {
delete data.state.overview;
socket.broadcast.emit( 'statechanged-speaker', data );
});
});
[ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) {
app.use( '/' + dir, staticDir( opts.baseDir + dir ) );
});
app.get('/', function( req, res ) {
res.writeHead( 200, { 'Content-Type': 'text/html' } );
fs.createReadStream( opts.baseDir + '/index.html' ).pipe( res );
});
app.get( '/notes/:socketId', function( req, res ) {
fs.readFile( opts.baseDir + 'plugin/notes-server/notes.html', function( err, data ) {
res.send( Mustache.to_html( data.toString(), {
socketId : req.params.socketId
}));
});
});
// Actually listen
server.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 in your JS console to go to the notes page' );
console.log( '3. Advance through your slides and your notes will advance automatically' );

View File

@@ -0,0 +1,585 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Slide Notes</title>
<style>
body {
font-family: Helvetica;
font-size: 18px;
}
#current-slide,
#upcoming-slide,
#speaker-controls {
padding: 6px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
#current-slide iframe,
#upcoming-slide iframe {
width: 100%;
height: 100%;
border: 1px solid #ddd;
}
#current-slide .label,
#upcoming-slide .label {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.overlay-element {
height: 34px;
line-height: 34px;
padding: 0 10px;
text-shadow: none;
background: rgba( 220, 220, 220, 0.8 );
color: #222;
font-size: 14px;
}
.overlay-element.interactive:hover {
background: rgba( 220, 220, 220, 1 );
}
#current-slide {
position: absolute;
width: 60%;
height: 100%;
top: 0;
left: 0;
padding-right: 0;
}
#upcoming-slide {
position: absolute;
width: 40%;
height: 40%;
right: 0;
top: 0;
}
/* Speaker controls */
#speaker-controls {
position: absolute;
top: 40%;
right: 0;
width: 40%;
height: 60%;
overflow: auto;
font-size: 18px;
}
.speaker-controls-time.hidden,
.speaker-controls-notes.hidden {
display: none;
}
.speaker-controls-time .label,
.speaker-controls-notes .label {
text-transform: uppercase;
font-weight: normal;
font-size: 0.66em;
color: #666;
margin: 0;
}
.speaker-controls-time {
border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
margin-bottom: 10px;
padding: 10px 16px;
padding-bottom: 20px;
cursor: pointer;
}
.speaker-controls-time .reset-button {
opacity: 0;
float: right;
color: #666;
text-decoration: none;
}
.speaker-controls-time:hover .reset-button {
opacity: 1;
}
.speaker-controls-time .timer,
.speaker-controls-time .clock {
width: 50%;
font-size: 1.9em;
}
.speaker-controls-time .timer {
float: left;
}
.speaker-controls-time .clock {
float: right;
text-align: right;
}
.speaker-controls-time span.mute {
color: #bbb;
}
.speaker-controls-notes {
padding: 10px 16px;
}
.speaker-controls-notes .value {
margin-top: 5px;
line-height: 1.4;
font-size: 1.2em;
}
/* Layout selector */
#speaker-layout {
position: absolute;
top: 10px;
right: 10px;
color: #222;
z-index: 10;
}
#speaker-layout select {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 0;
box-shadow: 0;
cursor: pointer;
opacity: 0;
font-size: 1em;
background-color: transparent;
-moz-appearance: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
#speaker-layout select:focus {
outline: none;
box-shadow: none;
}
.clear {
clear: both;
}
/* Speaker layout: Wide */
body[data-speaker-layout="wide"] #current-slide,
body[data-speaker-layout="wide"] #upcoming-slide {
width: 50%;
height: 45%;
padding: 6px;
}
body[data-speaker-layout="wide"] #current-slide {
top: 0;
left: 0;
}
body[data-speaker-layout="wide"] #upcoming-slide {
top: 0;
left: 50%;
}
body[data-speaker-layout="wide"] #speaker-controls {
top: 45%;
left: 0;
width: 100%;
height: 50%;
font-size: 1.25em;
}
/* Speaker layout: Tall */
body[data-speaker-layout="tall"] #current-slide,
body[data-speaker-layout="tall"] #upcoming-slide {
width: 45%;
height: 50%;
padding: 6px;
}
body[data-speaker-layout="tall"] #current-slide {
top: 0;
left: 0;
}
body[data-speaker-layout="tall"] #upcoming-slide {
top: 50%;
left: 0;
}
body[data-speaker-layout="tall"] #speaker-controls {
padding-top: 40px;
top: 0;
left: 45%;
width: 55%;
height: 100%;
font-size: 1.25em;
}
/* Speaker layout: Notes only */
body[data-speaker-layout="notes-only"] #current-slide,
body[data-speaker-layout="notes-only"] #upcoming-slide {
display: none;
}
body[data-speaker-layout="notes-only"] #speaker-controls {
padding-top: 40px;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 1.25em;
}
</style>
</head>
<body>
<div id="current-slide"></div>
<div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
<div id="speaker-controls">
<div class="speaker-controls-time">
<h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
<div class="clock">
<span class="clock-value">0:00 AM</span>
</div>
<div class="timer">
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
</div>
<div class="clear"></div>
</div>
<div class="speaker-controls-notes hidden">
<h4 class="label">Notes</h4>
<div class="value"></div>
</div>
</div>
<div id="speaker-layout" class="overlay-element interactive">
<span class="speaker-layout-label"></span>
<select class="speaker-layout-dropdown"></select>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="/plugin/markdown/marked.js"></script>
<script>
(function() {
var notes,
notesValue,
currentState,
currentSlide,
upcomingSlide,
layoutLabel,
layoutDropdown,
connected = false;
var socket = io.connect( window.location.origin ),
socketId = '{{socketId}}';
var SPEAKER_LAYOUTS = {
'default': 'Default',
'wide': 'Wide',
'tall': 'Tall',
'notes-only': 'Notes only'
};
socket.on( 'statechanged', function( data ) {
// ignore data from sockets that aren't ours
if( data.socketId !== socketId ) { return; }
if( connected === false ) {
connected = true;
setupKeyboard();
setupNotes();
setupTimer();
}
handleStateMessage( data );
} );
setupLayout();
// Load our presentation iframes
setupIframes();
// Once the iframes have loaded, emit a signal saying there's
// a new subscriber which will trigger a 'statechanged'
// message to be sent back
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.data );
if( data && data.namespace === 'reveal' ) {
if( /ready/.test( data.eventName ) ) {
socket.emit( 'new-subscriber', { socketId: socketId } );
}
}
// Messages sent by reveal.js inside of the current slide preview
if( data && data.namespace === 'reveal' ) {
if( /slidechanged|fragmentshown|fragmenthidden|overviewshown|overviewhidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
socket.emit( 'statechanged-speaker', { state: data.state } );
}
}
} );
/**
* Called when the main window sends an updated state.
*/
function handleStateMessage( data ) {
// Store the most recently set state to avoid circular loops
// applying the same state
currentState = JSON.stringify( data.state );
// No need for updating the notes in case of fragment changes
if ( data.notes ) {
notes.classList.remove( 'hidden' );
if( data.markdown ) {
notesValue.innerHTML = marked( data.notes );
}
else {
notesValue.innerHTML = data.notes;
}
}
else {
notes.classList.add( 'hidden' );
}
// Update the note slides
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
}
// Limit to max one state update per X ms
handleStateMessage = debounce( handleStateMessage, 200 );
/**
* Forward keyboard events to the current slide window.
* This enables keyboard events to work even if focus
* isn't set on the current slide iframe.
*/
function setupKeyboard() {
document.addEventListener( 'keydown', function( event ) {
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
} );
}
/**
* Creates the preview iframes.
*/
function setupIframes() {
var params = [
'receiver',
'progress=false',
'history=false',
'transition=none',
'backgroundTransition=none'
].join( '&' );
var currentURL = '/?' + params + '&postMessageEvents=true';
var upcomingURL = '/?' + params + '&controls=false';
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );
currentSlide.setAttribute( 'height', 1024 );
currentSlide.setAttribute( 'src', currentURL );
document.querySelector( '#current-slide' ).appendChild( currentSlide );
upcomingSlide = document.createElement( 'iframe' );
upcomingSlide.setAttribute( 'width', 640 );
upcomingSlide.setAttribute( 'height', 512 );
upcomingSlide.setAttribute( 'src', upcomingURL );
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
}
/**
* Setup the notes UI.
*/
function setupNotes() {
notes = document.querySelector( '.speaker-controls-notes' );
notesValue = document.querySelector( '.speaker-controls-notes .value' );
}
/**
* Create the timer and clock and start updating them
* at an interval.
*/
function setupTimer() {
var start = new Date(),
timeEl = document.querySelector( '.speaker-controls-time' ),
clockEl = timeEl.querySelector( '.clock-value' ),
hoursEl = timeEl.querySelector( '.hours-value' ),
minutesEl = timeEl.querySelector( '.minutes-value' ),
secondsEl = timeEl.querySelector( '.seconds-value' );
function _updateTimer() {
var diff, hours, minutes, seconds,
now = new Date();
diff = now.getTime() - start.getTime();
hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
seconds = Math.floor( ( diff / 1000 ) % 60 );
clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
hoursEl.innerHTML = zeroPadInteger( hours );
hoursEl.className = hours > 0 ? '' : 'mute';
minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
minutesEl.className = minutes > 0 ? '' : 'mute';
secondsEl.innerHTML = ':' + zeroPadInteger( seconds );
}
// Update once directly
_updateTimer();
// Then update every second
setInterval( _updateTimer, 1000 );
timeEl.addEventListener( 'click', function() {
start = new Date();
_updateTimer();
return false;
} );
}
/**
* Sets up the speaker view layout and layout selector.
*/
function setupLayout() {
layoutDropdown = document.querySelector( '.speaker-layout-dropdown' );
layoutLabel = document.querySelector( '.speaker-layout-label' );
// Render the list of available layouts
for( var id in SPEAKER_LAYOUTS ) {
var option = document.createElement( 'option' );
option.setAttribute( 'value', id );
option.textContent = SPEAKER_LAYOUTS[ id ];
layoutDropdown.appendChild( option );
}
// Monitor the dropdown for changes
layoutDropdown.addEventListener( 'change', function( event ) {
setLayout( layoutDropdown.value );
}, false );
// Restore any currently persisted layout
setLayout( getLayout() );
}
/**
* Sets a new speaker view layout. The layout is persisted
* in local storage.
*/
function setLayout( value ) {
var title = SPEAKER_LAYOUTS[ value ];
layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );
layoutDropdown.value = value;
document.body.setAttribute( 'data-speaker-layout', value );
// Persist locally
if( window.localStorage ) {
window.localStorage.setItem( 'reveal-speaker-layout', value );
}
}
/**
* Returns the ID of the most recently set speaker layout
* or our default layout if none has been set.
*/
function getLayout() {
if( window.localStorage ) {
var layout = window.localStorage.getItem( 'reveal-speaker-layout' );
if( layout ) {
return layout;
}
}
// Default to the first record in the layouts hash
for( var id in SPEAKER_LAYOUTS ) {
return id;
}
}
function zeroPadInteger( num ) {
var str = '00' + parseInt( num );
return str.substring( str.length - 2 );
}
/**
* Limits the frequency at which a function can be called.
*/
function debounce( fn, ms ) {
var lastTime = 0,
timeout;
return function() {
var args = arguments;
var context = this;
clearTimeout( timeout );
var timeSinceLastCall = Date.now() - lastTime;
if( timeSinceLastCall > ms ) {
fn.apply( context, args );
lastTime = Date.now();
}
else {
timeout = setTimeout( function() {
fn.apply( context, args );
lastTime = Date.now();
}, ms - timeSinceLastCall );
}
}
}
})();
</script>
</body>
</html>

759
docs/static/slides/plugin/notes/notes.html vendored Executable file
View File

@@ -0,0 +1,759 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Slide Notes</title>
<style>
body {
font-family: Helvetica;
font-size: 18px;
}
#current-slide,
#upcoming-slide,
#speaker-controls {
padding: 6px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
#current-slide iframe,
#upcoming-slide iframe {
width: 100%;
height: 100%;
border: 1px solid #ddd;
}
#current-slide .label,
#upcoming-slide .label {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.overlay-element {
height: 34px;
line-height: 34px;
padding: 0 10px;
text-shadow: none;
background: rgba( 220, 220, 220, 0.8 );
color: #222;
font-size: 14px;
}
.overlay-element.interactive:hover {
background: rgba( 220, 220, 220, 1 );
}
#current-slide {
position: absolute;
width: 60%;
height: 100%;
top: 0;
left: 0;
padding-right: 0;
}
#upcoming-slide {
position: absolute;
width: 40%;
height: 40%;
right: 0;
top: 0;
}
/* Speaker controls */
#speaker-controls {
position: absolute;
top: 40%;
right: 0;
width: 40%;
height: 60%;
overflow: auto;
font-size: 18px;
}
.speaker-controls-time.hidden,
.speaker-controls-notes.hidden {
display: none;
}
.speaker-controls-time .label,
.speaker-controls-pace .label,
.speaker-controls-notes .label {
text-transform: uppercase;
font-weight: normal;
font-size: 0.66em;
color: #666;
margin: 0;
}
.speaker-controls-time, .speaker-controls-pace {
border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
margin-bottom: 10px;
padding: 10px 16px;
padding-bottom: 20px;
cursor: pointer;
}
.speaker-controls-time .reset-button {
opacity: 0;
float: right;
color: #666;
text-decoration: none;
}
.speaker-controls-time:hover .reset-button {
opacity: 1;
}
.speaker-controls-time .timer,
.speaker-controls-time .clock {
width: 50%;
}
.speaker-controls-time .timer,
.speaker-controls-time .clock,
.speaker-controls-time .pacing .hours-value,
.speaker-controls-time .pacing .minutes-value,
.speaker-controls-time .pacing .seconds-value {
font-size: 1.9em;
}
.speaker-controls-time .timer {
float: left;
}
.speaker-controls-time .clock {
float: right;
text-align: right;
}
.speaker-controls-time span.mute {
opacity: 0.3;
}
.speaker-controls-time .pacing-title {
margin-top: 5px;
}
.speaker-controls-time .pacing.ahead {
color: blue;
}
.speaker-controls-time .pacing.on-track {
color: green;
}
.speaker-controls-time .pacing.behind {
color: red;
}
.speaker-controls-notes {
padding: 10px 16px;
}
.speaker-controls-notes .value {
margin-top: 5px;
line-height: 1.4;
font-size: 1.2em;
}
/* Layout selector */
#speaker-layout {
position: absolute;
top: 10px;
right: 10px;
color: #222;
z-index: 10;
}
#speaker-layout select {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 0;
box-shadow: 0;
cursor: pointer;
opacity: 0;
font-size: 1em;
background-color: transparent;
-moz-appearance: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
#speaker-layout select:focus {
outline: none;
box-shadow: none;
}
.clear {
clear: both;
}
/* Speaker layout: Wide */
body[data-speaker-layout="wide"] #current-slide,
body[data-speaker-layout="wide"] #upcoming-slide {
width: 50%;
height: 45%;
padding: 6px;
}
body[data-speaker-layout="wide"] #current-slide {
top: 0;
left: 0;
}
body[data-speaker-layout="wide"] #upcoming-slide {
top: 0;
left: 50%;
}
body[data-speaker-layout="wide"] #speaker-controls {
top: 45%;
left: 0;
width: 100%;
height: 50%;
font-size: 1.25em;
}
/* Speaker layout: Tall */
body[data-speaker-layout="tall"] #current-slide,
body[data-speaker-layout="tall"] #upcoming-slide {
width: 45%;
height: 50%;
padding: 6px;
}
body[data-speaker-layout="tall"] #current-slide {
top: 0;
left: 0;
}
body[data-speaker-layout="tall"] #upcoming-slide {
top: 50%;
left: 0;
}
body[data-speaker-layout="tall"] #speaker-controls {
padding-top: 40px;
top: 0;
left: 45%;
width: 55%;
height: 100%;
font-size: 1.25em;
}
/* Speaker layout: Notes only */
body[data-speaker-layout="notes-only"] #current-slide,
body[data-speaker-layout="notes-only"] #upcoming-slide {
display: none;
}
body[data-speaker-layout="notes-only"] #speaker-controls {
padding-top: 40px;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 1.25em;
}
@media screen and (max-width: 1080px) {
body[data-speaker-layout="default"] #speaker-controls {
font-size: 16px;
}
}
@media screen and (max-width: 900px) {
body[data-speaker-layout="default"] #speaker-controls {
font-size: 14px;
}
}
@media screen and (max-width: 800px) {
body[data-speaker-layout="default"] #speaker-controls {
font-size: 12px;
}
}
</style>
</head>
<body>
<div id="current-slide"></div>
<div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
<div id="speaker-controls">
<div class="speaker-controls-time">
<h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
<div class="clock">
<span class="clock-value">0:00 AM</span>
</div>
<div class="timer">
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
</div>
<div class="clear"></div>
<h4 class="label pacing-title" style="display: none">Pacing Time to finish current slide</h4>
<div class="pacing" style="display: none">
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
</div>
</div>
<div class="speaker-controls-notes hidden">
<h4 class="label">Notes</h4>
<div class="value"></div>
</div>
</div>
<div id="speaker-layout" class="overlay-element interactive">
<span class="speaker-layout-label"></span>
<select class="speaker-layout-dropdown"></select>
</div>
<script src="../../plugin/markdown/marked.js"></script>
<script>
(function() {
var notes,
notesValue,
currentState,
currentSlide,
upcomingSlide,
layoutLabel,
layoutDropdown,
connected = false;
var SPEAKER_LAYOUTS = {
'default': 'Default',
'wide': 'Wide',
'tall': 'Tall',
'notes-only': 'Notes only'
};
setupLayout();
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.data );
// The overview mode is only useful to the reveal.js instance
// where navigation occurs so we don't sync it
if( data.state ) delete data.state.overview;
// Messages sent by the notes plugin inside of the main window
if( data && data.namespace === 'reveal-notes' ) {
if( data.type === 'connect' ) {
handleConnectMessage( data );
}
else if( data.type === 'state' ) {
handleStateMessage( data );
}
}
// Messages sent by the reveal.js inside of the current slide preview
else if( data && data.namespace === 'reveal' ) {
if( /ready/.test( data.eventName ) ) {
// Send a message back to notify that the handshake is complete
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );
}
else if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ]} ), '*' );
}
}
} );
/**
* Called when the main window is trying to establish a
* connection.
*/
function handleConnectMessage( data ) {
if( connected === false ) {
connected = true;
setupIframes( data );
setupKeyboard();
setupNotes();
setupTimer();
}
}
/**
* Called when the main window sends an updated state.
*/
function handleStateMessage( data ) {
// Store the most recently set state to avoid circular loops
// applying the same state
currentState = JSON.stringify( data.state );
// No need for updating the notes in case of fragment changes
if ( data.notes ) {
notes.classList.remove( 'hidden' );
notesValue.style.whiteSpace = data.whitespace;
if( data.markdown ) {
notesValue.innerHTML = marked( data.notes );
}
else {
notesValue.innerHTML = data.notes;
}
}
else {
notes.classList.add( 'hidden' );
}
// Update the note slides
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
}
// Limit to max one state update per X ms
handleStateMessage = debounce( handleStateMessage, 200 );
/**
* Forward keyboard events to the current slide window.
* This enables keyboard events to work even if focus
* isn't set on the current slide iframe.
*/
function setupKeyboard() {
document.addEventListener( 'keydown', function( event ) {
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
} );
}
/**
* Creates the preview iframes.
*/
function setupIframes( data ) {
var params = [
'receiver',
'progress=false',
'history=false',
'transition=none',
'autoSlide=0',
'backgroundTransition=none'
].join( '&' );
var urlSeparator = /\?/.test(data.url) ? '&' : '?';
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );
currentSlide.setAttribute( 'height', 1024 );
currentSlide.setAttribute( 'src', currentURL );
document.querySelector( '#current-slide' ).appendChild( currentSlide );
upcomingSlide = document.createElement( 'iframe' );
upcomingSlide.setAttribute( 'width', 640 );
upcomingSlide.setAttribute( 'height', 512 );
upcomingSlide.setAttribute( 'src', upcomingURL );
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
}
/**
* Setup the notes UI.
*/
function setupNotes() {
notes = document.querySelector( '.speaker-controls-notes' );
notesValue = document.querySelector( '.speaker-controls-notes .value' );
}
function getTimings() {
var slides = Reveal.getSlides();
var defaultTiming = Reveal.getConfig().defaultTiming;
if (defaultTiming == null) {
return null;
}
var timings = [];
for ( var i in slides ) {
var slide = slides[i];
var timing = defaultTiming;
if( slide.hasAttribute( 'data-timing' )) {
var t = slide.getAttribute( 'data-timing' );
timing = parseInt(t);
if( isNaN(timing) ) {
console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming);
timing = defaultTiming;
}
}
timings.push(timing);
}
return timings;
}
/**
* Return the number of seconds allocated for presenting
* all slides up to and including this one.
*/
function getTimeAllocated(timings) {
var slides = Reveal.getSlides();
var allocated = 0;
var currentSlide = Reveal.getSlidePastCount();
for (var i in slides.slice(0, currentSlide + 1)) {
allocated += timings[i];
}
return allocated;
}
/**
* Create the timer and clock and start updating them
* at an interval.
*/
function setupTimer() {
var start = new Date(),
timeEl = document.querySelector( '.speaker-controls-time' ),
clockEl = timeEl.querySelector( '.clock-value' ),
hoursEl = timeEl.querySelector( '.hours-value' ),
minutesEl = timeEl.querySelector( '.minutes-value' ),
secondsEl = timeEl.querySelector( '.seconds-value' ),
pacingTitleEl = timeEl.querySelector( '.pacing-title' ),
pacingEl = timeEl.querySelector( '.pacing' ),
pacingHoursEl = pacingEl.querySelector( '.hours-value' ),
pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ),
pacingSecondsEl = pacingEl.querySelector( '.seconds-value' );
var timings = getTimings();
if (timings !== null) {
pacingTitleEl.style.removeProperty('display');
pacingEl.style.removeProperty('display');
}
function _displayTime( hrEl, minEl, secEl, time) {
var sign = Math.sign(time) == -1 ? "-" : "";
time = Math.abs(Math.round(time / 1000));
var seconds = time % 60;
var minutes = Math.floor( time / 60 ) % 60 ;
var hours = Math.floor( time / ( 60 * 60 )) ;
hrEl.innerHTML = sign + zeroPadInteger( hours );
if (hours == 0) {
hrEl.classList.add( 'mute' );
}
else {
hrEl.classList.remove( 'mute' );
}
minEl.innerHTML = ':' + zeroPadInteger( minutes );
if (hours == 0 && minutes == 0) {
minEl.classList.add( 'mute' );
}
else {
minEl.classList.remove( 'mute' );
}
secEl.innerHTML = ':' + zeroPadInteger( seconds );
}
function _updateTimer() {
var diff, hours, minutes, seconds,
now = new Date();
diff = now.getTime() - start.getTime();
clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
_displayTime( hoursEl, minutesEl, secondsEl, diff );
if (timings !== null) {
_updatePacing(diff);
}
}
function _updatePacing(diff) {
var slideEndTiming = getTimeAllocated(timings) * 1000;
var currentSlide = Reveal.getSlidePastCount();
var currentSlideTiming = timings[currentSlide] * 1000;
var timeLeftCurrentSlide = slideEndTiming - diff;
if (timeLeftCurrentSlide < 0) {
pacingEl.className = 'pacing behind';
}
else if (timeLeftCurrentSlide < currentSlideTiming) {
pacingEl.className = 'pacing on-track';
}
else {
pacingEl.className = 'pacing ahead';
}
_displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide );
}
// Update once directly
_updateTimer();
// Then update every second
setInterval( _updateTimer, 1000 );
function _resetTimer() {
if (timings == null) {
start = new Date();
}
else {
// Reset timer to beginning of current slide
var slideEndTiming = getTimeAllocated(timings) * 1000;
var currentSlide = Reveal.getSlidePastCount();
var currentSlideTiming = timings[currentSlide] * 1000;
var previousSlidesTiming = slideEndTiming - currentSlideTiming;
var now = new Date();
start = new Date(now.getTime() - previousSlidesTiming);
}
_updateTimer();
}
timeEl.addEventListener( 'click', function() {
_resetTimer();
return false;
} );
}
/**
* Sets up the speaker view layout and layout selector.
*/
function setupLayout() {
layoutDropdown = document.querySelector( '.speaker-layout-dropdown' );
layoutLabel = document.querySelector( '.speaker-layout-label' );
// Render the list of available layouts
for( var id in SPEAKER_LAYOUTS ) {
var option = document.createElement( 'option' );
option.setAttribute( 'value', id );
option.textContent = SPEAKER_LAYOUTS[ id ];
layoutDropdown.appendChild( option );
}
// Monitor the dropdown for changes
layoutDropdown.addEventListener( 'change', function( event ) {
setLayout( layoutDropdown.value );
}, false );
// Restore any currently persisted layout
setLayout( getLayout() );
}
/**
* Sets a new speaker view layout. The layout is persisted
* in local storage.
*/
function setLayout( value ) {
var title = SPEAKER_LAYOUTS[ value ];
layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );
layoutDropdown.value = value;
document.body.setAttribute( 'data-speaker-layout', value );
// Persist locally
if( supportsLocalStorage() ) {
window.localStorage.setItem( 'reveal-speaker-layout', value );
}
}
/**
* Returns the ID of the most recently set speaker layout
* or our default layout if none has been set.
*/
function getLayout() {
if( supportsLocalStorage() ) {
var layout = window.localStorage.getItem( 'reveal-speaker-layout' );
if( layout ) {
return layout;
}
}
// Default to the first record in the layouts hash
for( var id in SPEAKER_LAYOUTS ) {
return id;
}
}
function supportsLocalStorage() {
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
return true;
}
catch( e ) {
return false;
}
}
function zeroPadInteger( num ) {
var str = '00' + parseInt( num );
return str.substring( str.length - 2 );
}
/**
* Limits the frequency at which a function can be called.
*/
function debounce( fn, ms ) {
var lastTime = 0,
timeout;
return function() {
var args = arguments;
var context = this;
clearTimeout( timeout );
var timeSinceLastCall = Date.now() - lastTime;
if( timeSinceLastCall > ms ) {
fn.apply( context, args );
lastTime = Date.now();
}
else {
timeout = setTimeout( function() {
fn.apply( context, args );
lastTime = Date.now();
}, ms - timeSinceLastCall );
}
}
}
})();
</script>
</body>
</html>

155
docs/static/slides/plugin/notes/notes.js vendored Executable file
View File

@@ -0,0 +1,155 @@
/**
* Handles opening of and synchronization with the reveal.js
* notes window.
*
* Handshake process:
* 1. This window posts 'connect' to notes window
* - Includes URL of presentation to show
* 2. Notes window responds with 'connected' when it is available
* 3. This window proceeds to send the current presentation state
* to the notes window
*/
var RevealNotes = (function() {
function openNotes( notesFilePath ) {
if( !notesFilePath ) {
var jsFileLocation = document.querySelector('script[src$="notes.js"]').src; // this js file path
jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path
notesFilePath = jsFileLocation + 'notes.html';
}
var notesPopup = window.open( notesFilePath, 'reveal.js - Notes', 'width=1100,height=700' );
// Allow popup window access to Reveal API
notesPopup.Reveal = this.Reveal;
/**
* Connect to the notes window through a postmessage handshake.
* Using postmessage enables us to work in situations where the
* origins differ, such as a presentation being opened from the
* file system.
*/
function connect() {
// Keep trying to connect until we get a 'connected' message back
var connectInterval = setInterval( function() {
notesPopup.postMessage( JSON.stringify( {
namespace: 'reveal-notes',
type: 'connect',
url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
state: Reveal.getState()
} ), '*' );
}, 500 );
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
} );
}
/**
* Posts the current slide data to the notes window
*/
function post( event ) {
var slideElement = Reveal.getCurrentSlide(),
notesElement = slideElement.querySelector( 'aside.notes' ),
fragmentElement = slideElement.querySelector( '.current-fragment' );
var messageData = {
namespace: 'reveal-notes',
type: 'state',
notes: '',
markdown: false,
whitespace: 'normal',
state: Reveal.getState()
};
// Look for notes defined in a slide attribute
if( slideElement.hasAttribute( 'data-notes' ) ) {
messageData.notes = slideElement.getAttribute( 'data-notes' );
messageData.whitespace = 'pre-wrap';
}
// Look for notes defined in a fragment
if( fragmentElement ) {
var fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
if( fragmentNotes ) {
notesElement = fragmentNotes;
}
else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
messageData.notes = fragmentElement.getAttribute( 'data-notes' );
messageData.whitespace = 'pre-wrap';
// In case there are slide notes
notesElement = null;
}
}
// Look for notes defined in an aside element
if( notesElement ) {
messageData.notes = notesElement.innerHTML;
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
}
notesPopup.postMessage( JSON.stringify( messageData ), '*' );
}
/**
* Called once we have established a connection to the notes
* window.
*/
function onConnected() {
// Monitor events that trigger a change in state
Reveal.addEventListener( 'slidechanged', post );
Reveal.addEventListener( 'fragmentshown', post );
Reveal.addEventListener( 'fragmenthidden', post );
Reveal.addEventListener( 'overviewhidden', post );
Reveal.addEventListener( 'overviewshown', post );
Reveal.addEventListener( 'paused', post );
Reveal.addEventListener( 'resumed', post );
// Post the initial state
post();
}
connect();
}
if( !/receiver/i.test( window.location.search ) ) {
// If the there's a 'notes' query set, open directly
if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
openNotes();
}
// Open the notes when the 's' key is hit
document.addEventListener( 'keydown', function( event ) {
// Disregard the event if the target is editable or a
// modifier is present
if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
// Disregard the event if keyboard is disabled
if ( Reveal.getConfig().keyboard === false ) return;
if( event.keyCode === 83 ) {
event.preventDefault();
openNotes();
}
}, false );
// Show our keyboard shortcut in the reveal.js help overlay
if( window.Reveal ) Reveal.registerKeyboardShortcut( 'S', 'Speaker notes view' );
}
return { open: openNotes };
})();

View File

@@ -0,0 +1,69 @@
/**
* phantomjs script for printing presentations to PDF.
*
* Example:
* phantomjs print-pdf.js "http://revealjs.com?print-pdf" reveal-demo.pdf
*
* @author Manuel Bieh (https://github.com/manuelbieh)
* @author Hakim El Hattab (https://github.com/hakimel)
* @author Manuel Riezebosch (https://github.com/riezebosch)
*/
// html2pdf.js
var system = require( 'system' );
var probePage = new WebPage();
var printPage = new WebPage();
var inputFile = system.args[1] || 'index.html?print-pdf';
var outputFile = system.args[2] || 'slides.pdf';
if( outputFile.match( /\.pdf$/gi ) === null ) {
outputFile += '.pdf';
}
console.log( 'Export PDF: Reading reveal.js config [1/4]' );
probePage.open( inputFile, function( status ) {
console.log( 'Export PDF: Preparing print layout [2/4]' );
var config = probePage.evaluate( function() {
return Reveal.getConfig();
} );
if( config ) {
printPage.paperSize = {
width: Math.floor( config.width * ( 1 + config.margin ) ),
height: Math.floor( config.height * ( 1 + config.margin ) ),
border: 0
};
printPage.open( inputFile, function( status ) {
console.log( 'Export PDF: Preparing pdf [3/4]')
printPage.evaluate(function() {
Reveal.isReady() ? window.callPhantom() : Reveal.addEventListener( 'pdf-ready', window.callPhantom );
});
} );
printPage.onCallback = function(data) {
// For some reason we need to "jump the queue" for syntax highlighting to work.
// See: http://stackoverflow.com/a/3580132/129269
setTimeout(function() {
console.log( 'Export PDF: Writing file [4/4]' );
printPage.render( outputFile );
console.log( 'Export PDF: Finished successfully!' );
phantom.exit();
}, 0);
};
}
else {
console.log( 'Export PDF: Unable to read reveal.js config. Make sure the input address points to a reveal.js page.' );
phantom.exit(1);
}
} );

206
docs/static/slides/plugin/search/search.js vendored Executable file
View File

@@ -0,0 +1,206 @@
/*
* Handles finding a text string anywhere in the slides and showing the next occurrence to the user
* by navigatating to that slide and highlighting it.
*
* By Jon Snyder <snyder.jon@gmail.com>, February 2013
*/
var RevealSearch = (function() {
var matchedSlides;
var currentMatchedIndex;
var searchboxDirty;
var myHilitor;
// Original JavaScript code by Chirp Internet: www.chirp.com.au
// Please acknowledge use of this code by including this header.
// 2/2013 jon: modified regex to display any match, not restricted to word boundaries.
function Hilitor(id, tag)
{
var targetNode = document.getElementById(id) || document.body;
var hiliteTag = tag || "EM";
var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$");
var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"];
var wordColor = [];
var colorIdx = 0;
var matchRegex = "";
var matchingSlides = [];
this.setRegex = function(input)
{
input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
matchRegex = new RegExp("(" + input + ")","i");
}
this.getRegex = function()
{
return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " ");
}
// recursively apply word highlighting
this.hiliteWords = function(node)
{
if(node == undefined || !node) return;
if(!matchRegex) return;
if(skipTags.test(node.nodeName)) return;
if(node.hasChildNodes()) {
for(var i=0; i < node.childNodes.length; i++)
this.hiliteWords(node.childNodes[i]);
}
if(node.nodeType == 3) { // NODE_TEXT
if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) {
//find the slide's section element and save it in our list of matching slides
var secnode = node;
while (secnode != null && secnode.nodeName != 'SECTION') {
secnode = secnode.parentNode;
}
var slideIndex = Reveal.getIndices(secnode);
var slidelen = matchingSlides.length;
var alreadyAdded = false;
for (var i=0; i < slidelen; i++) {
if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) {
alreadyAdded = true;
}
}
if (! alreadyAdded) {
matchingSlides.push(slideIndex);
}
if(!wordColor[regs[0].toLowerCase()]) {
wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length];
}
var match = document.createElement(hiliteTag);
match.appendChild(document.createTextNode(regs[0]));
match.style.backgroundColor = wordColor[regs[0].toLowerCase()];
match.style.fontStyle = "inherit";
match.style.color = "#000";
var after = node.splitText(regs.index);
after.nodeValue = after.nodeValue.substring(regs[0].length);
node.parentNode.insertBefore(match, after);
}
}
};
// remove highlighting
this.remove = function()
{
var arr = document.getElementsByTagName(hiliteTag);
while(arr.length && (el = arr[0])) {
el.parentNode.replaceChild(el.firstChild, el);
}
};
// start highlighting at target node
this.apply = function(input)
{
if(input == undefined || !input) return;
this.remove();
this.setRegex(input);
this.hiliteWords(targetNode);
return matchingSlides;
};
}
function openSearch() {
//ensure the search term input dialog is visible and has focus:
var inputboxdiv = document.getElementById("searchinputdiv");
var inputbox = document.getElementById("searchinput");
inputboxdiv.style.display = "inline";
inputbox.focus();
inputbox.select();
}
function closeSearch() {
var inputboxdiv = document.getElementById("searchinputdiv");
inputboxdiv.style.display = "none";
if(myHilitor) myHilitor.remove();
}
function toggleSearch() {
var inputboxdiv = document.getElementById("searchinputdiv");
if (inputboxdiv.style.display !== "inline") {
openSearch();
}
else {
closeSearch();
}
}
function doSearch() {
//if there's been a change in the search term, perform a new search:
if (searchboxDirty) {
var searchstring = document.getElementById("searchinput").value;
if (searchstring === '') {
if(myHilitor) myHilitor.remove();
matchedSlides = null;
}
else {
//find the keyword amongst the slides
myHilitor = new Hilitor("slidecontent");
matchedSlides = myHilitor.apply(searchstring);
currentMatchedIndex = 0;
}
}
if (matchedSlides) {
//navigate to the next slide that has the keyword, wrapping to the first if necessary
if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) {
currentMatchedIndex = 0;
}
if (matchedSlides.length > currentMatchedIndex) {
Reveal.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v);
currentMatchedIndex++;
}
}
}
var dom = {};
dom.wrapper = document.querySelector( '.reveal' );
if( !dom.wrapper.querySelector( '.searchbox' ) ) {
var searchElement = document.createElement( 'div' );
searchElement.id = "searchinputdiv";
searchElement.classList.add( 'searchdiv' );
searchElement.style.position = 'absolute';
searchElement.style.top = '10px';
searchElement.style.right = '10px';
searchElement.style.zIndex = 10;
//embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/:
searchElement.innerHTML = '<span><input type="search" id="searchinput" class="searchinput" style="vertical-align: top;"/><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJiSURBVHjatFZNaxNBGH5md+Mmu92NVdKDRipSAyqCghgQD4L4cRe86UUtAQ+eFCxoa4/25EXBFi8eBE+eRPoDhB6KgiiixdAPCEkx2pjvTXadd9yNsflwuyUDD/O+u8PzzDPvzOwyx3EwyCZhwG3gAkp7MnpjgbopjsltcD4gjuXZZKeAR348MYLYTm3LzOs/y3j3JTfZxgXWXmTuwPHIc4VmoOmv5IrI53+AO2DdHLjkDWQ3GoEEVFXtXQOvkSnPWcyUceviLhwbDYv8/XIVj97kse7TodLvZXxYxrPUHkQ1ufXs3FEdybEIxucySOesoNvUgWU1cP3MkCBfTFdw9fGaAMVmRELq7LBw2Q3/FaAxxWIRpw+ZIr/7IouPqzUBiqmdHAv7EuhRAwf1er2Vy4x1jW3b2d5Jfvu5IPp7l2LYbcgCFFNb+FoJ7oBqEAqFMPNqFcmEgVMJDfMT+1tvN0pNjERlMS6QA5pFOKxiKVPFhakPeL3It+WGJUDxt2wFR+JhzI7v5ctkd8DXOZAkCYYxhO+lKm4+Xfqz/rIixBuNBl7eOYzkQQNzqX249mRl6zUgEcYkaJrGhUwBinVdh6IouPzwE6/DL5w4oLkH8y981aDf+uq6hlKpJESiUdNfDZi7/ehG9K6KfiA3pml0PLcsq+cSMTj2NL9ukc4UOmz7AZ3+crkC4mHujFvXNaMFB3bEr8xPS6p5O+jXxq4VZtaen7/PwzrntjcLUE0iHPS1Ud1cdiEJl/8WivZk0wXd7zWOMkeF8s0CcAmkNrC2nvXZDbbbN73ccYnZoH9bfgswAFzAe9/h3dbKAAAAAElFTkSuQmCC" id="searchbutton" class="searchicon" style="vertical-align: top; margin-top: -1px;"/></span>';
dom.wrapper.appendChild( searchElement );
}
document.getElementById("searchbutton").addEventListener( 'click', function(event) {
doSearch();
}, false );
document.getElementById("searchinput").addEventListener( 'keyup', function( event ) {
switch (event.keyCode) {
case 13:
event.preventDefault();
doSearch();
searchboxDirty = false;
break;
default:
searchboxDirty = true;
}
}, false );
document.addEventListener( 'keydown', function( event ) {
if( event.key == "F" && (event.ctrlKey || event.metaKey) ) {//Control+Shift+f
event.preventDefault();
toggleSearch();
}
}, false );
if( window.Reveal ) Reveal.registerKeyboardShortcut( 'Ctrl-Shift-F', 'Search' );
closeSearch();
return { open: openSearch };
})();

272
docs/static/slides/plugin/zoom-js/zoom.js vendored Executable file
View File

@@ -0,0 +1,272 @@
// Custom reveal.js integration
(function(){
var revealElement = document.querySelector( '.reveal' );
if( revealElement ) {
revealElement.addEventListener( 'mousedown', function( event ) {
var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
var zoomLevel = ( Reveal.getConfig().zoomLevel ? Reveal.getConfig().zoomLevel : 2 );
if( event[ modifier ] && !Reveal.isOverview() ) {
event.preventDefault();
zoom.to({
x: event.clientX,
y: event.clientY,
scale: zoomLevel,
pan: false
});
}
} );
}
})();
/*!
* zoom.js 0.3 (modified for use with reveal.js)
* http://lab.hakim.se/zoom-js
* MIT licensed
*
* Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
*/
var zoom = (function(){
// The current zoom level (scale)
var level = 1;
// The current mouse position, used for panning
var mouseX = 0,
mouseY = 0;
// Timeout before pan is activated
var panEngageTimeout = -1,
panUpdateInterval = -1;
// Check for transform support so that we can fallback otherwise
var supportsTransforms = '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;
if( supportsTransforms ) {
// The easing that will be applied when we zoom in/out
document.body.style.transition = 'transform 0.8s ease';
document.body.style.OTransition = '-o-transform 0.8s ease';
document.body.style.msTransition = '-ms-transform 0.8s ease';
document.body.style.MozTransition = '-moz-transform 0.8s ease';
document.body.style.WebkitTransition = '-webkit-transform 0.8s ease';
}
// Zoom out if the user hits escape
document.addEventListener( 'keyup', function( event ) {
if( level !== 1 && event.keyCode === 27 ) {
zoom.out();
}
} );
// Monitor mouse movement for panning
document.addEventListener( 'mousemove', function( event ) {
if( level !== 1 ) {
mouseX = event.clientX;
mouseY = event.clientY;
}
} );
/**
* Applies the CSS required to zoom in, prefers the use of CSS3
* transforms but falls back on zoom for IE.
*
* @param {Object} rect
* @param {Number} scale
*/
function magnify( rect, scale ) {
var scrollOffset = getScrollOffset();
// Ensure a width/height is set
rect.width = rect.width || 1;
rect.height = rect.height || 1;
// Center the rect within the zoomed viewport
rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2;
rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2;
if( supportsTransforms ) {
// Reset
if( scale === 1 ) {
document.body.style.transform = '';
document.body.style.OTransform = '';
document.body.style.msTransform = '';
document.body.style.MozTransform = '';
document.body.style.WebkitTransform = '';
}
// Scale
else {
var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')';
document.body.style.transformOrigin = origin;
document.body.style.OTransformOrigin = origin;
document.body.style.msTransformOrigin = origin;
document.body.style.MozTransformOrigin = origin;
document.body.style.WebkitTransformOrigin = origin;
document.body.style.transform = transform;
document.body.style.OTransform = transform;
document.body.style.msTransform = transform;
document.body.style.MozTransform = transform;
document.body.style.WebkitTransform = transform;
}
}
else {
// Reset
if( scale === 1 ) {
document.body.style.position = '';
document.body.style.left = '';
document.body.style.top = '';
document.body.style.width = '';
document.body.style.height = '';
document.body.style.zoom = '';
}
// Scale
else {
document.body.style.position = 'relative';
document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px';
document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px';
document.body.style.width = ( scale * 100 ) + '%';
document.body.style.height = ( scale * 100 ) + '%';
document.body.style.zoom = scale;
}
}
level = scale;
if( document.documentElement.classList ) {
if( level !== 1 ) {
document.documentElement.classList.add( 'zoomed' );
}
else {
document.documentElement.classList.remove( 'zoomed' );
}
}
}
/**
* Pan the document when the mosue cursor approaches the edges
* of the window.
*/
function pan() {
var range = 0.12,
rangeX = window.innerWidth * range,
rangeY = window.innerHeight * range,
scrollOffset = getScrollOffset();
// Up
if( mouseY < rangeY ) {
window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
}
// Down
else if( mouseY > window.innerHeight - rangeY ) {
window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
}
// Left
if( mouseX < rangeX ) {
window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
}
// Right
else if( mouseX > window.innerWidth - rangeX ) {
window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
}
}
function getScrollOffset() {
return {
x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset
}
}
return {
/**
* Zooms in on either a rectangle or HTML element.
*
* @param {Object} options
* - element: HTML element to zoom in on
* OR
* - x/y: coordinates in non-transformed space to zoom in on
* - width/height: the portion of the screen to zoom in on
* - scale: can be used instead of width/height to explicitly set scale
*/
to: function( options ) {
// Due to an implementation limitation we can't zoom in
// to another element without zooming out first
if( level !== 1 ) {
zoom.out();
}
else {
options.x = options.x || 0;
options.y = options.y || 0;
// If an element is set, that takes precedence
if( !!options.element ) {
// Space around the zoomed in element to leave on screen
var padding = 20;
var bounds = options.element.getBoundingClientRect();
options.x = bounds.left - padding;
options.y = bounds.top - padding;
options.width = bounds.width + ( padding * 2 );
options.height = bounds.height + ( padding * 2 );
}
// If width/height values are set, calculate scale from those values
if( options.width !== undefined && options.height !== undefined ) {
options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
}
if( options.scale > 1 ) {
options.x *= options.scale;
options.y *= options.scale;
magnify( options, options.scale );
if( options.pan !== false ) {
// Wait with engaging panning as it may conflict with the
// zoom transition
panEngageTimeout = setTimeout( function() {
panUpdateInterval = setInterval( pan, 1000 / 60 );
}, 800 );
}
}
}
},
/**
* Resets the document zoom state to its default.
*/
out: function() {
clearTimeout( panEngageTimeout );
clearInterval( panUpdateInterval );
magnify( { x: 0, y: 0 }, 1 );
level = 1;
},
// Alias
magnify: function( options ) { this.to( options ) },
reset: function() { this.out() },
zoomLevel: function() {
return level;
}
}
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

6
docs/static/slides/reveal.js.txt vendored Executable file
View File

@@ -0,0 +1,6 @@
The files and folders in this folder (folder slides) are based on reveal.js
MIT licensed
Copyright (C) 2015 Hakim El Hattab, http://hakim.se
see https://github.com/hakimel/reveal.js/