4 * Adds a presenter console to impress.js
6 * MIT Licensed, see license.txt.
8 * Copyright 2012 impress-console contributors (see README.txt)
14 (function ( document, window ) {
17 // This is the default template for the speaker console window
18 var consoleTemplate = '<!DOCTYPE html>' +
19 '<html id="impressconsole"><head>' +
20 '<link rel="stylesheet" type="text/css" media="screen" href="{{cssFile}}">' +
22 '<div id="console">' +
24 '<iframe id="slideView" scrolling="no"></iframe>' +
25 '<iframe id="preView" scrolling="no"></iframe>' +
26 '<div id="blocker"></div>' +
28 '<div id="notes"></div>' +
30 '<div id="controls"> ' +
31 '<div id="prev"><a href="#" onclick="impress().prev(); return false;" />Prev</a></div>' +
32 '<div id="next"><a href="#" onclick="impress().next(); return false;" />Next</a></div>' +
33 '<div id="clock">00:00:00 AM</div>' +
34 '<div id="timer" onclick="timerReset()">00m 00s</div>' +
35 '<div id="status">Loading</div>' +
39 // Default css location
40 var cssFile = "css/impressConsole.css";
42 // All console windows, so that you can call console() repeatedly.
47 // Zero padding helper function:
48 var zeroPad = function(i) {
49 return (i < 10 ? '0' : '') + i;
53 var console = window.console = function (rootId) {
55 rootId = rootId || 'impress';
57 if (allConsoles[rootId]) {
58 return allConsoles[rootId];
61 // root presentation elements
62 var root = document.getElementById( rootId );
64 var consoleWindow = null;
66 var nextStep = function() {
67 var nextElement = document.querySelector('.active').nextElementSibling;
70 classes = nextElement.attributes['class'];
71 if (classes && classes.value.indexOf('step') !== -1) {
74 nextElement = nextElement.nextElementSibling;
76 // No next element. Pick the first
77 return document.querySelector('.step');
80 // Sync the notes to the step
81 var onStepLeave = function(){
83 // Set notes to next steps notes.
84 var newNotes = document.querySelector('.active').querySelector('.notes');
86 newNotes = newNotes.innerHTML;
88 newNotes = 'No notes for this step';
90 consoleWindow.document.getElementById('notes').innerHTML = newNotes;
93 var baseURL = document.URL.substring(0, document.URL.search('#/'));
94 var slideSrc = baseURL + '#' + document.querySelector('.active').id;
95 var preSrc = baseURL + '#' + nextStep().id;
96 var slideView = consoleWindow.document.getElementById('slideView');
97 // Setting them when they are already set causes glithes in firexof, so we check first:
98 if (slideView.src !== slideSrc) {
99 slideView.src = slideSrc;
101 var preView = consoleWindow.document.getElementById('preView');
102 if (preView.src !== preSrc) {
103 preView.src = preSrc;
106 consoleWindow.document.getElementById('status').innerHTML = '<span style="color: red">Moving</span>';
110 // Sync the previews to the step
111 var onStepEnter = function(){
113 // We do everything here again, because if you stopped the previos step to
114 // early, the onstepleave trigger is not called for that step, so
115 // we need this to sync things.
116 var newNotes = document.querySelector('.active').querySelector('.notes');
118 newNotes = newNotes.innerHTML;
120 newNotes = 'No notes for this step';
122 var notes = consoleWindow.document.getElementById('notes');
123 notes.innerHTML = newNotes;
127 var baseURL = document.URL.substring(0, document.URL.search('#/'));
128 var slideSrc = baseURL + '#' + document.querySelector('.active').id;
129 var preSrc = baseURL + '#' + nextStep().id;
130 var slideView = consoleWindow.document.getElementById('slideView');
131 // Setting them when they are already set causes glithes in firexof, so we check first:
132 if (slideView.src !== slideSrc) {
133 slideView.src = slideSrc;
135 var preView = consoleWindow.document.getElementById('preView');
136 if (preView.src !== preSrc) {
137 preView.src = preSrc;
140 consoleWindow.document.getElementById('status').innerHTML = '<span style="color: green">Ready</span>';
144 var spaceHandler = function () {
145 var notes = consoleWindow.document.getElementById('notes');
146 if (notes.scrollTopMax - notes.scrollTop > 20) {
147 notes.scrollTop = notes.scrollTop + notes.clientHeight * 0.8;
153 var timerReset = function () {
154 consoleWindow.timerStart = new Date();
158 var clockTick = function () {
159 var now = new Date();
160 var hours = now.getHours();
161 var minutes = now.getMinutes();
162 var seconds = now.getSeconds();
166 ampm = ( hours < 12 ) ? 'AM' : 'PM';
167 hours = ( hours > 12 ) ? hours - 12 : hours;
168 hours = ( hours === 0 ) ? 12 : hours;
172 var clockStr = zeroPad(hours) + ':' + zeroPad(minutes) + ':' + zeroPad(seconds) + ' ' + ampm;
173 consoleWindow.document.getElementById('clock').firstChild.nodeValue = clockStr;
176 seconds = Math.floor((now - consoleWindow.timerStart) / 1000);
177 minutes = Math.floor(seconds / 60);
178 seconds = Math.floor(seconds % 60);
179 consoleWindow.document.getElementById('timer').firstChild.nodeValue = zeroPad(minutes) + 'm ' + zeroPad(seconds) + 's';
181 if (!consoleWindow.initialized) {
182 // Nudge the slide windows after load, or they will scrolled wrong on Firefox.
183 consoleWindow.document.getElementById('slideView').contentWindow.scrollTo(0,0);
184 consoleWindow.document.getElementById('preView').contentWindow.scrollTo(0,0);
185 consoleWindow.initialized = true;
189 var registerKeyEvent = function(keyCodes, handler, window) {
190 if (window === undefined) {
191 window = consoleWindow;
194 // prevent default keydown action when one of supported key is pressed
195 window.document.addEventListener("keydown", function ( event ) {
196 if ( !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey && keyCodes.indexOf(event.keyCode) !== -1) {
197 event.preventDefault();
201 // trigger impress action on keyup
202 window.document.addEventListener("keyup", function ( event ) {
203 if ( !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey && keyCodes.indexOf(event.keyCode) !== -1) {
205 event.preventDefault();
210 var open = function() {
211 if(top.isconsoleWindow){
215 if (consoleWindow && !consoleWindow.closed) {
216 consoleWindow.focus();
218 consoleWindow = window.open();
219 // This sets the window location to the main window location, so css can be loaded:
220 consoleWindow.document.open();
221 // Write the template:
222 consoleWindow.document.write(consoleTemplate.replace("{{cssFile}}", cssFile));
223 consoleWindow.document.title = 'Speaker Console (' + document.title + ')';
224 consoleWindow.impress = window.impress;
225 // We set this flag so we can detect it later, to prevent infinite popups.
226 consoleWindow.isconsoleWindow = true;
228 consoleWindow.timerStart = new Date();
229 consoleWindow.timerReset = timerReset;
230 consoleWindow.clockInterval = setInterval('console("' + rootId + '").clockTick()', 1000 );
232 // keyboard navigation handlers
233 // 33: pg up, 37: left, 38: up
234 registerKeyEvent([33, 37, 38], impress().prev);
235 // 34: pg down, 39: right, 40: down
236 registerKeyEvent([34, 39, 40], impress().next);
238 registerKeyEvent([32], spaceHandler);
241 consoleWindow.onbeforeunload = function() {
242 // I don't know why onunload doesn't work here.
243 clearInterval(consoleWindow.clockInterval);
246 // It will need a little nudge on Firefox, but only after loading:
248 consoleWindow.initialized = false;
249 consoleWindow.document.close();
251 return consoleWindow;
255 var init = function(css) {
256 if (css !== undefined) {
260 // Register the event
261 root.addEventListener('impress:stepleave', onStepLeave);
262 root.addEventListener('impress:stepenter', onStepEnter);
264 //When the window closes, clean up after ourselves.
265 window.onunload = function(){
266 if (consoleWindow && !consoleWindow.closed) {
267 consoleWindow.close();
271 //Open speaker console when they press 'p'
272 registerKeyEvent([80], open, window);
276 allConsoles[rootId] = {init: init, open: open, clockTick: clockTick, registerKeyEvent: registerKeyEvent};
277 return allConsoles[rootId];
281 })(document, window);