--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var PERMANENT_URL_PREFIX = '/static/';
+
+var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
+
+var PM_TOUCH_SENSITIVITY = 15;
+
+var curSlide;
+
+/* ---------------------------------------------------------------------- */
+/* classList polyfill by Eli Grey
+ * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
+
+if (
+ typeof document !== 'undefined' &&
+ !('classList' in document.createElement('a'))
+) {
+ (function(view) {
+ var classListProp = 'classList',
+ protoProp = 'prototype',
+ elemCtrProto = (view.HTMLElement || view.Element)[protoProp],
+ objCtr = Object;
+ (strTrim =
+ String[protoProp].trim ||
+ function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ }),
+ (arrIndexOf =
+ Array[protoProp].indexOf ||
+ function(item) {
+ for (var i = 0, len = this.length; i < len; i++) {
+ if (i in this && this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ }),
+ // Vendors: please allow content code to instantiate DOMExceptions
+ (DOMEx = function(type, message) {
+ this.name = type;
+ this.code = DOMException[type];
+ this.message = message;
+ }),
+ (checkTokenAndGetIndex = function(classList, token) {
+ if (token === '') {
+ throw new DOMEx(
+ 'SYNTAX_ERR',
+ 'An invalid or illegal string was specified'
+ );
+ }
+ if (/\s/.test(token)) {
+ throw new DOMEx(
+ 'INVALID_CHARACTER_ERR',
+ 'String contains an invalid character'
+ );
+ }
+ return arrIndexOf.call(classList, token);
+ }),
+ (ClassList = function(elem) {
+ var trimmedClasses = strTrim.call(elem.className),
+ classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [];
+ for (var i = 0, len = classes.length; i < len; i++) {
+ this.push(classes[i]);
+ }
+ this._updateClassName = function() {
+ elem.className = this.toString();
+ };
+ }),
+ (classListProto = ClassList[protoProp] = []),
+ (classListGetter = function() {
+ return new ClassList(this);
+ });
+ // Most DOMException implementations don't allow calling DOMException's toString()
+ // on non-DOMExceptions. Error's toString() is sufficient here.
+ DOMEx[protoProp] = Error[protoProp];
+ classListProto.item = function(i) {
+ return this[i] || null;
+ };
+ classListProto.contains = function(token) {
+ token += '';
+ return checkTokenAndGetIndex(this, token) !== -1;
+ };
+ classListProto.add = function(token) {
+ token += '';
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.push(token);
+ this._updateClassName();
+ }
+ };
+ classListProto.remove = function(token) {
+ token += '';
+ var index = checkTokenAndGetIndex(this, token);
+ if (index !== -1) {
+ this.splice(index, 1);
+ this._updateClassName();
+ }
+ };
+ classListProto.toggle = function(token) {
+ token += '';
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.add(token);
+ } else {
+ this.remove(token);
+ }
+ };
+ classListProto.toString = function() {
+ return this.join(' ');
+ };
+
+ if (objCtr.defineProperty) {
+ var classListPropDesc = {
+ get: classListGetter,
+ enumerable: true,
+ configurable: true,
+ };
+ try {
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ } catch (ex) {
+ // IE 8 doesn't support enumerable:true
+ if (ex.number === -0x7ff5ec54) {
+ classListPropDesc.enumerable = false;
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ }
+ }
+ } else if (objCtr[protoProp].__defineGetter__) {
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
+ }
+ })(self);
+}
+/* ---------------------------------------------------------------------- */
+
+/* Slide movement */
+
+function hideHelpText() {
+ document.getElementById('help').style.display = 'none';
+}
+
+function getSlideEl(no) {
+ if (no < 0 || no >= slideEls.length) {
+ return null;
+ } else {
+ return slideEls[no];
+ }
+}
+
+function updateSlideClass(slideNo, className) {
+ var el = getSlideEl(slideNo);
+
+ if (!el) {
+ return;
+ }
+
+ if (className) {
+ el.classList.add(className);
+ }
+
+ for (var i in SLIDE_CLASSES) {
+ if (className != SLIDE_CLASSES[i]) {
+ el.classList.remove(SLIDE_CLASSES[i]);
+ }
+ }
+}
+
+function updateSlides() {
+ if (window.trackPageview) window.trackPageview();
+
+ for (var i = 0; i < slideEls.length; i++) {
+ switch (i) {
+ case curSlide - 2:
+ updateSlideClass(i, 'far-past');
+ break;
+ case curSlide - 1:
+ updateSlideClass(i, 'past');
+ break;
+ case curSlide:
+ updateSlideClass(i, 'current');
+ break;
+ case curSlide + 1:
+ updateSlideClass(i, 'next');
+ break;
+ case curSlide + 2:
+ updateSlideClass(i, 'far-next');
+ break;
+ default:
+ updateSlideClass(i);
+ break;
+ }
+ }
+
+ triggerLeaveEvent(curSlide - 1);
+ triggerEnterEvent(curSlide);
+
+ window.setTimeout(function() {
+ // Hide after the slide
+ disableSlideFrames(curSlide - 2);
+ }, 301);
+
+ enableSlideFrames(curSlide - 1);
+ enableSlideFrames(curSlide + 2);
+
+ updateHash();
+}
+
+function prevSlide() {
+ hideHelpText();
+ if (curSlide > 0) {
+ curSlide--;
+
+ updateSlides();
+ }
+
+ if (notesEnabled) localStorage.setItem(destSlideKey(), curSlide);
+}
+
+function nextSlide() {
+ hideHelpText();
+ if (curSlide < slideEls.length - 1) {
+ curSlide++;
+
+ updateSlides();
+ }
+
+ if (notesEnabled) localStorage.setItem(destSlideKey(), curSlide);
+}
+
+/* Slide events */
+
+function triggerEnterEvent(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var onEnter = el.getAttribute('onslideenter');
+ if (onEnter) {
+ new Function(onEnter).call(el);
+ }
+
+ var evt = document.createEvent('Event');
+ evt.initEvent('slideenter', true, true);
+ evt.slideNumber = no + 1; // Make it readable
+
+ el.dispatchEvent(evt);
+}
+
+function triggerLeaveEvent(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var onLeave = el.getAttribute('onslideleave');
+ if (onLeave) {
+ new Function(onLeave).call(el);
+ }
+
+ var evt = document.createEvent('Event');
+ evt.initEvent('slideleave', true, true);
+ evt.slideNumber = no + 1; // Make it readable
+
+ el.dispatchEvent(evt);
+}
+
+/* Touch events */
+
+function handleTouchStart(event) {
+ if (event.touches.length == 1) {
+ touchDX = 0;
+ touchDY = 0;
+
+ touchStartX = event.touches[0].pageX;
+ touchStartY = event.touches[0].pageY;
+
+ document.body.addEventListener('touchmove', handleTouchMove, true);
+ document.body.addEventListener('touchend', handleTouchEnd, true);
+ }
+}
+
+function handleTouchMove(event) {
+ if (event.touches.length > 1) {
+ cancelTouch();
+ } else {
+ touchDX = event.touches[0].pageX - touchStartX;
+ touchDY = event.touches[0].pageY - touchStartY;
+ event.preventDefault();
+ }
+}
+
+function handleTouchEnd(event) {
+ var dx = Math.abs(touchDX);
+ var dy = Math.abs(touchDY);
+
+ if (dx > PM_TOUCH_SENSITIVITY && dy < (dx * 2) / 3) {
+ if (touchDX > 0) {
+ prevSlide();
+ } else {
+ nextSlide();
+ }
+ }
+
+ cancelTouch();
+}
+
+function cancelTouch() {
+ document.body.removeEventListener('touchmove', handleTouchMove, true);
+ document.body.removeEventListener('touchend', handleTouchEnd, true);
+}
+
+/* Preloading frames */
+
+function disableSlideFrames(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var frames = el.getElementsByTagName('iframe');
+ for (var i = 0, frame; (frame = frames[i]); i++) {
+ disableFrame(frame);
+ }
+}
+
+function enableSlideFrames(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var frames = el.getElementsByTagName('iframe');
+ for (var i = 0, frame; (frame = frames[i]); i++) {
+ enableFrame(frame);
+ }
+}
+
+function disableFrame(frame) {
+ frame.src = 'about:blank';
+}
+
+function enableFrame(frame) {
+ var src = frame._src;
+
+ if (frame.src != src && src != 'about:blank') {
+ frame.src = src;
+ }
+}
+
+function setupFrames() {
+ var frames = document.querySelectorAll('iframe');
+ for (var i = 0, frame; (frame = frames[i]); i++) {
+ frame._src = frame.src;
+ disableFrame(frame);
+ }
+
+ enableSlideFrames(curSlide);
+ enableSlideFrames(curSlide + 1);
+ enableSlideFrames(curSlide + 2);
+}
+
+function setupInteraction() {
+ /* Clicking and tapping */
+
+ var el = document.createElement('div');
+ el.className = 'slide-area';
+ el.id = 'prev-slide-area';
+ el.addEventListener('click', prevSlide, false);
+ document.querySelector('section.slides').appendChild(el);
+
+ var el = document.createElement('div');
+ el.className = 'slide-area';
+ el.id = 'next-slide-area';
+ el.addEventListener('click', nextSlide, false);
+ document.querySelector('section.slides').appendChild(el);
+
+ /* Swiping */
+
+ document.body.addEventListener('touchstart', handleTouchStart, false);
+}
+
+/* Hash functions */
+
+function getCurSlideFromHash() {
+ var slideNo = parseInt(location.hash.substr(1));
+
+ if (slideNo) {
+ curSlide = slideNo - 1;
+ } else {
+ curSlide = 0;
+ }
+}
+
+function updateHash() {
+ location.replace('#' + (curSlide + 1));
+}
+
+/* Event listeners */
+
+function handleBodyKeyDown(event) {
+ // If we're in a code element, only handle pgup/down.
+ var inCode = event.target.classList.contains('code');
+
+ switch (event.keyCode) {
+ case 78: // 'N' opens presenter notes window
+ if (!inCode && notesEnabled) toggleNotesWindow();
+ break;
+ case 72: // 'H' hides the help text
+ case 27: // escape key
+ if (!inCode) hideHelpText();
+ break;
+
+ case 39: // right arrow
+ case 13: // Enter
+ case 32: // space
+ if (inCode) break;
+ case 34: // PgDn
+ nextSlide();
+ event.preventDefault();
+ break;
+
+ case 37: // left arrow
+ case 8: // Backspace
+ if (inCode) break;
+ case 33: // PgUp
+ prevSlide();
+ event.preventDefault();
+ break;
+
+ case 40: // down arrow
+ if (inCode) break;
+ nextSlide();
+ event.preventDefault();
+ break;
+
+ case 38: // up arrow
+ if (inCode) break;
+ prevSlide();
+ event.preventDefault();
+ break;
+ }
+}
+
+function scaleSmallViewports() {
+ var el = document.querySelector('section.slides');
+ var transform = '';
+ var sWidthPx = 1250;
+ var sHeightPx = 750;
+ var sAspectRatio = sWidthPx / sHeightPx;
+ var wAspectRatio = window.innerWidth / window.innerHeight;
+
+ if (wAspectRatio <= sAspectRatio && window.innerWidth < sWidthPx) {
+ transform = 'scale(' + window.innerWidth / sWidthPx + ')';
+ } else if (window.innerHeight < sHeightPx) {
+ transform = 'scale(' + window.innerHeight / sHeightPx + ')';
+ }
+ el.style.transform = transform;
+}
+
+function addEventListeners() {
+ document.addEventListener('keydown', handleBodyKeyDown, false);
+ var resizeTimeout;
+ window.addEventListener('resize', function() {
+ // throttle resize events
+ window.clearTimeout(resizeTimeout);
+ resizeTimeout = window.setTimeout(function() {
+ resizeTimeout = null;
+ scaleSmallViewports();
+ }, 50);
+ });
+
+ // Force reset transform property of section.slides when printing page.
+ // Use both onbeforeprint and matchMedia for compatibility with different browsers.
+ var beforePrint = function() {
+ var el = document.querySelector('section.slides');
+ el.style.transform = '';
+ };
+ window.onbeforeprint = beforePrint;
+ if (window.matchMedia) {
+ var mediaQueryList = window.matchMedia('print');
+ mediaQueryList.addListener(function(mql) {
+ if (mql.matches) beforePrint();
+ });
+ }
+}
+
+/* Initialization */
+
+function addFontStyle() {
+ var el = document.createElement('link');
+ el.rel = 'stylesheet';
+ el.type = 'text/css';
+ el.href =
+ '//fonts.googleapis.com/css?family=' +
+ 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
+
+ document.body.appendChild(el);
+}
+
+function addGeneralStyle() {
+ var el = document.createElement('link');
+ el.rel = 'stylesheet';
+ el.type = 'text/css';
+ el.href = PERMANENT_URL_PREFIX + 'styles.css';
+ document.body.appendChild(el);
+
+ var el = document.createElement('meta');
+ el.name = 'viewport';
+ el.content = 'width=device-width,height=device-height,initial-scale=1';
+ document.querySelector('head').appendChild(el);
+
+ var el = document.createElement('meta');
+ el.name = 'apple-mobile-web-app-capable';
+ el.content = 'yes';
+ document.querySelector('head').appendChild(el);
+
+ scaleSmallViewports();
+}
+
+function handleDomLoaded() {
+ slideEls = document.querySelectorAll('section.slides > article');
+
+ setupFrames();
+
+ addFontStyle();
+ addGeneralStyle();
+ addEventListeners();
+
+ updateSlides();
+
+ setupInteraction();
+
+ if (
+ window.location.hostname == 'localhost' ||
+ window.location.hostname == '127.0.0.1' ||
+ window.location.hostname == '::1'
+ ) {
+ hideHelpText();
+ }
+
+ document.body.classList.add('loaded');
+
+ setupNotesSync();
+}
+
+function initialize() {
+ getCurSlideFromHash();
+
+ if (window['_DEBUG']) {
+ PERMANENT_URL_PREFIX = '../';
+ }
+
+ if (window['_DCL']) {
+ handleDomLoaded();
+ } else {
+ document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
+ }
+}
+
+// If ?debug exists then load the script relative instead of absolute
+if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
+ document.addEventListener(
+ 'DOMContentLoaded',
+ function() {
+ // Avoid missing the DomContentLoaded event
+ window['_DCL'] = true;
+ },
+ false
+ );
+
+ window['_DEBUG'] = true;
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = '../slides.js';
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(script, s);
+
+ // Remove this script
+ s.parentNode.removeChild(s);
+} else {
+ initialize();
+}
+
+/* Synchronize windows when notes are enabled */
+
+function setupNotesSync() {
+ if (!notesEnabled) return;
+
+ function setupPlayResizeSync() {
+ var out = document.getElementsByClassName('output');
+ for (var i = 0; i < out.length; i++) {
+ $(out[i]).bind('resize', function(event) {
+ if ($(event.target).hasClass('ui-resizable')) {
+ localStorage.setItem('play-index', i);
+ localStorage.setItem('output-style', out[i].style.cssText);
+ }
+ });
+ }
+ }
+ function setupPlayCodeSync() {
+ var play = document.querySelectorAll('div.playground');
+ for (var i = 0; i < play.length; i++) {
+ play[i].addEventListener('input', inputHandler, false);
+
+ function inputHandler(e) {
+ localStorage.setItem('play-index', i);
+ localStorage.setItem('play-code', e.target.innerHTML);
+ }
+ }
+ }
+
+ setupPlayCodeSync();
+ setupPlayResizeSync();
+ localStorage.setItem(destSlideKey(), curSlide);
+ window.addEventListener('storage', updateOtherWindow, false);
+}
+
+// An update to local storage is caught only by the other window
+// The triggering window does not handle any sync actions
+function updateOtherWindow(e) {
+ // Ignore remove storage events which are not meant to update the other window
+ var isRemoveStorageEvent = !e.newValue;
+ if (isRemoveStorageEvent) return;
+
+ var destSlide = localStorage.getItem(destSlideKey());
+ while (destSlide > curSlide) {
+ nextSlide();
+ }
+ while (destSlide < curSlide) {
+ prevSlide();
+ }
+
+ updatePlay(e);
+ updateNotes();
+}