// Presentation // Copyright (c) 2011-2014, Matthew Wronka // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the copyright holder (Matthew Wronka) nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL MATTHEW WRONKA BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // http://matt.wronka.org/stuff/projects/presentation/ // v.0.1 Mon Mar 28 17:56:07 EDT 2011 Initial Public Release // v.0.6 Fri Oct 31 08:17:25 EDT 2014 var onLoadCallbacks = []; var slides = []; var slideSets = {}; var currentSlide = undefined; var config = {}; $ = function(v, parentNode) { if(v === undefined) return undefined; var x = parentNode?undefined:document.getElementById(v); if(x) return x; if(parentNode == undefined) return undefined; x = getElementsByClass(parentNode, v, "*"); if(x.length >0 ) return x[0]; x = parentNode.getElementsByTagName(v); if(x.length >0 ) return x[0]; return undefined; } init = function() { onLoadCallbacks.forEach(function(method) { method(); }); var meta = getElementsByClass(document.body, 'meta', '*'); if(meta && meta.length) { for(var i = 0; i< meta.length; ++i) { var txt = meta[i].textContent; var kv = txt.split(':'); config[kv[0]] = eval(kv[1]); } } // set a base for startDeck showSlide(getSlideByIndex(0)); // check the fragment if( location.hash ) { jumpToSlide(location.hash.substr(1)); } if(config['autoshow']) { autoIncrement(); } } jumpToSlide = function(s) { if( !s ) return; if( parseInt(s) > 0) { showSlide(getSlideByIndex(parseInt(s)-1)); } else { // maybe we have a deck here? var deckslide = s.split(','); if(deckslide[0]) startDeck(deckslide[0]); if(deckslide[1] && parseInt(deckslide[1]) > 0) { showSlide(getSlideByIndex(parseInt(deckslide[1]) -1 )); } } } hasClass = function(node, className) { if(!className) return false; return node.className.match(new RegExp('(^|\\s)' + className + '(\\s|$)')); } removeClass = function(node, className) { if(hasClass(node,className)) { node.className = node.className.replace(new RegExp('(^|\\s)' + className + '(\\s|$)'), ' '); } } addClass = function(node, className) { if(!hasClass(node, className)) { if(node.className) node.className += ' ' + className; else node.className = className; } } toggleClass = function(node, className) { if(hasClass(node,className)) { removeClass(node,className); } else { addClass(node,className); } } getElementsByClass = function(node, className, tag) { var rval = new Array(); if(className === undefined) return rval; var elements = node.getElementsByTagName(tag); for(i=elements.length; --i >=0; ) { if(hasClass(elements[i], className)) { rval.unshift(elements[i]); } } return rval; } addVideoToFlow = function() { var nodes = document.getElementsByTagName('video'); if( ! nodes ) return; for(var i=nodes.length; --i >= 0; ) { var node = nodes[i]; addClass(node, 'incremental'); addClass(node, 'play'); node.addEventListener('click', function(e) { e.stopPropagation(); }, false); node.addEventListener('ended', function(n) { return function() { if(currentSlide.incrementals.length && currentSlide.incrementals[0] == n) { currentSlide.incrementals.shift(); } slideIncrement(); };}(node), false); } } ensureNode = function (parentNode, id, className, type) { var nodes = undefined; var node = undefined; if(id) node = $(id); else if(className) nodes = getElementsByClass(parentNode, className, type); if(node) { return node; } else if(nodes && nodes.length >0) { return nodes[0]; } else { var node = document.createElement(type); if(className) node.className=className; if(id) node.id=id; parentNode.appendChild(node); return node; } } buildLayout = function () { // This is used to build the S5-ish layout // Support either with or without existing layout. var layout = ensureNode(document.body, "", "layout", "div"); var controls = ensureNode(layout, "controls","","div"); var currentSlide = ensureNode(layout, "currentSlide","","div"); var nextSlide = ensureNode(layout, "nextSlide","","div"); var backdropSlide = ensureNode(layout, "backdropSlide","displaySlide","div"); var header = ensureNode(layout, "header","","div"); var footer = ensureNode(layout, "footer","","div"); var menu = ensureNode(layout, "menu","","div"); var blankout=ensureNode(document.body, "blankout","","div"); $('currentSlide').addEventListener('click', handleMouseSlide, false); } onLoadCallbacks.push(buildLayout); alterAnchors = function() { var hrefs = document.getElementsByTagName("a"); for(var i=hrefs.length ; --i>= 0; ) { var node = hrefs[i]; node.setAttribute('target', '_blank'); } } onLoadCallbacks.push(alterAnchors); getWidth = function() { return document.documentElement.clientWidth; } getHeight = function() { return document.documentElement.clientHeight; } // We expect that setFontSizeAndImages was already called resizeFont = function() { var docHeight = getHeight(); var docWidth = getWidth(); var vScale = 640 / 20; var hScale = 480 / 20; var nFontSize = Math.min(Math.round(docHeight/vScale), Math.round(docWidth/hScale)); var nImageScale= nFontSize / parseFloat(document.body.style.fontSize) ; document.body.style.fontSize = nFontSize + "px"; var images = document.getElementsByTagName("img"); for(var i=images.length; --i >=0; ) { // nb: for maximum compatability, you really should // have both a height and width. var image = images[i]; if(image.height) { image.style.height = (image.height*nImageScale) + "px"; } if(image.width) { image.style.width = (image.width*nImageScale) + "px"; } } } setFontSizeAndImages = function() { var docHeight = getHeight(); var docWidth = getWidth(); var vScale = 640 / 20; var hScale = 480 / 20; var nFontSize = Math.min(Math.round(docHeight/vScale), Math.round(docWidth/hScale)); document.body.style.fontSize = nFontSize + "px"; var images = document.getElementsByTagName("img"); for(var i=images.length; --i >=0; ) { // nb: for maximum compatability, you really should // have both a height and width. var image = images[i]; if(image.height) { image.style.height = (image.height*nFontSize/16) + "px"; } if(image.width) { image.style.width = (image.width*nFontSize/16) + "px"; } } } onLoadCallbacks.push(setFontSizeAndImages); indexOSF = function() { // try to find slides from Opera Show Format var items = getElementsByClass(document.body, 'slide', "*"); if(! (items && items.length)) { items = getElementsByClass(document.body, 'projection', "div"); } if(! (items && items.length)) { return false; // no idea what to do } var itemsLength = items.length; for(var i=0; i< itemsLength; ++i) { var e = items[i]; var mySlide = { "title": '', "content": e.innerHTML, "className": hasClass(e, 'incremental')?'incremental':'', // S5 compatiability "index": slides.length }; slides.push(mySlide); } slideSets["[Default]"] = slides; return true; } indexSlides = function() { var sets = document.getElementsByTagName("dl"); var setsLength = sets.length; if(setsLength ==0) { if(!indexOSF()) { $('currentSlide').innerHTML="Could not find slides!"; } return; } for(var dli = 0; dli< setsLength; ++dli) { var dl = sets[dli]; if(dl.parentNode != document.body) continue; var items = dl.getElementsByTagName("di"); var itemsLength = items.length; var mySlides = []; var label = dl.getElementsByTagName("label"); label = (label.length > 0)? label[0].textContent : undefined; for(var i=0; i< itemsLength; ++i) { var e = items[i]; if(e.parentNode != dl) continue; var title = $('dt', e); var definition = $('dd', e); var mySlide = { "title": title.innerHTML, "content": definition.innerHTML, "className": definition.className, "index": mySlides.length }; if( ! mySlide.className ) { mySlide.className = 'fade'; } mySlides.push(mySlide); } if(label) { slideSets[label] = mySlides; } else { slideSets["[Default]"] = mySlides; } } slides = slideSets["[Default]"]; } onLoadCallbacks.push(indexSlides); showSplash = function() { var splash = $('_splash'); addClass(splash, 'visible'); setTimeout( 'hideSplash()', 4000); } hideSplash = function() { var splash = $('_splash'); if(splash) removeClass(splash, 'visible'); if(splash) addClass(splash, 'removed'); } doSplash = function() { var splash = document.createElement("div"); splash.id = '_splash'; splash.innerHTML = "
" + "Press F1 for help.
"; document.body.appendChild(splash); setTimeout( 'showSplash()', 2000); } onLoadCallbacks.push(doSplash); handleMouseSlide = function(e) { var bAlt = false; if(!e) { bAlt = true; e = window.event; } if(bAlt) { if(e.button == 1) slideIncrement(); else slideDecrement(); } else { if(e.button == 0) slideIncrement(); else slideDecrement(); } } handleTouchStart = function(e) { if(!window._activeTouch && e.changedTouches && e.changedTouches.length) { window._activeTouch = e.changedTouches[0]; } } handleTouchEnd = function(e) { if( window._activeTouch && e.changedTouches && e.changedTouches.length) { var t = e.changedTouches[0]; // check for left/right swipe var dx= t.pageX - window._activeTouch.pageX; if( Math.abs(dx) > 100 ) // count this as a swipe { handleSwipe(dx); e.preventDefault(); } window._activeTouch = null; } } handleSwipe = function(dir) { if( dir > 0 ) // left-to-right { slidePrev(); } else if( dir < 0 ) // right-to-left { slideNext(); } } handleKey = function(e) { if(!e) e = window.event; var key = ((window.event) ? window.event.keyCode: e.which); var bAlwaysSupress = false; var bIsOutline = hasClass(document.body, 'outline'); switch(key) { case 112: // 'F1' if(!bIsOutline) showHelp(); break; break; case 84: // 't' toggleOutline(); bAlwaysSupress = true; break; case 65: // 'a' if(!bIsOutline) autoIncrement(); break; case 8: // bksp case 80: // 'p' if(!bIsOutline) slideDecrement(); break; case 37: // left case 38: // up case 33: // pg-up if(!bIsOutline) slidePrev(); break; case 13: // enter case 32: // space case 78: // 'n' if(!bIsOutline) slideIncrement(); break; case 34: // pg-down case 39: // right case 40: // down if(!bIsOutline) slideNext(); break; case 70: // 'f' if(!bIsOutline) toggleFullscreen(); break; case 66: // 'b' if(!bIsOutline) blankScreen('#000'); break; case 71: // 'g' if(!bIsOutline) blankScreen('#777'); break; case 87: // 'w' if(!bIsOutline) blankScreen('#fff'); break; case 67: // 'c' toggleHidingPointer(); break; case 27: // escape case 18: // alt case 77: // 'm' if(!bIsOutline) toggleAlternates(); break; case 36: // home if(!bIsOutline) showSlide(getSlideByIndex(0)); break; case 35: // end if(!bIsOutline) showSlide(getSlideByIndex(-1)); break; default: //console.log(key); return; } if(bAlwaysSupress || !bIsOutline) { e.cancelBubble =true; e.preventDefault(); } } toggleHidingPointer = function() { toggleClass( document.body, 'hidePointer' ); } hidePointer = function() { addClass( document.body, 'hidePointer' ); } showPointer = function() { removeClass( document.body, 'hidePointer' ); } startDeck = function(deckName) { slides.lastShown = currentSlide.index; slides=slideSets[deckName]; showSlide(getSlideByIndex(slides.lastShown, deckName)); } getSlideByIndex = function(slideIndex, deckName) { var deck = deckName == undefined? slides : slideSets[deckName]; if(slideIndex < 0) { slideIndex = deck.length + slideIndex; } var s = deck[slideIndex]; if(s) return s; return slides[0]; } blankScreen = function(color) { var blank = $('blankout'); if( !blank ) return; if( blank.style.display == 'block' ) blank.style.display='none'; else { blank.style.display='block'; blank.style.backgroundColor = color; } } toggleFullscreen = function() { if ( !document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) { if ( document.documentElement.requestFullscreen ) { document.documentElement.requestFullscreen(); } else if ( document.documentElement.webkitRequestFullscreen ) { document.documentElement.webkitRequestFullscreen(); } else if ( document.documentElement.msRequestFullscreen ) { document.documentElement.msRequestFullscreen(); } else if ( document.documentElement.mozRequestFullScreen ) { document.documentElement.mozRequestFullScreen(); } } else { if ( document.exitFullscreen ) { document.exitFullscreen(); } else if ( document.webkitExitFullscreen ) { document.webkitExitFullscreen(); } else if ( document.msExitFullscreen ) { document.msExitFullscreen(); } else if ( document.mozCancelFullScreen ) { document.mozCancelFullScreen(); } } } slideNext = function() { var currentNdx = currentSlide.index; if(currentNdx+1 == slides.length) return; showSlide(getSlideByIndex(currentNdx +1)); } slidePrev = function() { var currentNdx = currentSlide.index; if(currentNdx == 0) return; showSlide(getSlideByIndex(currentNdx -1)); } autoIncrement = function () { var bIsOutline = hasClass(document.body, 'outline'); if( bIsOutline ) return; var timeout = config['timer'] || 0; if(timeout <=0) timeout = 5000; if(config['timeoutId']) { clearInterval(config['timeoutId']); config['timeoutId'] = undefined; } else { config['timeoutId'] = setInterval('slideIncrement()', timeout); } } // for incrementally loading pages slideIncrement = function() { if(! currentSlide || ! currentSlide.incrementals) return; // check for an incremental var nextIncremental; if(nextIncremental = currentSlide.incrementals.shift()) { if(nextIncremental.tagName == 'video') { nextIncremental.play(); } else if(hasClass(nextIncremental, 'toggle')) { toggleClass(nextIncremental, 'incremental'); } else if(hasClass(nextIncremental, 'slide')) { if(hasClass(nextIncremental, 'left')) nextIncremental.style.right = '50%'; else if(hasClass(nextIncremental, 'right')) nextIncremental.style.left = '50%'; } else if(hasClass(nextIncremental, 'left')) nextIncremental.style.left = 0; else if(hasClass(nextIncremental, 'right')) nextIncremental.style.right = 0; else if(hasClass(nextIncremental, 'top')) nextIncremental.style.top = 0; else if(hasClass(nextIncremental, 'bottom')) nextIncremental.style.bottom = 0; else if(hasClass(nextIncremental, 'fade')) nextIncremental.style.opacity = 1; else { if( nextIncremental.tagName == 'span' ) nextIncremental.style.display = 'inline'; else nextIncremental.style.display = 'block'; } currentSlide.incrementalsShown.unshift(nextIncremental); } else { slideNext(); } } slideDecrement = function() { // restarts a slide if(! currentSlide) return; // check for an incremental var nextIncremental; if(nextIncremental = currentSlide.incrementalsShown.shift()) { if(hasClass(nextIncremental, 'toggle')) { toggleClass(nextIncremental, 'incremental'); } else if(hasClass(nextIncremental, 'slide')) { if(hasClass(nextIncremental, 'left')) nextIncremental.style.right = 0; else if(hasClass(nextIncremental, 'right')) nextIncremental.style.left = 0; } else if(hasClass(nextIncremental, 'left')) nextIncremental.style.left = '-9000px'; else if(hasClass(nextIncremental, 'right')) nextIncremental.style.right = '-9000px'; else if(hasClass(nextIncremental, 'top')) nextIncremental.style.top= '-9000px'; else if(hasClass(nextIncremental, 'bottom')) nextIncremental.style.bottom= '-9000px'; else if(hasClass(nextIncremental, 'fade')) nextIncremental.style.opacity = 0; else nextIncremental.style.display = 'none'; currentSlide.incrementals.unshift(nextIncremental); } else { slidePrev(); } } _prepareSlide = function(slide) { var n = $('nextSlide'); n.className = slide.className + ' displaySlide'; n.innerHTML = "