presentation
[semainetech_django.git] / project / media / ppp / theme / js / slides.js
1 function main() {
2 // Since we don't have the fallback of attachEvent and
3 // other IE only stuff we won't try to run JS for IE.
4 // It will run though when using Google Chrome Frame
5 if (document.all) { return; }
6
7 var currentSlideNo;
8 var notesOn = false;
9 var expanded = false;
10 var hiddenContext = false;
11 var blanked = false;
12 var slides = document.getElementsByClassName('slide');
13 var touchStartX = 0;
14 var spaces = /\s+/, a1 = [''];
15 var tocOpened = false;
16 var helpOpened = false;
17 var overviewActive = false;
18 var modifierKeyDown = false;
19 var scale = 1;
20 var showingPresenterView = false;
21 var presenterViewWin = null;
22 var isPresenterView = false;
23
24 var str2array = function(s) {
25 if (typeof s == 'string' || s instanceof String) {
26 if (s.indexOf(' ') < 0) {
27 a1[0] = s;
28 return a1;
29 } else {
30 return s.split(spaces);
31 }
32 }
33 return s;
34 };
35
36 var trim = function(str) {
37 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
38 };
39
40 var addClass = function(node, classStr) {
41 classStr = str2array(classStr);
42 var cls = ' ' + node.className + ' ';
43 for (var i = 0, len = classStr.length, c; i < len; ++i) {
44 c = classStr[i];
45 if (c && cls.indexOf(' ' + c + ' ') < 0) {
46 cls += c + ' ';
47 }
48 }
49 node.className = trim(cls);
50 };
51
52 var removeClass = function(node, classStr) {
53 var cls;
54 if (!node) {
55 throw 'no node provided';
56 }
57 if (classStr !== undefined) {
58 classStr = str2array(classStr);
59 cls = ' ' + node.className + ' ';
60 for (var i = 0, len = classStr.length; i < len; ++i) {
61 cls = cls.replace(' ' + classStr[i] + ' ', ' ');
62 }
63 cls = trim(cls);
64 } else {
65 cls = '';
66 }
67 if (node.className != cls) {
68 node.className = cls;
69 }
70 };
71
72 var getSlideEl = function(slideNo) {
73 if (slideNo > 0) {
74 return slides[slideNo - 1];
75 } else {
76 return null;
77 }
78 };
79
80 var getSlideTitle = function(slideNo) {
81 var el = getSlideEl(slideNo);
82 if (el) {
83 var headers = el.getElementsByTagName('header');
84 if (headers.length > 0) {
85 return el.getElementsByTagName('header')[0].innerText;
86 }
87 }
88 return null;
89 };
90
91 var getSlidePresenterNote = function(slideNo) {
92 var el = getSlideEl(slideNo);
93 if (el) {
94 var n = el.getElementsByClassName('presenter_notes');
95 if (n.length > 0) {
96 return n[0];
97 }
98 }
99 return null;
100 };
101
102 var changeSlideElClass = function(slideNo, className) {
103 var el = getSlideEl(slideNo);
104 if (el) {
105 removeClass(el, 'far-past past current future far-future');
106 addClass(el, className);
107 }
108 };
109
110 var updateSlideClasses = function(updateOther) {
111 window.location.hash = (isPresenterView ? "presenter" : "slide") + currentSlideNo;
112
113 for (var i=1; i<currentSlideNo-1; i++) {
114 changeSlideElClass(i, 'far-past');
115 }
116
117 changeSlideElClass(currentSlideNo - 1, 'past');
118 changeSlideElClass(currentSlideNo, 'current');
119 changeSlideElClass(currentSlideNo + 1, 'future');
120
121 for (i=currentSlideNo+2; i<slides.length+1; i++) {
122 changeSlideElClass(i, 'far-future');
123 }
124
125 highlightCurrentTocLink();
126
127 processContext();
128
129 document.getElementsByTagName('title')[0].innerText = getSlideTitle(currentSlideNo);
130
131 updatePresenterNotes();
132
133 if (updateOther) { updateOtherPage(); }
134 };
135
136 var updatePresenterNotes = function() {
137 if (!isPresenterView) { return; }
138
139 var existingNote = document.getElementById('current_presenter_notes');
140 var currentNote = getSlidePresenterNote(currentSlideNo).cloneNode(true);
141 currentNote.setAttribute('id', 'presenter_note');
142
143 existingNote.replaceChild(currentNote, document.getElementById('presenter_note'));
144 };
145
146 var highlightCurrentTocLink = function() {
147 var toc = document.getElementById('toc');
148
149 if (toc) {
150 var tocRows = toc.getElementsByTagName('tr');
151 for (var i=0; i<tocRows.length; i++) {
152 removeClass(tocRows.item(i), 'active');
153 }
154
155 var currentTocRow = document.getElementById('toc-row-' + currentSlideNo);
156 if (currentTocRow) {
157 addClass(currentTocRow, 'active');
158 }
159 }
160 };
161
162 var updateOtherPage = function() {
163 if (!showingPresenterView) { return; }
164
165 var w = isPresenterView ? window.opener : presenterViewWin;
166 w.postMessage('slide#' + currentSlideNo, '*');
167 };
168
169 var nextSlide = function() {
170 if (currentSlideNo < slides.length) {
171 currentSlideNo++;
172 }
173 updateSlideClasses(true);
174 };
175
176 var prevSlide = function() {
177 if (currentSlideNo > 1) {
178 currentSlideNo--;
179 }
180 updateSlideClasses(true);
181 };
182
183 var showNotes = function() {
184 var notes = getSlideEl(currentSlideNo).getElementsByClassName('notes');
185 for (var i = 0, len = notes.length; i < len; i++) {
186 notes.item(i).style.display = (notesOn) ? 'none':'block';
187 }
188 notesOn = !notesOn;
189 };
190
191 var showSlideNumbers = function() {
192 var asides = document.getElementsByClassName('page_number');
193 var hidden = asides[0].style.display != 'block';
194 for (var i = 0; i < asides.length; i++) {
195 asides.item(i).style.display = hidden ? 'block' : 'none';
196 }
197 };
198
199 var showSlideSources = function() {
200 var asides = document.getElementsByClassName('source');
201 var hidden = asides[0].style.display != 'block';
202 for (var i = 0; i < asides.length; i++) {
203 asides.item(i).style.display = hidden ? 'block' : 'none';
204 }
205 };
206
207 var showToc = function() {
208 if (helpOpened) {
209 showHelp();
210 }
211 var toc = document.getElementById('toc');
212 if (toc) {
213 toc.style.marginLeft = tocOpened ? '-' + (toc.clientWidth + 20) + 'px' : '0px';
214 tocOpened = !tocOpened;
215 }
216 updateOverview();
217 };
218
219 var showHelp = function() {
220 if (tocOpened) {
221 showToc();
222 }
223
224 var help = document.getElementById('help');
225
226 if (help) {
227 help.style.marginLeft = helpOpened ? '-' + (help.clientWidth + 20) + 'px' : '0px';
228 helpOpened = !helpOpened;
229 }
230 };
231
232 var showPresenterView = function() {
233 if (isPresenterView) { return; }
234
235 if (showingPresenterView) {
236 presenterViewWin.close();
237 presenterViewWin = null;
238 showingPresenterView = false;
239 } else {
240 presenterViewWin = open(window.location.pathname + "#presenter" + currentSlideNo, 'presenter_notes',
241 'directories=no,location=no,toolbar=no,menubar=no,copyhistory=no');
242 showingPresenterView = true;
243 }
244 };
245
246 var switch3D = function() {
247 if (document.body.className.indexOf('three-d') == -1) {
248 document.getElementsByClassName('presentation')[0].style.webkitPerspective = '1000px';
249 document.body.className += ' three-d';
250 } else {
251 window.setTimeout('document.getElementsByClassName(\'presentation\')[0].style.webkitPerspective = \'0\';', 2000);
252 document.body.className = document.body.className.replace(/three-d/, '');
253 }
254 };
255
256 var toggleOverview = function() {
257 if (!overviewActive) {
258 addClass(document.body, 'expose');
259 overviewActive = true;
260 setScale(1);
261 } else {
262 removeClass(document.body, 'expose');
263 overviewActive = false;
264 if (expanded) {
265 setScale(scale); // restore scale
266 }
267 }
268 processContext();
269 updateOverview();
270 };
271
272 var updateOverview = function() {
273 try {
274 var presentation = document.getElementsByClassName('presentation')[0];
275 } catch (e) {
276 return;
277 }
278
279 if (isPresenterView) {
280 var action = overviewActive ? removeClass : addClass;
281 action(document.body, 'presenter_view');
282 }
283
284 var toc = document.getElementById('toc');
285
286 if (!toc) {
287 return;
288 }
289
290 if (!tocOpened || !overviewActive) {
291 presentation.style.marginLeft = '0px';
292 presentation.style.width = '100%';
293 } else {
294 presentation.style.marginLeft = toc.clientWidth + 'px';
295 presentation.style.width = (presentation.clientWidth - toc.clientWidth) + 'px';
296 }
297 };
298
299 var computeScale = function() {
300 var cSlide = document.getElementsByClassName('current')[0];
301 var sx = cSlide.clientWidth / window.innerWidth;
302 var sy = cSlide.clientHeight / window.innerHeight;
303 return 1 / Math.max(sx, sy);
304 };
305
306 var setScale = function(scale) {
307 var presentation = document.getElementsByClassName('slides')[0];
308 var transform = 'scale(' + scale + ')';
309 presentation.style.MozTransform = transform;
310 presentation.style.WebkitTransform = transform;
311 presentation.style.OTransform = transform;
312 presentation.style.msTransform = transform;
313 presentation.style.transform = transform;
314 };
315
316 var expandSlides = function() {
317 if (overviewActive) {
318 return;
319 }
320 if (expanded) {
321 setScale(1);
322 expanded = false;
323 } else {
324 scale = computeScale();
325 setScale(scale);
326 expanded = true;
327 }
328 };
329
330 var showContext = function() {
331 try {
332 var presentation = document.getElementsByClassName('slides')[0];
333 removeClass(presentation, 'nocontext');
334 } catch (e) {}
335 };
336
337 var hideContext = function() {
338 try {
339 var presentation = document.getElementsByClassName('slides')[0];
340 addClass(presentation, 'nocontext');
341 } catch (e) {}
342 };
343
344 var processContext = function() {
345 if (hiddenContext) {
346 hideContext();
347 } else {
348 showContext();
349 }
350 };
351
352 var toggleContext = function() {
353 hiddenContext = !hiddenContext;
354 processContext();
355 };
356
357 var toggleBlank = function() {
358 blank_elem = document.getElementById('blank');
359
360 blank_elem.style.display = blanked ? 'none' : 'block';
361
362 blanked = !blanked;
363 };
364
365 var isModifierKey = function(keyCode) {
366 switch (keyCode) {
367 case 16: // shift
368 case 17: // ctrl
369 case 18: // alt
370 case 91: // command
371 return true;
372 break;
373 default:
374 return false;
375 break;
376 }
377 };
378
379 var checkModifierKeyUp = function(event) {
380 if (isModifierKey(event.keyCode)) {
381 modifierKeyDown = false;
382 }
383 };
384
385 var checkModifierKeyDown = function(event) {
386 if (isModifierKey(event.keyCode)) {
387 modifierKeyDown = true;
388 }
389 };
390
391 var handleBodyKeyDown = function(event) {
392 switch (event.keyCode) {
393 case 13: // Enter
394 if (overviewActive) {
395 toggleOverview();
396 }
397 break;
398 case 27: // ESC
399 toggleOverview();
400 break;
401 case 37: // left arrow
402 case 33: // page up
403 prevSlide();
404 break;
405 case 39: // right arrow
406 case 32: // space
407 case 34: // page down
408 nextSlide();
409 break;
410 case 50: // 2
411 if (!modifierKeyDown) {
412 showNotes();
413 }
414 break;
415 case 51: // 3
416 if (!modifierKeyDown && !overviewActive) {
417 switch3D();
418 }
419 break;
420 case 190: // .
421 case 48: // 0
422 case 66: // b
423 if (!modifierKeyDown && !overviewActive) {
424 toggleBlank();
425 }
426 break;
427 case 67: // c
428 if (!modifierKeyDown && !overviewActive) {
429 toggleContext();
430 }
431 break;
432 case 69: // e
433 if (!modifierKeyDown && !overviewActive) {
434 expandSlides();
435 }
436 break;
437 case 72: // h
438 showHelp();
439 break;
440 case 78: // n
441 if (!modifierKeyDown && !overviewActive) {
442 showSlideNumbers();
443 }
444 break;
445 case 80: // p
446 if (!modifierKeyDown && !overviewActive) {
447 showPresenterView();
448 }
449 break;
450 case 83: // s
451 if (!modifierKeyDown && !overviewActive) {
452 showSlideSources();
453 }
454 break;
455 case 84: // t
456 showToc();
457 break;
458 }
459 };
460
461 var handleWheel = function(event) {
462 if (tocOpened || helpOpened || overviewActive) {
463 return;
464 }
465
466 var delta = 0;
467
468 if (!event) {
469 event = window.event;
470 }
471
472 if (event.wheelDelta) {
473 delta = event.wheelDelta/120;
474 if (window.opera) delta = -delta;
475 } else if (event.detail) {
476 delta = -event.detail/3;
477 }
478
479 if (delta && delta <0) {
480 nextSlide();
481 } else if (delta) {
482 prevSlide();
483 }
484 };
485
486 var addSlideClickListeners = function() {
487 for (var i=0; i < slides.length; i++) {
488 var slide = slides.item(i);
489 slide.num = i + 1;
490 slide.addEventListener('click', function(e) {
491 if (overviewActive) {
492 currentSlideNo = this.num;
493 toggleOverview();
494 updateSlideClasses(true);
495 e.preventDefault();
496 }
497 return false;
498 }, true);
499 }
500 };
501
502 var addRemoteWindowControls = function() {
503 window.addEventListener("message", function(e) {
504 if (e.data.indexOf("slide#") != -1) {
505 currentSlideNo = Number(e.data.replace('slide#', ''));
506 updateSlideClasses(false);
507 }
508 }, false);
509 };
510
511 var addTouchListeners = function() {
512 document.addEventListener('touchstart', function(e) {
513 touchStartX = e.touches[0].pageX;
514 }, false);
515 document.addEventListener('touchend', function(e) {
516 var pixelsMoved = touchStartX - e.changedTouches[0].pageX;
517 var SWIPE_SIZE = 150;
518 if (pixelsMoved > SWIPE_SIZE) {
519 nextSlide();
520 }
521 else if (pixelsMoved < -SWIPE_SIZE) {
522 prevSlide();
523 }
524 }, false);
525 };
526
527 var addTocLinksListeners = function() {
528 var toc = document.getElementById('toc');
529 if (toc) {
530 var tocLinks = toc.getElementsByTagName('a');
531 for (var i=0; i < tocLinks.length; i++) {
532 tocLinks.item(i).addEventListener('click', function(e) {
533 currentSlideNo = Number(this.attributes['href'].value.replace('#slide', ''));
534 updateSlideClasses(true);
535 e.preventDefault();
536 }, true);
537 }
538 }
539 };
540
541 // initialize
542
543 (function() {
544 if (window.location.hash == "") {
545 currentSlideNo = 1;
546 } else if (window.location.hash.indexOf("#presenter") != -1) {
547 currentSlideNo = Number(window.location.hash.replace('#presenter', ''));
548 isPresenterView = true;
549 showingPresenterView = true;
550 presenterViewWin = window;
551 addClass(document.body, 'presenter_view');
552 } else {
553 currentSlideNo = Number(window.location.hash.replace('#slide', ''));
554 }
555
556 document.addEventListener('keyup', checkModifierKeyUp, false);
557 document.addEventListener('keydown', handleBodyKeyDown, false);
558 document.addEventListener('keydown', checkModifierKeyDown, false);
559 document.addEventListener('DOMMouseScroll', handleWheel, false);
560
561 window.onmousewheel = document.onmousewheel = handleWheel;
562 window.onresize = expandSlides;
563
564 for (var i = 0, el; el = slides[i]; i++) {
565 addClass(el, 'slide far-future');
566 }
567 updateSlideClasses(false);
568
569 // add support for finger events (filter it by property detection?)
570 addTouchListeners();
571
572 addTocLinksListeners();
573
574 addSlideClickListeners();
575
576 addRemoteWindowControls();
577 })();
578 }