Mercurial > public > dvcs_intro_brownbag
comparison my_theme/js/slides.js @ 12:727c48601d66
Copied default theme to my_theme so that the generated relative HTML can find the CSS and Javascript if I present on a different computer that doesn't have landslide installed.
I also fixed a small typo in the presentation.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Wed, 30 Nov 2011 18:44:44 -0600 |
parents | |
children | 048e5aea6d36 |
comparison
equal
deleted
inserted
replaced
11:6d067ae303f3 | 12:727c48601d66 |
---|---|
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 } |