From 4a0630beb04ed7fb317c571d39ee1a4b0a006aa8 Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Wed, 18 Apr 2018 00:05:14 +0100 Subject: [PATCH] cinera.c: Reference and player tweaks Different ISBN database, and slightly more flexible BuildReference() Permit line-breaking on '/' in the references menu cinera.css and cinera_player*.js: More subtle "click here to regain focus" - for @insofaras Persist theatre mode - for @insofaras Resume in-progress video at previous timecode - for @AsafGartner --- cinera/cinera.c | 167 +++++++++++++++++------------------ cinera/cinera.css | 11 --- cinera/cinera_player_post.js | 31 +++++-- cinera/cinera_player_pre.js | 28 ++++-- 4 files changed, 124 insertions(+), 113 deletions(-) diff --git a/cinera/cinera.c b/cinera/cinera.c index 916ab4c..bd43638 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -14,7 +14,7 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 5, - .Patch = 44 + .Patch = 45 }; // TODO(matt): Copy in the DB 3 stuff from cinera_working.c @@ -657,30 +657,38 @@ CopyStringToBufferHTMLSafe(buffer *Dest, char *String) } switch(*String) { - case '<': - CopyStringToBuffer(Dest, "<"); - String++; - break; - case '>': - CopyStringToBuffer(Dest, ">"); - String++; - break; - case '&': - CopyStringToBuffer(Dest, "&"); - String++; - break; - case '\"': - CopyStringToBuffer(Dest, """); - String++; - break; - case '\'': - CopyStringToBuffer(Dest, "'"); - String++; - break; - default: - *Dest->Ptr++ = *String++; - break; + case '<': CopyStringToBuffer(Dest, "<"); break; + case '>': CopyStringToBuffer(Dest, ">"); break; + case '&': CopyStringToBuffer(Dest, "&"); break; + case '\"': CopyStringToBuffer(Dest, """); break; + case '\'': CopyStringToBuffer(Dest, "'"); break; + default: *Dest->Ptr++ = *String; break; } + ++String; + } +} + +void +CopyStringToBufferHTMLSafeBreakingOnSlash(buffer *Dest, char *String) +{ + while(*String) + { + if(Dest->Ptr - Dest->Location >= Dest->Size) + { + fprintf(stderr, "CopyStringToBufferHTMLSafeBreakingOnSlash: %s cannot accommodate %d-character string\n", Dest->ID, StringLength(String)); + __asm__("int3"); + } + switch(*String) + { + case '<': CopyStringToBuffer(Dest, "<"); break; + case '>': CopyStringToBuffer(Dest, ">"); break; + case '&': CopyStringToBuffer(Dest, "&"); break; + case '\"': CopyStringToBuffer(Dest, """); break; + case '\'': CopyStringToBuffer(Dest, "'"); break; + case '/': CopyStringToBuffer(Dest, "/\u200B"); break; + default: *Dest->Ptr++ = *String; break; + } + ++String; } } @@ -691,7 +699,7 @@ CopyStringToBufferJSONSafe(buffer *Dest, char *String) { if(Dest->Ptr - Dest->Location >= Dest->Size) { - fprintf(stderr, "CopyStringToBufferHTMLSafe: %s cannot accommodate %d-character string\n", Dest->ID, StringLength(String)); + fprintf(stderr, "CopyStringToBufferJSONSafe: %s cannot accommodate %d-character string\n", Dest->ID, StringLength(String)); __asm__("int3"); } switch(*String) @@ -918,7 +926,8 @@ DeclaimBuffer(buffer *Buffer) Buffer->Size = 0; } -void RewindBuffer(buffer *Buffer) +void +RewindBuffer(buffer *Buffer) { #if DEBUG float PercentageUsed = (float)(Buffer->Ptr - Buffer->Location) / Buffer->Size * 100; @@ -1561,109 +1570,93 @@ BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Meta enum { - REF_SITE = 1 << 0, - REF_PAGE = 1 << 1, - REF_URL = 1 << 2, - REF_TITLE = 1 << 3, - REF_ARTICLE = 1 << 4, - REF_AUTHOR = 1 << 5, - REF_EDITOR = 1 << 6, + REF_SITE = 1 << 0, + REF_PAGE = 1 << 1, + REF_URL = 1 << 2, + REF_TITLE = 1 << 3, + REF_ARTICLE = 1 << 4, + REF_AUTHOR = 1 << 5, + REF_EDITOR = 1 << 6, REF_PUBLISHER = 1 << 7, - REF_ISBN = 1 << 8, + REF_ISBN = 1 << 8, } reference_fields; int BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMML_Reference *Ref, HMML_Annotation *Anno) { - int Mask = 0; + if(Ref->isbn) + { + CopyString(ReferencesArray[UniqueRefs].ID, Ref->isbn); + if(!Ref->url) { CopyString(ReferencesArray[UniqueRefs].URL, "https://isbndb.com/book/%s", Ref->isbn); } + else { CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } + } + else if(Ref->url) + { + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); + } + else { return RC_INVALID_REFERENCE; } + int Mask = 0; if(Ref->site) { Mask |= REF_SITE; } if(Ref->page) { Mask |= REF_PAGE; } - if(Ref->url) { Mask |= REF_URL; } if(Ref->title) { Mask |= REF_TITLE; } if(Ref->article) { Mask |= REF_ARTICLE; } if(Ref->author) { Mask |= REF_AUTHOR; } if(Ref->editor) { Mask |= REF_EDITOR; } if(Ref->publisher) { Mask |= REF_PUBLISHER; } - if(Ref->isbn) { Mask |= REF_ISBN; } - // TODO(matt): Consider handling the various combinations more flexibly + // TODO(matt): Consider handling the various combinations more flexibly, unless we defer this stuff until we have the + // reference store, in which we could optionally customise the display of each reference entry switch(Mask) { - case (REF_URL | REF_TITLE | REF_AUTHOR | REF_PUBLISHER | REF_ISBN): + case (REF_TITLE | REF_AUTHOR | REF_PUBLISHER): { - CopyString(ReferencesArray[UniqueRefs].ID, Ref->isbn); CopyString(ReferencesArray[UniqueRefs].Source, "%s (%s)", Ref->author, Ref->publisher); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_AUTHOR | REF_SITE | REF_PAGE | REF_URL): + case (REF_AUTHOR | REF_SITE | REF_PAGE): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); CopyString(ReferencesArray[UniqueRefs].RefTitle, "%s: \"%s\"", Ref->author, Ref->page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_PAGE | REF_URL | REF_TITLE): + case (REF_PAGE | REF_TITLE): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->title); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_SITE | REF_PAGE | REF_URL): + case (REF_SITE | REF_PAGE): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_SITE | REF_URL | REF_TITLE): + case (REF_SITE | REF_TITLE): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_TITLE | REF_AUTHOR | REF_ISBN): + case (REF_TITLE | REF_AUTHOR): { - CopyString(ReferencesArray[UniqueRefs].ID, Ref->isbn); - CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->author); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyString(ReferencesArray[UniqueRefs].URL, "http://www.openisbn.com/isbn/%s", Ref->isbn); } break; - case (REF_URL | REF_ARTICLE | REF_AUTHOR): + case (REF_ARTICLE | REF_AUTHOR): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); - CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->author); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->article); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_URL | REF_TITLE | REF_AUTHOR): + case (REF_TITLE | REF_PUBLISHER): { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); - CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->publisher); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_URL | REF_TITLE | REF_PUBLISHER): + case REF_TITLE: { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); - CopyString(ReferencesArray[UniqueRefs].Source, Ref->publisher); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; - case (REF_URL | REF_TITLE): + case REF_SITE: { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); - } break; - case (REF_SITE | REF_URL): - { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->site); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } break; default: return RC_INVALID_REFERENCE; break; } @@ -3170,8 +3163,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen "
\n" " ", StringsDiffer(Config.Theme, "") ? Config.Theme : HMML.metadata.project); CopyStringToBufferHTMLSafe(&CollationBuffers->Menus, HMML.metadata.title); - CopyStringToBuffer(&CollationBuffers->Menus, "\n" - " ⚠ Click here to regain focus ⚠\n"); + CopyStringToBuffer(&CollationBuffers->Menus, "\n"); CopyStringToBuffer(&CollationBuffers->Player, "
\n" @@ -3778,17 +3770,17 @@ AppendedIdentifier: { CopyStringToBuffer(&ReferenceMenu, "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].Source); + CopyStringToBufferHTMLSafeBreakingOnSlash(&ReferenceMenu, ReferencesArray[i].Source); CopyStringToBuffer(&ReferenceMenu, "
\n" "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); + CopyStringToBufferHTMLSafeBreakingOnSlash(&ReferenceMenu, ReferencesArray[i].RefTitle); CopyStringToBuffer(&ReferenceMenu, "
\n"); } else { CopyStringToBuffer(&ReferenceMenu, "
"); - CopyStringToBufferHTMLSafe(&ReferenceMenu, ReferencesArray[i].RefTitle); + CopyStringToBufferHTMLSafeBreakingOnSlash(&ReferenceMenu, ReferencesArray[i].RefTitle); CopyStringToBuffer(&ReferenceMenu, "
\n"); } CopyStringToBuffer(&ReferenceMenu, @@ -5154,7 +5146,8 @@ enum LINK_NEXT } link_directions; -int InsertNeighbourLink(file_buffer *FromFile, index_metadata *From, index_metadata *To, int LinkDirection, char *ProjectName, bool FromHasOneNeighbour) +int +InsertNeighbourLink(file_buffer *FromFile, index_metadata *From, index_metadata *To, int LinkDirection, char *ProjectName, bool FromHasOneNeighbour) { if(ReadFileIntoBuffer(FromFile, 0) == RC_SUCCESS) { @@ -5272,7 +5265,8 @@ int InsertNeighbourLink(file_buffer *FromFile, index_metadata *From, index_metad } } -int DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata) +int +DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata) { if(ReadFileIntoBuffer(File, 0) == RC_SUCCESS) { @@ -5293,7 +5287,8 @@ int DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata) else { return RC_ERROR_FILE; } } -int LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) +int +LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) { switch(ReadFileIntoBuffer(&Index->Metadata, 0)) { diff --git a/cinera/cinera.css b/cinera/cinera.css index 3566087..fed5531 100644 --- a/cinera/cinera.css +++ b/cinera/cinera.css @@ -131,17 +131,6 @@ flex: 1; } -.cineraMenus > #focus-warn { - background-color: rgba(0, 0, 0, 0.8); - border-radius: 16px; - color: #F00; - flex: 1; - margin: 0 auto 0 50%; - display: none; - z-index: 16; - position: absolute; -} - .cineraMenus > .menu { position: relative; } diff --git a/cinera/cinera_player_post.js b/cinera/cinera_player_post.js index ab80b9d..185ce26 100644 --- a/cinera/cinera_player_post.js +++ b/cinera/cinera_player_post.js @@ -184,6 +184,16 @@ if(creditsMenu) var sourceMenus = titleBar.querySelectorAll(".menu"); var helpButton = titleBar.querySelector(".help"); +window.addEventListener("blur", function(){ + helpButton.firstElementChild.innerText = "¿"; + helpButton.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"; +}); + +window.addEventListener("focus", function(){ + helpButton.firstElementChild.innerText = "?"; + helpButton.firstElementChild.title = "" +}); + var helpDocumentation = helpButton.querySelector(".help_container"); helpButton.addEventListener("click", function(ev) { handleMouseOverMenu(this, ev.type); @@ -196,6 +206,13 @@ var playerContainer = document.querySelector(".cineraPlayerContainer") var cinera = playerContainer.parentNode; var player = new Player(playerContainer, onRefChanged); + +var cineraViewStorageItem = "cineraView"; +if(viewsMenu && localStorage.getItem(cineraViewStorageItem)) +{ + toggleTheatreMode(); +} + window.addEventListener("resize", function() { player.updateSize(); }); document.addEventListener("keydown", function(ev) { var key = ev.key; @@ -224,14 +241,6 @@ var prevEpisode = playerContainer.querySelector(".episodeMarker.prev"); var nextEpisode = playerContainer.querySelector(".episodeMarker.next"); var testMarkers = playerContainer.querySelectorAll(".marker"); -window.addEventListener("blur", function(){ - document.getElementById("focus-warn").style.display = "block"; -}); - -window.addEventListener("focus", function(){ - document.getElementById("focus-warn").style.display = "none"; -}); - var colouredItems = playerContainer.querySelectorAll(".author, .member, .project"); for(i = 0; i < colouredItems.length; ++i) { @@ -244,6 +253,12 @@ for(var i = 0; i < topicDots.length; ++i) setDotLightness(topicDots[i]); } +var lastAnnotationStorageItem = "cineraTimecode_" + window.location.pathname; +var lastAnnotation; if(location.hash) { player.setTime(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash); } +else if(lastAnnotation = localStorage.getItem(lastAnnotationStorageItem)) +{ + player.setTime(lastAnnotation); +} diff --git a/cinera/cinera_player_pre.js b/cinera/cinera_player_pre.js index 90740c0..0ea4cf6 100644 --- a/cinera/cinera_player_pre.js +++ b/cinera/cinera_player_pre.js @@ -228,6 +228,14 @@ Player.prototype.updateProgress = function() { } if (this.currentMarker) { + if(this.currentMarkerIdx == this.markers.length - 1) + { + localStorage.removeItem(lastAnnotationStorageItem); + } + else + { + localStorage.setItem(lastAnnotationStorageItem, this.currentMarker.timestamp); + } this.currentMarker.el.classList.add("current"); this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0; this.scrollPosition = this.markersContainer.scrollTop; @@ -283,12 +291,16 @@ Player.prototype.onYoutubePlayerStateChange = function(ev) { if (ev.data == YT.PlayerState.PLAYING) { this.playing = true; this.currentTime = this.youtubePlayer.getCurrentTime(); - } else if (ev.data == YT.PlayerState.PAUSED || ev.data == YT.PlayerState.BUFFERING) { - this.playing = false; - this.currentTime = this.youtubePlayer.getCurrentTime(); - this.updateProgress(); } else { this.playing = false; + if (ev.data == YT.PlayerState.PAUSED || ev.data == YT.PlayerState.BUFFERING) { + this.currentTime = this.youtubePlayer.getCurrentTime(); + this.updateProgress(); + } else if (ev.data == YT.PlayerState.ENDED) { + localStorage.removeItem(lastAnnotationStorageItem); + this.currentTime = null; + this.updateProgress(); + } } this.buffering = ev.data == YT.PlayerState.BUFFERING; @@ -482,7 +494,7 @@ function toggleTheatreMode() { viewItems[0].setAttribute("data-id", "regular"); viewItems[0].setAttribute("title", "Regular mode"); viewItems[0].firstChild.nodeValue = "📺"; - } cineraProps.V = views.THEATRE; break; + } cineraProps.V = views.THEATRE; localStorage.setItem(cineraViewStorageItem, views.THEATRE); break; case views.SUPERTHEATRE: { leaveFullScreen_(); @@ -502,7 +514,7 @@ function toggleTheatreMode() { viewItems[0].setAttribute("data-id", "theatre"); viewItems[0].setAttribute("title", "Theatre mode"); viewItems[0].firstChild.nodeValue = "🎭"; - } cineraProps.V = views.REGULAR; break; + } cineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break; } player.updateSize(); } @@ -518,12 +530,12 @@ function toggleSuperTheatreMode() case views.THEATRE: { enterFullScreen_(); - } cineraProps.V = views.SUPERTHEATRE; break; + } cineraProps.V = views.SUPERTHEATRE; localStorage.setItem(cineraViewStorageItem, views.SUPERTHEATRE); break; case views.SUPERTHEATRE: { leaveFullScreen_(); toggleTheatreMode(); - } cineraProps.V = views.REGULAR; break; + } cineraProps.V = views.REGULAR; localStorage.removeItem(cineraViewStorageItem); break; } player.updateSize(); }