merge in multiplex (#98)
This commit is contained in:
commit
2bd5e8b65b
9 changed files with 241 additions and 10 deletions
11
README.md
11
README.md
|
@ -344,6 +344,17 @@ Reveal.toggleOverview();
|
||||||
### Fullscreen mode
|
### Fullscreen mode
|
||||||
Just press »F« on your keyboard to show your presentation in fullscreen mode. Press the »ESC« key to exit fullscreen mode.
|
Just press »F« on your keyboard to show your presentation in fullscreen mode. Press the »ESC« key to exit fullscreen mode.
|
||||||
|
|
||||||
|
## Multiplexing
|
||||||
|
|
||||||
|
The multiplex plugin allows your audience to view the slides on their own phone, tablet or laptop. As the master navigates the slides, all clients will update in real time. See a demo at [http://revealjs.jit.su/](http://revealjs.jit.su)
|
||||||
|
|
||||||
|
Configuration is via the multiplex object in index.html. To generate unique secret and token values, visit [revealjs.jit.su/token](revealjs.jit.su/token)
|
||||||
|
|
||||||
|
multiplex.secret should only be configured on those pages you wish to be able to control slide navigatoin for all clients. Multi-master configurations work, but if you don't wish your audience to be able to control your slides, set the secret to null. In this master/slave setup, you should create a publicly accessible page with secret set to null, and a protected page containing your secret.
|
||||||
|
|
||||||
|
You are very welcome to use the server running at reveal.jit.su, however availability and stability are not guaranteed. For anything mission critical I recommend you run your own server. It is simple to deploy to nodejitsu or run on your own environment.
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
|
||||||
## PDF Export
|
## PDF Export
|
||||||
|
|
||||||
|
|
13
index.html
13
index.html
|
@ -358,6 +358,14 @@ function linkify( selector ) {
|
||||||
|
|
||||||
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
|
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
|
||||||
transition: Reveal.getQueryHash().transition || 'default', // default/cube/page/concave/zoom/linear/fade/none
|
transition: Reveal.getQueryHash().transition || 'default', // default/cube/page/concave/zoom/linear/fade/none
|
||||||
|
globals: {
|
||||||
|
// Generate a unique id and secret at http://revealjs.jit.su/token
|
||||||
|
multiplex: {
|
||||||
|
id: '7d10234555b923e2',
|
||||||
|
secret: '13627859051503309760',
|
||||||
|
url: 'revealjs.jit.su:80'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Optional libraries used to extend on reveal.js
|
// Optional libraries used to extend on reveal.js
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
@ -366,8 +374,11 @@ function linkify( selector ) {
|
||||||
{ src: 'plugin/markdown/markdown.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, callback: function() { hljs.initHighlightingOnLoad(); } },
|
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
|
||||||
{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
|
{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
|
||||||
|
// { src: 'plugin/remotes/remotes.js', async: true, condition: function() { return !!document.body.classList; } },
|
||||||
|
// { src: 'socket.io/socket.io.js', async: true, condition: function() { return !!document.body.classList; } },
|
||||||
|
// { src: 'plugin/multiplex/client.js', async: true, condition: function() { return !!document.body.classList; } },
|
||||||
|
// { src: 'plugin/multiplex/master.js', async: true, condition: function() { return !!document.body.classList; } },
|
||||||
{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
|
{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
|
||||||
// { src: 'plugin/remotes/remotes.js', async: true, condition: function() { return !!document.body.classList; } }
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,9 @@ var Reveal = (function(){
|
||||||
// Copy options over to our config object
|
// Copy options over to our config object
|
||||||
extend( config, options );
|
extend( config, options );
|
||||||
|
|
||||||
|
// Push up globals
|
||||||
|
window.globals = config.globals;
|
||||||
|
|
||||||
// Hide the address bar in mobile browsers
|
// Hide the address bar in mobile browsers
|
||||||
hideAddressBar();
|
hideAddressBar();
|
||||||
|
|
||||||
|
@ -1019,8 +1022,9 @@ var Reveal = (function(){
|
||||||
* @param {int} v Vertical index of the target slide
|
* @param {int} v Vertical index of the target slide
|
||||||
* @param {int} f Optional index of a fragment within the
|
* @param {int} f Optional index of a fragment within the
|
||||||
* target slide to activate
|
* target slide to activate
|
||||||
|
* @param {int} o Optional origin for use in multimaster environments
|
||||||
*/
|
*/
|
||||||
function slide( h, v, f ) {
|
function slide( h, v, f, o ) {
|
||||||
|
|
||||||
// Remember where we were at before
|
// Remember where we were at before
|
||||||
previousSlide = currentSlide;
|
previousSlide = currentSlide;
|
||||||
|
@ -1115,7 +1119,8 @@ var Reveal = (function(){
|
||||||
'indexh': indexh,
|
'indexh': indexh,
|
||||||
'indexv': indexv,
|
'indexv': indexv,
|
||||||
'previousSlide': previousSlide,
|
'previousSlide': previousSlide,
|
||||||
'currentSlide': currentSlide
|
'currentSlide': currentSlide,
|
||||||
|
'origin': o
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
4
js/reveal.min.js
vendored
4
js/reveal.min.js
vendored
File diff suppressed because one or more lines are too long
12
package.json
12
package.json
|
@ -3,6 +3,11 @@
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"description": "The HTML Presentation Framework",
|
"description": "The HTML Presentation Framework",
|
||||||
"homepage": "http://lab.hakim.se/reveal-js",
|
"homepage": "http://lab.hakim.se/reveal-js",
|
||||||
|
"subdomain": "revealjs",
|
||||||
|
"scripts": {
|
||||||
|
"test": "grunt jshint",
|
||||||
|
"start": ""
|
||||||
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Hakim El Hattab",
|
"name": "Hakim El Hattab",
|
||||||
"email": "hakim.elhattab@gmail.com",
|
"email": "hakim.elhattab@gmail.com",
|
||||||
|
@ -15,14 +20,11 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "~0.8.0"
|
"node": "~0.8.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"test": "grunt jshint"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"underscore": "~1.3.3",
|
"underscore": "~1.3.3",
|
||||||
"express": "~2.5.9",
|
"express": "~2.5.9",
|
||||||
"socket.io": "~0.9.6",
|
"mustache": "~0.4.0",
|
||||||
"mustache": "~0.4.0"
|
"socket.io": "~0.9.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt-contrib-jshint": "~0.2.0",
|
"grunt-contrib-jshint": "~0.2.0",
|
||||||
|
|
13
plugin/multiplex/client.js
Normal file
13
plugin/multiplex/client.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(function() {
|
||||||
|
var multiplex = window.globals.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.slide(data.indexh, data.indexv, null, 'remote');
|
||||||
|
});
|
||||||
|
}());
|
48
plugin/multiplex/index.js
Normal file
48
plugin/multiplex/index.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
var express = require('express');
|
||||||
|
var fs = require('fs');
|
||||||
|
var io = require('socket.io');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
var app = express.createServer();
|
||||||
|
var staticDir = express.static;
|
||||||
|
|
||||||
|
io = io.listen(app);
|
||||||
|
|
||||||
|
var opts = {
|
||||||
|
port: 1948,
|
||||||
|
baseDir : __dirname + '/../../'
|
||||||
|
};
|
||||||
|
|
||||||
|
io.sockets.on('connection', function(socket) {
|
||||||
|
socket.on('slidechanged', function(slideData) {
|
||||||
|
if (createHash(slideData.secret) === slideData.socketId) {
|
||||||
|
slideData.secret = null;
|
||||||
|
socket.broadcast.emit(slideData.socketId, slideData);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure(function() {
|
||||||
|
[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
|
||||||
|
app.use('/' + dir, staticDir(opts.baseDir + dir));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/", function(req, res) {
|
||||||
|
fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/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
|
||||||
|
app.listen(opts.port || null);
|
32
plugin/multiplex/master.js
Normal file
32
plugin/multiplex/master.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
(function() {
|
||||||
|
// don't emit events from inside the previews themselves
|
||||||
|
if ( window.location.search.match( /receiver/gi ) ) { return; }
|
||||||
|
var multiplex = window.globals.multiplex;
|
||||||
|
|
||||||
|
var socket = io.connect(multiplex.url);
|
||||||
|
|
||||||
|
Reveal.addEventListener( 'slidechanged', function( event ) {
|
||||||
|
var nextindexh;
|
||||||
|
var nextindexv;
|
||||||
|
var slideElement = event.currentSlide;
|
||||||
|
|
||||||
|
if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
|
||||||
|
nextindexh = event.indexh;
|
||||||
|
nextindexv = event.indexv + 1;
|
||||||
|
} else {
|
||||||
|
nextindexh = event.indexh + 1;
|
||||||
|
nextindexv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var slideData = {
|
||||||
|
indexh : event.indexh,
|
||||||
|
indexv : event.indexv,
|
||||||
|
nextindexh : nextindexh,
|
||||||
|
nextindexv : nextindexv,
|
||||||
|
secret: multiplex.secret,
|
||||||
|
socketId : multiplex.id
|
||||||
|
};
|
||||||
|
|
||||||
|
if( typeof event.origin === 'undefined' && event.origin !== 'remote' ) socket.emit('slidechanged', slideData);
|
||||||
|
} );
|
||||||
|
}());
|
109
plugin/multiplex/notes.html
Normal file
109
plugin/multiplex/notes.html
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>reveal.js - Slide Notes</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Helvetica;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notes {
|
||||||
|
font-size: 24px;
|
||||||
|
width: 640px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrap-current-slide {
|
||||||
|
width: 640px;
|
||||||
|
height: 512px;
|
||||||
|
float: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-slide {
|
||||||
|
width: 1280px;
|
||||||
|
height: 1024px;
|
||||||
|
border: none;
|
||||||
|
-moz-transform: scale(0.5);
|
||||||
|
-moz-transform-origin: 0 0;
|
||||||
|
-o-transform: scale(0.5);
|
||||||
|
-o-transform-origin: 0 0;
|
||||||
|
-webkit-transform: scale(0.5);
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrap-next-slide {
|
||||||
|
width: 320px;
|
||||||
|
height: 256px;
|
||||||
|
float: left;
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#next-slide {
|
||||||
|
width: 1280px;
|
||||||
|
height: 1024px;
|
||||||
|
border: none;
|
||||||
|
-moz-transform: scale(0.25);
|
||||||
|
-moz-transform-origin: 0 0;
|
||||||
|
-o-transform: scale(0.25);
|
||||||
|
-o-transform-origin: 0 0;
|
||||||
|
-webkit-transform: scale(0.25);
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slides {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgb(28, 30, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slides span {
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 3px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba( 255, 255, 255, 0.9 );
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="wrap-current-slide" class="slides">
|
||||||
|
<iframe src="/?receiver" width="1280" height="1024" id="current-slide"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="wrap-next-slide" class="slides">
|
||||||
|
<iframe src="/?receiver" width="640" height="512" id="next-slide"></iframe>
|
||||||
|
<span>UPCOMING:</span>
|
||||||
|
</div>
|
||||||
|
<div id="notes"></div>
|
||||||
|
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var socketId = '{{socketId}}';
|
||||||
|
var socket = io.connect(window.location.origin);
|
||||||
|
var notes = document.getElementById('notes');
|
||||||
|
var currentSlide = document.getElementById('current-slide');
|
||||||
|
var nextSlide = document.getElementById('next-slide');
|
||||||
|
|
||||||
|
socket.on('slidedata', function(data) {
|
||||||
|
// ignore data from sockets that aren't ours
|
||||||
|
if (data.socketId !== socketId) { return; }
|
||||||
|
|
||||||
|
notes.innerHTML = data.notes;
|
||||||
|
currentSlide.contentWindow.Reveal.navigateTo(data.indexh, data.indexv);
|
||||||
|
nextSlide.contentWindow.Reveal.navigateTo(data.nextindexh, data.nextindexv);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue