reveal-ck 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/files/reveal.js/CONTRIBUTING.md +4 -0
  3. data/files/reveal.js/Gruntfile.js +44 -26
  4. data/files/reveal.js/LICENSE +1 -1
  5. data/files/reveal.js/README.md +375 -161
  6. data/files/reveal.js/bower.json +27 -0
  7. data/files/reveal.js/css/print/paper.css +4 -3
  8. data/files/reveal.js/css/print/pdf.css +53 -38
  9. data/files/reveal.js/css/reveal.css +452 -206
  10. data/files/reveal.js/css/reveal.scss +328 -175
  11. data/files/reveal.js/css/theme/README.md +2 -6
  12. data/files/reveal.js/css/theme/beige.css +81 -50
  13. data/files/reveal.js/css/theme/black.css +70 -39
  14. data/files/reveal.js/css/theme/blood.css +81 -57
  15. data/files/reveal.js/css/theme/league.css +75 -44
  16. data/files/reveal.js/css/theme/moon.css +75 -44
  17. data/files/reveal.js/css/theme/night.css +70 -39
  18. data/files/reveal.js/css/theme/serif.css +72 -41
  19. data/files/reveal.js/css/theme/simple.css +72 -38
  20. data/files/reveal.js/css/theme/sky.css +75 -44
  21. data/files/reveal.js/css/theme/solarized.css +75 -44
  22. data/files/reveal.js/css/theme/source/black.scss +2 -2
  23. data/files/reveal.js/css/theme/source/blood.scss +3 -16
  24. data/files/reveal.js/css/theme/source/night.scss +0 -1
  25. data/files/reveal.js/css/theme/source/simple.scss +5 -0
  26. data/files/reveal.js/css/theme/source/white.scss +2 -2
  27. data/files/reveal.js/css/theme/template/settings.scss +1 -1
  28. data/files/reveal.js/css/theme/template/theme.scss +36 -23
  29. data/files/reveal.js/css/theme/white.css +75 -44
  30. data/files/reveal.js/demo.html +410 -0
  31. data/files/reveal.js/index.html +14 -373
  32. data/files/reveal.js/js/reveal.js +1186 -350
  33. data/files/reveal.js/lib/css/zenburn.css +41 -78
  34. data/files/reveal.js/lib/js/head.min.js +9 -8
  35. data/files/reveal.js/package.json +22 -26
  36. data/files/reveal.js/plugin/highlight/highlight.js +52 -4
  37. data/files/reveal.js/plugin/markdown/example.html +1 -1
  38. data/files/reveal.js/plugin/markdown/markdown.js +40 -21
  39. data/files/reveal.js/plugin/markdown/marked.js +2 -33
  40. data/files/reveal.js/plugin/math/math.js +5 -2
  41. data/files/reveal.js/plugin/multiplex/client.js +1 -1
  42. data/files/reveal.js/plugin/multiplex/index.js +24 -16
  43. data/files/reveal.js/plugin/multiplex/master.js +22 -42
  44. data/files/reveal.js/plugin/multiplex/package.json +19 -0
  45. data/files/reveal.js/plugin/notes-server/client.js +6 -1
  46. data/files/reveal.js/plugin/notes-server/index.js +17 -14
  47. data/files/reveal.js/plugin/notes-server/notes.html +215 -26
  48. data/files/reveal.js/plugin/notes/notes.html +372 -32
  49. data/files/reveal.js/plugin/notes/notes.js +40 -7
  50. data/files/reveal.js/plugin/print-pdf/print-pdf.js +47 -26
  51. data/files/reveal.js/plugin/zoom-js/zoom.js +12 -2
  52. data/files/reveal.js/test/examples/math.html +1 -1
  53. data/files/reveal.js/test/examples/slide-backgrounds.html +1 -1
  54. data/files/reveal.js/test/examples/slide-transitions.html +101 -0
  55. data/files/reveal.js/test/simple.md +12 -0
  56. data/files/reveal.js/test/test-markdown-element-attributes.html +3 -3
  57. data/files/reveal.js/test/test-markdown-element-attributes.js +1 -1
  58. data/files/reveal.js/test/test-markdown-external.html +36 -0
  59. data/files/reveal.js/test/test-markdown-external.js +24 -0
  60. data/files/reveal.js/test/test-markdown-options.html +41 -0
  61. data/files/reveal.js/test/test-markdown-options.js +26 -0
  62. data/files/reveal.js/test/test-markdown.html +1 -1
  63. data/files/reveal.js/test/test.html +5 -1
  64. data/files/reveal.js/test/test.js +26 -1
  65. data/lib/reveal-ck/version.rb +1 -1
  66. metadata +11 -4
  67. data/files/reveal.js/plugin/leap/leap.js +0 -159
  68. data/files/reveal.js/plugin/remotes/remotes.js +0 -39
@@ -7,14 +7,17 @@
7
7
  var RevealMath = window.RevealMath || (function(){
8
8
 
9
9
  var options = Reveal.getConfig().math || {};
10
- options.mathjax = options.mathjax || 'http://cdn.mathjax.org/mathjax/latest/MathJax.js';
10
+ options.mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
11
11
  options.config = options.config || 'TeX-AMS_HTML-full';
12
12
 
13
13
  loadScript( options.mathjax + '?config=' + options.config, function() {
14
14
 
15
15
  MathJax.Hub.Config({
16
16
  messageStyle: 'none',
17
- tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] },
17
+ tex2jax: {
18
+ inlineMath: [['$','$'],['\\(','\\)']] ,
19
+ skipTags: ['script','noscript','style','textarea','pre']
20
+ },
18
21
  skipStartupTypeset: true
19
22
  });
20
23
 
@@ -8,6 +8,6 @@
8
8
  if (data.socketId !== socketId) { return; }
9
9
  if( window.location.host === 'localhost:1947' ) return;
10
10
 
11
- Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote');
11
+ Reveal.setState(data.state);
12
12
  });
13
13
  }());
@@ -1,37 +1,45 @@
1
+ var http = require('http');
1
2
  var express = require('express');
2
3
  var fs = require('fs');
3
4
  var io = require('socket.io');
4
5
  var crypto = require('crypto');
5
6
 
6
- var app = express.createServer();
7
- var staticDir = express.static;
7
+ var app = express();
8
+ var staticDir = express.static;
9
+ var server = http.createServer(app);
8
10
 
9
- io = io.listen(app);
11
+ io = io(server);
10
12
 
11
13
  var opts = {
12
- port: 1948,
14
+ port: process.env.PORT || 1948,
13
15
  baseDir : __dirname + '/../../'
14
16
  };
15
17
 
16
- io.sockets.on('connection', function(socket) {
17
- socket.on('slidechanged', function(slideData) {
18
- if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
19
- if (createHash(slideData.secret) === slideData.socketId) {
20
- slideData.secret = null;
21
- socket.broadcast.emit(slideData.socketId, slideData);
18
+ io.on( 'connection', function( socket ) {
19
+ socket.on('multiplex-statechanged', function(data) {
20
+ if (typeof data.secret == 'undefined' || data.secret == null || data.secret === '') return;
21
+ if (createHash(data.secret) === data.socketId) {
22
+ data.secret = null;
23
+ socket.broadcast.emit(data.socketId, data);
22
24
  };
23
25
  });
24
26
  });
25
27
 
26
- app.configure(function() {
27
- [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
28
- app.use('/' + dir, staticDir(opts.baseDir + dir));
29
- });
28
+ [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
29
+ app.use('/' + dir, staticDir(opts.baseDir + dir));
30
30
  });
31
31
 
32
32
  app.get("/", function(req, res) {
33
33
  res.writeHead(200, {'Content-Type': 'text/html'});
34
- fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
34
+
35
+ var stream = fs.createReadStream(opts.baseDir + '/index.html');
36
+ stream.on('error', function( error ) {
37
+ res.write('<style>body{font-family: sans-serif;}</style><h2>reveal.js multiplex server.</h2><a href="/token">Generate token</a>');
38
+ res.end();
39
+ });
40
+ stream.on('readable', function() {
41
+ stream.pipe(res);
42
+ });
35
43
  });
36
44
 
37
45
  app.get("/token", function(req,res) {
@@ -47,7 +55,7 @@ var createHash = function(secret) {
47
55
  };
48
56
 
49
57
  // Actually listen
50
- app.listen(opts.port || null);
58
+ server.listen( opts.port || null );
51
59
 
52
60
  var brown = '\033[33m',
53
61
  green = '\033[32m',
@@ -1,51 +1,31 @@
1
1
  (function() {
2
+
2
3
  // Don't emit events from inside of notes windows
3
4
  if ( window.location.search.match( /receiver/gi ) ) { return; }
4
5
 
5
6
  var multiplex = Reveal.getConfig().multiplex;
6
7
 
7
- var socket = io.connect(multiplex.url);
8
-
9
- var notify = function( slideElement, indexh, indexv, origin ) {
10
- if( typeof origin === 'undefined' && origin !== 'remote' ) {
11
- var nextindexh;
12
- var nextindexv;
13
-
14
- var fragmentindex = Reveal.getIndices().f;
15
- if (typeof fragmentindex == 'undefined') {
16
- fragmentindex = 0;
17
- }
18
-
19
- if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
20
- nextindexh = indexh;
21
- nextindexv = indexv + 1;
22
- } else {
23
- nextindexh = indexh + 1;
24
- nextindexv = 0;
25
- }
26
-
27
- var slideData = {
28
- indexh : indexh,
29
- indexv : indexv,
30
- indexf : fragmentindex,
31
- nextindexh : nextindexh,
32
- nextindexv : nextindexv,
33
- secret: multiplex.secret,
34
- socketId : multiplex.id
35
- };
36
-
37
- socket.emit('slidechanged', slideData);
38
- }
39
- }
40
-
41
- Reveal.addEventListener( 'slidechanged', function( event ) {
42
- notify( event.currentSlide, event.indexh, event.indexv, event.origin );
43
- } );
44
-
45
- var fragmentNotify = function( event ) {
46
- notify( Reveal.getCurrentSlide(), Reveal.getIndices().h, Reveal.getIndices().v, event.origin );
8
+ var socket = io.connect( multiplex.url );
9
+
10
+ function post() {
11
+
12
+ var messageData = {
13
+ state: Reveal.getState(),
14
+ secret: multiplex.secret,
15
+ socketId: multiplex.id
16
+ };
17
+
18
+ socket.emit( 'multiplex-statechanged', messageData );
19
+
47
20
  };
48
21
 
49
- Reveal.addEventListener( 'fragmentshown', fragmentNotify );
50
- Reveal.addEventListener( 'fragmenthidden', fragmentNotify );
22
+ // Monitor events that trigger a change in state
23
+ Reveal.addEventListener( 'slidechanged', post );
24
+ Reveal.addEventListener( 'fragmentshown', post );
25
+ Reveal.addEventListener( 'fragmenthidden', post );
26
+ Reveal.addEventListener( 'overviewhidden', post );
27
+ Reveal.addEventListener( 'overviewshown', post );
28
+ Reveal.addEventListener( 'paused', post );
29
+ Reveal.addEventListener( 'resumed', post );
30
+
51
31
  }());
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "reveal-js-multiplex",
3
+ "version": "1.0.0",
4
+ "description": "reveal.js multiplex server",
5
+ "homepage": "http://lab.hakim.se/reveal-js",
6
+ "scripts": {
7
+ "start": "node index.js"
8
+ },
9
+ "engines": {
10
+ "node": "~4.1.1"
11
+ },
12
+ "dependencies": {
13
+ "express": "~4.13.3",
14
+ "grunt-cli": "~0.1.13",
15
+ "mustache": "~2.2.1",
16
+ "socket.io": "~1.3.7"
17
+ },
18
+ "license": "MIT"
19
+ }
@@ -41,10 +41,15 @@
41
41
  }
42
42
 
43
43
  // When a new notes window connects, post our current state
44
- socket.on( 'connect', function( data ) {
44
+ socket.on( 'new-subscriber', function( data ) {
45
45
  post();
46
46
  } );
47
47
 
48
+ // When the state changes from inside of the speaker view
49
+ socket.on( 'statechanged-speaker', function( data ) {
50
+ Reveal.setState( data.state );
51
+ } );
52
+
48
53
  // Monitor events that trigger a change in state
49
54
  Reveal.addEventListener( 'slidechanged', post );
50
55
  Reveal.addEventListener( 'fragmentshown', post );
@@ -1,39 +1,42 @@
1
+ var http = require('http');
1
2
  var express = require('express');
2
3
  var fs = require('fs');
3
4
  var io = require('socket.io');
4
- var _ = require('underscore');
5
5
  var Mustache = require('mustache');
6
6
 
7
- var app = express.createServer();
7
+ var app = express();
8
8
  var staticDir = express.static;
9
+ var server = http.createServer(app);
9
10
 
10
- io = io.listen(app);
11
+ io = io(server);
11
12
 
12
13
  var opts = {
13
14
  port : 1947,
14
15
  baseDir : __dirname + '/../../'
15
16
  };
16
17
 
17
- io.sockets.on( 'connection', function( socket ) {
18
+ io.on( 'connection', function( socket ) {
18
19
 
19
- socket.on( 'connect', function( data ) {
20
- socket.broadcast.emit( 'connect', data );
20
+ socket.on( 'new-subscriber', function( data ) {
21
+ socket.broadcast.emit( 'new-subscriber', data );
21
22
  });
22
23
 
23
24
  socket.on( 'statechanged', function( data ) {
25
+ delete data.state.overview;
24
26
  socket.broadcast.emit( 'statechanged', data );
25
27
  });
26
28
 
27
- });
28
-
29
- app.configure( function() {
30
-
31
- [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) {
32
- app.use( '/' + dir, staticDir( opts.baseDir + dir ) );
29
+ socket.on( 'statechanged-speaker', function( data ) {
30
+ delete data.state.overview;
31
+ socket.broadcast.emit( 'statechanged-speaker', data );
33
32
  });
34
33
 
35
34
  });
36
35
 
36
+ [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) {
37
+ app.use( '/' + dir, staticDir( opts.baseDir + dir ) );
38
+ });
39
+
37
40
  app.get('/', function( req, res ) {
38
41
 
39
42
  res.writeHead( 200, { 'Content-Type': 'text/html' } );
@@ -52,7 +55,7 @@ app.get( '/notes/:socketId', function( req, res ) {
52
55
  });
53
56
 
54
57
  // Actually listen
55
- app.listen( opts.port || null );
58
+ server.listen( opts.port || null );
56
59
 
57
60
  var brown = '\033[33m',
58
61
  green = '\033[32m',
@@ -62,5 +65,5 @@ var slidesLocation = 'http://localhost' + ( opts.port ? ( ':' + opts.port ) : ''
62
65
 
63
66
  console.log( brown + 'reveal.js - Speaker Notes' + reset );
64
67
  console.log( '1. Open the slides at ' + green + slidesLocation + reset );
65
- console.log( '2. Click on the link your JS console to go to the notes page' );
68
+ console.log( '2. Click on the link in your JS console to go to the notes page' );
66
69
  console.log( '3. Advance through your slides and your notes will advance automatically' );
@@ -8,6 +8,7 @@
8
8
  <style>
9
9
  body {
10
10
  font-family: Helvetica;
11
+ font-size: 18px;
11
12
  }
12
13
 
13
14
  #current-slide,
@@ -30,15 +31,26 @@
30
31
  position: absolute;
31
32
  top: 10px;
32
33
  left: 10px;
33
- font-weight: bold;
34
- font-size: 14px;
35
34
  z-index: 2;
36
- color: rgba( 255, 255, 255, 0.9 );
35
+ }
36
+
37
+ .overlay-element {
38
+ height: 34px;
39
+ line-height: 34px;
40
+ padding: 0 10px;
41
+ text-shadow: none;
42
+ background: rgba( 220, 220, 220, 0.8 );
43
+ color: #222;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .overlay-element.interactive:hover {
48
+ background: rgba( 220, 220, 220, 1 );
37
49
  }
38
50
 
39
51
  #current-slide {
40
52
  position: absolute;
41
- width: 65%;
53
+ width: 60%;
42
54
  height: 100%;
43
55
  top: 0;
44
56
  left: 0;
@@ -47,19 +59,20 @@
47
59
 
48
60
  #upcoming-slide {
49
61
  position: absolute;
50
- width: 35%;
62
+ width: 40%;
51
63
  height: 40%;
52
64
  right: 0;
53
65
  top: 0;
54
66
  }
55
67
 
68
+ /* Speaker controls */
56
69
  #speaker-controls {
57
70
  position: absolute;
58
71
  top: 40%;
59
72
  right: 0;
60
- width: 35%;
73
+ width: 40%;
61
74
  height: 60%;
62
-
75
+ overflow: auto;
63
76
  font-size: 18px;
64
77
  }
65
78
 
@@ -124,26 +137,108 @@
124
137
  font-size: 1.2em;
125
138
  }
126
139
 
140
+ /* Layout selector */
141
+ #speaker-layout {
142
+ position: absolute;
143
+ top: 10px;
144
+ right: 10px;
145
+ color: #222;
146
+ z-index: 10;
147
+ }
148
+ #speaker-layout select {
149
+ position: absolute;
150
+ width: 100%;
151
+ height: 100%;
152
+ top: 0;
153
+ left: 0;
154
+ border: 0;
155
+ box-shadow: 0;
156
+ cursor: pointer;
157
+ opacity: 0;
158
+
159
+ font-size: 1em;
160
+ background-color: transparent;
161
+
162
+ -moz-appearance: none;
163
+ -webkit-appearance: none;
164
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
165
+ }
166
+
167
+ #speaker-layout select:focus {
168
+ outline: none;
169
+ box-shadow: none;
170
+ }
171
+
127
172
  .clear {
128
173
  clear: both;
129
174
  }
130
175
 
131
- @media screen and (max-width: 1080px) {
132
- #speaker-controls {
133
- font-size: 16px;
134
- }
176
+ /* Speaker layout: Wide */
177
+ body[data-speaker-layout="wide"] #current-slide,
178
+ body[data-speaker-layout="wide"] #upcoming-slide {
179
+ width: 50%;
180
+ height: 45%;
181
+ padding: 6px;
135
182
  }
136
183
 
137
- @media screen and (max-width: 900px) {
138
- #speaker-controls {
139
- font-size: 14px;
140
- }
184
+ body[data-speaker-layout="wide"] #current-slide {
185
+ top: 0;
186
+ left: 0;
141
187
  }
142
188
 
143
- @media screen and (max-width: 800px) {
144
- #speaker-controls {
145
- font-size: 12px;
146
- }
189
+ body[data-speaker-layout="wide"] #upcoming-slide {
190
+ top: 0;
191
+ left: 50%;
192
+ }
193
+
194
+ body[data-speaker-layout="wide"] #speaker-controls {
195
+ top: 45%;
196
+ left: 0;
197
+ width: 100%;
198
+ height: 50%;
199
+ font-size: 1.25em;
200
+ }
201
+
202
+ /* Speaker layout: Tall */
203
+ body[data-speaker-layout="tall"] #current-slide,
204
+ body[data-speaker-layout="tall"] #upcoming-slide {
205
+ width: 45%;
206
+ height: 50%;
207
+ padding: 6px;
208
+ }
209
+
210
+ body[data-speaker-layout="tall"] #current-slide {
211
+ top: 0;
212
+ left: 0;
213
+ }
214
+
215
+ body[data-speaker-layout="tall"] #upcoming-slide {
216
+ top: 50%;
217
+ left: 0;
218
+ }
219
+
220
+ body[data-speaker-layout="tall"] #speaker-controls {
221
+ padding-top: 40px;
222
+ top: 0;
223
+ left: 45%;
224
+ width: 55%;
225
+ height: 100%;
226
+ font-size: 1.25em;
227
+ }
228
+
229
+ /* Speaker layout: Notes only */
230
+ body[data-speaker-layout="notes-only"] #current-slide,
231
+ body[data-speaker-layout="notes-only"] #upcoming-slide {
232
+ display: none;
233
+ }
234
+
235
+ body[data-speaker-layout="notes-only"] #speaker-controls {
236
+ padding-top: 40px;
237
+ top: 0;
238
+ left: 0;
239
+ width: 100%;
240
+ height: 100%;
241
+ font-size: 1.25em;
147
242
  }
148
243
 
149
244
  </style>
@@ -152,7 +247,7 @@
152
247
  <body>
153
248
 
154
249
  <div id="current-slide"></div>
155
- <div id="upcoming-slide"><span class="label">UPCOMING:</span></div>
250
+ <div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
156
251
  <div id="speaker-controls">
157
252
  <div class="speaker-controls-time">
158
253
  <h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
@@ -170,6 +265,10 @@
170
265
  <div class="value"></div>
171
266
  </div>
172
267
  </div>
268
+ <div id="speaker-layout" class="overlay-element interactive">
269
+ <span class="speaker-layout-label"></span>
270
+ <select class="speaker-layout-dropdown"></select>
271
+ </div>
173
272
 
174
273
  <script src="/socket.io/socket.io.js"></script>
175
274
  <script src="/plugin/markdown/marked.js"></script>
@@ -182,11 +281,20 @@
182
281
  currentState,
183
282
  currentSlide,
184
283
  upcomingSlide,
284
+ layoutLabel,
285
+ layoutDropdown,
185
286
  connected = false;
186
287
 
187
288
  var socket = io.connect( window.location.origin ),
188
289
  socketId = '{{socketId}}';
189
290
 
291
+ var SPEAKER_LAYOUTS = {
292
+ 'default': 'Default',
293
+ 'wide': 'Wide',
294
+ 'tall': 'Tall',
295
+ 'notes-only': 'Notes only'
296
+ };
297
+
190
298
  socket.on( 'statechanged', function( data ) {
191
299
 
192
300
  // ignore data from sockets that aren't ours
@@ -195,7 +303,6 @@
195
303
  if( connected === false ) {
196
304
  connected = true;
197
305
 
198
- setupIframes( data );
199
306
  setupKeyboard();
200
307
  setupNotes();
201
308
  setupTimer();
@@ -206,13 +313,28 @@
206
313
 
207
314
  } );
208
315
 
316
+ setupLayout();
317
+
318
+ // Load our presentation iframes
319
+ setupIframes();
320
+
321
+ // Once the iframes have loaded, emit a signal saying there's
322
+ // a new subscriber which will trigger a 'statechanged'
323
+ // message to be sent back
209
324
  window.addEventListener( 'message', function( event ) {
210
325
 
211
326
  var data = JSON.parse( event.data );
212
327
 
213
328
  if( data && data.namespace === 'reveal' ) {
214
329
  if( /ready/.test( data.eventName ) ) {
215
- socket.emit( 'connect', { socketId: socketId } );
330
+ socket.emit( 'new-subscriber', { socketId: socketId } );
331
+ }
332
+ }
333
+
334
+ // Messages sent by reveal.js inside of the current slide preview
335
+ if( data && data.namespace === 'reveal' ) {
336
+ if( /slidechanged|fragmentshown|fragmenthidden|overviewshown|overviewhidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
337
+ socket.emit( 'statechanged-speaker', { state: data.state } );
216
338
  }
217
339
  }
218
340
 
@@ -267,7 +389,7 @@
267
389
  /**
268
390
  * Creates the preview iframes.
269
391
  */
270
- function setupIframes( data ) {
392
+ function setupIframes() {
271
393
 
272
394
  var params = [
273
395
  'receiver',
@@ -277,9 +399,8 @@
277
399
  'backgroundTransition=none'
278
400
  ].join( '&' );
279
401
 
280
- var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
281
- var currentURL = '/?' + params + '&postMessageEvents=true' + hash;
282
- var upcomingURL = '/?' + params + '&controls=false' + hash;
402
+ var currentURL = '/?' + params + '&postMessageEvents=true';
403
+ var upcomingURL = '/?' + params + '&controls=false';
283
404
 
284
405
  currentSlide = document.createElement( 'iframe' );
285
406
  currentSlide.setAttribute( 'width', 1280 );
@@ -351,6 +472,74 @@
351
472
 
352
473
  }
353
474
 
475
+ /**
476
+ * Sets up the speaker view layout and layout selector.
477
+ */
478
+ function setupLayout() {
479
+
480
+ layoutDropdown = document.querySelector( '.speaker-layout-dropdown' );
481
+ layoutLabel = document.querySelector( '.speaker-layout-label' );
482
+
483
+ // Render the list of available layouts
484
+ for( var id in SPEAKER_LAYOUTS ) {
485
+ var option = document.createElement( 'option' );
486
+ option.setAttribute( 'value', id );
487
+ option.textContent = SPEAKER_LAYOUTS[ id ];
488
+ layoutDropdown.appendChild( option );
489
+ }
490
+
491
+ // Monitor the dropdown for changes
492
+ layoutDropdown.addEventListener( 'change', function( event ) {
493
+
494
+ setLayout( layoutDropdown.value );
495
+
496
+ }, false );
497
+
498
+ // Restore any currently persisted layout
499
+ setLayout( getLayout() );
500
+
501
+ }
502
+
503
+ /**
504
+ * Sets a new speaker view layout. The layout is persisted
505
+ * in local storage.
506
+ */
507
+ function setLayout( value ) {
508
+
509
+ var title = SPEAKER_LAYOUTS[ value ];
510
+
511
+ layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );
512
+ layoutDropdown.value = value;
513
+
514
+ document.body.setAttribute( 'data-speaker-layout', value );
515
+
516
+ // Persist locally
517
+ if( window.localStorage ) {
518
+ window.localStorage.setItem( 'reveal-speaker-layout', value );
519
+ }
520
+
521
+ }
522
+
523
+ /**
524
+ * Returns the ID of the most recently set speaker layout
525
+ * or our default layout if none has been set.
526
+ */
527
+ function getLayout() {
528
+
529
+ if( window.localStorage ) {
530
+ var layout = window.localStorage.getItem( 'reveal-speaker-layout' );
531
+ if( layout ) {
532
+ return layout;
533
+ }
534
+ }
535
+
536
+ // Default to the first record in the layouts hash
537
+ for( var id in SPEAKER_LAYOUTS ) {
538
+ return id;
539
+ }
540
+
541
+ }
542
+
354
543
  function zeroPadInteger( num ) {
355
544
 
356
545
  var str = '00' + parseInt( num );