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`.
2021-06-23 14:13:41 +00:00
var vod _platform = {
2021-07-07 15:33:54 +00:00
DIRECT : 0 ,
VIMEO : 1 ,
YOUTUBE : 2 ,
2021-06-23 14:13:41 +00:00
} ;
function
GetVODPlatformFromString ( VODPlatformString )
{
var Result = null ;
switch ( VODPlatformString )
{
2021-07-07 15:33:54 +00:00
case "direct" : Result = vod _platform . DIRECT ; break ;
2021-06-23 14:13:41 +00:00
case "vimeo" : Result = vod _platform . VIMEO ; break ;
case "youtube" : Result = vod _platform . YOUTUBE ; break ;
default : break ;
}
return Result ;
}
2017-03-10 14:19:25 +00:00
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 ( ) { } ;
2021-06-23 14:13:41 +00:00
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." ) ;
2017-03-10 14:19:25 +00:00
}
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 ) ;
}
2021-06-23 14:13:41 +00:00
this . vod _platform = GetVODPlatformFromString ( this . videoContainer . getAttribute ( "data-platform" ) ) ;
2017-03-10 14:19:25 +00:00
this . currentMarker = null ;
this . currentMarkerIdx = null ;
2021-06-23 14:13:41 +00:00
this . platformPlayer = null ;
this . platformPlayerReady = false ;
2021-07-07 15:33:54 +00:00
this . duration = null ;
2017-03-10 14:19:25 +00:00
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 ) ) ;
2021-06-23 14:13:41 +00:00
Player . initializePlatform ( this . vod _platform , this . onPlatformReady . 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 ( ) {
2021-06-23 14:13:41 +00:00
if ( this . platformPlayerReady ) {
2017-03-10 14:19:25 +00:00
if ( ! this . playing ) {
2021-06-23 14:13:41 +00:00
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
this . platformPlayer . play ( ) ;
} break ;
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
this . platformPlayer . play ( ) ;
} break ;
case vod _platform . YOUTUBE :
{
this . platformPlayer . playVideo ( ) ;
} break ;
}
this . pauseAfterBuffer = false ;
} else {
this . shouldPlay = true ;
2017-03-10 14:19:25 +00:00
}
}
} ;
// 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 ( ) {
2021-06-23 14:13:41 +00:00
if ( this . platformPlayerReady ) {
2017-03-10 14:19:25 +00:00
if ( this . playing ) {
2021-06-23 14:13:41 +00:00
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
this . platformPlayer . pause ( ) ;
} break ;
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
this . platformPlayer . pause ( ) ;
} break ;
case vod _platform . YOUTUBE :
{
this . platformPlayer . pauseVideo ( ) ;
} break ;
}
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 ;
}
} ;
2021-06-23 14:13:41 +00:00
// Sets the current time then plays.
2017-03-10 14:19:25 +00:00
// If the player hasn't loaded yet, it will seek to this time when ready.
2021-06-23 14:13:41 +00:00
Player . prototype . setTimeThenPlay = function ( time ) {
2017-03-10 14:19:25 +00:00
this . currentTime = time ;
2021-06-23 14:13:41 +00:00
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
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 ;
2021-06-23 14:13:41 +00:00
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 ;
2017-03-10 14:19:25 +00:00
}
} ;
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 ;
}
2021-06-23 14:13:41 +00:00
this . setTimeThenPlay ( targetTime ) ;
2017-03-10 14:19:25 +00:00
} ;
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 ;
}
2021-06-23 14:13:41 +00:00
this . setTimeThenPlay ( targetTime ) ;
2017-03-10 14:19:25 +00:00
} ;
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
}
2021-07-07 15:33:54 +00:00
// Call this after changing the size of the video container in order to update the platform player.
2017-03-10 14:19:25 +00:00
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-06-11 13:03:13 +00:00
var VisibleArea = MaxDimensionsOfElement ( this . container , GetWindowDim ( false ) ) ;
2021-02-10 22:41:46 +00:00
var AvailableHeight = VisibleArea . Y - titleBar . offsetHeight ;
2021-06-11 13:03:13 +00:00
var VerticalScrollBarWidth = this . markersContainer . offsetWidth - this . markersContainer . clientWidth ;
width = VisibleArea . X - ( this . markersContainer . scrollWidth + VerticalScrollBarWidth ) ;
2021-01-25 18:09:30 +00:00
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
}
2021-07-07 15:33:54 +00:00
if ( this . platformPlayerReady )
2021-06-23 14:13:41 +00:00
{
2021-07-07 15:33:54 +00:00
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 :
{
2021-06-23 14:13:41 +00:00
this . platformPlayer . setSize ( Math . floor ( width ) , Math . floor ( height ) ) ;
2021-07-07 15:33:54 +00:00
} break ;
default : break ;
}
2017-03-10 14:19:25 +00:00
}
}
// 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 ( ) ;
}
}
2021-06-23 14:13:41 +00:00
Player . initializePlatform = function ( platform _id , callback ) {
switch ( platform _id )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
callback ( ) ;
} break ;
case vod _platform . YOUTUBE :
{
2021-09-01 15:38:23 +00:00
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 ( ) ;
}
2021-06-23 14:13:41 +00:00
}
} break ;
2017-03-10 14:19:25 +00:00
}
}
// 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 ;
}
2021-06-23 14:13:41 +00:00
this . setTimeThenPlay ( time ) ;
2017-03-10 14:19:25 +00:00
} ;
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 )
{
2021-07-07 15:33:54 +00:00
localStorage . removeItem ( lastTimestampStorageItem ) ;
2018-04-17 23:05:14 +00:00
}
else
{
2021-07-07 15:33:54 +00:00
localStorage . setItem ( lastTimestampStorageItem , this . currentMarker . timestamp ) ;
2018-04-17 23:05:14 +00:00
}
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 ) {
2021-06-23 14:13:41 +00:00
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
this . currentTime = this . platformPlayer . currentTime ;
this . updateProgress ( ) ;
} break ;
2021-06-23 14:13:41 +00:00
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 ;
}
2017-03-10 14:19:25 +00:00
}
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
} ;
2021-06-23 14:13:41 +00:00
Player . prototype . setStyleAndQuality = function ( ) {
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
// NOTE(matt): onPlatformReady() has set the width and height
} break ;
2021-06-23 14:13:41 +00:00
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 ;
}
2017-03-10 14:19:25 +00:00
this . updateSize ( ) ;
2021-06-23 14:13:41 +00:00
}
Player . prototype . setDurationThenAutoplay = function ( Duration ) {
this . duration = Duration ;
this . markers [ this . markers . length - 1 ] . endTime = this . duration ;
2017-03-10 14:19:25 +00:00
if ( this . currentTime > 0 ) {
2021-06-23 14:13:41 +00:00
this . currentTime = Math . max ( 0 , Math . min ( this . currentTime , this . duration ) ) ;
this . setTimeThenPlay ( this . currentTime ) ;
2017-03-10 14:19:25 +00:00
}
if ( this . shouldPlay ) {
2021-06-23 14:13:41 +00:00
this . play ( ) ;
2017-03-10 14:19:25 +00:00
}
2021-06-23 14:13:41 +00:00
}
2017-03-10 14:19:25 +00:00
2021-06-23 14:13:41 +00:00
Player . prototype . acquireDurationThenAutoplay = function ( ) {
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
this . setDurationThenAutoplay ( this . platformPlayer . duration ) ;
} break ;
2021-06-23 14:13:41 +00:00
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 ;
2017-03-10 14:19:25 +00:00
}
2021-06-23 14:13:41 +00:00
}
2017-03-11 02:48:11 +00:00
2021-06-23 14:13:41 +00:00
Player . prototype . onPlatformPlayerReady = function ( ) {
this . platformPlayerReady = true ;
this . setStyleAndQuality ( ) ;
this . acquireDurationThenAutoplay ( ) ;
2017-03-10 14:19:25 +00:00
} ;
2021-06-23 14:13:41 +00:00
Player . prototype . onPlaybackRateChange = function ( Rate )
{
this . speed = Rate ;
}
2021-07-07 15:33:54 +00:00
Player . prototype . onDirectPlayerPlaybackRateChange = function ( ev ) {
this . onPlaybackRateChange ( this . platformPlayer . playbackRate ) ;
} ;
2021-06-23 14:13:41 +00:00
Player . prototype . onVimeoPlayerPlaybackRateChange = function ( ev ) {
this . onPlaybackRateChange ( ev . playbackRate ) ;
2017-03-10 14:19:25 +00:00
} ;
2021-06-23 14:13:41 +00:00
Player . prototype . onYouTubePlayerPlaybackRateChange = function ( ev ) {
this . onPlaybackRateChange ( ev . data ) ;
2017-03-10 14:19:25 +00:00
} ;
2021-06-23 14:13:41 +00:00
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 ;
2021-07-07 15:33:54 +00:00
localStorage . removeItem ( lastTimestampStorageItem ) ;
2021-06-23 14:13:41 +00:00
this . currentTime = null ;
this . updateProgress ( ) ;
}
Player . prototype . onStatePaused = function ( CurrentTime )
{
this . buffering = false ;
this . playing = false ;
this . currentTime = CurrentTime ;
this . updateProgress ( ) ;
}
2017-05-31 00:21:21 +00:00
2021-06-23 14:13:41 +00:00
Player . prototype . onStatePlaying = function ( CurrentTime )
{
this . buffering = false ;
this . playing = true ;
this . currentTime = CurrentTime ;
if ( this . pauseAfterBuffer )
{
this . pauseAfterBuffer = false ;
this . pause ( ) ;
}
}
2021-07-07 15:33:54 +00:00
Player . prototype . onDirectPlayerStateChange _Paused = function ( ev )
{
this . onStatePaused ( this . platformPlayer . currentTime ) ;
}
Player . prototype . onDirectPlayerStateChange _Playing = function ( ev )
{
this . onStatePlaying ( this . platformPlayer . currentTime ) ;
}
2021-06-23 14:13:41 +00:00
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 ++ ;
2021-07-07 15:33:54 +00:00
var platformPlayerDivPlaced = this . videoContainer . appendChild ( platformPlayerDiv ) ;
2021-06-23 14:13:41 +00:00
switch ( this . vod _platform )
{
2021-07-07 15:33:54 +00:00
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 ;
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
this . videoContainer . style . position = "relative" ;
this . videoContainer . style . alignSelf = "unset" ;
2023-01-11 19:24:41 +00:00
var CallData = {
2021-06-23 14:13:41 +00:00
id : this . videoContainer . getAttribute ( "data-videoId" ) ,
title : false ,
2023-01-11 19:24:41 +00:00
} ;
var CCLang = this . videoContainer . getAttribute ( "data-ccLang" ) ;
if ( CCLang != null )
{
CallData . texttrack = CCLang ;
}
this . platformPlayer = new Vimeo . Player ( platformPlayerDiv . id , CallData ) ;
2021-06-23 14:13:41 +00:00
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 :
{
2023-01-11 19:24:41 +00:00
var CallData = {
2021-06-23 14:13:41 +00:00
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 )
}
2023-01-11 19:24:41 +00:00
} ;
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 ) ;
2021-06-23 14:13:41 +00:00
} break ;
}
} ;
Player . platformPlayerCount = 0 ;
2017-05-31 00:21:21 +00:00
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 )
{
2021-07-07 15:33:54 +00:00
if ( linkTimestamp == true )
2018-05-22 21:43:59 +00:00
{
if ( player . currentMarker )
{
link . value = baseURL + "#" + player . currentMarker . timestamp ;
}
else
{
link . value = baseURL ;
}
}
else
{
2021-06-23 14:13:41 +00:00
switch ( player . vod _platform )
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
link . value = baseURL + "#" + Math . round ( player . platformPlayer . currentTime ) ;
} break ;
2021-06-23 14:13:41 +00:00
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 ;
}
2018-05-22 21:43:59 +00:00
}
}
}
function toggleLinkMode ( linkMode , link )
{
2021-07-07 15:33:54 +00:00
linkTimestamp = ! linkTimestamp ;
if ( linkTimestamp == true )
2018-05-22 21:43:59 +00:00
{
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
}
2021-06-23 14:13:41 +00:00
function IsFullScreen ( )
{
return ( document . fullscreen || document . webkitIsFullScreen || document . mozFullScreen || document . msFullscreenElement || document . fullscreenElement ) ;
}
2018-01-15 21:52:24 +00:00
function enterFullScreen _ ( )
{
2021-06-23 14:13:41 +00:00
if ( ! IsFullScreen ( ) )
2018-01-15 21:52:24 +00:00
{
2021-06-23 14:13:41 +00:00
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 ( ) ; }
2018-01-15 21:52:24 +00:00
}
}
function leaveFullScreen _ ( )
{
2021-06-23 14:13:41 +00:00
if ( IsFullScreen ( ) )
2018-01-15 21:52:24 +00:00
{
2021-06-23 14:13:41 +00:00
if ( document . exitFullscreen ) { document . exitFullscreen ( ) ; }
else if ( document . mozCancelFullScreen ) { document . mozCancelFullScreen ( ) ; }
else if ( document . webkitCancelFullScreen ) { document . webkitCancelFullScreen ( ) ; }
else if ( document . msExitFullscreen ) { document . msExitFullscreen ( ) ; }
2018-01-15 21:52:24 +00:00
}
}
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" ) ;
2021-06-23 14:13:41 +00:00
player . setTimeThenPlay ( parseInt ( time ) ) ;
2017-05-31 00:21:21 +00:00
}
else if ( focusedElement . parentNode . classList . contains ( "references_container" ) )
{
var time = focusedIdentifier . getAttribute ( "data-timestamp" ) ;
2021-06-23 14:13:41 +00:00
player . setTimeThenPlay ( parseInt ( time ) ) ;
2017-05-31 00:21:21 +00:00
}
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
{
2021-06-23 14:13:41 +00:00
if ( player ) { player . pause ( ) ; }
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" ) )
{
2021-06-23 14:13:41 +00:00
if ( player ) { player . pause ( ) ; }
2017-05-31 00:21:21 +00:00
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 )
{
2021-07-07 15:33:54 +00:00
if ( linkTimestamp == false && player . playing )
2018-05-22 21:43:59 +00:00
{
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 )
{
2021-06-23 14:13:41 +00:00
player . setTimeThenPlay ( parseInt ( time , 10 ) ) ;
2017-05-31 00:21:21 +00:00
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
}