173 lines
6.4 KiB
JavaScript
173 lines
6.4 KiB
JavaScript
|
var Alectryon;
|
||
|
(function(Alectryon) {
|
||
|
(function (slideshow) {
|
||
|
function anchor(sentence) { return "#" + sentence.id; }
|
||
|
|
||
|
function current_sentence() { return slideshow.sentences[slideshow.pos]; }
|
||
|
|
||
|
function unhighlight() {
|
||
|
var sentence = current_sentence();
|
||
|
if (sentence) sentence.classList.remove("alectryon-target");
|
||
|
slideshow.pos = -1;
|
||
|
}
|
||
|
|
||
|
function highlight(sentence) {
|
||
|
sentence.classList.add("alectryon-target");
|
||
|
}
|
||
|
|
||
|
function scroll(sentence) {
|
||
|
// Put the top of the current fragment close to the top of the
|
||
|
// screen, but scroll it out of view if showing it requires pushing
|
||
|
// the sentence past half of the screen. If sentence is already in
|
||
|
// a reasonable position, don't move.
|
||
|
var parent = sentence.parentElement;
|
||
|
/* We want to scroll the whole document, so start at root… */
|
||
|
while (parent && !parent.classList.contains("alectryon-root"))
|
||
|
parent = parent.parentElement;
|
||
|
/* … and work up from there to find a scrollable element.
|
||
|
parent.scrollHeight can be greater than parent.clientHeight
|
||
|
without showing scrollbars, so we add a 10px buffer. */
|
||
|
while (parent && parent.scrollHeight <= parent.clientHeight + 10)
|
||
|
parent = parent.parentElement;
|
||
|
/* <body> and <html> elements can have their client rect overflow
|
||
|
* the window if their height is unset, so scroll the window
|
||
|
* instead */
|
||
|
if (parent && (parent.nodeName == "BODY" || parent.nodeName == "HTML"))
|
||
|
parent = null;
|
||
|
|
||
|
var rect = function(e) { return e.getBoundingClientRect(); };
|
||
|
var parent_box = parent ? rect(parent) : { y: 0, height: window.innerHeight },
|
||
|
sentence_y = rect(sentence).y - parent_box.y,
|
||
|
fragment_y = rect(sentence.parentElement).y - parent_box.y;
|
||
|
|
||
|
// The assertion below sometimes fails for the first element in a block.
|
||
|
// console.assert(sentence_y >= fragment_y);
|
||
|
|
||
|
if (sentence_y < 0.1 * parent_box.height ||
|
||
|
sentence_y > 0.7 * parent_box.height) {
|
||
|
(parent || window).scrollBy(
|
||
|
0, Math.max(sentence_y - 0.5 * parent_box.height,
|
||
|
fragment_y - 0.1 * parent_box.height));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function highlighted(pos) {
|
||
|
return slideshow.pos == pos;
|
||
|
}
|
||
|
|
||
|
function navigate(pos, inhibitScroll) {
|
||
|
unhighlight();
|
||
|
slideshow.pos = Math.min(Math.max(pos, 0), slideshow.sentences.length - 1);
|
||
|
var sentence = current_sentence();
|
||
|
highlight(sentence);
|
||
|
if (!inhibitScroll)
|
||
|
scroll(sentence);
|
||
|
}
|
||
|
|
||
|
var keys = {
|
||
|
PAGE_UP: 33,
|
||
|
PAGE_DOWN: 34,
|
||
|
ARROW_UP: 38,
|
||
|
ARROW_DOWN: 40,
|
||
|
h: 72, l: 76, p: 80, n: 78
|
||
|
};
|
||
|
|
||
|
function onkeydown(e) {
|
||
|
e = e || window.event;
|
||
|
if (e.ctrlKey || e.metaKey) {
|
||
|
if (e.keyCode == keys.ARROW_UP)
|
||
|
slideshow.previous();
|
||
|
else if (e.keyCode == keys.ARROW_DOWN)
|
||
|
slideshow.next();
|
||
|
else
|
||
|
return;
|
||
|
} else {
|
||
|
// if (e.keyCode == keys.PAGE_UP || e.keyCode == keys.p || e.keyCode == keys.h)
|
||
|
// slideshow.previous();
|
||
|
// else if (e.keyCode == keys.PAGE_DOWN || e.keyCode == keys.n || e.keyCode == keys.l)
|
||
|
// slideshow.next();
|
||
|
// else
|
||
|
return;
|
||
|
}
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
|
||
|
function start() {
|
||
|
slideshow.navigate(0);
|
||
|
}
|
||
|
|
||
|
function toggleHighlight(idx) {
|
||
|
if (highlighted(idx))
|
||
|
unhighlight();
|
||
|
else
|
||
|
navigate(idx, true);
|
||
|
}
|
||
|
|
||
|
function handleClick(evt) {
|
||
|
if (evt.ctrlKey || evt.metaKey) {
|
||
|
var sentence = evt.currentTarget;
|
||
|
|
||
|
// Ensure that the goal is shown on the side, not inline
|
||
|
var checkbox = sentence.getElementsByClassName("alectryon-toggle")[0];
|
||
|
if (checkbox)
|
||
|
checkbox.checked = false;
|
||
|
|
||
|
toggleHighlight(sentence.alectryon_index);
|
||
|
evt.preventDefault();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function init() {
|
||
|
document.onkeydown = onkeydown;
|
||
|
slideshow.pos = -1;
|
||
|
slideshow.sentences = Array.from(document.getElementsByClassName("alectryon-sentence"));
|
||
|
slideshow.sentences.forEach(function (s, idx) {
|
||
|
s.addEventListener('click', handleClick, false);
|
||
|
s.alectryon_index = idx;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
slideshow.start = start;
|
||
|
slideshow.end = unhighlight;
|
||
|
slideshow.navigate = navigate;
|
||
|
slideshow.next = function() { navigate(slideshow.pos + 1); };
|
||
|
slideshow.previous = function() { navigate(slideshow.pos + -1); };
|
||
|
window.addEventListener('DOMContentLoaded', init);
|
||
|
})(Alectryon.slideshow || (Alectryon.slideshow = {}));
|
||
|
|
||
|
(function (styles) {
|
||
|
var styleNames = ["centered", "floating", "windowed"];
|
||
|
|
||
|
function className(style) {
|
||
|
return "alectryon-" + style;
|
||
|
}
|
||
|
|
||
|
function setStyle(style) {
|
||
|
var root = document.getElementsByClassName("alectryon-root")[0];
|
||
|
styleNames.forEach(function (s) {
|
||
|
root.classList.remove(className(s)); });
|
||
|
root.classList.add(className(style));
|
||
|
}
|
||
|
|
||
|
function init() {
|
||
|
var banner = document.getElementsByClassName("alectryon-banner")[0];
|
||
|
if (banner) {
|
||
|
banner.append(" Style: ");
|
||
|
styleNames.forEach(function (styleName, idx) {
|
||
|
var s = styleName;
|
||
|
var a = document.createElement("a");
|
||
|
a.onclick = function() { setStyle(s); };
|
||
|
a.append(styleName);
|
||
|
if (idx > 0) banner.append("; ");
|
||
|
banner.appendChild(a);
|
||
|
});
|
||
|
banner.append(".");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
window.addEventListener('DOMContentLoaded', init);
|
||
|
|
||
|
styles.setStyle = setStyle;
|
||
|
})(Alectryon.styles || (Alectryon.styles = {}));
|
||
|
})(Alectryon || (Alectryon = {}));
|