2017-03-10 14:19:25 +00:00
// 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`.
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-videoId" ) ) {
console . error ( "Expected to find data-videoId attribute on" , this . videoContainer , "for player initialized on" , this . container ) ;
throw new Error ( "Missing 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 . currentMarker = null ;
this . currentMarkerIdx = null ;
this . youtubePlayer = null ;
this . youtubePlayerReady = false ;
this . playing = false ;
this . shouldPlay = false ;
2017-03-11 02:48:11 +00:00
this . buffering = false ;
this . pauseAfterBuffer = false ;
2017-03-10 14:19:25 +00:00
this . speed = 1 ;
2018-02-28 20:18:11 +00:00
this . currentTime = - 1 ;
2017-03-10 14:19:25 +00:00
this . scrollTo = - 1 ;
this . scrollPosition = 0 ;
this . nextFrame = null ;
this . looping = false ;
2017-06-03 01:32:18 +00:00
2017-03-10 14:19:25 +00:00
this . markersContainer . addEventListener ( "wheel" , function ( ev ) {
this . scrollTo = - 1 ;
} . bind ( this ) ) ;
Player . initializeYoutube ( this . onYoutubeReady . bind ( this ) ) ;
2021-01-25 18:09:30 +00:00
var PendingMobileStyleInitialisation = true ;
this . updateSize ( PendingMobileStyleInitialisation ) ;
2018-02-28 20:18:11 +00:00
this . resume ( ) ;
2017-03-10 14:19:25 +00:00
}
// 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 . youtubePlayerReady ) {
if ( ! this . playing ) {
this . youtubePlayer . playVideo ( ) ;
}
2017-03-15 02:10:11 +00:00
this . pauseAfterBuffer = false ;
2017-03-10 14:19:25 +00:00
} 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 . youtubePlayerReady ) {
if ( this . playing ) {
this . youtubePlayer . pauseVideo ( ) ;
2017-03-11 02:48:11 +00:00
} else if ( this . buffering ) {
this . pauseAfterBuffer = true ;
2017-03-10 14:19:25 +00:00
}
} else {
this . shouldPlay = false ;
}
} ;
// Sets the current time. Does not affect play status.
// If the player hasn't loaded yet, it will seek to this time when ready.
Player . prototype . setTime = function ( time ) {
this . currentTime = time ;
if ( this . youtubePlayerReady ) {
this . currentTime = Math . max ( 0 , Math . min ( this . currentTime , this . youtubePlayer . getDuration ( ) ) ) ;
this . youtubePlayer . seekTo ( this . currentTime ) ;
}
this . updateProgress ( ) ;
} ;
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 ;
2021-01-25 18:09:30 +00:00
while ( targetMarkerIdx < this . markers . length && this . markers [ targetMarkerIdx ] . el . classList . contains ( "skip" ) )
{
++ targetMarkerIdx ;
targetTime = this . markers [ targetMarkerIdx ] . timestamp ;
}
2017-03-10 14:19:25 +00:00
this . setTime ( targetTime ) ;
this . play ( ) ;
} ;
Player . prototype . jumpToPrevMarker = function ( ) {
var targetMarkerIdx = Math . max ( 0 , ( this . currentMarkerIdx === null ? 0 : this . currentMarkerIdx - 1 ) ) ;
var targetTime = this . markers [ targetMarkerIdx ] . timestamp ;
2021-01-25 18:09:30 +00:00
while ( targetMarkerIdx >= 0 && this . markers [ targetMarkerIdx ] . el . classList . contains ( "skip" ) )
{
-- targetMarkerIdx ;
targetTime = this . markers [ targetMarkerIdx ] . timestamp ;
}
2017-03-10 14:19:25 +00:00
this . setTime ( targetTime ) ;
this . play ( ) ;
} ;
2021-01-25 18:09:30 +00:00
function
GetHeightOfHideableElement ( Element , UnhidingClass )
2019-03-07 20:16:27 +00:00
{
2021-01-25 18:09:30 +00:00
var Result = 0 ;
if ( Element . classList . contains ( UnhidingClass ) )
2019-03-07 20:16:27 +00:00
{
2021-01-25 18:09:30 +00:00
Result = Element . offsetHeight ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
else
2019-03-07 20:16:27 +00:00
{
2021-01-25 18:09:30 +00:00
var ZOffset = Element . style . zOffset ;
Element . style . zOffset = - 1 ;
Element . classList . add ( UnhidingClass ) ;
Result = Element . offsetHeight ;
Element . classList . remove ( UnhidingClass ) ;
Element . style . zOffset = ZOffset ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
return Result ;
}
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
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 ) ;
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
Result = Element . offsetWidth ;
Element . classList . remove ( UnhidingClass ) ;
Element . style . zOffset = ZOffset ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
return Result ;
}
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
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 ;
}
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
if ( Result < 0 )
{
Result = 0 ;
}
return Result ;
}
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
function sizeAndPositionMenuContainer ( TitleBar , SizerElement , Menu )
{
if ( Menu )
{
var Toggler = Menu . parentElement ;
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
var TitleBarDimX = TitleBar . offsetWidth ;
var TitleBarDimY = TitleBar . offsetHeight ;
var SizerElementDimX = SizerElement . offsetWidth ;
var SizerElementDimY = SizerElement . offsetHeight ;
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
var ContainerDimX = SizerElementDimX ;
var ContainerDimY = SizerElementDimY ;
2019-03-07 20:16:27 +00:00
2021-02-04 00:13:55 +00:00
switch ( CineraProps . O )
2021-01-25 18:09:30 +00:00
{
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" ;
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
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 )
{
2021-02-04 00:13:55 +00:00
var Result ;
2021-01-25 18:09:30 +00:00
Selector . style . height = "unset" ;
2021-02-04 00:13:55 +00:00
Result = ComputeTallest ( Elements , UnhidingClass ) ;
Selector . style . height = Result + "px" ;
return Result ;
2021-01-25 18:09:30 +00:00
}
2021-02-12 23:48:02 +00:00
function ApplyMobileStyle ( VideoContainer )
2021-01-25 18:09:30 +00:00
{
2021-02-04 00:13:55 +00:00
var WindowDim = DeriveReliableWindowDimensions ( ) ;
2021-02-12 23:48:02 +00:00
var MaxWidth = MaxWidthOfElement ( cinera , WindowDim ) ;
2021-02-04 00:13:55 +00:00
var MaxHeight = MaxHeightOfElement ( cinera , WindowDim ) ;
2021-01-25 18:09:30 +00:00
var IndicesBar = playerContainer . querySelector ( ".markers_container" ) ;
2021-02-12 23:48:02 +00:00
2021-01-25 18:09:30 +00:00
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 ;
}
2021-02-04 00:13:55 +00:00
switch ( CineraProps . O )
2021-01-25 18:09:30 +00:00
{
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 ;
}
2021-02-04 00:13:55 +00:00
var HeightOfTallestIndex ;
2021-01-25 18:09:30 +00:00
if ( MobileCineraContentRule !== undefined )
{
MobileCineraContentRule . style . width = CineraContentWidth + "px" ;
var MarkerList = Markers . querySelectorAll ( ".marker" ) ;
2021-02-04 00:13:55 +00:00
HeightOfTallestIndex = ComputeAndSetTallest ( MobileCineraContentRule , MarkerList , "current" ) ;
IndicesBar . style . height = HeightOfTallestIndex + "px" ;
Markers . style . width = CineraContentWidth + "px" ;
2021-01-25 18:09:30 +00:00
}
2021-02-12 23:48:02 +00:00
var VideoMaxDimX = MaxWidth ;
var VideoMaxDimY = MaxHeight ;
var MinimumVideoHeight = 32 ;
if ( MaxHeight - HeightOfTallestIndex > MinimumVideoHeight )
{
VideoMaxDimY -= HeightOfTallestIndex ;
}
2021-01-25 18:09:30 +00:00
2021-02-04 00:13:55 +00:00
switch ( CineraProps . O )
2021-01-25 18:09:30 +00:00
{
case orientations . PORTRAIT :
{
2021-02-04 00:13:55 +00:00
VideoMaxDimY -= titleBar . offsetHeight ;
2021-01-25 18:09:30 +00:00
} break ;
case orientations . LANDSCAPE _LEFT :
case orientations . LANDSCAPE _RIGHT :
{
2021-02-04 00:13:55 +00:00
VideoMaxDimX -= titleBar . offsetWidth ;
2021-01-25 18:09:30 +00:00
} 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 )
2019-03-07 20:16:27 +00:00
{
2021-01-25 18:09:30 +00:00
VideoDimY = Math . floor ( VideoMaxDimY ) ;
VideoDimX = Math . floor ( VideoDimXFromMaxY ) ;
2019-03-07 20:16:27 +00:00
}
else
{
2021-01-25 18:09:30 +00:00
VideoDimX = Math . floor ( VideoMaxDimX ) ;
VideoDimY = Math . floor ( VideoDimYFromMaxX ) ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
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 ) ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
function
IconifyMenuTogglers ( )
2019-03-07 20:16:27 +00:00
{
if ( quotesMenu )
{
2021-01-25 18:09:30 +00:00
quotesMenu . previousElementSibling . textContent = '\u{1F5E9}' ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
2019-03-07 20:16:27 +00:00
if ( referencesMenu )
{
2021-01-25 18:09:30 +00:00
referencesMenu . previousElementSibling . textContent = '\u{1F4D6}' ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
2019-03-07 20:16:27 +00:00
if ( creditsMenu )
{
2021-01-25 18:09:30 +00:00
creditsMenu . previousElementSibling . textContent = '\u{1F46A}' ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
if ( viewsMenu )
{
viewsMenu . remove ( ) ;
viewsMenu = null ;
}
}
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
function
InitMobileControls ( )
{
var rightmost = { } ;
var markersContainer = cinera . querySelector ( ".markers_container" ) ;
markersContainer . style . height = "auto" ;
var episodeMarkerFirst = markersContainer . querySelector ( ".episodeMarker.first" ) ;
2019-03-07 20:16:27 +00:00
var episodeMarkerPrev = markersContainer . querySelector ( ".episodeMarker.prev" ) ;
var episodeMarkerNext = markersContainer . querySelector ( ".episodeMarker.next" ) ;
2021-01-25 18:09:30 +00:00
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 ; }
2019-03-07 20:16:27 +00:00
2021-01-25 18:09:30 +00:00
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 ( ) ;
2021-02-12 23:48:02 +00:00
var VideoContainer = cinera . querySelector ( ".video_container" ) ;
ApplyMobileStyle ( VideoContainer ) ;
2021-01-25 18:09:30 +00:00
}
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 ( ) ;
} ) ;
2019-03-07 20:16:27 +00:00
}
2017-03-10 14:19:25 +00:00
// Call this after changing the size of the video container in order to update the youtube player.
Player . prototype . updateSize = function ( ) {
2021-01-25 18:09:30 +00:00
var width = 0 ;
var height = 0 ;
2021-02-04 00:13:55 +00:00
CineraProps . O = GetRealOrientation ( orientations . LANDSCAPE _LEFT , CineraProps . IsMobile ) ;
2021-02-10 22:41:46 +00:00
if ( ! CineraProps . IsMobile )
2019-03-07 20:16:27 +00:00
{
2021-02-10 22:41:46 +00:00
var VisibleArea = GetWindowDim ( false ) ;
var AvailableHeight = VisibleArea . Y - titleBar . offsetHeight ;
2021-01-25 18:09:30 +00:00
width = this . videoContainer . offsetWidth ;
height = width / 16 * 9 ; // TODO(matt): Get the aspect ratio from the video itself?
2021-02-10 22:41:46 +00:00
if ( height > AvailableHeight )
{
height = AvailableHeight ;
width = height / 9 * 16 ;
}
2021-01-25 18:09:30 +00:00
this . markersContainer . style . height = height + "px" ;
2021-01-27 21:58:56 +00:00
2021-02-10 22:41:46 +00:00
var VacantPixelsBelowMenus = 4 ;
var MenuMaxHeight = cinera . offsetHeight - titleBar . offsetHeight - VacantPixelsBelowMenus ;
2021-01-27 21:58:56 +00:00
MenuContainerRule . style . maxHeight = MenuMaxHeight + "px" ;
2019-03-07 20:16:27 +00:00
}
else
{
2021-02-12 23:48:02 +00:00
ApplyMobileStyle ( this . videoContainer ) ;
2021-01-25 18:09:30 +00:00
width = this . videoContainer . offsetWidth ;
height = this . videoContainer . offsetHeight ;
2019-03-07 20:16:27 +00:00
}
2017-03-10 14:19:25 +00:00
if ( this . youtubePlayerReady ) {
this . youtubePlayer . setSize ( Math . floor ( width ) , Math . floor ( height ) ) ;
}
}
// 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 . initializeYoutube = function ( callback ) {
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 ] ( ) ;
}
} ;
var scriptTag = document . createElement ( "SCRIPT" ) ;
scriptTag . setAttribute ( "type" , "text/javascript" ) ;
scriptTag . setAttribute ( "src" , "https://www.youtube.com/iframe_api" ) ;
document . body . appendChild ( scriptTag ) ;
} else if ( window . APYoutubeAPIReady === false ) {
window . APCallbacks . push ( callback ) ;
} else if ( window . APYoutubeAPIReady === true ) {
callback ( ) ;
}
}
// 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 . setTime ( time ) ;
this . play ( ) ;
} ;
Player . prototype . onMarkerMouseMove = function ( marker , ev ) {
if ( this . currentMarker == marker ) {
2021-01-25 18:09:30 +00:00
var CineraContent = this . currentMarker . el . querySelector ( ".cineraContent" ) ;
marker . hoverx = ( ev . pageX - getElementXOffsetFromPage ( CineraContent ) ) / CineraContent . offsetWidth ;
2017-03-10 14:19:25 +00:00
}
} ;
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 ) {
2021-01-25 18:09:30 +00:00
var CineraContent = this . currentMarker . el . querySelector ( ".cineraContent" ) ;
var totalWidth = CineraContent . offsetWidth ;
2017-03-10 14:19:25 +00:00
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 ) {
2018-04-17 23:05:14 +00:00
if ( this . currentMarkerIdx == this . markers . length - 1 )
{
localStorage . removeItem ( lastAnnotationStorageItem ) ;
}
else
{
localStorage . setItem ( lastAnnotationStorageItem , this . currentMarker . timestamp ) ;
}
2017-03-10 14:19:25 +00:00
this . currentMarker . el . classList . add ( "current" ) ;
this . scrollTo = this . currentMarker . el . offsetTop + this . currentMarker . el . offsetHeight / 2.0 ;
this . scrollPosition = this . markersContainer . scrollTop ;
2018-02-28 20:18:11 +00:00
this . refsCallback ( this . currentMarker . ref , this . currentMarker . el , this ) ;
2017-03-10 14:19:25 +00:00
} else if ( prevMarker && prevMarker . ref ) {
this . refsCallback ( null ) ;
}
}
} ;
Player . prototype . doFrame = function ( ) {
if ( this . playing ) {
2018-05-15 19:45:03 +00:00
this . currentTime = this . youtubePlayer . getCurrentTime ( ) ;
2017-03-10 14:19:25 +00:00
}
this . updateProgress ( ) ;
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 ) ) ;
2018-05-22 21:43:59 +00:00
updateLink ( ) ;
2017-03-10 14:19:25 +00:00
} ;
Player . prototype . onYoutubePlayerReady = function ( ) {
this . youtubePlayerReady = true ;
this . markers [ this . markers . length - 1 ] . endTime = this . youtubePlayer . getDuration ( ) ;
this . updateSize ( ) ;
this . youtubePlayer . setPlaybackQuality ( "hd1080" ) ;
if ( this . currentTime > 0 ) {
this . currentTime = Math . max ( 0 , Math . min ( this . currentTime , this . youtubePlayer . getDuration ( ) ) ) ;
this . youtubePlayer . seekTo ( this . currentTime , true ) ;
}
if ( this . shouldPlay ) {
this . youtubePlayer . playVideo ( ) ;
}
} ;
Player . prototype . onYoutubePlayerStateChange = function ( ev ) {
if ( ev . data == YT . PlayerState . PLAYING ) {
this . playing = true ;
this . currentTime = this . youtubePlayer . getCurrentTime ( ) ;
} else {
this . playing = false ;
2018-04-17 23:05:14 +00:00
if ( ev . data == YT . PlayerState . PAUSED || ev . data == YT . PlayerState . BUFFERING ) {
this . currentTime = this . youtubePlayer . getCurrentTime ( ) ;
this . updateProgress ( ) ;
} else if ( ev . data == YT . PlayerState . ENDED ) {
localStorage . removeItem ( lastAnnotationStorageItem ) ;
this . currentTime = null ;
this . updateProgress ( ) ;
}
2017-03-10 14:19:25 +00:00
}
2017-03-11 02:48:11 +00:00
this . buffering = ev . data == YT . PlayerState . BUFFERING ;
if ( this . playing && this . pauseAfterBuffer ) {
this . pauseAfterBuffering = false ;
this . pause ( ) ;
}
2017-03-10 14:19:25 +00:00
} ;
Player . prototype . onYoutubePlayerPlaybackRateChange = function ( ev ) {
this . speed = ev . data ;
} ;
Player . prototype . onYoutubeReady = function ( ) {
var youtubePlayerDiv = document . createElement ( "DIV" ) ;
youtubePlayerDiv . id = "youtube_player_" + Player . youtubePlayerCount ++ ;
this . videoContainer . appendChild ( youtubePlayerDiv ) ;
this . youtubePlayer = new YT . Player ( youtubePlayerDiv . id , {
videoId : this . videoContainer . getAttribute ( "data-videoId" ) ,
width : this . videoContainer . offsetWidth ,
height : this . videoContainer . offsetWidth / 16 * 9 ,
2021-01-25 18:09:30 +00:00
playerVars : { 'playsinline' : 1 } ,
2017-06-03 01:32:18 +00:00
//playerVars: { disablekb: 1 },
2017-03-10 14:19:25 +00:00
events : {
"onReady" : this . onYoutubePlayerReady . bind ( this ) ,
"onStateChange" : this . onYoutubePlayerStateChange . bind ( this ) ,
"onPlaybackRateChange" : this . onYoutubePlayerPlaybackRateChange . bind ( this )
}
} ) ;
} ;
Player . youtubePlayerCount = 0 ;
2017-05-31 00:21:21 +00:00
// NOTE(matt): Hereafter is my stuff. Beware!
2018-05-22 21:43:59 +00:00
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 ( linkAnnotation == true )
{
if ( player . currentMarker )
{
link . value = baseURL + "#" + player . currentMarker . timestamp ;
}
else
{
link . value = baseURL ;
}
}
else
{
link . value = baseURL + "#" + Math . round ( player . youtubePlayer . getCurrentTime ( ) ) ;
}
}
}
function toggleLinkMode ( linkMode , link )
{
linkAnnotation = ! linkAnnotation ;
if ( linkAnnotation == true )
{
2020-05-09 17:33:25 +00:00
linkMode . textContent = "Link to: current timestamp" ;
2018-05-22 21:43:59 +00:00
}
else
{
2020-05-09 17:33:25 +00:00
linkMode . textContent = "Link to: nearest second" ;
2018-05-22 21:43:59 +00:00
}
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 ) ;
}
}
}
2017-05-31 00:21:21 +00:00
function toggleMenuVisibility ( element ) {
if ( element . classList . contains ( "visible" ) )
{
element . classList . remove ( "visible" ) ;
element . parentNode . classList . remove ( "visible" ) ;
2018-05-22 21:43:59 +00:00
if ( focusedElement )
{
focusedElement . classList . remove ( "focused" ) ;
focusedElement = null ;
}
2017-05-31 00:21:21 +00:00
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 )
{
2018-04-04 22:39:38 +00:00
if ( element . querySelectorAll ( ".credit .person" ) [ 0 ] . nextElementSibling )
2017-05-31 00:21:21 +00:00
{
lastFocusedCreditItem = element . querySelectorAll ( ".credit .support" ) [ 0 ] ;
2017-11-11 00:34:47 +00:00
focusedElement = lastFocusedCreditItem ;
focusedElement . classList . add ( "focused" ) ;
2020-05-09 17:33:25 +00:00
setSpriteLightness ( focusedElement . firstChild ) ;
2017-05-31 00:21:21 +00:00
}
else
{
lastFocusedCreditItem = element . querySelectorAll ( ".credit .person" ) [ 0 ] ;
2017-11-11 00:34:47 +00:00
focusedElement = lastFocusedCreditItem ;
focusedElement . classList . add ( "focused" ) ;
2017-05-31 00:21:21 +00:00
}
}
2017-11-11 00:34:47 +00:00
else
{
focusedElement = lastFocusedCreditItem ;
focusedElement . classList . add ( "focused" ) ;
}
2017-05-31 00:21:21 +00:00
}
}
}
2018-01-17 20:15:00 +00:00
function handleMouseOverViewsMenu ( )
2018-01-15 21:52:24 +00:00
{
2021-02-04 00:13:55 +00:00
switch ( CineraProps . V )
2018-01-17 20:15:00 +00:00
{
case views . REGULAR :
case views . THEATRE :
{
viewsContainer . style . display = "block" ;
} break ;
case views . SUPERTHEATRE :
{
viewsContainer . style . display = "none" ;
} break ;
}
2018-01-15 21:52:24 +00:00
}
function enterFullScreen _ ( )
{
if ( ! document . mozFullScreen && ! document . webkitFullScreen )
{
2021-01-25 18:09:30 +00:00
if ( document . mozRequestFullScreen )
2018-01-15 21:52:24 +00:00
{
2021-01-25 18:09:30 +00:00
document . mozRequestFullScreen ( ) ;
2018-01-15 21:52:24 +00:00
}
else
{
2021-01-25 18:09:30 +00:00
document . webkitRequestFullScreen ( Element . ALLOW _KEYBOARD _INPUT ) ;
2018-01-15 21:52:24 +00:00
}
}
}
function leaveFullScreen _ ( )
{
if ( document . mozCancelFullScreen )
{
document . mozCancelFullScreen ( ) ;
}
else
{
document . webkitExitFullscreen ( ) ;
}
}
function toggleTheatreMode ( ) {
2021-02-04 00:13:55 +00:00
switch ( CineraProps . V )
2018-01-15 21:52:24 +00:00
{
case views . REGULAR :
{
2021-02-04 00:13:55 +00:00
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 ;
2018-01-17 20:15:00 +00:00
cinera . style . backgroundColor = "#000" ;
cinera . style . zIndex = 64 ;
cinera . style . left = 0 ;
cinera . style . top = 0 ;
cinera . style . width = "100%" ;
2018-02-23 23:36:42 +00:00
cinera . style . maxWidth = "100%" ;
2018-01-17 20:15:00 +00:00
cinera . style . height = "100%" ;
2018-02-23 23:36:42 +00:00
cinera . style . maxHeight = "100%" ;
2018-01-17 20:15:00 +00:00
cinera . style . position = "fixed" ;
2021-01-25 18:09:30 +00:00
cinera . style . display = "flex" ;
cinera . style . flexDirection = "column" ;
cinera . style . justifyContent = "center" ;
2018-01-17 20:15:00 +00:00
viewItems [ 0 ] . setAttribute ( "data-id" , "regular" ) ;
viewItems [ 0 ] . setAttribute ( "title" , "Regular mode" ) ;
viewItems [ 0 ] . firstChild . nodeValue = "📺" ;
2021-02-04 00:13:55 +00:00
} CineraProps . V = views . THEATRE ; localStorage . setItem ( cineraViewStorageItem , views . THEATRE ) ; break ;
2018-01-15 21:52:24 +00:00
case views . SUPERTHEATRE :
{
leaveFullScreen _ ( ) ;
}
case views . THEATRE :
{
2021-02-04 00:13:55 +00:00
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 ;
2021-01-25 18:09:30 +00:00
window . scroll (
{
2021-02-04 00:13:55 +00:00
top : CineraProps . ScrollY ,
left : CineraProps . ScrollX
2021-01-25 18:09:30 +00:00
}
) ;
2018-01-17 20:15:00 +00:00
viewItems [ 0 ] . setAttribute ( "data-id" , "theatre" ) ;
viewItems [ 0 ] . setAttribute ( "title" , "Theatre mode" ) ;
viewItems [ 0 ] . firstChild . nodeValue = "🎭" ;
2021-02-04 00:13:55 +00:00
} CineraProps . V = views . REGULAR ; localStorage . removeItem ( cineraViewStorageItem ) ; break ;
2018-01-15 21:52:24 +00:00
}
player . updateSize ( ) ;
}
function toggleSuperTheatreMode ( )
{
2021-02-04 00:13:55 +00:00
switch ( CineraProps . V )
2018-01-15 21:52:24 +00:00
{
case views . REGULAR :
{
toggleTheatreMode ( ) ;
}
case views . THEATRE :
{
enterFullScreen _ ( ) ;
2021-02-04 00:13:55 +00:00
} CineraProps . V = views . SUPERTHEATRE ; localStorage . setItem ( cineraViewStorageItem , views . SUPERTHEATRE ) ; break ;
2018-01-15 21:52:24 +00:00
case views . SUPERTHEATRE :
{
leaveFullScreen _ ( ) ;
toggleTheatreMode ( ) ;
2021-02-04 00:13:55 +00:00
} CineraProps . V = views . REGULAR ; localStorage . removeItem ( cineraViewStorageItem ) ; break ;
2018-01-15 21:52:24 +00:00
}
player . updateSize ( ) ;
}
2018-05-22 21:43:59 +00:00
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 ) ;
}
2017-05-31 00:21:21 +00:00
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 ;
2018-05-22 21:43:59 +00:00
case "y" : {
if ( linkMenu )
{
toggleMenuVisibility ( linkMenu )
}
break ;
}
2017-05-31 00:21:21 +00:00
case "c" : {
if ( creditsMenu )
{
toggleMenuVisibility ( creditsMenu )
}
} break ;
2018-01-15 21:52:24 +00:00
case "t" : {
if ( cinera )
{
toggleTheatreMode ( ) ;
}
} break ;
case "T" : {
if ( cinera )
{
toggleSuperTheatreMode ( ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
case "Enter" : {
if ( focusedElement )
{
if ( focusedElement . parentNode . classList . contains ( "quotes_container" ) )
{
var time = focusedElement . querySelector ( ".timecode" ) . getAttribute ( "data-timestamp" ) ;
player . setTime ( parseInt ( time , 10 ) ) ;
player . play ( ) ;
}
else if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
var time = focusedIdentifier . getAttribute ( "data-timestamp" ) ;
player . setTime ( parseInt ( time , 10 ) ) ;
player . play ( ) ;
}
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 )
{
2017-08-10 01:05:41 +00:00
if ( focusedElement . parentNode . classList . contains ( "references_container" ) ||
focusedElement . parentNode . classList . contains ( "quotes_container" ) )
2017-05-31 00:21:21 +00:00
{
var url = focusedElement . getAttribute ( "href" ) ;
window . open ( url , "_blank" ) ;
}
else if ( focusedElement . parentNode . classList . contains ( "credit" ) )
{
if ( focusedElement . hasAttribute ( "href" ) )
{
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" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedQuote = focusedElement . previousElementSibling ;
focusedElement = lastFocusedQuote ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
if ( focusedElement . previousElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
lastFocusedReference = focusedElement . previousElementSibling ;
focusedElement = lastFocusedReference ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedIdentifier = focusedElement . querySelector ( ".ref_indices" ) . firstElementChild ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . parentNode . classList . contains ( "filters" ) )
{
if ( focusedElement . previousElementSibling &&
focusedElement . previousElementSibling . classList . contains ( "filter_content" ) )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCategory = focusedElement . previousElementSibling ;
focusedElement = lastFocusedCategory ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "credit" ) )
{
if ( focusedElement . parentNode . previousElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
if ( focusedElement . parentNode . previousElementSibling . querySelector ( ".support" ) &&
focusedElement . classList . contains ( "support" ) )
{
2020-05-09 17:33:25 +00:00
setSpriteLightness ( focusedElement . firstChild ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCreditItem = focusedElement . parentNode . previousElementSibling . querySelector ( ".support" ) ;
focusedElement = lastFocusedCreditItem ;
2017-11-11 00:34:47 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
else
{
lastFocusedCreditItem = focusedElement . parentNode . previousElementSibling . querySelector ( ".person" ) ;
focusedElement = lastFocusedCreditItem ;
2017-11-11 00:34:47 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
} break ;
case "s" : case "j" : case "ArrowDown" : {
if ( focusedElement )
{
if ( focusedElement . parentNode . classList . contains ( "quotes_container" ) )
{
if ( focusedElement . nextElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedQuote = focusedElement . nextElementSibling ;
focusedElement = lastFocusedQuote ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
if ( focusedElement . nextElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
lastFocusedReference = focusedElement . nextElementSibling ;
focusedElement = lastFocusedReference ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedIdentifier = focusedElement . querySelector ( ".ref_indices" ) . firstElementChild ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . parentNode . classList . contains ( "filters" ) )
{
if ( focusedElement . nextElementSibling &&
focusedElement . nextElementSibling . classList . contains ( "filter_content" ) )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCategory = focusedElement . nextElementSibling ;
focusedElement = lastFocusedCategory ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "credit" ) )
{
if ( focusedElement . parentNode . nextElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
if ( focusedElement . parentNode . nextElementSibling . querySelector ( ".support" ) &&
focusedElement . classList . contains ( "support" ) )
{
2020-05-09 17:33:25 +00:00
setSpriteLightness ( focusedElement . firstChild ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCreditItem = focusedElement . parentNode . nextElementSibling . querySelector ( ".support" ) ;
focusedElement = lastFocusedCreditItem ;
2017-11-11 00:34:47 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
else
{
lastFocusedCreditItem = focusedElement . parentNode . nextElementSibling . querySelector ( ".person" ) ;
focusedElement = lastFocusedCreditItem ;
2017-11-11 00:34:47 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
} break ;
case "a" : case "h" : case "ArrowLeft" : {
if ( focusedElement )
{
if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
if ( focusedIdentifier . previousElementSibling )
{
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
lastFocusedIdentifier = focusedIdentifier . previousElementSibling ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
}
2017-11-11 00:34:47 +00:00
else if ( focusedIdentifier . parentNode . previousElementSibling . classList . contains ( "ref_indices" ) )
{
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-11-11 00:34:47 +00:00
lastFocusedIdentifier = focusedIdentifier . parentNode . previousElementSibling . lastElementChild ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-11-11 00:34:47 +00:00
}
2017-05-31 00:21:21 +00:00
}
else if ( focusedElement . classList . contains ( "filter_content" ) )
{
if ( focusedElement . parentNode . classList . contains ( "filter_media" ) &&
focusedElement . parentNode . previousElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedMedium = focusedElement ;
if ( ! lastFocusedTopic )
{
lastFocusedTopic = focusedElement . parentNode . previousElementSibling . children [ 1 ] ;
}
lastFocusedCategory = lastFocusedTopic ;
focusedElement = lastFocusedCategory ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "credit" ) )
{
if ( focusedElement . classList . contains ( "support" ) )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCreditItem = focusedElement . previousElementSibling ;
2021-01-25 18:09:30 +00:00
setSpriteLightness ( focusedElement . firstChild ) ;
2017-05-31 00:21:21 +00:00
focusedElement = lastFocusedCreditItem ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
}
} break ;
case "d" : case "l" : case "ArrowRight" : {
if ( focusedElement )
{
if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
if ( focusedIdentifier . nextElementSibling )
{
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
lastFocusedIdentifier = focusedIdentifier . nextElementSibling ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-05-31 00:21:21 +00:00
}
2017-11-11 00:34:47 +00:00
else if ( focusedIdentifier . parentNode . nextElementSibling )
{
focusedIdentifier . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedIdentifier ) ;
2017-11-11 00:34:47 +00:00
lastFocusedIdentifier = focusedIdentifier . parentNode . nextElementSibling . firstElementChild ;
focusedIdentifier = lastFocusedIdentifier ;
focusedIdentifier . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedIdentifier ) ;
2017-11-11 00:34:47 +00:00
}
2017-05-31 00:21:21 +00:00
}
else if ( focusedElement . classList . contains ( "filter_content" ) )
{
if ( focusedElement . parentNode . classList . contains ( "filter_topics" ) &&
focusedElement . parentNode . nextElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedTopic = focusedElement ;
if ( ! lastFocusedMedium )
{
lastFocusedMedium = focusedElement . parentNode . nextElementSibling . children [ 1 ] ;
}
lastFocusedCategory = lastFocusedMedium ;
focusedElement = lastFocusedCategory ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
else if ( focusedElement . parentNode . classList . contains ( "credit" ) )
{
if ( focusedElement . classList . contains ( "person" ) &&
focusedElement . nextElementSibling )
{
focusedElement . classList . remove ( "focused" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
lastFocusedCreditItem = focusedElement . nextElementSibling ;
focusedElement = lastFocusedCreditItem ;
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
}
} 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" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-06-11 22:49:04 +00:00
if ( focusedElement . parentNode . classList . contains ( "filter_topics" ) )
{
lastFocusedTopic = focusedElement . nextElementSibling ;
lastFocusedCategory = lastFocusedTopic ;
}
else
{
lastFocusedMedium = focusedElement . nextElementSibling ;
lastFocusedCategory = lastFocusedMedium ;
}
lastFocusedElement = lastFocusedCategory ;
focusedElement = lastFocusedElement ;
2017-05-31 00:21:21 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
} 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" ) ;
2021-01-25 18:09:30 +00:00
unfocusSprite ( focusedElement ) ;
2017-06-11 22:49:04 +00:00
if ( focusedElement . parentNode . classList . contains ( "filter_topics" ) )
{
lastFocusedTopic = focusedElement . previousElementSibling ;
lastFocusedCategory = lastFocusedTopic ;
}
else
{
lastFocusedMedium = focusedElement . previousElementSibling ;
lastFocusedCategory = lastFocusedMedium ;
}
lastFocusedElement = lastFocusedCategory ;
focusedElement = lastFocusedElement ;
2017-05-31 00:21:21 +00:00
focusedElement . classList . add ( "focused" ) ;
2021-01-25 18:09:30 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "z" : {
2018-05-22 21:43:59 +00:00
toggleFilterOrLinkMode ( ) ;
2017-05-31 00:21:21 +00:00
} break ;
case "v" : {
if ( focusedElement && focusedElement . classList . contains ( "filter_content" ) )
{
invertFilter ( focusedElement )
}
} break ;
case "V" : {
resetFilter ( ) ;
} break ;
case "?" : {
2021-01-25 18:09:30 +00:00
if ( helpDocumentation )
{
helpDocumentation . classList . toggle ( "visible" ) ;
}
2017-06-03 01:32:18 +00:00
} break ;
2017-05-31 00:21:21 +00:00
case 'N' :
2018-02-28 01:04:06 +00:00
case 'J' :
2017-05-31 00:21:21 +00:00
case 'S' : {
player . jumpToNextMarker ( ) ;
} break ;
case 'P' :
2018-02-28 01:04:06 +00:00
case 'K' :
2017-05-31 00:21:21 +00:00
case 'W' : {
player . jumpToPrevMarker ( ) ;
} break ;
2018-02-28 01:04:06 +00:00
case '[' :
case '<' : {
if ( prevEpisode )
{
location = prevEpisode . href ;
}
} break ;
case ']' :
case '>' : {
if ( nextEpisode )
{
location = nextEpisode . href ;
}
} break ;
2018-05-22 21:43:59 +00:00
case 'Y' : {
if ( cineraLink )
{
if ( linkAnnotation == false && player . playing )
{
player . pause ( ) ;
}
if ( linkMenu && ! linkMenu . classList . contains ( "visible" ) )
{
toggleMenuVisibility ( linkMenu ) ;
}
SelectText ( cineraLink ) ;
}
}
2017-05-31 00:21:21 +00:00
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" ) ;
2020-05-09 17:33:25 +00:00
disableSprite ( filterItem ) ;
2017-06-11 22:49:04 +00:00
if ( ! filterItem . parentNode . classList . contains ( "filter_media" ) )
{
filterItem . querySelector ( ".icon" ) . style . backgroundColor = "transparent" ;
}
2018-01-15 00:03:11 +00:00
var testMarkers = playerContainer . querySelectorAll ( ".marker." + selectedCategory + ", .marker.cat_" + selectedCategory ) ;
2017-05-31 00:21:21 +00:00
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" ) ;
2017-06-10 15:56:04 +00:00
markerCategories [ k ] . style . backgroundColor = "transparent" ;
2017-05-31 00:21:21 +00:00
}
}
}
else
{
2018-01-15 00:03:11 +00:00
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" ) ;
2020-05-09 17:33:25 +00:00
disableSprite ( markerCategories [ k ] ) ;
2018-01-15 00:03:11 +00:00
}
}
2017-05-31 00:21:21 +00:00
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" ) ;
2020-05-09 17:33:25 +00:00
enableSprite ( filterItem ) ;
2017-06-11 22:49:04 +00:00
if ( ! filterItem . parentNode . classList . contains ( "filter_media" ) )
{
filterItem . querySelector ( ".icon" ) . style . backgroundColor = getComputedStyle ( filterItem . querySelector ( ".icon" ) ) . getPropertyValue ( "border-color" ) ;
}
2017-06-10 15:56:04 +00:00
setDotLightness ( filterItem . querySelector ( ".icon" ) ) ;
2021-01-25 18:09:30 +00:00
var testMarkers = cinera . querySelectorAll ( ".marker.off_" + selectedCategory ) ;
2017-05-31 00:21:21 +00:00
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" ) ;
2017-06-10 15:56:04 +00:00
markerCategories [ k ] . style . backgroundColor = getComputedStyle ( markerCategories [ k ] ) . getPropertyValue ( "border-color" ) ;
setDotLightness ( markerCategories [ k ] ) ;
2017-05-31 00:21:21 +00:00
}
}
}
else
{
testMarkers [ j ] . classList . remove ( "off_" + selectedCategory ) ;
testMarkers [ j ] . classList . add ( selectedCategory ) ;
2018-01-15 00:03:11 +00:00
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" ) ;
2020-05-09 17:33:25 +00:00
enableSprite ( markerCategories [ k ] ) ;
2018-01-15 00:03:11 +00:00
}
}
2017-05-31 00:21:21 +00:00
}
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 )
{
2017-11-11 00:34:47 +00:00
if ( filterItems [ i ] . classList )
2017-05-31 00:21:21 +00:00
{
2017-11-11 00:34:47 +00:00
var selectedCategory = filterItems [ i ] . classList [ 1 ] ;
if ( filterInitState [ selectedCategory ] . off ^ filterState [ selectedCategory ] . off )
{
filterItemToggle ( filterItems [ i ] ) ;
}
2017-05-31 00:21:21 +00:00
}
}
2017-11-11 00:34:47 +00:00
2018-01-15 00:03:11 +00:00
if ( filterMode == "inclusive" )
2017-05-31 00:21:21 +00:00
{
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" ) ;
}
}
2018-02-28 20:18:11 +00:00
function onRefChanged ( ref , element , player ) {
2017-05-31 00:21:21 +00:00
if ( element . classList . contains ( "skip" ) )
{
2018-02-28 01:04:06 +00:00
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 ;
}
2017-05-31 00:21:21 +00:00
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" ) ;
}
}
2018-02-23 23:36:42 +00:00
if ( element . classList [ selector ] . startsWith ( "cat_" ) || element . classList [ selector ] in filterState )
2017-05-31 00:21:21 +00:00
{
if ( ! filter . querySelector ( ".filter_mode" ) . classList . add ( "responsible" ) )
{
filter . querySelector ( ".filter_mode" ) . classList . add ( "responsible" ) ;
}
}
setTimeout ( resetFade , 8000 ) ;
}
2018-02-23 23:36:42 +00:00
if ( player && player . playing )
{
player . jumpToNextMarker ( ) ;
}
2017-05-31 00:21:21 +00:00
}
2021-01-25 18:09:30 +00:00
else
2017-05-31 00:21:21 +00:00
{
2021-01-25 18:09:30 +00:00
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" ) ;
2017-05-31 00:21:21 +00:00
} else {
2021-01-25 18:09:30 +00:00
sourceMenus [ MenuIndex ] . classList . remove ( "current" ) ;
2017-05-31 00:21:21 +00:00
}
2021-01-25 18:09:30 +00:00
2017-05-31 00:21:21 +00:00
} else {
sourceMenus [ MenuIndex ] . classList . remove ( "current" ) ;
2021-01-25 18:09:30 +00:00
var refs = sourceMenus [ MenuIndex ] . querySelectorAll ( ".refs .ref" ) ;
for ( var i = 0 ; i < refs . length ; ++ i ) {
refs [ i ] . classList . remove ( "current" ) ;
}
2017-05-31 00:21:21 +00:00
}
}
}
}
function navigateFilter ( filterItem ) {
if ( filterItem != lastFocusedCategory )
{
lastFocusedCategory . classList . remove ( "focused" ) ;
2020-05-09 17:33:25 +00:00
unfocusSprite ( lastFocusedCategory ) ;
2017-05-31 00:21:21 +00:00
if ( filterItem . parentNode . classList . contains ( "filter_topics" ) )
{
lastFocusedTopic = filterItem ;
lastFocusedCategory = lastFocusedTopic ;
}
else
{
lastFocusedMedium = filterItem ;
lastFocusedCategory = lastFocusedMedium ;
}
focusedElement = lastFocusedCategory ;
focusedElement . classList . add ( "focused" ) ;
2020-05-09 17:33:25 +00:00
focusSprite ( focusedElement ) ;
2017-05-31 00:21:21 +00:00
}
}
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" ) ;
2017-11-11 00:34:47 +00:00
var ourIdentifiers = reference . querySelectorAll ( ".timecode" ) ;
2017-05-31 00:21:21 +00:00
weWereLastFocused = false ;
for ( var k = 0 ; k < ourIdentifiers . length ; ++ k )
{
if ( ourIdentifiers [ k ] == lastFocusedIdentifier )
{
weWereLastFocused = true ;
}
}
if ( ! weWereLastFocused )
{
lastFocusedIdentifier . classList . remove ( "focused" ) ;
2017-11-11 00:34:47 +00:00
lastFocusedIdentifier = ourIdentifiers [ 0 ] ;
2017-05-31 00:21:21 +00:00
}
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 . setTime ( parseInt ( time , 10 ) ) ;
player . play ( ) ;
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
return false ;
}
2021-01-25 18:09:30 +00:00
function handleMenuTogglerInteraction ( menu , eventType )
2017-05-31 00:21:21 +00:00
{
2017-06-03 01:32:18 +00:00
if ( ! ( menu . classList . contains ( "visible" ) ) && eventType == "mouseenter" ||
2021-01-25 18:09:30 +00:00
menu . classList . contains ( "visible" ) && eventType == "mouseleave" ||
( eventType == "click" && ! menu . classList . contains ( "cineraHelp" ) ) )
2017-05-31 00:21:21 +00:00
{
if ( menu . classList . contains ( "quotes" ) )
{
toggleMenuVisibility ( quotesMenu ) ;
}
else if ( menu . classList . contains ( "references" ) )
{
toggleMenuVisibility ( referencesMenu ) ;
}
else if ( menu . classList . contains ( "filter" ) )
{
toggleMenuVisibility ( filterMenu ) ;
}
2018-05-22 21:43:59 +00:00
else if ( menu . classList . contains ( "link" ) )
{
toggleMenuVisibility ( linkMenu ) ;
}
2017-05-31 00:21:21 +00:00
else if ( menu . classList . contains ( "credits" ) )
{
toggleMenuVisibility ( creditsMenu ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-05-31 00:21:21 +00:00
}
2017-06-09 22:04:07 +00:00
function RGBtoHSL ( colour )
{
var rgb = colour . slice ( 4 , - 1 ) . split ( ", " ) ;
var red = rgb [ 0 ] ;
var green = rgb [ 1 ] ;
var blue = rgb [ 2 ] ;
var min = Math . min ( red , green , blue ) ;
var max = Math . max ( red , green , blue ) ;
var chroma = max - min ;
var hue = 0 ;
if ( max == red )
{
hue = ( ( green - blue ) / chroma ) % 6 ;
}
else if ( max == green )
{
hue = ( ( blue - red ) / chroma ) + 2 ;
}
else if ( max == blue )
{
hue = ( ( red - green ) / chroma ) + 4 ;
}
var saturation = chroma / 255 * 100 ;
hue = ( hue * 60 ) < 0 ? 360 + ( hue * 60 ) : ( hue * 60 ) ;
return [ hue , saturation ]
}
function getBackgroundBrightness ( element ) {
var colour = getComputedStyle ( element ) . getPropertyValue ( "background-color" ) ;
var depth = 0 ;
2017-06-10 15:56:04 +00:00
while ( ( colour == "transparent" || colour == "rgba(0, 0, 0, 0)" ) && depth <= 4 )
2017-06-09 22:04:07 +00:00
{
element = element . parentNode ;
colour = getComputedStyle ( element ) . getPropertyValue ( "background-color" ) ;
++ depth ;
}
var rgb = colour . slice ( 4 , - 1 ) . split ( ", " ) ;
2017-06-10 15:56:04 +00:00
var result = Math . sqrt ( rgb [ 0 ] * rgb [ 0 ] * . 241 +
2017-06-09 22:04:07 +00:00
rgb [ 1 ] * rgb [ 1 ] * . 691 +
rgb [ 2 ] * rgb [ 2 ] * . 068 ) ;
2017-06-10 15:56:04 +00:00
return result ;
2017-06-09 22:04:07 +00:00
}
function setTextLightness ( textElement )
{
var textHue = textElement . getAttribute ( "data-hue" ) ;
var textSaturation = textElement . getAttribute ( "data-saturation" ) ;
if ( getBackgroundBrightness ( textElement . parentNode ) < 127 )
{
textElement . style . color = ( "hsl(" + textHue + ", " + textSaturation + ", 76%)" ) ;
}
else
{
textElement . style . color = ( "hsl(" + textHue + ", " + textSaturation + ", 24%)" ) ;
}
}
function setDotLightness ( topicDot )
{
var Hue = RGBtoHSL ( getComputedStyle ( topicDot ) . getPropertyValue ( "background-color" ) ) [ 0 ] ;
var Saturation = RGBtoHSL ( getComputedStyle ( topicDot ) . getPropertyValue ( "background-color" ) ) [ 1 ] ;
if ( getBackgroundBrightness ( topicDot . parentNode ) < 127 )
{
topicDot . style . backgroundColor = ( "hsl(" + Hue + ", " + Saturation + "%, 76%)" ) ;
topicDot . style . borderColor = ( "hsl(" + Hue + ", " + Saturation + "%, 76%)" ) ;
}
else
{
2017-11-11 00:34:47 +00:00
topicDot . style . backgroundColor = ( "hsl(" + Hue + ", " + Saturation + "%, 47%)" ) ;
topicDot . style . borderColor = ( "hsl(" + Hue + ", " + Saturation + "%, 47%)" ) ;
}
}