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