2023-03-18 01:27:05 +00:00
"use strict" ;
var devices = {
DESKTOP : 0 ,
MOBILE : 1 ,
} ;
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 ;
}
2023-03-18 01:27:05 +00:00
var focus _level = {
ITEM : 0 ,
IDENTIFIER : 1 ,
} ;
var menu _id = {
UNSET : - 1 ,
MARKERS : 0 ,
QUOTES : 1 ,
REFERENCES : 2 ,
FILTER : 3 ,
VIEWS : 4 ,
LINK : 5 ,
CREDITS : 6 ,
COUNT : 7 ,
} ;
var trigger _id = {
KEYBOARD : 0 ,
MOUSE : 1 ,
} ;
var views = {
REGULAR : 0 ,
THEATRE : 1 ,
SUPERTHEATRE : 2 ,
} ;
// 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 ( cineraElement , refsCallback ) {
this . root = cineraElement ;
this . container = this . root . querySelector ( ".cineraPlayerContainer" )
this . originalTextContent = {
TitleQuotes : null ,
TitleReferences : null ,
TitleCredits : null ,
EpisodePrev : null ,
EpisodeNext : null ,
} ;
this . prevEpisode = this . container . querySelector ( ".episodeMarker.prev" ) ;
if ( this . prevEpisode ) { this . originalTextContent . EpisodePrev = this . prevEpisode . firstChild . textContent ; }
this . nextEpisode = this . container . querySelector ( ".episodeMarker.next" ) ;
if ( this . nextEpisode ) { this . originalTextContent . EpisodeNext = this . nextEpisode . firstChild . textContent ; }
2017-03-10 14:19:25 +00:00
this . videoContainer = this . container . querySelector ( ".video_container" ) ;
this . refsCallback = refsCallback || function ( ) { } ;
2023-03-18 01:27:05 +00:00
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
}
2023-03-18 01:27:05 +00:00
var colouredItems = this . container . querySelectorAll ( ".author, .member, .project" ) ;
for ( var i = 0 ; i < colouredItems . length ; ++ i )
{
setTextLightness ( colouredItems [ i ] ) ;
}
var topicDots = this . root . querySelectorAll ( ".category" ) ;
for ( var i = 0 ; i < topicDots . length ; ++ i )
{
setDotLightness ( topicDots [ i ] ) ;
}
this . titleBar = this . root . querySelector ( ".cineraMenus" ) ;
this . MenusFocused = {
MenuID : menu _id . UNSET ,
Item : null ,
Identifier : null ,
} ;
this . Menus = [ ] ;
this . Menus . length = menu _id . COUNT ;
this . Menus [ menu _id . MARKERS ] = {
Container : this . container . querySelector ( ".markers_container" ) ,
Elements : null ,
Item : { LastFocused : null , } ,
Scroll : { To : - 1 , Position : 0 , } ,
} ;
this . Menus [ menu _id . QUOTES ] = {
Container : null ,
Elements : null , // The "Source" part of each Item
Item : { LastFocused : null , } ,
Identifier : { LastFocused : null , } ,
Scroll : { To : - 1 , Position : 0 , } ,
} ;
this . Menus [ menu _id . REFERENCES ] = {
Container : null ,
Elements : null , // The "Source" part of each Item
Item : { LastFocused : null , } ,
Identifier : { LastFocused : null , } ,
Scroll : { To : - 1 , Position : 0 , } ,
} ;
this . Menus [ menu _id . FILTER ] = {
Container : null ,
Elements : null ,
Category : { LastFocused : null , } ,
Topic : { LastFocused : null , } ,
Medium : { LastFocused : null , } ,
Scroll : { To : - 1 , Position : 0 , } ,
} ;
this . Menus [ menu _id . VIEWS ] = {
Toggler : null ,
Container : null ,
} ;
this . Menus [ menu _id . LINK ] = {
Container : null ,
} ;
this . Menus [ menu _id . CREDITS ] = {
Container : null ,
Item : { LastFocused : null , } ,
Scroll : { To : - 1 , Position : 0 , } ,
} ;
this . initTitleBar ( ) ;
this . Menus [ menu _id . MARKERS ] . Elements = this . Menus [ menu _id . MARKERS ] . Container . querySelectorAll ( ".marker" ) ;
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
if ( CineraProps . IsMobile )
{
this . InitMobileStyle ( ) ;
}
2017-03-10 14:19:25 +00:00
this . markers = [ ] ;
2023-03-18 01:27:05 +00:00
var markerEls = this . Menus [ menu _id . MARKERS ] . Elements ;
2017-03-10 14:19:25 +00:00
if ( markerEls . length == 0 ) {
2023-03-18 01:27:05 +00:00
console . error ( "No markers found in" , this . Menus [ menu _id . MARKERS ] . Container , "for player initialized on" , this . container ) ;
2017-03-10 14:19:25 +00:00
throw new Error ( "Missing markers." ) ;
}
for ( var i = 0 ; i < markerEls . length ; ++ i ) {
2023-03-18 01:27:05 +00:00
var markerEl = markerEls [ i ] ;
2017-03-10 14:19:25 +00:00
var marker = {
2023-03-25 00:04:34 +00:00
timestamp : parseFloat ( markerEl . getAttribute ( "data-timestamp" ) ) ,
2023-03-18 01:27:05 +00:00
ref : markerEl . getAttribute ( "data-ref" ) ,
2023-03-25 00:04:34 +00:00
endTime : ( i < markerEls . length - 1 ? parseFloat ( markerEls [ i + 1 ] . getAttribute ( "data-timestamp" ) ) : null ) ,
2023-03-18 01:27:05 +00:00
el : markerEl ,
fadedProgress : markerEl . querySelector ( ".progress.faded" ) ,
progress : markerEl . querySelector ( ".progress.main" ) ,
2017-03-10 14:19:25 +00:00
hoverx : null
} ;
marker . el . addEventListener ( "click" , this . onMarkerClick . bind ( this , marker ) ) ;
marker . el . addEventListener ( "mousemove" , this . onMarkerMouseMove . bind ( this , marker ) ) ;
2023-03-18 01:27:05 +00:00
marker . el . addEventListener ( "mouseenter" , this . onMarkerMouseEnter . bind ( this , marker ) ) ;
2017-03-10 14:19:25 +00:00
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 ;
2023-03-18 01:27:05 +00:00
this . desiredTime = - 1 ;
2017-03-10 14:19:25 +00:00
this . nextFrame = null ;
this . looping = false ;
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . MARKERS ] . Container . addEventListener ( "wheel" , function ( ev ) {
this . Menus [ menu _id . MARKERS ] . Scroll . To = - 1 ;
2017-03-10 14:19:25 +00:00
} . bind ( this ) ) ;
2021-06-23 14:13:41 +00:00
Player . initializePlatform ( this . vod _platform , this . onPlatformReady . bind ( this ) ) ;
2023-03-18 01:27:05 +00:00
this . updateSize ( ) ;
this . cineraViewStorageItem = "cineraView" ;
if ( this . Menus [ menu _id . VIEWS ] . Toggler && localStorage . getItem ( this . cineraViewStorageItem ) )
{
this . toggleTheatreMode ( ) ;
}
FlipClear ( ) ;
2018-02-28 20:18:11 +00:00
this . resume ( ) ;
2023-03-18 01:27:05 +00:00
InitScrollEventListener ( this . root , CineraProps . IsMobile , null ) ;
this . lastTimestampStorageItem = "cineraTimecode_" + window . location . pathname ;
var lastTimestamp ;
if ( location . hash ) {
this . setTimeThenPlay ( location . hash . startsWith ( '#' ) ? location . hash . substr ( 1 ) : location . hash ) ;
}
else if ( lastTimestamp = localStorage . getItem ( this . lastTimestampStorageItem ) )
{
this . setTimeThenPlay ( lastTimestamp ) ;
}
}
Player . prototype . addMenuTogglingMouseListeners = function ( MenuID )
{
var player = this ;
var menuToggler = player . Menus [ MenuID ] . Container . closest ( ".menu" ) ;
menuToggler . addEventListener ( "mouseenter" , function ( ev ) {
player . handleMenuTogglerInteraction ( this , ev . type ) ;
} )
menuToggler . addEventListener ( "mouseleave" , function ( ev ) {
player . handleMenuTogglerInteraction ( this , ev . type ) ;
} )
menuToggler . addEventListener ( "click" , function ( ev ) {
player . handleMenuTogglerInteraction ( this , ev . type ) ;
} )
}
Player . prototype . addReferencesOrQuotesMouseListeners = function ( MenuID )
{
var player = this ;
var Items = player . Menus [ MenuID ] . Container . querySelectorAll ( ".ref" ) ;
for ( var i = 0 ; i < Items . length ; ++ i )
{
Items [ i ] . addEventListener ( "mouseenter" , function ( ev ) {
player . mouseOverReferencesOrQuotes ( MenuID , this ) ;
} )
} ;
// The "Source" part of each Item
player . Menus [ MenuID ] . Elements = player . Menus [ MenuID ] . Container . querySelectorAll ( ".refs .ref" ) ;
for ( var i = 0 ; i < player . Menus [ MenuID ] . Elements . length ; ++ i ) {
player . Menus [ MenuID ] . Elements [ i ] . addEventListener ( "click" , function ( ev ) {
player . pause ( ) ;
} ) ;
}
var Timecodes = player . Menus [ MenuID ] . Container . querySelectorAll ( ".refs .ref .ref_indices .timecode" ) ;
for ( var i = 0 ; i < Timecodes . length ; ++ i ) {
Timecodes [ i ] . addEventListener ( "click" , function ( ev ) {
var time = ev . currentTarget . getAttribute ( "data-timestamp" ) ;
mouseSkipToTimecode ( player , time , ev ) ;
player . setScroller ( player . Menus [ MenuID ] , this . closest ( ".ref" ) , true , false ) ;
} ) ;
Timecodes [ i ] . addEventListener ( "mouseenter" , function ( ev ) {
player . mouseOverReferenceOrQuoteIdentifier ( MenuID , this ) ;
} ) ;
}
}
Player . prototype . initMenu _Quotes = function ( )
{
var player = this ;
var MenuID = menu _id . QUOTES ;
player . Menus [ MenuID ] . Container = player . titleBar . querySelector ( ".quotes_container" ) ;
if ( player . Menus [ MenuID ] . Container )
{
player . originalTextContent . TitleQuotes = player . Menus [ MenuID ] . Container . previousElementSibling . textContent ;
player . addReferencesOrQuotesMouseListeners ( MenuID ) ;
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenu _References = function ( )
{
var player = this ;
var MenuID = menu _id . REFERENCES ;
player . Menus [ MenuID ] . Container = player . titleBar . querySelector ( ".references_container" ) ;
if ( player . Menus [ MenuID ] . Container )
{
player . originalTextContent . TitleReferences = player . Menus [ MenuID ] . Container . previousElementSibling . textContent ;
player . addReferencesOrQuotesMouseListeners ( MenuID ) ;
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenu _Filter = function ( )
{
var player = this ;
var MenuID = menu _id . FILTER ;
player . Menus [ MenuID ] . Container = player . titleBar . querySelector ( ".filter_container" ) ;
if ( player . Menus [ MenuID ] . Container )
{
player . filter = player . Menus [ MenuID ] . Container . parentNode ;
player . filterModeElement = player . filter . querySelector ( ".filter_mode" ) ;
player . filterModeElement . addEventListener ( "click" , function ( ev ) {
ev . stopPropagation ( ) ;
player . toggleFilterMode ( ) ;
} ) ;
player . filterMode = player . filterModeElement . classList [ 1 ] ;
player . Menus [ menu _id . FILTER ] . Elements = player . filter . querySelectorAll ( ".filter_content" ) ;
player . filterInitState = new Object ( ) ;
player . filterState = new Object ( ) ;
for ( var i = 0 ; i < player . Menus [ menu _id . FILTER ] . Elements . length ; ++ i )
{
var Item = player . Menus [ menu _id . FILTER ] . Elements [ i ] ;
Item . addEventListener ( "mouseenter" , function ( ev ) {
player . navigateFilter ( this ) ;
} )
Item . addEventListener ( "click" , function ( ev ) {
ev . stopPropagation ( ) ;
player . filterItemToggle ( this ) ;
} ) ;
var filterItemName = Item . classList . item ( 1 ) ;
if ( Item . parentNode . classList . contains ( "filter_topics" ) )
{
player . filterInitState [ filterItemName ] = { "type" : "topic" , "off" : ( Item . classList . item ( 2 ) == "off" ) } ;
player . filterState [ filterItemName ] = { "type" : "topic" , "off" : ( Item . classList . item ( 2 ) == "off" ) } ;
}
else
{
player . filterInitState [ filterItemName ] = { "type" : "medium" , "off" : ( Item . classList . item ( 2 ) == "off" ) } ;
player . filterState [ filterItemName ] = { "type" : "medium" , "off" : ( Item . classList . item ( 2 ) == "off" ) } ;
}
}
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenu _Views = function ( )
{
var player = this ;
var MenuID = menu _id . VIEWS ;
player . Menus [ MenuID ] . Toggler = player . titleBar . querySelector ( ".views" ) ;
if ( player . Menus [ MenuID ] . Toggler && ! CineraProps . IsMobile )
{
player . Menus [ MenuID ] . Container = player . Menus [ MenuID ] . Toggler . querySelector ( ".views_container" ) ;
player . Menus [ MenuID ] . Toggler . addEventListener ( "mouseenter" , function ( ev ) {
player . handleMouseOverViewsMenu ( ) ;
} ) ;
player . Menus [ MenuID ] . Toggler . addEventListener ( "mouseleave" , function ( ev ) {
player . Menus [ MenuID ] . Container . classList . remove ( "visible" ) ;
player . MenusFocused . MenuID = menu _id . UNSET ;
} ) ;
player . viewItems = player . Menus [ MenuID ] . Toggler . querySelectorAll ( ".view" ) ;
for ( var i = 0 ; i < player . viewItems . length ; ++ i )
{
player . viewItems [ i ] . addEventListener ( "click" , function ( ev ) {
switch ( this . getAttribute ( "data-id" ) )
{
case "regular" :
case "theatre" :
{
player . toggleTheatreMode ( ) ;
} break ;
case "super" :
{
player . toggleSuperTheatreMode ( ) ;
} break ;
}
} ) ;
}
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenu _Link = function ( )
{
var player = this ;
var MenuID = menu _id . LINK ;
player . Menus [ MenuID ] . Container = player . titleBar . querySelector ( ".link_container" ) ;
if ( player . Menus [ MenuID ] . Container )
{
player . linkMode = player . Menus [ MenuID ] . Container . querySelector ( "#cineraLinkMode" ) ;
player . link = player . Menus [ MenuID ] . Container . querySelector ( "#cineraLink" ) ;
player . linkTimestamp = true ;
player . linkMode . addEventListener ( "click" , function ( ev ) {
ev . stopPropagation ( ) ;
player . toggleLinkMode ( ) ;
} ) ;
player . link . addEventListener ( "click" , function ( ev ) {
player . CopyToClipboard ( player . link ) ;
player . toggleMenuVisibility ( MenuID , trigger _id . MOUSE ) ;
} ) ;
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenu _Credits = function ( )
{
var player = this ;
var MenuID = menu _id . CREDITS ;
player . Menus [ MenuID ] . Container = player . titleBar . querySelector ( ".credits_container" ) ;
if ( player . Menus [ MenuID ] . Container )
{
player . originalTextContent . TitleCredits = player . Menus [ MenuID ] . Container . previousElementSibling . textContent ;
var creditItems = player . Menus [ MenuID ] . Container . querySelectorAll ( ".person, .support" ) ;
for ( var i = 0 ; i < creditItems . length ; ++ i )
{
var creditItem = creditItems [ i ] ;
creditItem . addEventListener ( "mouseenter" , function ( ev ) {
player . mouseOverCredits ( this ) ;
} ) ;
if ( creditItem . tagName == "A" )
{
creditItem . addEventListener ( "click" , function ( ev ) {
player . pause ( ) ;
} ) ;
}
}
player . addMenuTogglingMouseListeners ( MenuID ) ;
}
}
Player . prototype . initMenus = function ( )
{
this . initMenu _Quotes ( ) ;
this . initMenu _References ( ) ;
this . initMenu _Filter ( ) ;
this . initMenu _Views ( ) ;
this . initMenu _Link ( ) ;
this . initMenu _Credits ( ) ;
}
Player . prototype . initTitleBar = function ( )
{
if ( this . titleBar )
{
this . initMenus ( ) ;
this . helpButton = this . titleBar . querySelector ( ".cineraHelp" ) ;
this . helpDocumentation = this . helpButton . querySelector ( ".help_container" ) ;
BindHelp ( this . helpButton , this . helpDocumentation ) ;
}
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 ) {
2023-03-18 01:27:05 +00:00
this . desiredTime = 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 ) {
2023-03-18 01:27:05 +00:00
this . desiredTime = Math . max ( 0 , Math . min ( this . desiredTime , this . duration ) ) ;
this . platformPlayer . currentTime = this . desiredTime ;
2021-07-07 15:33:54 +00:00
this . updateProgress ( ) ;
this . play ( ) ;
}
} break ;
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
if ( this . platformPlayerReady ) {
2023-03-18 01:27:05 +00:00
this . desiredTime = Math . max ( 0 , Math . min ( this . desiredTime , this . duration ) ) ;
2021-06-23 14:13:41 +00:00
var Parent = this ;
2023-03-18 01:27:05 +00:00
this . platformPlayer . setCurrentTime ( this . desiredTime )
2021-06-23 14:13:41 +00:00
. then ( function ( ) {
Parent . updateProgress ( ) ;
Parent . play ( ) ;
} ) ;
}
} break ;
case vod _platform . YOUTUBE :
{
if ( this . platformPlayerReady ) {
2023-03-18 01:27:05 +00:00
this . desiredTime = Math . max ( 0 , Math . min ( this . desiredTime , this . duration ) ) ;
this . platformPlayer . seekTo ( this . desiredTime ) ;
2021-06-23 14:13:41 +00:00
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
2023-03-18 01:27:05 +00:00
function
ComputeHorizontalOffsetForMenu ( Menu , Toggler , VideoContainerDimX )
{
var Result = 0 ;
var MenuWidth = GetWidthOfHideableElement ( Menu , "visible" ) ;
var TogglerWidth = Toggler . offsetWidth ;
var TogglerOffset = Toggler . offsetLeft ;
var Result = TogglerOffset + ( TogglerWidth / 2 ) - ( MenuWidth / 2 ) ;
var Protrusion = MenuWidth + Result - VideoContainerDimX ;
if ( Protrusion > 0 )
{
Result -= Protrusion ;
}
if ( Result < 0 )
{
Result = 0 ;
}
return Result ;
}
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" ;
2023-03-18 01:27:05 +00:00
var MenuHorizontalOffset = ComputeHorizontalOffsetForMenu ( Menu , Toggler , SizerElementDimX ) ;
2021-01-25 18:09:30 +00:00
Menu . style . top = TitleBarDimY + "px" ;
2023-03-18 01:27:05 +00:00
Menu . style . left = MenuHorizontalOffset + "px" ;
2021-01-25 18:09:30 +00:00
} 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 ) ;
2023-03-18 01:27:05 +00:00
var MenuWidth = GetWidthOfHideableElement ( Menu , "visible" ) ;
2021-01-25 18:09:30 +00:00
Menu . style . top = MenuVerticalOffset + "px" ;
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
}
2023-03-18 01:27:05 +00:00
Player . prototype . ApplyMobileStyle = function ( VideoContainer )
2021-01-25 18:09:30 +00:00
{
2021-02-04 00:13:55 +00:00
var WindowDim = DeriveReliableWindowDimensions ( ) ;
2023-03-18 01:27:05 +00:00
var MaxWidth = MaxWidthOfElement ( this . root , WindowDim ) ;
var MaxHeight = MaxHeightOfElement ( this . root , WindowDim ) ;
2021-01-25 18:09:30 +00:00
2023-03-18 01:27:05 +00:00
var IndicesBar = this . Menus [ menu _id . 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 :
{
2023-03-18 01:27:05 +00:00
this . root . style . flexDirection = "column" ;
this . titleBar . style . flexDirection = "row" ;
2021-01-25 18:09:30 +00:00
} break ;
case orientations . LANDSCAPE _LEFT :
{
2023-03-18 01:27:05 +00:00
this . root . style . flexDirection = "row" ;
this . titleBar . style . flexDirection = "column-reverse" ;
CineraContentWidth -= this . titleBar . offsetWidth ;
2021-01-25 18:09:30 +00:00
} break ;
case orientations . LANDSCAPE _RIGHT :
{
2023-03-18 01:27:05 +00:00
this . root . style . flexDirection = "row-reverse" ;
this . titleBar . style . flexDirection = "column" ;
CineraContentWidth -= this . titleBar . offsetWidth ;
2021-01-25 18:09:30 +00:00
} 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" ;
2023-03-18 01:27:05 +00:00
HeightOfTallestIndex = ComputeAndSetTallest ( MobileCineraContentRule , this . Menus [ menu _id . MARKERS ] . Elements , "current" ) ;
2021-02-04 00:13:55 +00:00
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 :
{
2023-03-18 01:27:05 +00:00
VideoMaxDimY -= this . titleBar . offsetHeight ;
2021-01-25 18:09:30 +00:00
} break ;
case orientations . LANDSCAPE _LEFT :
case orientations . LANDSCAPE _RIGHT :
{
2023-03-18 01:27:05 +00:00
VideoMaxDimX -= this . 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" ;
2023-03-18 01:27:05 +00:00
sizeAndPositionMenuContainer ( this . titleBar , this . root , this . Menus [ menu _id . QUOTES ] . Container ) ;
sizeAndPositionMenuContainer ( this . titleBar , this . root , this . Menus [ menu _id . REFERENCES ] . Container ) ;
sizeAndPositionMenuContainer ( this . titleBar , this . root , this . Menus [ menu _id . FILTER ] . Container ) ;
sizeAndPositionMenuContainer ( this . titleBar , this . root , this . Menus [ menu _id . LINK ] . Container ) ;
sizeAndPositionMenuContainer ( this . titleBar , this . root , this . Menus [ menu _id . CREDITS ] . Container ) ;
2019-03-07 20:16:27 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . IconifyMenuTogglers = function ( )
2019-03-07 20:16:27 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . QUOTES ] . Container )
2019-03-07 20:16:27 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . QUOTES ] . Container . previousElementSibling . textContent = '\u{1F5E9}' ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . REFERENCES ] . Container )
2019-03-07 20:16:27 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . REFERENCES ] . Container . previousElementSibling . textContent = '\u{1F4D6}' ;
2019-03-07 20:16:27 +00:00
}
2021-01-25 18:09:30 +00:00
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . CREDITS ] . Container )
2019-03-07 20:16:27 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . CREDITS ] . Container . previousElementSibling . textContent = '\u{1F46A}' ;
2019-03-07 20:16:27 +00:00
}
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . VIEWS ] . Toggler )
2021-01-25 18:09:30 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . VIEWS ] . Toggler . remove ( ) ;
this . Menus [ menu _id . VIEWS ] . Toggler = null ;
2021-01-25 18:09:30 +00:00
}
}
2019-03-07 20:16:27 +00:00
2023-03-18 01:27:05 +00:00
Player . prototype . InitMobileControls = function ( )
2021-01-25 18:09:30 +00:00
{
var rightmost = { } ;
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . MARKERS ] . Container . style . height = "auto" ;
var episodeMarkerFirst = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.first" ) ;
var episodeMarkerPrev = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.prev" ) ;
var episodeMarkerNext = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.next" ) ;
var episodeMarkerLast = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.last" ) ;
2021-01-25 18:09:30 +00:00
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 ) ;
2023-03-18 01:27:05 +00:00
var markers = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".markers" ) ;
this . Menus [ menu _id . MARKERS ] . Container . insertBefore ( controlPrevTimestamp , markers ) ;
2021-01-25 18:09:30 +00:00
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 )
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . MARKERS ] . Container . insertBefore ( controlNextTimestamp , rightmost ) ;
2021-01-25 18:09:30 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . MARKERS ] . Container . appendChild ( controlNextTimestamp ) ;
2021-01-25 18:09:30 +00:00
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . ConnectMobileControls = function ( )
2021-01-25 18:09:30 +00:00
{
2023-03-18 01:27:05 +00:00
var player = this ;
var ControlPrevTimestamp = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.prevTimestamp" ) ;
2021-01-25 18:09:30 +00:00
ControlPrevTimestamp . addEventListener ( "click" , function ( ev ) {
player . jumpToPrevMarker ( ) ;
} ) ;
2023-03-18 01:27:05 +00:00
var ControlNextTimestamp = this . Menus [ menu _id . MARKERS ] . Container . querySelector ( ".episodeMarker.nextTimestamp" ) ;
2021-01-25 18:09:30 +00:00
ControlNextTimestamp . addEventListener ( "click" , function ( ev ) {
player . jumpToNextMarker ( ) ;
} ) ;
2019-03-07 20:16:27 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . InitMobileStyle = function ( )
{
this . root . classList . add ( "mobile" ) ;
this . IconifyMenuTogglers ( ) ;
this . InitMobileControls ( ) ;
this . ConnectMobileControls ( ) ;
2023-03-21 19:34:24 +00:00
this . ApplyMobileStyle ( this . videoContainer ) ;
2023-03-18 01:27:05 +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 ) ) ;
2023-03-18 01:27:05 +00:00
var AvailableHeight = VisibleArea . Y - this . titleBar . offsetHeight ;
var VerticalScrollBarWidth = this . Menus [ menu _id . MARKERS ] . Container . offsetWidth - this . Menus [ menu _id . MARKERS ] . Container . clientWidth ;
width = VisibleArea . X - ( this . Menus [ menu _id . MARKERS ] . Container . 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 ;
}
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . MARKERS ] . Container . style . height = height + "px" ;
2021-01-27 21:58:56 +00:00
2021-02-10 22:41:46 +00:00
var VacantPixelsBelowMenus = 4 ;
2023-02-20 20:29:16 +00:00
var MenuMaxHeight = height - VacantPixelsBelowMenus ;
2021-01-27 21:58:56 +00:00
MenuContainerRule . style . maxHeight = MenuMaxHeight + "px" ;
2019-03-07 20:16:27 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . 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
}
}
2023-03-18 01:27:05 +00:00
function
DelayedUpdateSize ( player )
{
player . updateSize ( ) ;
}
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 ) {
2023-03-18 01:27:05 +00:00
if ( ! marker . el . classList . contains ( "skip" ) )
{
var time = marker . timestamp ;
if ( this . currentMarker == marker && marker . hoverx !== null ) {
time += ( marker . endTime - marker . timestamp ) * marker . hoverx ;
}
this . setTimeThenPlay ( time ) ;
2017-03-10 14:19:25 +00:00
}
} ;
Player . prototype . onMarkerMouseMove = function ( marker , ev ) {
2023-03-18 01:27:05 +00:00
if ( ! marker . el . classList . contains ( "skip" ) )
{
if ( this . currentMarker == marker ) {
var CineraContent = this . currentMarker . el . querySelector ( ".cineraContent" ) ;
marker . hoverx = ( ev . pageX - getElementXOffsetFromPage ( CineraContent ) ) / CineraContent . offsetWidth ;
}
}
} ;
Player . prototype . onMarkerMouseEnter = function ( marker , ev ) {
if ( ! marker . el . classList . contains ( "skip" ) )
{
if ( this . MenusFocused . MenuID == menu _id . UNSET || this . MenusFocused . MenuID == menu _id . MARKERS )
{
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , marker . el ) ;
}
2017-03-10 14:19:25 +00:00
}
} ;
Player . prototype . onMarkerMouseLeave = function ( marker , ev ) {
2023-03-18 01:27:05 +00:00
if ( ! marker . el . classList . contains ( "skip" ) )
{
marker . hoverx = null ;
var CurrentFocus = this . MenusFocused . MenuID ;
this . unfocusUIElement ( focus _level . ITEM ) ;
if ( CurrentFocus != menu _id . MARKERS )
{
this . MenusFocused . MenuID = CurrentFocus ;
}
}
2017-03-10 14:19:25 +00:00
} ;
2023-03-18 01:27:05 +00:00
function
computeCentreScrollOffset ( container , targetTop , targetBottom )
{
var Result = 0 ;
var Bottom = targetBottom . offsetTop + targetBottom . offsetHeight ;
var Midpoint = ( Bottom - targetTop . offsetTop ) / 2.0 ;
Result += Midpoint - container . offsetHeight / 2.0
return Result ;
}
Player . prototype . computeDesiredScrollTo = function ( MenuEntity , targetTop , targetBottom , centre )
{
MenuEntity . Scroll . To = targetTop . offsetTop ;
var container = MenuEntity . Container ;
if ( centre )
{
MenuEntity . Scroll . To += computeCentreScrollOffset ( container , targetTop , targetBottom ) ;
MenuEntity . Scroll . To = Math . max ( 0 , Math . min ( MenuEntity . Scroll . To , targetTop . offsetTop ) ) ;
}
MenuEntity . Scroll . Position = container . scrollTop ;
}
Player . prototype . setScrollerRanged = function ( MenuEntity , targetTop , targetBottom , centre , calledEveryFrame ) {
if ( ! calledEveryFrame || this . desiredTime >= 0 )
{
this . computeDesiredScrollTo ( MenuEntity , targetTop , targetBottom , centre ) ;
}
}
Player . prototype . setScroller = function ( MenuEntity , element , centre , calledEveryFrame ) {
if ( ! calledEveryFrame || this . desiredTime >= 0 )
{
this . computeDesiredScrollTo ( MenuEntity , element , element , centre ) ;
}
}
2017-03-10 14:19:25 +00:00
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 )
{
2023-03-18 01:27:05 +00:00
localStorage . removeItem ( this . lastTimestampStorageItem ) ;
2018-04-17 23:05:14 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
localStorage . setItem ( this . 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" ) ;
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . currentMarker . el , true , true ) ;
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 ) ;
}
}
2023-03-18 01:27:05 +00:00
}
Player . prototype . smoothScroll = function ( MenuEntity )
{
var container = MenuEntity . Container ;
var scroller = MenuEntity . Scroll ;
var targetPosition = scroller . To ;
targetPosition = Math . max ( 0 , Math . min ( targetPosition , container . scrollHeight - container . offsetHeight ) ) ;
scroller . Position += ( targetPosition - scroller . Position ) * 0.1 ;
if ( Math . abs ( scroller . Position - targetPosition ) < 1.0 ) {
container . scrollTop = targetPosition ;
scroller . To = - 1 ;
} else {
container . scrollTop = scroller . Position ;
}
}
2017-03-10 14:19:25 +00:00
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 ;
2023-03-18 01:27:05 +00:00
if ( this . desiredTime == - 1 ) { this . desiredTime = this . currentTime ; }
2021-07-07 15:33:54 +00:00
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 ;
2023-03-18 01:54:42 +00:00
if ( Parent . desiredTime == - 1 ) { Parent . desiredTime = Parent . currentTime ; }
2021-06-23 14:13:41 +00:00
Parent . updateProgress ( ) ;
} ) ;
} break ;
case vod _platform . YOUTUBE :
{
this . currentTime = this . platformPlayer . getCurrentTime ( ) ;
2023-03-18 01:27:05 +00:00
if ( this . desiredTime == - 1 ) { this . desiredTime = this . currentTime ; }
2021-06-23 14:13:41 +00:00
this . updateProgress ( ) ;
} break ;
}
2017-03-10 14:19:25 +00:00
}
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . MARKERS ] . Scroll . To >= 0 ) {
this . smoothScroll ( this . Menus [ menu _id . MARKERS ] ) ;
2017-03-10 14:19:25 +00:00
}
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . QUOTES ] . Container && this . Menus [ menu _id . QUOTES ] . Scroll . To >= 0 ) {
this . smoothScroll ( this . Menus [ menu _id . QUOTES ] ) ;
}
if ( this . Menus [ menu _id . REFERENCES ] . Container && this . Menus [ menu _id . REFERENCES ] . Scroll . To >= 0 ) {
this . smoothScroll ( this . Menus [ menu _id . REFERENCES ] ) ;
}
if ( this . Menus [ menu _id . FILTER ] . Container && this . Menus [ menu _id . FILTER ] . Scroll . To >= 0 ) {
this . smoothScroll ( this . Menus [ menu _id . FILTER ] ) ;
2023-02-20 20:29:16 +00:00
}
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . CREDITS ] . Container && this . Menus [ menu _id . CREDITS ] . Scroll . To >= 0 ) {
this . smoothScroll ( this . Menus [ menu _id . CREDITS ] ) ;
}
if ( this . desiredTime == this . currentTime ) { this . desiredTime = - 1 ; }
2017-03-10 14:19:25 +00:00
this . nextFrame = requestAnimationFrame ( this . doFrame . bind ( this ) ) ;
2023-03-18 01:27:05 +00:00
this . 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 ;
2023-03-18 01:27:05 +00:00
if ( this . desiredTime > 0 ) {
this . desiredTime = Math . max ( 0 , Math . min ( this . desiredTime , this . duration ) ) ;
this . setTimeThenPlay ( this . desiredTime ) ;
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 ;
2023-03-18 01:27:05 +00:00
localStorage . removeItem ( this . 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" ;
2023-03-21 19:34:24 +00:00
if ( ! CineraProps . IsMobile )
{
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
2023-03-18 01:27:05 +00:00
Player . prototype . toggleFilterMode = function ( ) {
if ( this . filterMode == "inclusive" )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . filterModeElement . classList . remove ( "inclusive" ) ;
this . filterModeElement . classList . add ( "exclusive" ) ;
this . filterMode = "exclusive" ;
2018-05-22 21:43:59 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . filterModeElement . classList . remove ( "exclusive" ) ;
this . filterModeElement . classList . add ( "inclusive" ) ;
this . filterMode = "inclusive" ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
this . applyFilter ( ) ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . updateLink = function ( )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . link )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . linkTimestamp == true )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . currentMarker )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . link . value = baseURL + "#" + this . currentMarker . timestamp ;
2018-05-22 21:43:59 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . link . value = baseURL ;
2018-05-22 21:43:59 +00:00
}
}
else
{
2023-03-18 01:27:05 +00:00
switch ( this . vod _platform )
2021-06-23 14:13:41 +00:00
{
2021-07-07 15:33:54 +00:00
case vod _platform . DIRECT :
{
2023-03-18 01:27:05 +00:00
this . link . value = baseURL + "#" + Math . round ( this . platformPlayer . currentTime ) ;
2021-07-07 15:33:54 +00:00
} break ;
2021-06-23 14:13:41 +00:00
case vod _platform . VIMEO :
{
2023-03-25 01:31:13 +00:00
var Parent = this ;
2023-03-18 01:27:05 +00:00
this . platformPlayer . getCurrentTime ( )
2021-06-23 14:13:41 +00:00
. then ( function ( Response )
{
2023-03-25 01:31:13 +00:00
Parent . link . value = baseURL + "#" + Math . round ( Response ) ;
2021-06-23 14:13:41 +00:00
} ) ;
} break ;
case vod _platform . YOUTUBE :
{
2023-03-18 01:27:05 +00:00
this . link . value = baseURL + "#" + Math . round ( this . platformPlayer . getCurrentTime ( ) ) ;
2021-06-23 14:13:41 +00:00
} break ;
}
2018-05-22 21:43:59 +00:00
}
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . toggleLinkMode = function ( )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . linkTimestamp = ! this . linkTimestamp ;
if ( this . linkTimestamp == true )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . linkMode . textContent = "Link to: current timestamp" ;
2018-05-22 21:43:59 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . linkMode . textContent = "Link to: nearest second" ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
this . updateLink ( ) ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . toggleFilterOrLinkMode = function ( )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . FILTER :
{
this . toggleFilterMode ( ) ;
} break ;
case menu _id . LINK :
{
this . toggleLinkMode ( ) ;
} break ;
2018-05-22 21:43:59 +00:00
}
}
2023-03-18 01:27:05 +00:00
function HideMenu ( MenuContainer )
{
if ( MenuContainer != null )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
MenuContainer . classList . remove ( "visible" ) ;
MenuContainer . parentNode . classList . remove ( "visible" ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
}
function ShowMenu ( MenuContainer )
{
if ( MenuContainer != null )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
MenuContainer . classList . add ( "visible" ) ;
MenuContainer . parentNode . classList . add ( "visible" ) ;
}
}
Player . prototype . toggleMenuVisibility = function ( MenuID , Trigger ) {
var element = this . Menus [ MenuID ] . Container ;
if ( this . MenusFocused . Item )
{
this . unfocusUIElement ( focus _level . ITEM ) ;
}
if ( this . MenusFocused . Identifier )
{
this . unfocusUIElement ( focus _level . IDENTIFIER ) ;
}
if ( element . classList . contains ( "visible" ) )
{
HideMenu ( element ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
if ( Trigger == trigger _id . KEYBOARD && this . Menus [ menu _id . MARKERS ] . Item . LastFocused )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Best = this . Menus [ menu _id . MARKERS ] . Item . LastFocused ;
while ( Best . classList . contains ( "skip" ) && Best . nextElementSibling )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = Best . nextElementSibling ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
if ( ! Best . classList . contains ( "skip" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , Best ) ;
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . MenusFocused . Item , true , false ) ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
}
else
{
this . MenusFocused . MenuID = MenuID ;
for ( var i in this . Menus )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
HideMenu ( this . Menus [ i ] . Container ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
ShowMenu ( element ) ;
switch ( MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . QUOTES :
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ! this . Menus [ menu _id . QUOTES ] . Item . LastFocused || ! this . Menus [ menu _id . QUOTES ] . Identifier . LastFocused )
{
this . Menus [ menu _id . QUOTES ] . Item . LastFocused = element . querySelector ( ".ref" ) ;
this . Menus [ menu _id . QUOTES ] . Identifier . LastFocused = this . Menus [ menu _id . QUOTES ] . Item . LastFocused . querySelector ( ".ref_indices" ) . firstElementChild ;
}
this . focusUIElement ( focus _level . ITEM , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Item , this . Menus [ menu _id . QUOTES ] . Item . LastFocused ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Identifier , this . Menus [ menu _id . QUOTES ] . Identifier . LastFocused ) ;
} break ;
case menu _id . REFERENCES :
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ! this . Menus [ menu _id . REFERENCES ] . Item . LastFocused || ! this . Menus [ menu _id . REFERENCES ] . Identifier . LastFocused )
{
this . Menus [ menu _id . REFERENCES ] . Item . LastFocused = element . querySelector ( ".ref" ) ;
this . Menus [ menu _id . REFERENCES ] . Identifier . LastFocused = this . Menus [ menu _id . REFERENCES ] . Item . LastFocused . querySelector ( ".ref_indices" ) . firstElementChild ;
}
this . focusUIElement ( focus _level . ITEM , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Item , this . Menus [ menu _id . REFERENCES ] . Item . LastFocused ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , this . Menus [ menu _id . REFERENCES ] . Identifier . LastFocused ) ;
} break ;
case menu _id . FILTER :
{
if ( ! this . Menus [ menu _id . FILTER ] . Category . LastFocused )
{
this . Menus [ menu _id . FILTER ] . Category . LastFocused = element . querySelector ( ".filter_content" ) ;
}
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . Menus [ menu _id . FILTER ] . Category . LastFocused ) ;
} break ;
case menu _id . CREDITS :
{
if ( ! this . Menus [ menu _id . CREDITS ] . Item . LastFocused )
{
if ( element . querySelector ( ".credit .person" ) . nextElementSibling )
{
this . Menus [ menu _id . CREDITS ] . Item . LastFocused = element . querySelector ( ".credit .support" ) ;
}
else
{
this . Menus [ menu _id . CREDITS ] . Item . LastFocused = element . querySelector ( ".credit .person" ) ;
}
}
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . Menus [ menu _id . CREDITS ] . Item . LastFocused ) ;
} break ;
2017-05-31 00:21:21 +00:00
}
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . handleMouseOverViewsMenu = function ( )
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 :
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . VIEWS ] . Container . classList . add ( "visible" ) ;
2018-01-17 20:15:00 +00:00
} break ;
case views . SUPERTHEATRE :
{
} break ;
}
2023-03-18 01:27:05 +00:00
this . MenusFocused . MenuID = menu _id . VIEWS ;
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
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . toggleTheatreMode = function ( ) {
if ( ! CineraProps . IsMobile )
2018-01-15 21:52:24 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( CineraProps . V )
{
case views . REGULAR :
{
CineraProps . C = this . root . style . backgroundColor ;
CineraProps . Z = this . root . style . zIndex ;
CineraProps . X = this . root . style . left ;
CineraProps . Y = this . root . style . top ;
CineraProps . W = this . root . style . width ;
CineraProps . mW = this . root . style . maxWidth ;
CineraProps . H = this . root . style . height ;
CineraProps . mH = this . root . style . maxHeight ;
CineraProps . P = this . root . style . position ;
CineraProps . Display = this . root . style . display ;
CineraProps . FlexDirection = this . root . style . flexDirection ;
CineraProps . JustifyContent = this . root . style . justifyContent ;
CineraProps . ScrollX = window . scrollX ;
CineraProps . ScrollY = window . scrollY ;
this . root . style . backgroundColor = "#000" ;
this . root . style . zIndex = 64 ;
this . root . style . left = 0 ;
this . root . style . top = 0 ;
this . root . style . width = "100%" ;
this . root . style . maxWidth = "100%" ;
this . root . style . height = "100%" ;
this . root . style . maxHeight = "100%" ;
this . root . style . position = "fixed" ;
this . root . style . display = "flex" ;
this . root . style . flexDirection = "column" ;
this . root . style . justifyContent = "center" ;
this . viewItems [ 0 ] . setAttribute ( "data-id" , "regular" ) ;
this . viewItems [ 0 ] . setAttribute ( "title" , "Regular mode" ) ;
this . viewItems [ 0 ] . firstChild . nodeValue = "📺" ;
} CineraProps . V = views . THEATRE ; localStorage . setItem ( this . cineraViewStorageItem , views . THEATRE ) ; break ;
case views . SUPERTHEATRE :
{
leaveFullScreen _ ( ) ;
}
case views . THEATRE :
{
this . root . style . backgroundColor = CineraProps . C ;
this . root . style . zIndex = CineraProps . Z ;
this . root . style . left = CineraProps . X ;
this . root . style . top = CineraProps . Y ;
this . root . style . width = CineraProps . W ;
this . root . style . maxWidth = CineraProps . mW ;
this . root . style . height = CineraProps . H ;
this . root . style . maxHeight = CineraProps . mH ;
this . root . style . position = CineraProps . P ;
this . root . style . display = CineraProps . Display ;
this . root . style . flexDirection = CineraProps . FlexDirection ;
this . root . style . justifyContent = CineraProps . JustifyContent ;
window . scroll (
{
top : CineraProps . ScrollY ,
left : CineraProps . ScrollX
}
) ;
2018-01-17 20:15:00 +00:00
2023-03-18 01:27:05 +00:00
this . viewItems [ 0 ] . setAttribute ( "data-id" , "theatre" ) ;
this . viewItems [ 0 ] . setAttribute ( "title" , "Theatre mode" ) ;
this . viewItems [ 0 ] . firstChild . nodeValue = "🎭" ;
} CineraProps . V = views . REGULAR ; localStorage . removeItem ( this . cineraViewStorageItem ) ; break ;
}
this . updateSize ( ) ;
2018-01-15 21:52:24 +00:00
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . toggleSuperTheatreMode = function ( )
2018-01-15 21:52:24 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ! CineraProps . IsMobile )
2018-01-15 21:52:24 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( CineraProps . V )
{
case views . REGULAR :
{
this . toggleTheatreMode ( ) ;
}
case views . THEATRE :
{
enterFullScreen _ ( ) ;
} CineraProps . V = views . SUPERTHEATRE ; localStorage . setItem ( this . cineraViewStorageItem , views . SUPERTHEATRE ) ; break ;
case views . SUPERTHEATRE :
{
leaveFullScreen _ ( ) ;
this . toggleTheatreMode ( ) ;
} CineraProps . V = views . REGULAR ; localStorage . removeItem ( this . cineraViewStorageItem ) ; break ;
}
this . updateSize ( ) ;
2018-01-15 21:52:24 +00:00
}
}
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 ( ) ;
}
2023-03-18 01:27:05 +00:00
Player . prototype . CopyToClipboard = function ( inputElement )
2018-05-22 21:43:59 +00:00
{
SelectText ( inputElement ) ;
document . execCommand ( "copy" ) ;
2023-03-18 01:27:05 +00:00
AscribeTemporaryResponsibility ( this . Menus [ menu _id . LINK ] . Container . parentNode , 8000 ) ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . unfocusUIElement = function ( FocusLevel )
{
switch ( FocusLevel )
{
case focus _level . ITEM :
{
if ( this . MenusFocused . Item )
{
this . MenusFocused . Item . classList . remove ( "focused" ) ;
unfocusSprite ( this . MenusFocused . Item ) ;
}
this . MenusFocused . Item = null ;
} break ;
case focus _level . IDENTIFIER :
{
if ( this . MenusFocused . Identifier )
{
this . MenusFocused . Identifier . classList . remove ( "focused" ) ;
unfocusSprite ( this . MenusFocused . Identifier ) ;
}
this . MenusFocused . Identifier = null ;
} break ;
}
this . MenusFocused . MenuID = menu _id . UNSET ;
}
Player . prototype . focusUIElement = function ( FocusLevel , MenuID , MenuFocalPoint , newElement )
{
switch ( FocusLevel )
{
case focus _level . ITEM :
{
if ( this . MenusFocused . Item )
{
this . MenusFocused . Item . classList . remove ( "focused" ) ;
unfocusSprite ( this . MenusFocused . Item ) ;
}
this . MenusFocused . Item = newElement ;
} break ;
case focus _level . IDENTIFIER :
{
if ( this . MenusFocused . Identifier )
{
this . MenusFocused . Identifier . classList . remove ( "focused" ) ;
unfocusSprite ( this . MenusFocused . Identifier ) ;
}
this . MenusFocused . Identifier = newElement ;
} break ;
}
this . MenusFocused . MenuID = MenuID ;
newElement . classList . add ( "focused" ) ;
focusSprite ( newElement ) ;
MenuFocalPoint . LastFocused = newElement ;
}
function
getMostRecentCitation ( currentTime , citations )
{
var Result = citations [ 0 ] ;
for ( var i = 0 ; i < citations . length ; ++ i )
{
var citation = citations [ i ] ;
if ( citation . getAttribute ( "data-timestamp" ) <= currentTime )
{
Result = citation ;
}
else { break ; }
}
return Result ;
}
Player . prototype . handleKey = function ( key ) {
2017-05-31 00:21:21 +00:00
var gotKey = true ;
switch ( key ) {
2023-03-18 01:27:05 +00:00
case "Escape" : {
switch ( this . MenusFocused . MenuID )
{
case menu _id . MARKERS :
{
this . unfocusUIElement ( focus _level . ITEM ) ;
if ( this . currentMarker && this . currentMarker . el )
{
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . currentMarker . el , true , false ) ;
}
this . Menus [ menu _id . MARKERS ] . Item . LastFocused = null ;
} break ;
case menu _id . QUOTES :
case menu _id . REFERENCES :
case menu _id . FILTER :
case menu _id . VIEWS :
case menu _id . LINK :
case menu _id . CREDITS :
{
this . toggleMenuVisibility ( this . MenusFocused . MenuID , trigger _id . KEYBOARD ) ;
} break ;
}
} break ;
2017-05-31 00:21:21 +00:00
case "q" : {
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . QUOTES ] . Container )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . QUOTES , trigger _id . KEYBOARD ) ;
2017-05-31 00:21:21 +00:00
}
} break ;
case "r" : {
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . REFERENCES ] . Container )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . REFERENCES , trigger _id . KEYBOARD ) ;
2017-05-31 00:21:21 +00:00
}
} break ;
case "f" : {
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . FILTER ] . Container )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . FILTER , trigger _id . KEYBOARD ) ;
2017-05-31 00:21:21 +00:00
}
} break ;
2018-05-22 21:43:59 +00:00
case "y" : {
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . LINK ] . Container )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . LINK , trigger _id . KEYBOARD ) ;
2018-05-22 21:43:59 +00:00
}
break ;
}
2017-05-31 00:21:21 +00:00
case "c" : {
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . CREDITS ] . Container )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . CREDITS , trigger _id . KEYBOARD ) ;
2017-05-31 00:21:21 +00:00
}
} break ;
2018-01-15 21:52:24 +00:00
case "t" : {
2023-03-18 01:27:05 +00:00
if ( this . root )
2018-01-15 21:52:24 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleTheatreMode ( ) ;
2018-01-15 21:52:24 +00:00
}
} break ;
case "T" : {
2023-03-18 01:27:05 +00:00
if ( this . root )
2018-01-15 21:52:24 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleSuperTheatreMode ( ) ;
2018-01-15 21:52:24 +00:00
}
} break ;
2017-05-31 00:21:21 +00:00
case "Enter" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . MARKERS :
{
var time = this . MenusFocused . Item . getAttribute ( "data-timestamp" ) ;
2023-03-25 00:04:34 +00:00
this . setTimeThenPlay ( parseFloat ( time ) ) ;
2023-03-18 01:27:05 +00:00
} break ;
case menu _id . QUOTES :
{
var time = this . MenusFocused . Item . querySelector ( ".timecode" ) . getAttribute ( "data-timestamp" ) ;
2023-03-25 00:04:34 +00:00
this . setTimeThenPlay ( parseFloat ( time ) ) ;
2023-03-18 01:27:05 +00:00
if ( this . currentMarker )
{
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . currentMarker . el , true , false ) ;
}
this . Menus [ menu _id . MARKERS ] . Item . LastFocused = null ;
} break ;
case menu _id . REFERENCES :
{
var time = this . MenusFocused . Identifier . getAttribute ( "data-timestamp" ) ;
2023-03-25 00:04:34 +00:00
this . setTimeThenPlay ( parseFloat ( time ) ) ;
2023-03-18 01:27:05 +00:00
if ( this . currentMarker )
{
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . currentMarker . el , true , false ) ;
}
this . Menus [ menu _id . MARKERS ] . Item . LastFocused = null ;
} break ;
case menu _id . CREDITS :
{
if ( this . MenusFocused . Item . hasAttribute )
{
var url = this . MenusFocused . Item . getAttribute ( "href" ) ;
if ( url ) { window . open ( url , "_blank" ) ; }
}
} break ;
2017-05-31 00:21:21 +00:00
}
}
else
{
2023-03-18 01:27:05 +00:00
if ( this . currentMarker && this . currentMarker . el )
{
var time = this . currentMarker . el . getAttribute ( "data-timestamp" ) ;
2023-03-25 00:04:34 +00:00
this . setTimeThenPlay ( parseFloat ( time ) ) ;
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . currentMarker . el , true , false ) ;
}
2017-05-31 00:21:21 +00:00
}
} break ;
case "o" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . REFERENCES :
case menu _id . QUOTES :
{
this . pause ( ) ;
var url = this . MenusFocused . Item . getAttribute ( "href" ) ;
window . open ( url , "_blank" ) ;
} break ;
case menu _id . CREDITS :
{
if ( this . MenusFocused . Item . hasAttribute ( "href" ) )
{
this . pause ( ) ;
var url = this . MenusFocused . Item . getAttribute ( "href" ) ;
window . open ( url , "_blank" ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "w" : case "k" : case "ArrowUp" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . MARKERS :
{
if ( key != "ArrowUp" )
{
if ( this . MenusFocused . Item . previousElementSibling )
{
var Best = this . MenusFocused . Item . previousElementSibling ;
while ( Best . classList . contains ( "skip" ) && Best . previousElementSibling )
{
Best = Best . previousElementSibling ;
}
if ( ! Best . classList . contains ( "skip" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , Best ) ;
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . MenusFocused . Item , true , false ) ;
}
}
}
} break ;
case menu _id . QUOTES :
{
if ( this . MenusFocused . Item . previousElementSibling )
{
this . focusUIElement ( focus _level . ITEM , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Item , this . MenusFocused . Item . previousElementSibling ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Identifier , this . MenusFocused . Item . querySelector ( ".ref_indices" ) . firstElementChild ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . QUOTES ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . REFERENCES :
{
if ( this . MenusFocused . Item . previousElementSibling )
{
this . focusUIElement ( focus _level . ITEM , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Item , this . MenusFocused . Item . previousElementSibling ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , getMostRecentCitation ( this . currentTime , this . MenusFocused . Item . querySelector ( ".ref_indices" ) . children ) ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . REFERENCES ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . FILTER :
{
if ( this . MenusFocused . Item . previousElementSibling &&
this . MenusFocused . Item . previousElementSibling . classList . contains ( "filter_content" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . MenusFocused . Item . previousElementSibling ) ;
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . CREDITS :
{
if ( this . MenusFocused . Item . parentNode . previousElementSibling )
{
if ( this . MenusFocused . Item . parentNode . previousElementSibling . querySelector ( ".support" ) &&
this . MenusFocused . Item . classList . contains ( "support" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . parentNode . previousElementSibling . querySelector ( ".support" ) ) ;
}
else
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . parentNode . previousElementSibling . querySelector ( ".person" ) ) ;
}
this . setScroller ( this . Menus [ menu _id . CREDITS ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
}
else
{
if ( key != "ArrowUp" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Best ;
if ( this . currentMarker && this . currentMarker . el )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = this . currentMarker . el ;
if ( Best . previousElementSibling )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = Best . previousElementSibling ;
while ( Best . classList . contains ( "skip" ) && Best . previousElementSibling )
{
Best = Best . previousElementSibling ;
}
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
}
else
{
Best = this . markers [ 0 ] . el ;
while ( Best . classList . contains ( "skip" ) && Best . nextElementSibling )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = Best . nextElementSibling ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
if ( ! Best . classList . contains ( "skip" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , Best ) ;
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . MenusFocused . Item , true , false ) ;
}
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "s" : case "j" : case "ArrowDown" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . MARKERS :
{
if ( key != "ArrowDown" )
{
if ( this . MenusFocused . Item . nextElementSibling )
{
var Best = this . MenusFocused . Item . nextElementSibling ;
while ( Best . classList . contains ( "skip" ) && Best . nextElementSibling )
{
Best = Best . nextElementSibling ;
}
if ( ! Best . classList . contains ( "skip" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , Best ) ;
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . MenusFocused . Item , true , false ) ;
}
}
}
} break ;
case menu _id . QUOTES :
{
if ( this . MenusFocused . Item . nextElementSibling )
{
this . focusUIElement ( focus _level . ITEM , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Item , this . MenusFocused . Item . nextElementSibling ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . QUOTES , this . Menus [ menu _id . QUOTES ] . Identifier , this . MenusFocused . Item . querySelector ( ".ref_indices" ) . firstElementChild ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . QUOTES ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . REFERENCES :
{
if ( this . MenusFocused . Item . nextElementSibling )
{
this . focusUIElement ( focus _level . ITEM , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Item , this . MenusFocused . Item . nextElementSibling ) ;
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , getMostRecentCitation ( this . currentTime , this . MenusFocused . Item . querySelector ( ".ref_indices" ) . children ) ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . REFERENCES ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . FILTER :
{
if ( this . MenusFocused . Item . nextElementSibling &&
this . MenusFocused . Item . nextElementSibling . classList . contains ( "filter_content" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . MenusFocused . Item . nextElementSibling ) ;
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . CREDITS :
{
if ( this . MenusFocused . Item . parentNode . nextElementSibling )
{
if ( this . MenusFocused . Item . parentNode . nextElementSibling . querySelector ( ".support" ) &&
this . MenusFocused . Item . classList . contains ( "support" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . parentNode . nextElementSibling . querySelector ( ".support" ) ) ;
}
else
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . parentNode . nextElementSibling . querySelector ( ".person" ) ) ;
}
this . setScroller ( this . Menus [ menu _id . CREDITS ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
}
else
{
if ( key != "ArrowDown" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Best ;
if ( this . currentMarker && this . currentMarker . el )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = this . currentMarker . el ;
if ( Best . nextElementSibling )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = Best . nextElementSibling ;
while ( Best . classList . contains ( "skip" ) && Best . nextElementSibling )
{
Best = Best . nextElementSibling ;
}
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
}
else
{
Best = this . markers [ 0 ] . el ;
while ( Best . classList . contains ( "skip" ) && Best . nextElementSibling )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Best = Best . nextElementSibling ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
if ( ! Best . classList . contains ( "skip" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . MARKERS , this . Menus [ menu _id . MARKERS ] . Item , Best ) ;
this . setScroller ( this . Menus [ menu _id . MARKERS ] , this . MenusFocused . Item , true , false ) ;
}
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "a" : case "h" : case "ArrowLeft" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . REFERENCES :
{
if ( this . MenusFocused . Identifier . previousElementSibling )
{
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , this . MenusFocused . Identifier . previousElementSibling ) ;
}
else if ( this . MenusFocused . Identifier . parentNode . previousElementSibling . classList . contains ( "ref_indices" ) )
{
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , this . MenusFocused . Identifier . parentNode . previousElementSibling . lastElementChild ) ;
}
} break ;
case menu _id . FILTER :
{
if ( this . MenusFocused . Item . parentNode . classList . contains ( "filter_media" ) &&
this . MenusFocused . Item . parentNode . previousElementSibling )
{
this . Menus [ menu _id . FILTER ] . Medium . LastFocused = this . MenusFocused . Item ;
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . Menus [ menu _id . FILTER ] . Topic . LastFocused || this . MenusFocused . Item . parentNode . previousElementSibling . children [ 0 ] ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . CREDITS :
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item . classList . contains ( "support" ) )
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . previousElementSibling ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "d" : case "l" : case "ArrowRight" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
switch ( this . MenusFocused . MenuID )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
case menu _id . REFERENCES :
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Identifier . nextElementSibling )
{
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , this . MenusFocused . Identifier . nextElementSibling ) ;
}
else if ( this . MenusFocused . Identifier . parentNode . nextElementSibling )
{
this . focusUIElement ( focus _level . IDENTIFIER , menu _id . REFERENCES , this . Menus [ menu _id . REFERENCES ] . Identifier , this . MenusFocused . Identifier . parentNode . nextElementSibling . firstElementChild ) ;
}
} break ;
case menu _id . FILTER :
{
if ( this . MenusFocused . Item . parentNode . classList . contains ( "filter_topics" ) &&
this . MenusFocused . Item . parentNode . nextElementSibling )
{
this . Menus [ menu _id . FILTER ] . Topic . LastFocused = this . MenusFocused . Item ;
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . Menus [ menu _id . FILTER ] . Medium . LastFocused || this . MenusFocused . Item . parentNode . nextElementSibling . children [ 0 ] ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
}
} break ;
case menu _id . CREDITS :
{
if ( this . MenusFocused . Item . classList . contains ( "person" ) &&
this . MenusFocused . Item . nextElementSibling )
{
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , this . MenusFocused . Item . nextElementSibling ) ;
}
} break ;
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "x" : case " " : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item && this . MenusFocused . MenuID == menu _id . FILTER )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . filterItemToggle ( this . MenusFocused . Item ) ;
if ( this . MenusFocused . Item . nextElementSibling &&
this . MenusFocused . Item . nextElementSibling . classList . contains ( "filter_content" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . MenusFocused . Item . nextElementSibling ) ;
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
if ( this . MenusFocused . Item . parentNode . classList . contains ( "filter_topics" ) )
2017-06-11 22:49:04 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . FILTER ] . Topic . LastFocused = this . MenusFocused . Item ;
2017-06-11 22:49:04 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . FILTER ] . Medium . LastFocused = this . MenusFocused . Item ;
2017-06-11 22:49:04 +00:00
}
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "X" : case "capitalSpace" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item && this . MenusFocused . MenuID == menu _id . FILTER )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . filterItemToggle ( this . MenusFocused . Item ) ;
if ( this . MenusFocused . Item . previousElementSibling &&
this . MenusFocused . Item . previousElementSibling . classList . contains ( "filter_content" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , this . MenusFocused . Item . previousElementSibling ) ;
this . setScroller ( this . Menus [ menu _id . FILTER ] , this . MenusFocused . Item , true , false ) ;
if ( this . MenusFocused . Item . parentNode . classList . contains ( "filter_topics" ) )
2017-06-11 22:49:04 +00:00
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . FILTER ] . Topic . LastFocused = this . MenusFocused . Item ;
2017-06-11 22:49:04 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
this . Menus [ menu _id . FILTER ] . Medium . LastFocused = this . MenusFocused . Item ;
2017-06-11 22:49:04 +00:00
}
2017-05-31 00:21:21 +00:00
}
}
} break ;
case "z" : {
2023-03-18 01:27:05 +00:00
this . toggleFilterOrLinkMode ( ) ;
2017-05-31 00:21:21 +00:00
} break ;
case "v" : {
2023-03-18 01:27:05 +00:00
if ( this . MenusFocused . Item && this . MenusFocused . MenuID == menu _id . FILTER )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . invertFilter ( this . MenusFocused . Item )
2017-05-31 00:21:21 +00:00
}
} break ;
case "V" : {
2023-03-18 01:27:05 +00:00
this . resetFilter ( ) ;
2017-05-31 00:21:21 +00:00
} break ;
case "?" : {
2023-03-18 01:27:05 +00:00
if ( this . helpDocumentation )
2021-01-25 18:09:30 +00:00
{
2023-03-18 01:27:05 +00:00
this . helpDocumentation . classList . toggle ( "visible" ) ;
2021-01-25 18:09:30 +00:00
}
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' : {
2023-03-18 01:27:05 +00:00
this . jumpToNextMarker ( ) ;
2017-05-31 00:21:21 +00:00
} break ;
case 'P' :
2018-02-28 01:04:06 +00:00
case 'K' :
2017-05-31 00:21:21 +00:00
case 'W' : {
2023-03-18 01:27:05 +00:00
this . jumpToPrevMarker ( ) ;
2017-05-31 00:21:21 +00:00
} break ;
2018-02-28 01:04:06 +00:00
case '[' :
case '<' : {
2023-03-18 01:27:05 +00:00
if ( this . prevEpisode )
2018-02-28 01:04:06 +00:00
{
2023-03-18 01:27:05 +00:00
location = this . prevEpisode . href ;
2018-02-28 01:04:06 +00:00
}
} break ;
case ']' :
case '>' : {
2023-03-18 01:27:05 +00:00
if ( this . nextEpisode )
2018-02-28 01:04:06 +00:00
{
2023-03-18 01:27:05 +00:00
location = this . nextEpisode . href ;
2018-02-28 01:04:06 +00:00
}
} break ;
2018-05-22 21:43:59 +00:00
case 'Y' : {
if ( cineraLink )
{
2023-03-18 01:27:05 +00:00
if ( this . linkTimestamp == false && this . playing )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . pause ( ) ;
2018-05-22 21:43:59 +00:00
}
2023-03-18 01:27:05 +00:00
if ( this . Menus [ menu _id . LINK ] . Container && ! this . Menus [ menu _id . LINK ] . Container . classList . contains ( "visible" ) )
2018-05-22 21:43:59 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . LINK , trigger _id . KEYBOARD ) ;
2018-05-22 21:43:59 +00:00
}
SelectText ( cineraLink ) ;
}
}
2017-05-31 00:21:21 +00:00
default : {
gotKey = false ;
} break ;
}
return gotKey ;
}
2023-03-18 01:27:05 +00:00
Player . prototype . applyFilter = function ( ) {
if ( this . filterMode == "exclusive" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
for ( var i = 0 ; i < this . Menus [ menu _id . MARKERS ] . Elements . length ; ++ i )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Item = this . Menus [ menu _id . MARKERS ] . Elements [ i ] ;
var testCategories = Item . classList ;
2017-05-31 00:21:21 +00:00
for ( var j = 0 ; j < testCategories . length ; ++ j )
{
2023-03-18 01:27:05 +00:00
if ( ( testCategories [ j ] . startsWith ( "off_" ) ) && ! Item . classList . contains ( "skip" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Item . classList . add ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
else
{
2023-03-18 01:27:05 +00:00
for ( var i = 0 ; i < this . Menus [ menu _id . MARKERS ] . Elements . length ; ++ i )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Item = this . Menus [ menu _id . MARKERS ] . Elements [ i ] ;
var testCategories = Item . classList ;
2017-05-31 00:21:21 +00:00
for ( var j = 0 ; j < testCategories . length ; ++ j )
{
2023-03-18 01:27:05 +00:00
var testCategory = testCategories [ j ] ;
if ( ( testCategory in this . filterState || testCategory . startsWith ( "cat_" ) ) && Item . classList . contains ( "skip" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Item . classList . remove ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . filterItemToggle = function ( filterItem ) {
2017-05-31 00:21:21 +00:00
var selectedCategory = filterItem . classList [ 1 ] ;
2023-03-18 01:27:05 +00:00
this . filterState [ selectedCategory ] . off = ! this . filterState [ selectedCategory ] . off ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
if ( this . filterState [ selectedCategory ] . off )
2017-05-31 00:21:21 +00:00
{
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" ;
}
2023-03-18 01:27:05 +00:00
var testMarkers = this . Menus [ menu _id . MARKERS ] . Container . querySelectorAll ( ".marker." + selectedCategory + ", .marker.cat_" + selectedCategory ) ;
for ( var i = 0 ; i < testMarkers . length ; ++ i )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var testMarker = testMarkers [ i ] ;
if ( this . filterState [ selectedCategory ] . type == "topic" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( "cat_" + selectedCategory ) ;
testMarker . classList . add ( "off_" + selectedCategory ) ;
var markerCategories = testMarker . querySelectorAll ( ".category." + selectedCategory ) ;
for ( var j = 0 ; j < markerCategories . length ; ++ j )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var markerCategory = markerCategories [ j ] ;
if ( markerCategory . classList . contains ( selectedCategory ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
markerCategory . classList . add ( "off" ) ;
markerCategory . style . backgroundColor = "transparent" ;
2017-05-31 00:21:21 +00:00
}
}
}
else
{
2023-03-18 01:27:05 +00:00
var markerCategories = testMarker . querySelectorAll ( ".categoryMedium." + selectedCategory ) ;
for ( var j = 0 ; j < markerCategories . length ; ++ j )
2018-01-15 00:03:11 +00:00
{
2023-03-18 01:27:05 +00:00
var markerCategory = markerCategories [ j ] ;
if ( markerCategory . classList . contains ( selectedCategory ) )
2018-01-15 00:03:11 +00:00
{
2023-03-18 01:27:05 +00:00
markerCategory . classList . add ( "off" ) ;
disableSprite ( markerCategory ) ;
2018-01-15 00:03:11 +00:00
}
}
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( selectedCategory ) ;
testMarker . classList . add ( "off_" + selectedCategory ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
var Skipping = true ;
if ( this . filterMode == "exclusive" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
testMarker . classList . add ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
var markerClasses = testMarker . classList ;
for ( var j = 0 ; j < markerClasses . length ; ++ j )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var markerClass = markerClasses [ j ] ;
if ( markerClass in this . filterState || markerClass . replace ( /^cat_/ , "" ) in this . filterState )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Skipping = false ;
2017-05-31 00:21:21 +00:00
}
}
if ( Skipping )
{
2023-03-18 01:27:05 +00:00
testMarker . classList . add ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
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" ) ) ;
2023-03-18 01:27:05 +00:00
var testMarkers = this . Menus [ menu _id . MARKERS ] . Container . querySelectorAll ( ".marker.off_" + selectedCategory ) ;
for ( var i = 0 ; i < testMarkers . length ; ++ i )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var testMarker = testMarkers [ i ] ;
if ( this . filterState [ selectedCategory ] . type == "topic" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( "off_" + selectedCategory ) ;
testMarker . classList . add ( "cat_" + selectedCategory ) ;
var markerCategories = testMarker . querySelectorAll ( ".category." + selectedCategory ) ;
for ( var j = 0 ; j < markerCategories . length ; ++ j )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var markerCategory = markerCategories [ j ] ;
if ( markerCategory . classList . contains ( selectedCategory ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
markerCategory . classList . remove ( "off" ) ;
markerCategory . style . backgroundColor = getComputedStyle ( markerCategory ) . getPropertyValue ( "border-color" ) ;
setDotLightness ( markerCategory ) ;
2017-05-31 00:21:21 +00:00
}
}
}
else
{
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( "off_" + selectedCategory ) ;
testMarker . classList . add ( selectedCategory ) ;
var markerCategories = testMarker . querySelectorAll ( ".categoryMedium." + selectedCategory ) ;
for ( var j = 0 ; j < markerCategories . length ; ++ j )
2018-01-15 00:03:11 +00:00
{
2023-03-18 01:27:05 +00:00
var markerCategory = markerCategories [ j ] ;
if ( markerCategory . classList . contains ( selectedCategory ) )
2018-01-15 00:03:11 +00:00
{
2023-03-18 01:27:05 +00:00
markerCategory . classList . remove ( "off" ) ;
enableSprite ( markerCategory ) ;
2018-01-15 00:03:11 +00:00
}
}
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
var Skipping = false ;
if ( this . filterMode == "inclusive" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
else
{
2023-03-18 01:27:05 +00:00
var markerClasses = testMarker . classList ;
for ( var j = 0 ; j < markerClasses . length ; ++ j )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( markerClasses [ j ] . startsWith ( "off_" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
Skipping = true ;
2017-05-31 00:21:21 +00:00
}
}
if ( ! Skipping )
{
2023-03-18 01:27:05 +00:00
testMarker . classList . remove ( "skip" ) ;
2017-05-31 00:21:21 +00:00
}
}
}
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . resetFilter = function ( ) {
for ( var i in this . Menus [ menu _id . FILTER ] . Elements )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var Item = this . Menus [ menu _id . FILTER ] . Elements [ i ] ;
if ( Item . classList )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var selectedCategory = Item . classList [ 1 ] ;
if ( this . filterInitState [ selectedCategory ] . off ^ this . filterState [ selectedCategory ] . off )
2017-11-11 00:34:47 +00:00
{
2023-03-18 01:27:05 +00:00
this . filterItemToggle ( Item ) ;
2017-11-11 00:34:47 +00:00
}
2017-05-31 00:21:21 +00:00
}
}
2017-11-11 00:34:47 +00:00
2023-03-18 01:27:05 +00:00
if ( this . filterMode == "inclusive" )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . toggleFilterMode ( ) ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . invertFilter = function ( FocusedElement ) {
var siblings = player . MenusFocused . Item . parentNode . querySelectorAll ( ".filter_content" ) ;
for ( var i in siblings )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
var sibling = siblings [ i ] ;
if ( sibling . classList )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . filterItemToggle ( sibling ) ;
2017-05-31 00:21:21 +00:00
}
}
}
2023-03-18 01:27:05 +00:00
function resetFade ( player ) {
player . filter . classList . remove ( "responsible" ) ;
player . filter . querySelector ( ".filter_mode" ) . classList . remove ( "responsible" ) ;
var responsibleCategories = player . filter . querySelectorAll ( ".filter_content.responsible" ) ;
2017-05-31 00:21:21 +00:00
for ( var i = 0 ; i < responsibleCategories . length ; ++ i )
{
responsibleCategories [ i ] . classList . remove ( "responsible" ) ;
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . alignMenuWithTimestamp = function ( ref , element , MenuID )
{
var MenuEntity = this . Menus [ MenuID ] ;
var Container = MenuEntity . Container ;
if ( Container )
{
var Toggler = Container . closest ( ".menu" ) ;
var SetMenu = 0 ;
if ( ref !== undefined && ref !== null ) {
var refs = ref . split ( "," ) ;
var TargetTop = null ;
var TargetBottom = null ;
for ( var i = 0 ; i < MenuEntity . Elements . length ; ++ i ) {
var thisRef = MenuEntity . Elements [ i ] ;
if ( refs . includes ( thisRef . getAttribute ( "data-id" ) ) ) {
thisRef . classList . add ( "current" ) ;
if ( ! SetMenu )
{
if ( this . MenusFocused . MenuID == MenuID )
{
this . focusUIElement ( focus _level . ITEM , MenuID , MenuEntity . Item , thisRef ) ;
}
else
{
MenuEntity . Item . LastFocused = thisRef ;
}
TargetTop = thisRef ;
var timecode = element . getAttribute ( "data-timestamp" ) ;
var ourIdentifiers = thisRef . querySelectorAll ( ".timecode" ) ;
for ( var j = 0 ; j < ourIdentifiers . length ; ++ j )
{
var thisIdentifier = ourIdentifiers [ j ] ;
if ( timecode == thisIdentifier . getAttribute ( "data-timestamp" ) )
{
if ( this . MenusFocused . MenuID == MenuID )
{
this . focusUIElement ( focus _level . IDENTIFIER , MenuID , MenuEntity . Identifier , thisIdentifier ) ;
}
else
{
MenuEntity . Identifier . LastFocused = thisIdentifier ;
}
break ;
}
}
}
TargetBottom = thisRef ;
SetMenu = 1 ;
} else {
thisRef . classList . remove ( "current" ) ;
}
}
if ( SetMenu ) {
Toggler . classList . add ( "current" ) ;
var CentreAlign = true ;
if ( ! Container . classList . contains ( "visible" ) )
{
Container . scrollTop = this . computeDesiredScrollTo ( MenuEntity , TargetTop , TargetBottom , CentreAlign ) ;
}
else
{
this . setScrollerRanged ( MenuEntity , TargetTop , TargetBottom , CentreAlign , true ) ;
}
} else {
Toggler . classList . remove ( "current" ) ;
}
} else {
Toggler . classList . remove ( "current" ) ;
for ( var i = 0 ; i < MenuEntity . Elements . length ; ++ i ) {
MenuEntity . Elements [ i ] . classList . remove ( "current" ) ;
}
}
}
}
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 ;
2023-03-18 01:27:05 +00:00
if ( ! player . filter ) { console . log ( "Missing filter_container div" ) ; ErrorCount ++ ; }
if ( ! player . filterState ) { console . log ( "Missing filterState object" ) ; ErrorCount ++ ; }
2018-02-28 01:04:06 +00:00
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 ;
}
2023-03-18 01:27:05 +00:00
if ( ! player . filter . classList . contains ( "responsible" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
player . filter . classList . add ( "responsible" ) ;
2017-05-31 00:21:21 +00:00
}
for ( var selector = 0 ; selector < element . classList . length ; ++ selector )
{
2023-03-18 01:27:05 +00:00
var elementClass = element . classList [ selector ] ;
if ( elementClass . startsWith ( "off_" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ! player . filter . querySelector ( ".filter_content." + elementClass . replace ( /^off_/ , "" ) ) . classList . contains ( "responsible" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
player . filter . querySelector ( ".filter_content." + elementClass . replace ( /^off_/ , "" ) ) . classList . add ( "responsible" ) ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
if ( elementClass . startsWith ( "cat_" ) || elementClass in player . filterState )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ! player . filter . querySelector ( ".filter_mode" ) . classList . add ( "responsible" ) )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
player . filter . querySelector ( ".filter_mode" ) . classList . add ( "responsible" ) ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
setTimeout ( resetFade , 8000 , player ) ;
2017-05-31 00:21:21 +00:00
}
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
{
2023-03-18 01:27:05 +00:00
player . alignMenuWithTimestamp ( ref , element , menu _id . QUOTES ) ;
player . alignMenuWithTimestamp ( ref , element , menu _id . REFERENCES ) ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . navigateFilter = function ( filterItem ) {
if ( filterItem != this . Menus [ menu _id . FILTER ] . Category . LastFocused )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . unfocusUIElement ( focus _level . ITEM ) ;
}
this . focusUIElement ( focus _level . ITEM , menu _id . FILTER , this . Menus [ menu _id . FILTER ] . Category , filterItem ) ;
if ( filterItem . parentNode . classList . contains ( "filter_topics" ) )
{
this . Menus [ menu _id . FILTER ] . Topic . LastFocused = this . Menus [ menu _id . FILTER ] . Category . LastFocused ;
}
else
{
this . Menus [ menu _id . FILTER ] . Medium . LastFocused = this . Menus [ menu _id . FILTER ] . Category . LastFocused ;
2017-05-31 00:21:21 +00:00
}
}
2023-03-18 01:27:05 +00:00
Player . prototype . mouseOverReferenceOrQuoteIdentifier = function ( MenuID , identifier ) {
var MenuEntity = this . Menus [ MenuID ] ;
if ( this . MenusFocused . Item && MenuEntity . Item . LastFocused != identifier . closest ( ".ref" ) )
{
this . unfocusUIElement ( focus _level . ITEM ) ;
}
this . focusUIElement ( focus _level . ITEM , MenuID , MenuEntity . Item , identifier . closest ( ".ref" ) ) ;
if ( this . MenusFocused . Identifier && identifier != MenuEntity . Identifier . LastFocused )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . unfocusUIElement ( focus _level . IDENTIFIER ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . IDENTIFIER , MenuID , MenuEntity . Identifier , identifier ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
Player . prototype . mouseOverReferencesOrQuotes = function ( MenuID , item ) {
var MenuEntity = this . Menus [ MenuID ] ;
if ( this . MenusFocused . Item && item != MenuEntity . Item . LastFocused )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . unfocusUIElement ( focus _level . ITEM ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . ITEM , MenuID , MenuEntity . Item , item ) ;
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
var ourIdentifiers = item . querySelectorAll ( ".timecode" ) ;
var weWereLastFocused = false ;
for ( var i = 0 ; i < ourIdentifiers . length ; ++ i )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
if ( ourIdentifiers [ i ] == MenuEntity . Identifier . LastFocused )
2017-05-31 00:21:21 +00:00
{
weWereLastFocused = true ;
}
}
if ( ! weWereLastFocused )
{
2023-03-18 01:27:05 +00:00
this . unfocusUIElement ( focus _level . IDENTIFIER ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . IDENTIFIER , MenuID , MenuEntity . Identifier , getMostRecentCitation ( this . currentTime , ourIdentifiers ) ) ;
}
2017-05-31 00:21:21 +00:00
2023-03-18 01:27:05 +00:00
Player . prototype . mouseOverCredits = function ( item ) {
if ( this . MenusFocused . Item && item != this . Menus [ menu _id . CREDITS ] . Item . LastFocused )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +00:00
this . unfocusUIElement ( focus _level . ITEM ) ;
2017-05-31 00:21:21 +00:00
}
2023-03-18 01:27:05 +00:00
this . focusUIElement ( focus _level . ITEM , menu _id . CREDITS , this . Menus [ menu _id . CREDITS ] . Item , item ) ;
2017-05-31 00:21:21 +00:00
}
function mouseSkipToTimecode ( player , time , ev )
{
2023-03-25 00:04:34 +00:00
player . setTimeThenPlay ( parseFloat ( time ) ) ;
2017-05-31 00:21:21 +00:00
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
return false ;
}
2023-03-18 01:27:05 +00:00
Player . prototype . handleMenuTogglerInteraction = function ( menu , eventType )
2017-05-31 00:21:21 +00:00
{
2023-03-18 01:27:05 +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" ) )
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . QUOTES , trigger _id . MOUSE ) ;
2017-05-31 00:21:21 +00:00
}
else if ( menu . classList . contains ( "references" ) )
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . REFERENCES , trigger _id . MOUSE ) ;
2017-05-31 00:21:21 +00:00
}
else if ( menu . classList . contains ( "filter" ) )
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . FILTER , trigger _id . MOUSE ) ;
2017-05-31 00:21:21 +00:00
}
2018-05-22 21:43:59 +00:00
else if ( menu . classList . contains ( "link" ) )
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . LINK , trigger _id . MOUSE ) ;
2018-05-22 21:43:59 +00:00
}
2017-05-31 00:21:21 +00:00
else if ( menu . classList . contains ( "credits" ) )
{
2023-03-18 01:27:05 +00:00
this . toggleMenuVisibility ( menu _id . CREDITS , trigger _id . MOUSE ) ;
2017-05-31 00:21:21 +00:00
}
2017-06-03 01:32:18 +00:00
}
2017-05-31 00:21:21 +00:00
}