Compare commits

...

12 Commits

Author SHA1 Message Date
Matt Mascarenhas 14dafa4abe cinera.c: Fix stack-use-after-return segfault
The ClashResolver, or a memory_book variable therein, triggered a
stack-use-after-return segfault. Fixed by initialising the ClashResolver
in main() and passing it to InitClashResolver() by pointer, rather than
initialising it in InitClashResolver().
2024-03-12 13:03:08 +00:00
Matt Mascarenhas 213bb2f882 cinera.c: Fix colouring of topic dots
The colour computed for topic dots and put into cinera_topics.css in HSL
format has its lightness value modified depending on whether it's on a
dark or light background. Web browsers do not tell us an element's
computed colour in HSL format, but in RGB, so we would need to convert
it from RGB to HSL when setting the lightness. Previously, this
conversion was busted, calculating too small a value for the saturation.

This commit obviates the need for any RGB→HSL conversion by writing the
hue and saturation values as attributes to the elements, which the
function responsible for setting the lightness may use directly.
2024-02-21 20:52:34 +00:00
Matt Mascarenhas 77aec74483 cinera_player_post.js: Fix fullscreen handling
It is possible to bring our player out of fullscreen mode without using
the provided views menu or keyboard shortcut, e.g. by pressing Escape.
Doing so leaves our state in the SUPERTHEATRE view, which omits
SUPERtheatre mode itself from the views menu. This commit fixes this by
switching our state to the THEATRE view.

Thanks to Aske Bisgaard Vammen for the report.
2024-01-30 15:41:30 +00:00
Matt Mascarenhas 6852d06e04 cinera_player_pre.js: Unset focused menu
When hiding any menu, set this.MenusFocused.MenuID = menu_id.UNSET, to
let hovering over the markers focus them after hiding the link menu.
2023-03-25 01:51:40 +00:00
Matt Mascarenhas 52d6d989f8 cinera_player_pre.js: Fix scoping of "this" 2023-03-25 01:31:13 +00:00
Matt Mascarenhas df93674bf7 cinera: Increase precision to milliseconds
This commit simply adds millisecond precision of timecodes. It includes
small changes to hmmlib.h, cinera.c and the frontend JS files.
2023-03-25 00:04:34 +00:00
Matt Mascarenhas 026585e50b cinera: Fix mobile scrolling and centring
This commit fixes spurious scrolling in DeriveReliableWindowDimensions()
on mobile. It also fixes centring of Vimeo videos on mobile.

In addition, it replaces some deprecated JavaScript:
•   window.orientation → screen.orientation.type
•   window.onorientationchange → screen.orientation.onchange
2023-03-21 19:34:24 +00:00
Matt Mascarenhas 9d5f0f9146 cinera_player_pre.js: Fix scoping of "this" 2023-03-18 01:54:42 +00:00
Matt Mascarenhas a7694d4c3b cinera: Improve keyboard controls and scrolling
This commit adds keyboard navigation of the indices, documented in the
"Help" text box. It also improves scrolling of menus to follow progress
through the video, with the ability to centre the scrolling around a
range of references. Finally in this UI work, it enables the keyboard
and mouse to work more cooperatively.

Other changes:

• Added a "Clear" so the player's initial sizing happens invisibly.
• Fixed getBackgroundColourRGB() to handle both rgb() and rgba().
• Deduplicated code, including spurious querySelectorAll() calls.
• Moved global variables into the Player object.
2023-03-18 01:27:05 +00:00
Matt Mascarenhas c9bf96c7aa cinera_player_pre.js: Improve sizing and scrolling
This commit fixes the sizing of the player's menus to not protrude below
the player.

It also adds scrolling of the references menu to the first menu item
cited in the newly-current timestamp, and fixes scrolling of the markers
container when switching to a timestamp after the user has scrolled the
container away from its default position.
2023-02-20 20:29:16 +00:00
Matt Mascarenhas 44a5008aa7 cinera: Resolve output location clashes
This commit adds support for tracking and resolving clashes of output
locations. Previously open-ended clash dependency chains could passively
resolve across multiple Cinera invocations (undesirable, but possible);
whereas closed-loop dependency chains could not resolve without the user
manually resolving them by moving one clash to a temporary location (not
acceptable). This new support handles such clashes in one fell swoop.

Other smaller changes in this commit:

•   Change message_control to use memory_book
•   Fix deletion of old .index files when reorganising projects
•   Fix deletion of moved entries when subdividing projects
2023-02-10 23:12:21 +00:00
Matt Mascarenhas 6576a91d11 cinera: Support message deduplication
This commit adds support for deduplication of messages emitted by
PrintEdit(). It's a first pass, only supporting single-line messages and
ones no larger than 512 bytes.

It also fixes StringContains() to skip strings shorter than the search
substring, and emits a more sensible error on empty "output" value.
2023-01-13 20:31:44 +00:00
8 changed files with 3689 additions and 2313 deletions

File diff suppressed because it is too large Load Diff

View File

@ -540,6 +540,16 @@ ul.cineraNavPlain li.current > a {
display: block;
}
.cineraHelp .help_container .help_custom_index {
background-color: #159 !important;
}
.cineraHelp .help_container h2 .help_title_key {
padding: .2em;
border: 1px solid;
border-radius: 4px;
}
.cineraHelp .help_container .help_key {
box-sizing: content-box;
font-family: Inconsolata;
@ -614,12 +624,11 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu .credits_container {
border: 1px solid;
border-top: none;
display: none;
z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */
overflow-y: auto;
position: absolute;
right: 0;
top: 100%;
z-index: 8;
}
.cineraMenus > .menu .refs,
@ -639,7 +648,7 @@ ul.cineraNavPlain li.current > a {
}
.cineraMenus > .menu .visible {
display: block;
z-index: 8;
}
.cineraMenus > .menu > .refs .ref {
@ -737,6 +746,11 @@ ul.cineraNavPlain li.current > a {
margin-right: 4px;
}
.cineraMenus > .menu > .filter_container .filter_header {
position: sticky;
top: 0;
}
.cineraMenus > .menu > .filter_container .filter_mode,
.cineraMenus > .menu > .link_container #cineraLinkMode {
cursor: pointer;
@ -756,11 +770,13 @@ ul.cineraNavPlain li.current > a {
text-align: center;
}
.cineraMenus > .menu > .filter_container .filter_header .filter_titles,
.cineraMenus > .menu > .filter_container .filters {
display: flex;
flex-flow: row nowrap;
}
.cineraMenus > .menu > .filter_container .filter_header .filter_titles > *,
.cineraMenus > .menu > .filter_container .filters > * {
width: 50%;
flex-grow: 1;

View File

@ -113,20 +113,20 @@ MakeString_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
int FormatLength = StringLength(Format);
if(FormatLength != ArgCount)
{
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d MakeString()", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr,
Colourise(CS_FAILURE); Print(stderr, "%s:%d MakeString()", Filename, LineNumber); Colourise(CS_END);
Print(stderr,
"\n"
" MakeString() has been passed a format string containing ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
Colourise(CS_BLUE_BOLD); Print(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr,
Print(stderr,
",\n"
" and ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
Colourise(CS_BLUE_BOLD); Print(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, ", but these numbers should be equal.\n");
Print(stderr, ", but these numbers should be equal.\n");
__asm__("int3");
}
@ -161,8 +161,8 @@ MakeString_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
} break;
default:
{
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, "\n"
Colourise(CS_FAILURE); Print(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
Print(stderr, "\n"
" MakeString() has been passed an unsupported format specifier: %c\n", Format[i]);
__asm__("int3");
} break;
@ -197,20 +197,20 @@ MakeString0_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
int FormatLength = StringLength(Format);
if(FormatLength != ArgCount)
{
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d MakeString0()", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr,
Colourise(CS_FAILURE); Print(stderr, "%s:%d MakeString0()", Filename, LineNumber); Colourise(CS_END);
Print(stderr,
"\n"
" MakeString0() has been passed a format string containing ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
Colourise(CS_BLUE_BOLD); Print(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr,
Print(stderr,
",\n"
" and ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
Colourise(CS_BLUE_BOLD); Print(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, ", but these numbers should be equal.\n");
Print(stderr, ", but these numbers should be equal.\n");
__asm__("int3");
}
@ -264,8 +264,8 @@ MakeString0_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
} break;
default:
{
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, "\n"
Colourise(CS_FAILURE); Print(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
Print(stderr, "\n"
" MakeString0() has been passed an unsupported format specifier: %c\n", Format[i]);
__asm__("int3");
} break;
@ -304,11 +304,11 @@ PushToken(tokens *Set, token *Token)
void
PrintToken(token *T, uint64_t Index)
{
fprintf(stderr, "[%lu, %s] ", Index, TokenTypeStrings[T->Type]);
Print(stderr, "[%lu, %s] ", Index, TokenTypeStrings[T->Type]);
if(T->Type == TOKEN_NUMBER)
{
Colourise(CS_BLUE_BOLD);
fprintf(stderr, "%li", T->int64_t);
Print(stderr, "%li", T->int64_t);
Colourise(CS_END);
}
@ -320,7 +320,7 @@ PrintToken(token *T, uint64_t Index)
{
PrintString(T->Content);
}
fprintf(stderr, "\n");
Print(stderr, "\n");
}
void
@ -1094,7 +1094,7 @@ PrintTypeField(config_type_field *F, config_identifier_id ParentScopeID, int Ind
}
else{
Colourise(CS_GREEN_BOLD);
fprintf(stderr, "\"%s\"", FieldTypeNames[F->Type]);
Print(stderr, "\"%s\"", FieldTypeNames[F->Type]);
Colourise(CS_END);
}
PrintC(CS_BLACK_BOLD, "");
@ -1190,7 +1190,7 @@ PrintTypeSpec(config_type_spec *S, int IndentationLevel)
void
PrintTypeSpecs(memory_book *TypeSpecs, int IndentationLevel)
{
fprintf(stderr, "\n");
Print(stderr, "\n");
// TODO(matt): Document the fact that scopes within scopes are fully configurable, even though
// we only print out the "id" of scopes, omitting their containing fields
for(int i = 0; i < TypeSpecs->ItemCount; ++i)
@ -1453,28 +1453,28 @@ FreeScopeTree(scope_tree *T)
void
PrintPair(config_pair *P, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline)
{
if(PrependNewline) { fprintf(stderr, "\n"); }
if(PrependNewline) { Print(stderr, "\n"); }
Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String);
fprintf(stderr, "%s", Delimiter);
Print(stderr, "%s", Delimiter);
if(P->Type == PT_BOOL)
{
if(P->bool == TRUE)
{
Colourise(CS_GREEN);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "true");
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "true");
if(FillSyntax) { Print(stderr, "\""); }
}
else
{
Colourise(CS_RED);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "false");
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "false");
if(FillSyntax) { Print(stderr, "\""); }
}
Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); }
if(FillSyntax) { Print(stderr, ";"); }
}
else if(P->Type == PT_INT64)
{
@ -1483,68 +1483,68 @@ PrintPair(config_pair *P, char *Delimiter, bool FillSyntax, uint64_t Indentation
case IDENT_GENRE:
{
Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "%s", GenreStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "%s", GenreStrings[P->int64_t]);
if(FillSyntax) { Print(stderr, "\""); }
} break;
case IDENT_LOG_LEVEL:
{
Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "%s", LogLevelStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "%s", LogLevelStrings[P->int64_t]);
if(FillSyntax) { Print(stderr, "\""); }
} break;
case IDENT_NUMBERING_SCHEME:
{
Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "%s", NumberingSchemeStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "%s", NumberingSchemeStrings[P->int64_t]);
if(FillSyntax) { Print(stderr, "\""); }
} break;
default:
{
Colourise(CS_BLUE_BOLD);
fprintf(stderr, "%li", P->int64_t);
Print(stderr, "%li", P->int64_t);
} break;
}
Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); }
if(FillSyntax) { Print(stderr, ";"); }
}
else if(P->Type == PT_STRING)
{
if(P->String.Length)
{
Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
PrintString(P->String);
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); }
if(FillSyntax) { Print(stderr, ";"); }
}
else
{
PrintC(CS_YELLOW, "[unset]");
}
}
if(AppendNewline) { fprintf(stderr, "\n"); }
if(AppendNewline) { Print(stderr, "\n"); }
}
void
PrintScopeID(config_pair *P, char *Delimiter, uint64_t Indentation)
{
fprintf(stderr, "\n");
Print(stderr, "\n");
Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String);
fprintf(stderr, "%s", Delimiter);
Print(stderr, "%s", Delimiter);
if(P->String.Length)
{
Colourise(CS_GREEN_BOLD);
fprintf(stderr, "\"");
Print(stderr, "\"");
PrintString(P->String);
fprintf(stderr, "\"");
Print(stderr, "\"");
Colourise(CS_END);
IndentedCarriageReturn(Indentation);
fprintf(stderr, "{");
Print(stderr, "{");
}
else
{
@ -1555,26 +1555,26 @@ PrintScopeID(config_pair *P, char *Delimiter, uint64_t Indentation)
void
PrintBool(char *Title, bool Bool, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline)
{
if(PrependNewline) { fprintf(stderr, "\n"); }
if(PrependNewline) { Print(stderr, "\n"); }
Indent(Indentation);
PrintC(CS_YELLOW_BOLD, Title);
fprintf(stderr, "%s", Delimiter);
Print(stderr, "%s", Delimiter);
if(Bool == TRUE)
{
Colourise(CS_GREEN);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "true");
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "true");
if(FillSyntax) { Print(stderr, "\""); }
}
else
{
Colourise(CS_RED);
if(FillSyntax) { fprintf(stderr, "\""); }
fprintf(stderr, "false");
if(FillSyntax) { fprintf(stderr, "\""); }
if(FillSyntax) { Print(stderr, "\""); }
Print(stderr, "false");
if(FillSyntax) { Print(stderr, "\""); }
}
Colourise(CS_END);
if(AppendNewline) { fprintf(stderr, "\n"); }
if(AppendNewline) { Print(stderr, "\n"); }
}
void PrintScopeTree(scope_tree *T, int IndentationLevel);
@ -1602,7 +1602,7 @@ PrintScopeTree(scope_tree *T, int IndentationLevel)
{
--IndentationLevel;
IndentedCarriageReturn(IndentationLevel);
fprintf(stderr, "}");
Print(stderr, "}");
}
}
}
@ -1982,7 +1982,7 @@ ParseIncludeRules(memory_book *TypeSpecs, config_type_spec *ParentTypeSpec, toke
{
token *This = GetPlaceInBook(&T->Token, T->CurrentIndex);
ConfigError(&Filepath, This->LineNumber, S_WARNING, "Invalid identifier in \"include\" scope: ", &This->Content);
fprintf(stderr,
Print(stderr,
" Valid identifiers:\n"
" allow\n"
" deny\n");
@ -4013,11 +4013,11 @@ PrintAssociationClashes(config_string_associations *A)
config_pair Assoc1 = { .Key = A->ID1, .String = Association->String1, .Type = PT_STRING };
PrintPair(&Assoc1, ": ", FALSE, IndentLevel, TRUE, FALSE);
}
fprintf(stderr, "\n");
Print(stderr, "\n");
for(int j = 0; j < Association->Projects.ItemCount; ++j)
{
project **P = GetPlaceInBook(&Association->Projects, j);
fprintf(stderr, " ");
Print(stderr, " ");
if(*P)
{
PrintStringC(CS_CYAN, (*P)->Lineage);
@ -4032,7 +4032,7 @@ PrintAssociationClashes(config_string_associations *A)
}
PrintStringC(CS_RED, Wrap0("]"));
}
fprintf(stderr, "\n");
Print(stderr, "\n");
}
}
}
@ -4056,11 +4056,11 @@ PrintVariants(uint64_t V, colour_code ColourCode, string Margin)
int Length = StringLength(String);
if(RunningAvailableColumns - Length >= 0)
{
fprintf(stderr, "%s", NeedsSpacing ? " " : "");
Print(stderr, "%s", NeedsSpacing ? " " : "");
}
else
{
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintString(Margin);
NeedsSpacing = FALSE;
RunningAvailableColumns = AvailableColumnsForValue;
@ -4091,7 +4091,7 @@ PrintVariantInconsistencies(memory_book *VariantStrings)
string Margin = Wrap0(" ");
variant V = *(variant *)GetPlaceInBook(&ThisVariantsString->Variants, j);
PrintVariants(V, j % 2 ? CS_CYAN_BOLD : CS_YELLOW_BOLD, Margin);
fprintf(stderr, "\n");
Print(stderr, "\n");
}
}
}
@ -4133,9 +4133,9 @@ PrintLogLevel(char *Title, log_level Level, char *Delimiter, uint64_t Indentatio
{
Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_LOG_LEVEL].String);
fprintf(stderr, "%s", Delimiter);
Print(stderr, "%s", Delimiter);
PrintC(CS_GREEN_BOLD, LogLevelStrings[Level]);
if(AppendNewline) { fprintf(stderr, "\n"); }
if(AppendNewline) { Print(stderr, "\n"); }
}
typedef struct
@ -4157,14 +4157,14 @@ PrintVerticals(typography *T, int Count)
{
for(int i = 0; i < Count; ++i)
{
fprintf(stderr, "%s", T->Vertical);
Print(stderr, "%s", T->Vertical);
}
}
void
CarriageReturn(typography *T, int VerticalsRequired)
{
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintVerticals(T, VerticalsRequired);
}
@ -4176,12 +4176,12 @@ TypesetIconType(typography *T, uint8_t Generation, icon_type Type, bool PrependN
// TODO(matt): Implement me, please!
//CarriageReturn(T, Generation);
//fprintf(stderr, "%s", T.Margin);
//Print(stderr, "%s", T.Margin);
#if 0
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_ICON_TYPE].String);
fprintf(stderr, "%s", T.Delimiter);
Print(stderr, "%s", T.Delimiter);
PrintC(CS_GREEN_BOLD, IconTypeStrings[Type]);
fprintf(stderr, "\n");
Print(stderr, "\n");
#endif
@ -4195,10 +4195,10 @@ TypesetIconType(typography *T, uint8_t Generation, icon_type Type, bool PrependN
++Result;
}
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
config_identifier_id Key = IDENT_ICON_TYPE;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter);
Print(stderr, "%s", T->Delimiter);
if(Type == IT_DEFAULT_UNSET)
{
PrintC(CS_YELLOW, "[unset]");
@ -4229,17 +4229,17 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
if(RowsRequired)
{
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
}
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter);
int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter;
int RunningAvailableColumns = AvailableColumnsForValue;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter);
Print(stderr, "%s", T->Delimiter);
bool NeedsSpacing = FALSE;
if(Value)
@ -4252,7 +4252,7 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
int Length = StringLength(String);
if(RunningAvailableColumns - Length >= 0)
{
fprintf(stderr, "%s", NeedsSpacing ? " " : "");
Print(stderr, "%s", NeedsSpacing ? " " : "");
}
else
{
@ -4260,12 +4260,12 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
if(RowsRequired)
{
--*RowsRequired;
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
}
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i)
{
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
}
NeedsSpacing = FALSE;
RunningAvailableColumns = AvailableColumnsForValue;
@ -4295,9 +4295,9 @@ PrintMedium(typography *T, medium *M, char *Delimiter, uint64_t Indentation, boo
{
PrintC(CS_BLACK_BOLD, " [hidden]");
}
fprintf(stderr, "%s", Delimiter);
Print(stderr, "%s", Delimiter);
PrintStringC(CS_MAGENTA, M->Name);
fprintf(stderr, " (");
Print(stderr, " (");
if(M->Icon.Length > 0)
{
PrintStringI(M->Icon, Indentation);
@ -4312,10 +4312,10 @@ PrintMedium(typography *T, medium *M, char *Delimiter, uint64_t Indentation, boo
}
TypesetIconType(T, Indentation, M->IconType, FALSE, AppendNewline);
TypesetVariants(T, Indentation, IDENT_ICON_VARIANTS, M->IconVariants, 0, 0, FALSE, AppendNewline);
fprintf(stderr, ")");
Print(stderr, ")");
if(AppendNewline)
{
fprintf(stderr, "\n");
Print(stderr, "\n");
}
}
@ -4324,13 +4324,13 @@ PrintSupport(support *S, typography *Typography, int IndentationLevel, uint8_t *
{
bool ShouldFillSyntax = FALSE;
config_pair ID = { .Key = IDENT_SUPPORT, .String = S->ID, .Type = PT_STRING };
fprintf(stderr, "%s%s", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&ID, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired;
if(S->URL.Length > 0)
{
config_pair URL = { .Key = IDENT_URL, .String = S->URL, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&URL, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired;
}
@ -4338,28 +4338,28 @@ PrintSupport(support *S, typography *Typography, int IndentationLevel, uint8_t *
if(S->Icon.Length > 0)
{
config_pair Icon = { .Key = IDENT_ICON, .String = S->Icon, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&Icon, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired;
}
if(S->IconNormal.Length > 0)
{
config_pair IconNormal = { .Key = IDENT_ICON_NORMAL, .String = S->IconNormal, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&IconNormal, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired;
}
if(S->IconFocused.Length > 0)
{
config_pair IconFocused = { .Key = IDENT_ICON_FOCUSED, .String = S->IconFocused, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&IconFocused, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired;
}
#if 1
// TODO(matt): FIX ME
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
*RowsRequired -= TypesetIconType(Typography, IndentationLevel, S->IconType, FALSE, TRUE);
uint8_t StartingColumn = 4;
uint64_t AvailableColumns = GetTerminalColumns() - StartingColumn;
@ -4424,11 +4424,11 @@ void
PrintPerson(person *P, typography *Typography)
{
bool HaveInfo = P->Name.Length > 0 || P->Homepage.Length > 0 || P->Support.ItemCount > 0;
fprintf(stderr, "\n"
Print(stderr, "\n"
"%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft,
Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight);
PrintStringC(CS_GREEN_BOLD, P->ID);
fprintf(stderr, "\n");
Print(stderr, "\n");
uint8_t RowsRequired = GetRowsRequiredForPersonInfo(Typography, P);
@ -4436,35 +4436,35 @@ PrintPerson(person *P, typography *Typography)
bool ShouldFillSyntax = FALSE;
if(P->Name.Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Name = { .Key = IDENT_NAME, .String = P->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
if(P->Abbreviations[AS_INITIAL].Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevInitial = { .Key = IDENT_ABBREV_INITIAL, .String = P->Abbreviations[AS_INITIAL], .Type = PT_STRING }; PrintPair(&AbbrevInitial, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
if(P->Abbreviations[AS_GIVEN_OR_NICKNAME].Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevGivenOrNickname = { .Key = IDENT_ABBREV_GIVEN_OR_NICKNAME, .String = P->Abbreviations[AS_GIVEN_OR_NICKNAME], .Type = PT_STRING }; PrintPair(&AbbrevGivenOrNickname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
if(P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME].Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevDottedInitialAndSurname = { .Key = IDENT_ABBREV_DOTTED_INITIAL_AND_SURNAME, .String = P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME], .Type = PT_STRING }; PrintPair(&AbbrevDottedInitialAndSurname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
if(P->Homepage.Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Homepage = { .Key = IDENT_HOMEPAGE, .String = P->Homepage, .Type = PT_STRING }; PrintPair(&Homepage, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
@ -4490,11 +4490,11 @@ void
PrintRole(role *R, typography *Typography)
{
bool HaveInfo = R->Name.Length > 0 || R->Plural.Length > 0;
fprintf(stderr, "\n"
Print(stderr, "\n"
"%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft,
Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight);
PrintStringC(CS_GREEN_BOLD, R->ID);
fprintf(stderr, "\n");
Print(stderr, "\n");
uint8_t RowsRequired = GetRowsRequiredForRoleInfo(R);
@ -4502,18 +4502,18 @@ PrintRole(role *R, typography *Typography)
bool ShouldFillSyntax = FALSE;
if(R->Name.Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Name = { .Key = IDENT_NAME, .String = R->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
if(R->Plural.Length > 0)
{
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Plural = { .Key = IDENT_PLURAL, .String = R->Plural, .Type = PT_STRING }; PrintPair(&Plural, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair NonSpeaking = { .Key = IDENT_NON_SPEAKING, .bool = R->NonSpeaking, .Type = PT_BOOL }; PrintPair(&NonSpeaking, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired;
}
@ -4529,7 +4529,21 @@ PrintLineage(string Lineage, bool AppendNewline)
}
string Us = GetFinalComponent(Lineage);
PrintStringC(CS_BLUE_BOLD, Us);
if(AppendNewline) { fprintf(stderr, "\n"); }
if(AppendNewline) { Print(stderr, "\n"); }
}
void
ExtendLineageInBook(memory_book *M, string Lineage, bool AppendNewline)
{
string Parent = StripComponentFromPath(Lineage);
ExtendStringCInBook(M, CS_BLACK_BOLD, Parent);
if(Parent.Length > 0)
{
ExtendStringCInBook(M, CS_BLACK_BOLD, Wrap0("/"));
}
string Us = GetFinalComponent(Lineage);
ExtendStringCInBook(M, CS_BLUE_BOLD, Us);
if(AppendNewline) { ExtendStringInBook(M, Wrap0("\n")); }
}
uint8_t
@ -4554,20 +4568,20 @@ PrintTitle(char *Text, typography *T, uint8_t Generation, int AvailableColumns,
int InnerCharsRequired = AvailableColumns - LeftmostCharLength - StringLength(Text) - RightmostCharLength - SpacesRequired;
int LeftCharsRequired = 4;
int RightCharsRequired = InnerCharsRequired - LeftCharsRequired;
fprintf(stderr, "%s", LeftmostChar);
Print(stderr, "%s", LeftmostChar);
for(int i = 0; i < LeftCharsRequired; ++i)
{
fprintf(stderr, "%s", InnerChar);
Print(stderr, "%s", InnerChar);
}
fprintf(stderr, " %s ", Text);
Print(stderr, " %s ", Text);
for(int i = 0; i < RightCharsRequired; ++i)
{
fprintf(stderr, "%s", InnerChar);
Print(stderr, "%s", InnerChar);
}
fprintf(stderr, "%s", RightmostChar);
Print(stderr, "%s", RightmostChar);
if(SectionItemCount == 0)
{
//fprintf(stderr, "\n");
//Print(stderr, "\n");
CarriageReturn(T, Generation);
}
}
@ -4577,10 +4591,10 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
{
int IndentationLevel = 0;
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintTitle("Media", T, Generation, AvailableColumns, P->Medium.ItemCount);
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
//AvailableColumns -= Generation + 1;
int RunningAvailableColumns = AvailableColumns;
medium *This = GetPlaceInBook(&P->Medium, 0);
@ -4597,19 +4611,19 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
ColumnsRequired = GetColumnsRequiredForMedium(This, T);
if(RunningAvailableColumns >= StringLengthOfSpacePlusSeparator)
{
fprintf(stderr, " %s", T->Separator);
Print(stderr, " %s", T->Separator);
RunningAvailableColumns -= StringLengthOfSpacePlusSeparator;
if(ColumnsRequired + 1 <= RunningAvailableColumns)
{
fprintf(stderr, " ");
Print(stderr, " ");
--RunningAvailableColumns;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
}
else
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
RunningAvailableColumns = AvailableColumns;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
}
@ -4617,9 +4631,9 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
else
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
RunningAvailableColumns = AvailableColumns;
fprintf(stderr, "%s ", T->Separator);
Print(stderr, "%s ", T->Separator);
RunningAvailableColumns -= StringLengthOfSpacePlusSeparator;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
}
@ -4647,7 +4661,7 @@ PrintCredits(config *C, project *P, typography *T, uint8_t Generation, int Inden
{
CarriageReturn(T, Generation);
int Alignment = 0;
Alignment += fprintf(stderr, "%s", T->Margin);
Alignment += Print(stderr, "%s", T->Margin);
if(CreditCount == 1)
{
Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Name.Length ? Role->Name : Role->ID);
@ -4664,7 +4678,7 @@ PrintCredits(config *C, project *P, typography *T, uint8_t Generation, int Inden
Alignment += PrintStringC(CS_YELLOW_BOLD, Wrap0("s"));
}
}
Alignment += fprintf(stderr, "%s", T->Delimiter);
Alignment += Print(stderr, "%s", T->Delimiter);
bool Printed = FALSE;
for(int j = 0; j < P->Credit.ItemCount; ++j)
{
@ -4688,13 +4702,13 @@ void
TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string Value, int AvailableColumns)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter);
int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter);
Print(stderr, "%s", T->Delimiter);
int CharactersToWrite = Value.Length;
if(Value.Length > 0)
@ -4706,10 +4720,10 @@ TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string
while(CharactersToWrite > 0)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i)
{
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
}
string Pen = { .Base = Value.Base + Value.Length - CharactersToWrite,
@ -4728,7 +4742,7 @@ void
TypesetNumber(typography *T, uint8_t Generation, config_identifier_id Key, int64_t Value)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
config_pair Pair = { .Key = Key, .int64_t = Value, .Type = PT_INT64 };
bool ShouldFillSyntax = FALSE;
PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE);
@ -4738,7 +4752,7 @@ void
TypesetBool(typography *T, uint8_t Generation, config_identifier_id Key, bool Value)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
config_pair Pair = { .Key = Key, .bool = Value, .Type = PT_BOOL };
bool ShouldFillSyntax = FALSE;
PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE);
@ -4748,9 +4762,9 @@ void
TypesetGenre(typography *T, uint8_t Generation, genre G)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_GENRE].String);
fprintf(stderr, "%s", T->Delimiter);
Print(stderr, "%s", T->Delimiter);
PrintC(CS_GREEN_BOLD, GenreStrings[G]);
}
@ -4758,9 +4772,9 @@ void
TypesetVODPlatform(typography *T, uint8_t Generation, vod_platform P)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_VOD_PLATFORM].String);
fprintf(stderr, "%s", T->Delimiter);
Print(stderr, "%s", T->Delimiter);
if(IsValidVODPlatform(P))
{
PrintC(CS_GREEN_BOLD, VODPlatformStrings[P]);
@ -4775,9 +4789,9 @@ void
TypesetNumberingMethod(typography *T, uint8_t Generation, numbering_method N)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_METHOD].String);
fprintf(stderr, "%s", T-> Delimiter);
Print(stderr, "%s", T-> Delimiter);
PrintC(CS_GREEN_BOLD, NumberingMethodStrings[N]);
}
@ -4785,9 +4799,9 @@ void
TypesetNumberingScheme(typography *T, uint8_t Generation, numbering_scheme N)
{
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_SCHEME].String);
fprintf(stderr, "%s", T-> Delimiter);
Print(stderr, "%s", T-> Delimiter);
PrintC(CS_GREEN_BOLD, NumberingSchemeStrings[N]);
}
@ -4797,7 +4811,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
int Generation = Ancestors + 1;
CarriageReturn(T, Ancestors);
fprintf(stderr, "%s%s%s%s%s ", T->UpperLeftCorner,
Print(stderr, "%s%s%s%s%s ", T->UpperLeftCorner,
T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight);
PrintLineage(P->Lineage, FALSE);
@ -4810,7 +4824,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
CarriageReturn(T, Generation);
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintTitle("Settings", T, Generation, AvailableColumns, -1);
TypesetPair(T, Generation, IDENT_TITLE, P->Title, AvailableColumns);
@ -4858,7 +4872,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
CarriageReturn(T, Generation);
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintTitle("Credits", T, Generation, AvailableColumns, P->Credit.ItemCount);
if(P->Credit.ItemCount > 0)
{
@ -4866,7 +4880,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
}
else
{
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW, "[none]");
}
@ -4874,7 +4888,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
{
CarriageReturn(T, Generation);
CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin);
Print(stderr, "%s", T->Margin);
PrintTitle("Children", T, Generation, AvailableColumns, -1);
++Ancestors;
for(int i = 0; i < P->Child.ItemCount; ++i)
@ -4884,7 +4898,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
--Ancestors;
}
CarriageReturn(T, Ancestors);
fprintf(stderr, "%s%s%s%s%s ", T->LowerLeftCorner,
Print(stderr, "%s%s%s%s%s ", T->LowerLeftCorner,
T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight);
PrintLineage(P->Lineage, FALSE);
}
@ -4911,7 +4925,7 @@ PrintConfig(config *C, bool ShouldClearTerminal)
};
// separate
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintTitle("Global Settings", &Typography, 0, TermCols, -1);
int IndentationLevel = 0;
@ -4920,14 +4934,14 @@ PrintConfig(config *C, bool ShouldClearTerminal)
TypesetPair(&Typography, 0, IDENT_CACHE_DIR, C->CacheDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_INSTANCE_TITLE, C->InstanceTitle, AvailableColumns);
fprintf(stderr, "\n");
Print(stderr, "\n");
TypesetPair(&Typography, 0, IDENT_GLOBAL_TEMPLATES_DIR, C->GlobalTemplatesDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_TEMPLATE, C->GlobalSearchTemplatePath, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_DIR, C->GlobalSearchDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_URL, C->GlobalSearchURL, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_THEME, C->GlobalTheme, AvailableColumns);
fprintf(stderr, "\n");
Print(stderr, "\n");
TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_DIR, C->AssetsRootDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_URL, C->AssetsRootURL, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_CSS_PATH, C->CSSDir, AvailableColumns);
@ -4935,32 +4949,32 @@ PrintConfig(config *C, bool ShouldClearTerminal)
TypesetPair(&Typography, 0, IDENT_JS_PATH, C->JSDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_QUERY_STRING, C->QueryString, AvailableColumns);
fprintf(stderr, "\n"
Print(stderr, "\n"
"\n"
"%s", Typography.Margin);
bool ShouldFillSyntax = FALSE;
PrintBool("Respecting Privacy (derived from projects)", C->RespectingPrivacy, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
fprintf(stderr, "%s", Typography.Margin);
Print(stderr, "%s", Typography.Margin);
int SecondsPerMinute = 60;
config_pair PrivacyCheckInterval = { .Key = IDENT_PRIVACY_CHECK_INTERVAL, .int64_t = C->PrivacyCheckInterval / SecondsPerMinute, .Type = PT_INT64 }; PrintPair(&PrivacyCheckInterval, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
fprintf(stderr, "%s", Typography.Margin);
Print(stderr, "%s", Typography.Margin);
PrintLogLevel(ConfigIdentifiers[IDENT_LOG_LEVEL].String, C->LogLevel, Typography.Delimiter, IndentationLevel, TRUE);
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintTitle("People", &Typography, 0, TermCols, C->Person.ItemCount);
for(int i = 0; i < C->Person.ItemCount; ++i)
{
PrintPerson(GetPlaceInBook(&C->Person, i), &Typography);
}
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintTitle("Roles", &Typography, 0, TermCols, C->Role.ItemCount);
for(int i = 0; i < C->Role.ItemCount; ++i)
{
PrintRole(GetPlaceInBook(&C->Role, i), &Typography);
}
fprintf(stderr, "\n");
Print(stderr, "\n");
PrintTitle("Projects", &Typography, 0, TermCols, -1);
for(int i = 0; i < C->Project.ItemCount; ++i)
{
@ -5055,7 +5069,7 @@ PositionRolesInConfig(config *C, resolution_errors *E, config_verifiers *V, scop
config_pair *Position = GetPair(*Role, IDENT_POSITION);
if(IsPositioned(Position))
{
int TargetPos;
int TargetPos = 0;
if(Position->int64_t < 0)
{
TargetPos = SlotCount + Position->int64_t;
@ -5368,7 +5382,7 @@ ParseConfig(string Path, memory_book *TokensList)
int
main(int ArgC, char **Args)
{
//fprintf(stderr, "%s\n", ConfigFile.Buffer.Location);
//Print(stderr, "%s\n", ConfigFile.Buffer.Location);
config_type_specs TypeSpecs = InitTypeSpecs();
config *Config = ParseConfig(&TypeSpecs, Wrap0("cinera.conf"));

View File

@ -1,40 +1,5 @@
var cinera = document.querySelector(".cinera");
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
var originalTextContent = {
TitleQuotes: null,
TitleReferences: null,
TitleCredits: null,
EpisodePrev: null,
EpisodeNext: null,
};
var menuState = [];
var titleBar = cinera.querySelector(".cineraMenus");
var quotesMenu = null;
var referencesMenu = null;
var filterMenu = null;
var viewsMenu = null;
var linkMenu = null;
var creditsMenu = null;
var sourceMenus = null;
var helpButton = null;
var helpDocumentation = null;
// NOTE(matt): One set of markers per page. There is code to support multiple, which we may want to extend everywhere
var MarkersContainer = cinera.querySelector(".markers_container");
var views = {
REGULAR: 0,
THEATRE: 1,
SUPERTHEATRE: 2,
};
var devices = {
DESKTOP: 0,
MOBILE: 1,
};
var CineraProps = {
C: null,
V: views.REGULAR,
@ -57,275 +22,19 @@ var CineraProps = {
};
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(titleBar)
{
quotesMenu = titleBar.querySelector(".quotes_container");
if(quotesMenu)
{
originalTextContent.TitleQuotes = quotesMenu.previousElementSibling.textContent;
menuState.push(quotesMenu);
var quoteItems = quotesMenu.querySelectorAll(".ref");
if(quoteItems)
{
for(var i = 0; i < quoteItems.length; ++i)
{
quoteItems[i].addEventListener("mouseenter", function(ev) {
mouseOverQuotes(this);
})
};
}
var quoteTimecodes = quotesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < quoteTimecodes.length; ++i) {
quoteTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
var lastFocusedQuote = null;
}
referencesMenu = titleBar.querySelector(".references_container");
if(referencesMenu)
{
originalTextContent.TitleReferences = referencesMenu.previousElementSibling.textContent;
menuState.push(referencesMenu);
var referenceItems = referencesMenu.querySelectorAll(".ref");
if(referenceItems)
{
for(var i = 0; i < referenceItems.length; ++i)
{
referenceItems[i].addEventListener("mouseenter", function(ev) {
mouseOverReferences(this);
})
};
var lastFocusedReference = null;
var lastFocusedIdentifier = null;
}
var refTimecodes = referencesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < refTimecodes.length; ++i) {
refTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
}
if(referencesMenu || quotesMenu)
{
var refSources = titleBar.querySelectorAll(".refs .ref"); // This is for both quotes and refs
for (var i = 0; i < refSources.length; ++i) {
refSources[i].addEventListener("click", function(ev) {
if (player) {
player.pause();
}
});
}
}
filterMenu = titleBar.querySelector(".filter_container");
if(filterMenu)
{
menuState.push(filterMenu);
var lastFocusedCategory = null;
var lastFocusedTopic = null;
var lastFocusedMedium = null;
var filter = filterMenu.parentNode;
var filterModeElement = filter.querySelector(".filter_mode");
filterModeElement.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleFilterMode();
});
var filterMode = filterModeElement.classList[1];
var filterItems = filter.querySelectorAll(".filter_content");
var filterInitState = new Object();
var filterState = new Object();
for(var i = 0; i < filterItems.length; ++i)
{
filterItems[i].addEventListener("mouseenter", function(ev) {
navigateFilter(this);
})
filterItems[i].addEventListener("click", function(ev) {
ev.stopPropagation();
filterItemToggle(this);
});
var filterItemName = filterItems[i].classList.item(1);
if(filterItems[i].parentNode.classList.contains("filter_topics"))
{
filterInitState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
}
else
{
filterInitState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
}
}
}
viewsMenu = titleBar.querySelector(".views");
if(viewsMenu && !CineraProps.IsMobile)
{
menuState.push(viewsMenu);
var viewsContainer = viewsMenu.querySelector(".views_container");
viewsMenu.addEventListener("mouseenter", function(ev) {
handleMouseOverViewsMenu();
});
viewsMenu.addEventListener("mouseleave", function(ev) {
viewsContainer.style.display = "none";
});
var viewItems = viewsMenu.querySelectorAll(".view");
for(var i = 0; i < viewItems.length; ++i)
{
viewItems[i].addEventListener("click", function(ev) {
switch(this.getAttribute("data-id"))
{
case "regular":
case "theatre":
{
toggleTheatreMode();
} break;
case "super":
{
toggleSuperTheatreMode();
} break;
}
});
}
}
linkMenu = titleBar.querySelector(".link_container");
linkTimestamp = true;
if(linkMenu)
{
menuState.push(linkMenu);
var linkMode = linkMenu.querySelector("#cineraLinkMode");
var link = linkMenu.querySelector("#cineraLink");
linkMode.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleLinkMode(linkMode, link);
});
link.addEventListener("click", function(ev) {
CopyToClipboard(link);
toggleMenuVisibility(linkMenu);
});
}
creditsMenu = titleBar.querySelector(".credits_container");
if(creditsMenu)
{
originalTextContent.TitleCredits = creditsMenu.previousElementSibling.textContent;
menuState.push(creditsMenu);
var lastFocusedCreditItem = null;
var creditItems = creditsMenu.querySelectorAll(".person, .support");
for(var i = 0; i < creditItems.length; ++i)
{
creditItems[i].addEventListener("mouseenter", function(ev) {
if(this != lastFocusedCreditItem)
{
lastFocusedCreditItem.classList.remove("focused");
unfocusSprite(lastFocusedCreditItem);
if(lastFocusedCreditItem.classList.contains("support"))
{
setSpriteLightness(lastFocusedCreditItem.firstChild);
}
lastFocusedCreditItem = this;
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
if(focusedElement.classList.contains("support"))
{
setSpriteLightness(focusedElement.firstChild);
}
}
});
if(creditItems[i].tagName == "A")
{
creditItems[i].addEventListener("click", function(ev) {
if(player)
{
player.pause();
}
});
}
}
}
sourceMenus = titleBar.querySelectorAll(".menu");
helpButton = titleBar.querySelector(".cineraHelp");
helpDocumentation = helpButton.querySelector(".help_container");
BindHelp(helpButton, helpDocumentation);
}
var focusedElement = null;
var focusedIdentifier = null;
var playerContainer = cinera.querySelector(".cineraPlayerContainer")
var prevEpisode = playerContainer.querySelector(".episodeMarker.prev");
if(prevEpisode) { originalTextContent.EpisodePrev = prevEpisode.firstChild.textContent; }
var nextEpisode = playerContainer.querySelector(".episodeMarker.next");
if(nextEpisode) { originalTextContent.EpisodeNext = nextEpisode.firstChild.textContent; }
var testMarkers = playerContainer.querySelectorAll(".marker");
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector);
var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container";
var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
if(CineraProps.IsMobile)
{
InitMobileStyle();
}
else
{
var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - 4;
MenuContainerRule.style.maxHeight = MenuMaxHeight + "px";
}
var player = new Player(playerContainer, onRefChanged);
if(CineraProps.IsMobile)
{
ConnectMobileControls(player);
}
var cineraViewStorageItem = "cineraView";
if(viewsMenu && localStorage.getItem(cineraViewStorageItem))
{
toggleTheatreMode();
}
InitScrollEventListener(cinera);
function
DelayedUpdateSize()
{
player.updateSize();
}
var cinera = document.querySelector(".cinera");
var player = new Player(cinera, onRefChanged);
window.addEventListener("resize", function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512);
setTimeout(DelayedUpdateSize, 512, player);
}
else
{
@ -333,10 +42,10 @@ window.addEventListener("resize", function() {
}
});
window.onorientationchange = function() {
screen.orientation.onchange = function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512);
setTimeout(DelayedUpdateSize, 512, player);
}
else
{
@ -351,43 +60,17 @@ document.addEventListener("keydown", function(ev) {
key = "capitalSpace";
}
if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement)
if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item)
{
ev.preventDefault();
}
});
for(var i = 0; i < sourceMenus.length; ++i)
{
sourceMenus[i].addEventListener("mouseenter", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
sourceMenus[i].addEventListener("mouseleave", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
sourceMenus[i].addEventListener("click", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
};
var colouredItems = playerContainer.querySelectorAll(".author, .member, .project");
for(i = 0; i < colouredItems.length; ++i)
{
setTextLightness(colouredItems[i]);
}
var topicDots = cinera.querySelectorAll(".category");
for(var i = 0; i < topicDots.length; ++i)
{
setDotLightness(topicDots[i]);
}
var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname;
var lastTimestamp;
if(location.hash) {
player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash);
}
else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem))
{
player.setTimeThenPlay(lastTimestamp);
}
document.addEventListener("fullscreenchange", function() {
if(!document.fullscreenElement && CineraProps.V == views.SUPERTHEATRE)
{
CineraProps.V = views.THEATRE;
localStorage.setItem(player.cineraViewStorageItem, views.THEATRE);
player.updateSize();
}
});

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,9 @@ DeriveReliableWindowDimensions()
Y: null,
};
var ScrollPosX = window.scrollX;
var ScrollPosY = window.scrollY;
var DisplaySettings = [];
for(var i = 0; i < document.body.children.length; ++i)
{
@ -40,6 +43,9 @@ DeriveReliableWindowDimensions()
Child.style.display = DisplaySettings.shift();
}
ScrollTriggeredInternally = true;
window.scroll(ScrollPosX, ScrollPosY);
return Result;
}
@ -76,7 +82,7 @@ function IsVisible(Element, WindowDim) {
function
GetRealOrientation(PreferredLandscape, IsMobile)
{
var Result = window.orientation;
var Result = screen.orientation.angle;
var WindowDim = GetWindowDim(IsMobile);
if(WindowDim.Y > WindowDim.X)
{
@ -186,14 +192,21 @@ function enableSprite(Element)
function disableSprite(Element)
{
if(Element.classList.contains("cineraSprite"))
if(Element.classList.contains("focused"))
{
setSpriteLightness(Element);
Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px";
focusSprite(Element);
}
for(var i = 0; i < Element.childElementCount; ++i)
else
{
disableSprite(Element.children[i]);
if(Element.classList.contains("cineraSprite"))
{
setSpriteLightness(Element);
Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px";
}
for(var i = 0; i < Element.childElementCount; ++i)
{
disableSprite(Element.children[i]);
}
}
}
@ -310,6 +323,7 @@ function IsInRangeEx(Min, N, Max)
}
/* Auto-scrolling */
var ScrollTriggeredInternally = false;
var LastScrollYPos = 0;
var ScrollTicking = false;
var ScrollerFunction;
@ -376,7 +390,11 @@ function
InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
{
window.addEventListener('scroll', function() {
if(ScrollCondition == undefined || ScrollCondition == true)
if(ScrollTriggeredInternally)
{
ScrollTriggeredInternally = false;
}
else if(ScrollCondition == undefined || ScrollCondition == true)
{
LastScrollYPos = window.scrollY;
@ -503,17 +521,29 @@ IsOverflowed(Element)
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
}
function
SetHelpUnfocused(Button)
{
Button.firstElementChild.innerText = "¿";
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
}
function
SetHelpFocused(Button)
{
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
}
function
BindHelp(Button, DocumentationContainer)
{
window.addEventListener("blur", function(){
Button.firstElementChild.innerText = "¿";
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
SetHelpUnfocused(Button);
});
window.addEventListener("focus", function(){
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
SetHelpFocused(Button);
});
Button.addEventListener("click", function() {
@ -521,45 +551,16 @@ BindHelp(Button, DocumentationContainer)
})
}
function RGBtoHSL(colour)
{
var rgb = colour.slice(4, -1).split(", ");
var red = rgb[0];
var green = rgb[1];
var blue = rgb[2];
var min = Math.min(red, green, blue);
var max = Math.max(red, green, blue);
var chroma = max - min;
var hue = 0;
if(max == red)
{
hue = ((green - blue) / chroma) % 6;
}
else if(max == green)
{
hue = ((blue - red) / chroma) + 2;
}
else if(max == blue)
{
hue = ((red - green) / chroma) + 4;
}
var saturation = chroma / 255 * 100;
hue = (hue * 60) < 0 ? 360 + (hue * 60) : (hue * 60);
return [hue, saturation]
}
function getBackgroundColourRGB(element) {
var Colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0;
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && depth <= 4)
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && element.parentElement && depth <= 4)
{
element = element.parentNode;
element = element.parentElement;
Colour = getComputedStyle(element).getPropertyValue("background-color");
++depth;
}
var Staging = Colour.slice(4, -1).split(", ");
var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", ");
var Result = {
R: parseInt(Staging[0]),
G: parseInt(Staging[1]),
@ -580,28 +581,34 @@ function setTextLightness(textElement)
{
var textHue = textElement.getAttribute("data-hue");
var textSaturation = textElement.getAttribute("data-saturation");
if(getBackgroundBrightness(textElement.parentNode) < 127)
if(textHue && textSaturation)
{
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
}
else
{
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
if(getBackgroundBrightness(textElement.parentNode) < 127)
{
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
}
else
{
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
}
}
}
function setDotLightness(topicDot)
{
var Hue = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[0];
var Saturation = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[1];
if(getBackgroundBrightness(topicDot.parentNode) < 127)
var dotHue = topicDot.getAttribute("data-hue");
var dotSaturation = topicDot.getAttribute("data-saturation");
if(dotHue && dotSaturation)
{
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
}
else
{
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
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%)");
}
}
}

View File

@ -213,11 +213,11 @@ function prepareToParseIndexFile(project)
mode = "markers";
episode.markers = [];
} else if (mode == "markers") {
var match = line.match(/"(\d+)": "(.+)"/);
var match = line.match(/"(\d+.\d+)": "(.+)"/);
if (match == null) {
console.log(name, line);
} else {
var totalTime = parseInt(line.slice(1));
var totalTime = parseFloat(line.slice(1));
var marker = {
totalTime: totalTime,
prettyTime: markerTime(totalTime),
@ -292,7 +292,7 @@ function markerTime(totalTime) {
var markTime = "(";
var hours = Math.floor(totalTime / 60 / 60);
var minutes = Math.floor(totalTime / 60) % 60;
var seconds = totalTime % 60;
var seconds = Math.floor(totalTime) % 60;
if (hours > 0) {
markTime += padTimeComponent(hours) + ":";
}
@ -460,7 +460,7 @@ function runSearch(refresh) {
Search.ResultsSummary.style.display = "none";
}
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + Math.floor(totalSeconds)%60 + "s ";
Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
}
@ -3866,7 +3866,7 @@ InitResizeEventListener()
function
InitOrientationChangeListener()
{
window.onorientationchange = function()
screen.orientation.onchange = function()
{
if(CineraProps.IsMobile)
{

View File

@ -76,7 +76,7 @@ typedef struct {
typedef struct {
int line;
int h, m, s;
int h, m, s, ms;
char* text;
char* author;
@ -471,7 +471,7 @@ next_attr:
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
{
unsigned int h = 0, m = 0, s = 0;
unsigned int h = 0, m = 0, s = 0, ms = 0;
int offset = 0;
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
@ -485,7 +485,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
if(c == ':') {
unsigned int tmp;
offset = 0;
if(sscanf(p->cursor, ":%u]%n", &tmp, &offset) != 1 || offset == 0) {
if(sscanf(p->cursor, ":%u%n", &tmp, &offset) != 1 || offset == 0) {
_hmml_err(p, "Unable to parse 3-part timecode");
}
@ -494,6 +494,27 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
s = tmp;
p->cursor += offset;
c = *p->cursor;
}
if(c == '.') {
unsigned int tmp;
offset = 0;
int non_number_chars = 2;
int digits_in_100 = 3;
int max_chars_to_parse = non_number_chars + digits_in_100;
if(sscanf(p->cursor, ".%u]%n", &tmp, &offset) != 1 || offset == 0 || offset > max_chars_to_parse) {
_hmml_err(p, "Unable to parse %u.5-part timecode", h ? 3 : 2);
}
for(int i = offset - non_number_chars; i < digits_in_100; ++i) {
tmp *= 10;
}
ms = tmp;
p->cursor += offset;
} else if(c != ']') {
_hmml_err(p, "Unable to parse timecode");
@ -501,6 +522,10 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
++p->cursor;
}
if(ms >= 1000) {
_hmml_err(p, "Milliseconds cannot exceed 999");
}
if(s >= 60) {
_hmml_err(p, "Seconds cannot exceed 59");
}
@ -512,6 +537,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
ts->h = h;
ts->m = m;
ts->s = s;
ts->ms = ms;
}
static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size)
@ -824,7 +850,7 @@ void hmml_free(HMML_Output* out)
}
const struct HMML_Version hmml_version = {
2, 0, 14
2, 0, 15
};
#undef HSTX