starterlog-theme 1.1.3 → 1.2.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -8
  3. data/_layouts/slide.html +105 -0
  4. data/_pages/profile/index.html +1 -1
  5. data/_pages/start/index.html +7 -2
  6. data/assets/modules/reveal.js/.gitignore +13 -0
  7. data/assets/modules/reveal.js/.travis.yml +5 -0
  8. data/assets/modules/reveal.js/CONTRIBUTING.md +23 -0
  9. data/assets/modules/reveal.js/Gruntfile.js +187 -0
  10. data/assets/modules/reveal.js/LICENSE +19 -0
  11. data/assets/modules/reveal.js/README.md +1185 -0
  12. data/assets/modules/reveal.js/bower.json +27 -0
  13. data/assets/modules/reveal.js/css/print/paper.css +203 -0
  14. data/assets/modules/reveal.js/css/print/pdf.css +171 -0
  15. data/assets/modules/reveal.js/css/reveal.css +1365 -0
  16. data/assets/modules/reveal.js/css/reveal.scss +1446 -0
  17. data/assets/modules/reveal.js/css/theme/README.md +21 -0
  18. data/assets/modules/reveal.js/css/theme/beige.css +296 -0
  19. data/assets/modules/reveal.js/css/theme/black.css +292 -0
  20. data/assets/modules/reveal.js/css/theme/blood.css +315 -0
  21. data/assets/modules/reveal.js/css/theme/league.css +298 -0
  22. data/assets/modules/reveal.js/css/theme/moon.css +296 -0
  23. data/assets/modules/reveal.js/css/theme/night.css +290 -0
  24. data/assets/modules/reveal.js/css/theme/serif.css +292 -0
  25. data/assets/modules/reveal.js/css/theme/simple.css +295 -0
  26. data/assets/modules/reveal.js/css/theme/sky.css +299 -0
  27. data/assets/modules/reveal.js/css/theme/solarized.css +296 -0
  28. data/assets/modules/reveal.js/css/theme/source/beige.scss +39 -0
  29. data/assets/modules/reveal.js/css/theme/source/black.scss +49 -0
  30. data/assets/modules/reveal.js/css/theme/source/blood.scss +78 -0
  31. data/assets/modules/reveal.js/css/theme/source/league.scss +34 -0
  32. data/assets/modules/reveal.js/css/theme/source/moon.scss +57 -0
  33. data/assets/modules/reveal.js/css/theme/source/night.scss +34 -0
  34. data/assets/modules/reveal.js/css/theme/source/serif.scss +35 -0
  35. data/assets/modules/reveal.js/css/theme/source/simple.scss +43 -0
  36. data/assets/modules/reveal.js/css/theme/source/sky.scss +46 -0
  37. data/assets/modules/reveal.js/css/theme/source/solarized.scss +63 -0
  38. data/assets/modules/reveal.js/css/theme/source/white.scss +49 -0
  39. data/assets/modules/reveal.js/css/theme/template/mixins.scss +29 -0
  40. data/assets/modules/reveal.js/css/theme/template/settings.scss +43 -0
  41. data/assets/modules/reveal.js/css/theme/template/theme.scss +352 -0
  42. data/assets/modules/reveal.js/css/theme/white.css +292 -0
  43. data/assets/modules/reveal.js/demo.html +410 -0
  44. data/assets/modules/reveal.js/index.html +50 -0
  45. data/assets/modules/reveal.js/js/reveal.js +4961 -0
  46. data/assets/modules/reveal.js/lib/css/zenburn.css +80 -0
  47. data/assets/modules/reveal.js/lib/font/league-gothic/LICENSE +2 -0
  48. data/assets/modules/reveal.js/lib/font/league-gothic/league-gothic.css +10 -0
  49. data/assets/modules/reveal.js/lib/font/league-gothic/league-gothic.eot +0 -0
  50. data/assets/modules/reveal.js/lib/font/league-gothic/league-gothic.ttf +0 -0
  51. data/assets/modules/reveal.js/lib/font/league-gothic/league-gothic.woff +0 -0
  52. data/assets/modules/reveal.js/lib/font/source-sans-pro/LICENSE +45 -0
  53. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-italic.eot +0 -0
  54. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-italic.ttf +0 -0
  55. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-italic.woff +0 -0
  56. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-regular.eot +0 -0
  57. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-regular.ttf +0 -0
  58. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-regular.woff +0 -0
  59. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibold.eot +0 -0
  60. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibold.ttf +0 -0
  61. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibold.woff +0 -0
  62. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot +0 -0
  63. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf +0 -0
  64. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff +0 -0
  65. data/assets/modules/reveal.js/lib/font/source-sans-pro/source-sans-pro.css +39 -0
  66. data/assets/modules/reveal.js/lib/js/classList.js +2 -0
  67. data/assets/modules/reveal.js/lib/js/head.min.js +9 -0
  68. data/assets/modules/reveal.js/lib/js/html5shiv.js +7 -0
  69. data/assets/modules/reveal.js/package.json +45 -0
  70. data/assets/modules/reveal.js/plugin/highlight/highlight.js +78 -0
  71. data/assets/modules/reveal.js/plugin/markdown/example.html +129 -0
  72. data/assets/modules/reveal.js/plugin/markdown/example.md +31 -0
  73. data/assets/modules/reveal.js/plugin/markdown/markdown.js +411 -0
  74. data/assets/modules/reveal.js/plugin/markdown/marked.js +6 -0
  75. data/assets/modules/reveal.js/plugin/math/math.js +67 -0
  76. data/assets/modules/reveal.js/plugin/multiplex/client.js +13 -0
  77. data/assets/modules/reveal.js/plugin/multiplex/index.js +64 -0
  78. data/assets/modules/reveal.js/plugin/multiplex/master.js +31 -0
  79. data/assets/modules/reveal.js/plugin/multiplex/package.json +19 -0
  80. data/assets/modules/reveal.js/plugin/notes-server/client.js +65 -0
  81. data/assets/modules/reveal.js/plugin/notes-server/index.js +69 -0
  82. data/assets/modules/reveal.js/plugin/notes-server/notes.html +585 -0
  83. data/assets/modules/reveal.js/plugin/notes/notes.html +609 -0
  84. data/assets/modules/reveal.js/plugin/notes/notes.js +145 -0
  85. data/assets/modules/reveal.js/plugin/print-pdf/print-pdf.js +51 -0
  86. data/assets/modules/reveal.js/plugin/search/search.js +196 -0
  87. data/assets/modules/reveal.js/plugin/zoom-js/zoom.js +288 -0
  88. data/assets/modules/reveal.js/test/examples/assets/image1.png +0 -0
  89. data/assets/modules/reveal.js/test/examples/assets/image2.png +0 -0
  90. data/assets/modules/reveal.js/test/examples/barebones.html +41 -0
  91. data/assets/modules/reveal.js/test/examples/embedded-media.html +49 -0
  92. data/assets/modules/reveal.js/test/examples/math.html +185 -0
  93. data/assets/modules/reveal.js/test/examples/slide-backgrounds.html +144 -0
  94. data/assets/modules/reveal.js/test/examples/slide-transitions.html +101 -0
  95. data/assets/modules/reveal.js/test/qunit-1.12.0.css +244 -0
  96. data/assets/modules/reveal.js/test/qunit-1.12.0.js +2212 -0
  97. data/assets/modules/reveal.js/test/simple.md +12 -0
  98. data/assets/modules/reveal.js/test/test-markdown-element-attributes.html +134 -0
  99. data/assets/modules/reveal.js/test/test-markdown-element-attributes.js +46 -0
  100. data/assets/modules/reveal.js/test/test-markdown-external.html +36 -0
  101. data/assets/modules/reveal.js/test/test-markdown-external.js +24 -0
  102. data/assets/modules/reveal.js/test/test-markdown-options.html +41 -0
  103. data/assets/modules/reveal.js/test/test-markdown-options.js +26 -0
  104. data/assets/modules/reveal.js/test/test-markdown-slide-attributes.html +128 -0
  105. data/assets/modules/reveal.js/test/test-markdown-slide-attributes.js +47 -0
  106. data/assets/modules/reveal.js/test/test-markdown.html +52 -0
  107. data/assets/modules/reveal.js/test/test-markdown.js +15 -0
  108. data/assets/modules/reveal.js/test/test-pdf.html +83 -0
  109. data/assets/modules/reveal.js/test/test-pdf.js +15 -0
  110. data/assets/modules/reveal.js/test/test.html +86 -0
  111. data/assets/modules/reveal.js/test/test.js +597 -0
  112. metadata +117 -10
@@ -0,0 +1,13 @@
1
+ (function() {
2
+ var multiplex = Reveal.getConfig().multiplex;
3
+ var socketId = multiplex.id;
4
+ var socket = io.connect(multiplex.url);
5
+
6
+ socket.on(multiplex.id, function(data) {
7
+ // ignore data from sockets that aren't ours
8
+ if (data.socketId !== socketId) { return; }
9
+ if( window.location.host === 'localhost:1947' ) return;
10
+
11
+ Reveal.setState(data.state);
12
+ });
13
+ }());
@@ -0,0 +1,64 @@
1
+ var http = require('http');
2
+ var express = require('express');
3
+ var fs = require('fs');
4
+ var io = require('socket.io');
5
+ var crypto = require('crypto');
6
+
7
+ var app = express();
8
+ var staticDir = express.static;
9
+ var server = http.createServer(app);
10
+
11
+ io = io(server);
12
+
13
+ var opts = {
14
+ port: process.env.PORT || 1948,
15
+ baseDir : __dirname + '/../../'
16
+ };
17
+
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);
24
+ };
25
+ });
26
+ });
27
+
28
+ [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
29
+ app.use('/' + dir, staticDir(opts.baseDir + dir));
30
+ });
31
+
32
+ app.get("/", function(req, res) {
33
+ res.writeHead(200, {'Content-Type': 'text/html'});
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
+ });
43
+ });
44
+
45
+ app.get("/token", function(req,res) {
46
+ var ts = new Date().getTime();
47
+ var rand = Math.floor(Math.random()*9999999);
48
+ var secret = ts.toString() + rand.toString();
49
+ res.send({secret: secret, socketId: createHash(secret)});
50
+ });
51
+
52
+ var createHash = function(secret) {
53
+ var cipher = crypto.createCipher('blowfish', secret);
54
+ return(cipher.final('hex'));
55
+ };
56
+
57
+ // Actually listen
58
+ server.listen( opts.port || null );
59
+
60
+ var brown = '\033[33m',
61
+ green = '\033[32m',
62
+ reset = '\033[0m';
63
+
64
+ console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );
@@ -0,0 +1,31 @@
1
+ (function() {
2
+
3
+ // Don't emit events from inside of notes windows
4
+ if ( window.location.search.match( /receiver/gi ) ) { return; }
5
+
6
+ var multiplex = Reveal.getConfig().multiplex;
7
+
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
+
20
+ };
21
+
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
+
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
+ }
@@ -0,0 +1,65 @@
1
+ (function() {
2
+
3
+ // don't emit events from inside the previews themselves
4
+ if( window.location.search.match( /receiver/gi ) ) { return; }
5
+
6
+ var socket = io.connect( window.location.origin ),
7
+ socketId = Math.random().toString().slice( 2 );
8
+
9
+ console.log( 'View slide notes at ' + window.location.origin + '/notes/' + socketId );
10
+
11
+ window.open( window.location.origin + '/notes/' + socketId, 'notes-' + socketId );
12
+
13
+ /**
14
+ * Posts the current slide data to the notes window
15
+ */
16
+ function post() {
17
+
18
+ var slideElement = Reveal.getCurrentSlide(),
19
+ notesElement = slideElement.querySelector( 'aside.notes' );
20
+
21
+ var messageData = {
22
+ notes: '',
23
+ markdown: false,
24
+ socketId: socketId,
25
+ state: Reveal.getState()
26
+ };
27
+
28
+ // Look for notes defined in a slide attribute
29
+ if( slideElement.hasAttribute( 'data-notes' ) ) {
30
+ messageData.notes = slideElement.getAttribute( 'data-notes' );
31
+ }
32
+
33
+ // Look for notes defined in an aside element
34
+ if( notesElement ) {
35
+ messageData.notes = notesElement.innerHTML;
36
+ messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
37
+ }
38
+
39
+ socket.emit( 'statechanged', messageData );
40
+
41
+ }
42
+
43
+ // When a new notes window connects, post our current state
44
+ socket.on( 'new-subscriber', function( data ) {
45
+ post();
46
+ } );
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
+
53
+ // Monitor events that trigger a change in state
54
+ Reveal.addEventListener( 'slidechanged', post );
55
+ Reveal.addEventListener( 'fragmentshown', post );
56
+ Reveal.addEventListener( 'fragmenthidden', post );
57
+ Reveal.addEventListener( 'overviewhidden', post );
58
+ Reveal.addEventListener( 'overviewshown', post );
59
+ Reveal.addEventListener( 'paused', post );
60
+ Reveal.addEventListener( 'resumed', post );
61
+
62
+ // Post the initial state
63
+ post();
64
+
65
+ }());
@@ -0,0 +1,69 @@
1
+ var http = require('http');
2
+ var express = require('express');
3
+ var fs = require('fs');
4
+ var io = require('socket.io');
5
+ var Mustache = require('mustache');
6
+
7
+ var app = express();
8
+ var staticDir = express.static;
9
+ var server = http.createServer(app);
10
+
11
+ io = io(server);
12
+
13
+ var opts = {
14
+ port : 1947,
15
+ baseDir : __dirname + '/../../'
16
+ };
17
+
18
+ io.on( 'connection', function( socket ) {
19
+
20
+ socket.on( 'new-subscriber', function( data ) {
21
+ socket.broadcast.emit( 'new-subscriber', data );
22
+ });
23
+
24
+ socket.on( 'statechanged', function( data ) {
25
+ delete data.state.overview;
26
+ socket.broadcast.emit( 'statechanged', data );
27
+ });
28
+
29
+ socket.on( 'statechanged-speaker', function( data ) {
30
+ delete data.state.overview;
31
+ socket.broadcast.emit( 'statechanged-speaker', data );
32
+ });
33
+
34
+ });
35
+
36
+ [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) {
37
+ app.use( '/' + dir, staticDir( opts.baseDir + dir ) );
38
+ });
39
+
40
+ app.get('/', function( req, res ) {
41
+
42
+ res.writeHead( 200, { 'Content-Type': 'text/html' } );
43
+ fs.createReadStream( opts.baseDir + '/index.html' ).pipe( res );
44
+
45
+ });
46
+
47
+ app.get( '/notes/:socketId', function( req, res ) {
48
+
49
+ fs.readFile( opts.baseDir + 'plugin/notes-server/notes.html', function( err, data ) {
50
+ res.send( Mustache.to_html( data.toString(), {
51
+ socketId : req.params.socketId
52
+ }));
53
+ });
54
+
55
+ });
56
+
57
+ // Actually listen
58
+ server.listen( opts.port || null );
59
+
60
+ var brown = '\033[33m',
61
+ green = '\033[32m',
62
+ reset = '\033[0m';
63
+
64
+ var slidesLocation = 'http://localhost' + ( opts.port ? ( ':' + opts.port ) : '' );
65
+
66
+ console.log( brown + 'reveal.js - Speaker Notes' + reset );
67
+ console.log( '1. Open the slides at ' + green + slidesLocation + reset );
68
+ console.log( '2. Click on the link in your JS console to go to the notes page' );
69
+ console.log( '3. Advance through your slides and your notes will advance automatically' );
@@ -0,0 +1,585 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+
6
+ <title>reveal.js - Slide Notes</title>
7
+
8
+ <style>
9
+ body {
10
+ font-family: Helvetica;
11
+ font-size: 18px;
12
+ }
13
+
14
+ #current-slide,
15
+ #upcoming-slide,
16
+ #speaker-controls {
17
+ padding: 6px;
18
+ box-sizing: border-box;
19
+ -moz-box-sizing: border-box;
20
+ }
21
+
22
+ #current-slide iframe,
23
+ #upcoming-slide iframe {
24
+ width: 100%;
25
+ height: 100%;
26
+ border: 1px solid #ddd;
27
+ }
28
+
29
+ #current-slide .label,
30
+ #upcoming-slide .label {
31
+ position: absolute;
32
+ top: 10px;
33
+ left: 10px;
34
+ z-index: 2;
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 );
49
+ }
50
+
51
+ #current-slide {
52
+ position: absolute;
53
+ width: 60%;
54
+ height: 100%;
55
+ top: 0;
56
+ left: 0;
57
+ padding-right: 0;
58
+ }
59
+
60
+ #upcoming-slide {
61
+ position: absolute;
62
+ width: 40%;
63
+ height: 40%;
64
+ right: 0;
65
+ top: 0;
66
+ }
67
+
68
+ /* Speaker controls */
69
+ #speaker-controls {
70
+ position: absolute;
71
+ top: 40%;
72
+ right: 0;
73
+ width: 40%;
74
+ height: 60%;
75
+ overflow: auto;
76
+ font-size: 18px;
77
+ }
78
+
79
+ .speaker-controls-time.hidden,
80
+ .speaker-controls-notes.hidden {
81
+ display: none;
82
+ }
83
+
84
+ .speaker-controls-time .label,
85
+ .speaker-controls-notes .label {
86
+ text-transform: uppercase;
87
+ font-weight: normal;
88
+ font-size: 0.66em;
89
+ color: #666;
90
+ margin: 0;
91
+ }
92
+
93
+ .speaker-controls-time {
94
+ border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
95
+ margin-bottom: 10px;
96
+ padding: 10px 16px;
97
+ padding-bottom: 20px;
98
+ cursor: pointer;
99
+ }
100
+
101
+ .speaker-controls-time .reset-button {
102
+ opacity: 0;
103
+ float: right;
104
+ color: #666;
105
+ text-decoration: none;
106
+ }
107
+ .speaker-controls-time:hover .reset-button {
108
+ opacity: 1;
109
+ }
110
+
111
+ .speaker-controls-time .timer,
112
+ .speaker-controls-time .clock {
113
+ width: 50%;
114
+ font-size: 1.9em;
115
+ }
116
+
117
+ .speaker-controls-time .timer {
118
+ float: left;
119
+ }
120
+
121
+ .speaker-controls-time .clock {
122
+ float: right;
123
+ text-align: right;
124
+ }
125
+
126
+ .speaker-controls-time span.mute {
127
+ color: #bbb;
128
+ }
129
+
130
+ .speaker-controls-notes {
131
+ padding: 10px 16px;
132
+ }
133
+
134
+ .speaker-controls-notes .value {
135
+ margin-top: 5px;
136
+ line-height: 1.4;
137
+ font-size: 1.2em;
138
+ }
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
+
172
+ .clear {
173
+ clear: both;
174
+ }
175
+
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;
182
+ }
183
+
184
+ body[data-speaker-layout="wide"] #current-slide {
185
+ top: 0;
186
+ left: 0;
187
+ }
188
+
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;
242
+ }
243
+
244
+ </style>
245
+ </head>
246
+
247
+ <body>
248
+
249
+ <div id="current-slide"></div>
250
+ <div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
251
+ <div id="speaker-controls">
252
+ <div class="speaker-controls-time">
253
+ <h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
254
+ <div class="clock">
255
+ <span class="clock-value">0:00 AM</span>
256
+ </div>
257
+ <div class="timer">
258
+ <span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
259
+ </div>
260
+ <div class="clear"></div>
261
+ </div>
262
+
263
+ <div class="speaker-controls-notes hidden">
264
+ <h4 class="label">Notes</h4>
265
+ <div class="value"></div>
266
+ </div>
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>
272
+
273
+ <script src="/socket.io/socket.io.js"></script>
274
+ <script src="/plugin/markdown/marked.js"></script>
275
+
276
+ <script>
277
+ (function() {
278
+
279
+ var notes,
280
+ notesValue,
281
+ currentState,
282
+ currentSlide,
283
+ upcomingSlide,
284
+ layoutLabel,
285
+ layoutDropdown,
286
+ connected = false;
287
+
288
+ var socket = io.connect( window.location.origin ),
289
+ socketId = '{{socketId}}';
290
+
291
+ var SPEAKER_LAYOUTS = {
292
+ 'default': 'Default',
293
+ 'wide': 'Wide',
294
+ 'tall': 'Tall',
295
+ 'notes-only': 'Notes only'
296
+ };
297
+
298
+ socket.on( 'statechanged', function( data ) {
299
+
300
+ // ignore data from sockets that aren't ours
301
+ if( data.socketId !== socketId ) { return; }
302
+
303
+ if( connected === false ) {
304
+ connected = true;
305
+
306
+ setupKeyboard();
307
+ setupNotes();
308
+ setupTimer();
309
+
310
+ }
311
+
312
+ handleStateMessage( data );
313
+
314
+ } );
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
324
+ window.addEventListener( 'message', function( event ) {
325
+
326
+ var data = JSON.parse( event.data );
327
+
328
+ if( data && data.namespace === 'reveal' ) {
329
+ if( /ready/.test( data.eventName ) ) {
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 } );
338
+ }
339
+ }
340
+
341
+ } );
342
+
343
+ /**
344
+ * Called when the main window sends an updated state.
345
+ */
346
+ function handleStateMessage( data ) {
347
+
348
+ // Store the most recently set state to avoid circular loops
349
+ // applying the same state
350
+ currentState = JSON.stringify( data.state );
351
+
352
+ // No need for updating the notes in case of fragment changes
353
+ if ( data.notes ) {
354
+ notes.classList.remove( 'hidden' );
355
+ if( data.markdown ) {
356
+ notesValue.innerHTML = marked( data.notes );
357
+ }
358
+ else {
359
+ notesValue.innerHTML = data.notes;
360
+ }
361
+ }
362
+ else {
363
+ notes.classList.add( 'hidden' );
364
+ }
365
+
366
+ // Update the note slides
367
+ currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
368
+ upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
369
+ upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
370
+
371
+ }
372
+
373
+ // Limit to max one state update per X ms
374
+ handleStateMessage = debounce( handleStateMessage, 200 );
375
+
376
+ /**
377
+ * Forward keyboard events to the current slide window.
378
+ * This enables keyboard events to work even if focus
379
+ * isn't set on the current slide iframe.
380
+ */
381
+ function setupKeyboard() {
382
+
383
+ document.addEventListener( 'keydown', function( event ) {
384
+ currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
385
+ } );
386
+
387
+ }
388
+
389
+ /**
390
+ * Creates the preview iframes.
391
+ */
392
+ function setupIframes() {
393
+
394
+ var params = [
395
+ 'receiver',
396
+ 'progress=false',
397
+ 'history=false',
398
+ 'transition=none',
399
+ 'backgroundTransition=none'
400
+ ].join( '&' );
401
+
402
+ var currentURL = '/?' + params + '&postMessageEvents=true';
403
+ var upcomingURL = '/?' + params + '&controls=false';
404
+
405
+ currentSlide = document.createElement( 'iframe' );
406
+ currentSlide.setAttribute( 'width', 1280 );
407
+ currentSlide.setAttribute( 'height', 1024 );
408
+ currentSlide.setAttribute( 'src', currentURL );
409
+ document.querySelector( '#current-slide' ).appendChild( currentSlide );
410
+
411
+ upcomingSlide = document.createElement( 'iframe' );
412
+ upcomingSlide.setAttribute( 'width', 640 );
413
+ upcomingSlide.setAttribute( 'height', 512 );
414
+ upcomingSlide.setAttribute( 'src', upcomingURL );
415
+ document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
416
+
417
+ }
418
+
419
+ /**
420
+ * Setup the notes UI.
421
+ */
422
+ function setupNotes() {
423
+
424
+ notes = document.querySelector( '.speaker-controls-notes' );
425
+ notesValue = document.querySelector( '.speaker-controls-notes .value' );
426
+
427
+ }
428
+
429
+ /**
430
+ * Create the timer and clock and start updating them
431
+ * at an interval.
432
+ */
433
+ function setupTimer() {
434
+
435
+ var start = new Date(),
436
+ timeEl = document.querySelector( '.speaker-controls-time' ),
437
+ clockEl = timeEl.querySelector( '.clock-value' ),
438
+ hoursEl = timeEl.querySelector( '.hours-value' ),
439
+ minutesEl = timeEl.querySelector( '.minutes-value' ),
440
+ secondsEl = timeEl.querySelector( '.seconds-value' );
441
+
442
+ function _updateTimer() {
443
+
444
+ var diff, hours, minutes, seconds,
445
+ now = new Date();
446
+
447
+ diff = now.getTime() - start.getTime();
448
+ hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
449
+ minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
450
+ seconds = Math.floor( ( diff / 1000 ) % 60 );
451
+
452
+ clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
453
+ hoursEl.innerHTML = zeroPadInteger( hours );
454
+ hoursEl.className = hours > 0 ? '' : 'mute';
455
+ minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
456
+ minutesEl.className = minutes > 0 ? '' : 'mute';
457
+ secondsEl.innerHTML = ':' + zeroPadInteger( seconds );
458
+
459
+ }
460
+
461
+ // Update once directly
462
+ _updateTimer();
463
+
464
+ // Then update every second
465
+ setInterval( _updateTimer, 1000 );
466
+
467
+ timeEl.addEventListener( 'click', function() {
468
+ start = new Date();
469
+ _updateTimer();
470
+ return false;
471
+ } );
472
+
473
+ }
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
+
543
+ function zeroPadInteger( num ) {
544
+
545
+ var str = '00' + parseInt( num );
546
+ return str.substring( str.length - 2 );
547
+
548
+ }
549
+
550
+ /**
551
+ * Limits the frequency at which a function can be called.
552
+ */
553
+ function debounce( fn, ms ) {
554
+
555
+ var lastTime = 0,
556
+ timeout;
557
+
558
+ return function() {
559
+
560
+ var args = arguments;
561
+ var context = this;
562
+
563
+ clearTimeout( timeout );
564
+
565
+ var timeSinceLastCall = Date.now() - lastTime;
566
+ if( timeSinceLastCall > ms ) {
567
+ fn.apply( context, args );
568
+ lastTime = Date.now();
569
+ }
570
+ else {
571
+ timeout = setTimeout( function() {
572
+ fn.apply( context, args );
573
+ lastTime = Date.now();
574
+ }, ms - timeSinceLastCall );
575
+ }
576
+
577
+ }
578
+
579
+ }
580
+
581
+ })();
582
+ </script>
583
+
584
+ </body>
585
+ </html>