bgneal@12: function main() { bgneal@12: // Since we don't have the fallback of attachEvent and bgneal@12: // other IE only stuff we won't try to run JS for IE. bgneal@12: // It will run though when using Google Chrome Frame bgneal@12: if (document.all) { return; } bgneal@12: bgneal@12: var currentSlideNo; bgneal@12: var notesOn = false; bgneal@12: var expanded = false; bgneal@12: var hiddenContext = false; bgneal@12: var blanked = false; bgneal@12: var slides = document.getElementsByClassName('slide'); bgneal@12: var touchStartX = 0; bgneal@12: var spaces = /\s+/, a1 = ['']; bgneal@12: var tocOpened = false; bgneal@12: var helpOpened = false; bgneal@12: var overviewActive = false; bgneal@12: var modifierKeyDown = false; bgneal@12: var scale = 1; bgneal@12: var showingPresenterView = false; bgneal@12: var presenterViewWin = null; bgneal@12: var isPresenterView = false; bgneal@12: bgneal@12: var str2array = function(s) { bgneal@12: if (typeof s == 'string' || s instanceof String) { bgneal@12: if (s.indexOf(' ') < 0) { bgneal@12: a1[0] = s; bgneal@12: return a1; bgneal@12: } else { bgneal@12: return s.split(spaces); bgneal@12: } bgneal@12: } bgneal@12: return s; bgneal@12: }; bgneal@12: bgneal@12: var trim = function(str) { bgneal@12: return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); bgneal@12: }; bgneal@12: bgneal@12: var addClass = function(node, classStr) { bgneal@12: classStr = str2array(classStr); bgneal@12: var cls = ' ' + node.className + ' '; bgneal@12: for (var i = 0, len = classStr.length, c; i < len; ++i) { bgneal@12: c = classStr[i]; bgneal@12: if (c && cls.indexOf(' ' + c + ' ') < 0) { bgneal@12: cls += c + ' '; bgneal@12: } bgneal@12: } bgneal@12: node.className = trim(cls); bgneal@12: }; bgneal@12: bgneal@12: var removeClass = function(node, classStr) { bgneal@12: var cls; bgneal@12: if (!node) { bgneal@12: throw 'no node provided'; bgneal@12: } bgneal@12: if (classStr !== undefined) { bgneal@12: classStr = str2array(classStr); bgneal@12: cls = ' ' + node.className + ' '; bgneal@12: for (var i = 0, len = classStr.length; i < len; ++i) { bgneal@12: cls = cls.replace(' ' + classStr[i] + ' ', ' '); bgneal@12: } bgneal@12: cls = trim(cls); bgneal@12: } else { bgneal@12: cls = ''; bgneal@12: } bgneal@12: if (node.className != cls) { bgneal@12: node.className = cls; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var getSlideEl = function(slideNo) { bgneal@12: if (slideNo > 0) { bgneal@12: return slides[slideNo - 1]; bgneal@12: } else { bgneal@12: return null; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var getSlideTitle = function(slideNo) { bgneal@12: var el = getSlideEl(slideNo); bgneal@12: if (el) { bgneal@12: var headers = el.getElementsByTagName('header'); bgneal@12: if (headers.length > 0) { bgneal@12: return el.getElementsByTagName('header')[0].innerText; bgneal@12: } bgneal@12: } bgneal@12: return null; bgneal@12: }; bgneal@12: bgneal@12: var getSlidePresenterNote = function(slideNo) { bgneal@12: var el = getSlideEl(slideNo); bgneal@12: if (el) { bgneal@12: var n = el.getElementsByClassName('presenter_notes'); bgneal@12: if (n.length > 0) { bgneal@12: return n[0]; bgneal@12: } bgneal@12: } bgneal@12: return null; bgneal@12: }; bgneal@12: bgneal@12: var changeSlideElClass = function(slideNo, className) { bgneal@12: var el = getSlideEl(slideNo); bgneal@12: if (el) { bgneal@12: removeClass(el, 'far-past past current future far-future'); bgneal@12: addClass(el, className); bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var updateSlideClasses = function(updateOther) { bgneal@12: window.location.hash = (isPresenterView ? "presenter" : "slide") + currentSlideNo; bgneal@12: bgneal@12: for (var i=1; i 1) { bgneal@12: currentSlideNo--; bgneal@12: } bgneal@12: updateSlideClasses(true); bgneal@12: }; bgneal@12: bgneal@12: var showNotes = function() { bgneal@12: var notes = getSlideEl(currentSlideNo).getElementsByClassName('notes'); bgneal@12: for (var i = 0, len = notes.length; i < len; i++) { bgneal@12: notes.item(i).style.display = (notesOn) ? 'none':'block'; bgneal@12: } bgneal@12: notesOn = !notesOn; bgneal@12: }; bgneal@12: bgneal@12: var showSlideNumbers = function() { bgneal@12: var asides = document.getElementsByClassName('page_number'); bgneal@12: var hidden = asides[0].style.display != 'block'; bgneal@12: for (var i = 0; i < asides.length; i++) { bgneal@12: asides.item(i).style.display = hidden ? 'block' : 'none'; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var showSlideSources = function() { bgneal@12: var asides = document.getElementsByClassName('source'); bgneal@12: var hidden = asides[0].style.display != 'block'; bgneal@12: for (var i = 0; i < asides.length; i++) { bgneal@12: asides.item(i).style.display = hidden ? 'block' : 'none'; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var showToc = function() { bgneal@12: if (helpOpened) { bgneal@12: showHelp(); bgneal@12: } bgneal@12: var toc = document.getElementById('toc'); bgneal@12: if (toc) { bgneal@12: toc.style.marginLeft = tocOpened ? '-' + (toc.clientWidth + 20) + 'px' : '0px'; bgneal@12: tocOpened = !tocOpened; bgneal@12: } bgneal@12: updateOverview(); bgneal@12: }; bgneal@12: bgneal@12: var showHelp = function() { bgneal@12: if (tocOpened) { bgneal@12: showToc(); bgneal@12: } bgneal@12: bgneal@12: var help = document.getElementById('help'); bgneal@12: bgneal@12: if (help) { bgneal@12: help.style.marginLeft = helpOpened ? '-' + (help.clientWidth + 20) + 'px' : '0px'; bgneal@12: helpOpened = !helpOpened; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var showPresenterView = function() { bgneal@12: if (isPresenterView) { return; } bgneal@12: bgneal@12: if (showingPresenterView) { bgneal@12: presenterViewWin.close(); bgneal@12: presenterViewWin = null; bgneal@12: showingPresenterView = false; bgneal@12: } else { bgneal@12: presenterViewWin = open(window.location.pathname + "#presenter" + currentSlideNo, 'presenter_notes', bgneal@12: 'directories=no,location=no,toolbar=no,menubar=no,copyhistory=no'); bgneal@12: showingPresenterView = true; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var switch3D = function() { bgneal@12: if (document.body.className.indexOf('three-d') == -1) { bgneal@12: document.getElementsByClassName('presentation')[0].style.webkitPerspective = '1000px'; bgneal@12: document.body.className += ' three-d'; bgneal@12: } else { bgneal@12: window.setTimeout('document.getElementsByClassName(\'presentation\')[0].style.webkitPerspective = \'0\';', 2000); bgneal@12: document.body.className = document.body.className.replace(/three-d/, ''); bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var toggleOverview = function() { bgneal@12: if (!overviewActive) { bgneal@12: addClass(document.body, 'expose'); bgneal@12: overviewActive = true; bgneal@12: setScale(1); bgneal@12: } else { bgneal@12: removeClass(document.body, 'expose'); bgneal@12: overviewActive = false; bgneal@12: if (expanded) { bgneal@12: setScale(scale); // restore scale bgneal@12: } bgneal@12: } bgneal@12: processContext(); bgneal@12: updateOverview(); bgneal@12: }; bgneal@12: bgneal@12: var updateOverview = function() { bgneal@12: try { bgneal@12: var presentation = document.getElementsByClassName('presentation')[0]; bgneal@12: } catch (e) { bgneal@12: return; bgneal@12: } bgneal@12: bgneal@12: if (isPresenterView) { bgneal@12: var action = overviewActive ? removeClass : addClass; bgneal@12: action(document.body, 'presenter_view'); bgneal@12: } bgneal@12: bgneal@12: var toc = document.getElementById('toc'); bgneal@12: bgneal@12: if (!toc) { bgneal@12: return; bgneal@12: } bgneal@12: bgneal@12: if (!tocOpened || !overviewActive) { bgneal@12: presentation.style.marginLeft = '0px'; bgneal@12: presentation.style.width = '100%'; bgneal@12: } else { bgneal@12: presentation.style.marginLeft = toc.clientWidth + 'px'; bgneal@12: presentation.style.width = (presentation.clientWidth - toc.clientWidth) + 'px'; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var computeScale = function() { bgneal@12: var cSlide = document.getElementsByClassName('current')[0]; bgneal@12: var sx = cSlide.clientWidth / window.innerWidth; bgneal@12: var sy = cSlide.clientHeight / window.innerHeight; bgneal@12: return 1 / Math.max(sx, sy); bgneal@12: }; bgneal@12: bgneal@12: var setScale = function(scale) { bgneal@12: var presentation = document.getElementsByClassName('slides')[0]; bgneal@12: var transform = 'scale(' + scale + ')'; bgneal@12: presentation.style.MozTransform = transform; bgneal@12: presentation.style.WebkitTransform = transform; bgneal@12: presentation.style.OTransform = transform; bgneal@12: presentation.style.msTransform = transform; bgneal@12: presentation.style.transform = transform; bgneal@12: }; bgneal@12: bgneal@12: var expandSlides = function() { bgneal@12: if (overviewActive) { bgneal@12: return; bgneal@12: } bgneal@12: if (expanded) { bgneal@12: setScale(1); bgneal@12: expanded = false; bgneal@12: } else { bgneal@12: scale = computeScale(); bgneal@12: setScale(scale); bgneal@12: expanded = true; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var showContext = function() { bgneal@12: try { bgneal@12: var presentation = document.getElementsByClassName('slides')[0]; bgneal@12: removeClass(presentation, 'nocontext'); bgneal@12: } catch (e) {} bgneal@12: }; bgneal@12: bgneal@12: var hideContext = function() { bgneal@12: try { bgneal@12: var presentation = document.getElementsByClassName('slides')[0]; bgneal@12: addClass(presentation, 'nocontext'); bgneal@12: } catch (e) {} bgneal@12: }; bgneal@12: bgneal@12: var processContext = function() { bgneal@12: if (hiddenContext) { bgneal@12: hideContext(); bgneal@12: } else { bgneal@12: showContext(); bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var toggleContext = function() { bgneal@12: hiddenContext = !hiddenContext; bgneal@12: processContext(); bgneal@12: }; bgneal@12: bgneal@12: var toggleBlank = function() { bgneal@12: blank_elem = document.getElementById('blank'); bgneal@12: bgneal@12: blank_elem.style.display = blanked ? 'none' : 'block'; bgneal@12: bgneal@12: blanked = !blanked; bgneal@12: }; bgneal@12: bgneal@12: var isModifierKey = function(keyCode) { bgneal@12: switch (keyCode) { bgneal@12: case 16: // shift bgneal@12: case 17: // ctrl bgneal@12: case 18: // alt bgneal@12: case 91: // command bgneal@12: return true; bgneal@12: break; bgneal@12: default: bgneal@12: return false; bgneal@12: break; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var checkModifierKeyUp = function(event) { bgneal@12: if (isModifierKey(event.keyCode)) { bgneal@12: modifierKeyDown = false; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var checkModifierKeyDown = function(event) { bgneal@12: if (isModifierKey(event.keyCode)) { bgneal@12: modifierKeyDown = true; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var handleBodyKeyDown = function(event) { bgneal@12: switch (event.keyCode) { bgneal@12: case 13: // Enter bgneal@12: if (overviewActive) { bgneal@12: toggleOverview(); bgneal@12: } bgneal@12: break; bgneal@12: case 27: // ESC bgneal@12: toggleOverview(); bgneal@12: break; bgneal@12: case 37: // left arrow bgneal@12: case 33: // page up bgneal@16: event.preventDefault(); bgneal@12: prevSlide(); bgneal@12: break; bgneal@12: case 39: // right arrow bgneal@12: case 32: // space bgneal@12: case 34: // page down bgneal@16: event.preventDefault(); bgneal@12: nextSlide(); bgneal@12: break; bgneal@12: case 50: // 2 bgneal@12: if (!modifierKeyDown) { bgneal@12: showNotes(); bgneal@12: } bgneal@12: break; bgneal@12: case 51: // 3 bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: switch3D(); bgneal@12: } bgneal@12: break; bgneal@12: case 190: // . bgneal@12: case 48: // 0 bgneal@12: case 66: // b bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: toggleBlank(); bgneal@12: } bgneal@12: break; bgneal@12: case 67: // c bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: toggleContext(); bgneal@12: } bgneal@12: break; bgneal@12: case 69: // e bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: expandSlides(); bgneal@12: } bgneal@12: break; bgneal@12: case 72: // h bgneal@12: showHelp(); bgneal@12: break; bgneal@12: case 78: // n bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: showSlideNumbers(); bgneal@12: } bgneal@12: break; bgneal@12: case 80: // p bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: showPresenterView(); bgneal@12: } bgneal@12: break; bgneal@12: case 83: // s bgneal@12: if (!modifierKeyDown && !overviewActive) { bgneal@12: showSlideSources(); bgneal@12: } bgneal@12: break; bgneal@12: case 84: // t bgneal@12: showToc(); bgneal@12: break; bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var handleWheel = function(event) { bgneal@12: if (tocOpened || helpOpened || overviewActive) { bgneal@12: return; bgneal@12: } bgneal@12: bgneal@12: var delta = 0; bgneal@12: bgneal@12: if (!event) { bgneal@12: event = window.event; bgneal@12: } bgneal@12: bgneal@12: if (event.wheelDelta) { bgneal@12: delta = event.wheelDelta/120; bgneal@12: if (window.opera) delta = -delta; bgneal@12: } else if (event.detail) { bgneal@12: delta = -event.detail/3; bgneal@12: } bgneal@12: bgneal@12: if (delta && delta <0) { bgneal@12: nextSlide(); bgneal@12: } else if (delta) { bgneal@12: prevSlide(); bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var addSlideClickListeners = function() { bgneal@12: for (var i=0; i < slides.length; i++) { bgneal@12: var slide = slides.item(i); bgneal@12: slide.num = i + 1; bgneal@12: slide.addEventListener('click', function(e) { bgneal@12: if (overviewActive) { bgneal@12: currentSlideNo = this.num; bgneal@12: toggleOverview(); bgneal@12: updateSlideClasses(true); bgneal@12: e.preventDefault(); bgneal@12: } bgneal@12: return false; bgneal@12: }, true); bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: var addRemoteWindowControls = function() { bgneal@12: window.addEventListener("message", function(e) { bgneal@12: if (e.data.indexOf("slide#") != -1) { bgneal@12: currentSlideNo = Number(e.data.replace('slide#', '')); bgneal@12: updateSlideClasses(false); bgneal@12: } bgneal@12: }, false); bgneal@12: }; bgneal@12: bgneal@12: var addTouchListeners = function() { bgneal@12: document.addEventListener('touchstart', function(e) { bgneal@12: touchStartX = e.touches[0].pageX; bgneal@12: }, false); bgneal@12: document.addEventListener('touchend', function(e) { bgneal@12: var pixelsMoved = touchStartX - e.changedTouches[0].pageX; bgneal@12: var SWIPE_SIZE = 150; bgneal@12: if (pixelsMoved > SWIPE_SIZE) { bgneal@12: nextSlide(); bgneal@12: } bgneal@12: else if (pixelsMoved < -SWIPE_SIZE) { bgneal@12: prevSlide(); bgneal@12: } bgneal@12: }, false); bgneal@12: }; bgneal@12: bgneal@12: var addTocLinksListeners = function() { bgneal@12: var toc = document.getElementById('toc'); bgneal@12: if (toc) { bgneal@12: var tocLinks = toc.getElementsByTagName('a'); bgneal@12: for (var i=0; i < tocLinks.length; i++) { bgneal@12: tocLinks.item(i).addEventListener('click', function(e) { bgneal@12: currentSlideNo = Number(this.attributes['href'].value.replace('#slide', '')); bgneal@12: updateSlideClasses(true); bgneal@12: e.preventDefault(); bgneal@12: }, true); bgneal@12: } bgneal@12: } bgneal@12: }; bgneal@12: bgneal@12: // initialize bgneal@12: bgneal@12: (function() { bgneal@12: if (window.location.hash == "") { bgneal@12: currentSlideNo = 1; bgneal@12: } else if (window.location.hash.indexOf("#presenter") != -1) { bgneal@12: currentSlideNo = Number(window.location.hash.replace('#presenter', '')); bgneal@12: isPresenterView = true; bgneal@12: showingPresenterView = true; bgneal@12: presenterViewWin = window; bgneal@12: addClass(document.body, 'presenter_view'); bgneal@12: } else { bgneal@12: currentSlideNo = Number(window.location.hash.replace('#slide', '')); bgneal@12: } bgneal@12: bgneal@12: document.addEventListener('keyup', checkModifierKeyUp, false); bgneal@12: document.addEventListener('keydown', handleBodyKeyDown, false); bgneal@12: document.addEventListener('keydown', checkModifierKeyDown, false); bgneal@12: document.addEventListener('DOMMouseScroll', handleWheel, false); bgneal@12: bgneal@12: window.onmousewheel = document.onmousewheel = handleWheel; bgneal@12: window.onresize = expandSlides; bgneal@12: bgneal@12: for (var i = 0, el; el = slides[i]; i++) { bgneal@12: addClass(el, 'slide far-future'); bgneal@12: } bgneal@12: updateSlideClasses(false); bgneal@12: bgneal@12: // add support for finger events (filter it by property detection?) bgneal@12: addTouchListeners(); bgneal@12: bgneal@12: addTocLinksListeners(); bgneal@12: bgneal@12: addSlideClickListeners(); bgneal@12: bgneal@12: addRemoteWindowControls(); bgneal@12: })(); bgneal@12: }