Annotation-System/cinera/cinera_player_pre.js

2232 lines
80 KiB
JavaScript

// refsCallback: (optional)
// Will be called when the player enters a marker that has a `data-ref` attribute. The value of `data-ref` will be passed to the function.
// When leaving a marker that a `data-ref` attribute, and entering a marker without one (or not entering a new marker at all), the function will be called with `null`.
var vod_platform = {
DIRECT: 0,
VIMEO: 1,
YOUTUBE: 2,
};
function
GetVODPlatformFromString(VODPlatformString)
{
var Result = null;
switch(VODPlatformString)
{
case "direct": Result = vod_platform.DIRECT; break;
case "vimeo": Result = vod_platform.VIMEO; break;
case "youtube": Result = vod_platform.YOUTUBE; break;
default: break;
}
return Result;
}
function Player(htmlContainer, refsCallback) {
this.container = htmlContainer;
this.markersContainer = this.container.querySelector(".markers_container");
this.videoContainer = this.container.querySelector(".video_container");
this.refsCallback = refsCallback || function() {};
if (!(this.videoContainer.getAttribute("data-platform") || this.videoContainer.getAttribute("data-videoId"))) {
console.error("Expected to find data-platform and data-videoId attribute on", this.videoContainer, "for player initialized on", this.container);
throw new Error("Missing data-platform or data-videoId attribute.");
}
this.markers = [];
var markerEls = this.markersContainer.querySelectorAll(".marker");
if (markerEls.length == 0) {
console.error("No markers found in", this.markersContainer, "for player initialized on", this.container);
throw new Error("Missing markers.");
}
for (var i = 0; i < markerEls.length; ++i) {
var marker = {
timestamp: parseInt(markerEls[i].getAttribute("data-timestamp"), 10),
ref: markerEls[i].getAttribute("data-ref"),
endTime: (i < markerEls.length - 1 ? parseInt(markerEls[i+1].getAttribute("data-timestamp"), 10) : null),
el: markerEls[i],
fadedProgress: markerEls[i].querySelector(".progress.faded"),
progress: markerEls[i].querySelector(".progress.main"),
hoverx: null
};
marker.el.addEventListener("click", this.onMarkerClick.bind(this, marker));
marker.el.addEventListener("mousemove", this.onMarkerMouseMove.bind(this, marker));
marker.el.addEventListener("mouseleave", this.onMarkerMouseLeave.bind(this, marker));
this.markers.push(marker);
}
this.vod_platform = GetVODPlatformFromString(this.videoContainer.getAttribute("data-platform"));
this.currentMarker = null;
this.currentMarkerIdx = null;
this.platformPlayer = null;
this.platformPlayerReady = false;
this.duration = null;
this.playing = false;
this.shouldPlay = false;
this.buffering = false;
this.pauseAfterBuffer = false;
this.speed = 1;
this.currentTime = -1;
this.scrollTo = -1;
this.scrollPosition = 0;
this.nextFrame = null;
this.looping = false;
this.markersContainer.addEventListener("wheel", function(ev) {
this.scrollTo = -1;
}.bind(this));
Player.initializePlatform(this.vod_platform, this.onPlatformReady.bind(this));
var PendingMobileStyleInitialisation = true;
this.updateSize(PendingMobileStyleInitialisation);
this.resume();
}
// Start playing the video from the current position.
// If the player hasn't loaded yet, it will autoplay when ready.
Player.prototype.play = function() {
if (this.platformPlayerReady) {
if (!this.playing) {
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
this.platformPlayer.play();
} break;
case vod_platform.VIMEO:
{
this.platformPlayer.play();
} break;
case vod_platform.YOUTUBE:
{
this.platformPlayer.playVideo();
} break;
}
this.pauseAfterBuffer = false;
} else {
this.shouldPlay = true;
}
}
};
// Pause the video at the current position.
// If the player hasn't loaded yet, it will not autoplay when ready. (This is the default)
Player.prototype.pause = function() {
if (this.platformPlayerReady) {
if (this.playing) {
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
this.platformPlayer.pause();
} break;
case vod_platform.VIMEO:
{
this.platformPlayer.pause();
} break;
case vod_platform.YOUTUBE:
{
this.platformPlayer.pauseVideo();
} break;
}
} else if (this.buffering) {
this.pauseAfterBuffer = true;
}
} else {
this.shouldPlay = false;
}
};
// Sets the current time then plays.
// If the player hasn't loaded yet, it will seek to this time when ready.
Player.prototype.setTimeThenPlay = function(time) {
this.currentTime = time;
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
if (this.platformPlayerReady) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration));
this.platformPlayer.currentTime = this.currentTime;
this.updateProgress();
this.play();
}
} break;
case vod_platform.VIMEO:
{
if (this.platformPlayerReady) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration));
var Parent = this;
this.platformPlayer.setCurrentTime(this.currentTime)
.then(function() {
Parent.updateProgress();
Parent.play();
});
}
} break;
case vod_platform.YOUTUBE:
{
if (this.platformPlayerReady) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration));
this.platformPlayer.seekTo(this.currentTime);
this.updateProgress();
this.play();
}
} break;
}
};
Player.prototype.jumpToNextMarker = function() {
var targetMarkerIdx = Math.min((this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx + 1), this.markers.length-1);
var targetTime = this.markers[targetMarkerIdx].timestamp;
while(targetMarkerIdx < this.markers.length && this.markers[targetMarkerIdx].el.classList.contains("skip"))
{
++targetMarkerIdx;
targetTime = this.markers[targetMarkerIdx].timestamp;
}
this.setTimeThenPlay(targetTime);
};
Player.prototype.jumpToPrevMarker = function() {
var targetMarkerIdx = Math.max(0, (this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx - 1));
var targetTime = this.markers[targetMarkerIdx].timestamp;
while(targetMarkerIdx >= 0 && this.markers[targetMarkerIdx].el.classList.contains("skip"))
{
--targetMarkerIdx;
targetTime = this.markers[targetMarkerIdx].timestamp;
}
this.setTimeThenPlay(targetTime);
};
function
GetHeightOfHideableElement(Element, UnhidingClass)
{
var Result = 0;
if(Element.classList.contains(UnhidingClass))
{
Result = Element.offsetHeight;
}
else
{
var ZOffset = Element.style.zOffset;
Element.style.zOffset = -1;
Element.classList.add(UnhidingClass);
Result = Element.offsetHeight;
Element.classList.remove(UnhidingClass);
Element.style.zOffset = ZOffset;
}
return Result;
}
function
GetWidthOfHideableElement(Element, UnhidingClass)
{
var Result = 0;
if(Element.classList.contains(UnhidingClass))
{
Result = Element.offsetWidth;
}
else
{
var ZOffset = Element.style.zOffset;
Element.style.zOffset = -1;
Element.classList.add(UnhidingClass);
Result = Element.offsetWidth;
Element.classList.remove(UnhidingClass);
Element.style.zOffset = ZOffset;
}
return Result;
}
function
ComputeVerticalOffsetForMenu(Menu, Toggler, VideoContainerDimY)
{
var Result = 0;
var MenuHeight = GetHeightOfHideableElement(Menu, "visible");
var TogglerHeight = Toggler.offsetHeight;
var TogglerOffset = Toggler.offsetTop;
var Result = TogglerOffset + (TogglerHeight / 2) - (MenuHeight / 2);
var LowerProtrusion = MenuHeight + Result - VideoContainerDimY;
if(LowerProtrusion > 0)
{
Result -= LowerProtrusion;
}
if(Result < 0)
{
Result = 0;
}
return Result;
}
function sizeAndPositionMenuContainer(TitleBar, SizerElement, Menu)
{
if(Menu)
{
var Toggler = Menu.parentElement;
var TitleBarDimX = TitleBar.offsetWidth;
var TitleBarDimY = TitleBar.offsetHeight;
var SizerElementDimX = SizerElement.offsetWidth;
var SizerElementDimY = SizerElement.offsetHeight;
var ContainerDimX = SizerElementDimX;
var ContainerDimY = SizerElementDimY;
switch(CineraProps.O)
{
case orientations.PORTRAIT:
{
Menu.style.borderTopWidth = 0;
Menu.style.borderTopStyle = "none";
Menu.style.borderRightWidth = "1px";
Menu.style.borderRightStyle = "solid";
Menu.style.borderLeftWidth = "1px";
Menu.style.borderLeftStyle = "solid";
Menu.style.maxWidth = ContainerDimX + "px";
ContainerDimY -= TitleBarDimY;
Menu.style.maxHeight = ContainerDimY + "px";
Menu.style.top = TitleBarDimY + "px";
Menu.style.left = 0 + "px";
} break;
case orientations.LANDSCAPE_LEFT:
{
Menu.style.borderTopWidth = "1px";
Menu.style.borderTopStyle = "solid";
Menu.style.borderRightWidth = "1px";
Menu.style.borderRightStyle = "solid";
Menu.style.borderLeftWidth = 0;
Menu.style.borderLeftStyle = "none";
ContainerDimX -= TitleBarDimX;
Menu.style.maxWidth = ContainerDimX + "px";
Menu.style.maxHeight = ContainerDimY + "px";
var MenuVerticalOffset = ComputeVerticalOffsetForMenu(Menu, Toggler, SizerElementDimY);
Menu.style.top = MenuVerticalOffset + "px";
Menu.style.left = TitleBarDimX + "px";
} break;
case orientations.LANDSCAPE_RIGHT:
{
Menu.style.borderTopWidth = "1px";
Menu.style.borderTopStyle = "solid";
Menu.style.borderRightWidth = 0;
Menu.style.borderRightStyle = "none";
Menu.style.borderLeftWidth = "1px";
Menu.style.borderLeftStyle = "solid";
ContainerDimX -= TitleBarDimX;
Menu.style.maxWidth = ContainerDimX + "px";
Menu.style.maxHeight = ContainerDimY + "px";
var MenuVerticalOffset = ComputeVerticalOffsetForMenu(Menu, Toggler, SizerElementDimY);
Menu.style.top = MenuVerticalOffset + "px";
var MenuWidth = GetWidthOfHideableElement(Menu, "visible");
Menu.style.left = -MenuWidth + "px";
} break;
}
}
}
function
ComputeTallest(Elements, UnhidingClass)
{
var Result = null;
for(var i = 0; i < Elements.length; ++i)
{
var This = Elements[i];
var Height = UnhidingClass ? GetHeightOfHideableElement(This, UnhidingClass) : This.offsetHeight;
if(Height > Result)
{
Result = Height;
}
}
return Result;
}
function
ComputeAndSetTallest(Selector, Elements, UnhidingClass)
{
var Result;
Selector.style.height = "unset";
Result = ComputeTallest(Elements, UnhidingClass);
Selector.style.height = Result + "px";
return Result;
}
function ApplyMobileStyle(VideoContainer)
{
var WindowDim = DeriveReliableWindowDimensions();
var MaxWidth = MaxWidthOfElement(cinera, WindowDim);
var MaxHeight = MaxHeightOfElement(cinera, WindowDim);
var IndicesBar = playerContainer.querySelector(".markers_container");
var Markers = IndicesBar.querySelector(".markers");
var CineraContentWidth = MaxWidth;
var EpisodeMarkers = IndicesBar.querySelectorAll(".episodeMarker");
for(var i = 0; i < EpisodeMarkers.length; ++i)
{
CineraContentWidth -= EpisodeMarkers[i].offsetWidth;
}
switch(CineraProps.O)
{
case orientations.PORTRAIT:
{
cinera.style.flexDirection = "column";
titleBar.style.flexDirection = "row";
} break;
case orientations.LANDSCAPE_LEFT:
{
cinera.style.flexDirection = "row";
titleBar.style.flexDirection = "column-reverse";
CineraContentWidth -= titleBar.offsetWidth;
} break;
case orientations.LANDSCAPE_RIGHT:
{
cinera.style.flexDirection = "row-reverse";
titleBar.style.flexDirection = "column";
CineraContentWidth -= titleBar.offsetWidth;
} break;
}
var HeightOfTallestIndex;
if(MobileCineraContentRule !== undefined)
{
MobileCineraContentRule.style.width = CineraContentWidth + "px";
var MarkerList = Markers.querySelectorAll(".marker");
HeightOfTallestIndex = ComputeAndSetTallest(MobileCineraContentRule, MarkerList, "current");
IndicesBar.style.height = HeightOfTallestIndex + "px";
Markers.style.width = CineraContentWidth + "px";
}
var VideoMaxDimX = MaxWidth;
var VideoMaxDimY = MaxHeight;
var MinimumVideoHeight = 32;
if(MaxHeight - HeightOfTallestIndex > MinimumVideoHeight)
{
VideoMaxDimY -= HeightOfTallestIndex;
}
switch(CineraProps.O)
{
case orientations.PORTRAIT:
{
VideoMaxDimY -= titleBar.offsetHeight;
} break;
case orientations.LANDSCAPE_LEFT:
case orientations.LANDSCAPE_RIGHT:
{
VideoMaxDimX -= titleBar.offsetWidth;
} break;
}
var VideoDimYFromMaxX = VideoMaxDimX * 9 / 16;
var VideoDimXFromMaxY = VideoMaxDimY * 16 / 9;
var VideoDimX = 0;
var VideoDimY = 0;
if(VideoDimXFromMaxY > VideoMaxDimX)
{
VideoDimX = Math.floor(VideoMaxDimX);
VideoDimY = Math.floor(VideoDimYFromMaxX);
}
else if(VideoDimYFromMaxX > VideoMaxDimY)
{
VideoDimY = Math.floor(VideoMaxDimY);
VideoDimX = Math.floor(VideoDimXFromMaxY);
}
else
{
VideoDimX = Math.floor(VideoMaxDimX);
VideoDimY = Math.floor(VideoDimYFromMaxX);
}
VideoContainer.style.width = VideoDimX + "px";
VideoContainer.style.height = VideoDimY + "px";
sizeAndPositionMenuContainer(titleBar, cinera, quotesMenu);
sizeAndPositionMenuContainer(titleBar, cinera, referencesMenu);
sizeAndPositionMenuContainer(titleBar, cinera, filterMenu);
sizeAndPositionMenuContainer(titleBar, cinera, linkMenu);
sizeAndPositionMenuContainer(titleBar, cinera, creditsMenu);
}
function
IconifyMenuTogglers()
{
if(quotesMenu)
{
quotesMenu.previousElementSibling.textContent = '\u{1F5E9}';
}
if(referencesMenu)
{
referencesMenu.previousElementSibling.textContent = '\u{1F4D6}';
}
if(creditsMenu)
{
creditsMenu.previousElementSibling.textContent = '\u{1F46A}';
}
if(viewsMenu)
{
viewsMenu.remove();
viewsMenu = null;
}
}
function
InitMobileControls()
{
var rightmost = {};
var markersContainer = cinera.querySelector(".markers_container");
markersContainer.style.height = "auto";
var episodeMarkerFirst = markersContainer.querySelector(".episodeMarker.first");
var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev");
var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next");
var episodeMarkerLast = markersContainer.querySelector(".episodeMarker.last");
if(episodeMarkerPrev) { episodeMarkerPrev.firstChild.textContent = '\u{23EE}'; }
if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = '\u{23ED}'; rightmost = episodeMarkerNext; }
else if (episodeMarkerLast) { rightmost = episodeMarkerLast; }
var controlPrevTimestamp = document.createElement("a");
controlPrevTimestamp.classList.add("episodeMarker");
controlPrevTimestamp.classList.add("prevTimestamp");
var controlPrevTimestampContent = document.createElement("div");
controlPrevTimestampContent.appendChild(document.createTextNode('\u{25C0}'));
controlPrevTimestamp.appendChild(controlPrevTimestampContent);
var markers = markersContainer.querySelector(".markers");
markersContainer.insertBefore(controlPrevTimestamp, markers);
var controlNextTimestamp = document.createElement("a");
controlNextTimestamp.classList.add("episodeMarker");
controlNextTimestamp.classList.add("nextTimestamp");
var controlNextTimestampContent = document.createElement("div");
controlNextTimestampContent.appendChild(document.createTextNode('\u{25B6}'));
controlNextTimestamp.appendChild(controlNextTimestampContent);
if(rightmost)
{
markersContainer.insertBefore(controlNextTimestamp, rightmost);
}
else
{
markersContainer.appendChild(controlNextTimestamp);
}
}
function InitMobileStyle()
{
cinera.classList.add("mobile");
IconifyMenuTogglers();
InitMobileControls();
var VideoContainer = cinera.querySelector(".video_container");
ApplyMobileStyle(VideoContainer);
}
function
ConnectMobileControls(player)
{
var markersContainer = player.markersContainer;
var ControlPrevTimestamp = markersContainer.querySelector(".episodeMarker.prevTimestamp");
ControlPrevTimestamp.addEventListener("click", function(ev) {
player.jumpToPrevMarker();
});
var ControlNextTimestamp = markersContainer.querySelector(".episodeMarker.nextTimestamp");
ControlNextTimestamp.addEventListener("click", function(ev) {
player.jumpToNextMarker();
});
}
// Call this after changing the size of the video container in order to update the platform player.
Player.prototype.updateSize = function() {
var width = 0;
var height = 0;
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(!CineraProps.IsMobile)
{
var VisibleArea = MaxDimensionsOfElement(this.container, GetWindowDim(false));
var AvailableHeight = VisibleArea.Y - titleBar.offsetHeight;
var VerticalScrollBarWidth = this.markersContainer.offsetWidth - this.markersContainer.clientWidth;
width = VisibleArea.X - (this.markersContainer.scrollWidth + VerticalScrollBarWidth);
height = width / 16 * 9; // TODO(matt): Get the aspect ratio from the video itself?
if(height > AvailableHeight)
{
height = AvailableHeight;
width = height / 9 * 16;
}
this.markersContainer.style.height = height + "px";
var VacantPixelsBelowMenus = 4;
var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - VacantPixelsBelowMenus;
MenuContainerRule.style.maxHeight = MenuMaxHeight + "px";
}
else
{
ApplyMobileStyle(this.videoContainer);
width = this.videoContainer.offsetWidth;
height = this.videoContainer.offsetHeight;
}
if(this.platformPlayerReady)
{
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
this.platformPlayer.setAttribute("width", Math.floor(width));
this.platformPlayer.setAttribute("height", Math.floor(height));
} break;
case vod_platform.VIMEO: break; // NOTE(matt): It responds automatically
case vod_platform.YOUTUBE:
{
this.platformPlayer.setSize(Math.floor(width), Math.floor(height));
} break;
default: break;
}
}
}
// Stops the per-frame work that the player does. Call when you want to hide or get rid of the player.
Player.prototype.halt = function() {
this.pause();
this.looping = false;
if (this.nextFrame) {
cancelAnimationFrame(this.nextFrame);
this.nextFrame = null;
}
}
// Resumes the per-frame work that the player does. Call when you want to show the player again after hiding.
Player.prototype.resume = function() {
this.looping = true;
if (!this.nextFrame) {
this.doFrame();
}
}
Player.initializePlatform = function(platform_id, callback) {
switch(platform_id)
{
case vod_platform.DIRECT:
case vod_platform.VIMEO:
{
callback();
} break;
case vod_platform.YOUTUBE:
{
if(window.YT && window.YT.loaded)
{
callback()
}
else
{
if (window.APYoutubeAPIReady === undefined) {
window.APYoutubeAPIReady = false;
window.APCallbacks = (callback ? [callback] : []);
window.onYouTubeIframeAPIReady = function() {
window.APYoutubeAPIReady = true;
for (var i = 0; i < APCallbacks.length; ++i) {
APCallbacks[i]();
}
};
} else if (window.APYoutubeAPIReady === false) {
window.APCallbacks.push(callback);
} else if (window.APYoutubeAPIReady === true) {
callback();
}
}
} break;
}
}
// END PUBLIC INTERFACE
Player.prototype.onMarkerClick = function(marker, ev) {
var time = marker.timestamp;
if (this.currentMarker == marker && marker.hoverx !== null) {
time += (marker.endTime - marker.timestamp) * marker.hoverx;
}
this.setTimeThenPlay(time);
};
Player.prototype.onMarkerMouseMove = function(marker, ev) {
if (this.currentMarker == marker) {
var CineraContent = this.currentMarker.el.querySelector(".cineraContent");
marker.hoverx = (ev.pageX - getElementXOffsetFromPage(CineraContent)) / CineraContent.offsetWidth;
}
};
Player.prototype.onMarkerMouseLeave = function(marker, ev) {
marker.hoverx = null;
};
Player.prototype.updateProgress = function() {
var prevMarker = this.currentMarker;
this.currentMarker = null;
this.currentMarkerIdx = null;
for (var i = 0; i < this.markers.length; ++i) {
var marker = this.markers[i];
if (marker.timestamp <= this.currentTime && this.currentTime < marker.endTime) {
this.currentMarker = marker;
this.currentMarkerIdx = i;
break;
}
}
if (this.currentMarker) {
var CineraContent = this.currentMarker.el.querySelector(".cineraContent");
var totalWidth = CineraContent.offsetWidth;
var progress = (this.currentTime - this.currentMarker.timestamp) / (this.currentMarker.endTime - this.currentMarker.timestamp);
if (this.currentMarker.hoverx === null) {
var pixelWidth = progress * totalWidth;
this.currentMarker.fadedProgress.style.width = Math.ceil(pixelWidth) + "px";
this.currentMarker.fadedProgress.style.opacity = pixelWidth - Math.floor(pixelWidth);
this.currentMarker.progress.style.width = Math.floor(pixelWidth) + "px";
} else {
this.currentMarker.fadedProgress.style.opacity = 1;
this.currentMarker.progress.style.width = Math.floor(Math.min(this.currentMarker.hoverx, progress) * totalWidth) + "px";
this.currentMarker.fadedProgress.style.width = Math.floor(Math.max(this.currentMarker.hoverx, progress) * totalWidth) + "px";
}
}
if (this.currentMarker != prevMarker) {
if (prevMarker) {
prevMarker.el.classList.remove("current");
prevMarker.fadedProgress.style.width = "0px";
prevMarker.progress.style.width = "0px";
prevMarker.hoverx = null;
}
if (this.currentMarker) {
if(this.currentMarkerIdx == this.markers.length - 1)
{
localStorage.removeItem(lastTimestampStorageItem);
}
else
{
localStorage.setItem(lastTimestampStorageItem, this.currentMarker.timestamp);
}
this.currentMarker.el.classList.add("current");
this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0;
this.scrollPosition = this.markersContainer.scrollTop;
this.refsCallback(this.currentMarker.ref, this.currentMarker.el, this);
} else if (prevMarker && prevMarker.ref) {
this.refsCallback(null);
}
}
};
Player.prototype.doFrame = function() {
if (this.playing) {
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
this.currentTime = this.platformPlayer.currentTime;
this.updateProgress();
} break;
case vod_platform.VIMEO:
{
var Parent = this;
this.platformPlayer.getCurrentTime()
.then(function(Result) {
Parent.currentTime = Result;
Parent.updateProgress();
});
} break;
case vod_platform.YOUTUBE:
{
this.currentTime = this.platformPlayer.getCurrentTime();
this.updateProgress();
} break;
}
}
if (this.scrollTo >= 0) {
var targetPosition = this.scrollTo - this.markersContainer.offsetHeight/2.0;
targetPosition = Math.max(0, Math.min(targetPosition, this.markersContainer.scrollHeight - this.markersContainer.offsetHeight));
this.scrollPosition += (targetPosition - this.scrollPosition) * 0.1;
if (Math.abs(this.scrollPosition - targetPosition) < 1.0) {
this.markersContainer.scrollTop = targetPosition;
this.scrollTo = -1;
} else {
this.markersContainer.scrollTop = this.scrollPosition;
}
}
this.nextFrame = requestAnimationFrame(this.doFrame.bind(this));
updateLink();
};
Player.prototype.setStyleAndQuality = function() {
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
// NOTE(matt): onPlatformReady() has set the width and height
} break;
case vod_platform.VIMEO:
{
this.platformPlayer.setQuality("1080p");
var frame = this.videoContainer.querySelector("iframe");
frame.style.width = "100%";
frame.style.height = "100%";
frame.style.position = "absolute"
frame.style.top = 0;
frame.style.left = 0;
} break;
case vod_platform.YOUTUBE:
{
this.platformPlayer.setPlaybackQuality("hd1080");
} break;
}
this.updateSize();
}
Player.prototype.setDurationThenAutoplay = function(Duration) {
this.duration = Duration;
this.markers[this.markers.length-1].endTime = this.duration;
if (this.currentTime > 0) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration));
this.setTimeThenPlay(this.currentTime);
}
if (this.shouldPlay) {
this.play();
}
}
Player.prototype.acquireDurationThenAutoplay = function() {
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
this.setDurationThenAutoplay(this.platformPlayer.duration);
} break;
case vod_platform.VIMEO:
{
var Parent = this;
this.platformPlayer.getDuration()
.then(function(Response)
{
Parent.setDurationThenAutoplay(Response);
});
} break;
case vod_platform.YOUTUBE:
{
this.setDurationThenAutoplay(this.platformPlayer.getDuration());
} break;
}
}
Player.prototype.onPlatformPlayerReady = function() {
this.platformPlayerReady = true;
this.setStyleAndQuality();
this.acquireDurationThenAutoplay();
};
Player.prototype.onPlaybackRateChange = function(Rate)
{
this.speed = Rate;
}
Player.prototype.onDirectPlayerPlaybackRateChange = function(ev) {
this.onPlaybackRateChange(this.platformPlayer.playbackRate);
};
Player.prototype.onVimeoPlayerPlaybackRateChange = function(ev) {
this.onPlaybackRateChange(ev.playbackRate);
};
Player.prototype.onYouTubePlayerPlaybackRateChange = function(ev) {
this.onPlaybackRateChange(ev.data);
};
Player.prototype.onStateBufferStart = function()
{
this.buffering = true;
this.updateProgress();
}
Player.prototype.onStateBufferEnd = function()
{
this.buffering = false;
this.updateProgress();
}
Player.prototype.onStateEnded = function()
{
this.buffering = false;
this.playing = false;
localStorage.removeItem(lastTimestampStorageItem);
this.currentTime = null;
this.updateProgress();
}
Player.prototype.onStatePaused = function(CurrentTime)
{
this.buffering = false;
this.playing = false;
this.currentTime = CurrentTime;
this.updateProgress();
}
Player.prototype.onStatePlaying = function(CurrentTime)
{
this.buffering = false;
this.playing = true;
this.currentTime = CurrentTime;
if(this.pauseAfterBuffer)
{
this.pauseAfterBuffer = false;
this.pause();
}
}
Player.prototype.onDirectPlayerStateChange_Paused = function(ev)
{
this.onStatePaused(this.platformPlayer.currentTime);
}
Player.prototype.onDirectPlayerStateChange_Playing = function(ev)
{
this.onStatePlaying(this.platformPlayer.currentTime);
}
Player.prototype.onVimeoPlayerStateChange_Paused = function(ev)
{
this.onStatePaused(ev.seconds);
}
Player.prototype.onVimeoPlayerStateChange_Playing = function(ev)
{
this.onStatePlaying(ev.seconds);
}
Player.prototype.onYouTubePlayerStateChange = function(ev) {
switch(ev.data)
{
case YT.PlayerState.BUFFERING: { this.onStateBufferStart(); }; break;
case YT.PlayerState.ENDED: { this.onStateEnded(); }; break;
case YT.PlayerState.PAUSED: { this.onStatePaused(this.platformPlayer.getCurrentTime()); }; break;
case YT.PlayerState.PLAYING: { this.onStatePlaying(this.platformPlayer.getCurrentTime()); }; break;
default: break;
}
};
Player.prototype.onPlatformReady = function() {
var platformPlayerDiv = document.createElement("DIV");
platformPlayerDiv.id = "platform_player_" + Player.platformPlayerCount++;
var platformPlayerDivPlaced = this.videoContainer.appendChild(platformPlayerDiv);
switch(this.vod_platform)
{
case vod_platform.DIRECT:
{
platformPlayerDivPlaced.classList.add("direct_video");
var videoNode = document.createElement("VIDEO");
videoNode.controls = true;
videoNode.setAttribute("width", this.videoContainer.offsetWidth);
videoNode.setAttribute("height", this.videoContainer.offsetWidth / 16 * 9);
var videoSourceNode = document.createElement("SOURCE");
videoSourceNode.setAttribute("src", this.videoContainer.getAttribute("data-videoId"));
videoNode.appendChild(videoSourceNode);
this.platformPlayer = platformPlayerDiv.appendChild(videoNode);
this.platformPlayer.addEventListener("loadedmetadata", this.onPlatformPlayerReady.bind(this));
this.platformPlayer.addEventListener("ratechange", this.onDirectPlayerPlaybackRateChange.bind(this));
this.platformPlayer.addEventListener("play", this.onDirectPlayerStateChange_Playing.bind(this));
this.platformPlayer.addEventListener("pause", this.onDirectPlayerStateChange_Paused.bind(this));
this.platformPlayer.addEventListener("waiting", this.onStateBufferStart.bind(this));
this.platformPlayer.addEventListener("playing", this.onStateBufferEnd.bind(this));
this.platformPlayer.addEventListener("ended", this.onStateEnded.bind(this));
} break;
case vod_platform.VIMEO:
{
this.videoContainer.style.position = "relative";
this.videoContainer.style.alignSelf = "unset";
var CallData = {
id: this.videoContainer.getAttribute("data-videoId"),
title: false,
};
var CCLang = this.videoContainer.getAttribute("data-ccLang");
if(CCLang != null)
{
CallData.texttrack = CCLang;
}
this.platformPlayer = new Vimeo.Player(platformPlayerDiv.id, CallData);
this.platformPlayer.ready()
.then(this.onPlatformPlayerReady.bind(this));
this.platformPlayer.on("playbackratechange", this.onVimeoPlayerPlaybackRateChange.bind(this));
this.platformPlayer.on("play", this.onVimeoPlayerStateChange_Playing.bind(this));
this.platformPlayer.on("pause", this.onVimeoPlayerStateChange_Paused.bind(this));
this.platformPlayer.on("bufferstart", this.onStateBufferStart.bind());
this.platformPlayer.on("bufferend", this.onStateBufferEnd.bind());
this.platformPlayer.on("ended", this.onStateEnded.bind());
} break;
case vod_platform.YOUTUBE:
{
var CallData = {
videoId: this.videoContainer.getAttribute("data-videoId"),
width: this.videoContainer.offsetWidth,
height: this.videoContainer.offsetWidth / 16 * 9,
playerVars: { 'playsinline': 1 },
events: {
"onReady": this.onPlatformPlayerReady.bind(this),
"onStateChange": this.onYouTubePlayerStateChange.bind(this),
"onPlaybackRateChange": this.onYouTubePlayerPlaybackRateChange.bind(this)
}
};
var CCLang = this.videoContainer.getAttribute("data-ccLang");
if(CCLang != null)
{
CallData.cc_lang_pref = CCLang;
CallData.cc_load_policy = 1;
}
this.platformPlayer = new YT.Player(platformPlayerDiv.id, CallData);
} break;
}
};
Player.platformPlayerCount = 0;
function toggleFilterMode() {
if(filterMode == "inclusive")
{
filterModeElement.classList.remove("inclusive");
filterModeElement.classList.add("exclusive");
filterMode = "exclusive";
}
else
{
filterModeElement.classList.remove("exclusive");
filterModeElement.classList.add("inclusive");
filterMode = "inclusive";
}
applyFilter();
}
function updateLink()
{
if(link && player)
{
if(linkTimestamp == true)
{
if(player.currentMarker)
{
link.value = baseURL + "#" + player.currentMarker.timestamp;
}
else
{
link.value = baseURL;
}
}
else
{
switch(player.vod_platform)
{
case vod_platform.DIRECT:
{
link.value = baseURL + "#" + Math.round(player.platformPlayer.currentTime);
} break;
case vod_platform.VIMEO:
{
player.platformPlayer.getCurrentTime()
.then(function(Response)
{
link.value = baseURL + "#" + Math.round(Response);
});
} break;
case vod_platform.YOUTUBE:
{
link.value = baseURL + "#" + Math.round(player.platformPlayer.getCurrentTime());
} break;
}
}
}
}
function toggleLinkMode(linkMode, link)
{
linkTimestamp = !linkTimestamp;
if(linkTimestamp == true)
{
linkMode.textContent = "Link to: current timestamp";
}
else
{
linkMode.textContent = "Link to: nearest second";
}
updateLink();
}
function toggleFilterOrLinkMode()
{
for(menuIndex in menuState)
{
if(menuState[menuIndex].classList.contains("filter_container") && menuState[menuIndex].classList.contains("visible"))
{
toggleFilterMode();
}
if(menuState[menuIndex].classList.contains("link_container") && menuState[menuIndex].classList.contains("visible"))
{
toggleLinkMode(linkMode, link);
}
}
}
function toggleMenuVisibility(element) {
if(element.classList.contains("visible"))
{
element.classList.remove("visible");
element.parentNode.classList.remove("visible");
if(focusedElement)
{
focusedElement.classList.remove("focused");
focusedElement = null;
}
if(focusedIdentifier)
{
focusedIdentifier.classList.remove("focused");
focusedIdentifier = null;
}
}
else
{
for(menuIndex in menuState)
{
menuState[menuIndex].classList.remove("visible");
menuState[menuIndex].parentNode.classList.remove("visible");
if(focusedElement)
{
focusedElement.classList.remove("focused");
}
if(focusedIdentifier)
{
focusedIdentifier.classList.remove("focused");
}
}
element.classList.add("visible");
element.parentNode.classList.add("visible");
if(element.classList.contains("quotes_container"))
{
if(!lastFocusedQuote)
{
lastFocusedQuote = element.querySelectorAll(".ref")[0];
}
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
}
else if(element.classList.contains("references_container"))
{
if(!lastFocusedReference || !lastFocusedIdentifier)
{
lastFocusedReference = element.querySelectorAll(".ref")[0];
lastFocusedIdentifier = lastFocusedReference.querySelector(".ref_indices").firstElementChild;
}
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
}
else if(element.classList.contains("filter_container"))
{
if(!lastFocusedCategory)
{
lastFocusedCategory = element.querySelectorAll(".filter_content")[0];
}
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
}
else if(element.classList.contains("credits_container"))
{
if(!lastFocusedCreditItem)
{
if(element.querySelectorAll(".credit .person")[0].nextElementSibling)
{
lastFocusedCreditItem = element.querySelectorAll(".credit .support")[0];
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
setSpriteLightness(focusedElement.firstChild);
}
else
{
lastFocusedCreditItem = element.querySelectorAll(".credit .person")[0];
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
}
}
else
{
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
}
}
}
}
function handleMouseOverViewsMenu()
{
switch(CineraProps.V)
{
case views.REGULAR:
case views.THEATRE:
{
viewsContainer.style.display = "block";
} break;
case views.SUPERTHEATRE:
{
viewsContainer.style.display = "none";
} break;
}
}
function IsFullScreen()
{
return (document.fullscreen || document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement);
}
function enterFullScreen_()
{
if(!IsFullScreen())
{
if(document.body.requestFullscreen) { document.body.requestFullscreen(); }
else if(document.body.mozRequestFullScreen) { document.body.mozRequestFullScreen(); }
else if(document.body.webkitRequestFullScreen) { document.body.webkitRequestFullScreen(); }
else if(document.body.msRequestFullscreen) { document.body.msRequestFullscreen(); }
}
}
function leaveFullScreen_()
{
if(IsFullScreen())
{
if(document.exitFullscreen) { document.exitFullscreen(); }
else if(document.mozCancelFullScreen) { document.mozCancelFullScreen(); }
else if(document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); }
else if(document.msExitFullscreen) { document.msExitFullscreen(); }
}
}
function toggleTheatreMode() {
switch(CineraProps.V)
{
case views.REGULAR:
{
CineraProps.C = cinera.style.backgroundColor;
CineraProps.Z = cinera.style.zIndex;
CineraProps.X = cinera.style.left;
CineraProps.Y = cinera.style.top;
CineraProps.W = cinera.style.width;
CineraProps.mW = cinera.style.maxWidth;
CineraProps.H = cinera.style.height;
CineraProps.mH = cinera.style.maxHeight;
CineraProps.P = cinera.style.position;
CineraProps.Display = cinera.style.display;
CineraProps.FlexDirection = cinera.style.flexDirection;
CineraProps.JustifyContent = cinera.style.justifyContent;
CineraProps.ScrollX = window.scrollX;
CineraProps.ScrollY = window.scrollY;
cinera.style.backgroundColor = "#000";
cinera.style.zIndex = 64;
cinera.style.left = 0;
cinera.style.top = 0;
cinera.style.width = "100%";
cinera.style.maxWidth = "100%";
cinera.style.height = "100%";
cinera.style.maxHeight = "100%";
cinera.style.position = "fixed";
cinera.style.display = "flex";
cinera.style.flexDirection = "column";
cinera.style.justifyContent = "center";
viewItems[0].setAttribute("data-id", "regular");
viewItems[0].setAttribute("title", "Regular mode");
viewItems[0].firstChild.nodeValue = "📺";
} CineraProps.V = views.THEATRE; localStorage.setItem(cineraViewStorageItem, views.THEATRE); break;
case views.SUPERTHEATRE:
{
leaveFullScreen_();
}
case views.THEATRE:
{
cinera.style.backgroundColor = CineraProps.C;
cinera.style.zIndex = CineraProps.Z;
cinera.style.left = CineraProps.X;
cinera.style.top = CineraProps.Y;
cinera.style.width = CineraProps.W;
cinera.style.maxWidth = CineraProps.mW;
cinera.style.height = CineraProps.H;
cinera.style.maxHeight = CineraProps.mH;
cinera.style.position = CineraProps.P;
cinera.style.display = CineraProps.Display;
cinera.style.flexDirection = CineraProps.FlexDirection;
cinera.style.justifyContent = CineraProps.JustifyContent;
window.scroll(
{
top: CineraProps.ScrollY,
left: CineraProps.ScrollX
}
);
viewItems[0].setAttribute("data-id", "theatre");
viewItems[0].setAttribute("title", "Theatre mode");
viewItems[0].firstChild.nodeValue = "🎭";
} CineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break;
}
player.updateSize();
}
function toggleSuperTheatreMode()
{
switch(CineraProps.V)
{
case views.REGULAR:
{
toggleTheatreMode();
}
case views.THEATRE:
{
enterFullScreen_();
} CineraProps.V = views.SUPERTHEATRE; localStorage.setItem(cineraViewStorageItem, views.SUPERTHEATRE); break;
case views.SUPERTHEATRE:
{
leaveFullScreen_();
toggleTheatreMode();
} CineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break;
}
player.updateSize();
}
function AscribeTemporaryResponsibility(Element, Milliseconds)
{
if(!Element.classList.contains("responsible"))
{
Element.classList.add("responsible");
}
setTimeout(function() { Element.classList.remove("responsible"); }, Milliseconds);
}
function SelectText(inputElement)
{
inputElement.select();
}
function CopyToClipboard(inputElement)
{
SelectText(inputElement);
document.execCommand("copy");
AscribeTemporaryResponsibility(linkMenu.parentNode, 8000);
}
function handleKey(key) {
var gotKey = true;
switch (key) {
case "q": {
if(quotesMenu)
{
toggleMenuVisibility(quotesMenu)
}
} break;
case "r": {
if(referencesMenu)
{
toggleMenuVisibility(referencesMenu)
}
} break;
case "f": {
if(filterMenu)
{
toggleMenuVisibility(filterMenu)
}
} break;
case "y": {
if(linkMenu)
{
toggleMenuVisibility(linkMenu)
}
break;
}
case "c": {
if(creditsMenu)
{
toggleMenuVisibility(creditsMenu)
}
} break;
case "t": {
if(cinera)
{
toggleTheatreMode();
}
} break;
case "T": {
if(cinera)
{
toggleSuperTheatreMode();
}
} break;
case "Enter": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("quotes_container"))
{
var time = focusedElement.querySelector(".timecode").getAttribute("data-timestamp");
player.setTimeThenPlay(parseInt(time));
}
else if(focusedElement.parentNode.classList.contains("references_container"))
{
var time = focusedIdentifier.getAttribute("data-timestamp");
player.setTimeThenPlay(parseInt(time));
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.hasAttribute)
{
var url = focusedElement.getAttribute("href");
window.open(url, "_blank");
}
}
}
else
{
console.log("TODO(matt): Implement me, perhaps?\n");
}
} break;
case "o": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("references_container") ||
focusedElement.parentNode.classList.contains("quotes_container"))
{
if(player) { player.pause(); }
var url = focusedElement.getAttribute("href");
window.open(url, "_blank");
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.hasAttribute("href"))
{
if(player) { player.pause(); }
var url = focusedElement.getAttribute("href");
window.open(url, "_blank");
}
}
}
} break;
case "w": case "k": case "ArrowUp": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("quotes_container"))
{
if(focusedElement.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.previousElementSibling;
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("references_container"))
{
if(focusedElement.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.previousElementSibling;
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
{
if(focusedElement.previousElementSibling &&
focusedElement.previousElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.previousElementSibling;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.parentNode.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.previousElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support"))
{
setSpriteLightness(focusedElement.firstChild);
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
else
{
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
}
} break;
case "s": case "j": case "ArrowDown": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("quotes_container"))
{
if(focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.nextElementSibling;
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("references_container"))
{
if(focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.nextElementSibling;
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
{
if(focusedElement.nextElementSibling &&
focusedElement.nextElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.nextElementSibling;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.parentNode.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.nextElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support"))
{
setSpriteLightness(focusedElement.firstChild);
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
else
{
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
}
} break;
case "a": case "h": case "ArrowLeft": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("references_container"))
{
if(focusedIdentifier.previousElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.previousElementSibling;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
else if(focusedIdentifier.parentNode.previousElementSibling.classList.contains("ref_indices"))
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.previousElementSibling.lastElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.classList.contains("filter_content"))
{
if(focusedElement.parentNode.classList.contains("filter_media") &&
focusedElement.parentNode.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedMedium = focusedElement;
if(!lastFocusedTopic)
{
lastFocusedTopic = focusedElement.parentNode.previousElementSibling.children[1];
}
lastFocusedCategory = lastFocusedTopic;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.classList.contains("support"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.previousElementSibling;
setSpriteLightness(focusedElement.firstChild);
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
} break;
case "d": case "l": case "ArrowRight": {
if(focusedElement)
{
if(focusedElement.parentNode.classList.contains("references_container"))
{
if(focusedIdentifier.nextElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.nextElementSibling;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
else if(focusedIdentifier.parentNode.nextElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.nextElementSibling.firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.classList.contains("filter_content"))
{
if(focusedElement.parentNode.classList.contains("filter_topics") &&
focusedElement.parentNode.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedTopic = focusedElement;
if(!lastFocusedMedium)
{
lastFocusedMedium = focusedElement.parentNode.nextElementSibling.children[1];
}
lastFocusedCategory = lastFocusedMedium;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
{
if(focusedElement.classList.contains("person") &&
focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.nextElementSibling;
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
} break;
case "x": case " ": {
if(focusedElement && focusedElement.classList.contains("filter_content"))
{
filterItemToggle(focusedElement);
if(focusedElement.nextElementSibling &&
focusedElement.nextElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics"))
{
lastFocusedTopic = focusedElement.nextElementSibling;
lastFocusedCategory = lastFocusedTopic;
}
else
{
lastFocusedMedium = focusedElement.nextElementSibling;
lastFocusedCategory = lastFocusedMedium;
}
lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
} break;
case "X": case "capitalSpace": {
if(focusedElement && focusedElement.classList.contains("filter_content"))
{
filterItemToggle(focusedElement);
if(focusedElement.previousElementSibling &&
focusedElement.previousElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics"))
{
lastFocusedTopic = focusedElement.previousElementSibling;
lastFocusedCategory = lastFocusedTopic;
}
else
{
lastFocusedMedium = focusedElement.previousElementSibling;
lastFocusedCategory = lastFocusedMedium;
}
lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
} break;
case "z": {
toggleFilterOrLinkMode();
} break;
case "v": {
if(focusedElement && focusedElement.classList.contains("filter_content"))
{
invertFilter(focusedElement)
}
} break;
case "V": {
resetFilter();
} break;
case "?": {
if(helpDocumentation)
{
helpDocumentation.classList.toggle("visible");
}
} break;
case 'N':
case 'J':
case 'S': {
player.jumpToNextMarker();
} break;
case 'P':
case 'K':
case 'W': {
player.jumpToPrevMarker();
} break;
case '[':
case '<': {
if(prevEpisode)
{
location = prevEpisode.href;
}
} break;
case ']':
case '>': {
if(nextEpisode)
{
location = nextEpisode.href;
}
} break;
case 'Y': {
if(cineraLink)
{
if(linkTimestamp == false && player.playing)
{
player.pause();
}
if(linkMenu && !linkMenu.classList.contains("visible"))
{
toggleMenuVisibility(linkMenu);
}
SelectText(cineraLink);
}
}
default: {
gotKey = false;
} break;
}
return gotKey;
}
function applyFilter() {
if(filterMode == "exclusive")
{
for(var i = 0; i < testMarkers.length; ++i)
{
var testCategories = testMarkers[i].classList;
for(var j = 0; j < testCategories.length; ++j)
{
if((testCategories[j].startsWith("off_")) && !testMarkers[i].classList.contains("skip"))
{
testMarkers[i].classList.add("skip");
}
}
}
}
else
{
for(var i = 0; i < testMarkers.length; ++i)
{
var testCategories = testMarkers[i].classList;
for(var j = 0; j < testCategories.length; ++j)
{
if((testCategories[j] in filterState || testCategories[j].startsWith("cat_")) && testMarkers[i].classList.contains("skip"))
{
testMarkers[i].classList.remove("skip");
}
}
}
}
}
function filterItemToggle(filterItem) {
var selectedCategory = filterItem.classList[1];
filterState[selectedCategory].off = !filterState[selectedCategory].off;
if(filterState[selectedCategory].off)
{
filterItem.classList.add("off");
disableSprite(filterItem);
if(!filterItem.parentNode.classList.contains("filter_media"))
{
filterItem.querySelector(".icon").style.backgroundColor = "transparent";
}
var testMarkers = playerContainer.querySelectorAll(".marker." + selectedCategory + ", .marker.cat_" + selectedCategory);
for(var j = 0; j < testMarkers.length; ++j)
{
if(filterState[selectedCategory].type == "topic")
{
testMarkers[j].classList.remove("cat_" + selectedCategory);
testMarkers[j].classList.add("off_" + selectedCategory);
var markerCategories = testMarkers[j].querySelectorAll(".category." + selectedCategory);
for(var k = 0; k < markerCategories.length; ++k)
{
if(markerCategories[k].classList.contains(selectedCategory))
{
markerCategories[k].classList.add("off");
markerCategories[k].style.backgroundColor = "transparent";
}
}
}
else
{
var markerCategories = testMarkers[j].querySelectorAll(".categoryMedium." + selectedCategory);
for(var k = 0; k < markerCategories.length; ++k)
{
if(markerCategories[k].classList.contains(selectedCategory))
{
markerCategories[k].classList.add("off");
disableSprite(markerCategories[k]);
}
}
testMarkers[j].classList.remove(selectedCategory);
testMarkers[j].classList.add("off_" + selectedCategory);
}
Skipping = 1;
if(filterMode == "exclusive")
{
testMarkers[j].classList.add("skip");
}
else
{
var markerClasses = testMarkers[j].classList;
for(var k = 0; k < markerClasses.length; ++k)
{
if(markerClasses[k] in filterState || markerClasses[k].replace(/^cat_/, "") in filterState)
{
Skipping = 0;
}
}
if(Skipping)
{
testMarkers[j].classList.add("skip");
}
}
}
}
else
{
filterItem.classList.remove("off");
enableSprite(filterItem);
if(!filterItem.parentNode.classList.contains("filter_media"))
{
filterItem.querySelector(".icon").style.backgroundColor = getComputedStyle(filterItem.querySelector(".icon")).getPropertyValue("border-color");
}
setDotLightness(filterItem.querySelector(".icon"));
var testMarkers = cinera.querySelectorAll(".marker.off_" + selectedCategory);
for(var j = 0; j < testMarkers.length; ++j)
{
if(filterState[selectedCategory].type == "topic")
{
testMarkers[j].classList.remove("off_" + selectedCategory);
testMarkers[j].classList.add("cat_" + selectedCategory);
var markerCategories = testMarkers[j].querySelectorAll(".category." + selectedCategory);
for(var k = 0; k < markerCategories.length; ++k)
{
if(markerCategories[k].classList.contains(selectedCategory))
{
markerCategories[k].classList.remove("off");
markerCategories[k].style.backgroundColor = getComputedStyle(markerCategories[k]).getPropertyValue("border-color");
setDotLightness(markerCategories[k]);
}
}
}
else
{
testMarkers[j].classList.remove("off_" + selectedCategory);
testMarkers[j].classList.add(selectedCategory);
var markerCategories = testMarkers[j].querySelectorAll(".categoryMedium." + selectedCategory);
for(var k = 0; k < markerCategories.length; ++k)
{
if(markerCategories[k].classList.contains(selectedCategory))
{
markerCategories[k].classList.remove("off");
enableSprite(markerCategories[k]);
}
}
}
Skipping = 0;
if(filterMode == "inclusive")
{
testMarkers[j].classList.remove("skip");
}
else
{
var markerClasses = testMarkers[j].classList;
for(var k = 0; k < markerClasses.length; ++k)
{
if(markerClasses[k].startsWith("off_"))
{
Skipping = 1;
}
}
if(!Skipping)
{
testMarkers[j].classList.remove("skip");
}
}
}
}
}
function resetFilter() {
for(i in filterItems)
{
if(filterItems[i].classList)
{
var selectedCategory = filterItems[i].classList[1];
if(filterInitState[selectedCategory].off ^ filterState[selectedCategory].off)
{
filterItemToggle(filterItems[i]);
}
}
}
if(filterMode == "inclusive")
{
toggleFilterMode();
}
}
function invertFilter(focusedElement) {
var siblings = focusedElement.parentNode.querySelectorAll(".filter_content");
for(i in siblings)
{
if(siblings[i].classList)
{
filterItemToggle(siblings[i]);
}
}
}
function resetFade() {
filter.classList.remove("responsible");
filter.querySelector(".filter_mode").classList.remove("responsible");
var responsibleCategories = filter.querySelectorAll(".filter_content.responsible");
for(var i = 0; i < responsibleCategories.length; ++i)
{
responsibleCategories[i].classList.remove("responsible");
}
}
function onRefChanged(ref, element, player) {
if(element.classList.contains("skip"))
{
var ErrorCount = 0;
if(!filter) { console.log("Missing filter_container div"); ErrorCount++; }
if(!filterState) { console.log("Missing filterState object"); ErrorCount++; }
if(ErrorCount > 0)
{
switch(ErrorCount)
{
case 1:
{ console.log("This should have been generated by Cinera along with the following element containing the \"skip\" class:"); } break;
default:
{ console.log("These should have been generated by Cinera along with the following element containing the \"skip\" class:"); } break;
}
console.log(element); return;
}
if(!filter.classList.contains("responsible"))
{
filter.classList.add("responsible");
}
for(var selector = 0; selector < element.classList.length; ++selector)
{
if(element.classList[selector].startsWith("off_"))
{
if(!filter.querySelector(".filter_content." + element.classList[selector].replace(/^off_/, "")).classList.contains("responsible"))
{
filter.querySelector(".filter_content." + element.classList[selector].replace(/^off_/, "")).classList.add("responsible");
}
}
if(element.classList[selector].startsWith("cat_") || element.classList[selector] in filterState)
{
if(!filter.querySelector(".filter_mode").classList.add("responsible"))
{
filter.querySelector(".filter_mode").classList.add("responsible");
}
}
setTimeout(resetFade, 8000);
}
if(player && player.playing)
{
player.jumpToNextMarker();
}
}
else
{
for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)
{
var SetMenu = 0;
if (ref !== undefined && ref !== null) {
var refElements = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
var refs = ref.split(",");
for (var i = 0; i < refElements.length; ++i) {
if (refs.includes(refElements[i].getAttribute("data-id"))) {
refElements[i].classList.add("current");
SetMenu = 1;
} else {
refElements[i].classList.remove("current");
}
}
if(SetMenu) {
sourceMenus[MenuIndex].classList.add("current");
} else {
sourceMenus[MenuIndex].classList.remove("current");
}
} else {
sourceMenus[MenuIndex].classList.remove("current");
var refs = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
for (var i = 0; i < refs.length; ++i) {
refs[i].classList.remove("current");
}
}
}
}
}
function navigateFilter(filterItem) {
if(filterItem != lastFocusedCategory)
{
lastFocusedCategory.classList.remove("focused");
unfocusSprite(lastFocusedCategory);
if(filterItem.parentNode.classList.contains("filter_topics"))
{
lastFocusedTopic = filterItem;
lastFocusedCategory = lastFocusedTopic;
}
else
{
lastFocusedMedium = filterItem;
lastFocusedCategory = lastFocusedMedium;
}
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
function mouseOverQuotes(quote) {
if(focusedElement && quote != lastFocusedQuote)
{
focusedElement.classList.remove("focused");
lastFocusedQuote = quote;
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
}
}
function mouseOverReferences(reference) {
if(focusedElement && reference != lastFocusedReference)
{
focusedElement.classList.remove("focused")
lastFocusedReference = reference;
}
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
var ourIdentifiers = reference.querySelectorAll(".timecode");
weWereLastFocused = false;
for(var k = 0; k < ourIdentifiers.length; ++k)
{
if(ourIdentifiers[k] == lastFocusedIdentifier)
{
weWereLastFocused = true;
}
}
if(!weWereLastFocused)
{
lastFocusedIdentifier.classList.remove("focused");
lastFocusedIdentifier = ourIdentifiers[0];
}
focusedIdentifer = lastFocusedIdentifier;
focusedIdentifer.classList.add("focused");
for(var l = 0; l < ourIdentifiers.length; ++l)
{
ourIdentifiers[l].addEventListener("mouseenter", function(ev) {
if(this != lastFocusedIdentifier)
{
lastFocusedIdentifier.classList.remove("focused");
lastFocusedIdentifier = this;
lastFocusedIdentifier.classList.add("focused");
}
})
}
}
function mouseSkipToTimecode(player, time, ev)
{
player.setTimeThenPlay(parseInt(time, 10));
ev.preventDefault();
ev.stopPropagation();
return false;
}
function handleMenuTogglerInteraction(menu, eventType)
{
if(!(menu.classList.contains("visible")) && eventType == "mouseenter" ||
menu.classList.contains("visible") && eventType == "mouseleave" ||
(eventType == "click" && !menu.classList.contains("cineraHelp")))
{
if(menu.classList.contains("quotes"))
{
toggleMenuVisibility(quotesMenu);
}
else if(menu.classList.contains("references"))
{
toggleMenuVisibility(referencesMenu);
}
else if(menu.classList.contains("filter"))
{
toggleMenuVisibility(filterMenu);
}
else if(menu.classList.contains("link"))
{
toggleMenuVisibility(linkMenu);
}
else if(menu.classList.contains("credits"))
{
toggleMenuVisibility(creditsMenu);
}
}
}