Compare commits

..

No commits in common. "master" and "b6fb755d29ca088c6b834b2fcbf1005c46df7c9d" have entirely different histories.

12 changed files with 3771 additions and 6542 deletions

202
README.md
View File

@ -5,26 +5,23 @@ deployment
### Install the dependencies ### Install the dependencies
1. curl 1. flex (for hmmlib)
2. curl (for cinera)
### Download, and copy the parser into place ### Download, and prepare the parser
1. `git clone https://git.handmade.network/Annotation-Pushers/Annotation-System.git` 1. `git clone git@gitssh.handmade.network:Annotation-Pushers/Annotation-System.git`
2. `cd Annotation-System/cinera` 2. `cd Annotation-System/hmmlib`
3. `cp ../hmmlib2/hmmlib.h .` 3. `make`
4. `cp hmml.a hmmlib.h ../cinera/`
5. `cd ../cinera/`
Note: For each parser update, remember to copy it into place. Note: For each parser update, remember to make and copy it into place.
### Build ### Build
1. `$SHELL cinera.c` 1. `$SHELL cinera.c`
### Configure
cinera -h
This documents the configuration file format and default settings.
### Configure the server ### Configure the server
If you enforce a strict Content Security Policy and X-Frame-Options in your If you enforce a strict Content Security Policy and X-Frame-Options in your
@ -41,24 +38,56 @@ and [Hardening your HTTP response headers](https://scotthelme.co.uk/hardening-yo
### Run ### Run
cinera -c /path/to/config.conf #### Single Edition operation
cinera test.hmml
This simply generates an HTML file (and updates `cinera_topics.css` if needed)
from `test.hmml` and outputs to `out.html` (configurable with -o).
#### Project Edition operation
cinera -p ProjectID
Setting the ProjectID with the `-p` flag triggers Project Edition. In this
edition _Cinera_ monitors the Project Input Directory for new, edited and
deleted .hmml files, and generates one table of contents / search page and a
player page each for valid sets of annotations (or removes them, if needed).
By default all directories - input and output - are set to the current working
directory. Typical operation will involve setting these flags:
-d Project Input Directory, the directory where the .hmml files reside
-r Asset Root Directory, path shallower than or equal to the CSS, Images and
JS directories
-R Asset Root URL, corresponding to the Root Directory
-c CSS Directory, relative to Root
-i Images Directory, relative to Root
-j JS Directory, relative to Root
-b Output Base Directory, location of the table of contents / search page
-B Output Base URL, corresponding to the Output Base Directory
-t Template Directory
-x Search Template Location, relative to Template Directory
-y Player Template Location, relative to Template Directory
#### Templates #### Templates
*(Global) Search Template* *Search Template*
- `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_ - `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_
- `<!-- __CINERA_SEARCH__ -->` _the table of contents and search functionality_ - `<!-- __CINERA_SEARCH__ -->` _the table of contents and search functionality_
*Player Template* *Player Template*
- `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_ - `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_
- `<!-- __CINERA_MENUS__ -->`
- `<!-- __CINERA_PLAYER__ -->` - `<!-- __CINERA_PLAYER__ -->`
- `<!-- __CINERA_SCRIPT__ -->` _must come after `<!-- __CINERA_MENUS__ -->` and `<!-- __CINERA_PLAYER__ -->`_
*Optional tags available for use in your Player Template* *Optional tags available for use in your Player Template*
- `<!-- __CINERA_TITLE__ -->` - `<!-- __CINERA_TITLE__ -->`
- `<!-- __CINERA_VIDEO_ID__ -->` - `<!-- __CINERA_VIDEO_ID__ -->`
- `<!-- __CINERA_VOD_PLATFORM__ -->` - `<!-- __CINERA_VOD_PLATFORM__ -->`
*Other tags available for use in any template* *Other tags available for use in either template*
- Asset tags: - Asset tags:
- `<!-- __CINERA_ASSET__ path.ext -->` - `<!-- __CINERA_ASSET__ path.ext -->`
General purpose tag that outputs the URL of the specified asset General purpose tag that outputs the URL of the specified asset
@ -67,50 +96,28 @@ and [Hardening your HTTP response headers](https://scotthelme.co.uk/hardening-yo
General purpose tag that outputs the URL of the specified asset General purpose tag that outputs the URL of the specified asset
relative to the Images Directory (-i) relative to the Images Directory (-i)
- `<!-- __CINERA_CSS__ path.ext -->` - `<!-- __CINERA_CSS__ path.ext -->`
Convenience tag that outputs a `<link rel="stylesheet"...>` node Convenience tag that outputs a <link rel="stylesheet"...> node
for the specified asset relative to the CSS Directory (-c), for for the specified asset relative to the CSS Directory (-c), for
use inside your `<head>` block use inside your <head> block
- `<!-- __CINERA_JS__ path.ext -->` - `<!-- __CINERA_JS__ path.ext -->`
Convenience tag that outputs a `<script type="text/javascript"...>` Convenience tag that outputs a <script type="text/javascript"...>
node for the specified asset relative to the JS Directory (-j), node for the specified asset relative to the JS Directory (-j),
for use wherever a `<script>` node is valid for use wherever a <script> node is valid
The path.ext in these tags supports parent directories to locate the The path.ext in these tags supports parent directories to locate the
asset file relative to its specified type directory (generic, CSS, image asset file relative to its specified type directory (generic, CSS, image
or JS), including the "../" directory, and paths containing spaces must or JS), including the "../" directory, and paths containing spaces must
be surrounded with double-quotes (\-escapable if the quoted path itself be surrounded with double-quotes (\-escapable if the quoted path itself
contains double-quotes). contains double-quotes).
All these asset tags additionally perform versioning, appending a query string All these asset tags additionally perform revving, appending a query
(-Q) and the file's checksum to the URL. Changes to a file trigger a rehash and string (-Q) and the file's checksum to the URL. Changes to a file
edit of all HTML pages citing this asset. trigger a rehash and edit of all HTML pages citing this asset.
- Navigation / Menu tags:
- `<!-- __CINERA_NAV__ -->`
This menu will contain only the current project, its siblings and
subprojects of both the aforementioned
- `<!-- __CINERA_GLOBAL_NAV__ -->`
This menu will contain every project in the configuration
These navigation tags additionally take one parameter, e.g.
`<!-- __CINERA_NAV__ horizontal -->`:
- `dropdown`
A dropdown menu, that opens on hover
- `horizontal`
More-or-less the same as `dropdown`, but always open
- `plain`
A straightforward `<ul>` for you to style how you like
- `<!-- __CINERA_PROJECT__ -->` - `<!-- __CINERA_PROJECT__ -->`
The project's `html_title` if configured, otherwise its `title`
- `<!-- __CINERA_PROJECT_PLAIN__ -->`
The project's `title`
- `<!-- __CINERA_PROJECT_ID__ -->` - `<!-- __CINERA_PROJECT_ID__ -->`
- `<!-- __CINERA_PROJECT_LINEAGE__ -->`
The IDs of all projects from the root to the current project, separated
by a spaced-slash
- `<!-- __CINERA_SEARCH_URL__ -->` - `<!-- __CINERA_SEARCH_URL__ -->`
- `<!-- __CINERA_THEME__ -->` - `<!-- __CINERA_THEME__ -->`
- `<!-- __CINERA_URL__ -->` - `<!-- __CINERA_URL__ -->` _Only really usable if BaseURL is set (-B)_
- `<!-- __CINERA_CUSTOM0__ -->` - `<!-- __CINERA_CUSTOM0__ -->`
- `<!-- __CINERA_CUSTOM1__ -->` - `<!-- __CINERA_CUSTOM1__ -->`
- `<!-- __CINERA_CUSTOM2__ -->` - `<!-- __CINERA_CUSTOM2__ -->`
@ -128,15 +135,106 @@ invalid, _Cinera_ will tell you what's wrong.
#### Arguments #### Arguments
Usage: ./cinera [option(s)] Usage: ./cinera [option(s)] filename(s)
Options: Options:
-c <config file path> Paths: (advisedly universal, but may be set per-(sub)project as required)
Set the main config file path -r <assets root directory>
Defaults to: $XDG_CONFIG_HOME/cinera/cinera.conf Override default assets root directory (".")
-0 -R <assets root URL>
Dry-run mode. Parse and print the config, but do not modify the Override default assets root URL ("")
filesystem IMPORTANT: -r and -R must correspond to the same location
UNSUPPORTED: If you move files from RootDir, the RootURL should
correspond to the resulting location
-c <CSS directory path>
Override default CSS directory (""), relative to root
-i <images directory path>
Override default images directory (""), relative to root
-j <JS directory path>
Override default JS directory (""), relative to root
-Q <revved resources query string>
Override default query string ("r")
To disable revved resources, set an empty string with -Q ""
Project Settings:
-p <project ID>
Set the project ID, triggering PROJECT EDITION
-m <default medium>
Override default default medium ("programming")
Known project defaults:
bitwise: programming
book: research
coad: research
reader: research
riscy: programming
risc: speech
chat: speech
code: programming
intro-to-c: programming
misc: admin
ray: programming
hmdshow: speech
lecture: speech
stream: programming
special: programming
obbg: programming
sysadmin: admin
-s <style>
Set the style / theme, corresponding to a cinera__*.css file
This is equal to the "project" field in the HMML files by default
Project Input Paths
-d <annotations directory>
Override default annotations directory (".")
-t <templates directory>
Override default templates directory (".")
-x <search template location>
Set search template file path, either absolute or relative to
template directory, and enable integration
-y <player template location>
Set player template file path, either absolute or relative
to template directory, and enable integration
Project Output Paths
-b <base output directory>
Override project's default base output directory (".")
-B <base URL>
Override default base URL ("")
NOTE: This must be set, if -n or -a are to be used
-n <search location>
Override default search location (""), relative to base
-a <player location>
Override default player location (""), relative to base
NOTE: The PlayerURLPrefix is currently hardcoded in cinera.c but
will be configurable in the full configuration system
Single Edition Output Path
-o <output location>
Override default output player location ("out.html")
-1
Open search result links in the same browser tab
NOTE: Ideal for a guide embedded in an iframe
-f
Force integration with an incomplete template
-g
Ignore video privacy status
NOTE: For use with projects whose videos are known to all be public,
to save us having to check their privacy status
-q
Quit after syncing with annotation files in project input directory
UNSUPPORTED: This is likely to be removed in the future
-w
Force quote cache rebuild (memory aid: "wget")
-l <n>
Override default log level (0), where n is from 0 (terse) to 7 (verbose)
-u <seconds>
Override default update interval (4)
-e -e
Display (examine) database and exit Display (examine) database and exit
-v -v

File diff suppressed because it is too large Load Diff

View File

@ -506,16 +506,15 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .cineraHelp, .cineraMenus > .cineraHelp,
.cineraHelp { .cineraHelp {
cursor: pointer; cursor: pointer;
box-sizing: content-box;
border: 1px solid; border: 1px solid;
border-radius: 4px; border-radius: 4px;
height: .5rem; height: 6px;
width: .5rem;
padding: 4px; padding: 4px;
width: 6px;
margin: 2px; margin: 2px;
font-size: .75rem; font-size: .75rem;
font-weight: bold; font-weight: bold;
line-height: .5rem; line-height: 6px;
text-align: center; text-align: center;
z-index: 64; z-index: 64;
} }
@ -540,16 +539,6 @@ ul.cineraNavPlain li.current > a {
display: block; 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 { .cineraHelp .help_container .help_key {
box-sizing: content-box; box-sizing: content-box;
font-family: Inconsolata; font-family: Inconsolata;
@ -624,11 +613,12 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu .credits_container { .cineraMenus > .menu .credits_container {
border: 1px solid; border: 1px solid;
border-top: none; border-top: none;
z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */ display: none;
overflow-y: auto; overflow-y: auto;
position: absolute; position: absolute;
right: 0; right: 0;
top: 100%; top: 100%;
z-index: 8;
} }
.cineraMenus > .menu .refs, .cineraMenus > .menu .refs,
@ -648,7 +638,7 @@ ul.cineraNavPlain li.current > a {
} }
.cineraMenus > .menu .visible { .cineraMenus > .menu .visible {
z-index: 8; display: block;
} }
.cineraMenus > .menu > .refs .ref { .cineraMenus > .menu > .refs .ref {
@ -746,11 +736,6 @@ ul.cineraNavPlain li.current > a {
margin-right: 4px; margin-right: 4px;
} }
.cineraMenus > .menu > .filter_container .filter_header {
position: sticky;
top: 0;
}
.cineraMenus > .menu > .filter_container .filter_mode, .cineraMenus > .menu > .filter_container .filter_mode,
.cineraMenus > .menu > .link_container #cineraLinkMode { .cineraMenus > .menu > .link_container #cineraLinkMode {
cursor: pointer; cursor: pointer;
@ -770,13 +755,11 @@ ul.cineraNavPlain li.current > a {
text-align: center; text-align: center;
} }
.cineraMenus > .menu > .filter_container .filter_header .filter_titles,
.cineraMenus > .menu > .filter_container .filters { .cineraMenus > .menu > .filter_container .filters {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
} }
.cineraMenus > .menu > .filter_container .filter_header .filter_titles > *,
.cineraMenus > .menu > .filter_container .filters > * { .cineraMenus > .menu > .filter_container .filters > * {
width: 50%; width: 50%;
flex-grow: 1; flex-grow: 1;
@ -985,7 +968,6 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu > .filter_container .filter_content .category, .cineraMenus > .menu > .filter_container .filter_content .category,
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent .cineraCategories .category { .cineraPlayerContainer .markers_container > .markers .marker .cineraContent .cineraCategories .category {
box-sizing: content-box;
border-radius: 50%; border-radius: 50%;
height: 5px; height: 5px;
width: 5px; width: 5px;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,40 @@
var cinera = document.querySelector(".cinera");
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location; 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 = { var CineraProps = {
C: null, C: null,
V: views.REGULAR, V: views.REGULAR,
@ -22,19 +57,275 @@ var CineraProps = {
}; };
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile); 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 MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector); 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 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); var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
var cinera = document.querySelector(".cinera"); if(CineraProps.IsMobile)
var player = new Player(cinera, onRefChanged); {
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();
}
window.addEventListener("resize", function() { window.addEventListener("resize", function() {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {
setTimeout(DelayedUpdateSize, 512, player); setTimeout(DelayedUpdateSize, 512);
} }
else else
{ {
@ -42,10 +333,10 @@ window.addEventListener("resize", function() {
} }
}); });
screen.orientation.onchange = function() { window.onorientationchange = function() {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {
setTimeout(DelayedUpdateSize, 512, player); setTimeout(DelayedUpdateSize, 512);
} }
else else
{ {
@ -60,17 +351,43 @@ document.addEventListener("keydown", function(ev) {
key = "capitalSpace"; key = "capitalSpace";
} }
if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item) if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement)
{ {
ev.preventDefault(); ev.preventDefault();
} }
}); });
document.addEventListener("fullscreenchange", function() { for(var i = 0; i < sourceMenus.length; ++i)
if(!document.fullscreenElement && CineraProps.V == views.SUPERTHEATRE)
{ {
CineraProps.V = views.THEATRE; sourceMenus[i].addEventListener("mouseenter", function(ev) {
localStorage.setItem(player.cineraViewStorageItem, views.THEATRE); handleMenuTogglerInteraction(this, ev.type);
player.updateSize(); })
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);
} }
});

File diff suppressed because it is too large Load Diff

View File

@ -13,9 +13,6 @@ DeriveReliableWindowDimensions()
Y: null, Y: null,
}; };
var ScrollPosX = window.scrollX;
var ScrollPosY = window.scrollY;
var DisplaySettings = []; var DisplaySettings = [];
for(var i = 0; i < document.body.children.length; ++i) for(var i = 0; i < document.body.children.length; ++i)
{ {
@ -43,9 +40,6 @@ DeriveReliableWindowDimensions()
Child.style.display = DisplaySettings.shift(); Child.style.display = DisplaySettings.shift();
} }
ScrollTriggeredInternally = true;
window.scroll(ScrollPosX, ScrollPosY);
return Result; return Result;
} }
@ -82,7 +76,7 @@ function IsVisible(Element, WindowDim) {
function function
GetRealOrientation(PreferredLandscape, IsMobile) GetRealOrientation(PreferredLandscape, IsMobile)
{ {
var Result = screen.orientation.angle; var Result = window.orientation;
var WindowDim = GetWindowDim(IsMobile); var WindowDim = GetWindowDim(IsMobile);
if(WindowDim.Y > WindowDim.X) if(WindowDim.Y > WindowDim.X)
{ {
@ -191,12 +185,6 @@ function enableSprite(Element)
} }
function disableSprite(Element) function disableSprite(Element)
{
if(Element.classList.contains("focused"))
{
focusSprite(Element);
}
else
{ {
if(Element.classList.contains("cineraSprite")) if(Element.classList.contains("cineraSprite"))
{ {
@ -208,7 +196,6 @@ function disableSprite(Element)
disableSprite(Element.children[i]); disableSprite(Element.children[i]);
} }
} }
}
function unfocusSprite(Element) function unfocusSprite(Element)
{ {
@ -323,7 +310,6 @@ function IsInRangeEx(Min, N, Max)
} }
/* Auto-scrolling */ /* Auto-scrolling */
var ScrollTriggeredInternally = false;
var LastScrollYPos = 0; var LastScrollYPos = 0;
var ScrollTicking = false; var ScrollTicking = false;
var ScrollerFunction; var ScrollerFunction;
@ -390,11 +376,7 @@ function
InitScrollEventListener(Element, IsMobile, StickyObscuringElement) InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
{ {
window.addEventListener('scroll', function() { window.addEventListener('scroll', function() {
if(ScrollTriggeredInternally) if(ScrollCondition == undefined || ScrollCondition == true)
{
ScrollTriggeredInternally = false;
}
else if(ScrollCondition == undefined || ScrollCondition == true)
{ {
LastScrollYPos = window.scrollY; LastScrollYPos = window.scrollY;
@ -521,29 +503,17 @@ IsOverflowed(Element)
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth; 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 function
BindHelp(Button, DocumentationContainer) BindHelp(Button, DocumentationContainer)
{ {
window.addEventListener("blur", function(){ window.addEventListener("blur", 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";
}); });
window.addEventListener("focus", function(){ window.addEventListener("focus", function(){
SetHelpFocused(Button); Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
}); });
Button.addEventListener("click", function() { Button.addEventListener("click", function() {
@ -551,16 +521,45 @@ BindHelp(Button, DocumentationContainer)
}) })
} }
function RGBtoHSL(colour)
{
var rgb = colour.slice(4, -1).split(", ");
var red = rgb[0];
var green = rgb[1];
var blue = rgb[2];
var min = Math.min(red, green, blue);
var max = Math.max(red, green, blue);
var chroma = max - min;
var hue = 0;
if(max == red)
{
hue = ((green - blue) / chroma) % 6;
}
else if(max == green)
{
hue = ((blue - red) / chroma) + 2;
}
else if(max == blue)
{
hue = ((red - green) / chroma) + 4;
}
var saturation = chroma / 255 * 100;
hue = (hue * 60) < 0 ? 360 + (hue * 60) : (hue * 60);
return [hue, saturation]
}
function getBackgroundColourRGB(element) { function getBackgroundColourRGB(element) {
var Colour = getComputedStyle(element).getPropertyValue("background-color"); var Colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0; var depth = 0;
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && element.parentElement && depth <= 4) while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && depth <= 4)
{ {
element = element.parentElement; element = element.parentNode;
Colour = getComputedStyle(element).getPropertyValue("background-color"); Colour = getComputedStyle(element).getPropertyValue("background-color");
++depth; ++depth;
} }
var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", "); var Staging = Colour.slice(4, -1).split(", ");
var Result = { var Result = {
R: parseInt(Staging[0]), R: parseInt(Staging[0]),
G: parseInt(Staging[1]), G: parseInt(Staging[1]),
@ -581,8 +580,6 @@ function setTextLightness(textElement)
{ {
var textHue = textElement.getAttribute("data-hue"); var textHue = textElement.getAttribute("data-hue");
var textSaturation = textElement.getAttribute("data-saturation"); var textSaturation = textElement.getAttribute("data-saturation");
if(textHue && textSaturation)
{
if(getBackgroundBrightness(textElement.parentNode) < 127) if(getBackgroundBrightness(textElement.parentNode) < 127)
{ {
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)"); textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
@ -592,23 +589,19 @@ function setTextLightness(textElement)
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)"); textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
} }
} }
}
function setDotLightness(topicDot) function setDotLightness(topicDot)
{ {
var dotHue = topicDot.getAttribute("data-hue"); var Hue = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[0];
var dotSaturation = topicDot.getAttribute("data-saturation"); var Saturation = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[1];
if(dotHue && dotSaturation)
{
if(getBackgroundBrightness(topicDot.parentNode) < 127) if(getBackgroundBrightness(topicDot.parentNode) < 127)
{ {
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)"); topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)"); topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
} }
else else
{ {
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)"); topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)"); topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
}
} }
} }

View File

@ -213,11 +213,11 @@ function prepareToParseIndexFile(project)
mode = "markers"; mode = "markers";
episode.markers = []; episode.markers = [];
} else if (mode == "markers") { } else if (mode == "markers") {
var match = line.match(/"(\d+.\d+)": "(.+)"/); var match = line.match(/"(\d+)": "(.+)"/);
if (match == null) { if (match == null) {
console.log(name, line); console.log(name, line);
} else { } else {
var totalTime = parseFloat(line.slice(1)); var totalTime = parseInt(line.slice(1));
var marker = { var marker = {
totalTime: totalTime, totalTime: totalTime,
prettyTime: markerTime(totalTime), prettyTime: markerTime(totalTime),
@ -292,7 +292,7 @@ function markerTime(totalTime) {
var markTime = "("; var markTime = "(";
var hours = Math.floor(totalTime / 60 / 60); var hours = Math.floor(totalTime / 60 / 60);
var minutes = Math.floor(totalTime / 60) % 60; var minutes = Math.floor(totalTime / 60) % 60;
var seconds = Math.floor(totalTime) % 60; var seconds = totalTime % 60;
if (hours > 0) { if (hours > 0) {
markTime += padTimeComponent(hours) + ":"; markTime += padTimeComponent(hours) + ":";
} }
@ -460,7 +460,7 @@ function runSearch(refresh) {
Search.ResultsSummary.style.display = "none"; Search.ResultsSummary.style.display = "none";
} }
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + Math.floor(totalSeconds)%60 + "s "; var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total."; Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
} }
@ -3817,8 +3817,6 @@ PseudoPushButton(Button)
function function
ResizeFunction() ResizeFunction()
{ {
var OriginalScrollX = scrollX;
var OriginalScrollY = scrollY;
CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile); CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {
@ -3843,7 +3841,7 @@ ResizeFunction()
PickListView(); PickListView();
// TODO(matt): Inform user that we've switched to the list view // TODO(matt): Inform user that we've switched to the list view
} }
scroll(OriginalScrollX, OriginalScrollY); ScrollToWithOffset(Nav.Nexus, 0);
} }
UpdateButtons(); UpdateButtons();
} }
@ -3866,7 +3864,7 @@ InitResizeEventListener()
function function
InitOrientationChangeListener() InitOrientationChangeListener()
{ {
screen.orientation.onchange = function() window.onorientationchange = function()
{ {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -24,8 +24,6 @@ typedef struct {
char* output; char* output;
char* template; char* template;
char* medium; char* medium;
char* number;
char* cc_lang;
HMML_Credit* credits; HMML_Credit* credits;
size_t credit_count; size_t credit_count;
@ -76,7 +74,7 @@ typedef struct {
typedef struct { typedef struct {
int line; int line;
int h, m, s, ms; int h, m, s;
char* text; char* text;
char* author; char* author;
@ -471,7 +469,7 @@ next_attr:
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts) static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
{ {
unsigned int h = 0, m = 0, s = 0, ms = 0; unsigned int h = 0, m = 0, s = 0;
int offset = 0; int offset = 0;
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset); int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
@ -485,7 +483,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
if(c == ':') { if(c == ':') {
unsigned int tmp; unsigned int tmp;
offset = 0; offset = 0;
if(sscanf(p->cursor, ":%u%n", &tmp, &offset) != 1 || offset == 0) { if(sscanf(p->cursor, ":%u]%n", &tmp, &offset) != 1 || offset == 0) {
_hmml_err(p, "Unable to parse 3-part timecode"); _hmml_err(p, "Unable to parse 3-part timecode");
} }
@ -494,27 +492,6 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
s = tmp; s = tmp;
p->cursor += offset; p->cursor += offset;
c = *p->cursor;
}
if(c == '.') {
unsigned int tmp;
offset = 0;
int non_number_chars = 2;
int digits_in_100 = 3;
int max_chars_to_parse = non_number_chars + digits_in_100;
if(sscanf(p->cursor, ".%u]%n", &tmp, &offset) != 1 || offset == 0 || offset > max_chars_to_parse) {
_hmml_err(p, "Unable to parse %u.5-part timecode", h ? 3 : 2);
}
for(int i = offset - non_number_chars; i < digits_in_100; ++i) {
tmp *= 10;
}
ms = tmp;
p->cursor += offset;
} else if(c != ']') { } else if(c != ']') {
_hmml_err(p, "Unable to parse timecode"); _hmml_err(p, "Unable to parse timecode");
@ -522,10 +499,6 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
++p->cursor; ++p->cursor;
} }
if(ms >= 1000) {
_hmml_err(p, "Milliseconds cannot exceed 999");
}
if(s >= 60) { if(s >= 60) {
_hmml_err(p, "Seconds cannot exceed 59"); _hmml_err(p, "Seconds cannot exceed 59");
} }
@ -537,7 +510,6 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
ts->h = h; ts->h = h;
ts->m = m; ts->m = m;
ts->s = s; ts->s = s;
ts->ms = ms;
} }
static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size) static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size)
@ -762,9 +734,7 @@ static void _hmml_parse_video(struct _hmml_parser* p)
{ HSTR("id") , &p->out.metadata.id }, { HSTR("id") , &p->out.metadata.id },
{ HSTR("template") , &p->out.metadata.template }, { HSTR("template") , &p->out.metadata.template },
{ HSTR("medium") , &p->out.metadata.medium }, { HSTR("medium") , &p->out.metadata.medium },
{ HSTR("number") , &p->out.metadata.number },
{ HSTR("output") , &p->out.metadata.output }, { HSTR("output") , &p->out.metadata.output },
{ HSTR("cc_lang") , &p->out.metadata.cc_lang },
}; };
for(;;) { for(;;) {
@ -850,7 +820,7 @@ void hmml_free(HMML_Output* out)
} }
const struct HMML_Version hmml_version = { const struct HMML_Version hmml_version = {
2, 0, 15 2, 0, 12
}; };
#undef HSTX #undef HSTX