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
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`
2. `cd Annotation-System/cinera`
3. `cp ../hmmlib2/hmmlib.h .`
1. `git clone git@gitssh.handmade.network:Annotation-Pushers/Annotation-System.git`
2. `cd Annotation-System/hmmlib`
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
1. `$SHELL cinera.c`
### Configure
cinera -h
This documents the configuration file format and default settings.
### Configure the server
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
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
*(Global) Search Template*
*Search Template*
- `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_
- `<!-- __CINERA_SEARCH__ -->` _the table of contents and search functionality_
*Player Template*
- `<!-- __CINERA_INCLUDES__ -->` _to put inside your own `<head></head>`_
- `<!-- __CINERA_MENUS__ -->`
- `<!-- __CINERA_PLAYER__ -->`
- `<!-- __CINERA_SCRIPT__ -->` _must come after `<!-- __CINERA_MENUS__ -->` and `<!-- __CINERA_PLAYER__ -->`_
*Optional tags available for use in your Player Template*
- `<!-- __CINERA_TITLE__ -->`
- `<!-- __CINERA_VIDEO_ID__ -->`
- `<!-- __CINERA_VOD_PLATFORM__ -->`
*Other tags available for use in any template*
*Other tags available for use in either template*
- Asset tags:
- `<!-- __CINERA_ASSET__ path.ext -->`
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
relative to the Images Directory (-i)
- `<!-- __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
use inside your `<head>` block
use inside your <head> block
- `<!-- __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),
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
asset file relative to its specified type directory (generic, CSS, image
or JS), including the "../" directory, and paths containing spaces must
be surrounded with double-quotes (\-escapable if the quoted path itself
contains double-quotes).
All these asset tags additionally perform versioning, appending a query string
(-Q) and the file's checksum to the URL. Changes to a file 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
All these asset tags additionally perform revving, appending a query
string (-Q) and the file's checksum to the URL. Changes to a file
trigger a rehash and edit of all HTML pages citing this asset.
- `<!-- __CINERA_PROJECT__ -->`
The project's `html_title` if configured, otherwise its `title`
- `<!-- __CINERA_PROJECT_PLAIN__ -->`
The project's `title`
- `<!-- __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_THEME__ -->`
- `<!-- __CINERA_URL__ -->`
- `<!-- __CINERA_URL__ -->` _Only really usable if BaseURL is set (-B)_
- `<!-- __CINERA_CUSTOM0__ -->`
- `<!-- __CINERA_CUSTOM1__ -->`
- `<!-- __CINERA_CUSTOM2__ -->`
@ -128,15 +135,106 @@ invalid, _Cinera_ will tell you what's wrong.
#### Arguments
Usage: ./cinera [option(s)]
Usage: ./cinera [option(s)] filename(s)
Options:
-c <config file path>
Set the main config file path
Defaults to: $XDG_CONFIG_HOME/cinera/cinera.conf
-0
Dry-run mode. Parse and print the config, but do not modify the
filesystem
Paths: (advisedly universal, but may be set per-(sub)project as required)
-r <assets root directory>
Override default assets root directory (".")
-R <assets root URL>
Override default assets root URL ("")
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
Display (examine) database and exit
-v

File diff suppressed because it is too large Load Diff

View File

@ -506,16 +506,15 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .cineraHelp,
.cineraHelp {
cursor: pointer;
box-sizing: content-box;
border: 1px solid;
border-radius: 4px;
height: .5rem;
width: .5rem;
height: 6px;
padding: 4px;
width: 6px;
margin: 2px;
font-size: .75rem;
font-weight: bold;
line-height: .5rem;
line-height: 6px;
text-align: center;
z-index: 64;
}
@ -540,16 +539,6 @@ 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;
@ -624,11 +613,12 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu .credits_container {
border: 1px solid;
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;
position: absolute;
right: 0;
top: 100%;
z-index: 8;
}
.cineraMenus > .menu .refs,
@ -648,7 +638,7 @@ ul.cineraNavPlain li.current > a {
}
.cineraMenus > .menu .visible {
z-index: 8;
display: block;
}
.cineraMenus > .menu > .refs .ref {
@ -746,11 +736,6 @@ 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;
@ -770,13 +755,11 @@ 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;
@ -985,7 +968,6 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu > .filter_container .filter_content .category,
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent .cineraCategories .category {
box-sizing: content-box;
border-radius: 50%;
height: 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 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,
@ -22,19 +57,275 @@ 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);
var cinera = document.querySelector(".cinera");
var player = new Player(cinera, onRefChanged);
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();
}
window.addEventListener("resize", function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512, player);
setTimeout(DelayedUpdateSize, 512);
}
else
{
@ -42,10 +333,10 @@ window.addEventListener("resize", function() {
}
});
screen.orientation.onchange = function() {
window.onorientationchange = function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512, player);
setTimeout(DelayedUpdateSize, 512);
}
else
{
@ -60,17 +351,43 @@ document.addEventListener("keydown", function(ev) {
key = "capitalSpace";
}
if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item)
if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement)
{
ev.preventDefault();
}
});
document.addEventListener("fullscreenchange", function() {
if(!document.fullscreenElement && CineraProps.V == views.SUPERTHEATRE)
for(var i = 0; i < sourceMenus.length; ++i)
{
CineraProps.V = views.THEATRE;
localStorage.setItem(player.cineraViewStorageItem, views.THEATRE);
player.updateSize();
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);
}
});

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -213,11 +213,11 @@ function prepareToParseIndexFile(project)
mode = "markers";
episode.markers = [];
} else if (mode == "markers") {
var match = line.match(/"(\d+.\d+)": "(.+)"/);
var match = line.match(/"(\d+)": "(.+)"/);
if (match == null) {
console.log(name, line);
} else {
var totalTime = parseFloat(line.slice(1));
var totalTime = parseInt(line.slice(1));
var marker = {
totalTime: totalTime,
prettyTime: markerTime(totalTime),
@ -292,7 +292,7 @@ function markerTime(totalTime) {
var markTime = "(";
var hours = 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) {
markTime += padTimeComponent(hours) + ":";
}
@ -460,7 +460,7 @@ function runSearch(refresh) {
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.";
}
@ -3817,8 +3817,6 @@ PseudoPushButton(Button)
function
ResizeFunction()
{
var OriginalScrollX = scrollX;
var OriginalScrollY = scrollY;
CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(CineraProps.IsMobile)
{
@ -3843,7 +3841,7 @@ ResizeFunction()
PickListView();
// TODO(matt): Inform user that we've switched to the list view
}
scroll(OriginalScrollX, OriginalScrollY);
ScrollToWithOffset(Nav.Nexus, 0);
}
UpdateButtons();
}
@ -3866,7 +3864,7 @@ InitResizeEventListener()
function
InitOrientationChangeListener()
{
screen.orientation.onchange = function()
window.onorientationchange = function()
{
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* template;
char* medium;
char* number;
char* cc_lang;
HMML_Credit* credits;
size_t credit_count;
@ -76,7 +74,7 @@ typedef struct {
typedef struct {
int line;
int h, m, s, ms;
int h, m, s;
char* text;
char* author;
@ -471,7 +469,7 @@ next_attr:
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 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 == ':') {
unsigned int tmp;
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");
}
@ -494,27 +492,6 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
s = tmp;
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 != ']') {
_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;
}
if(ms >= 1000) {
_hmml_err(p, "Milliseconds cannot exceed 999");
}
if(s >= 60) {
_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->m = m;
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)
@ -762,9 +734,7 @@ static void _hmml_parse_video(struct _hmml_parser* p)
{ HSTR("id") , &p->out.metadata.id },
{ HSTR("template") , &p->out.metadata.template },
{ HSTR("medium") , &p->out.metadata.medium },
{ HSTR("number") , &p->out.metadata.number },
{ HSTR("output") , &p->out.metadata.output },
{ HSTR("cc_lang") , &p->out.metadata.cc_lang },
};
for(;;) {
@ -850,7 +820,7 @@ void hmml_free(HMML_Output* out)
}
const struct HMML_Version hmml_version = {
2, 0, 15
2, 0, 12
};
#undef HSTX