Ignore rally_conf.json
[functest-xtesting.git] / docs / com / plugin / notes-server / notes.html
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                         }
12
13                         #current-slide,
14                         #upcoming-slide,
15                         #speaker-controls {
16                                 padding: 6px;
17                                 box-sizing: border-box;
18                                 -moz-box-sizing: border-box;
19                         }
20
21                         #current-slide iframe,
22                         #upcoming-slide iframe {
23                                 width: 100%;
24                                 height: 100%;
25                                 border: 1px solid #ddd;
26                         }
27
28                         #current-slide .label,
29                         #upcoming-slide .label {
30                                 position: absolute;
31                                 top: 10px;
32                                 left: 10px;
33                                 font-weight: bold;
34                                 font-size: 14px;
35                                 z-index: 2;
36                                 color: rgba( 255, 255, 255, 0.9 );
37                         }
38
39                         #current-slide {
40                                 position: absolute;
41                                 width: 65%;
42                                 height: 100%;
43                                 top: 0;
44                                 left: 0;
45                                 padding-right: 0;
46                         }
47
48                         #upcoming-slide {
49                                 position: absolute;
50                                 width: 35%;
51                                 height: 40%;
52                                 right: 0;
53                                 top: 0;
54                         }
55
56                         #speaker-controls {
57                                 position: absolute;
58                                 top: 40%;
59                                 right: 0;
60                                 width: 35%;
61                                 height: 60%;
62
63                                 font-size: 18px;
64                         }
65
66                                 .speaker-controls-time.hidden,
67                                 .speaker-controls-notes.hidden {
68                                         display: none;
69                                 }
70
71                                 .speaker-controls-time .label,
72                                 .speaker-controls-notes .label {
73                                         text-transform: uppercase;
74                                         font-weight: normal;
75                                         font-size: 0.66em;
76                                         color: #666;
77                                         margin: 0;
78                                 }
79
80                                 .speaker-controls-time {
81                                         border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
82                                         margin-bottom: 10px;
83                                         padding: 10px 16px;
84                                         padding-bottom: 20px;
85                                         cursor: pointer;
86                                 }
87
88                                 .speaker-controls-time .reset-button {
89                                         opacity: 0;
90                                         float: right;
91                                         color: #666;
92                                         text-decoration: none;
93                                 }
94                                 .speaker-controls-time:hover .reset-button {
95                                         opacity: 1;
96                                 }
97
98                                 .speaker-controls-time .timer,
99                                 .speaker-controls-time .clock {
100                                         width: 50%;
101                                         font-size: 1.9em;
102                                 }
103
104                                 .speaker-controls-time .timer {
105                                         float: left;
106                                 }
107
108                                 .speaker-controls-time .clock {
109                                         float: right;
110                                         text-align: right;
111                                 }
112
113                                 .speaker-controls-time span.mute {
114                                         color: #bbb;
115                                 }
116
117                                 .speaker-controls-notes {
118                                         padding: 10px 16px;
119                                 }
120
121                                 .speaker-controls-notes .value {
122                                         margin-top: 5px;
123                                         line-height: 1.4;
124                                         font-size: 1.2em;
125                                 }
126
127                         .clear {
128                                 clear: both;
129                         }
130
131                         @media screen and (max-width: 1080px) {
132                                 #speaker-controls {
133                                         font-size: 16px;
134                                 }
135                         }
136
137                         @media screen and (max-width: 900px) {
138                                 #speaker-controls {
139                                         font-size: 14px;
140                                 }
141                         }
142
143                         @media screen and (max-width: 800px) {
144                                 #speaker-controls {
145                                         font-size: 12px;
146                                 }
147                         }
148
149                 </style>
150         </head>
151
152         <body>
153
154                 <div id="current-slide"></div>
155                 <div id="upcoming-slide"><span class="label">UPCOMING:</span></div>
156                 <div id="speaker-controls">
157                         <div class="speaker-controls-time">
158                                 <h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
159                                 <div class="clock">
160                                         <span class="clock-value">0:00 AM</span>
161                                 </div>
162                                 <div class="timer">
163                                         <span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
164                                 </div>
165                                 <div class="clear"></div>
166                         </div>
167
168                         <div class="speaker-controls-notes hidden">
169                                 <h4 class="label">Notes</h4>
170                                 <div class="value"></div>
171                         </div>
172                 </div>
173
174                 <script src="/socket.io/socket.io.js"></script>
175                 <script src="/plugin/markdown/marked.js"></script>
176
177                 <script>
178                 (function() {
179
180                         var notes,
181                                 notesValue,
182                                 currentState,
183                                 currentSlide,
184                                 upcomingSlide,
185                                 connected = false;
186
187                         var socket = io.connect( window.location.origin ),
188                                 socketId = '{{socketId}}';
189
190                         socket.on( 'statechanged', function( data ) {
191
192                                 // ignore data from sockets that aren't ours
193                                 if( data.socketId !== socketId ) { return; }
194
195                                 if( connected === false ) {
196                                         connected = true;
197
198                                         setupKeyboard();
199                                         setupNotes();
200                                         setupTimer();
201
202                                 }
203
204                                 handleStateMessage( data );
205
206                         } );
207
208                         // Load our presentation iframes
209                         setupIframes();
210
211                         // Once the iframes have loaded, emit a signal saying there's
212                         // a new subscriber which will trigger a 'statechanged'
213                         // message to be sent back
214                         window.addEventListener( 'message', function( event ) {
215
216                                 var data = JSON.parse( event.data );
217
218                                 if( data && data.namespace === 'reveal' ) {
219                                         if( /ready/.test( data.eventName ) ) {
220                                                 socket.emit( 'new-subscriber', { socketId: socketId } );
221                                         }
222                                 }
223
224                         } );
225
226                         /**
227                          * Called when the main window sends an updated state.
228                          */
229                         function handleStateMessage( data ) {
230
231                                 // Store the most recently set state to avoid circular loops
232                                 // applying the same state
233                                 currentState = JSON.stringify( data.state );
234
235                                 // No need for updating the notes in case of fragment changes
236                                 if ( data.notes ) {
237                                         notes.classList.remove( 'hidden' );
238                                         if( data.markdown ) {
239                                                 notesValue.innerHTML = marked( data.notes );
240                                         }
241                                         else {
242                                                 notesValue.innerHTML = data.notes;
243                                         }
244                                 }
245                                 else {
246                                         notes.classList.add( 'hidden' );
247                                 }
248
249                                 // Update the note slides
250                                 currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
251                                 upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
252                                 upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
253
254                         }
255
256                         // Limit to max one state update per X ms
257                         handleStateMessage = debounce( handleStateMessage, 200 );
258
259                         /**
260                          * Forward keyboard events to the current slide window.
261                          * This enables keyboard events to work even if focus
262                          * isn't set on the current slide iframe.
263                          */
264                         function setupKeyboard() {
265
266                                 document.addEventListener( 'keydown', function( event ) {
267                                         currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
268                                 } );
269
270                         }
271
272                         /**
273                          * Creates the preview iframes.
274                          */
275                         function setupIframes() {
276
277                                 var params = [
278                                         'receiver',
279                                         'progress=false',
280                                         'history=false',
281                                         'transition=none',
282                                         'backgroundTransition=none'
283                                 ].join( '&' );
284
285                                 var currentURL = '/?' + params + '&postMessageEvents=true';
286                                 var upcomingURL = '/?' + params + '&controls=false';
287
288                                 currentSlide = document.createElement( 'iframe' );
289                                 currentSlide.setAttribute( 'width', 1280 );
290                                 currentSlide.setAttribute( 'height', 1024 );
291                                 currentSlide.setAttribute( 'src', currentURL );
292                                 document.querySelector( '#current-slide' ).appendChild( currentSlide );
293
294                                 upcomingSlide = document.createElement( 'iframe' );
295                                 upcomingSlide.setAttribute( 'width', 640 );
296                                 upcomingSlide.setAttribute( 'height', 512 );
297                                 upcomingSlide.setAttribute( 'src', upcomingURL );
298                                 document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
299
300                         }
301
302                         /**
303                          * Setup the notes UI.
304                          */
305                         function setupNotes() {
306
307                                 notes = document.querySelector( '.speaker-controls-notes' );
308                                 notesValue = document.querySelector( '.speaker-controls-notes .value' );
309
310                         }
311
312                         /**
313                          * Create the timer and clock and start updating them
314                          * at an interval.
315                          */
316                         function setupTimer() {
317
318                                 var start = new Date(),
319                                         timeEl = document.querySelector( '.speaker-controls-time' ),
320                                         clockEl = timeEl.querySelector( '.clock-value' ),
321                                         hoursEl = timeEl.querySelector( '.hours-value' ),
322                                         minutesEl = timeEl.querySelector( '.minutes-value' ),
323                                         secondsEl = timeEl.querySelector( '.seconds-value' );
324
325                                 function _updateTimer() {
326
327                                         var diff, hours, minutes, seconds,
328                                                 now = new Date();
329
330                                         diff = now.getTime() - start.getTime();
331                                         hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
332                                         minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
333                                         seconds = Math.floor( ( diff / 1000 ) % 60 );
334
335                                         clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
336                                         hoursEl.innerHTML = zeroPadInteger( hours );
337                                         hoursEl.className = hours > 0 ? '' : 'mute';
338                                         minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
339                                         minutesEl.className = minutes > 0 ? '' : 'mute';
340                                         secondsEl.innerHTML = ':' + zeroPadInteger( seconds );
341
342                                 }
343
344                                 // Update once directly
345                                 _updateTimer();
346
347                                 // Then update every second
348                                 setInterval( _updateTimer, 1000 );
349
350                                 timeEl.addEventListener( 'click', function() {
351                                         start = new Date();
352                                         _updateTimer();
353                                         return false;
354                                 } );
355
356                         }
357
358                         function zeroPadInteger( num ) {
359
360                                 var str = '00' + parseInt( num );
361                                 return str.substring( str.length - 2 );
362
363                         }
364
365                         /**
366                          * Limits the frequency at which a function can be called.
367                          */
368                         function debounce( fn, ms ) {
369
370                                 var lastTime = 0,
371                                         timeout;
372
373                                 return function() {
374
375                                         var args = arguments;
376                                         var context = this;
377
378                                         clearTimeout( timeout );
379
380                                         var timeSinceLastCall = Date.now() - lastTime;
381                                         if( timeSinceLastCall > ms ) {
382                                                 fn.apply( context, args );
383                                                 lastTime = Date.now();
384                                         }
385                                         else {
386                                                 timeout = setTimeout( function() {
387                                                         fn.apply( context, args );
388                                                         lastTime = Date.now();
389                                                 }, ms - timeSinceLastCall );
390                                         }
391
392                                 }
393
394                         }
395
396                 })();
397                 </script>
398
399         </body>
400 </html>