Compare commits
105 Commits
Author | SHA1 | Date |
---|---|---|
Matt Mascarenhas | 14dafa4abe | |
Matt Mascarenhas | 213bb2f882 | |
Matt Mascarenhas | 77aec74483 | |
Matt Mascarenhas | 6852d06e04 | |
Matt Mascarenhas | 52d6d989f8 | |
Matt Mascarenhas | df93674bf7 | |
Matt Mascarenhas | 026585e50b | |
Matt Mascarenhas | 9d5f0f9146 | |
Matt Mascarenhas | a7694d4c3b | |
Matt Mascarenhas | c9bf96c7aa | |
Matt Mascarenhas | 44a5008aa7 | |
Matt Mascarenhas | 6576a91d11 | |
Matt Mascarenhas | 1280142d45 | |
Matt Mascarenhas | 6b09247cd2 | |
Matt Mascarenhas | 554f7393ff | |
Matt Mascarenhas | e8ed2f0143 | |
Matt Mascarenhas | ac4b155e73 | |
Matt Mascarenhas | 0f15957cb5 | |
Matt Mascarenhas | 68d1469212 | |
Matt Mascarenhas | f321a6b6bf | |
Matt Mascarenhas | 6ea5cb6d22 | |
Matt Mascarenhas | 0825cbb5ff | |
Matt Mascarenhas | 6e9c7edd1d | |
Matt Mascarenhas | b8f143e350 | |
Matt Mascarenhas | f60cf3087f | |
Matt Mascarenhas | 1cf703e346 | |
Matt Mascarenhas | 26e304f62c | |
Matt Mascarenhas | 8dffa513cc | |
Matt Mascarenhas | e17c3aa78c | |
Matt Mascarenhas | 84a35b9ece | |
Matt Mascarenhas | ef9937e95b | |
Matt Mascarenhas | 652d8f186f | |
Matt Mascarenhas | 91c33c580b | |
Matt Mascarenhas | f7a6df994f | |
Matt Mascarenhas | b6fb755d29 | |
Matt Mascarenhas | d036145b7f | |
Matt Mascarenhas | 60615322dd | |
Matt Mascarenhas | 2deb244cce | |
Matt Mascarenhas | 4dc034ecc3 | |
Matt Mascarenhas | ee788b0c30 | |
Matt Mascarenhas | c839e2ed85 | |
Matt Mascarenhas | 012d1608a0 | |
Matt Mascarenhas | 97806c2c9c | |
Matt Mascarenhas | dd33bb49b3 | |
Matt Mascarenhas | e39a09c0ad | |
Matt Mascarenhas | 744515dac4 | |
Matt Mascarenhas | 209979f63a | |
Matt Mascarenhas | 12b06812bf | |
Matt Mascarenhas | 373195069a | |
Matt Mascarenhas | 07795b7cbd | |
Matt Mascarenhas | b0501d20ac | |
Matt Mascarenhas | af1bff4218 | |
Matt Mascarenhas | 63ab9d5bc2 | |
Matt Mascarenhas | 9cdb3b96d9 | |
Matt Mascarenhas | 61fa4e4808 | |
Matt Mascarenhas | cf51ba24e3 | |
Matt Mascarenhas | a6a9653306 | |
Matt Mascarenhas | c104500020 | |
Matt Mascarenhas | 0d88c24db6 | |
Matt Mascarenhas | 65fe93fb57 | |
Matt Mascarenhas | b20192e445 | |
Matt Mascarenhas | 9a31a1b10c | |
Matt Mascarenhas | d1440a7ac6 | |
Matt Mascarenhas | 0f67d4e658 | |
Alex Baines | 00fc6c0ae5 | |
Alex Baines | c0c3558473 | |
Alex Baines | 3f47e62a7e | |
Alex Baines | c911e393d5 | |
Matt Mascarenhas | 05b7883033 | |
Alex Baines | e4c2ae4ffa | |
Alex Baines | 1d916415c1 | |
Alex Baines | 678be54e51 | |
Alex Baines | 1993e9f1dd | |
Alex Baines | 968437d263 | |
Alex Baines | 67c4942b60 | |
Matt Mascarenhas | 828fff7a7a | |
Matt Mascarenhas | 8283e2a9e5 | |
Matt Mascarenhas | 22633794b5 | |
Matt Mascarenhas | 0cfa87ba84 | |
Matt Mascarenhas | 806c9feba8 | |
Matt Mascarenhas | b6fff3cc24 | |
Matt Mascarenhas | 20d7e82a66 | |
Matt Mascarenhas | 33f6186aa1 | |
Matt Mascarenhas | 2d26562301 | |
Matt Mascarenhas | 4ba03f9338 | |
Matt Mascarenhas | 7e80017434 | |
Matt Mascarenhas | 29df4df8a4 | |
Matt Mascarenhas | 5857a30eaf | |
Matt Mascarenhas | 0a9d51b0d7 | |
Matt Mascarenhas | 50dbc7b5c3 | |
Matt Mascarenhas | e7d70ccb3a | |
Matt Mascarenhas | f519a0977f | |
Matt Mascarenhas | 545938d766 | |
Matt Mascarenhas | 2cf8739a60 | |
Matt Mascarenhas | eccd02cc71 | |
Matt Mascarenhas | 3945ac883c | |
Matt Mascarenhas | 6eeb588adf | |
Matt Mascarenhas | b3470e0f48 | |
Matt Mascarenhas | 2748687839 | |
Matt Mascarenhas | 6da970d48c | |
Matt Mascarenhas | 0959fa2774 | |
Matt Mascarenhas | 7edcecfd40 | |
Matt Mascarenhas | bc6f21f71d | |
Matt Mascarenhas | 8aa67a386a | |
Matt Mascarenhas | ce9a0e7635 |
202
README.md
202
README.md
|
@ -5,23 +5,26 @@ deployment
|
||||||
|
|
||||||
### Install the dependencies
|
### Install the dependencies
|
||||||
|
|
||||||
1. flex (for hmmlib)
|
1. curl
|
||||||
2. curl (for cinera)
|
|
||||||
|
|
||||||
### Download, and prepare the parser
|
### Download, and copy the parser into place
|
||||||
|
|
||||||
1. `git clone git@gitssh.handmade.network:Annotation-Pushers/Annotation-System.git`
|
1. `git clone https://git.handmade.network/Annotation-Pushers/Annotation-System.git`
|
||||||
2. `cd Annotation-System/hmmlib`
|
2. `cd Annotation-System/cinera`
|
||||||
3. `make`
|
3. `cp ../hmmlib2/hmmlib.h .`
|
||||||
4. `cp hmml.a hmmlib.h ../cinera/`
|
|
||||||
5. `cd ../cinera/`
|
|
||||||
|
|
||||||
Note: For each parser update, remember to make and copy it into place.
|
Note: For each parser update, remember to 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
|
||||||
|
@ -38,56 +41,24 @@ and [Hardening your HTTP response headers](https://scotthelme.co.uk/hardening-yo
|
||||||
|
|
||||||
### Run
|
### Run
|
||||||
|
|
||||||
#### Single Edition operation
|
cinera -c /path/to/config.conf
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
*Search Template*
|
*(Global) 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 either template*
|
*Other tags available for use in any 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
|
||||||
|
@ -96,28 +67,50 @@ directory. Typical operation will involve setting these flags:
|
||||||
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 revving, appending a query
|
All these asset tags additionally perform versioning, appending a query string
|
||||||
string (-Q) and the file's checksum to the URL. Changes to a file
|
(-Q) and the file's checksum to the URL. Changes to a file trigger a rehash and
|
||||||
trigger a rehash and edit of all HTML pages citing this asset.
|
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__ -->` _Only really usable if BaseURL is set (-B)_
|
- `<!-- __CINERA_URL__ -->`
|
||||||
- `<!-- __CINERA_CUSTOM0__ -->`
|
- `<!-- __CINERA_CUSTOM0__ -->`
|
||||||
- `<!-- __CINERA_CUSTOM1__ -->`
|
- `<!-- __CINERA_CUSTOM1__ -->`
|
||||||
- `<!-- __CINERA_CUSTOM2__ -->`
|
- `<!-- __CINERA_CUSTOM2__ -->`
|
||||||
|
@ -135,106 +128,15 @@ invalid, _Cinera_ will tell you what's wrong.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
|
||||||
Usage: ./cinera [option(s)] filename(s)
|
Usage: ./cinera [option(s)]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
Paths: (advisedly universal, but may be set per-(sub)project as required)
|
-c <config file path>
|
||||||
-r <assets root directory>
|
Set the main config file path
|
||||||
Override default assets root directory (".")
|
Defaults to: $XDG_CONFIG_HOME/cinera/cinera.conf
|
||||||
-R <assets root URL>
|
-0
|
||||||
Override default assets root URL ("")
|
Dry-run mode. Parse and print the config, but do not modify the
|
||||||
IMPORTANT: -r and -R must correspond to the same location
|
filesystem
|
||||||
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
|
||||||
|
|
12334
cinera/cinera.c
12334
cinera/cinera.c
File diff suppressed because it is too large
Load Diff
|
@ -58,7 +58,7 @@ nav.cineraNavDropdown .cineraNavTitle {
|
||||||
nav.cineraNavDropdown ul.cineraNavHorizontal {
|
nav.cineraNavDropdown ul.cineraNavHorizontal {
|
||||||
display: none;
|
display: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 8;
|
z-index: 4096;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,12 +85,124 @@ ul.cineraNavPlain li.current > a {
|
||||||
|
|
||||||
/* Index */
|
/* Index */
|
||||||
|
|
||||||
#cineraIndexControl {
|
#cineraIndex
|
||||||
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 16px auto;
|
flex-direction: column;
|
||||||
max-width: 1024px;
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexControl
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 64;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Portrait
|
||||||
|
{
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Landscape.Left
|
||||||
|
{
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Landscape.Right
|
||||||
|
{
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal
|
||||||
|
{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal .cineraButton.prev
|
||||||
|
{
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal .cineraButton.next
|
||||||
|
{
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.anim .cineraTraversal,
|
||||||
|
#cineraIndex.anim .cineraTraversal * p
|
||||||
|
{
|
||||||
|
transition: transform .32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraTraversalContainer
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraTraversal
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.reversed .cineraTraversal
|
||||||
|
{
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraTraversalContainer .cineraButton
|
||||||
|
{
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
height: 42px;
|
||||||
|
width: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraTraversal * p
|
||||||
|
{
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.reversed .cineraTraversal .ascension p
|
||||||
|
{
|
||||||
|
transform: rotate(-180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.reversed .cineraTraversal .prev p,
|
||||||
|
#cineraIndex.reversed .cineraTraversal .next p
|
||||||
|
{
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraTraversal .next.ascension p
|
||||||
|
{
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.reversed .cineraTraversal .next.ascension p
|
||||||
|
{
|
||||||
|
transform: rotate(-315deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraFilterProject .cineraText,
|
.cineraFilterProject .cineraText,
|
||||||
|
@ -99,15 +211,32 @@ ul.cineraNavPlain li.current > a {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraIndexFilter {
|
.cineraMenu.cineraMenuTitle {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraIndexFilter .filter_container
|
.cineraMenuContainer
|
||||||
{
|
{
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
z-index: 64;
|
z-index: 64;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
border: 1px solid;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraMenuContainer.visible
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
max-height: 88vh;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraFilterProject,
|
.cineraFilterProject,
|
||||||
|
@ -120,17 +249,16 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraIndexProject .cineraProjectTitle,
|
.cineraIndexProject .cineraProjectTitle,
|
||||||
.cineraFilterProject
|
.cineraMenuItem
|
||||||
{
|
{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 12px;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraQueryContainer {
|
.cineraQueryContainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: horizontal;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,19 +269,23 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraQueryContainer .inputContainer {
|
.cineraQueryContainer .inputContainer {
|
||||||
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
padding: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraQueryContainer #query {
|
.cineraQueryContainer #query {
|
||||||
width: 100%;
|
width: 0px;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraQueryContainer .inputContainer .spinner {
|
.cineraQueryContainer .inputContainer .spinner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
color: black;
|
font-size: .5rem;
|
||||||
|
color: #000000;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +295,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraResults,
|
#cineraResults,
|
||||||
#cineraIndex {
|
#cineraIndexList {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
@ -173,22 +305,104 @@ ul.cineraNavPlain li.current > a {
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraIndexControl #cineraIndexSort {
|
#cineraIndexList .cineraIndexEntries {
|
||||||
padding: 4px;
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraIndexControl #cineraIndexSort,
|
#cineraIndex #cineraIndexGrid
|
||||||
#cineraIndexControl .cineraIndexFilter .filter_container {
|
{
|
||||||
cursor: pointer;
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
perspective-origin: center;
|
||||||
|
-webkit-perspective-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButtons
|
||||||
|
{
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 2px;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex .cineraButton
|
||||||
|
{
|
||||||
|
border: 1px solid;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraIndex .cineraIndexEntries {
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision
|
||||||
|
{
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .head-item,
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .tail-item
|
||||||
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .head-item p,
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .tail-item p
|
||||||
|
{
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf
|
||||||
|
{
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf a
|
||||||
|
{
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf a:hover {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .head-item,
|
||||||
|
#cineraIndex.reversed #cineraIndexGrid .cineraButton.subdivision .tail-item
|
||||||
|
{
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .tail-item,
|
||||||
|
#cineraIndex.reversed #cineraIndexGrid .cineraButton.subdivision .head-item
|
||||||
|
{
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndexList.hidden,
|
||||||
|
#cineraIndex .cineraIndexGridContainer.hidden
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraResults .dayContainer {
|
#cineraResults .dayContainer {
|
||||||
|
@ -199,11 +413,11 @@ ul.cineraNavPlain li.current > a {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 12px;
|
font-size: .75rem;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
width: 200px;
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraIndexEntries div a {
|
.cineraIndexEntries div a {
|
||||||
|
@ -243,36 +457,6 @@ ul.cineraNavPlain li.current > a {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px), (max-height: 512px)
|
|
||||||
{
|
|
||||||
#cineraIndexControl {
|
|
||||||
margin: 4px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cineraIndexControl,
|
|
||||||
#cineraResultsSummary {
|
|
||||||
font-size: 64%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cineraResults .dayContainer {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cineraResults .dayContainer .dayName {
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cineraResults .dayContainer .markerList {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraIndexEntries div a {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Player */
|
/* Player */
|
||||||
|
|
||||||
/* Player / Structure */
|
/* Player / Structure */
|
||||||
|
@ -299,7 +483,14 @@ ul.cineraNavPlain li.current > a {
|
||||||
|
|
||||||
.cineraMenus > .menu {
|
.cineraMenus > .menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
align-self: center;
|
min-height: 1em;
|
||||||
|
min-width: 1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu.filter.responsible,
|
.cineraMenus > .menu.filter.responsible,
|
||||||
|
@ -312,107 +503,117 @@ ul.cineraNavPlain li.current > a {
|
||||||
animation-iteration-count: 1;
|
animation-iteration-count: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_key {
|
.cineraMenus > .cineraHelp,
|
||||||
font-family: Inconsolata;
|
.cineraHelp {
|
||||||
font-size: 16px;
|
|
||||||
border: 1px solid;
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #111; /* Per project */
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
padding: 4px;
|
|
||||||
line-height: 16px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_key.word {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_key.modifer {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_key.unavailable,
|
|
||||||
.cineraMenus .help .help_container .help_text.unavailable,
|
|
||||||
.cineraMenus .help .help_container h2 .unavailable {
|
|
||||||
opacity: 0.32;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .key_block {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
flex-direction: row;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_text {
|
|
||||||
margin: 0 8px 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container h1 {
|
|
||||||
display: inline;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container h1:after {
|
|
||||||
content: "\a";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container h2 {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_paragraph {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus > .help {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
box-sizing: content-box;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
height: 6px;
|
height: .5rem;
|
||||||
|
width: .5rem;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
width: 6px;
|
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
font-size: 12px;
|
font-size: .75rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 6px;
|
line-height: .5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 64;
|
z-index: 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus .help .help_container .help_grid {
|
.cineraHelp .help_container {
|
||||||
display: inline-flex;
|
background-color: #000000; /* Per project */
|
||||||
flex-direction: column;
|
color: #EEEEEE; /* Per project */
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus .help .help_container {
|
|
||||||
background-color: black; /* Per project */
|
|
||||||
color: #EEE; /* Per project */
|
|
||||||
display: none;
|
display: none;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 612px;
|
top: 50%;
|
||||||
top: 42px;
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
max-height: 97%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus .help .help_container.visible {
|
.cineraHelp .help_container.visible {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cineraIndexControl .cineraIndexFilter .filter_container
|
.cineraHelp .help_container .help_custom_index {
|
||||||
{
|
background-color: #159 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container h2 .help_title_key {
|
||||||
|
padding: .2em;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-top: none;
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_key {
|
||||||
|
box-sizing: content-box;
|
||||||
|
font-family: Inconsolata;
|
||||||
|
font-size: 1rem;
|
||||||
|
border: 1px solid;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #111111; /* Per project */
|
||||||
|
border-radius: 4px;
|
||||||
|
min-height: 1em;
|
||||||
|
min-width: 1em;
|
||||||
|
padding: 4px;
|
||||||
|
line-height: 16px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_key.word {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_key.modifer {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_key.unavailable,
|
||||||
|
.cineraHelp .help_container .help_text.unavailable,
|
||||||
|
.cineraHelp .help_container h2 .unavailable {
|
||||||
|
opacity: 0.32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .key_block {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_text {
|
||||||
|
margin: 0 8px 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cineraHelp .help_container h1 {
|
||||||
|
display: inline;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container h1:after {
|
||||||
|
content: "\a";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cineraHelp .help_container h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_paragraph {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_grid {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .quotes_container,
|
.cineraMenus > .menu .quotes_container,
|
||||||
|
@ -423,18 +624,17 @@ 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;
|
||||||
display: none;
|
z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */
|
||||||
/* TODO(matt): Set the height to the player's height */
|
|
||||||
max-height: 512px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .refs,
|
.cineraMenus > .menu .refs,
|
||||||
.cineraMenus > .menu .link_container {
|
.cineraMenus > .menu .filter_container,
|
||||||
|
.cineraMenus > .menu .link_container,
|
||||||
|
.cineraMenus > .menu .credits_container {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,16 +642,13 @@ ul.cineraNavPlain li.current > a {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .filter_container {
|
.cineraMenus > .menu .sizing {
|
||||||
min-width: 350px;
|
z-index: -1;
|
||||||
}
|
display: block;
|
||||||
|
|
||||||
.cineraMenus > .menu .credits_container {
|
|
||||||
min-width: 240px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .visible {
|
.cineraMenus > .menu .visible {
|
||||||
display: block;
|
z-index: 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu > .refs .ref {
|
.cineraMenus > .menu > .refs .ref {
|
||||||
|
@ -484,7 +681,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
.cineraMenus > .menu > .refs .ref .timecode,
|
.cineraMenus > .menu > .refs .ref .timecode,
|
||||||
.cineraMenus > .menu > .filter_container .filter_mode,
|
.cineraMenus > .menu > .filter_container .filter_mode,
|
||||||
.cineraMenus > .menu > .link_container #cineraLinkMode {
|
.cineraMenus > .menu > .link_container #cineraLinkMode {
|
||||||
font-size: 12px;
|
font-size: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu > .filter_container .filter_mode,
|
.cineraMenus > .menu > .filter_container .filter_mode,
|
||||||
|
@ -514,7 +711,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
.cineraMenus > .menu > .refs .ref .quote_byline,
|
.cineraMenus > .menu > .refs .ref .quote_byline,
|
||||||
.cineraMenus > .menu > .filter_container .filter_title,
|
.cineraMenus > .menu > .filter_container .filter_title,
|
||||||
.cineraMenus > .menu > .credits_container .credit .role {
|
.cineraMenus > .menu > .credits_container .credit .role {
|
||||||
font-size: 10px;
|
font-size: .64rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu > .refs .ref .source,
|
.cineraMenus > .menu > .refs .ref .source,
|
||||||
|
@ -549,6 +746,11 @@ 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;
|
||||||
|
@ -568,11 +770,13 @@ 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;
|
||||||
|
@ -638,8 +842,16 @@ ul.cineraNavPlain li.current > a {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cineraPlayerContainer .video_container .direct_video {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraPlayerContainer .video_container .direct_video video {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container {
|
.cineraPlayerContainer .markers_container {
|
||||||
flex-shrink: 0;
|
flex-shrink: 1;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -647,10 +859,20 @@ ul.cineraNavPlain li.current > a {
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker {
|
.cineraPlayerContainer .markers_container > .episodeMarker {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 11px;
|
font-size: .75rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cineraPlayerContainer {
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraPlayerContainer .video_container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > a.episodeMarker {
|
.cineraPlayerContainer .markers_container > a.episodeMarker {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -665,7 +887,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker div:not(:nth-of-type(2)) {
|
.cineraPlayerContainer .markers_container > .episodeMarker div:not(:nth-of-type(2)) {
|
||||||
font-size: 15px;
|
font-size: .9rem;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,7 +905,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
max-height: 320px;
|
max-height: 320px; /* NOTE(matt): Required for the transition */
|
||||||
transition: max-height .32s;
|
transition: max-height .32s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,10 +928,10 @@ ul.cineraNavPlain li.current > a {
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 14px;
|
font-size: .8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker.skip {
|
.cinera:not(.mobile) .cineraPlayerContainer .markers_container > .markers .marker.skip {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
transition: max-height .32s;
|
transition: max-height .32s;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -745,7 +967,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .timecode {
|
.cineraPlayerContainer .markers_container > .markers .marker .timecode {
|
||||||
font-size: 9px;
|
font-size: .6rem;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -763,6 +985,7 @@ 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;
|
||||||
|
@ -781,97 +1004,124 @@ ul.cineraNavPlain li.current > a {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px), (max-height: 512px)
|
/* NOTE(matt): Mobile Style */
|
||||||
{
|
|
||||||
.cineraMenus .episode_name,
|
|
||||||
.cineraMenus > .menu > .view,
|
|
||||||
.cineraMenus > .menu > .views_container .view,
|
|
||||||
.cineraMenus > .help,
|
|
||||||
.markers_container > .episodeMarker div:nth-child(2),
|
|
||||||
.markers_container > .episodeMarker div:nth-child(3),
|
|
||||||
.markers_container > .markers .marker:not(.current) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraMenus {
|
#cineraIndex.mobile .cineraMenu.ViewSettings .cineraMenuItem,
|
||||||
justify-content: center;
|
#cineraIndex.mobile .cineraFilterProject .cineraText,
|
||||||
}
|
#cineraIndex.mobile .cineraIndexEntries div a {
|
||||||
|
display: flex;
|
||||||
.cineraMenus .menu .quotes_container,
|
box-sizing: border-box;
|
||||||
.cineraMenus .menu .references_container,
|
align-items: center;
|
||||||
.cineraMenus .menu .filter_container,
|
min-height: 10mm; /* NOTE(matt): From https://web.dev/accessible-tap-targets/ */
|
||||||
.cineraMenus .menu .link_container,
|
|
||||||
.cineraMenus .menu .credits_container {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
margin: auto;
|
|
||||||
max-height: 80%;
|
|
||||||
max-width: 97%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.cineraPlayerContainer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker {
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
|
||||||
width: 456px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 580px)
|
|
||||||
{
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
|
||||||
width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (max-width: 450px)
|
|
||||||
{
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
|
||||||
width: 256px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@media (max-width: 375px)
|
|
||||||
{
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.first,
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.prev,
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker,
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.last,
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.next {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.first,
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.prev {
|
|
||||||
border-right: 3px double;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.last,
|
|
||||||
.cineraPlayerContainer .markers_container > .episodeMarker.next {
|
|
||||||
border-left: 3px double;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cineraIndex.mobile .cineraMenu.ViewSettings .cineraMenuItem,
|
||||||
|
#cineraIndex.mobile .cineraFilterProject .cineraText,
|
||||||
|
#cineraIndex.mobile .cineraIndexEntries div {
|
||||||
|
margin: 8px; /* NOTE(matt): From https://web.dev/accessible-tap-targets/ */
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.mobile #cineraResults .dayContainer {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.mobile #cineraResults .dayContainer .dayName {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cineraIndex.mobile #cineraResults .dayContainer .markerList {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 0;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraMenus {
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraMenus,
|
||||||
|
.cinera.mobile .player_container {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraMenus .episode_name,
|
||||||
|
.cinera.mobile .cineraMenus > .menu > .view,
|
||||||
|
.cinera.mobile .cineraMenus > .menu > .views_container .view,
|
||||||
|
.cinera.mobile .cineraHelp,
|
||||||
|
#cineraIndex.mobile .cineraHelp,
|
||||||
|
.cinera.mobile .markers_container > .episodeMarker div:nth-child(2),
|
||||||
|
.cinera.mobile .markers_container > .episodeMarker div:nth-child(3),
|
||||||
|
.cinera.mobile .markers_container > .markers .marker:not(.current) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraMenus .menu {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraMenus .menu .quotes_container,
|
||||||
|
.cinera.mobile .cineraMenus .menu .references_container,
|
||||||
|
.cinera.mobile .cineraMenus .menu .filter_container,
|
||||||
|
.cinera.mobile .cineraMenus .menu .link_container,
|
||||||
|
.cinera.mobile .cineraMenus .menu .credits_container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker {
|
||||||
|
width: 32px;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .markers {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.first,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.prev,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.last,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.next {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.first,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.prev {
|
||||||
|
border-right: 3px double;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.last,
|
||||||
|
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.next {
|
||||||
|
border-left: 3px double;
|
||||||
|
}
|
||||||
|
/* Mobile Style End */
|
||||||
|
|
||||||
/* CUSTOM PAGE STYLE */
|
/* CUSTOM PAGE STYLE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -889,4 +1139,4 @@ Open menu: ▾ ▾
|
||||||
Open link in new tab: ⤻ or &10559; or &8599; or ⭷
|
Open link in new tab: ⤻ or &10559; or &8599; or ⭷
|
||||||
Play from timecode: ⏵ (or, if ↓ ▸)
|
Play from timecode: ⏵ (or, if ↓ ▸)
|
||||||
Playable from timecode: ▹
|
Playable from timecode: ▹
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
// NOTE(matt): To use, source this file inside the element you wish to be obscured while processing occurs.
|
||||||
|
// After processing is complete, call FlipClear()
|
||||||
|
|
||||||
|
var ThisScriptElement = document.currentScript;
|
||||||
|
var NodeToHide = ThisScriptElement.parentNode;
|
||||||
|
|
||||||
|
var CineraClearElement = document.createElement("DIV");
|
||||||
|
CineraClearElement.style.position = "fixed";
|
||||||
|
CineraClearElement.style.height = "100%";
|
||||||
|
CineraClearElement.style.width = "100%";
|
||||||
|
CineraClearElement.style.zIndex = 64;
|
||||||
|
|
||||||
|
var CineraPlacedClear = NodeToHide.appendChild(CineraClearElement);
|
||||||
|
var Colour = getBackgroundColourRGB(CineraPlacedClear);
|
||||||
|
CineraPlacedClear.style.background = "rgb(" + Colour.R + ", " + Colour.G + ", " + Colour.B + ")"
|
||||||
|
document.body.style.overflowY = "hidden";
|
||||||
|
|
||||||
|
function
|
||||||
|
FlipClear(BodyOverflowY)
|
||||||
|
{
|
||||||
|
document.body.style.overflowY = BodyOverflowY ? BodyOverflowY : null;
|
||||||
|
CineraPlacedClear.parentNode.removeChild(CineraPlacedClear);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,137 +1,6 @@
|
||||||
var originalTextContent = {
|
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
|
||||||
TitleQuotes: null,
|
|
||||||
TitleReferences: null,
|
|
||||||
TitleCredits: null,
|
|
||||||
EpisodePrev: null,
|
|
||||||
EpisodeNext: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
var menuState = [];
|
var CineraProps = {
|
||||||
var titleBar = document.querySelector(".cineraMenus");
|
|
||||||
var 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var 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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var 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) {
|
|
||||||
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) {
|
|
||||||
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") };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var views = {
|
|
||||||
REGULAR: 0,
|
|
||||||
THEATRE: 1,
|
|
||||||
SUPERTHEATRE: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
var devices = {
|
|
||||||
DESKTOP: 0,
|
|
||||||
MOBILE: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
var cineraProps = {
|
|
||||||
C: null,
|
C: null,
|
||||||
V: views.REGULAR,
|
V: views.REGULAR,
|
||||||
Z: null,
|
Z: null,
|
||||||
|
@ -142,132 +11,48 @@ var cineraProps = {
|
||||||
H: null,
|
H: null,
|
||||||
mH: null,
|
mH: null,
|
||||||
P: null,
|
P: null,
|
||||||
D: devices.DESKTOP,
|
Display: null,
|
||||||
|
FlexDirection: null,
|
||||||
|
JustifyContent: null,
|
||||||
|
O: null,
|
||||||
|
IsMobile: IsMobile(),
|
||||||
|
ScrollX: null,
|
||||||
|
ScrollY: null,
|
||||||
|
VODPlatform: null,
|
||||||
|
};
|
||||||
|
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
if(CineraProps.IsMobile)
|
||||||
|
{
|
||||||
|
setTimeout(DelayedUpdateSize, 512, player);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player.updateSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
screen.orientation.onchange = function() {
|
||||||
|
if(CineraProps.IsMobile)
|
||||||
|
{
|
||||||
|
setTimeout(DelayedUpdateSize, 512, player);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player.updateSize();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewsMenu = titleBar.querySelector(".views");
|
|
||||||
if(viewsMenu)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
|
|
||||||
var linkMenu = titleBar.querySelector(".link_container");
|
|
||||||
linkAnnotation = true;
|
|
||||||
if(linkMenu)
|
|
||||||
{
|
|
||||||
menuState.push(linkMenu);
|
|
||||||
|
|
||||||
var linkMode = linkMenu.querySelector("#cineraLinkMode");
|
|
||||||
var link = linkMenu.querySelector("#cineraLink");
|
|
||||||
|
|
||||||
linkMode.addEventListener("click", function(ev) {
|
|
||||||
toggleLinkMode(linkMode, link);
|
|
||||||
});
|
|
||||||
|
|
||||||
link.addEventListener("click", function(ev) {
|
|
||||||
CopyToClipboard(link);
|
|
||||||
toggleMenuVisibility(linkMenu);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
})
|
|
||||||
|
|
||||||
var focusedElement = null;
|
|
||||||
var focusedIdentifier = null;
|
|
||||||
|
|
||||||
var playerContainer = document.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");
|
|
||||||
var cinera = playerContainer.parentNode;
|
|
||||||
|
|
||||||
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
|
|
||||||
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) {
|
document.addEventListener("keydown", function(ev) {
|
||||||
var key = ev.key;
|
var key = ev.key;
|
||||||
if(ev.getModifierState("Shift") && key == " ")
|
if(ev.getModifierState("Shift") && key == " ")
|
||||||
|
@ -275,532 +60,17 @@ document.addEventListener("keydown", function(ev) {
|
||||||
key = "capitalSpace";
|
key = "capitalSpace";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement)
|
if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item)
|
||||||
{
|
{
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for(var i = 0; i < sourceMenus.length; ++i)
|
document.addEventListener("fullscreenchange", function() {
|
||||||
{
|
if(!document.fullscreenElement && CineraProps.V == views.SUPERTHEATRE)
|
||||||
sourceMenus[i].addEventListener("mouseenter", function(ev) {
|
{
|
||||||
handleMouseOverMenu(this, ev.type);
|
CineraProps.V = views.THEATRE;
|
||||||
})
|
localStorage.setItem(player.cineraViewStorageItem, views.THEATRE);
|
||||||
sourceMenus[i].addEventListener("mouseleave", function(ev) {
|
player.updateSize();
|
||||||
handleMouseOverMenu(this, ev.type);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
var colouredItems = playerContainer.querySelectorAll(".author, .member, .project");
|
|
||||||
for(i = 0; i < colouredItems.length; ++i)
|
|
||||||
{
|
|
||||||
setTextLightness(colouredItems[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var topicDots = document.querySelectorAll(".category");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKey(key) {
|
|
||||||
var gotKey = true;
|
|
||||||
switch (key) {
|
|
||||||
case "q": {
|
|
||||||
if(quotesMenu)
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(quotesMenu)
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "r": {
|
|
||||||
if(referencesMenu)
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(referencesMenu)
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "f": {
|
|
||||||
if(filterMenu)
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(filterMenu)
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "y": {
|
|
||||||
if(linkMenu)
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(linkMenu)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "c": {
|
|
||||||
if(creditsMenu)
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(creditsMenu)
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "t": {
|
|
||||||
if(cinera)
|
|
||||||
{
|
|
||||||
toggleTheatreMode();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case "T": {
|
|
||||||
if(cinera)
|
|
||||||
{
|
|
||||||
toggleSuperTheatreMode();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "Enter": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("quotes_container"))
|
|
||||||
{
|
|
||||||
var time = focusedElement.querySelector(".timecode").getAttribute("data-timestamp");
|
|
||||||
player.setTime(parseInt(time, 10));
|
|
||||||
player.play();
|
|
||||||
}
|
|
||||||
else if(focusedElement.parentNode.classList.contains("references_container"))
|
|
||||||
{
|
|
||||||
var time = focusedIdentifier.getAttribute("data-timestamp");
|
|
||||||
player.setTime(parseInt(time, 10));
|
|
||||||
player.play();
|
|
||||||
}
|
|
||||||
else if(focusedElement.parentNode.classList.contains("credit"))
|
|
||||||
{
|
|
||||||
if(focusedElement.hasAttribute)
|
|
||||||
{
|
|
||||||
var url = focusedElement.getAttribute("href");
|
|
||||||
window.open(url, "_blank");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log("TODO(matt): Implement me, perhaps?\n");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "o": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("references_container") ||
|
|
||||||
focusedElement.parentNode.classList.contains("quotes_container"))
|
|
||||||
{
|
|
||||||
var url = focusedElement.getAttribute("href");
|
|
||||||
window.open(url, "_blank");
|
|
||||||
}
|
|
||||||
else if(focusedElement.parentNode.classList.contains("credit"))
|
|
||||||
{
|
|
||||||
if(focusedElement.hasAttribute("href"))
|
|
||||||
{
|
|
||||||
var url = focusedElement.getAttribute("href");
|
|
||||||
window.open(url, "_blank");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "w": case "k": case "ArrowUp": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("quotes_container"))
|
|
||||||
{
|
|
||||||
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"))
|
|
||||||
{
|
|
||||||
setSpriteLightness(focusedElement.firstChild);
|
|
||||||
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support");
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person");
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "s": case "j": case "ArrowDown": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("quotes_container"))
|
|
||||||
{
|
|
||||||
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"))
|
|
||||||
{
|
|
||||||
setSpriteLightness(focusedElement.firstChild);
|
|
||||||
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support");
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person");
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "a": case "h": case "ArrowLeft": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("references_container"))
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
console.log(focusedElement);
|
|
||||||
unfocusSprite(focusedElement);
|
|
||||||
|
|
||||||
lastFocusedCreditItem = focusedElement.previousElementSibling;
|
|
||||||
setSpriteLightness(focusedElement.firstChild);
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "d": case "l": case "ArrowRight": {
|
|
||||||
if(focusedElement)
|
|
||||||
{
|
|
||||||
if(focusedElement.parentNode.classList.contains("references_container"))
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
lastFocusedCreditItem = focusedElement.nextElementSibling;
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "x": case " ": {
|
|
||||||
if(focusedElement && focusedElement.classList.contains("filter_content"))
|
|
||||||
{
|
|
||||||
filterItemToggle(focusedElement);
|
|
||||||
if(focusedElement.nextElementSibling &&
|
|
||||||
focusedElement.nextElementSibling.classList.contains("filter_content"))
|
|
||||||
{
|
|
||||||
focusedElement.classList.remove("focused");
|
|
||||||
unfocusSprite(focusedElement);
|
|
||||||
if(focusedElement.parentNode.classList.contains("filter_topics"))
|
|
||||||
{
|
|
||||||
lastFocusedTopic = focusedElement.nextElementSibling;
|
|
||||||
lastFocusedCategory = lastFocusedTopic;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastFocusedMedium = focusedElement.nextElementSibling;
|
|
||||||
lastFocusedCategory = lastFocusedMedium;
|
|
||||||
}
|
|
||||||
lastFocusedElement = lastFocusedCategory;
|
|
||||||
focusedElement = lastFocusedElement;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "X": case "capitalSpace": {
|
|
||||||
if(focusedElement && focusedElement.classList.contains("filter_content"))
|
|
||||||
{
|
|
||||||
filterItemToggle(focusedElement);
|
|
||||||
if(focusedElement.previousElementSibling &&
|
|
||||||
focusedElement.previousElementSibling.classList.contains("filter_content"))
|
|
||||||
{
|
|
||||||
focusedElement.classList.remove("focused");
|
|
||||||
unfocusSprite(focusedElement);
|
|
||||||
if(focusedElement.parentNode.classList.contains("filter_topics"))
|
|
||||||
{
|
|
||||||
lastFocusedTopic = focusedElement.previousElementSibling;
|
|
||||||
lastFocusedCategory = lastFocusedTopic;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastFocusedMedium = focusedElement.previousElementSibling;
|
|
||||||
lastFocusedCategory = lastFocusedMedium;
|
|
||||||
}
|
|
||||||
lastFocusedElement = lastFocusedCategory;
|
|
||||||
focusedElement = lastFocusedElement;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "z": {
|
|
||||||
toggleFilterOrLinkMode();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "v": {
|
|
||||||
if(focusedElement && focusedElement.classList.contains("filter_content"))
|
|
||||||
{
|
|
||||||
invertFilter(focusedElement)
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "V": {
|
|
||||||
resetFilter();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case "?": {
|
|
||||||
helpDocumentation.classList.toggle("visible");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'N':
|
|
||||||
case 'J':
|
|
||||||
case 'S': {
|
|
||||||
player.jumpToNextMarker();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'P':
|
|
||||||
case 'K':
|
|
||||||
case 'W': {
|
|
||||||
player.jumpToPrevMarker();
|
|
||||||
} break;
|
|
||||||
case '[':
|
|
||||||
case '<': {
|
|
||||||
if(prevEpisode)
|
|
||||||
{
|
|
||||||
location = prevEpisode.href;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case ']':
|
|
||||||
case '>': {
|
|
||||||
if(nextEpisode)
|
|
||||||
{
|
|
||||||
location = nextEpisode.href;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case 'Y': {
|
|
||||||
if(cineraLink)
|
|
||||||
{
|
|
||||||
if(linkAnnotation == false && player.playing)
|
|
||||||
{
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
if(linkMenu && !linkMenu.classList.contains("visible"))
|
|
||||||
{
|
|
||||||
toggleMenuVisibility(linkMenu);
|
|
||||||
}
|
|
||||||
SelectText(cineraLink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
gotKey = false;
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
return gotKey;
|
});
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,115 @@
|
||||||
|
var orientations = {
|
||||||
|
PORTRAIT: 0,
|
||||||
|
LANDSCAPE_LEFT: 90,
|
||||||
|
LANDSCAPE_RIGHT: -90,
|
||||||
|
};
|
||||||
|
|
||||||
|
function
|
||||||
|
DeriveReliableWindowDimensions()
|
||||||
|
{
|
||||||
|
// https://www.howtocreate.co.uk/tutorials/javascript/browserwindow
|
||||||
|
var Result = {
|
||||||
|
X: null,
|
||||||
|
Y: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ScrollPosX = window.scrollX;
|
||||||
|
var ScrollPosY = window.scrollY;
|
||||||
|
|
||||||
|
var DisplaySettings = [];
|
||||||
|
for(var i = 0; i < document.body.children.length; ++i)
|
||||||
|
{
|
||||||
|
var Child = document.body.children[i];
|
||||||
|
DisplaySettings.push(Child.style.display);
|
||||||
|
Child.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
var Element = document.createElement("div");
|
||||||
|
Element.style.position = "fixed";
|
||||||
|
Element.style.top = 0;
|
||||||
|
Element.style.right = 0;
|
||||||
|
Element.style.bottom = 0;
|
||||||
|
Element.style.left = 0;
|
||||||
|
Element.style.zOffset = -1;
|
||||||
|
Element.style.opacity = 0;
|
||||||
|
var ElementInPlace = document.body.appendChild(Element);
|
||||||
|
Result.X = ElementInPlace.offsetWidth;
|
||||||
|
Result.Y = ElementInPlace.offsetHeight;
|
||||||
|
ElementInPlace.remove();
|
||||||
|
|
||||||
|
for(var i = 0; i < document.body.children.length; ++i)
|
||||||
|
{
|
||||||
|
var Child = document.body.children[i];
|
||||||
|
Child.style.display = DisplaySettings.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollTriggeredInternally = true;
|
||||||
|
window.scroll(ScrollPosX, ScrollPosY);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
GetWindowDim(IsMobile)
|
||||||
|
{
|
||||||
|
var Result = {
|
||||||
|
X: null,
|
||||||
|
Y: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(IsMobile)
|
||||||
|
{
|
||||||
|
Result = DeriveReliableWindowDimensions();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result.X = document.documentElement.clientWidth;
|
||||||
|
Result.Y = document.documentElement.clientHeight;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsVisible(Element, WindowDim) {
|
||||||
|
var BoundingRect = Element.getBoundingClientRect();
|
||||||
|
return ((BoundingRect.top >= 0 && BoundingRect.top <= WindowDim.Y) ||
|
||||||
|
(BoundingRect.bottom >= 0 && BoundingRect.bottom <= WindowDim.Y) ||
|
||||||
|
(BoundingRect.top < 0 && BoundingRect.bottom > WindowDim.Y))
|
||||||
|
&& ((BoundingRect.left >= 0 && BoundingRect.left <= WindowDim.X) ||
|
||||||
|
(BoundingRect.right >= 0 && BoundingRect.right <= WindowDim.X) ||
|
||||||
|
(BoundingRect.left < 0 && BoundingRect.right > WindowDim.X));
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
GetRealOrientation(PreferredLandscape, IsMobile)
|
||||||
|
{
|
||||||
|
var Result = screen.orientation.angle;
|
||||||
|
var WindowDim = GetWindowDim(IsMobile);
|
||||||
|
if(WindowDim.Y > WindowDim.X)
|
||||||
|
{
|
||||||
|
Result = orientations.PORTRAIT;
|
||||||
|
}
|
||||||
|
else if(Result == undefined || Result == orientations.PORTRAIT)
|
||||||
|
{
|
||||||
|
Result = PreferredLandscape;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var DebugConsoleMessageCount = 0;
|
||||||
|
function Say(Message)
|
||||||
|
{
|
||||||
|
var DebugConsole = document.getElementById("debug-console");
|
||||||
|
if(DebugConsole)
|
||||||
|
{
|
||||||
|
DebugConsole.textContent += DebugConsoleMessageCount++ + ": " + Message + "\n";
|
||||||
|
DebugConsole.scrollTo(
|
||||||
|
{
|
||||||
|
top: DebugConsole.scrollHeight,
|
||||||
|
behavior: "smooth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getBackgroundBrightness(element) {
|
function getBackgroundBrightness(element) {
|
||||||
var colour = getComputedStyle(element).getPropertyValue("background-color");
|
var colour = getComputedStyle(element).getPropertyValue("background-color");
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
|
@ -80,14 +192,21 @@ function enableSprite(Element)
|
||||||
|
|
||||||
function disableSprite(Element)
|
function disableSprite(Element)
|
||||||
{
|
{
|
||||||
if(Element.classList.contains("cineraSprite"))
|
if(Element.classList.contains("focused"))
|
||||||
{
|
{
|
||||||
setSpriteLightness(Element);
|
focusSprite(Element);
|
||||||
Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px";
|
|
||||||
}
|
}
|
||||||
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,3 +229,386 @@ function unfocusSprite(Element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function IsMobile() {
|
||||||
|
// NOTE(matt): From https://medium.com/simplejs/detect-the-users-device-type-with-a-simple-javascript-check-4fc656b735e1
|
||||||
|
var Identifier = navigator.userAgent||navigator.vendor||window.opera;
|
||||||
|
var Result = (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(Identifier)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(Identifier.substr(0,4)));
|
||||||
|
return Result;
|
||||||
|
};
|
||||||
|
|
||||||
|
function GetRule(SelectorText)
|
||||||
|
{
|
||||||
|
// NOTE(matt): Modifying CSS style
|
||||||
|
// From https://stackoverflow.com/a/566445
|
||||||
|
// https://usefulangle.com/post/39/adding-css-to-stylesheet-with-javascript
|
||||||
|
var Result = undefined;
|
||||||
|
|
||||||
|
var StyleSheets = document.styleSheets;
|
||||||
|
var cssRuleCode = document.all ? 'rules' : 'cssRules'; // account for IE and FF
|
||||||
|
for(var StyleSheetIndex = StyleSheets.length - 1; StyleSheetIndex >= 0; --StyleSheetIndex)
|
||||||
|
{
|
||||||
|
var ThisSheet = StyleSheets[StyleSheetIndex];
|
||||||
|
if(ThisSheet.href !== null && ThisSheet.href.includes(location.hostname))
|
||||||
|
{
|
||||||
|
var Rules = ThisSheet[cssRuleCode];
|
||||||
|
for(var RuleIndex = Rules.length - 1; RuleIndex >= 0; --RuleIndex)
|
||||||
|
{
|
||||||
|
var ThisRule = Rules[RuleIndex];
|
||||||
|
if(SelectorText === ThisRule.selectorText)
|
||||||
|
{
|
||||||
|
Result = ThisRule;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Result !== undefined) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
GetRulesOfStyleSheetIndex(Index)
|
||||||
|
{
|
||||||
|
var Result = undefined;
|
||||||
|
var StyleSheets = document.styleSheets;
|
||||||
|
var cssRuleCode = document.all ? 'rules' : 'cssRules'; // account for IE and FF
|
||||||
|
var StyleSheet = StyleSheets[Index];
|
||||||
|
if(StyleSheet)
|
||||||
|
{
|
||||||
|
Result = StyleSheet[cssRuleCode];
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
GetLocalStyleSheet()
|
||||||
|
{
|
||||||
|
var Result;
|
||||||
|
var StyleSheets = document.styleSheets;
|
||||||
|
for(var StyleSheetIndex = StyleSheets.length - 1; StyleSheetIndex >= 0; --StyleSheetIndex)
|
||||||
|
{
|
||||||
|
var This = StyleSheets[StyleSheetIndex];
|
||||||
|
if(This.href !== null && This.href.includes(location.hostname) && !This.disabled)
|
||||||
|
{
|
||||||
|
Result = This;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
GetOrSetRule(SelectorText)
|
||||||
|
{
|
||||||
|
var Result = GetRule(SelectorText);
|
||||||
|
if(Result === undefined)
|
||||||
|
{
|
||||||
|
var StyleSheet = GetLocalStyleSheet();
|
||||||
|
if(StyleSheet)
|
||||||
|
{
|
||||||
|
var cssRuleCode = document.all ? 'rules' : 'cssRules'; // account for IE and FF
|
||||||
|
var Rules = StyleSheet[cssRuleCode];
|
||||||
|
var RuleIndex = StyleSheet.insertRule(SelectorText + "{}", Rules.length - 1);
|
||||||
|
Result = Rules[RuleIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsInRangeEx(Min, N, Max)
|
||||||
|
{
|
||||||
|
return N > Min && N < Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-scrolling */
|
||||||
|
var ScrollTriggeredInternally = false;
|
||||||
|
var LastScrollYPos = 0;
|
||||||
|
var ScrollTicking = false;
|
||||||
|
var ScrollerFunction;
|
||||||
|
var ScrollCondition;
|
||||||
|
|
||||||
|
function ScrollTo(Element, ScrollPos, IsMobile, StickyObscuringElement) {
|
||||||
|
if(Element.offsetWidth && Element.offsetHeight)
|
||||||
|
{
|
||||||
|
var ScrolledToTop = ScrollPos == 0;
|
||||||
|
var ScrolledToBottom = (window.innerHeight + Math.ceil(window.pageYOffset)) >= document.body.scrollHeight;
|
||||||
|
if(!ScrolledToTop || !ScrolledToBottom)
|
||||||
|
{
|
||||||
|
var Ceiling = StickyObscuringElement ? StickyObscuringElement.offsetHeight : 0;
|
||||||
|
var VisibleArea = GetWindowDim(IsMobile);
|
||||||
|
VisibleArea.Y -= Ceiling;
|
||||||
|
|
||||||
|
var PercentageOfVisibleHeightToGather = 5;
|
||||||
|
var GatherableHeight = VisibleArea.Y * PercentageOfVisibleHeightToGather / 100;
|
||||||
|
|
||||||
|
var BoundingRect = Element.getBoundingClientRect();
|
||||||
|
|
||||||
|
var ElementTop = BoundingRect.top - Ceiling;
|
||||||
|
var ElementBottom = ElementTop + BoundingRect.height;
|
||||||
|
|
||||||
|
var UpperProtrusion = -ElementTop;
|
||||||
|
var LowerProtrusion = ElementBottom - VisibleArea.Y;
|
||||||
|
|
||||||
|
var DesiredScroll = null;
|
||||||
|
var YOffsetFromPage = getElementYOffsetFromPage(Element);
|
||||||
|
if(IsInRangeEx(0, UpperProtrusion, GatherableHeight))
|
||||||
|
{
|
||||||
|
if(!ScrolledToBottom)
|
||||||
|
{
|
||||||
|
DesiredScroll = YOffsetFromPage - Ceiling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(IsInRangeEx(0, LowerProtrusion, GatherableHeight))
|
||||||
|
{
|
||||||
|
if(!ScrolledToTop)
|
||||||
|
{
|
||||||
|
if(IsInRangeEx(0, UpperProtrusion + LowerProtrusion, GatherableHeight))
|
||||||
|
{
|
||||||
|
DesiredScroll = YOffsetFromPage - Ceiling;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DesiredScroll = ScrollPos + LowerProtrusion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DesiredScroll !== null && DesiredScroll != ScrollPos)
|
||||||
|
{
|
||||||
|
window.scrollTo({
|
||||||
|
top: DesiredScroll,
|
||||||
|
behavior: "smooth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
|
||||||
|
{
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
if(ScrollTriggeredInternally)
|
||||||
|
{
|
||||||
|
ScrollTriggeredInternally = false;
|
||||||
|
}
|
||||||
|
else if(ScrollCondition == undefined || ScrollCondition == true)
|
||||||
|
{
|
||||||
|
LastScrollYPos = window.scrollY;
|
||||||
|
|
||||||
|
if (!ScrollTicking) {
|
||||||
|
window.requestAnimationFrame(function() {
|
||||||
|
clearTimeout(ScrollerFunction);
|
||||||
|
ScrollerFunction = setTimeout(ScrollTo, 2000, Element, LastScrollYPos, IsMobile, StickyObscuringElement);
|
||||||
|
ScrollTicking = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ScrollTicking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* /Auto-scrolling */
|
||||||
|
|
||||||
|
function getElementXOffsetFromPage(el) {
|
||||||
|
var left = 0;
|
||||||
|
do {
|
||||||
|
left += el.offsetLeft;
|
||||||
|
} while (el = el.offsetParent);
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElementYOffsetFromPage(el) {
|
||||||
|
var top = 0;
|
||||||
|
do {
|
||||||
|
top += el.offsetTop;
|
||||||
|
} while (el = el.offsetParent);
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
MaxWidthOfElement(Element, WindowDim)
|
||||||
|
{
|
||||||
|
// NOTE(matt): This works fine for Elements whose natural max width fills the whole line, i.e. block elements and children
|
||||||
|
// thereof. To support inline elements, we'll need to mirror MaxHeightOfElement() and may as well roll the two
|
||||||
|
// functions into one.
|
||||||
|
var Result = 0;
|
||||||
|
|
||||||
|
var OriginalWidth = Element.style.width;
|
||||||
|
Element.style.width = "100%";
|
||||||
|
var NaturalMax = Element.offsetWidth;
|
||||||
|
Element.style.width = OriginalWidth;
|
||||||
|
|
||||||
|
var InnerWidth = WindowDim ? WindowDim.X : document.documentElement.clientWidth;
|
||||||
|
Result = Math.min(NaturalMax, InnerWidth);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
MaxHeightOfElement(Element, WindowDim)
|
||||||
|
{
|
||||||
|
var Result = 0;
|
||||||
|
|
||||||
|
var DisplaySettings = [];
|
||||||
|
for(var i = 0; i < Element.children.length; ++i)
|
||||||
|
{
|
||||||
|
var Child = Element.children[i];
|
||||||
|
DisplaySettings.push(Child.style.display);
|
||||||
|
Child.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
var OriginalHeight = Element.style.height;
|
||||||
|
Element.style.height = "100%";
|
||||||
|
var NaturalMax = Element.offsetHeight;
|
||||||
|
Element.style.height = OriginalHeight;
|
||||||
|
|
||||||
|
var InnerHeight = WindowDim ? WindowDim.Y : document.documentElement.clientHeight;
|
||||||
|
if(NaturalMax > 0)
|
||||||
|
{
|
||||||
|
Result = Math.min(NaturalMax, InnerHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = InnerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < Element.children.length; ++i)
|
||||||
|
{
|
||||||
|
var Child = Element.children[i];
|
||||||
|
Child.style.display = DisplaySettings.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
MaxDimensionsOfElement(Element, WindowDim)
|
||||||
|
{
|
||||||
|
var Result = {
|
||||||
|
X: null,
|
||||||
|
Y: null,
|
||||||
|
};
|
||||||
|
Result.X = MaxWidthOfElement(Element, WindowDim);
|
||||||
|
Result.Y = MaxHeightOfElement(Element, WindowDim);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
Clamp(EndA, N, EndB)
|
||||||
|
{
|
||||||
|
var Min = EndA < EndB ? EndA : EndB;
|
||||||
|
var Max = EndA > EndB ? EndA : EndB;
|
||||||
|
return N < Min ? Min : N > Max ? Max : N;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
Clamp01(N)
|
||||||
|
{
|
||||||
|
return N < 0 ? 0 : N > 1 ? 1 : N;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
Lerp(A, t, B)
|
||||||
|
{
|
||||||
|
return (1-t)*A + t*B;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("focus", function(){
|
||||||
|
SetHelpFocused(Button);
|
||||||
|
});
|
||||||
|
|
||||||
|
Button.addEventListener("click", function() {
|
||||||
|
DocumentationContainer.classList.toggle("visible");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
element = element.parentElement;
|
||||||
|
Colour = getComputedStyle(element).getPropertyValue("background-color");
|
||||||
|
++depth;
|
||||||
|
}
|
||||||
|
var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", ");
|
||||||
|
var Result = {
|
||||||
|
R: parseInt(Staging[0]),
|
||||||
|
G: parseInt(Staging[1]),
|
||||||
|
B: parseInt(Staging[2]),
|
||||||
|
};
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBackgroundBrightness(element) {
|
||||||
|
var Colour = getBackgroundColourRGB(element);
|
||||||
|
var Result = Math.sqrt(Colour.R * Colour.R * .241 +
|
||||||
|
Colour.G * Colour.G * .691 +
|
||||||
|
Colour.B * Colour.B * .068);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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%)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if(getBackgroundBrightness(topicDot.parentNode) < 127)
|
||||||
|
{
|
||||||
|
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
|
||||||
|
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
|
||||||
|
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,641 +0,0 @@
|
||||||
document.body.style.overflowY = "scroll";
|
|
||||||
|
|
||||||
if (location.hash && location.hash.length > 0) {
|
|
||||||
var initialQuery = location.hash;
|
|
||||||
if (initialQuery[0] == "#") {
|
|
||||||
initialQuery = initialQuery.slice(1);
|
|
||||||
}
|
|
||||||
document.getElementById("query").value = decodeURIComponent(initialQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
var indexControl = document.getElementById("cineraIndexControl");
|
|
||||||
var indexSort = indexControl.querySelector("#cineraIndexSort");
|
|
||||||
var indexSortChronological = true;
|
|
||||||
|
|
||||||
var filterMenu = indexControl.querySelector(".cineraIndexFilter");
|
|
||||||
if(filterMenu)
|
|
||||||
{
|
|
||||||
var filterContainer = filterMenu.querySelector(".filter_container");
|
|
||||||
//menuState.push(linkMenu);
|
|
||||||
|
|
||||||
filterMenu.addEventListener("mouseenter", function(ev) {
|
|
||||||
filterContainer.style.display = "block";
|
|
||||||
});
|
|
||||||
|
|
||||||
filterMenu.addEventListener("mouseleave", function(ev) {
|
|
||||||
filterContainer.style.display = "none";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideEntriesOfProject(ProjectElement)
|
|
||||||
{
|
|
||||||
if(!ProjectElement.classList.contains("off"))
|
|
||||||
{
|
|
||||||
ProjectElement.classList.add("off");
|
|
||||||
}
|
|
||||||
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var ThisProject = projects[i];
|
|
||||||
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
||||||
{
|
|
||||||
ThisProject.filteredOut = true;
|
|
||||||
if(ThisProject.entriesContainer != null)
|
|
||||||
{
|
|
||||||
ThisProject.entriesContainer.style.display = "none";
|
|
||||||
disableSprite(ThisProject.entriesContainer.parentElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showEntriesOfProject(ProjectElement)
|
|
||||||
{
|
|
||||||
if(ProjectElement.classList.contains("off"))
|
|
||||||
{
|
|
||||||
ProjectElement.classList.remove("off");
|
|
||||||
}
|
|
||||||
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var ThisProject = projects[i];
|
|
||||||
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
||||||
{
|
|
||||||
ThisProject.filteredOut = false;
|
|
||||||
if(ThisProject.entriesContainer != null)
|
|
||||||
{
|
|
||||||
ThisProject.entriesContainer.style.display = "flex";
|
|
||||||
enableSprite(ThisProject.entriesContainer.parentElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideProjectSearchResults(baseURL, searchLocation, playerLocation)
|
|
||||||
{
|
|
||||||
var cineraResults = document.getElementById("cineraResults");
|
|
||||||
if(cineraResults)
|
|
||||||
{
|
|
||||||
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
|
|
||||||
for(var i = 0; i < cineraResultsProjects.length; ++i)
|
|
||||||
{
|
|
||||||
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
|
|
||||||
{
|
|
||||||
cineraResultsProjects[i].style.display = "none";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showProjectSearchResults(baseURL, searchLocation, playerLocation)
|
|
||||||
{
|
|
||||||
var cineraResults = document.getElementById("cineraResults");
|
|
||||||
if(cineraResults)
|
|
||||||
{
|
|
||||||
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
|
|
||||||
for(var i = 0; i < cineraResultsProjects.length; ++i)
|
|
||||||
{
|
|
||||||
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
|
|
||||||
{
|
|
||||||
cineraResultsProjects[i].style.display = "flex";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
|
|
||||||
{
|
|
||||||
var baseURL = ProjectFilterElement.attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var searchLocation = ProjectFilterElement.attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var playerLocation = ProjectFilterElement.attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
var shouldShow = ProjectFilterElement.classList.contains("off");
|
|
||||||
if(shouldShow)
|
|
||||||
{
|
|
||||||
ProjectFilterElement.classList.remove("off");
|
|
||||||
enableSprite(ProjectFilterElement);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProjectFilterElement.classList.add("off");
|
|
||||||
disableSprite(ProjectFilterElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var ThisProject = projects[i];
|
|
||||||
|
|
||||||
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
||||||
{
|
|
||||||
if(shouldShow)
|
|
||||||
{
|
|
||||||
ThisProject.filteredOut = false;
|
|
||||||
enableSprite(ThisProject.projectTitleElement.parentElement);
|
|
||||||
if(ThisProject.entriesContainer != null)
|
|
||||||
{
|
|
||||||
ThisProject.entriesContainer.style.display = "flex";
|
|
||||||
}
|
|
||||||
showProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThisProject.filteredOut = true;
|
|
||||||
disableSprite(ThisProject.projectTitleElement.parentElement);
|
|
||||||
if(ThisProject.entriesContainer != null)
|
|
||||||
{
|
|
||||||
ThisProject.entriesContainer.style.display = "none";
|
|
||||||
}
|
|
||||||
hideProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var indexChildFilterProjects = ProjectFilterElement.querySelectorAll(".cineraFilterProject");
|
|
||||||
|
|
||||||
for(var j = 0; j < indexChildFilterProjects.length; ++j)
|
|
||||||
{
|
|
||||||
var ThisElement = indexChildFilterProjects[j];
|
|
||||||
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var searchLocation = ThisElement.attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var playerLocation = ThisElement.attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
if(shouldShow)
|
|
||||||
{
|
|
||||||
showEntriesOfProject(ThisElement);
|
|
||||||
showProjectSearchResults(baseURL, searchLocation, playerLocation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hideEntriesOfProject(ThisElement);
|
|
||||||
hideProjectSearchResults(baseURL, searchLocation, playerLocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var indexFilter = indexControl.querySelector(".cineraIndexFilter");
|
|
||||||
if(indexFilter)
|
|
||||||
{
|
|
||||||
var indexFilterProjects = indexFilter.querySelectorAll(".cineraFilterProject");
|
|
||||||
for(var i = 0; i < indexFilterProjects.length; ++i)
|
|
||||||
{
|
|
||||||
indexFilterProjects[i].addEventListener("mouseover", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.classList.add("focused");
|
|
||||||
focusSprite(this);
|
|
||||||
});
|
|
||||||
indexFilterProjects[i].addEventListener("mouseout", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.classList.remove("focused");
|
|
||||||
unfocusSprite(this);
|
|
||||||
});
|
|
||||||
indexFilterProjects[i].addEventListener("click", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
toggleEntriesOfProjectAndChildren(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultsSummary = document.getElementById("cineraResultsSummary");
|
|
||||||
var resultsContainer = document.getElementById("cineraResults");
|
|
||||||
|
|
||||||
var indexContainer = document.getElementById("cineraIndex");
|
|
||||||
|
|
||||||
var projectsContainer = indexContainer.querySelectorAll(".cineraIndexProject");
|
|
||||||
|
|
||||||
var projectContainerPrototype = document.createElement("DIV");
|
|
||||||
projectContainerPrototype.classList.add("projectContainer");
|
|
||||||
|
|
||||||
var dayContainerPrototype = document.createElement("DIV");
|
|
||||||
dayContainerPrototype.classList.add("dayContainer");
|
|
||||||
|
|
||||||
var dayNamePrototype = document.createElement("SPAN");
|
|
||||||
dayNamePrototype.classList.add("dayName");
|
|
||||||
dayContainerPrototype.appendChild(dayNamePrototype);
|
|
||||||
|
|
||||||
var markerListPrototype = document.createElement("DIV");
|
|
||||||
markerListPrototype.classList.add("markerList");
|
|
||||||
dayContainerPrototype.appendChild(markerListPrototype);
|
|
||||||
|
|
||||||
var markerPrototype = document.createElement("A");
|
|
||||||
markerPrototype.classList.add("marker");
|
|
||||||
if(resultsContainer.getAttribute("data-single") == 0)
|
|
||||||
{
|
|
||||||
markerPrototype.setAttribute("target", "_blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareToParseIndexFile(project)
|
|
||||||
{
|
|
||||||
project.xhr.addEventListener("load", function() {
|
|
||||||
var contents = project.xhr.response;
|
|
||||||
var lines = contents.split("\n");
|
|
||||||
var mode = "none";
|
|
||||||
var episode = null;
|
|
||||||
for (var i = 0; i < lines.length; ++i) {
|
|
||||||
var line = lines[i];
|
|
||||||
if (line.trim().length == 0) { continue; }
|
|
||||||
if (line == "---") {
|
|
||||||
if (episode != null && episode.name != null && episode.title != null) {
|
|
||||||
episode.filename = episode.name;
|
|
||||||
episode.day = getEpisodeName(episode.filename + ".html.md");
|
|
||||||
episode.dayContainerPrototype = project.dayContainerPrototype;
|
|
||||||
episode.markerPrototype = markerPrototype;
|
|
||||||
episode.playerURLPrefix = project.playerURLPrefix;
|
|
||||||
project.episodes.push(episode);
|
|
||||||
}
|
|
||||||
episode = {};
|
|
||||||
mode = "none";
|
|
||||||
} else if (line.startsWith("name:")) {
|
|
||||||
episode.name = line.slice(6);
|
|
||||||
} else if (line.startsWith("title:")) {
|
|
||||||
episode.title = line.slice(7).trim().slice(1, -1);
|
|
||||||
} else if (line.startsWith("markers")) {
|
|
||||||
mode = "markers";
|
|
||||||
episode.markers = [];
|
|
||||||
} else if (mode == "markers") {
|
|
||||||
var match = line.match(/"(\d+)": "(.+)"/);
|
|
||||||
if (match == null) {
|
|
||||||
console.log(name, line);
|
|
||||||
} else {
|
|
||||||
var totalTime = parseInt(line.slice(1));
|
|
||||||
var marker = {
|
|
||||||
totalTime: totalTime,
|
|
||||||
prettyTime: markerTime(totalTime),
|
|
||||||
text: match[2].replace(/\\"/g, "\"")
|
|
||||||
}
|
|
||||||
episode.markers.push(marker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.querySelector(".spinner").classList.remove("show");
|
|
||||||
project.parsed = true;
|
|
||||||
runSearch(true);
|
|
||||||
});
|
|
||||||
project.xhr.addEventListener("error", function() {
|
|
||||||
console.error("Failed to load content");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var projects = [];
|
|
||||||
function prepareProjects()
|
|
||||||
{
|
|
||||||
for(var i = 0; i < projectsContainer.length; ++i)
|
|
||||||
{
|
|
||||||
var ID = projectsContainer[i].attributes.getNamedItem("data-project").value;
|
|
||||||
var baseURL = projectsContainer[i].attributes.getNamedItem("data-baseURL").value;
|
|
||||||
var searchLocation = projectsContainer[i].attributes.getNamedItem("data-searchLocation").value;
|
|
||||||
var playerLocation = projectsContainer[i].attributes.getNamedItem("data-playerLocation").value;
|
|
||||||
var theme = projectsContainer[i].classList.item(1);
|
|
||||||
|
|
||||||
projects[i] =
|
|
||||||
{
|
|
||||||
baseURL: baseURL,
|
|
||||||
searchLocation: searchLocation,
|
|
||||||
playerLocation: playerLocation,
|
|
||||||
playerURLPrefix: (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : ""),
|
|
||||||
indexLocation: (baseURL ? baseURL + "/" : "") + (searchLocation ? searchLocation + "/" : "") + ID + ".index",
|
|
||||||
projectTitleElement: projectsContainer[i].querySelector(":scope > .cineraProjectTitle"),
|
|
||||||
entriesContainer: projectsContainer[i].querySelector(":scope > .cineraIndexEntries"),
|
|
||||||
dayContainerPrototype: dayContainerPrototype.cloneNode(true),
|
|
||||||
filteredOut: false,
|
|
||||||
parsed: false,
|
|
||||||
searched: false,
|
|
||||||
resultsToRender: [],
|
|
||||||
resultsIndex: 0,
|
|
||||||
theme: theme,
|
|
||||||
episodes: [],
|
|
||||||
xhr: new XMLHttpRequest(),
|
|
||||||
}
|
|
||||||
|
|
||||||
projects[i].dayContainerPrototype.classList.add(theme);
|
|
||||||
projects[i].dayContainerPrototype.children[1].classList.add(theme);
|
|
||||||
|
|
||||||
document.querySelector(".spinner").classList.add("show");
|
|
||||||
projects[i].xhr.open("GET", projects[i].indexLocation);
|
|
||||||
projects[i].xhr.setRequestHeader("Content-Type", "text/plain");
|
|
||||||
projects[i].xhr.send();
|
|
||||||
prepareToParseIndexFile(projects[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prepareProjects();
|
|
||||||
|
|
||||||
indexSort.addEventListener("click", function(ev) {
|
|
||||||
if(indexSortChronological)
|
|
||||||
{
|
|
||||||
this.firstChild.nodeValue = "Sort: New to Old ⏷"
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
if(projects[i].entriesContainer)
|
|
||||||
{
|
|
||||||
projects[i].entriesContainer.style.flexFlow = "column-reverse";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.firstChild.nodeValue = "Sort: Old to New ⏶"
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
if(projects[i].entriesContainer)
|
|
||||||
{
|
|
||||||
projects[i].entriesContainer.style.flexFlow = "column";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
indexSortChronological = !indexSortChronological;
|
|
||||||
runSearch(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
var lastQuery = null;
|
|
||||||
var markerList = null;
|
|
||||||
var projectContainer = null;
|
|
||||||
var resultsMarkerIndex = -1;
|
|
||||||
var rendering = false;
|
|
||||||
|
|
||||||
var highlightPrototype = document.createElement("B");
|
|
||||||
|
|
||||||
function getEpisodeName(filename) {
|
|
||||||
var day = filename;
|
|
||||||
var dayParts = day.match(/([a-zA-Z_-]+)([0-9]+)?([a-zA-Z]+)?/);
|
|
||||||
day = dayParts[1].slice(0, 1).toUpperCase() + dayParts[1].slice(1) + (dayParts[2] ? " " + dayParts[2] : "") + (dayParts[3] ? " " + dayParts[3].toUpperCase() : "");
|
|
||||||
return day;
|
|
||||||
}
|
|
||||||
|
|
||||||
function markerTime(totalTime) {
|
|
||||||
var markTime = "(";
|
|
||||||
var hours = Math.floor(totalTime / 60 / 60);
|
|
||||||
var minutes = Math.floor(totalTime / 60) % 60;
|
|
||||||
var seconds = totalTime % 60;
|
|
||||||
if (hours > 0) {
|
|
||||||
markTime += padTimeComponent(hours) + ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
markTime += padTimeComponent(minutes) + ":" + padTimeComponent(seconds) + ")";
|
|
||||||
|
|
||||||
return markTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
function padTimeComponent(component) {
|
|
||||||
return (component < 10 ? "0" + component : component);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetProjectsForSearch()
|
|
||||||
{
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var project = projects[i];
|
|
||||||
project.searched = false;
|
|
||||||
project.resultsToRender = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var renderHandle;
|
|
||||||
|
|
||||||
function runSearch(refresh) {
|
|
||||||
var queryStr = document.getElementById("query").value;
|
|
||||||
if (refresh || lastQuery != queryStr) {
|
|
||||||
var oldResultsContainer = resultsContainer;
|
|
||||||
resultsContainer = oldResultsContainer.cloneNode(false);
|
|
||||||
oldResultsContainer.parentNode.insertBefore(resultsContainer, oldResultsContainer);
|
|
||||||
oldResultsContainer.remove();
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
projects[i].resultsIndex = 0;
|
|
||||||
}
|
|
||||||
resultsMarkerIndex = -1;
|
|
||||||
}
|
|
||||||
lastQuery = queryStr;
|
|
||||||
|
|
||||||
resetProjectsForSearch();
|
|
||||||
|
|
||||||
var numEpisodes = 0;
|
|
||||||
var numMarkers = 0;
|
|
||||||
var totalSeconds = 0;
|
|
||||||
|
|
||||||
// NOTE(matt): Function defined within runSearch() so that we can modify numEpisodes, numMarkers and totalSeconds
|
|
||||||
function runSearchInterior(resultsToRender, query, episode)
|
|
||||||
{
|
|
||||||
var matches = [];
|
|
||||||
for (var k = 0; k < episode.markers.length; ++k) {
|
|
||||||
query.lastIndex = 0;
|
|
||||||
var result = query.exec(episode.markers[k].text);
|
|
||||||
if (result && result[0].length > 0) {
|
|
||||||
numMarkers++;
|
|
||||||
matches.push(episode.markers[k]);
|
|
||||||
if (k < episode.markers.length-1) {
|
|
||||||
totalSeconds += episode.markers[k+1].totalTime - episode.markers[k].totalTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matches.length > 0) {
|
|
||||||
numEpisodes++;
|
|
||||||
resultsToRender.push({
|
|
||||||
query: query,
|
|
||||||
episode: episode,
|
|
||||||
matches: matches
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryStr && queryStr.length > 0) {
|
|
||||||
indexContainer.style.display = "none";
|
|
||||||
resultsSummary.style.display = "block";
|
|
||||||
var shouldRender = false;
|
|
||||||
var query = new RegExp(queryStr.replace("(", "\\(").replace(")", "\\)").replace(/\|+/, "\|").replace(/\|$/, "").replace(/(^|[^\\])\\$/, "$1"), "gi");
|
|
||||||
|
|
||||||
// Visible
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var project = projects[i];
|
|
||||||
if(project.parsed && !project.filteredOut && project.episodes.length > 0) {
|
|
||||||
if(indexSortChronological)
|
|
||||||
{
|
|
||||||
for(var j = 0; j < project.episodes.length; ++j) {
|
|
||||||
var episode = project.episodes[j];
|
|
||||||
runSearchInterior(project.resultsToRender, query, episode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(var j = project.episodes.length; j > 0; --j) {
|
|
||||||
var episode = project.episodes[j - 1];
|
|
||||||
runSearchInterior(project.resultsToRender, query, episode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldRender = true;
|
|
||||||
project.searched = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invisible
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var project = projects[i];
|
|
||||||
if(project.parsed && project.filteredOut && !project.searched && project.episodes.length > 0) {
|
|
||||||
if(indexSortChronological)
|
|
||||||
{
|
|
||||||
for(var j = 0; j < project.episodes.length; ++j) {
|
|
||||||
var episode = project.episodes[j];
|
|
||||||
runSearchInterior(project.resultsToRender, query, episode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(var j = project.episodes.length; j > 0; --j) {
|
|
||||||
var episode = project.episodes[j - 1];
|
|
||||||
runSearchInterior(project.resultsToRender, query, episode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldRender = true;
|
|
||||||
project.searched = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(shouldRender)
|
|
||||||
{
|
|
||||||
if (rendering) {
|
|
||||||
clearTimeout(renderHandle);
|
|
||||||
}
|
|
||||||
renderResults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indexContainer.style.display = "block";
|
|
||||||
resultsSummary.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
|
|
||||||
|
|
||||||
resultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderResults() {
|
|
||||||
var maxItems = 42;
|
|
||||||
var numItems = 0;
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
var project = projects[i];
|
|
||||||
if (project.resultsIndex < project.resultsToRender.length) {
|
|
||||||
rendering = true;
|
|
||||||
while (numItems < maxItems && project.resultsIndex < project.resultsToRender.length) {
|
|
||||||
var query = project.resultsToRender[project.resultsIndex].query;
|
|
||||||
var episode = project.resultsToRender[project.resultsIndex].episode;
|
|
||||||
var matches = project.resultsToRender[project.resultsIndex].matches;
|
|
||||||
if (resultsMarkerIndex == -1) {
|
|
||||||
if(project.resultsIndex == 0 || project.resultsToRender[project.resultsIndex - 1].episode.playerURLPrefix != episode.playerURLPrefix)
|
|
||||||
{
|
|
||||||
projectContainer = projectContainerPrototype.cloneNode(true);
|
|
||||||
for(var i = 0; i < projects.length; ++i)
|
|
||||||
{
|
|
||||||
if(projects[i].playerURLPrefix === episode.playerURLPrefix)
|
|
||||||
{
|
|
||||||
projectContainer.setAttribute("data-baseURL", projects[i].baseURL);
|
|
||||||
projectContainer.setAttribute("data-playerLocation", projects[i].playerLocation);
|
|
||||||
if(projects[i].filteredOut)
|
|
||||||
{
|
|
||||||
projectContainer.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultsContainer.appendChild(projectContainer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
projectContainer = resultsContainer.lastElementChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var dayContainer = episode.dayContainerPrototype.cloneNode(true);
|
|
||||||
var dayName = dayContainer.children[0];
|
|
||||||
markerList = dayContainer.children[1];
|
|
||||||
dayName.textContent = episode.day + ": " + episode.title;
|
|
||||||
projectContainer.appendChild(dayContainer);
|
|
||||||
resultsMarkerIndex = 0;
|
|
||||||
numItems++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (numItems < maxItems && resultsMarkerIndex < matches.length) {
|
|
||||||
var match = matches[resultsMarkerIndex];
|
|
||||||
var marker = episode.markerPrototype.cloneNode(true);
|
|
||||||
marker.setAttribute("href", episode.playerURLPrefix + episode.filename.replace(/"/g, "") + "/#" + match.totalTime);
|
|
||||||
query.lastIndex = 0;
|
|
||||||
var cursor = 0;
|
|
||||||
var text = match.text;
|
|
||||||
var result = null;
|
|
||||||
marker.appendChild(document.createTextNode(match.prettyTime + " "));
|
|
||||||
while (result = query.exec(text)) {
|
|
||||||
if (result.index > cursor) {
|
|
||||||
marker.appendChild(document.createTextNode(text.slice(cursor, result.index)));
|
|
||||||
}
|
|
||||||
var highlightEl = highlightPrototype.cloneNode();
|
|
||||||
highlightEl.textContent = result[0];
|
|
||||||
marker.appendChild(highlightEl);
|
|
||||||
cursor = result.index + result[0].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor < text.length) {
|
|
||||||
marker.appendChild(document.createTextNode(text.slice(cursor, text.length)));
|
|
||||||
}
|
|
||||||
markerList.appendChild(marker);
|
|
||||||
numItems++;
|
|
||||||
resultsMarkerIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultsMarkerIndex == matches.length) {
|
|
||||||
resultsMarkerIndex = -1;
|
|
||||||
project.resultsIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
renderHandle = setTimeout(renderResults, 0);
|
|
||||||
} else {
|
|
||||||
rendering = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function IsVisible(el) {
|
|
||||||
var xPos = 0;
|
|
||||||
var yPos = 0;
|
|
||||||
var Height = parseInt(getComputedStyle(el).height);
|
|
||||||
|
|
||||||
while (el) {
|
|
||||||
if (el.tagName == "BODY") {
|
|
||||||
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
|
|
||||||
var yScroll = el.scrollTop || document.documentElement.scrollTop;
|
|
||||||
|
|
||||||
xPos += (el.offsetLeft - xScroll + el.clientLeft)
|
|
||||||
yPos += (el.offsetTop - yScroll + el.clientTop)
|
|
||||||
} else {
|
|
||||||
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
|
|
||||||
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
el = el.offsetParent;
|
|
||||||
}
|
|
||||||
return ((xPos > 0 && xPos < window.innerWidth) && (yPos > 0 && yPos + Height < window.innerHeight));
|
|
||||||
}
|
|
||||||
|
|
||||||
var queryEl = document.getElementById("query");
|
|
||||||
if(document.hasFocus() && IsVisible(queryEl)) { queryEl.focus(); }
|
|
||||||
queryEl.addEventListener("input", function(ev) {
|
|
||||||
history.replaceState(null, null, "#" + encodeURIComponent(queryEl.value));
|
|
||||||
runSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
runSearch();
|
|
||||||
|
|
||||||
// Testing
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
document.body.style.overflowY = "scroll";
|
||||||
|
CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
||||||
|
|
||||||
|
// Element Selection
|
||||||
|
//
|
||||||
|
Nav.Nexus = document.getElementById("cineraIndex");
|
||||||
|
Nav.Controls.Header = document.getElementById("cineraIndexControl");
|
||||||
|
Nav.Controls.Sort = Nav.Controls.Header.querySelector(".cineraMenuItem.sort");
|
||||||
|
Nav.Controls.View = Nav.Controls.Header.querySelector(".cineraMenuItem.view");
|
||||||
|
Nav.Controls.Anim = Nav.Controls.Header.querySelector(".cineraMenuItem.anim");
|
||||||
|
Nav.Controls.Save = Nav.Controls.Header.querySelector(".cineraMenuItem.save");
|
||||||
|
Nav.Controls.Help = Nav.Nexus.querySelector(".cineraHelp");
|
||||||
|
Nav.Controls.HelpDocumentation = Nav.Controls.Help.querySelector(".help_container");
|
||||||
|
Nav.GridContainer = Nav.Nexus.querySelector(".cineraIndexGridContainer");
|
||||||
|
Nav.Controls.GridTraversal.Container = Nav.GridContainer.querySelector(".cineraTraversalContainer");
|
||||||
|
Nav.Controls.GridTraversal.Header = Nav.GridContainer.querySelector(".cineraTraversal");
|
||||||
|
Nav.Controls.GridTraversal.Ascend = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.ascension");
|
||||||
|
Nav.Controls.GridTraversal.Prev = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.prev");
|
||||||
|
Nav.Controls.GridTraversal.Next = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.next");
|
||||||
|
|
||||||
|
Search.QueryElement = document.getElementById("query");
|
||||||
|
Search.ResultsSummary = document.getElementById("cineraResultsSummary");
|
||||||
|
Search.ResultsContainer = document.getElementById("cineraResults");
|
||||||
|
Search.IndexContainer = document.getElementById("cineraIndexList");
|
||||||
|
Search.ProjectsContainer = Search.IndexContainer.querySelectorAll(".cineraIndexProject");
|
||||||
|
//
|
||||||
|
///
|
||||||
|
|
||||||
|
// NOTE(matt): Initialisation
|
||||||
|
//
|
||||||
|
if(CineraProps.IsMobile)
|
||||||
|
{
|
||||||
|
Nav.Nexus.classList.add("mobile");
|
||||||
|
}
|
||||||
|
InitTraversalStack();
|
||||||
|
InitNexus();
|
||||||
|
InitHelpKeys(Nav.Controls.HelpDocumentation);
|
||||||
|
Nav.GridSize = ComputeOptimalGridSize();
|
||||||
|
SetHelpKeyAvailability(Nav.GridSize);
|
||||||
|
InitButtons(); // NOTE(matt): Also does "keydown" listeners, needed before UpdateButtons()
|
||||||
|
UpdateButtons();
|
||||||
|
|
||||||
|
InitQuery(Search.QueryElement);
|
||||||
|
InitPrototypes(Search.ResultsContainer);
|
||||||
|
prepareProjects();
|
||||||
|
|
||||||
|
SyncNavState();
|
||||||
|
FlipClear("scroll");
|
||||||
|
//
|
||||||
|
////
|
||||||
|
|
||||||
|
// NOTE(matt): Listeners
|
||||||
|
//
|
||||||
|
BindControlKeys();
|
||||||
|
BindGridKeys(Nav.GridSize);
|
||||||
|
BindControls();
|
||||||
|
InitResizeEventListener();
|
||||||
|
InitOrientationChangeListener();
|
||||||
|
InitScrollEventListener(Nav.GridContainer, CineraProps.IsMobile, Nav.Controls.Header);
|
||||||
|
//
|
||||||
|
////
|
||||||
|
|
||||||
|
// NOTE(matt): On-load Execution
|
||||||
|
//
|
||||||
|
runSearch();
|
||||||
|
//
|
||||||
|
////
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
/utils/hmmldump
|
||||||
|
/utils/fuzz/fuzz.gcno
|
||||||
|
/utils/fuzz/fuzz.gcda
|
||||||
|
/utils/fuzz/output
|
||||||
|
/utils/fuzz/fuzz
|
|
@ -0,0 +1,6 @@
|
||||||
|
This is a single-header library for parsing the HMML Format.
|
||||||
|
See https://git.handmade.network/Annotation-Pushers/Annotation-System/-/wikis/hmmlspec
|
||||||
|
|
||||||
|
To use:
|
||||||
|
1. #include "hmmlib.h" in any files that use the functions / data structures.
|
||||||
|
2. in **one** of your .c files, #define HMMLIB_IMPLEMENTATION before including it.
|
|
@ -0,0 +1,859 @@
|
||||||
|
#ifndef HMMLIB_H_
|
||||||
|
#define HMMLIB_H_
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// Data structures
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
char* role;
|
||||||
|
} HMML_Credit;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* key;
|
||||||
|
char* value;
|
||||||
|
} HMML_VideoCustomMetaData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* stream_platform;
|
||||||
|
char* project;
|
||||||
|
char* title;
|
||||||
|
char* vod_platform;
|
||||||
|
char* id;
|
||||||
|
char* output;
|
||||||
|
char* template;
|
||||||
|
char* medium;
|
||||||
|
char* number;
|
||||||
|
char* cc_lang;
|
||||||
|
|
||||||
|
HMML_Credit* credits;
|
||||||
|
size_t credit_count;
|
||||||
|
|
||||||
|
HMML_Credit* uncredits;
|
||||||
|
size_t uncredit_count;
|
||||||
|
|
||||||
|
HMML_VideoCustomMetaData* custom;
|
||||||
|
size_t custom_count;
|
||||||
|
|
||||||
|
} HMML_VideoMetaData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* site;
|
||||||
|
char* page;
|
||||||
|
char* url;
|
||||||
|
char* title;
|
||||||
|
char* article;
|
||||||
|
char* author;
|
||||||
|
char* editor;
|
||||||
|
char* publisher;
|
||||||
|
char* isbn;
|
||||||
|
int offset;
|
||||||
|
} HMML_Reference;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HMML_CATEGORY,
|
||||||
|
HMML_MEMBER,
|
||||||
|
HMML_PROJECT,
|
||||||
|
|
||||||
|
HMML_MARKER_COUNT,
|
||||||
|
} HMML_MarkerType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HMML_MarkerType type;
|
||||||
|
char* marker;
|
||||||
|
char* parameter;
|
||||||
|
char* episode;
|
||||||
|
int offset;
|
||||||
|
} HMML_Marker;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
_Bool present;
|
||||||
|
int id;
|
||||||
|
char* author;
|
||||||
|
} HMML_Quote;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int line;
|
||||||
|
|
||||||
|
int h, m, s, ms;
|
||||||
|
|
||||||
|
char* text;
|
||||||
|
char* author;
|
||||||
|
|
||||||
|
HMML_Reference* references;
|
||||||
|
size_t reference_count;
|
||||||
|
|
||||||
|
HMML_Marker* markers;
|
||||||
|
size_t marker_count;
|
||||||
|
|
||||||
|
HMML_Quote quote;
|
||||||
|
} HMML_Timestamp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
char* message;
|
||||||
|
} HMML_Error;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
_Bool well_formed;
|
||||||
|
HMML_VideoMetaData metadata;
|
||||||
|
HMML_Timestamp* timestamps;
|
||||||
|
size_t timestamp_count;
|
||||||
|
HMML_Error error;
|
||||||
|
void* free_list; // implementation detail
|
||||||
|
} HMML_Output;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
HMML_Output hmml_parse (const char* string);
|
||||||
|
void hmml_free (HMML_Output* output);
|
||||||
|
|
||||||
|
// Version
|
||||||
|
|
||||||
|
extern const struct HMML_Version {
|
||||||
|
int Major, Minor, Patch;
|
||||||
|
} hmml_version;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HMMLIB_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define HSTX(x) x, sizeof(x)-1
|
||||||
|
#define HSTR(x) (const struct _hmml_str){ HSTX(x) }
|
||||||
|
|
||||||
|
#ifndef MALLOC
|
||||||
|
#define MALLOC malloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef REALLOC
|
||||||
|
#define REALLOC realloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef countof
|
||||||
|
#define countof(x) (sizeof(x)/sizeof(*x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _hmml_debug(...)
|
||||||
|
//#define _hmml_debug printf
|
||||||
|
|
||||||
|
struct _hmml_parser {
|
||||||
|
HMML_Output out;
|
||||||
|
const char* mem;
|
||||||
|
const char* cursor;
|
||||||
|
jmp_buf err_buf;
|
||||||
|
uintptr_t* free_list;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _hmml_str {
|
||||||
|
const char* ptr;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
// memory management boilerplate stuff
|
||||||
|
|
||||||
|
static void* _hmml_store_ptr(struct _hmml_parser* p, void* input)
|
||||||
|
{
|
||||||
|
uintptr_t* ptr;
|
||||||
|
if(p->free_list) {
|
||||||
|
ptr = p->free_list;
|
||||||
|
if(ptr[1] + 1 == ptr[0]) {
|
||||||
|
size_t n = ptr[0] << 1;
|
||||||
|
ptr = REALLOC(ptr, n * sizeof(uintptr_t));
|
||||||
|
ptr[0] = n;
|
||||||
|
}
|
||||||
|
ptr[ptr[1]] = (uintptr_t)input;
|
||||||
|
ptr[1]++;
|
||||||
|
} else {
|
||||||
|
ptr = MALLOC(8 * sizeof(uintptr_t));
|
||||||
|
ptr[0] = 8;
|
||||||
|
ptr[1] = 3;
|
||||||
|
ptr[2] = (uintptr_t)input;
|
||||||
|
}
|
||||||
|
p->free_list = ptr;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* _hmml_persist_str(struct _hmml_parser* p, const struct _hmml_str str)
|
||||||
|
{
|
||||||
|
char* mem = MALLOC(str.len+1);
|
||||||
|
memcpy(mem, str.ptr, str.len);
|
||||||
|
mem[str.len] = '\0';
|
||||||
|
return _hmml_store_ptr(p, mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_persist_array_fn(struct _hmml_parser* p, void** out, size_t* out_count, void* in, size_t in_size)
|
||||||
|
{
|
||||||
|
void* base;
|
||||||
|
if(!*out) {
|
||||||
|
base = MALLOC(in_size + sizeof(size_t));
|
||||||
|
_hmml_store_ptr(p, base);
|
||||||
|
*(size_t*)base = p->free_list[1]-1;
|
||||||
|
} else {
|
||||||
|
base = (char*)(*out) - sizeof(size_t);
|
||||||
|
base = REALLOC(base, (*out_count + 1) * in_size + sizeof(size_t));
|
||||||
|
size_t free_list_off = *(size_t*)base;
|
||||||
|
p->free_list[free_list_off] = (intptr_t)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = (char*)base + sizeof(size_t);
|
||||||
|
memcpy((char*)*out + (*out_count * in_size), in, in_size);
|
||||||
|
++(*out_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _hmml_persist_array(p, out, out_count, in) \
|
||||||
|
_hmml_persist_array_fn((p), (void**)(out), (out_count), &(in), sizeof(in))
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
|
||||||
|
#define _hmml_err(p, fmt, ...) \
|
||||||
|
_hmml_err_fn((p), fmt "\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
__attribute__((noreturn))
|
||||||
|
static void _hmml_err_fn(struct _hmml_parser* p, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static char error_buf[4096];
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
int n = vsnprintf(error_buf, sizeof(error_buf), fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
int line = 1, col = 1;
|
||||||
|
for(const char* ptr = p->mem; ptr != p->cursor; ++ptr) {
|
||||||
|
if(*ptr == '\n') {
|
||||||
|
++line;
|
||||||
|
col = 1;
|
||||||
|
} else {
|
||||||
|
++col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p->out.error.message = _hmml_persist_str(p, (struct _hmml_str){ error_buf, n });
|
||||||
|
p->out.error.line = line;
|
||||||
|
p->out.error.col = col;
|
||||||
|
|
||||||
|
longjmp(p->err_buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual parsing stuff
|
||||||
|
|
||||||
|
static void _hmml_skip_ws(struct _hmml_parser* p)
|
||||||
|
{
|
||||||
|
for(;;) {
|
||||||
|
uint8_t c = *p->cursor;
|
||||||
|
if(c && c <= ' ') {
|
||||||
|
if(c == '\n') {
|
||||||
|
++p->line;
|
||||||
|
}
|
||||||
|
++p->cursor;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Bool _hmml_str_eq(struct _hmml_str a, struct _hmml_str b)
|
||||||
|
{
|
||||||
|
return a.len == b.len && memcmp(a.ptr, b.ptr, a.len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Bool _hmml_unesc(char in, char* out)
|
||||||
|
{
|
||||||
|
if(strchr("[]:@~\\\"", in)) {
|
||||||
|
*out = in;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* _hmml_read_attr(struct _hmml_parser* p, char* mem, size_t mem_size, _Bool break_on_punct)
|
||||||
|
{
|
||||||
|
const char* src = p->cursor;
|
||||||
|
char* dst = mem;
|
||||||
|
|
||||||
|
if(*src == '"') {
|
||||||
|
++src;
|
||||||
|
while(*src && *src != '"' && (size_t)(src - p->cursor) < mem_size) {
|
||||||
|
char converted;
|
||||||
|
if(*src == '\\' && _hmml_unesc(src[1], &converted)) {
|
||||||
|
*dst++ = converted;
|
||||||
|
src += 2;
|
||||||
|
} else {
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*src != '"') {
|
||||||
|
_hmml_err(p, "Partially quoted attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = '\0';
|
||||||
|
p->cursor = src+1;
|
||||||
|
} else {
|
||||||
|
const char* breaks = break_on_punct
|
||||||
|
? " ]\r\n\t:,'-.#=[\\?!…()\"%"
|
||||||
|
: " ]\r\n\t"
|
||||||
|
;
|
||||||
|
|
||||||
|
size_t n = strcspn(src, breaks);
|
||||||
|
if(n >= mem_size) {
|
||||||
|
_hmml_err(p, "Attribute [%.10s...] too long", p->cursor);
|
||||||
|
}
|
||||||
|
memcpy(dst, src, n);
|
||||||
|
dst += n;
|
||||||
|
*dst = '\0';
|
||||||
|
p->cursor += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_read_kv(struct _hmml_parser* p, struct _hmml_str* key, struct _hmml_str* val)
|
||||||
|
{
|
||||||
|
static char key_memory[64];
|
||||||
|
static char val_memory[1024];
|
||||||
|
|
||||||
|
size_t key_len = strcspn(p->cursor, " \r\n\t=");
|
||||||
|
if(key_len >= sizeof(key_memory)) {
|
||||||
|
_hmml_err(p, "Attribute key [%.10s...] too long", p->cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key_memory, p->cursor, key_len);
|
||||||
|
key_memory[key_len] = '\0';
|
||||||
|
p->cursor += key_len;
|
||||||
|
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
|
||||||
|
if(*p->cursor != '=') {
|
||||||
|
_hmml_err(p, "Expected '=', got [%.3s]", p->cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
++p->cursor;
|
||||||
|
|
||||||
|
char* end = _hmml_read_attr(p, val_memory, sizeof(val_memory), 0);
|
||||||
|
|
||||||
|
_hmml_debug("read kv [%s] = [%s]\n", key_memory, val_memory);
|
||||||
|
|
||||||
|
key->ptr = key_memory;
|
||||||
|
key->len = key_len;
|
||||||
|
|
||||||
|
val->ptr = val_memory;
|
||||||
|
val->len = end - val_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMML_Marker _hmml_parse_marker(struct _hmml_parser* p)
|
||||||
|
{
|
||||||
|
static char marker_mem[4096];
|
||||||
|
|
||||||
|
// the extended markers are inside [ ] and can contain parameters
|
||||||
|
_Bool extended = *p->cursor == '[';
|
||||||
|
if(extended) {
|
||||||
|
++p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMML_Marker marker = {
|
||||||
|
.offset = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
char c = *p->cursor;
|
||||||
|
if(c == '~') {
|
||||||
|
marker.type = HMML_PROJECT;
|
||||||
|
} else if(c == '@') {
|
||||||
|
marker.type = HMML_MEMBER;
|
||||||
|
} else if(c == ':') {
|
||||||
|
marker.type = HMML_CATEGORY;
|
||||||
|
} else {
|
||||||
|
_hmml_err(p, "Unknown marker type");
|
||||||
|
}
|
||||||
|
|
||||||
|
++p->cursor;
|
||||||
|
|
||||||
|
char* end = _hmml_read_attr(p, marker_mem, sizeof(marker_mem), !extended);
|
||||||
|
marker.marker = _hmml_persist_str(p, (struct _hmml_str){ marker_mem, end - marker_mem });
|
||||||
|
|
||||||
|
if(extended) {
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
|
||||||
|
if(*p->cursor == '#') {
|
||||||
|
++p->cursor;
|
||||||
|
size_t n = strcspn(p->cursor, " ");
|
||||||
|
marker.episode = _hmml_persist_str(p, (struct _hmml_str){ p->cursor, n });
|
||||||
|
p->cursor += n + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*p->cursor != ']') {
|
||||||
|
const char* end = p->cursor;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
if(!*end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char converted;
|
||||||
|
if(*end == '\\' && _hmml_unesc(end[1], &converted)) {
|
||||||
|
end += 2;
|
||||||
|
} else if(*end == ']'){
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marker.parameter = _hmml_persist_str(p, (struct _hmml_str){ p->cursor, end - p->cursor });
|
||||||
|
p->cursor = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*p->cursor != ']') {
|
||||||
|
_hmml_err(p, "Expected ']'");
|
||||||
|
}
|
||||||
|
++p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMML_Reference _hmml_parse_ref(struct _hmml_parser* p)
|
||||||
|
{
|
||||||
|
HMML_Reference ref = {
|
||||||
|
.offset = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct str_attr {
|
||||||
|
struct _hmml_str str;
|
||||||
|
char** dest;
|
||||||
|
} str_attrs[] = {
|
||||||
|
{ HSTR("site") , &ref.site },
|
||||||
|
{ HSTR("page") , &ref.page },
|
||||||
|
{ HSTR("url") , &ref.url },
|
||||||
|
{ HSTR("title") , &ref.title },
|
||||||
|
{ HSTR("article") , &ref.article },
|
||||||
|
{ HSTR("author") , &ref.author },
|
||||||
|
{ HSTR("editor") , &ref.editor },
|
||||||
|
{ HSTR("publisher"), &ref.publisher },
|
||||||
|
{ HSTR("isbn") , &ref.isbn },
|
||||||
|
};
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
next_attr:
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
|
||||||
|
if(*p->cursor == ']') {
|
||||||
|
++p->cursor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _hmml_str key, value;
|
||||||
|
_hmml_read_kv(p, &key, &value);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < countof(str_attrs); ++i) {
|
||||||
|
struct str_attr* s = str_attrs + i;
|
||||||
|
if(_hmml_str_eq(key, s->str)) {
|
||||||
|
*s->dest = _hmml_persist_str(p, value);
|
||||||
|
goto next_attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_hmml_err(p, "Unknown reference attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
|
{
|
||||||
|
unsigned int h = 0, m = 0, s = 0, ms = 0;
|
||||||
|
int offset = 0;
|
||||||
|
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
|
||||||
|
|
||||||
|
if(count < 2) {
|
||||||
|
_hmml_err(p, "Unable to parse timecode");
|
||||||
|
}
|
||||||
|
|
||||||
|
p->cursor += offset;
|
||||||
|
char c = *p->cursor;
|
||||||
|
|
||||||
|
if(c == ':') {
|
||||||
|
unsigned int tmp;
|
||||||
|
offset = 0;
|
||||||
|
if(sscanf(p->cursor, ":%u%n", &tmp, &offset) != 1 || offset == 0) {
|
||||||
|
_hmml_err(p, "Unable to parse 3-part timecode");
|
||||||
|
}
|
||||||
|
|
||||||
|
h = m;
|
||||||
|
m = s;
|
||||||
|
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");
|
||||||
|
} else {
|
||||||
|
++p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ms >= 1000) {
|
||||||
|
_hmml_err(p, "Milliseconds cannot exceed 999");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s >= 60) {
|
||||||
|
_hmml_err(p, "Seconds cannot exceed 59");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m >= 60) {
|
||||||
|
_hmml_err(p, "Minutes cannot exceed 59");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
HMML_Marker m = _hmml_parse_marker(p);
|
||||||
|
m.offset = (*out) - text_mem;
|
||||||
|
_hmml_persist_array(p, &ts->markers, &ts->marker_count, m);
|
||||||
|
|
||||||
|
const char* marker_text = m.parameter
|
||||||
|
? m.parameter
|
||||||
|
: m.marker
|
||||||
|
;
|
||||||
|
|
||||||
|
size_t text_len = strlen(marker_text);
|
||||||
|
if((*out) + text_len > text_mem + text_mem_size) {\
|
||||||
|
_hmml_err(p, "Not enough text memory");\
|
||||||
|
}
|
||||||
|
memcpy(*out, marker_text, text_len);
|
||||||
|
*out += text_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t _hmml_parse_text(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
|
{
|
||||||
|
static char text_mem[4096];
|
||||||
|
char* out = text_mem;
|
||||||
|
|
||||||
|
memset(text_mem, 0, sizeof(text_mem));
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
size_t n = strcspn(p->cursor, "\\\n\r[]:@~");
|
||||||
|
char c = p->cursor[n];
|
||||||
|
|
||||||
|
if(out + n > text_mem + sizeof(text_mem)) {\
|
||||||
|
_hmml_err(p, "Not enough text memory");\
|
||||||
|
}
|
||||||
|
memcpy(out, p->cursor, n);
|
||||||
|
|
||||||
|
p->cursor += n;
|
||||||
|
out += n;
|
||||||
|
|
||||||
|
if(c == '\0') {
|
||||||
|
_hmml_err(p, "Unexpected EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(c == ']') {
|
||||||
|
++p->cursor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(c == '\\') {
|
||||||
|
char converted;
|
||||||
|
if(_hmml_unesc(p->cursor[1], &converted)) {
|
||||||
|
*out++ = converted;
|
||||||
|
p->cursor += 2;
|
||||||
|
} else {
|
||||||
|
*out++ = '\\';
|
||||||
|
p->cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(c == '\n' || c == '\r') {
|
||||||
|
++p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(c == '[') {
|
||||||
|
if(strncmp(p->cursor + 1, "ref", 3) == 0) {
|
||||||
|
p->cursor += 4;
|
||||||
|
HMML_Reference ref = _hmml_parse_ref(p);
|
||||||
|
ref.offset = out - text_mem;
|
||||||
|
_hmml_persist_array(p, &ts->references, &ts->reference_count, ref);
|
||||||
|
} else {
|
||||||
|
_hmml_store_marker(p, ts, &out, text_mem, sizeof(text_mem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is a @ ~ or : marker without parameters
|
||||||
|
else {
|
||||||
|
// if next char is a space, or prev char is not a space*, then it can't be a marker
|
||||||
|
// * unless it's the first char
|
||||||
|
if(strchr(" \t\r\n", p->cursor[1]) || !(out == text_mem || strchr(" \t\r\n", p->cursor[-1]))) {
|
||||||
|
*out++ = c;
|
||||||
|
++p->cursor;
|
||||||
|
} else {
|
||||||
|
_hmml_store_marker(p, ts, &out, text_mem, sizeof(text_mem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((size_t)(out - text_mem) >= sizeof(text_mem)) {
|
||||||
|
_hmml_err(p, "Not enough text memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing whitespace
|
||||||
|
while(out > text_mem && (uint8_t)(out[-1]) <= ' ') {
|
||||||
|
out[-1] = '\0';
|
||||||
|
--out;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t text_size = out - text_mem;
|
||||||
|
ts->text = _hmml_persist_str(p, (struct _hmml_str){ text_mem, text_size });
|
||||||
|
|
||||||
|
return text_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_parse_quote(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
|
{
|
||||||
|
char member[256];
|
||||||
|
int id;
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
if(sscanf(p->cursor, "[quote %255s %d]%n", member, &id, &off) == 2 && off) {
|
||||||
|
ts->quote.present = 1;
|
||||||
|
ts->quote.id = id;
|
||||||
|
ts->quote.author = _hmml_persist_str(p, (struct _hmml_str){ member, strlen(member) });
|
||||||
|
} else if(sscanf(p->cursor, "[quote %d]%n", &id, &off) == 1 && off) {
|
||||||
|
ts->quote.present = 1;
|
||||||
|
ts->quote.id = id;
|
||||||
|
} else {
|
||||||
|
_hmml_err(p, "Unable to parse quote");
|
||||||
|
}
|
||||||
|
|
||||||
|
p->cursor += off;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_parse_timestamps(struct _hmml_parser* p)
|
||||||
|
{
|
||||||
|
for(;;) {
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
|
||||||
|
if(*p->cursor == '\0') {
|
||||||
|
_hmml_err(p, "Unexpected EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strncmp(p->cursor, "[/video]", 8) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMML_Timestamp ts = {
|
||||||
|
.line = p->line
|
||||||
|
};
|
||||||
|
|
||||||
|
_hmml_parse_timecode(p, &ts);
|
||||||
|
|
||||||
|
if(*p->cursor != '[') {
|
||||||
|
_hmml_err(p, "Expected '['");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(p->cursor[1] == '@') {
|
||||||
|
HMML_Marker m = _hmml_parse_marker(p);
|
||||||
|
ts.author = m.marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p->cursor;
|
||||||
|
|
||||||
|
int text_len = _hmml_parse_text(p, &ts);
|
||||||
|
|
||||||
|
if(p->cursor[0] == '[' && p->cursor[1] == ':') {
|
||||||
|
++p->cursor;
|
||||||
|
do {
|
||||||
|
HMML_Marker m = _hmml_parse_marker(p);
|
||||||
|
_hmml_persist_array(p, &ts.markers, &ts.marker_count, m);
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
if(*p->cursor != ':' && *p->cursor != ']') {
|
||||||
|
_hmml_err(p, "Unterminated post-text category node");
|
||||||
|
}
|
||||||
|
} while(*p->cursor == ':');
|
||||||
|
++p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(p->cursor[0] == '[' && p->cursor[1] == 'q') {
|
||||||
|
_hmml_parse_quote(p, &ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert all markers to lowercase, fix any out of range offsets
|
||||||
|
for(size_t i = 0; i < ts.marker_count; ++i) {
|
||||||
|
HMML_Marker* m = ts.markers + i;
|
||||||
|
for(char* c = m->marker; *c; ++c) {
|
||||||
|
if(*c >= 'A' && *c <= 'Z') {
|
||||||
|
*c = (*c - ('A' - 'a'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(m->offset > text_len) {
|
||||||
|
m->offset = text_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ts.reference_count; ++i) {
|
||||||
|
HMML_Reference* ref = ts.references + i;
|
||||||
|
if(ref->offset > text_len) {
|
||||||
|
ref->offset = text_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_hmml_persist_array(p, &p->out.timestamps, &p->out.timestamp_count, ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMML_Credit _hmml_parse_credit(struct _hmml_parser* p, struct _hmml_str value)
|
||||||
|
{
|
||||||
|
HMML_Credit credit = {};
|
||||||
|
char* colon = strchr(value.ptr, ':');
|
||||||
|
if(colon) {
|
||||||
|
*colon = '\0';
|
||||||
|
credit.name = _hmml_persist_str(p, (struct _hmml_str){ value.ptr, colon - value.ptr });
|
||||||
|
credit.role = _hmml_persist_str(p, (struct _hmml_str){ colon+1, value.len - ((colon+1) - value.ptr) });
|
||||||
|
} else {
|
||||||
|
credit.name = _hmml_persist_str(p, value);
|
||||||
|
}
|
||||||
|
return credit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hmml_parse_video(struct _hmml_parser* p)
|
||||||
|
{
|
||||||
|
struct str_attr {
|
||||||
|
struct _hmml_str str;
|
||||||
|
char** dest;
|
||||||
|
} str_attrs[] = {
|
||||||
|
{ HSTR("stream_platform"), &p->out.metadata.stream_platform },
|
||||||
|
{ HSTR("project") , &p->out.metadata.project },
|
||||||
|
{ HSTR("title") , &p->out.metadata.title },
|
||||||
|
{ HSTR("vod_platform") , &p->out.metadata.vod_platform },
|
||||||
|
{ 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(;;) {
|
||||||
|
next_attr:
|
||||||
|
_hmml_skip_ws(p);
|
||||||
|
|
||||||
|
if(*p->cursor == ']') {
|
||||||
|
++p->cursor;
|
||||||
|
_hmml_parse_timestamps(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _hmml_str key, value;
|
||||||
|
_hmml_read_kv(p, &key, &value);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < countof(str_attrs); ++i) {
|
||||||
|
struct str_attr* s = str_attrs + i;
|
||||||
|
if(_hmml_str_eq(key, s->str)) {
|
||||||
|
*s->dest = _hmml_persist_str(p, value);
|
||||||
|
goto next_attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_hmml_str_eq(key, HSTR("credit"))) {
|
||||||
|
HMML_Credit credit = _hmml_parse_credit(p, value);
|
||||||
|
_hmml_persist_array(p, &p->out.metadata.credits, &p->out.metadata.credit_count, credit);
|
||||||
|
goto next_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_hmml_str_eq(key, HSTR("uncredit"))) {
|
||||||
|
HMML_Credit uncredit = _hmml_parse_credit(p, value);
|
||||||
|
_hmml_persist_array(p, &p->out.metadata.uncredits, &p->out.metadata.uncredit_count, uncredit);
|
||||||
|
goto next_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMML_VideoCustomMetaData custom = {
|
||||||
|
.key = _hmml_persist_str(p, key),
|
||||||
|
.value = _hmml_persist_str(p, value),
|
||||||
|
};
|
||||||
|
|
||||||
|
_hmml_persist_array(p, &p->out.metadata.custom, &p->out.metadata.custom_count, custom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HMML_Output hmml_parse(const char* string)
|
||||||
|
{
|
||||||
|
struct _hmml_parser p = {
|
||||||
|
.mem = string,
|
||||||
|
.cursor = string,
|
||||||
|
.line = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(setjmp(p.err_buf) == 1) {
|
||||||
|
// if it returns 1, an error happened
|
||||||
|
p.out.free_list = p.free_list;
|
||||||
|
return p.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct _hmml_str prefix = HSTR("[video");
|
||||||
|
if(strncmp(p.cursor, prefix.ptr, prefix.len)) {
|
||||||
|
_hmml_err(&p, "Missing initial video tag.");
|
||||||
|
} else {
|
||||||
|
p.cursor += prefix.len;
|
||||||
|
_hmml_parse_video(&p);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.out.free_list = p.free_list;
|
||||||
|
p.out.well_formed = 1;
|
||||||
|
return p.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmml_free(HMML_Output* out)
|
||||||
|
{
|
||||||
|
if(!out->free_list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uintptr_t i = 2; i < ((uintptr_t*)out->free_list)[1]; ++i) {
|
||||||
|
free(((void**)out->free_list)[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(out->free_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct HMML_Version hmml_version = {
|
||||||
|
2, 0, 15
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef HSTX
|
||||||
|
#undef HSTR
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,2 @@
|
||||||
|
hmmldump: dump.c ../hmmlib.h stb_sb.h
|
||||||
|
gcc -Wall -Wextra -I.. -g $< -o $@
|
|
@ -0,0 +1,241 @@
|
||||||
|
#define HMMLIB_IMPLEMENTATION
|
||||||
|
#include "hmmlib.h"
|
||||||
|
#include "stb_sb.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* text;
|
||||||
|
int* lines;
|
||||||
|
} Index;
|
||||||
|
|
||||||
|
static Index* index_find(Index* base, const char* text)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < sb_count(base); ++i){
|
||||||
|
if(strcmp(base[i].text, text) == 0){
|
||||||
|
return base + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmml_dump(HMML_Output* hmml, bool extra)
|
||||||
|
{
|
||||||
|
if(!hmml){
|
||||||
|
puts("(null)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!hmml->well_formed){
|
||||||
|
printf("Error:%d:%d %s\n", hmml->error.line, hmml->error.col, hmml->error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extra) {
|
||||||
|
puts("Metadata:");
|
||||||
|
static const char* meta[] = { "stream_platform", "project", "title", "vod_platform", "id", "output", "template", "medium" };
|
||||||
|
for(size_t i = 0; i < countof(meta); ++i) {
|
||||||
|
const char* value = ((char**)&hmml->metadata)[i];
|
||||||
|
printf(" %s = %s\n", meta[i], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(" Credits:");
|
||||||
|
for(size_t i = 0; i < hmml->metadata.credit_count; ++i) {
|
||||||
|
HMML_Credit* c = hmml->metadata.credits + i;
|
||||||
|
printf(" %s [%s]\n", c->name, c->role);
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(" Custom:");
|
||||||
|
for(size_t i = 0; i < hmml->metadata.custom_count; ++i) {
|
||||||
|
HMML_VideoCustomMetaData* m = hmml->metadata.custom + i;
|
||||||
|
printf(" %s = %s\n", m->key, m->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Annotations:");
|
||||||
|
for(size_t i = 0; i < hmml->annotation_count; ++i){
|
||||||
|
HMML_Annotation* a = hmml->annotations + i;
|
||||||
|
|
||||||
|
char time_buf[256];
|
||||||
|
char* tp = time_buf;
|
||||||
|
|
||||||
|
if(a->h) {
|
||||||
|
*tp++ = (a->h%10) + '0';
|
||||||
|
sprintf(tp, ":%02d:%02d", a->m, a->s);
|
||||||
|
} else {
|
||||||
|
sprintf(tp, " %2d:%02d", a->m, a->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\t%3d [%s] [%s]\n", a->line, time_buf, a->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Index* authors = NULL;
|
||||||
|
Index* markers[HMML_MARKER_COUNT] = {};
|
||||||
|
int max_text_len = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < hmml->annotation_count; ++i){
|
||||||
|
HMML_Annotation* a = hmml->annotations + i;
|
||||||
|
|
||||||
|
if(a->author){
|
||||||
|
int len = strlen(a->author);
|
||||||
|
if(len > max_text_len){
|
||||||
|
max_text_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index* idx;
|
||||||
|
if(!(idx = index_find(authors, a->author))){
|
||||||
|
Index x = { .text = a->author };
|
||||||
|
sb_push(authors, x);
|
||||||
|
idx = &sb_last(authors);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb_push(idx->lines, a->line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < hmml->annotation_count; ++i){
|
||||||
|
HMML_Annotation* a = hmml->annotations + i;
|
||||||
|
|
||||||
|
for(size_t j = 0; j < a->marker_count; ++j){
|
||||||
|
int type = a->markers[j].type;
|
||||||
|
char* text = a->markers[j].marker;
|
||||||
|
|
||||||
|
int len = strlen(text);
|
||||||
|
if(len > max_text_len){
|
||||||
|
max_text_len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index* idx;
|
||||||
|
if(!(idx = index_find(markers[type], text))){
|
||||||
|
Index x = { .text = text };
|
||||||
|
sb_push(markers[type], x);
|
||||||
|
idx = &sb_last(markers[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb_push(idx->lines, a->line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Authors:");
|
||||||
|
for(size_t i = 0; i < sb_count(authors); ++i){
|
||||||
|
printf("\t %*s: ", max_text_len, authors[i].text);
|
||||||
|
for(size_t j = 0; j < sb_count(authors[i].lines); ++j){
|
||||||
|
printf("%3d ", authors[i].lines[j]);
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char* m_tags[HMML_MARKER_COUNT] = { "Categories", "Members", "Projects" };
|
||||||
|
|
||||||
|
for(size_t i = 0; i < HMML_MARKER_COUNT; ++i){
|
||||||
|
printf("%s:\n", m_tags[i]);
|
||||||
|
for(size_t j = 0; j < sb_count(markers[i]); ++j){
|
||||||
|
printf("\t %*s: ", max_text_len, markers[i][j].text);
|
||||||
|
for(size_t k = 0; k < sb_count(markers[i][j].lines); ++k){
|
||||||
|
printf("%3d ", markers[i][j].lines[k]);
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* r_tags[] = { "Site", "Page", "URL", "Title", "Article", "Author", "Editor", "Publisher", "ISBN" };
|
||||||
|
puts("References:");
|
||||||
|
for(size_t i = 0; i < hmml->annotation_count; ++i){
|
||||||
|
HMML_Annotation* a = hmml->annotations + i;
|
||||||
|
for(size_t j = 0; j < a->reference_count; ++j){
|
||||||
|
printf("\t%3d ", a->line);
|
||||||
|
HMML_Reference* r = a->references + j;
|
||||||
|
for(size_t k = 0; k < countof(r_tags); ++k){
|
||||||
|
char* item = ((char**)r)[k];
|
||||||
|
if(item){
|
||||||
|
printf("[%s = %s] ", r_tags[k], item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Quotes:");
|
||||||
|
for(size_t i = 0; i < hmml->annotation_count; ++i){
|
||||||
|
HMML_Annotation* a = hmml->annotations + i;
|
||||||
|
if(a->quote.present){
|
||||||
|
if(a->quote.author){
|
||||||
|
printf("\t%3d [Quote #%d, by %s]", a->line, a->quote.id, a->quote.author);
|
||||||
|
} else {
|
||||||
|
printf("\t%3d [Quote #%d]", a->line, a->quote.id);
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sb_count(authors); ++i){
|
||||||
|
sb_free(authors[i].lines);
|
||||||
|
}
|
||||||
|
sb_free(authors);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < HMML_MARKER_COUNT; ++i){
|
||||||
|
for(size_t j = 0; j < sb_count(markers[i]); ++j){
|
||||||
|
sb_free(markers[i][j].lines);
|
||||||
|
}
|
||||||
|
sb_free(markers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char* argv0, FILE* out)
|
||||||
|
{
|
||||||
|
fprintf(out, "Usage: %s [-bx] [file]\n", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
bool dump_extra = false;
|
||||||
|
bool breakpoint = false;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while((opt = getopt(argc, argv, "bx")) != -1) {
|
||||||
|
if(opt == 'x') {
|
||||||
|
dump_extra = true;
|
||||||
|
} else if(opt == 'b') {
|
||||||
|
breakpoint = true;
|
||||||
|
} else {
|
||||||
|
usage(argv[0], stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(optind >= argc){
|
||||||
|
usage(argv[0], stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= (optind-1);
|
||||||
|
argv += (optind-1);
|
||||||
|
|
||||||
|
FILE* f = fopen(argv[1], "r");
|
||||||
|
if(!f) {
|
||||||
|
perror(argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long size = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
char* mem = malloc(size+1);
|
||||||
|
mem[size] = 0;
|
||||||
|
|
||||||
|
fread(mem, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
HMML_Output out = hmml_parse(mem);
|
||||||
|
free(mem);
|
||||||
|
|
||||||
|
if(breakpoint) {
|
||||||
|
asm("int3");
|
||||||
|
}
|
||||||
|
|
||||||
|
hmml_dump(&out, dump_extra);
|
||||||
|
|
||||||
|
hmml_free(&out);
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
fuzz: fuzz.c ../../hmmlib.h
|
||||||
|
afl-gcc -I../.. -g -D_GNU_SOURCE -fprofile-arcs -ftest-coverage $< -o $@
|
||||||
|
|
||||||
|
run: fuzz | output
|
||||||
|
afl-fuzz -i tests -o output ./$<
|
||||||
|
|
||||||
|
cov: fuzz | output
|
||||||
|
afl-cov -d output --coverage-cmd 'cat AFL_FILE | ./fuzz' --code-dir . --enable-branch-coverage --overwrite
|
||||||
|
|
||||||
|
output:
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf fuzz fuzz.gcno fuzz.gcda output
|
||||||
|
|
||||||
|
.PHONY: run cov clean
|
|
@ -0,0 +1,20 @@
|
||||||
|
#define HMMLIB_IMPLEMENTATION
|
||||||
|
#include "hmmlib.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
char* mem = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
while(fgets(buf, BUFSIZ, stdin)) {
|
||||||
|
size_t n = strlen(buf) + 1;
|
||||||
|
mem = realloc(mem, size + n);
|
||||||
|
memcpy(mem + size, buf, n);
|
||||||
|
size += n - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMML_Output out = hmml_parse(mem);
|
||||||
|
free(mem);
|
||||||
|
hmml_free(&out);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
[video member=nothings credit="inso:programmer" annotator=Miblo]
|
||||||
|
[2:01][Recap and update the TODO list]
|
||||||
|
[38:58][Continue implementing parse_tag()][:parsing :test]
|
||||||
|
[41:40][@miblo][Ohhh, right. Yeah, the \[video\] and \[/video\] tags are the only :ones that [:have a s d f] that open-close format. All the [~other] [@tags abc] are "single" tags]
|
||||||
|
[45:17][Enable parse_tag() to tokenise the \[video\] node][:parsing][quote bob 1]
|
||||||
|
[3:42:11][@miblo][@nothings2: That's fine, yeah! They are also in: [ref
|
||||||
|
site="GitLab: Annotation-Pushers / Annotation-Game"
|
||||||
|
page="projects/nothings/obbg"
|
||||||
|
url=http://git.handmadedev.org/Annotation-Pushers/Annotation-Game/tree/master/projects/nothings/obbg]]
|
||||||
|
[3:50:44][Take a break][quote 5]
|
||||||
|
[/video]
|
|
@ -0,0 +1,3 @@
|
||||||
|
[video credit=inso:programmer]
|
||||||
|
[2:01][Recap and :update the ~TODO @list]
|
||||||
|
[/video]
|
|
@ -0,0 +1,25 @@
|
||||||
|
[video member=nothings stream_platform=twitch stream_username=nothings2 project=obbg title="Open Block Building Game Development #32 (1/2)" vod_platform=youtube id=Vm-0ZUVMHHc annotator=Miblo]
|
||||||
|
[2:01][Recap and update the TODO list]
|
||||||
|
[10:08][Consult the example YouTube description file and the longest .hmml file][:test1 :test2 :abcd]
|
||||||
|
[38:58][Continue implementing parse_tag()][:parsing]
|
||||||
|
[41:40][@miblo][Ohhh, right. Yeah, the \[video\] and \[/video\] tags are the only ones that have that open-close format. All the other tags are "single" tags]
|
||||||
|
[41:51][Continue writing parse_tag() anyway][:parsing]
|
||||||
|
[43:10][@miblo][@nothings2: There isn't really, other than the rambling in: [ref
|
||||||
|
site="GitLab: Annotation-Pushers / Annotation-System / Issues"
|
||||||
|
page="Handmade Annotation Markup Language (previously MibloMarkup)"
|
||||||
|
url=http://git.handmadedev.org/Annotation-Pushers/Annotation-System/issues/2]]
|
||||||
|
[45:17][Enable parse_tag() to tokenise the \[video\] node][:parsing]
|
||||||
|
[1:51:32][Parse out the embedded \: and \@ tags][:parsing]
|
||||||
|
[1:57:50][:Run it to see if those two cases work and tweak the username conversion]
|
||||||
|
[3:15:14][@experior][Add a space after the pound? Or another character]
|
||||||
|
[3:16:40][Delete the \@ in the text node][:parsing]
|
||||||
|
[3:42:11][@miblo][@nothings2: That's fine, yeah! They are also in: [ref
|
||||||
|
site="GitLab: Annotation-Pushers / Annotation-Game"
|
||||||
|
page="projects/nothings/obbg"
|
||||||
|
url=http://git.handmadedev.org/Annotation-Pushers/Annotation-Game/tree/master/projects/nothings/obbg]]
|
||||||
|
[3:47:14][@insofaras][[ref
|
||||||
|
site="GitHub: nothings/obbg"
|
||||||
|
page="Pull Request #8: get it building and running on linux by insofaras"
|
||||||
|
url=https://github.com/nothings/obbg/pull/8/files#diff-90c561ba68b3be193f3378639ef489a0R1831]]
|
||||||
|
[3:50:44][Take a break]
|
||||||
|
[/video]
|
|
@ -0,0 +1,59 @@
|
||||||
|
// stb stretchy_buffer.h v1.02 nothings.org/stb
|
||||||
|
// with custom addtions sb_end, sb_pop, sb_erase
|
||||||
|
|
||||||
|
#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
#define STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
|
||||||
|
#define sb_free stb_sb_free
|
||||||
|
#define sb_push stb_sb_push
|
||||||
|
#define sb_count stb_sb_count
|
||||||
|
#define sb_add stb_sb_add
|
||||||
|
#define sb_last stb_sb_last
|
||||||
|
#define sb_end stb_sb_end
|
||||||
|
#define sb_pop stb_sb_pop
|
||||||
|
#define sb_erase stb_sb_erase
|
||||||
|
#define sb_each stb_sb_each
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),(a)=0,0 : 0)
|
||||||
|
#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||||
|
#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
|
||||||
|
#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||||
|
#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
|
||||||
|
#define stb_sb_end(a) ((a) ? (a) + stb__sbn(a) : 0)
|
||||||
|
#define stb_sb_pop(a) (--stb__sbn(a))
|
||||||
|
#define stb_sb_erase(a,i) ((a) ? memmove((a)+(i), (a)+(i)+1, sizeof(*(a))*((--stb__sbn(a))-(i))),0 : 0);
|
||||||
|
|
||||||
|
#define stb__sbraw(a) ((size_t *) (a) - 2)
|
||||||
|
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||||
|
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
|
||||||
|
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||||
|
#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a))))
|
||||||
|
|
||||||
|
#define stb_sb_each(n,h) for(typeof(h) n = h; n < sb_end(h); ++n)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static inline void * stb__sbgrowf(void *arr, int increment, int itemsize)
|
||||||
|
{
|
||||||
|
size_t inc_cur = arr ? stb__sbm(arr) + (stb__sbm(arr) >> 1) : 0;
|
||||||
|
size_t min_needed = stb_sb_count(arr) + increment;
|
||||||
|
size_t m = inc_cur > min_needed ? inc_cur : min_needed;
|
||||||
|
size_t *p = (size_t *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(size_t)*2);
|
||||||
|
if (p) {
|
||||||
|
if (!arr)
|
||||||
|
p[1] = 0;
|
||||||
|
p[0] = m;
|
||||||
|
return p+2;
|
||||||
|
} else {
|
||||||
|
#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||||
|
STRETCHY_BUFFER_OUT_OF_MEMORY ;
|
||||||
|
#endif
|
||||||
|
return (void *) (2*sizeof(size_t)); // try to force a NULL pointer exception later
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // STB_STRETCHY_BUFFER_H_INCLUDED
|
Loading…
Reference in New Issue