1
0
mirror of https://github.com/golang/go synced 2024-11-19 11:34:49 -07:00
go/cmd/present/static/slides.js
tell-k 85a87a81a9 cmd/present: fix CSS when printing slides
The existing CSS style was causing slides to not align with page
boundaries when printing slides. The cause of that is the CSS transform
property that is applied to automatically scale size of slides.
This change clears the transform property just before printing.

Fixes golang/go#29480

Change-Id: I6f719ad1b716e9bda8ba83007c3d1d7dece9ce08
GitHub-Last-Rev: d46dfee57601b2e9127c5e8e80e596dc2a2f24fa
GitHub-Pull-Request: golang/tools#67
Reviewed-on: https://go-review.googlesource.com/c/155940
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
2019-01-23 18:36:29 +00:00

627 lines
15 KiB
JavaScript

// 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('destSlide', curSlide);
};
function nextSlide() {
hideHelpText();
if (curSlide < slideEls.length - 1) {
curSlide++;
updateSlides();
}
if (notesEnabled) localStorage.setItem('destSlide', 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('destSlide', 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('destSlide');
while (destSlide > curSlide) {
nextSlide();
}
while (destSlide < curSlide) {
prevSlide();
}
updatePlay(e);
updateNotes();
}