From a7694d4c3b4e32146a86bb02088b8760d9bd468a Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Sat, 18 Mar 2023 01:27:05 +0000 Subject: [PATCH] cinera: Improve keyboard controls and scrolling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds keyboard navigation of the indices, documented in the "Help" text box. It also improves scrolling of menus to follow progress through the video, with the ability to centre the scrolling around a range of references. Finally in this UI work, it enables the keyboard and mouse to work more cooperatively. Other changes: • Added a "Clear" so the player's initial sizing happens invisibly. • Fixed getBackgroundColourRGB() to handle both rgb() and rgba(). • Deduplicated code, including spurious querySelectorAll() calls. • Moved global variables into the Player object. --- cinera/cinera.c | 80 +- cinera/cinera.css | 22 +- cinera/cinera_player_post.js | 336 +---- cinera/cinera_player_pre.js | 2381 ++++++++++++++++++++++------------ cinera/cinera_pre.js | 43 +- 5 files changed, 1643 insertions(+), 1219 deletions(-) diff --git a/cinera/cinera.c b/cinera/cinera.c index e6ab1e9..daa962f 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -23,7 +23,7 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 10, - .Patch = 24 + .Patch = 25 }; #define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW @@ -11098,14 +11098,14 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m if(Result == RC_SUCCESS) { CopyStringToBuffer(&MenuBuffers->Quote, - " \n" - " \n" + " \n" + " \n" " \n" "
Quote %d
\n" "
", + *QuoteIdentifier, (int)QuoteUsername.Length, QuoteUsername.Base, Timestamp->quote.id, - *QuoteIdentifier, Timestamp->quote.id); CopyStringToBufferHTMLSafe(&MenuBuffers->Quote, QuoteInfo.Text); @@ -11952,14 +11952,30 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF CopyStringToBuffer(&MenuBuffers.Filter, "\">\n" "
\n" - "
Filter mode:
\n" - "
\n"); + "
\n" + "
Filter mode:
\n" + "
\n"); if(Topics.ItemCount > 0) { CopyStringToBuffer(&MenuBuffers.Filter, - "
\n" - "
Topics
\n"); + "
Topics
\n"); + } + if(Media.ItemCount > 0) + { + CopyStringToBuffer(&MenuBuffers.Filter, + "
Media
\n"); + } + + CopyStringToBuffer(&MenuBuffers.Filter, + "
\n" + "
\n" + "
\n"); + + if(Topics.ItemCount > 0) + { + CopyStringToBuffer(&MenuBuffers.Filter, + "
\n"); for(int i = 0; i < Topics.ItemCount; ++i) { category_info *This = GetPlaceInBook(&Topics, i); @@ -11988,8 +12004,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF if(Media.ItemCount > 0) { CopyStringToBuffer(&MenuBuffers.FilterMedia, - "
\n" - "
Media
\n"); + "
\n"); for(int i = 0; i < Media.ItemCount; ++i) { category_info *This = GetPlaceInBook(&Media, i); @@ -12056,7 +12071,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF "\n" "

Global Keys

\n" " [, < / ], > Jump to previous / next episode
\n" - " W, K, P / S, J, N Jump to previous / next marker
\n" + " W, K, P / S, J, N Jump to previous / next timestamp
\n" " t / T Toggle theatre / SUPERtheatre mode
\n" " V Revert filter to original state Y Select link (requires manual Ctrl-c)\n", @@ -12078,15 +12093,15 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF CopyStringToBuffer(&PlayerBuffers.Menus, "\n" - "

In-Menu Movement

\n" + "

In-Menu and Index Controls

\n" "
\n" "
\n" "
\n" " a\n" "
\n" "
\n" - " w
\n" - " s\n" + " w
\n" + " s\n" "
\n" "
\n" " d\n" @@ -12094,8 +12109,8 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF "
\n" "
\n" " h\n" - " j\n" - " k\n" + " j\n" + " k\n" " l\n" "
\n" "
\n" @@ -12110,25 +12125,29 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF " \n" "
\n" "
\n" + "

\n" + "
\n" + " Esc Close menu / unfocus timestamp\n" "
\n" "
\n"); CopyStringToBuffer(&PlayerBuffers.Menus, - "

%sQuotes %sand%s References%s Menus%s

\n" - " Enter Jump to timecode
\n", + "

%sQuotes %sand%s References%s Menus and%s Index

\n" + " Enter Jump to timestamp
\n", // Q R // - // 0 0

Quotes and References Menus

- // 0 1

Quotes and References Menus

- // 1 0

Quotes and References Menus

- // 1 1

Quotes and References Menus

+ // 0 0

Quotes and References Menus and Index

+ // 0 1

Quotes and References Menus and Index

+ // 1 0

Quotes and References Menus and Index

+ // 1 1

Quotes and References Menus and Index

HasQuoteMenu ? "" : "", HasQuoteMenu && !HasReferenceMenu ? "" : "", !HasQuoteMenu && HasReferenceMenu ? "" : "", HasQuoteMenu && !HasReferenceMenu ? "" : "", - !HasQuoteMenu && !HasReferenceMenu ? "" : "", - HasQuoteMenu || HasReferenceMenu ? "" : " unavailable", HasQuoteMenu || HasReferenceMenu ? "" : " unavailable"); + !HasQuoteMenu && !HasReferenceMenu ? "" : ""); + //HasQuoteMenu || HasReferenceMenu ? "" : " unavailable", + //HasQuoteMenu || HasReferenceMenu ? "" : " unavailable"); CopyStringToBuffer(&PlayerBuffers.Menus, "\n" @@ -12371,6 +12390,19 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF CopyStringToBuffer(&CollationBuffers->Player, "
\n" " "); + + asset *JSClear = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_CLEAR].Filename), ASSET_JS); + ConstructResolvedAssetURL(&URL, JSClear, PAGE_PLAYER); + CopyStringToBuffer(&CollationBuffers->Player, + ""); + + CopyStringToBuffer(&CollationBuffers->Player, "\n" + " "); CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER); CopyStringToBuffer(&CollationBuffers->Player, "\n" " "); diff --git a/cinera/cinera.css b/cinera/cinera.css index 0b7a053..c2e51f5 100644 --- a/cinera/cinera.css +++ b/cinera/cinera.css @@ -540,6 +540,16 @@ ul.cineraNavPlain li.current > a { display: block; } +.cineraHelp .help_container .help_custom_index { + background-color: #159 !important; +} + +.cineraHelp .help_container h2 .help_title_key { + padding: .2em; + border: 1px solid; + border-radius: 4px; +} + .cineraHelp .help_container .help_key { box-sizing: content-box; font-family: Inconsolata; @@ -614,12 +624,11 @@ ul.cineraNavPlain li.current > a { .cineraMenus > .menu .credits_container { border: 1px solid; border-top: none; - display: none; + z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */ overflow-y: auto; position: absolute; right: 0; top: 100%; - z-index: 8; } .cineraMenus > .menu .refs, @@ -639,7 +648,7 @@ ul.cineraNavPlain li.current > a { } .cineraMenus > .menu .visible { - display: block; + z-index: 8; } .cineraMenus > .menu > .refs .ref { @@ -737,6 +746,11 @@ ul.cineraNavPlain li.current > a { margin-right: 4px; } +.cineraMenus > .menu > .filter_container .filter_header { + position: sticky; + top: 0; +} + .cineraMenus > .menu > .filter_container .filter_mode, .cineraMenus > .menu > .link_container #cineraLinkMode { cursor: pointer; @@ -756,11 +770,13 @@ ul.cineraNavPlain li.current > a { text-align: center; } +.cineraMenus > .menu > .filter_container .filter_header .filter_titles, .cineraMenus > .menu > .filter_container .filters { display: flex; flex-flow: row nowrap; } +.cineraMenus > .menu > .filter_container .filter_header .filter_titles > *, .cineraMenus > .menu > .filter_container .filters > * { width: 50%; flex-grow: 1; diff --git a/cinera/cinera_player_post.js b/cinera/cinera_player_post.js index 71113f5..de2e18f 100644 --- a/cinera/cinera_player_post.js +++ b/cinera/cinera_player_post.js @@ -1,40 +1,5 @@ -var cinera = document.querySelector(".cinera"); var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location; -var originalTextContent = { - TitleQuotes: null, - TitleReferences: null, - TitleCredits: null, - EpisodePrev: null, - EpisodeNext: null, -}; - -var menuState = []; -var titleBar = cinera.querySelector(".cineraMenus"); -var quotesMenu = null; -var referencesMenu = null; -var filterMenu = null; -var viewsMenu = null; -var linkMenu = null; -var creditsMenu = null; -var sourceMenus = null; -var helpButton = null; -var helpDocumentation = null; - -// NOTE(matt): One set of markers per page. There is code to support multiple, which we may want to extend everywhere -var MarkersContainer = cinera.querySelector(".markers_container"); - -var views = { - REGULAR: 0, - THEATRE: 1, - SUPERTHEATRE: 2, -}; - -var devices = { - DESKTOP: 0, - MOBILE: 1, -}; - var CineraProps = { C: null, V: views.REGULAR, @@ -57,275 +22,19 @@ var CineraProps = { }; CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile); -if(titleBar) -{ - quotesMenu = titleBar.querySelector(".quotes_container"); - if(quotesMenu) - { - originalTextContent.TitleQuotes = quotesMenu.previousElementSibling.textContent; - menuState.push(quotesMenu); - var quoteItems = quotesMenu.querySelectorAll(".ref"); - if(quoteItems) - { - for(var i = 0; i < quoteItems.length; ++i) - { - quoteItems[i].addEventListener("mouseenter", function(ev) { - mouseOverQuotes(this); - }) - }; - } - var quoteTimecodes = quotesMenu.querySelectorAll(".refs .ref .ref_indices .timecode"); - for (var i = 0; i < quoteTimecodes.length; ++i) { - quoteTimecodes[i].addEventListener("click", function(ev) { - if (player) { - var time = ev.currentTarget.getAttribute("data-timestamp"); - mouseSkipToTimecode(player, time, ev); - } - }); - } - var lastFocusedQuote = null; - } - - referencesMenu = titleBar.querySelector(".references_container"); - if(referencesMenu) - { - originalTextContent.TitleReferences = referencesMenu.previousElementSibling.textContent; - menuState.push(referencesMenu); - var referenceItems = referencesMenu.querySelectorAll(".ref"); - if(referenceItems) - { - for(var i = 0; i < referenceItems.length; ++i) - { - referenceItems[i].addEventListener("mouseenter", function(ev) { - mouseOverReferences(this); - }) - }; - var lastFocusedReference = null; - var lastFocusedIdentifier = null; - } - - var refTimecodes = referencesMenu.querySelectorAll(".refs .ref .ref_indices .timecode"); - for (var i = 0; i < refTimecodes.length; ++i) { - refTimecodes[i].addEventListener("click", function(ev) { - if (player) { - var time = ev.currentTarget.getAttribute("data-timestamp"); - mouseSkipToTimecode(player, time, ev); - } - }); - } - } - - if(referencesMenu || quotesMenu) - { - var refSources = titleBar.querySelectorAll(".refs .ref"); // This is for both quotes and refs - for (var i = 0; i < refSources.length; ++i) { - refSources[i].addEventListener("click", function(ev) { - if (player) { - player.pause(); - } - }); - } - } - - filterMenu = titleBar.querySelector(".filter_container"); - if(filterMenu) - { - menuState.push(filterMenu); - var lastFocusedCategory = null; - var lastFocusedTopic = null; - var lastFocusedMedium = null; - - var filter = filterMenu.parentNode; - - var filterModeElement = filter.querySelector(".filter_mode"); - filterModeElement.addEventListener("click", function(ev) { - ev.stopPropagation(); - toggleFilterMode(); - }); - - var filterMode = filterModeElement.classList[1]; - var filterItems = filter.querySelectorAll(".filter_content"); - - var filterInitState = new Object(); - var filterState = new Object(); - for(var i = 0; i < filterItems.length; ++i) - { - filterItems[i].addEventListener("mouseenter", function(ev) { - navigateFilter(this); - }) - - filterItems[i].addEventListener("click", function(ev) { - ev.stopPropagation(); - filterItemToggle(this); - }); - - var filterItemName = filterItems[i].classList.item(1); - if(filterItems[i].parentNode.classList.contains("filter_topics")) - { - filterInitState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") }; - filterState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") }; - } - else - { - filterInitState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") }; - filterState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") }; - } - } - } - - viewsMenu = titleBar.querySelector(".views"); - if(viewsMenu && !CineraProps.IsMobile) - { - menuState.push(viewsMenu); - var viewsContainer = viewsMenu.querySelector(".views_container"); - viewsMenu.addEventListener("mouseenter", function(ev) { - handleMouseOverViewsMenu(); - }); - viewsMenu.addEventListener("mouseleave", function(ev) { - viewsContainer.style.display = "none"; - }); - - var viewItems = viewsMenu.querySelectorAll(".view"); - for(var i = 0; i < viewItems.length; ++i) - { - viewItems[i].addEventListener("click", function(ev) { - switch(this.getAttribute("data-id")) - { - case "regular": - case "theatre": - { - toggleTheatreMode(); - } break; - case "super": - { - toggleSuperTheatreMode(); - } break; - } - }); - } - } - - linkMenu = titleBar.querySelector(".link_container"); - linkTimestamp = true; - if(linkMenu) - { - menuState.push(linkMenu); - - var linkMode = linkMenu.querySelector("#cineraLinkMode"); - var link = linkMenu.querySelector("#cineraLink"); - - linkMode.addEventListener("click", function(ev) { - ev.stopPropagation(); - toggleLinkMode(linkMode, link); - }); - - link.addEventListener("click", function(ev) { - CopyToClipboard(link); - toggleMenuVisibility(linkMenu); - }); - } - - creditsMenu = titleBar.querySelector(".credits_container"); - if(creditsMenu) - { - originalTextContent.TitleCredits = creditsMenu.previousElementSibling.textContent; - menuState.push(creditsMenu); - var lastFocusedCreditItem = null; - - var creditItems = creditsMenu.querySelectorAll(".person, .support"); - for(var i = 0; i < creditItems.length; ++i) - { - creditItems[i].addEventListener("mouseenter", function(ev) { - if(this != lastFocusedCreditItem) - { - lastFocusedCreditItem.classList.remove("focused"); - unfocusSprite(lastFocusedCreditItem); - if(lastFocusedCreditItem.classList.contains("support")) - { - setSpriteLightness(lastFocusedCreditItem.firstChild); - } - lastFocusedCreditItem = this; - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - if(focusedElement.classList.contains("support")) - { - setSpriteLightness(focusedElement.firstChild); - } - } - }); - if(creditItems[i].tagName == "A") - { - creditItems[i].addEventListener("click", function(ev) { - if(player) - { - player.pause(); - } - }); - } - } - } - - sourceMenus = titleBar.querySelectorAll(".menu"); - - helpButton = titleBar.querySelector(".cineraHelp"); - helpDocumentation = helpButton.querySelector(".help_container"); - BindHelp(helpButton, helpDocumentation); -} - -var focusedElement = null; -var focusedIdentifier = null; - -var playerContainer = cinera.querySelector(".cineraPlayerContainer") -var prevEpisode = playerContainer.querySelector(".episodeMarker.prev"); -if(prevEpisode) { originalTextContent.EpisodePrev = prevEpisode.firstChild.textContent; } -var nextEpisode = playerContainer.querySelector(".episodeMarker.next"); -if(nextEpisode) { originalTextContent.EpisodeNext = nextEpisode.firstChild.textContent; } -var testMarkers = playerContainer.querySelectorAll(".marker"); - -// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent"; var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector); var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container"; var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector); -if(CineraProps.IsMobile) -{ - InitMobileStyle(); -} -else -{ - var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - 4; - MenuContainerRule.style.maxHeight = MenuMaxHeight + "px"; -} - -var player = new Player(playerContainer, onRefChanged); - -if(CineraProps.IsMobile) -{ - ConnectMobileControls(player); -} - -var cineraViewStorageItem = "cineraView"; - -if(viewsMenu && localStorage.getItem(cineraViewStorageItem)) -{ - toggleTheatreMode(); -} - -InitScrollEventListener(cinera); - -function -DelayedUpdateSize() -{ - player.updateSize(); -} +var cinera = document.querySelector(".cinera"); +var player = new Player(cinera, onRefChanged); window.addEventListener("resize", function() { if(CineraProps.IsMobile) { - setTimeout(DelayedUpdateSize, 512); + setTimeout(DelayedUpdateSize, 512, player); } else { @@ -336,7 +45,7 @@ window.addEventListener("resize", function() { window.onorientationchange = function() { if(CineraProps.IsMobile) { - setTimeout(DelayedUpdateSize, 512); + setTimeout(DelayedUpdateSize, 512, player); } else { @@ -351,43 +60,8 @@ document.addEventListener("keydown", function(ev) { key = "capitalSpace"; } - if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement) + if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item) { ev.preventDefault(); } }); - -for(var i = 0; i < sourceMenus.length; ++i) -{ - sourceMenus[i].addEventListener("mouseenter", function(ev) { - handleMenuTogglerInteraction(this, ev.type); - }) - sourceMenus[i].addEventListener("mouseleave", function(ev) { - handleMenuTogglerInteraction(this, ev.type); - }) - sourceMenus[i].addEventListener("click", function(ev) { - handleMenuTogglerInteraction(this, ev.type); - }) -}; - -var colouredItems = playerContainer.querySelectorAll(".author, .member, .project"); -for(i = 0; i < colouredItems.length; ++i) -{ - setTextLightness(colouredItems[i]); -} - -var topicDots = cinera.querySelectorAll(".category"); -for(var i = 0; i < topicDots.length; ++i) -{ - setDotLightness(topicDots[i]); -} - -var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname; -var lastTimestamp; -if(location.hash) { - player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash); -} -else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem)) -{ - player.setTimeThenPlay(lastTimestamp); -} diff --git a/cinera/cinera_player_pre.js b/cinera/cinera_player_pre.js index b67f9a3..714ab43 100644 --- a/cinera/cinera_player_pre.js +++ b/cinera/cinera_player_pre.js @@ -1,6 +1,10 @@ -// 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`. +"use strict"; + +var devices = { + DESKTOP: 0, + MOBILE: 1, +}; + var vod_platform = { DIRECT: 0, VIMEO: 1, @@ -21,34 +25,155 @@ GetVODPlatformFromString(VODPlatformString) return Result; } -function Player(htmlContainer, refsCallback) { - this.container = htmlContainer; - this.markersContainer = this.container.querySelector(".markers_container"); +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; } + this.videoContainer = this.container.querySelector(".video_container"); this.refsCallback = refsCallback || function() {}; + if (!(this.videoContainer.getAttribute("data-platform") || this.videoContainer.getAttribute("data-videoId"))) { console.error("Expected to find data-platform and data-videoId attribute on", this.videoContainer, "for player initialized on", this.container); throw new Error("Missing data-platform or data-videoId attribute."); } + + 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(); + } + this.markers = []; - var markerEls = this.markersContainer.querySelectorAll(".marker"); + var markerEls = this.Menus[menu_id.MARKERS].Elements; if (markerEls.length == 0) { - console.error("No markers found in", this.markersContainer, "for player initialized on", this.container); + console.error("No markers found in", this.Menus[menu_id.MARKERS].Container, "for player initialized on", this.container); throw new Error("Missing markers."); } for (var i = 0; i < markerEls.length; ++i) { + var markerEl = markerEls[i]; var marker = { - timestamp: parseInt(markerEls[i].getAttribute("data-timestamp"), 10), - ref: markerEls[i].getAttribute("data-ref"), + timestamp: parseInt(markerEl.getAttribute("data-timestamp"), 10), + ref: markerEl.getAttribute("data-ref"), endTime: (i < markerEls.length - 1 ? parseInt(markerEls[i+1].getAttribute("data-timestamp"), 10) : null), - el: markerEls[i], - fadedProgress: markerEls[i].querySelector(".progress.faded"), - progress: markerEls[i].querySelector(".progress.main"), + el: markerEl, + fadedProgress: markerEl.querySelector(".progress.faded"), + progress: markerEl.querySelector(".progress.main"), hoverx: null }; marker.el.addEventListener("click", this.onMarkerClick.bind(this, marker)); marker.el.addEventListener("mousemove", this.onMarkerMouseMove.bind(this, marker)); + marker.el.addEventListener("mouseenter", this.onMarkerMouseEnter.bind(this, marker)); marker.el.addEventListener("mouseleave", this.onMarkerMouseLeave.bind(this, marker)); this.markers.push(marker); } @@ -67,23 +192,277 @@ function Player(htmlContainer, refsCallback) { this.pauseAfterBuffer = false; this.speed = 1; this.currentTime = -1; - this.scrollTo = -1; - this.scrollPosition = 0; - this.scrollRefsTo = -1; - this.scrollRefsPosition = 0; + this.desiredTime = -1; + this.nextFrame = null; this.looping = false; - - this.markersContainer.addEventListener("wheel", function(ev) { - this.scrollTo = -1; + this.Menus[menu_id.MARKERS].Container.addEventListener("wheel", function(ev) { + this.Menus[menu_id.MARKERS].Scroll.To = -1; }.bind(this)); Player.initializePlatform(this.vod_platform, this.onPlatformReady.bind(this)); - var PendingMobileStyleInitialisation = true; - this.updateSize(PendingMobileStyleInitialisation); + this.updateSize(); + + this.cineraViewStorageItem = "cineraView"; + if(this.Menus[menu_id.VIEWS].Toggler && localStorage.getItem(this.cineraViewStorageItem)) + { + this.toggleTheatreMode(); + } + + FlipClear(); + this.resume(); + + 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); + } } // Start playing the video from the current position. @@ -144,14 +523,14 @@ Player.prototype.pause = function() { // Sets the current time then plays. // If the player hasn't loaded yet, it will seek to this time when ready. Player.prototype.setTimeThenPlay = function(time) { - this.currentTime = time; + this.desiredTime = time; switch(this.vod_platform) { case vod_platform.DIRECT: { if (this.platformPlayerReady) { - this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration)); - this.platformPlayer.currentTime = this.currentTime; + this.desiredTime = Math.max(0, Math.min(this.desiredTime, this.duration)); + this.platformPlayer.currentTime = this.desiredTime; this.updateProgress(); this.play(); } @@ -159,9 +538,9 @@ Player.prototype.setTimeThenPlay = function(time) { case vod_platform.VIMEO: { if (this.platformPlayerReady) { - this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration)); + this.desiredTime = Math.max(0, Math.min(this.desiredTime, this.duration)); var Parent = this; - this.platformPlayer.setCurrentTime(this.currentTime) + this.platformPlayer.setCurrentTime(this.desiredTime) .then(function() { Parent.updateProgress(); Parent.play(); @@ -171,8 +550,8 @@ Player.prototype.setTimeThenPlay = function(time) { case vod_platform.YOUTUBE: { if (this.platformPlayerReady) { - this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration)); - this.platformPlayer.seekTo(this.currentTime); + this.desiredTime = Math.max(0, Math.min(this.desiredTime, this.duration)); + this.platformPlayer.seekTo(this.desiredTime); this.updateProgress(); this.play(); } @@ -246,6 +625,29 @@ GetWidthOfHideableElement(Element, UnhidingClass) return Result; } +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; +} + function ComputeVerticalOffsetForMenu(Menu, Toggler, VideoContainerDimY) { @@ -298,8 +700,10 @@ function sizeAndPositionMenuContainer(TitleBar, SizerElement, Menu) ContainerDimY -= TitleBarDimY; Menu.style.maxHeight = ContainerDimY + "px"; + var MenuHorizontalOffset = ComputeHorizontalOffsetForMenu(Menu, Toggler, SizerElementDimX); + Menu.style.top = TitleBarDimY + "px"; - Menu.style.left = 0 + "px"; + Menu.style.left = MenuHorizontalOffset + "px"; } break; case orientations.LANDSCAPE_LEFT: { @@ -333,9 +737,9 @@ function sizeAndPositionMenuContainer(TitleBar, SizerElement, Menu) Menu.style.maxHeight = ContainerDimY + "px"; var MenuVerticalOffset = ComputeVerticalOffsetForMenu(Menu, Toggler, SizerElementDimY); + var MenuWidth = GetWidthOfHideableElement(Menu, "visible"); Menu.style.top = MenuVerticalOffset + "px"; - var MenuWidth = GetWidthOfHideableElement(Menu, "visible"); Menu.style.left = -MenuWidth + "px"; } break; @@ -369,13 +773,13 @@ ComputeAndSetTallest(Selector, Elements, UnhidingClass) return Result; } -function ApplyMobileStyle(VideoContainer) +Player.prototype.ApplyMobileStyle = function(VideoContainer) { var WindowDim = DeriveReliableWindowDimensions(); - var MaxWidth = MaxWidthOfElement(cinera, WindowDim); - var MaxHeight = MaxHeightOfElement(cinera, WindowDim); + var MaxWidth = MaxWidthOfElement(this.root, WindowDim); + var MaxHeight = MaxHeightOfElement(this.root, WindowDim); - var IndicesBar = playerContainer.querySelector(".markers_container"); + var IndicesBar = this.Menus[menu_id.MARKERS].Container; var Markers = IndicesBar.querySelector(".markers"); var CineraContentWidth = MaxWidth; @@ -390,20 +794,20 @@ function ApplyMobileStyle(VideoContainer) { case orientations.PORTRAIT: { - cinera.style.flexDirection = "column"; - titleBar.style.flexDirection = "row"; + this.root.style.flexDirection = "column"; + this.titleBar.style.flexDirection = "row"; } break; case orientations.LANDSCAPE_LEFT: { - cinera.style.flexDirection = "row"; - titleBar.style.flexDirection = "column-reverse"; - CineraContentWidth -= titleBar.offsetWidth; + this.root.style.flexDirection = "row"; + this.titleBar.style.flexDirection = "column-reverse"; + CineraContentWidth -= this.titleBar.offsetWidth; } break; case orientations.LANDSCAPE_RIGHT: { - cinera.style.flexDirection = "row-reverse"; - titleBar.style.flexDirection = "column"; - CineraContentWidth -= titleBar.offsetWidth; + this.root.style.flexDirection = "row-reverse"; + this.titleBar.style.flexDirection = "column"; + CineraContentWidth -= this.titleBar.offsetWidth; } break; } @@ -411,8 +815,7 @@ function ApplyMobileStyle(VideoContainer) if(MobileCineraContentRule !== undefined) { MobileCineraContentRule.style.width = CineraContentWidth + "px"; - var MarkerList = Markers.querySelectorAll(".marker"); - HeightOfTallestIndex = ComputeAndSetTallest(MobileCineraContentRule, MarkerList, "current"); + HeightOfTallestIndex = ComputeAndSetTallest(MobileCineraContentRule, this.Menus[menu_id.MARKERS].Elements, "current"); IndicesBar.style.height = HeightOfTallestIndex + "px"; Markers.style.width = CineraContentWidth + "px"; } @@ -429,12 +832,12 @@ function ApplyMobileStyle(VideoContainer) { case orientations.PORTRAIT: { - VideoMaxDimY -= titleBar.offsetHeight; + VideoMaxDimY -= this.titleBar.offsetHeight; } break; case orientations.LANDSCAPE_LEFT: case orientations.LANDSCAPE_RIGHT: { - VideoMaxDimX -= titleBar.offsetWidth; + VideoMaxDimX -= this.titleBar.offsetWidth; } break; } @@ -462,48 +865,45 @@ function ApplyMobileStyle(VideoContainer) VideoContainer.style.width = VideoDimX + "px"; VideoContainer.style.height = VideoDimY + "px"; - sizeAndPositionMenuContainer(titleBar, cinera, quotesMenu); - sizeAndPositionMenuContainer(titleBar, cinera, referencesMenu); - sizeAndPositionMenuContainer(titleBar, cinera, filterMenu); - sizeAndPositionMenuContainer(titleBar, cinera, linkMenu); - sizeAndPositionMenuContainer(titleBar, cinera, creditsMenu); + 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); } -function -IconifyMenuTogglers() +Player.prototype.IconifyMenuTogglers = function() { - if(quotesMenu) + if(this.Menus[menu_id.QUOTES].Container) { - quotesMenu.previousElementSibling.textContent = '\u{1F5E9}'; + this.Menus[menu_id.QUOTES].Container.previousElementSibling.textContent = '\u{1F5E9}'; } - if(referencesMenu) + if(this.Menus[menu_id.REFERENCES].Container) { - referencesMenu.previousElementSibling.textContent = '\u{1F4D6}'; + this.Menus[menu_id.REFERENCES].Container.previousElementSibling.textContent = '\u{1F4D6}'; } - if(creditsMenu) + if(this.Menus[menu_id.CREDITS].Container) { - creditsMenu.previousElementSibling.textContent = '\u{1F46A}'; + this.Menus[menu_id.CREDITS].Container.previousElementSibling.textContent = '\u{1F46A}'; } - if(viewsMenu) + if(this.Menus[menu_id.VIEWS].Toggler) { - viewsMenu.remove(); - viewsMenu = null; + this.Menus[menu_id.VIEWS].Toggler.remove(); + this.Menus[menu_id.VIEWS].Toggler = null; } } -function -InitMobileControls() +Player.prototype.InitMobileControls = function() { var rightmost = {}; - var markersContainer = cinera.querySelector(".markers_container"); - markersContainer.style.height = "auto"; - var episodeMarkerFirst = markersContainer.querySelector(".episodeMarker.first"); - var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev"); - var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next"); - var episodeMarkerLast = markersContainer.querySelector(".episodeMarker.last"); + 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"); if(episodeMarkerPrev) { episodeMarkerPrev.firstChild.textContent = '\u{23EE}'; } if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = '\u{23ED}'; rightmost = episodeMarkerNext; } @@ -516,8 +916,8 @@ InitMobileControls() controlPrevTimestampContent.appendChild(document.createTextNode('\u{25C0}')); controlPrevTimestamp.appendChild(controlPrevTimestampContent); - var markers = markersContainer.querySelector(".markers"); - markersContainer.insertBefore(controlPrevTimestamp, markers); + var markers = this.Menus[menu_id.MARKERS].Container.querySelector(".markers"); + this.Menus[menu_id.MARKERS].Container.insertBefore(controlPrevTimestamp, markers); var controlNextTimestamp = document.createElement("a"); controlNextTimestamp.classList.add("episodeMarker"); @@ -528,37 +928,37 @@ InitMobileControls() if(rightmost) { - markersContainer.insertBefore(controlNextTimestamp, rightmost); + this.Menus[menu_id.MARKERS].Container.insertBefore(controlNextTimestamp, rightmost); } else { - markersContainer.appendChild(controlNextTimestamp); + this.Menus[menu_id.MARKERS].Container.appendChild(controlNextTimestamp); } } -function InitMobileStyle() +Player.prototype.ConnectMobileControls = function() { - cinera.classList.add("mobile"); - IconifyMenuTogglers(); - InitMobileControls(); - var VideoContainer = cinera.querySelector(".video_container"); - ApplyMobileStyle(VideoContainer); -} - -function -ConnectMobileControls(player) -{ - var markersContainer = player.markersContainer; - var ControlPrevTimestamp = markersContainer.querySelector(".episodeMarker.prevTimestamp"); + var player = this; + var ControlPrevTimestamp = this.Menus[menu_id.MARKERS].Container.querySelector(".episodeMarker.prevTimestamp"); ControlPrevTimestamp.addEventListener("click", function(ev) { player.jumpToPrevMarker(); }); - var ControlNextTimestamp = markersContainer.querySelector(".episodeMarker.nextTimestamp"); + var ControlNextTimestamp = this.Menus[menu_id.MARKERS].Container.querySelector(".episodeMarker.nextTimestamp"); ControlNextTimestamp.addEventListener("click", function(ev) { player.jumpToNextMarker(); }); } +Player.prototype.InitMobileStyle = function() +{ + this.root.classList.add("mobile"); + this.IconifyMenuTogglers(); + this.InitMobileControls(); + this.ConnectMobileControls(); + var VideoContainer = this.root.querySelector(".video_container"); + this.ApplyMobileStyle(VideoContainer); +} + // Call this after changing the size of the video container in order to update the platform player. Player.prototype.updateSize = function() { var width = 0; @@ -567,16 +967,16 @@ Player.prototype.updateSize = function() { if(!CineraProps.IsMobile) { var VisibleArea = MaxDimensionsOfElement(this.container, GetWindowDim(false)); - var AvailableHeight = VisibleArea.Y - titleBar.offsetHeight; - var VerticalScrollBarWidth = this.markersContainer.offsetWidth - this.markersContainer.clientWidth; - width = VisibleArea.X - (this.markersContainer.scrollWidth + VerticalScrollBarWidth); + 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); height = width / 16 * 9; // TODO(matt): Get the aspect ratio from the video itself? if(height > AvailableHeight) { height = AvailableHeight; width = height / 9 * 16; } - this.markersContainer.style.height = height + "px"; + this.Menus[menu_id.MARKERS].Container.style.height = height + "px"; var VacantPixelsBelowMenus = 4; var MenuMaxHeight = height - VacantPixelsBelowMenus; @@ -584,7 +984,7 @@ Player.prototype.updateSize = function() { } else { - ApplyMobileStyle(this.videoContainer); + this.ApplyMobileStyle(this.videoContainer); width = this.videoContainer.offsetWidth; height = this.videoContainer.offsetHeight; } @@ -608,6 +1008,12 @@ Player.prototype.updateSize = function() { } } +function +DelayedUpdateSize(player) +{ + player.updateSize(); +} + // 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(); @@ -664,24 +1070,85 @@ Player.initializePlatform = function(platform_id, callback) { // END PUBLIC INTERFACE Player.prototype.onMarkerClick = function(marker, ev) { - var time = marker.timestamp; - if (this.currentMarker == marker && marker.hoverx !== null) { - time += (marker.endTime - marker.timestamp) * marker.hoverx; + 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); } - this.setTimeThenPlay(time); }; Player.prototype.onMarkerMouseMove = function(marker, ev) { - if (this.currentMarker == marker) { - var CineraContent = this.currentMarker.el.querySelector(".cineraContent"); - marker.hoverx = (ev.pageX - getElementXOffsetFromPage(CineraContent)) / CineraContent.offsetWidth; + 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); + } } }; Player.prototype.onMarkerMouseLeave = function(marker, ev) { - marker.hoverx = null; + 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; + } + } }; +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); + } +} + Player.prototype.updateProgress = function() { var prevMarker = this.currentMarker; this.currentMarker = null; @@ -724,24 +1191,36 @@ Player.prototype.updateProgress = function() { if (this.currentMarker) { if(this.currentMarkerIdx == this.markers.length - 1) { - localStorage.removeItem(lastTimestampStorageItem); + localStorage.removeItem(this.lastTimestampStorageItem); } else { - localStorage.setItem(lastTimestampStorageItem, this.currentMarker.timestamp); + localStorage.setItem(this.lastTimestampStorageItem, this.currentMarker.timestamp); } this.currentMarker.el.classList.add("current"); - if(this.scrollTo == -1) - { - this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0; - this.scrollPosition = this.markersContainer.scrollTop; - } + + this.setScroller(this.Menus[menu_id.MARKERS], this.currentMarker.el, true, true); this.refsCallback(this.currentMarker.ref, this.currentMarker.el, this); } else if (prevMarker && prevMarker.ref) { this.refsCallback(null); } } -}; +} + +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; + } +} Player.prototype.doFrame = function() { if (this.playing) { @@ -750,6 +1229,7 @@ Player.prototype.doFrame = function() { case vod_platform.DIRECT: { this.currentTime = this.platformPlayer.currentTime; + if(this.desiredTime == -1) { this.desiredTime = this.currentTime; } this.updateProgress(); } break; case vod_platform.VIMEO: @@ -758,43 +1238,43 @@ Player.prototype.doFrame = function() { this.platformPlayer.getCurrentTime() .then(function(Result) { Parent.currentTime = Result; + if(this.desiredTime == -1) { this.desiredTime = this.currentTime; } Parent.updateProgress(); }); } break; case vod_platform.YOUTUBE: { this.currentTime = this.platformPlayer.getCurrentTime(); + if(this.desiredTime == -1) { this.desiredTime = this.currentTime; } this.updateProgress(); } break; } } - if (this.scrollTo >= 0) { - var targetPosition = this.scrollTo - this.markersContainer.offsetHeight/2.0; - targetPosition = Math.max(0, Math.min(targetPosition, this.markersContainer.scrollHeight - this.markersContainer.offsetHeight)); - this.scrollPosition += (targetPosition - this.scrollPosition) * 0.1; - if (Math.abs(this.scrollPosition - targetPosition) < 1.0) { - this.markersContainer.scrollTop = targetPosition; - this.scrollTo = -1; - } else { - this.markersContainer.scrollTop = this.scrollPosition; - } + if (this.Menus[menu_id.MARKERS].Scroll.To >= 0) { + this.smoothScroll(this.Menus[menu_id.MARKERS]); } - if(referencesMenu && this.scrollRefsTo >= 0) { - var targetRefsPosition = this.scrollRefsTo; - targetRefsPosition = Math.max(0, Math.min(targetRefsPosition, referencesMenu.scrollHeight - referencesMenu.offsetHeight)); - this.scrollRefsPosition += (targetRefsPosition - this.scrollRefsPosition) * 0.1; - if (Math.abs(this.scrollRefsPosition - targetRefsPosition) < 1.0) { - referencesMenu.scrollTop = targetRefsPosition; - this.scrollRefsTo = -1; - } else { - referencesMenu.scrollTop = this.scrollRefsPosition; - } + 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]); + } + + 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; } + this.nextFrame = requestAnimationFrame(this.doFrame.bind(this)); - updateLink(); + this.updateLink(); }; Player.prototype.setStyleAndQuality = function() { @@ -825,9 +1305,9 @@ Player.prototype.setStyleAndQuality = function() { Player.prototype.setDurationThenAutoplay = function(Duration) { this.duration = Duration; this.markers[this.markers.length-1].endTime = this.duration; - if (this.currentTime > 0) { - this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration)); - this.setTimeThenPlay(this.currentTime); + if (this.desiredTime > 0) { + this.desiredTime = Math.max(0, Math.min(this.desiredTime, this.duration)); + this.setTimeThenPlay(this.desiredTime); } if (this.shouldPlay) { this.play(); @@ -896,7 +1376,7 @@ Player.prototype.onStateEnded = function() { this.buffering = false; this.playing = false; - localStorage.removeItem(lastTimestampStorageItem); + localStorage.removeItem(this.lastTimestampStorageItem); this.currentTime = null; this.updateProgress(); } @@ -1031,196 +1511,211 @@ Player.prototype.onPlatformReady = function() { Player.platformPlayerCount = 0; -function toggleFilterMode() { - if(filterMode == "inclusive") +Player.prototype.toggleFilterMode = function() { + if(this.filterMode == "inclusive") { - filterModeElement.classList.remove("inclusive"); - filterModeElement.classList.add("exclusive"); - filterMode = "exclusive"; + this.filterModeElement.classList.remove("inclusive"); + this.filterModeElement.classList.add("exclusive"); + this.filterMode = "exclusive"; } else { - filterModeElement.classList.remove("exclusive"); - filterModeElement.classList.add("inclusive"); - filterMode = "inclusive"; + this.filterModeElement.classList.remove("exclusive"); + this.filterModeElement.classList.add("inclusive"); + this.filterMode = "inclusive"; } - applyFilter(); + this.applyFilter(); } -function updateLink() +Player.prototype.updateLink = function() { - if(link && player) + if(this.link) { - if(linkTimestamp == true) + if(this.linkTimestamp == true) { - if(player.currentMarker) + if(this.currentMarker) { - link.value = baseURL + "#" + player.currentMarker.timestamp; + this.link.value = baseURL + "#" + this.currentMarker.timestamp; } else { - link.value = baseURL; + this.link.value = baseURL; } } else { - switch(player.vod_platform) + switch(this.vod_platform) { case vod_platform.DIRECT: { - link.value = baseURL + "#" + Math.round(player.platformPlayer.currentTime); + this.link.value = baseURL + "#" + Math.round(this.platformPlayer.currentTime); } break; case vod_platform.VIMEO: { - player.platformPlayer.getCurrentTime() + this.platformPlayer.getCurrentTime() .then(function(Response) { - link.value = baseURL + "#" + Math.round(Response); + this.link.value = baseURL + "#" + Math.round(Response); }); } break; case vod_platform.YOUTUBE: { - link.value = baseURL + "#" + Math.round(player.platformPlayer.getCurrentTime()); + this.link.value = baseURL + "#" + Math.round(this.platformPlayer.getCurrentTime()); } break; } } } } -function toggleLinkMode(linkMode, link) +Player.prototype.toggleLinkMode = function() { - linkTimestamp = !linkTimestamp; - if(linkTimestamp == true) + this.linkTimestamp = !this.linkTimestamp; + if(this.linkTimestamp == true) { - linkMode.textContent = "Link to: current timestamp"; + this.linkMode.textContent = "Link to: current timestamp"; } else { - linkMode.textContent = "Link to: nearest second"; + this.linkMode.textContent = "Link to: nearest second"; } - updateLink(); + this.updateLink(); } -function toggleFilterOrLinkMode() +Player.prototype.toggleFilterOrLinkMode = function() { - for(menuIndex in menuState) + switch(this.MenusFocused.MenuID) { - if(menuState[menuIndex].classList.contains("filter_container") && menuState[menuIndex].classList.contains("visible")) - { - toggleFilterMode(); - } - if(menuState[menuIndex].classList.contains("link_container") && menuState[menuIndex].classList.contains("visible")) - { - toggleLinkMode(linkMode, link); - } + case menu_id.FILTER: + { + this.toggleFilterMode(); + } break; + case menu_id.LINK: + { + this.toggleLinkMode(); + } break; } } -function toggleMenuVisibility(element) { +function HideMenu(MenuContainer) +{ + if(MenuContainer != null) + { + MenuContainer.classList.remove("visible"); + MenuContainer.parentNode.classList.remove("visible"); + } +} + +function ShowMenu(MenuContainer) +{ + if(MenuContainer != null) + { + 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")) { - element.classList.remove("visible"); - element.parentNode.classList.remove("visible"); - if(focusedElement) + HideMenu(element); + + if(Trigger == trigger_id.KEYBOARD && this.Menus[menu_id.MARKERS].Item.LastFocused) { - focusedElement.classList.remove("focused"); - focusedElement = null; - } - if(focusedIdentifier) - { - focusedIdentifier.classList.remove("focused"); - focusedIdentifier = null; + var Best = this.Menus[menu_id.MARKERS].Item.LastFocused; + 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); + } } } else { - for(menuIndex in menuState) + this.MenusFocused.MenuID = MenuID; + for(var i in this.Menus) { - menuState[menuIndex].classList.remove("visible"); - menuState[menuIndex].parentNode.classList.remove("visible"); - if(focusedElement) - { - focusedElement.classList.remove("focused"); - } - if(focusedIdentifier) - { - focusedIdentifier.classList.remove("focused"); - } + HideMenu(this.Menus[i].Container); } - element.classList.add("visible"); - element.parentNode.classList.add("visible"); + ShowMenu(element); - if(element.classList.contains("quotes_container")) + switch(MenuID) { - if(!lastFocusedQuote) - { - lastFocusedQuote = element.querySelectorAll(".ref")[0]; - } - focusedElement = lastFocusedQuote; - focusedElement.classList.add("focused"); - } - else if(element.classList.contains("references_container")) - { - if(!lastFocusedReference || !lastFocusedIdentifier) - { - lastFocusedReference = element.querySelectorAll(".ref")[0]; - lastFocusedIdentifier = lastFocusedReference.querySelector(".ref_indices").firstElementChild; - } - focusedElement = lastFocusedReference; - focusedElement.classList.add("focused"); - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - } - else if(element.classList.contains("filter_container")) - { - if(!lastFocusedCategory) - { - lastFocusedCategory = element.querySelectorAll(".filter_content")[0]; - } - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - } - else if(element.classList.contains("credits_container")) - { - if(!lastFocusedCreditItem) - { - if(element.querySelectorAll(".credit .person")[0].nextElementSibling) + case menu_id.QUOTES: { - lastFocusedCreditItem = element.querySelectorAll(".credit .support")[0]; - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - setSpriteLightness(focusedElement.firstChild); - } - else + 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: { - lastFocusedCreditItem = element.querySelectorAll(".credit .person")[0]; - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - } - } - else - { - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - } + 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; } } } -function handleMouseOverViewsMenu() +Player.prototype.handleMouseOverViewsMenu = function() { switch(CineraProps.V) { case views.REGULAR: case views.THEATRE: { - viewsContainer.style.display = "block"; + this.Menus[menu_id.VIEWS].Container.classList.add("visible"); } break; case views.SUPERTHEATRE: { - viewsContainer.style.display = "none"; } break; } + this.MenusFocused.MenuID = menu_id.VIEWS; } function IsFullScreen() @@ -1250,95 +1745,101 @@ function leaveFullScreen_() } } -function toggleTheatreMode() { - switch(CineraProps.V) +Player.prototype.toggleTheatreMode = function() { + if(!CineraProps.IsMobile) { - case views.REGULAR: - { - CineraProps.C = cinera.style.backgroundColor; - CineraProps.Z = cinera.style.zIndex; - CineraProps.X = cinera.style.left; - CineraProps.Y = cinera.style.top; - CineraProps.W = cinera.style.width; - CineraProps.mW = cinera.style.maxWidth; - CineraProps.H = cinera.style.height; - CineraProps.mH = cinera.style.maxHeight; - CineraProps.P = cinera.style.position; - CineraProps.Display = cinera.style.display; - CineraProps.FlexDirection = cinera.style.flexDirection; - CineraProps.JustifyContent = cinera.style.justifyContent; - CineraProps.ScrollX = window.scrollX; - CineraProps.ScrollY = window.scrollY; + 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; - cinera.style.backgroundColor = "#000"; - cinera.style.zIndex = 64; - cinera.style.left = 0; - cinera.style.top = 0; - cinera.style.width = "100%"; - cinera.style.maxWidth = "100%"; - cinera.style.height = "100%"; - cinera.style.maxHeight = "100%"; - cinera.style.position = "fixed"; - cinera.style.display = "flex"; - cinera.style.flexDirection = "column"; - cinera.style.justifyContent = "center"; + 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"; - viewItems[0].setAttribute("data-id", "regular"); - viewItems[0].setAttribute("title", "Regular mode"); - viewItems[0].firstChild.nodeValue = "📺"; - } CineraProps.V = views.THEATRE; localStorage.setItem(cineraViewStorageItem, views.THEATRE); break; - case views.SUPERTHEATRE: - { - leaveFullScreen_(); - } - case views.THEATRE: - { - cinera.style.backgroundColor = CineraProps.C; - cinera.style.zIndex = CineraProps.Z; - cinera.style.left = CineraProps.X; - cinera.style.top = CineraProps.Y; - cinera.style.width = CineraProps.W; - cinera.style.maxWidth = CineraProps.mW; - cinera.style.height = CineraProps.H; - cinera.style.maxHeight = CineraProps.mH; - cinera.style.position = CineraProps.P; - cinera.style.display = CineraProps.Display; - cinera.style.flexDirection = CineraProps.FlexDirection; - cinera.style.justifyContent = CineraProps.JustifyContent; - window.scroll( - { - top: CineraProps.ScrollY, - left: CineraProps.ScrollX - } - ); + 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 + } + ); - viewItems[0].setAttribute("data-id", "theatre"); - viewItems[0].setAttribute("title", "Theatre mode"); - viewItems[0].firstChild.nodeValue = "🎭"; - } CineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break; + 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(); } - player.updateSize(); } -function toggleSuperTheatreMode() +Player.prototype.toggleSuperTheatreMode = function() { - switch(CineraProps.V) + if(!CineraProps.IsMobile) { - case views.REGULAR: - { - toggleTheatreMode(); - } - case views.THEATRE: - { - enterFullScreen_(); - } CineraProps.V = views.SUPERTHEATRE; localStorage.setItem(cineraViewStorageItem, views.SUPERTHEATRE); break; - case views.SUPERTHEATRE: - { - leaveFullScreen_(); - toggleTheatreMode(); - } CineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break; + 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(); } - player.updateSize(); } function AscribeTemporaryResponsibility(Element, Milliseconds) @@ -1355,496 +1856,631 @@ function SelectText(inputElement) inputElement.select(); } -function CopyToClipboard(inputElement) +Player.prototype.CopyToClipboard = function(inputElement) { SelectText(inputElement); document.execCommand("copy"); - AscribeTemporaryResponsibility(linkMenu.parentNode, 8000); + AscribeTemporaryResponsibility(this.Menus[menu_id.LINK].Container.parentNode, 8000); } -function handleKey(key) { +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) { var gotKey = true; switch (key) { - case "q": { - if(quotesMenu) + case "Escape": { + switch(this.MenusFocused.MenuID) { - toggleMenuVisibility(quotesMenu) + 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; + case "q": { + if(this.Menus[menu_id.QUOTES].Container) + { + this.toggleMenuVisibility(menu_id.QUOTES, trigger_id.KEYBOARD); } } break; case "r": { - if(referencesMenu) + if(this.Menus[menu_id.REFERENCES].Container) { - toggleMenuVisibility(referencesMenu) + this.toggleMenuVisibility(menu_id.REFERENCES, trigger_id.KEYBOARD); } } break; case "f": { - if(filterMenu) + if(this.Menus[menu_id.FILTER].Container) { - toggleMenuVisibility(filterMenu) + this.toggleMenuVisibility(menu_id.FILTER, trigger_id.KEYBOARD); } } break; case "y": { - if(linkMenu) + if(this.Menus[menu_id.LINK].Container) { - toggleMenuVisibility(linkMenu) + this.toggleMenuVisibility(menu_id.LINK, trigger_id.KEYBOARD); } break; } case "c": { - if(creditsMenu) + if(this.Menus[menu_id.CREDITS].Container) { - toggleMenuVisibility(creditsMenu) + this.toggleMenuVisibility(menu_id.CREDITS, trigger_id.KEYBOARD); } } break; case "t": { - if(cinera) + if(this.root) { - toggleTheatreMode(); + this.toggleTheatreMode(); } } break; case "T": { - if(cinera) + if(this.root) { - toggleSuperTheatreMode(); + this.toggleSuperTheatreMode(); } } break; case "Enter": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("quotes_container")) + switch(this.MenusFocused.MenuID) { - var time = focusedElement.querySelector(".timecode").getAttribute("data-timestamp"); - player.setTimeThenPlay(parseInt(time)); - } - else if(focusedElement.parentNode.classList.contains("references_container")) - { - var time = focusedIdentifier.getAttribute("data-timestamp"); - player.setTimeThenPlay(parseInt(time)); - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.hasAttribute) - { - var url = focusedElement.getAttribute("href"); - window.open(url, "_blank"); - } + case menu_id.MARKERS: + { + var time = this.MenusFocused.Item.getAttribute("data-timestamp"); + this.setTimeThenPlay(parseInt(time)); + } break; + case menu_id.QUOTES: + { + var time = this.MenusFocused.Item.querySelector(".timecode").getAttribute("data-timestamp"); + this.setTimeThenPlay(parseInt(time)); + 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"); + this.setTimeThenPlay(parseInt(time)); + 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; } } else { - console.log("TODO(matt): Implement me, perhaps?\n"); + if(this.currentMarker && this.currentMarker.el) + { + var time = this.currentMarker.el.getAttribute("data-timestamp"); + this.setTimeThenPlay(parseInt(time)); + this.setScroller(this.Menus[menu_id.MARKERS], this.currentMarker.el, true, false); + } } } break; case "o": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("references_container") || - focusedElement.parentNode.classList.contains("quotes_container")) + switch(this.MenusFocused.MenuID) { - if(player) { player.pause(); } - var url = focusedElement.getAttribute("href"); - window.open(url, "_blank"); - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.hasAttribute("href")) - { - if(player) { player.pause(); } - var url = focusedElement.getAttribute("href"); - window.open(url, "_blank"); - } + 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; } } } break; case "w": case "k": case "ArrowUp": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("quotes_container")) + switch(this.MenusFocused.MenuID) { - if(focusedElement.previousElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - - lastFocusedQuote = focusedElement.previousElementSibling; - focusedElement = lastFocusedQuote; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("references_container")) - { - if(focusedElement.previousElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - - lastFocusedReference = focusedElement.previousElementSibling; - focusedElement = lastFocusedReference; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - - lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - } - else if(focusedElement.parentNode.parentNode.classList.contains("filters")) - { - if(focusedElement.previousElementSibling && - focusedElement.previousElementSibling.classList.contains("filter_content")) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - - lastFocusedCategory = focusedElement.previousElementSibling; - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.parentNode.previousElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - if(focusedElement.parentNode.previousElementSibling.querySelector(".support") && - focusedElement.classList.contains("support")) + case menu_id.MARKERS: { - setSpriteLightness(focusedElement.firstChild); - lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support"); - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - else + 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: { - lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person"); - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); + 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); + + 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)); + + 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; + } + } + else + { + if(key != "ArrowUp") + { + var Best; + if(this.currentMarker && this.currentMarker.el) + { + Best = this.currentMarker.el; + if(Best.previousElementSibling) + { + Best = Best.previousElementSibling; + while(Best.classList.contains("skip") && Best.previousElementSibling) + { + Best = Best.previousElementSibling; + } } } + else + { + Best = this.markers[0].el; + 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 "s": case "j": case "ArrowDown": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("quotes_container")) + switch(this.MenusFocused.MenuID) { - if(focusedElement.nextElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - - lastFocusedQuote = focusedElement.nextElementSibling; - focusedElement = lastFocusedQuote; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("references_container")) - { - if(focusedElement.nextElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - - lastFocusedReference = focusedElement.nextElementSibling; - focusedElement = lastFocusedReference; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - - lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - } - else if(focusedElement.parentNode.parentNode.classList.contains("filters")) - { - if(focusedElement.nextElementSibling && - focusedElement.nextElementSibling.classList.contains("filter_content")) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - - lastFocusedCategory = focusedElement.nextElementSibling; - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.parentNode.nextElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - if(focusedElement.parentNode.nextElementSibling.querySelector(".support") && - focusedElement.classList.contains("support")) + case menu_id.MARKERS: { - setSpriteLightness(focusedElement.firstChild); - lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support"); - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - else + 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: { - lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person"); - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); + 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); + + 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)); + + 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; + } + } + else + { + if(key != "ArrowDown") + { + var Best; + if(this.currentMarker && this.currentMarker.el) + { + Best = this.currentMarker.el; + if(Best.nextElementSibling) + { + Best = Best.nextElementSibling; + while(Best.classList.contains("skip") && Best.nextElementSibling) + { + Best = Best.nextElementSibling; + } } } + else + { + Best = this.markers[0].el; + 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 "a": case "h": case "ArrowLeft": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("references_container")) + switch(this.MenusFocused.MenuID) { - if(focusedIdentifier.previousElementSibling) - { - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - lastFocusedIdentifier = focusedIdentifier.previousElementSibling; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - else if(focusedIdentifier.parentNode.previousElementSibling.classList.contains("ref_indices")) - { - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - lastFocusedIdentifier = focusedIdentifier.parentNode.previousElementSibling.lastElementChild; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - } - else if(focusedElement.classList.contains("filter_content")) - { - if(focusedElement.parentNode.classList.contains("filter_media") && - focusedElement.parentNode.previousElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - lastFocusedMedium = focusedElement; - - if(!lastFocusedTopic) + case menu_id.REFERENCES: { - lastFocusedTopic = focusedElement.parentNode.previousElementSibling.children[1]; - } - lastFocusedCategory = lastFocusedTopic; - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.classList.contains("support")) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); + 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]); - lastFocusedCreditItem = focusedElement.previousElementSibling; - setSpriteLightness(focusedElement.firstChild); - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } + this.setScroller(this.Menus[menu_id.FILTER], this.MenusFocused.Item, true, false); + } + } break; + case menu_id.CREDITS: + { + 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; } } } break; case "d": case "l": case "ArrowRight": { - if(focusedElement) + if(this.MenusFocused.Item) { - if(focusedElement.parentNode.classList.contains("references_container")) + switch(this.MenusFocused.MenuID) { - if(focusedIdentifier.nextElementSibling) - { - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - - lastFocusedIdentifier = focusedIdentifier.nextElementSibling; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - else if(focusedIdentifier.parentNode.nextElementSibling) - { - focusedIdentifier.classList.remove("focused"); - unfocusSprite(focusedIdentifier); - lastFocusedIdentifier = focusedIdentifier.parentNode.nextElementSibling.firstElementChild; - focusedIdentifier = lastFocusedIdentifier; - focusedIdentifier.classList.add("focused"); - focusSprite(focusedIdentifier); - } - } - else if(focusedElement.classList.contains("filter_content")) - { - if(focusedElement.parentNode.classList.contains("filter_topics") && - focusedElement.parentNode.nextElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - lastFocusedTopic = focusedElement; - - if(!lastFocusedMedium) + case menu_id.REFERENCES: { - lastFocusedMedium = focusedElement.parentNode.nextElementSibling.children[1]; - } - lastFocusedCategory = lastFocusedMedium; - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } - } - else if(focusedElement.parentNode.classList.contains("credit")) - { - if(focusedElement.classList.contains("person") && - focusedElement.nextElementSibling) - { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); + 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]); - lastFocusedCreditItem = focusedElement.nextElementSibling; - focusedElement = lastFocusedCreditItem; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); - } + 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; } } } break; case "x": case " ": { - if(focusedElement && focusedElement.classList.contains("filter_content")) + if(this.MenusFocused.Item && this.MenusFocused.MenuID == menu_id.FILTER) { - filterItemToggle(focusedElement); - if(focusedElement.nextElementSibling && - focusedElement.nextElementSibling.classList.contains("filter_content")) + this.filterItemToggle(this.MenusFocused.Item); + if(this.MenusFocused.Item.nextElementSibling && + this.MenusFocused.Item.nextElementSibling.classList.contains("filter_content")) { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - if(focusedElement.parentNode.classList.contains("filter_topics")) + 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")) { - lastFocusedTopic = focusedElement.nextElementSibling; - lastFocusedCategory = lastFocusedTopic; + this.Menus[menu_id.FILTER].Topic.LastFocused = this.MenusFocused.Item; } else { - lastFocusedMedium = focusedElement.nextElementSibling; - lastFocusedCategory = lastFocusedMedium; + this.Menus[menu_id.FILTER].Medium.LastFocused = this.MenusFocused.Item; } - lastFocusedElement = lastFocusedCategory; - focusedElement = lastFocusedElement; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); } } } break; case "X": case "capitalSpace": { - if(focusedElement && focusedElement.classList.contains("filter_content")) + if(this.MenusFocused.Item && this.MenusFocused.MenuID == menu_id.FILTER) { - filterItemToggle(focusedElement); - if(focusedElement.previousElementSibling && - focusedElement.previousElementSibling.classList.contains("filter_content")) + this.filterItemToggle(this.MenusFocused.Item); + if(this.MenusFocused.Item.previousElementSibling && + this.MenusFocused.Item.previousElementSibling.classList.contains("filter_content")) { - focusedElement.classList.remove("focused"); - unfocusSprite(focusedElement); - if(focusedElement.parentNode.classList.contains("filter_topics")) + 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")) { - lastFocusedTopic = focusedElement.previousElementSibling; - lastFocusedCategory = lastFocusedTopic; + this.Menus[menu_id.FILTER].Topic.LastFocused = this.MenusFocused.Item; } else { - lastFocusedMedium = focusedElement.previousElementSibling; - lastFocusedCategory = lastFocusedMedium; + this.Menus[menu_id.FILTER].Medium.LastFocused = this.MenusFocused.Item; } - lastFocusedElement = lastFocusedCategory; - focusedElement = lastFocusedElement; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); } } } break; case "z": { - toggleFilterOrLinkMode(); + this.toggleFilterOrLinkMode(); } break; case "v": { - if(focusedElement && focusedElement.classList.contains("filter_content")) + if(this.MenusFocused.Item && this.MenusFocused.MenuID == menu_id.FILTER) { - invertFilter(focusedElement) + this.invertFilter(this.MenusFocused.Item) } } break; case "V": { - resetFilter(); + this.resetFilter(); } break; case "?": { - if(helpDocumentation) + if(this.helpDocumentation) { - helpDocumentation.classList.toggle("visible"); + this.helpDocumentation.classList.toggle("visible"); } } break; case 'N': case 'J': case 'S': { - player.jumpToNextMarker(); + this.jumpToNextMarker(); } break; case 'P': case 'K': case 'W': { - player.jumpToPrevMarker(); + this.jumpToPrevMarker(); } break; case '[': case '<': { - if(prevEpisode) + if(this.prevEpisode) { - location = prevEpisode.href; + location = this.prevEpisode.href; } } break; case ']': case '>': { - if(nextEpisode) + if(this.nextEpisode) { - location = nextEpisode.href; + location = this.nextEpisode.href; } } break; case 'Y': { if(cineraLink) { - if(linkTimestamp == false && player.playing) + if(this.linkTimestamp == false && this.playing) { - player.pause(); + this.pause(); } - if(linkMenu && !linkMenu.classList.contains("visible")) + if(this.Menus[menu_id.LINK].Container && !this.Menus[menu_id.LINK].Container.classList.contains("visible")) { - toggleMenuVisibility(linkMenu); + this.toggleMenuVisibility(menu_id.LINK, trigger_id.KEYBOARD); } SelectText(cineraLink); } @@ -1856,42 +2492,45 @@ function handleKey(key) { return gotKey; } -function applyFilter() { - if(filterMode == "exclusive") +Player.prototype.applyFilter = function() { + if(this.filterMode == "exclusive") { - for(var i = 0; i < testMarkers.length; ++i) + for(var i = 0; i < this.Menus[menu_id.MARKERS].Elements.length; ++i) { - var testCategories = testMarkers[i].classList; + var Item = this.Menus[menu_id.MARKERS].Elements[i]; + var testCategories = Item.classList; for(var j = 0; j < testCategories.length; ++j) { - if((testCategories[j].startsWith("off_")) && !testMarkers[i].classList.contains("skip")) + if((testCategories[j].startsWith("off_")) && !Item.classList.contains("skip")) { - testMarkers[i].classList.add("skip"); + Item.classList.add("skip"); } } } } else { - for(var i = 0; i < testMarkers.length; ++i) + for(var i = 0; i < this.Menus[menu_id.MARKERS].Elements.length; ++i) { - var testCategories = testMarkers[i].classList; + var Item = this.Menus[menu_id.MARKERS].Elements[i]; + var testCategories = Item.classList; for(var j = 0; j < testCategories.length; ++j) { - if((testCategories[j] in filterState || testCategories[j].startsWith("cat_")) && testMarkers[i].classList.contains("skip")) + var testCategory = testCategories[j]; + if((testCategory in this.filterState || testCategory.startsWith("cat_")) && Item.classList.contains("skip")) { - testMarkers[i].classList.remove("skip"); + Item.classList.remove("skip"); } } } } } -function filterItemToggle(filterItem) { +Player.prototype.filterItemToggle = function(filterItem) { var selectedCategory = filterItem.classList[1]; - filterState[selectedCategory].off = !filterState[selectedCategory].off; + this.filterState[selectedCategory].off = !this.filterState[selectedCategory].off; - if(filterState[selectedCategory].off) + if(this.filterState[selectedCategory].off) { filterItem.classList.add("off"); disableSprite(filterItem); @@ -1899,59 +2538,62 @@ function filterItemToggle(filterItem) { { filterItem.querySelector(".icon").style.backgroundColor = "transparent"; } - var testMarkers = playerContainer.querySelectorAll(".marker." + selectedCategory + ", .marker.cat_" + selectedCategory); - for(var j = 0; j < testMarkers.length; ++j) + var testMarkers = this.Menus[menu_id.MARKERS].Container.querySelectorAll(".marker." + selectedCategory + ", .marker.cat_" + selectedCategory); + for(var i = 0; i < testMarkers.length; ++i) { - if(filterState[selectedCategory].type == "topic") + var testMarker = testMarkers[i]; + if(this.filterState[selectedCategory].type == "topic") { - testMarkers[j].classList.remove("cat_" + selectedCategory); - testMarkers[j].classList.add("off_" + selectedCategory); - var markerCategories = testMarkers[j].querySelectorAll(".category." + selectedCategory); - for(var k = 0; k < markerCategories.length; ++k) + testMarker.classList.remove("cat_" + selectedCategory); + testMarker.classList.add("off_" + selectedCategory); + var markerCategories = testMarker.querySelectorAll(".category." + selectedCategory); + for(var j = 0; j < markerCategories.length; ++j) { - if(markerCategories[k].classList.contains(selectedCategory)) + var markerCategory = markerCategories[j]; + if(markerCategory.classList.contains(selectedCategory)) { - markerCategories[k].classList.add("off"); - markerCategories[k].style.backgroundColor = "transparent"; + markerCategory.classList.add("off"); + markerCategory.style.backgroundColor = "transparent"; } } } else { - var markerCategories = testMarkers[j].querySelectorAll(".categoryMedium." + selectedCategory); - for(var k = 0; k < markerCategories.length; ++k) + var markerCategories = testMarker.querySelectorAll(".categoryMedium." + selectedCategory); + for(var j = 0; j < markerCategories.length; ++j) { - if(markerCategories[k].classList.contains(selectedCategory)) + var markerCategory = markerCategories[j]; + if(markerCategory.classList.contains(selectedCategory)) { - markerCategories[k].classList.add("off"); - disableSprite(markerCategories[k]); + markerCategory.classList.add("off"); + disableSprite(markerCategory); } } - testMarkers[j].classList.remove(selectedCategory); - testMarkers[j].classList.add("off_" + selectedCategory); + testMarker.classList.remove(selectedCategory); + testMarker.classList.add("off_" + selectedCategory); } - Skipping = 1; - if(filterMode == "exclusive") + var Skipping = true; + if(this.filterMode == "exclusive") { - testMarkers[j].classList.add("skip"); + testMarker.classList.add("skip"); } else { - var markerClasses = testMarkers[j].classList; - for(var k = 0; k < markerClasses.length; ++k) + var markerClasses = testMarker.classList; + for(var j = 0; j < markerClasses.length; ++j) { - if(markerClasses[k] in filterState || markerClasses[k].replace(/^cat_/, "") in filterState) + var markerClass = markerClasses[j]; + if(markerClass in this.filterState || markerClass.replace(/^cat_/, "") in this.filterState) { - Skipping = 0; + Skipping = false; } } if(Skipping) { - testMarkers[j].classList.add("skip"); + testMarker.classList.add("skip"); } } - } } else @@ -1963,109 +2605,197 @@ function filterItemToggle(filterItem) { filterItem.querySelector(".icon").style.backgroundColor = getComputedStyle(filterItem.querySelector(".icon")).getPropertyValue("border-color"); } setDotLightness(filterItem.querySelector(".icon")); - var testMarkers = cinera.querySelectorAll(".marker.off_" + selectedCategory); - for(var j = 0; j < testMarkers.length; ++j) + var testMarkers = this.Menus[menu_id.MARKERS].Container.querySelectorAll(".marker.off_" + selectedCategory); + for(var i = 0; i < testMarkers.length; ++i) { - if(filterState[selectedCategory].type == "topic") + var testMarker = testMarkers[i]; + if(this.filterState[selectedCategory].type == "topic") { - testMarkers[j].classList.remove("off_" + selectedCategory); - testMarkers[j].classList.add("cat_" + selectedCategory); - var markerCategories = testMarkers[j].querySelectorAll(".category." + selectedCategory); - for(var k = 0; k < markerCategories.length; ++k) + testMarker.classList.remove("off_" + selectedCategory); + testMarker.classList.add("cat_" + selectedCategory); + var markerCategories = testMarker.querySelectorAll(".category." + selectedCategory); + for(var j = 0; j < markerCategories.length; ++j) { - if(markerCategories[k].classList.contains(selectedCategory)) + var markerCategory = markerCategories[j]; + if(markerCategory.classList.contains(selectedCategory)) { - markerCategories[k].classList.remove("off"); - markerCategories[k].style.backgroundColor = getComputedStyle(markerCategories[k]).getPropertyValue("border-color"); - setDotLightness(markerCategories[k]); + markerCategory.classList.remove("off"); + markerCategory.style.backgroundColor = getComputedStyle(markerCategory).getPropertyValue("border-color"); + setDotLightness(markerCategory); } } } else { - testMarkers[j].classList.remove("off_" + selectedCategory); - testMarkers[j].classList.add(selectedCategory); - var markerCategories = testMarkers[j].querySelectorAll(".categoryMedium." + selectedCategory); - for(var k = 0; k < markerCategories.length; ++k) + testMarker.classList.remove("off_" + selectedCategory); + testMarker.classList.add(selectedCategory); + var markerCategories = testMarker.querySelectorAll(".categoryMedium." + selectedCategory); + for(var j = 0; j < markerCategories.length; ++j) { - if(markerCategories[k].classList.contains(selectedCategory)) + var markerCategory = markerCategories[j]; + if(markerCategory.classList.contains(selectedCategory)) { - markerCategories[k].classList.remove("off"); - enableSprite(markerCategories[k]); + markerCategory.classList.remove("off"); + enableSprite(markerCategory); } } } - Skipping = 0; - if(filterMode == "inclusive") + var Skipping = false; + if(this.filterMode == "inclusive") { - testMarkers[j].classList.remove("skip"); + testMarker.classList.remove("skip"); } else { - var markerClasses = testMarkers[j].classList; - for(var k = 0; k < markerClasses.length; ++k) + var markerClasses = testMarker.classList; + for(var j = 0; j < markerClasses.length; ++j) { - if(markerClasses[k].startsWith("off_")) + if(markerClasses[j].startsWith("off_")) { - Skipping = 1; + Skipping = true; } } if(!Skipping) { - testMarkers[j].classList.remove("skip"); + testMarker.classList.remove("skip"); } } } } } -function resetFilter() { - for(i in filterItems) +Player.prototype.resetFilter = function() { + for(var i in this.Menus[menu_id.FILTER].Elements) { - if(filterItems[i].classList) + var Item = this.Menus[menu_id.FILTER].Elements[i]; + if(Item.classList) { - var selectedCategory = filterItems[i].classList[1]; - if(filterInitState[selectedCategory].off ^ filterState[selectedCategory].off) + var selectedCategory = Item.classList[1]; + if(this.filterInitState[selectedCategory].off ^ this.filterState[selectedCategory].off) { - filterItemToggle(filterItems[i]); + this.filterItemToggle(Item); } } } - if(filterMode == "inclusive") + if(this.filterMode == "inclusive") { - toggleFilterMode(); + this.toggleFilterMode(); } } -function invertFilter(focusedElement) { - var siblings = focusedElement.parentNode.querySelectorAll(".filter_content"); - for(i in siblings) +Player.prototype.invertFilter = function(FocusedElement) { + var siblings = player.MenusFocused.Item.parentNode.querySelectorAll(".filter_content"); + for(var i in siblings) { - if(siblings[i].classList) + var sibling = siblings[i]; + if(sibling.classList) { - filterItemToggle(siblings[i]); + this.filterItemToggle(sibling); } } } -function resetFade() { - filter.classList.remove("responsible"); - filter.querySelector(".filter_mode").classList.remove("responsible"); - var responsibleCategories = filter.querySelectorAll(".filter_content.responsible"); +function resetFade(player) { + player.filter.classList.remove("responsible"); + player.filter.querySelector(".filter_mode").classList.remove("responsible"); + var responsibleCategories = player.filter.querySelectorAll(".filter_content.responsible"); for(var i = 0; i < responsibleCategories.length; ++i) { responsibleCategories[i].classList.remove("responsible"); } } +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"); + } + } + } +} + function onRefChanged(ref, element, player) { if(element.classList.contains("skip")) { var ErrorCount = 0; - if(!filter) { console.log("Missing filter_container div"); ErrorCount++; } - if(!filterState) { console.log("Missing filterState object"); ErrorCount++; } + if(!player.filter) { console.log("Missing filter_container div"); ErrorCount++; } + if(!player.filterState) { console.log("Missing filterState object"); ErrorCount++; } if(ErrorCount > 0) { switch(ErrorCount) @@ -2078,28 +2808,29 @@ function onRefChanged(ref, element, player) { console.log(element); return; } - if(!filter.classList.contains("responsible")) + if(!player.filter.classList.contains("responsible")) { - filter.classList.add("responsible"); + player.filter.classList.add("responsible"); } for(var selector = 0; selector < element.classList.length; ++selector) { - if(element.classList[selector].startsWith("off_")) + var elementClass = element.classList[selector]; + if(elementClass.startsWith("off_")) { - if(!filter.querySelector(".filter_content." + element.classList[selector].replace(/^off_/, "")).classList.contains("responsible")) + if(!player.filter.querySelector(".filter_content." + elementClass.replace(/^off_/, "")).classList.contains("responsible")) { - filter.querySelector(".filter_content." + element.classList[selector].replace(/^off_/, "")).classList.add("responsible"); + player.filter.querySelector(".filter_content." + elementClass.replace(/^off_/, "")).classList.add("responsible"); } } - if(element.classList[selector].startsWith("cat_") || element.classList[selector] in filterState) + if(elementClass.startsWith("cat_") || elementClass in player.filterState) { - if(!filter.querySelector(".filter_mode").classList.add("responsible")) + if(!player.filter.querySelector(".filter_mode").classList.add("responsible")) { - filter.querySelector(".filter_mode").classList.add("responsible"); + player.filter.querySelector(".filter_mode").classList.add("responsible"); } } - setTimeout(resetFade, 8000); + setTimeout(resetFade, 8000, player); } if(player && player.playing) { @@ -2108,121 +2839,73 @@ function onRefChanged(ref, element, player) { } else { - for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex) - { - var Menu = sourceMenus[MenuIndex]; - var SetMenu = 0; - if (ref !== undefined && ref !== null) { - var refElements = Menu.querySelectorAll(".refs .ref"); - var refs = ref.split(","); - - for (var i = 0; i < refElements.length; ++i) { - if (refs.includes(refElements[i].getAttribute("data-id"))) { - refElements[i].classList.add("current"); - if(!SetMenu) - { - if(!referencesMenu.classList.contains("visible")) - { - referencesMenu.classList.add("visible"); - referencesMenu.scrollTop = refElements[i].offsetTop; - referencesMenu.classList.remove("visible"); - } - else - { - player.scrollRefsTo = refElements[i].offsetTop; - player.scrollRefsPosition = referencesMenu.scrollTop; - } - } - SetMenu = 1; - } else { - refElements[i].classList.remove("current"); - } - } - if(SetMenu) { - Menu.classList.add("current"); - } else { - Menu.classList.remove("current"); - } - - } else { - Menu.classList.remove("current"); - var refs = Menu.querySelectorAll(".refs .ref"); - for (var i = 0; i < refs.length; ++i) { - refs[i].classList.remove("current"); - } - } - } + player.alignMenuWithTimestamp(ref, element, menu_id.QUOTES); + player.alignMenuWithTimestamp(ref, element, menu_id.REFERENCES); } } -function navigateFilter(filterItem) { - if(filterItem != lastFocusedCategory) +Player.prototype.navigateFilter = function(filterItem) { + if(filterItem != this.Menus[menu_id.FILTER].Category.LastFocused) { - lastFocusedCategory.classList.remove("focused"); - unfocusSprite(lastFocusedCategory); - if(filterItem.parentNode.classList.contains("filter_topics")) - { - lastFocusedTopic = filterItem; - lastFocusedCategory = lastFocusedTopic; - } - else - { - lastFocusedMedium = filterItem; - lastFocusedCategory = lastFocusedMedium; - } - focusedElement = lastFocusedCategory; - focusedElement.classList.add("focused"); - focusSprite(focusedElement); + 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; } } -function mouseOverQuotes(quote) { - if(focusedElement && quote != lastFocusedQuote) +Player.prototype.mouseOverReferenceOrQuoteIdentifier = function(MenuID, identifier) { + var MenuEntity = this.Menus[MenuID]; + if(this.MenusFocused.Item && MenuEntity.Item.LastFocused != identifier.closest(".ref")) { - focusedElement.classList.remove("focused"); - lastFocusedQuote = quote; - focusedElement = lastFocusedQuote; - focusedElement.classList.add("focused"); + this.unfocusUIElement(focus_level.ITEM); } + this.focusUIElement(focus_level.ITEM, MenuID, MenuEntity.Item, identifier.closest(".ref")); + + if(this.MenusFocused.Identifier && identifier != MenuEntity.Identifier.LastFocused) + { + this.unfocusUIElement(focus_level.IDENTIFIER); + } + this.focusUIElement(focus_level.IDENTIFIER, MenuID, MenuEntity.Identifier, identifier); } -function mouseOverReferences(reference) { - if(focusedElement && reference != lastFocusedReference) +Player.prototype.mouseOverReferencesOrQuotes = function(MenuID, item) { + var MenuEntity = this.Menus[MenuID]; + if(this.MenusFocused.Item && item != MenuEntity.Item.LastFocused) { - focusedElement.classList.remove("focused") - lastFocusedReference = reference; + this.unfocusUIElement(focus_level.ITEM); } - focusedElement = lastFocusedReference; - focusedElement.classList.add("focused"); + this.focusUIElement(focus_level.ITEM, MenuID, MenuEntity.Item, item); - var ourIdentifiers = reference.querySelectorAll(".timecode"); - weWereLastFocused = false; - for(var k = 0; k < ourIdentifiers.length; ++k) + var ourIdentifiers = item.querySelectorAll(".timecode"); + var weWereLastFocused = false; + for(var i = 0; i < ourIdentifiers.length; ++i) { - if(ourIdentifiers[k] == lastFocusedIdentifier) + if(ourIdentifiers[i] == MenuEntity.Identifier.LastFocused) { weWereLastFocused = true; } } if(!weWereLastFocused) { - lastFocusedIdentifier.classList.remove("focused"); - lastFocusedIdentifier = ourIdentifiers[0]; + this.unfocusUIElement(focus_level.IDENTIFIER); } - focusedIdentifer = lastFocusedIdentifier; - focusedIdentifer.classList.add("focused"); + this.focusUIElement(focus_level.IDENTIFIER, MenuID, MenuEntity.Identifier, getMostRecentCitation(this.currentTime, ourIdentifiers)); +} - for(var l = 0; l < ourIdentifiers.length; ++l) +Player.prototype.mouseOverCredits = function(item) { + if(this.MenusFocused.Item && item != this.Menus[menu_id.CREDITS].Item.LastFocused) { - ourIdentifiers[l].addEventListener("mouseenter", function(ev) { - if(this != lastFocusedIdentifier) - { - lastFocusedIdentifier.classList.remove("focused"); - lastFocusedIdentifier = this; - lastFocusedIdentifier.classList.add("focused"); - } - }) + this.unfocusUIElement(focus_level.ITEM); } + this.focusUIElement(focus_level.ITEM, menu_id.CREDITS, this.Menus[menu_id.CREDITS].Item, item); } function mouseSkipToTimecode(player, time, ev) @@ -2233,31 +2916,31 @@ function mouseSkipToTimecode(player, time, ev) return false; } -function handleMenuTogglerInteraction(menu, eventType) +Player.prototype.handleMenuTogglerInteraction = function(menu, eventType) { - if(!(menu.classList.contains("visible")) && eventType == "mouseenter" || + if(!menu.classList.contains("visible") && eventType == "mouseenter" || menu.classList.contains("visible") && eventType == "mouseleave" || (eventType == "click" && !menu.classList.contains("cineraHelp"))) { if(menu.classList.contains("quotes")) { - toggleMenuVisibility(quotesMenu); + this.toggleMenuVisibility(menu_id.QUOTES, trigger_id.MOUSE); } else if(menu.classList.contains("references")) { - toggleMenuVisibility(referencesMenu); + this.toggleMenuVisibility(menu_id.REFERENCES, trigger_id.MOUSE); } else if(menu.classList.contains("filter")) { - toggleMenuVisibility(filterMenu); + this.toggleMenuVisibility(menu_id.FILTER, trigger_id.MOUSE); } else if(menu.classList.contains("link")) { - toggleMenuVisibility(linkMenu); + this.toggleMenuVisibility(menu_id.LINK, trigger_id.MOUSE); } else if(menu.classList.contains("credits")) { - toggleMenuVisibility(creditsMenu); + this.toggleMenuVisibility(menu_id.CREDITS, trigger_id.MOUSE); } } } diff --git a/cinera/cinera_pre.js b/cinera/cinera_pre.js index 1827d90..cab734c 100644 --- a/cinera/cinera_pre.js +++ b/cinera/cinera_pre.js @@ -186,14 +186,21 @@ function enableSprite(Element) function disableSprite(Element) { - if(Element.classList.contains("cineraSprite")) + if(Element.classList.contains("focused")) { - setSpriteLightness(Element); - Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px"; + focusSprite(Element); } - for(var i = 0; i < Element.childElementCount; ++i) + else { - disableSprite(Element.children[i]); + if(Element.classList.contains("cineraSprite")) + { + setSpriteLightness(Element); + Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px"; + } + for(var i = 0; i < Element.childElementCount; ++i) + { + disableSprite(Element.children[i]); + } } } @@ -503,17 +510,29 @@ IsOverflowed(Element) return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth; } +function +SetHelpUnfocused(Button) +{ + Button.firstElementChild.innerText = "¿"; + Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button"; +} + +function +SetHelpFocused(Button) +{ + Button.firstElementChild.innerText = "?"; + Button.firstElementChild.title = "" +} + function BindHelp(Button, DocumentationContainer) { window.addEventListener("blur", function(){ - Button.firstElementChild.innerText = "¿"; - Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button"; + SetHelpUnfocused(Button); }); window.addEventListener("focus", function(){ - Button.firstElementChild.innerText = "?"; - Button.firstElementChild.title = "" + SetHelpFocused(Button); }); Button.addEventListener("click", function() { @@ -553,13 +572,13 @@ function RGBtoHSL(colour) function getBackgroundColourRGB(element) { var Colour = getComputedStyle(element).getPropertyValue("background-color"); var depth = 0; - while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && depth <= 4) + while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && element.parentElement && depth <= 4) { - element = element.parentNode; + element = element.parentElement; Colour = getComputedStyle(element).getPropertyValue("background-color"); ++depth; } - var Staging = Colour.slice(4, -1).split(", "); + var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", "); var Result = { R: parseInt(Staging[0]), G: parseInt(Staging[1]),