Compare commits
9 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 |
360
cinera/cinera.c
360
cinera/cinera.c
|
@ -23,7 +23,7 @@ typedef struct
|
||||||
version CINERA_APP_VERSION = {
|
version CINERA_APP_VERSION = {
|
||||||
.Major = 0,
|
.Major = 0,
|
||||||
.Minor = 10,
|
.Minor = 10,
|
||||||
.Patch = 24
|
.Patch = 30
|
||||||
};
|
};
|
||||||
|
|
||||||
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
|
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
|
||||||
|
@ -210,7 +210,12 @@ typedef struct
|
||||||
int C;
|
int C;
|
||||||
int Seconds;
|
int Seconds;
|
||||||
};
|
};
|
||||||
} v3;
|
union
|
||||||
|
{
|
||||||
|
int D;
|
||||||
|
int Milliseconds;
|
||||||
|
};
|
||||||
|
} v4;
|
||||||
|
|
||||||
void
|
void
|
||||||
Clear(void *V, uint64_t Size)
|
Clear(void *V, uint64_t Size)
|
||||||
|
@ -898,8 +903,8 @@ FreeBook(memory_book *M)
|
||||||
void
|
void
|
||||||
FreeAndReinitialiseBook(memory_book *M)
|
FreeAndReinitialiseBook(memory_book *M)
|
||||||
{
|
{
|
||||||
int PageSize = M->PageSize;
|
uint64_t PageSize = M->PageSize;
|
||||||
int DataWidthInBytes = M->DataWidthInBytes;
|
uint64_t DataWidthInBytes = M->DataWidthInBytes;
|
||||||
|
|
||||||
FreeBook(M);
|
FreeBook(M);
|
||||||
|
|
||||||
|
@ -2167,7 +2172,7 @@ IndexingError(string Filename, uint64_t LineNumber, severity Severity, char *Mes
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintTimecode(FILE *Dest, v3 Timecode)
|
PrintTimecode(FILE *Dest, v4 Timecode)
|
||||||
{
|
{
|
||||||
Colourise(CS_BLUE_BOLD);
|
Colourise(CS_BLUE_BOLD);
|
||||||
if(Timecode.Hours)
|
if(Timecode.Hours)
|
||||||
|
@ -2182,7 +2187,7 @@ PrintTimecode(FILE *Dest, v3 Timecode)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IndexingChronologyError(string *Filename, uint64_t LineNumber, v3 ThisTimecode, v3 PrevTimecode)
|
IndexingChronologyError(string *Filename, uint64_t LineNumber, v4 ThisTimecode, v4 PrevTimecode)
|
||||||
{
|
{
|
||||||
severity Severity = S_ERROR;
|
severity Severity = S_ERROR;
|
||||||
ErrorFilenameAndLineNumber(Filename, LineNumber, Severity, ED_INDEXING);
|
ErrorFilenameAndLineNumber(Filename, LineNumber, Severity, ED_INDEXING);
|
||||||
|
@ -4167,6 +4172,68 @@ LogEdit(edit_type_id EditType, string Lineage, string EntryID, string *EntryTitl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CINERA_HSL_TRANSPARENT_HUE 65535
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int Hue:16;
|
||||||
|
unsigned int Saturation:8;
|
||||||
|
unsigned int Lightness:8;
|
||||||
|
} hsl_colour;
|
||||||
|
|
||||||
|
hsl_colour
|
||||||
|
CharToColour(char Char)
|
||||||
|
{
|
||||||
|
hsl_colour Colour;
|
||||||
|
if(Char >= 'a' && Char <= 'z')
|
||||||
|
{
|
||||||
|
Colour.Hue = (((float)Char - 'a') / ('z' - 'a') * 360);
|
||||||
|
Colour.Saturation = (((float)Char - 'a') / ('z' - 'a') * 26 + 74);
|
||||||
|
}
|
||||||
|
else if(Char >= 'A' && Char <= 'Z')
|
||||||
|
{
|
||||||
|
Colour.Hue = (((float)Char - 'A') / ('Z' - 'A') * 360);
|
||||||
|
Colour.Saturation = (((float)Char - 'A') / ('Z' - 'A') * 26 + 74);
|
||||||
|
}
|
||||||
|
else if(Char >= '0' && Char <= '9')
|
||||||
|
{
|
||||||
|
Colour.Hue = (((float)Char - '0') / ('9' - '0') * 360);
|
||||||
|
Colour.Saturation = (((float)Char - '0') / ('9' - '0') * 26 + 74);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Colour.Hue = 180;
|
||||||
|
Colour.Saturation = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StringToColourHash(hsl_colour *Colour, string String)
|
||||||
|
{
|
||||||
|
Colour->Hue = 0;
|
||||||
|
Colour->Saturation = 0;
|
||||||
|
Colour->Lightness = 74;
|
||||||
|
|
||||||
|
for(int i = 0; i < String.Length; ++i)
|
||||||
|
{
|
||||||
|
Colour->Hue += CharToColour(String.Base[i]).Hue;
|
||||||
|
Colour->Saturation += CharToColour(String.Base[i]).Saturation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Colour->Hue = Colour->Hue % 360;
|
||||||
|
Colour->Saturation = Colour->Saturation % 26 + 74;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsValidHSLColour(hsl_colour Colour)
|
||||||
|
{
|
||||||
|
return (Colour.Hue >= 0 && Colour.Hue < 360)
|
||||||
|
&& (Colour.Saturation >= 0 && Colour.Saturation <= 100)
|
||||||
|
&& (Colour.Lightness >= 0 && Colour.Lightness <= 100);
|
||||||
|
}
|
||||||
|
|
||||||
#define SLASH 1
|
#define SLASH 1
|
||||||
#define NULLTERM 1
|
#define NULLTERM 1
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -4304,7 +4371,7 @@ typedef struct
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
v3 Timecode;
|
v4 Timecode;
|
||||||
int Identifier;
|
int Identifier;
|
||||||
} identifier;
|
} identifier;
|
||||||
|
|
||||||
|
@ -4321,6 +4388,7 @@ typedef struct
|
||||||
{
|
{
|
||||||
string Marker;
|
string Marker;
|
||||||
string WrittenText;
|
string WrittenText;
|
||||||
|
hsl_colour Colour;
|
||||||
} category_info;
|
} category_info;
|
||||||
|
|
||||||
#define CopyString(Dest, DestSize, Format, ...) CopyString_(__LINE__, (Dest), (DestSize), (Format), ##__VA_ARGS__)
|
#define CopyString(Dest, DestSize, Format, ...) CopyString_(__LINE__, (Dest), (DestSize), (Format), ##__VA_ARGS__)
|
||||||
|
@ -4430,7 +4498,7 @@ CopyStringToBuffer_(int LineNumber, buffer *Dest, char *Format, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
DigitsInTimecode(v3 Timecode)
|
DigitsInTimecode(v4 Timecode)
|
||||||
{
|
{
|
||||||
int Result = 0;
|
int Result = 0;
|
||||||
int ColonChar = 1;
|
int ColonChar = 1;
|
||||||
|
@ -4453,7 +4521,7 @@ DigitsInTimecode(v3 Timecode)
|
||||||
|
|
||||||
#define CopyTimecodeToBuffer(Dest, Timecode) CopyTimecodeToBuffer_(__LINE__, Dest, Timecode)
|
#define CopyTimecodeToBuffer(Dest, Timecode) CopyTimecodeToBuffer_(__LINE__, Dest, Timecode)
|
||||||
void
|
void
|
||||||
CopyTimecodeToBuffer_(int LineNumber, buffer *Dest, v3 Timecode)
|
CopyTimecodeToBuffer_(int LineNumber, buffer *Dest, v4 Timecode)
|
||||||
{
|
{
|
||||||
if(DigitsInTimecode(Timecode) + (Dest->Ptr - Dest->Location) >= Dest->Size)
|
if(DigitsInTimecode(Timecode) + (Dest->Ptr - Dest->Location) >= Dest->Size)
|
||||||
{
|
{
|
||||||
|
@ -4715,18 +4783,16 @@ typedef struct
|
||||||
bool Resolving;
|
bool Resolving;
|
||||||
} clash_resolver;
|
} clash_resolver;
|
||||||
|
|
||||||
clash_resolver
|
void
|
||||||
InitClashResolver(void)
|
InitClashResolver(clash_resolver *ClashResolver)
|
||||||
{
|
{
|
||||||
clash_resolver Result = {};
|
ClashResolver->Book[0] = InitBook(sizeof(clash_entry), 8);
|
||||||
Result.Book[0] = InitBook(sizeof(clash_entry), 8);
|
ClashResolver->Book[1] = InitBook(sizeof(clash_entry), 8);
|
||||||
Result.Book[1] = InitBook(sizeof(clash_entry), 8);
|
ClashResolver->Main = &ClashResolver->Book[0];
|
||||||
Result.Main = &Result.Book[0];
|
ClashResolver->Holder = &ClashResolver->Book[1];
|
||||||
Result.Holder = &Result.Book[1];
|
ClashResolver->Chain = InitBookOfPointers(8);
|
||||||
Result.Chain = InitBookOfPointers(8);
|
ClashResolver->ChainStructure = CS_OPEN_ENDED;
|
||||||
Result.ChainStructure = CS_OPEN_ENDED;
|
ClashResolver->Resolving = FALSE;
|
||||||
Result.Resolving = FALSE;
|
|
||||||
return Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -5463,69 +5529,17 @@ InitTemplate(template *Template, string Location, template_type Type)
|
||||||
Template->Metadata.NavBuffer = InitBook(sizeof(navigation_buffer), 4);
|
Template->Metadata.NavBuffer = InitBook(sizeof(navigation_buffer), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
v3
|
v4
|
||||||
V3(int A, int B, int C)
|
V4(int A, int B, int C, int D)
|
||||||
{
|
{
|
||||||
v3 Result = { .A = A, .B = B, .C = C };
|
v4 Result = { .A = A, .B = B, .C = C, .D = D };
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
float
|
||||||
TimecodeToSeconds(v3 Timecode)
|
TimecodeToDottedSeconds(v4 Timecode)
|
||||||
{
|
{
|
||||||
return Timecode.Hours * SECONDS_PER_HOUR + Timecode.Minutes * SECONDS_PER_MINUTE + Timecode.Seconds;
|
return (float)Timecode.Hours * SECONDS_PER_HOUR + (float)Timecode.Minutes * SECONDS_PER_MINUTE + (float)Timecode.Seconds + (float)Timecode.Milliseconds / 1000;
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned int Hue:16;
|
|
||||||
unsigned int Saturation:8;
|
|
||||||
unsigned int Lightness:8;
|
|
||||||
} hsl_colour;
|
|
||||||
|
|
||||||
hsl_colour
|
|
||||||
CharToColour(char Char)
|
|
||||||
{
|
|
||||||
hsl_colour Colour;
|
|
||||||
if(Char >= 'a' && Char <= 'z')
|
|
||||||
{
|
|
||||||
Colour.Hue = (((float)Char - 'a') / ('z' - 'a') * 360);
|
|
||||||
Colour.Saturation = (((float)Char - 'a') / ('z' - 'a') * 26 + 74);
|
|
||||||
}
|
|
||||||
else if(Char >= 'A' && Char <= 'Z')
|
|
||||||
{
|
|
||||||
Colour.Hue = (((float)Char - 'A') / ('Z' - 'A') * 360);
|
|
||||||
Colour.Saturation = (((float)Char - 'A') / ('Z' - 'A') * 26 + 74);
|
|
||||||
}
|
|
||||||
else if(Char >= '0' && Char <= '9')
|
|
||||||
{
|
|
||||||
Colour.Hue = (((float)Char - '0') / ('9' - '0') * 360);
|
|
||||||
Colour.Saturation = (((float)Char - '0') / ('9' - '0') * 26 + 74);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Colour.Hue = 180;
|
|
||||||
Colour.Saturation = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StringToColourHash(hsl_colour *Colour, string String)
|
|
||||||
{
|
|
||||||
Colour->Hue = 0;
|
|
||||||
Colour->Saturation = 0;
|
|
||||||
Colour->Lightness = 74;
|
|
||||||
|
|
||||||
for(int i = 0; i < String.Length; ++i)
|
|
||||||
{
|
|
||||||
Colour->Hue += CharToColour(String.Base[i]).Hue;
|
|
||||||
Colour->Saturation += CharToColour(String.Base[i]).Saturation;
|
|
||||||
}
|
|
||||||
|
|
||||||
Colour->Hue = Colour->Hue % 360;
|
|
||||||
Colour->Saturation = Colour->Saturation % 26 + 74;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
|
@ -8097,7 +8111,7 @@ BuildCredits(string HMMLFilepath, buffer *CreditsMenu, HMML_VideoMetaData *Metad
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_info) *LocalTopics, _memory_book(category_info) *GlobalMedia, _memory_book(category_info) *LocalMedia, string Marker)
|
InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_info) *LocalTopics, _memory_book(category_info) *GlobalMedia, _memory_book(category_info) *LocalMedia, string Marker, hsl_colour *Colour)
|
||||||
{
|
{
|
||||||
medium *Medium = GetMediumFromProject(CurrentProject, Marker);
|
medium *Medium = GetMediumFromProject(CurrentProject, Marker);
|
||||||
|
|
||||||
|
@ -8198,11 +8212,15 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
|
||||||
{
|
{
|
||||||
category_info *Src = GetPlaceInBook(LocalTopics, CategoryCount - 1);
|
category_info *Src = GetPlaceInBook(LocalTopics, CategoryCount - 1);
|
||||||
category_info *Dest = GetPlaceInBook(LocalTopics, CategoryCount);
|
category_info *Dest = GetPlaceInBook(LocalTopics, CategoryCount);
|
||||||
Dest->Marker = Src->Marker;
|
*Dest = *Src;
|
||||||
}
|
}
|
||||||
|
|
||||||
category_info *New = GetPlaceInBook(LocalTopics, CategoryCount);
|
category_info *New = GetPlaceInBook(LocalTopics, CategoryCount);
|
||||||
New->Marker = Marker;
|
New->Marker = Marker;
|
||||||
|
if(Colour)
|
||||||
|
{
|
||||||
|
New->Colour = *Colour;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8213,6 +8231,10 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
|
||||||
{
|
{
|
||||||
category_info *New = GetPlaceInBook(LocalTopics, TopicIndex);
|
category_info *New = GetPlaceInBook(LocalTopics, TopicIndex);
|
||||||
New->Marker = Marker;
|
New->Marker = Marker;
|
||||||
|
if(Colour)
|
||||||
|
{
|
||||||
|
New->Colour = *Colour;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MadeGlobalSpace = FALSE;
|
bool MadeGlobalSpace = FALSE;
|
||||||
|
@ -8236,11 +8258,15 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
|
||||||
{
|
{
|
||||||
category_info *Src = GetPlaceInBook(GlobalTopics, CategoryCount - 1);
|
category_info *Src = GetPlaceInBook(GlobalTopics, CategoryCount - 1);
|
||||||
category_info *Dest = GetPlaceInBook(GlobalTopics, CategoryCount);
|
category_info *Dest = GetPlaceInBook(GlobalTopics, CategoryCount);
|
||||||
Dest->Marker = Src->Marker;
|
*Dest = *Src;
|
||||||
}
|
}
|
||||||
|
|
||||||
category_info *New = GetPlaceInBook(GlobalTopics, CategoryCount);
|
category_info *New = GetPlaceInBook(GlobalTopics, CategoryCount);
|
||||||
New->Marker = Marker;
|
New->Marker = Marker;
|
||||||
|
if(Colour)
|
||||||
|
{
|
||||||
|
New->Colour = *Colour;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8252,6 +8278,10 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
|
||||||
{
|
{
|
||||||
category_info *New = GetPlaceInBook(GlobalTopics, TopicIndex);
|
category_info *New = GetPlaceInBook(GlobalTopics, TopicIndex);
|
||||||
New->Marker = Marker;
|
New->Marker = Marker;
|
||||||
|
if(Colour)
|
||||||
|
{
|
||||||
|
New->Colour = *Colour;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8343,9 +8373,18 @@ BuildCategoryIcons(buffer *CategoryIcons, _memory_book(category_info) *LocalTopi
|
||||||
CopyString(SanitisedMarker, sizeof(SanitisedMarker), "%.*s", (int)This->Marker.Length, This->Marker.Base);
|
CopyString(SanitisedMarker, sizeof(SanitisedMarker), "%.*s", (int)This->Marker.Length, This->Marker.Base);
|
||||||
SanitisePunctuation(SanitisedMarker);
|
SanitisePunctuation(SanitisedMarker);
|
||||||
|
|
||||||
CopyStringToBuffer(CategoryIcons, "<div title=\"%.*s\" class=\"category %s\"></div>",
|
CopyStringToBuffer(CategoryIcons,
|
||||||
|
"<div title=\"%.*s\" class=\"category %s\"",
|
||||||
(int)This->Marker.Length, This->Marker.Base,
|
(int)This->Marker.Length, This->Marker.Base,
|
||||||
SanitisedMarker);
|
SanitisedMarker);
|
||||||
|
if(IsValidHSLColour(This->Colour))
|
||||||
|
{
|
||||||
|
CopyStringToBuffer(CategoryIcons,
|
||||||
|
" data-hue=\"%u\" data-saturation=\"%u%%\"",
|
||||||
|
This->Colour.Hue, This->Colour.Saturation);
|
||||||
|
}
|
||||||
|
CopyStringToBuffer(CategoryIcons,
|
||||||
|
"></div>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8625,7 +8664,7 @@ BuildQuote(memory_book *Strings, quote_info *Info, string Speaker, int ID, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
rc
|
rc
|
||||||
GenerateTopicColours(neighbourhood *N, string Topic)
|
GenerateTopicColours(neighbourhood *N, string Topic, hsl_colour *Dest)
|
||||||
{
|
{
|
||||||
rc Result = RC_SUCCESS;
|
rc Result = RC_SUCCESS;
|
||||||
// NOTE(matt): Stack-string
|
// NOTE(matt): Stack-string
|
||||||
|
@ -8636,6 +8675,15 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
||||||
medium *Medium = GetMediumFromProject(CurrentProject, Topic);
|
medium *Medium = GetMediumFromProject(CurrentProject, Topic);
|
||||||
if(!Medium)
|
if(!Medium)
|
||||||
{
|
{
|
||||||
|
if(StringsMatch(Topic, Wrap0("nullTopic")))
|
||||||
|
{
|
||||||
|
Dest->Hue = CINERA_HSL_TRANSPARENT_HUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringToColourHash(Dest, Topic);
|
||||||
|
}
|
||||||
|
|
||||||
file Topics = {};
|
file Topics = {};
|
||||||
Topics.Path = 0;
|
Topics.Path = 0;
|
||||||
Topics.Buffer.ID = BID_TOPICS;
|
Topics.Buffer.ID = BID_TOPICS;
|
||||||
|
@ -8701,10 +8749,8 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hsl_colour Colour;
|
|
||||||
StringToColourHash(&Colour, Topic);
|
|
||||||
WriteToFile(Topics.Handle, ".category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); }\n",
|
WriteToFile(Topics.Handle, ".category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); }\n",
|
||||||
SanitisedTopic, Colour.Hue, Colour.Saturation, Colour.Lightness, Colour.Hue, Colour.Saturation, Colour.Lightness);
|
SanitisedTopic, Dest->Hue, Dest->Saturation, Dest->Lightness, Dest->Hue, Dest->Saturation, Dest->Lightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG_MEM
|
#if DEBUG_MEM
|
||||||
|
@ -10816,9 +10862,9 @@ HMMLOutputLocationIs(neighbourhood *N, char *OutputLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TimecodeIs(v3 Timecode, int Hours, int Minutes, int Seconds)
|
TimecodeIs(v4 Timecode, int Hours, int Minutes, int Seconds, int Milliseconds)
|
||||||
{
|
{
|
||||||
return Timecode.Hours == Hours && Timecode.Minutes == Minutes && Timecode.Seconds == Seconds;
|
return Timecode.Hours == Hours && Timecode.Minutes == Minutes && Timecode.Seconds == Seconds && Timecode.Milliseconds == Milliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc
|
rc
|
||||||
|
@ -10829,14 +10875,14 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
bool *HasQuoteMenu, bool *HasReferenceMenu, bool *HasFilterMenu, bool *RequiresCineraJS,
|
bool *HasQuoteMenu, bool *HasReferenceMenu, bool *HasFilterMenu, bool *RequiresCineraJS,
|
||||||
int *QuoteIdentifier, int *RefIdentifier,
|
int *QuoteIdentifier, int *RefIdentifier,
|
||||||
_memory_book(category_info) *Topics, _memory_book(category_info) *Media,
|
_memory_book(category_info) *Topics, _memory_book(category_info) *Media,
|
||||||
HMML_Timestamp *Timestamp, v3 *PreviousTimecode)
|
HMML_Timestamp *Timestamp, v4 *PreviousTimecode)
|
||||||
{
|
{
|
||||||
MEM_TEST_TOP();
|
MEM_TEST_TOP();
|
||||||
// TODO(matt): Introduce and use a SystemError() in here
|
// TODO(matt): Introduce and use a SystemError() in here
|
||||||
rc Result = RC_SUCCESS;
|
rc Result = RC_SUCCESS;
|
||||||
|
|
||||||
v3 Timecode = V3(Timestamp->h, Timestamp->m, Timestamp->s);
|
v4 Timecode = V4(Timestamp->h, Timestamp->m, Timestamp->s, Timestamp->ms);
|
||||||
if(TimecodeToSeconds(Timecode) >= TimecodeToSeconds(*PreviousTimecode))
|
if(TimecodeToDottedSeconds(Timecode) >= TimecodeToDottedSeconds(*PreviousTimecode))
|
||||||
{
|
{
|
||||||
*PreviousTimecode = Timecode;
|
*PreviousTimecode = Timecode;
|
||||||
|
|
||||||
|
@ -10857,8 +10903,8 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
|
|
||||||
|
|
||||||
CopyStringToBuffer(&IndexBuffers->Header,
|
CopyStringToBuffer(&IndexBuffers->Header,
|
||||||
" <div data-timestamp=\"%d\"",
|
" <div data-timestamp=\"%.3f\"",
|
||||||
TimecodeToSeconds(Timecode));
|
TimecodeToDottedSeconds(Timecode));
|
||||||
|
|
||||||
CopyStringToBuffer(&IndexBuffers->Class,
|
CopyStringToBuffer(&IndexBuffers->Class,
|
||||||
" class=\"marker");
|
" class=\"marker");
|
||||||
|
@ -10894,7 +10940,7 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
{
|
{
|
||||||
*HasFilterMenu = TRUE;
|
*HasFilterMenu = TRUE;
|
||||||
}
|
}
|
||||||
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("authored"));
|
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("authored"), NULL);
|
||||||
hsl_colour AuthorColour;
|
hsl_colour AuthorColour;
|
||||||
StringToColourHash(&AuthorColour, Author);
|
StringToColourHash(&AuthorColour, Author);
|
||||||
// TODO(matt): That EDITION_NETWORK site database API-polling stuff
|
// TODO(matt): That EDITION_NETWORK site database API-polling stuff
|
||||||
|
@ -10919,14 +10965,15 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
HMML_MarkerType Type = Timestamp->markers[MarkerIndex].type;
|
HMML_MarkerType Type = Timestamp->markers[MarkerIndex].type;
|
||||||
if(Type == HMML_CATEGORY)
|
if(Type == HMML_CATEGORY)
|
||||||
{
|
{
|
||||||
Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker));
|
hsl_colour TopicColour = {};
|
||||||
|
Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
|
||||||
if(Result == RC_SUCCESS)
|
if(Result == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!*HasFilterMenu)
|
if(!*HasFilterMenu)
|
||||||
{
|
{
|
||||||
*HasFilterMenu = TRUE;
|
*HasFilterMenu = TRUE;
|
||||||
}
|
}
|
||||||
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker));
|
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
|
||||||
CopyStringToBuffer(&IndexBuffers->Text, "%.*s", (int)StringLength(Readable), InPtr);
|
CopyStringToBuffer(&IndexBuffers->Text, "%.*s", (int)StringLength(Readable), InPtr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -11098,14 +11145,14 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
if(Result == RC_SUCCESS)
|
if(Result == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&MenuBuffers->Quote,
|
CopyStringToBuffer(&MenuBuffers->Quote,
|
||||||
" <a target=\"_blank\" class=\"ref\" href=\"https://dev.abaines.me.uk/quotes/%.*s/%d\">\n"
|
" <a target=\"_blank\" data-id=\"&#%d;\" class=\"ref\" href=\"https://dev.abaines.me.uk/quotes/%.*s/%d\">\n"
|
||||||
" <span data-id=\"&#%d;\">\n"
|
" <span>\n"
|
||||||
" <span class=\"ref_content\">\n"
|
" <span class=\"ref_content\">\n"
|
||||||
" <div class=\"source\">Quote %d</div>\n"
|
" <div class=\"source\">Quote %d</div>\n"
|
||||||
" <div class=\"ref_title\">",
|
" <div class=\"ref_title\">",
|
||||||
|
*QuoteIdentifier,
|
||||||
(int)QuoteUsername.Length, QuoteUsername.Base,
|
(int)QuoteUsername.Length, QuoteUsername.Base,
|
||||||
Timestamp->quote.id,
|
Timestamp->quote.id,
|
||||||
*QuoteIdentifier,
|
|
||||||
Timestamp->quote.id);
|
Timestamp->quote.id);
|
||||||
|
|
||||||
CopyStringToBufferHTMLSafe(&MenuBuffers->Quote, QuoteInfo.Text);
|
CopyStringToBufferHTMLSafe(&MenuBuffers->Quote, QuoteInfo.Text);
|
||||||
|
@ -11115,10 +11162,10 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
" <div class=\"quote_byline\">—%.*s, %.*s</div>\n"
|
" <div class=\"quote_byline\">—%.*s, %.*s</div>\n"
|
||||||
" </span>\n"
|
" </span>\n"
|
||||||
" <div class=\"ref_indices\">\n"
|
" <div class=\"ref_indices\">\n"
|
||||||
" <span data-timestamp=\"%d\" class=\"timecode\"><span class=\"ref_index\">[&#%d;]</span><span class=\"time\">",
|
" <span data-timestamp=\"%.3f\" class=\"timecode\"><span class=\"ref_index\">[&#%d;]</span><span class=\"time\">",
|
||||||
(int)QuoteUsername.Length, QuoteUsername.Base,
|
(int)QuoteUsername.Length, QuoteUsername.Base,
|
||||||
(int)DateString.Length, DateString.Base, // TODO(matt): Convert Unixtime to date-string
|
(int)DateString.Length, DateString.Base, // TODO(matt): Convert Unixtime to date-string
|
||||||
TimecodeToSeconds(Timecode),
|
TimecodeToDottedSeconds(Timecode),
|
||||||
*QuoteIdentifier);
|
*QuoteIdentifier);
|
||||||
CopyTimecodeToBuffer(&MenuBuffers->Quote, Timecode);
|
CopyTimecodeToBuffer(&MenuBuffers->Quote, Timecode);
|
||||||
CopyStringToBuffer(&MenuBuffers->Quote, "</span></span>\n"
|
CopyStringToBuffer(&MenuBuffers->Quote, "</span></span>\n"
|
||||||
|
@ -11142,7 +11189,7 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
|
|
||||||
if(Result == RC_SUCCESS)
|
if(Result == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"%d\": \"", TimecodeToSeconds(Timecode));
|
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"%.3f\": \"", TimecodeToDottedSeconds(Timecode));
|
||||||
if(Timestamp->quote.present && !Timestamp->text[0])
|
if(Timestamp->quote.present && !Timestamp->text[0])
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\u201C");
|
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\u201C");
|
||||||
|
@ -11157,14 +11204,15 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
|
|
||||||
while(MarkerIndex < Timestamp->marker_count)
|
while(MarkerIndex < Timestamp->marker_count)
|
||||||
{
|
{
|
||||||
Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker));
|
hsl_colour TopicColour = {};
|
||||||
|
Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
|
||||||
if(Result == RC_SUCCESS)
|
if(Result == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!*HasFilterMenu)
|
if(!*HasFilterMenu)
|
||||||
{
|
{
|
||||||
*HasFilterMenu = TRUE;
|
*HasFilterMenu = TRUE;
|
||||||
}
|
}
|
||||||
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker));
|
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
|
||||||
++MarkerIndex;
|
++MarkerIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -11177,10 +11225,11 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
{
|
{
|
||||||
if(LocalTopics.ItemCount == 0)
|
if(LocalTopics.ItemCount == 0)
|
||||||
{
|
{
|
||||||
Result = GenerateTopicColours(N, Wrap0("nullTopic"));
|
hsl_colour TopicColour = {};
|
||||||
|
Result = GenerateTopicColours(N, Wrap0("nullTopic"), &TopicColour);
|
||||||
if(Result == RC_SUCCESS)
|
if(Result == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("nullTopic"));
|
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("nullTopic"), &TopicColour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11188,7 +11237,7 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
|
||||||
{
|
{
|
||||||
if(LocalMedia.ItemCount == 0)
|
if(LocalMedia.ItemCount == 0)
|
||||||
{
|
{
|
||||||
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, DefaultMedium->ID);
|
InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, DefaultMedium->ID, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildTimestampClass(&IndexBuffers->Class, &LocalTopics, &LocalMedia, DefaultMedium->ID);
|
BuildTimestampClass(&IndexBuffers->Class, &LocalTopics, &LocalMedia, DefaultMedium->ID);
|
||||||
|
@ -11831,7 +11880,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
Print(stdout, "\n\n --- Entering Timestamps Loop ---\n\n\n\n");
|
Print(stdout, "\n\n --- Entering Timestamps Loop ---\n\n\n\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
v3 PreviousTimecode = {};
|
v4 PreviousTimecode = {};
|
||||||
for(int TimestampIndex = 0; TimestampIndex < HMML.timestamp_count; ++TimestampIndex)
|
for(int TimestampIndex = 0; TimestampIndex < HMML.timestamp_count; ++TimestampIndex)
|
||||||
{
|
{
|
||||||
// TODO(matt): Thoroughly test this reorganisation
|
// TODO(matt): Thoroughly test this reorganisation
|
||||||
|
@ -11918,7 +11967,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
{
|
{
|
||||||
identifier *ThisIdentifier = GetPlaceInBook(&This->Identifier, j);
|
identifier *ThisIdentifier = GetPlaceInBook(&This->Identifier, j);
|
||||||
CopyStringToBuffer(&MenuBuffers.Reference,
|
CopyStringToBuffer(&MenuBuffers.Reference,
|
||||||
"<span data-timestamp=\"%d\" class=\"timecode\"><span class=\"ref_index\">[%d]</span><span class=\"time\">", TimecodeToSeconds(ThisIdentifier->Timecode), ThisIdentifier->Identifier);
|
"<span data-timestamp=\"%.3f\" class=\"timecode\"><span class=\"ref_index\">[%d]</span><span class=\"time\">", TimecodeToDottedSeconds(ThisIdentifier->Timecode), ThisIdentifier->Identifier);
|
||||||
CopyTimecodeToBuffer(&MenuBuffers.Reference, ThisIdentifier->Timecode);
|
CopyTimecodeToBuffer(&MenuBuffers.Reference, ThisIdentifier->Timecode);
|
||||||
CopyStringToBuffer(&MenuBuffers.Reference, "</span></span>");
|
CopyStringToBuffer(&MenuBuffers.Reference, "</span></span>");
|
||||||
}
|
}
|
||||||
|
@ -11952,14 +12001,30 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
CopyStringToBuffer(&MenuBuffers.Filter,
|
CopyStringToBuffer(&MenuBuffers.Filter,
|
||||||
"\"></span>\n"
|
"\"></span>\n"
|
||||||
" <div class=\"filter_container\">\n"
|
" <div class=\"filter_container\">\n"
|
||||||
" <div class=\"filter_mode exclusive\">Filter mode: </div>\n"
|
" <div class=\"filter_header\">\n"
|
||||||
" <div class=\"filters\">\n");
|
" <div class=\"filter_mode exclusive\">Filter mode: </div>\n"
|
||||||
|
" <div class=\"filter_titles\">\n");
|
||||||
|
|
||||||
if(Topics.ItemCount > 0)
|
if(Topics.ItemCount > 0)
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&MenuBuffers.Filter,
|
CopyStringToBuffer(&MenuBuffers.Filter,
|
||||||
" <div class=\"filter_topics\">\n"
|
" <div class=\"filter_title\">Topics</div>\n");
|
||||||
" <div class=\"filter_title\">Topics</div>\n");
|
}
|
||||||
|
if(Media.ItemCount > 0)
|
||||||
|
{
|
||||||
|
CopyStringToBuffer(&MenuBuffers.Filter,
|
||||||
|
" <div class=\"filter_title\">Media</div>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyStringToBuffer(&MenuBuffers.Filter,
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <div class=\"filters\">\n");
|
||||||
|
|
||||||
|
if(Topics.ItemCount > 0)
|
||||||
|
{
|
||||||
|
CopyStringToBuffer(&MenuBuffers.Filter,
|
||||||
|
" <div class=\"filter_topics\">\n");
|
||||||
for(int i = 0; i < Topics.ItemCount; ++i)
|
for(int i = 0; i < Topics.ItemCount; ++i)
|
||||||
{
|
{
|
||||||
category_info *This = GetPlaceInBook(&Topics, i);
|
category_info *This = GetPlaceInBook(&Topics, i);
|
||||||
|
@ -11970,13 +12035,21 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
|
|
||||||
bool NullTopic = StringsMatch(This->Marker, Wrap0("nullTopic"));
|
bool NullTopic = StringsMatch(This->Marker, Wrap0("nullTopic"));
|
||||||
CopyStringToBuffer(&MenuBuffers.FilterTopics,
|
CopyStringToBuffer(&MenuBuffers.FilterTopics,
|
||||||
" <div %sclass=\"filter_content %s\">\n"
|
" <div%s class=\"filter_content %s\">\n"
|
||||||
" <span class=\"icon category %s\"></span><span class=\"cineraText\">%.*s</span>\n"
|
" <span class=\"icon category %s\"",
|
||||||
" </div>\n",
|
|
||||||
|
|
||||||
NullTopic ? "title=\"Timestamps that don't fit into the above topic(s) may be filtered using this pseudo-topic\" " : "",
|
NullTopic ? " title=\"Timestamps that don't fit into the above topic(s) may be filtered using this pseudo-topic\"" : "",
|
||||||
SanitisedMarker,
|
|
||||||
SanitisedMarker,
|
SanitisedMarker,
|
||||||
|
SanitisedMarker);
|
||||||
|
if(IsValidHSLColour(This->Colour))
|
||||||
|
{
|
||||||
|
CopyStringToBuffer(&MenuBuffers.FilterTopics,
|
||||||
|
" data-hue=\"%u\" data-saturation=\"%u%%\"",
|
||||||
|
This->Colour.Hue, This->Colour.Saturation);
|
||||||
|
}
|
||||||
|
CopyStringToBuffer(&MenuBuffers.FilterTopics,
|
||||||
|
"></span><span class=\"cineraText\">%.*s</span>\n"
|
||||||
|
" </div>\n",
|
||||||
NullTopic ? (int)sizeof("(null topic)")-1 : (int)This->Marker.Length,
|
NullTopic ? (int)sizeof("(null topic)")-1 : (int)This->Marker.Length,
|
||||||
NullTopic ? "(null topic)" : This->Marker.Base);
|
NullTopic ? "(null topic)" : This->Marker.Base);
|
||||||
}
|
}
|
||||||
|
@ -11988,8 +12061,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
if(Media.ItemCount > 0)
|
if(Media.ItemCount > 0)
|
||||||
{
|
{
|
||||||
CopyStringToBuffer(&MenuBuffers.FilterMedia,
|
CopyStringToBuffer(&MenuBuffers.FilterMedia,
|
||||||
" <div class=\"filter_media\">\n"
|
" <div class=\"filter_media\">\n");
|
||||||
" <div class=\"filter_title\">Media</div>\n");
|
|
||||||
for(int i = 0; i < Media.ItemCount; ++i)
|
for(int i = 0; i < Media.ItemCount; ++i)
|
||||||
{
|
{
|
||||||
category_info *This = GetPlaceInBook(&Media, i);
|
category_info *This = GetPlaceInBook(&Media, i);
|
||||||
|
@ -12056,7 +12128,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
"\n"
|
"\n"
|
||||||
" <h2>Global Keys</h2>\n"
|
" <h2>Global Keys</h2>\n"
|
||||||
" <span class=\"help_key\">[</span>, <span class=\"help_key\"><</span> / <span class=\"help_key\">]</span>, <span class=\"help_key\">></span> <span class=\"help_text\">Jump to previous / next episode</span><br>\n"
|
" <span class=\"help_key\">[</span>, <span class=\"help_key\"><</span> / <span class=\"help_key\">]</span>, <span class=\"help_key\">></span> <span class=\"help_text\">Jump to previous / next episode</span><br>\n"
|
||||||
" <span class=\"help_key\">W</span>, <span class=\"help_key\">K</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">J</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next marker</span><br>\n"
|
" <span class=\"help_key\">W</span>, <span class=\"help_key\">K</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">J</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next timestamp</span><br>\n"
|
||||||
" <span class=\"help_key\">t</span> / <span class=\"help_key\">T</span> <span class=\"help_text\">Toggle theatre / SUPERtheatre mode</span><br>\n"
|
" <span class=\"help_key\">t</span> / <span class=\"help_key\">T</span> <span class=\"help_text\">Toggle theatre / SUPERtheatre mode</span><br>\n"
|
||||||
" <span class=\"help_key%s\">V</span> <span class=\"help_text%s\">Revert filter to original state</span> <span class=\"help_key\">Y</span> <span class=\"help_text\">Select link (requires manual Ctrl-c)</span>\n",
|
" <span class=\"help_key%s\">V</span> <span class=\"help_text%s\">Revert filter to original state</span> <span class=\"help_key\">Y</span> <span class=\"help_text\">Select link (requires manual Ctrl-c)</span>\n",
|
||||||
|
|
||||||
|
@ -12078,15 +12150,15 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
|
|
||||||
CopyStringToBuffer(&PlayerBuffers.Menus,
|
CopyStringToBuffer(&PlayerBuffers.Menus,
|
||||||
"\n"
|
"\n"
|
||||||
" <h2>In-Menu Movement</h2>\n"
|
" <h2>In-Menu and <span class=\"help_title_key help_custom_index\">Index</span> Controls</h2>\n"
|
||||||
" <div class=\"help_paragraph\">\n"
|
" <div class=\"help_paragraph\">\n"
|
||||||
" <div class=\"key_block\">\n"
|
" <div class=\"key_block\">\n"
|
||||||
" <div>\n"
|
" <div>\n"
|
||||||
" <span class=\"help_key\">a</span>\n"
|
" <span class=\"help_key\">a</span>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <div>\n"
|
" <div>\n"
|
||||||
" <span class=\"help_key\">w</span><br>\n"
|
" <span class=\"help_key help_custom_index\">w</span><br>\n"
|
||||||
" <span class=\"help_key\">s</span>\n"
|
" <span class=\"help_key help_custom_index\">s</span>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <div>\n"
|
" <div>\n"
|
||||||
" <span class=\"help_key\">d</span>\n"
|
" <span class=\"help_key\">d</span>\n"
|
||||||
|
@ -12094,8 +12166,8 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <div class=\"key_block\">\n"
|
" <div class=\"key_block\">\n"
|
||||||
" <span class=\"help_key\">h</span>\n"
|
" <span class=\"help_key\">h</span>\n"
|
||||||
" <span class=\"help_key\">j</span>\n"
|
" <span class=\"help_key help_custom_index\">j</span>\n"
|
||||||
" <span class=\"help_key\">k</span>\n"
|
" <span class=\"help_key help_custom_index\">k</span>\n"
|
||||||
" <span class=\"help_key\">l</span>\n"
|
" <span class=\"help_key\">l</span>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <div class=\"key_block\">\n"
|
" <div class=\"key_block\">\n"
|
||||||
|
@ -12110,25 +12182,29 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
" <span class=\"help_key\">→</span>\n"
|
" <span class=\"help_key\">→</span>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
|
" </div><br>\n"
|
||||||
|
" <div class=\"help_paragraph\">\n"
|
||||||
|
" <span class=\"help_key help_custom_index\">Esc</span> <span class=\"help_text\">Close menu / unfocus timestamp</span>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <br>\n");
|
" <br>\n");
|
||||||
|
|
||||||
CopyStringToBuffer(&PlayerBuffers.Menus,
|
CopyStringToBuffer(&PlayerBuffers.Menus,
|
||||||
" <h2>%sQuotes %sand%s References%s Menus%s</h2>\n"
|
" <h2>%sQuotes %sand%s References%s Menus and%s Index</h2>\n"
|
||||||
" <span class=\"help_key word%s\">Enter</span> <span class=\"help_text%s\">Jump to timecode</span><br>\n",
|
" <span class=\"help_key word\">Enter</span> <span class=\"help_text\">Jump to timestamp</span><br>\n",
|
||||||
// Q R
|
// Q R
|
||||||
//
|
//
|
||||||
// 0 0 <h2><span off>Quotes and References Menus</span></h2>
|
// 0 0 <h2><span off>Quotes and References Menus and</span> Index</h2>
|
||||||
// 0 1 <h2><span off>Quotes and</span> References Menus</h2>
|
// 0 1 <h2><span off>Quotes and</span> References Menus and Index</h2>
|
||||||
// 1 0 <h2>Quotes <span off>and References</span> Menus</h2>
|
// 1 0 <h2>Quotes <span off>and References</span> Menus and Index</h2>
|
||||||
// 1 1 <h2>Quotes and References Menus</h2>
|
// 1 1 <h2>Quotes and References Menus and Index</h2>
|
||||||
|
|
||||||
HasQuoteMenu ? "" : "<span class=\"unavailable\">",
|
HasQuoteMenu ? "" : "<span class=\"unavailable\">",
|
||||||
HasQuoteMenu && !HasReferenceMenu ? "<span class=\"unavailable\">" : "",
|
HasQuoteMenu && !HasReferenceMenu ? "<span class=\"unavailable\">" : "",
|
||||||
!HasQuoteMenu && HasReferenceMenu ? "</span>" : "",
|
!HasQuoteMenu && HasReferenceMenu ? "</span>" : "",
|
||||||
HasQuoteMenu && !HasReferenceMenu ? "</span>" : "",
|
HasQuoteMenu && !HasReferenceMenu ? "</span>" : "",
|
||||||
!HasQuoteMenu && !HasReferenceMenu ? "</span>" : "",
|
!HasQuoteMenu && !HasReferenceMenu ? "</span>" : "");
|
||||||
HasQuoteMenu || HasReferenceMenu ? "" : " unavailable", HasQuoteMenu || HasReferenceMenu ? "" : " unavailable");
|
//HasQuoteMenu || HasReferenceMenu ? "" : " unavailable",
|
||||||
|
//HasQuoteMenu || HasReferenceMenu ? "" : " unavailable");
|
||||||
|
|
||||||
CopyStringToBuffer(&PlayerBuffers.Menus,
|
CopyStringToBuffer(&PlayerBuffers.Menus,
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -12371,6 +12447,19 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
|
||||||
|
|
||||||
CopyStringToBuffer(&CollationBuffers->Player, "<div class=\"cinera\">\n"
|
CopyStringToBuffer(&CollationBuffers->Player, "<div class=\"cinera\">\n"
|
||||||
" ");
|
" ");
|
||||||
|
|
||||||
|
asset *JSClear = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_CLEAR].Filename), ASSET_JS);
|
||||||
|
ConstructResolvedAssetURL(&URL, JSClear, PAGE_PLAYER);
|
||||||
|
CopyStringToBuffer(&CollationBuffers->Player,
|
||||||
|
"<script type=\"text/javascript\" src=\"%s",
|
||||||
|
URL.Location);
|
||||||
|
DeclaimBuffer(&URL);
|
||||||
|
PushAssetLandmark(&CollationBuffers->Player, JSClear, PAGE_PLAYER, 0);
|
||||||
|
CopyStringToBuffer(&CollationBuffers->Player,
|
||||||
|
"\"></script>");
|
||||||
|
|
||||||
|
CopyStringToBuffer(&CollationBuffers->Player, "\n"
|
||||||
|
" ");
|
||||||
CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER);
|
CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER);
|
||||||
CopyStringToBuffer(&CollationBuffers->Player, "\n"
|
CopyStringToBuffer(&CollationBuffers->Player, "\n"
|
||||||
" ");
|
" ");
|
||||||
|
@ -18234,7 +18323,8 @@ main(int ArgC, char **Args)
|
||||||
CollationBuffers.Search.ID = BID_COLLATION_BUFFERS_SEARCH; // NOTE(matt): Allocated by SearchToBuffer()
|
CollationBuffers.Search.ID = BID_COLLATION_BUFFERS_SEARCH; // NOTE(matt): Allocated by SearchToBuffer()
|
||||||
|
|
||||||
memory_book TokensList = InitBook(sizeof(tokens), 8);
|
memory_book TokensList = InitBook(sizeof(tokens), 8);
|
||||||
clash_resolver ClashResolver = InitClashResolver();
|
clash_resolver ClashResolver = {};
|
||||||
|
InitClashResolver(&ClashResolver);
|
||||||
template BespokeTemplate = {};
|
template BespokeTemplate = {};
|
||||||
neighbourhood Neighbourhood = {};
|
neighbourhood Neighbourhood = {};
|
||||||
|
|
||||||
|
|
|
@ -540,6 +540,16 @@ ul.cineraNavPlain li.current > a {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container .help_custom_index {
|
||||||
|
background-color: #159 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cineraHelp .help_container h2 .help_title_key {
|
||||||
|
padding: .2em;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.cineraHelp .help_container .help_key {
|
.cineraHelp .help_container .help_key {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
font-family: Inconsolata;
|
font-family: Inconsolata;
|
||||||
|
@ -614,12 +624,11 @@ 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 */
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
z-index: 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .refs,
|
.cineraMenus > .menu .refs,
|
||||||
|
@ -639,7 +648,7 @@ ul.cineraNavPlain li.current > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu .visible {
|
.cineraMenus > .menu .visible {
|
||||||
display: block;
|
z-index: 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu > .refs .ref {
|
.cineraMenus > .menu > .refs .ref {
|
||||||
|
@ -737,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;
|
||||||
|
@ -756,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;
|
||||||
|
|
|
@ -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 baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
|
||||||
|
|
||||||
var originalTextContent = {
|
|
||||||
TitleQuotes: null,
|
|
||||||
TitleReferences: null,
|
|
||||||
TitleCredits: null,
|
|
||||||
EpisodePrev: null,
|
|
||||||
EpisodeNext: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
var menuState = [];
|
|
||||||
var titleBar = cinera.querySelector(".cineraMenus");
|
|
||||||
var quotesMenu = null;
|
|
||||||
var referencesMenu = null;
|
|
||||||
var filterMenu = null;
|
|
||||||
var viewsMenu = null;
|
|
||||||
var linkMenu = null;
|
|
||||||
var creditsMenu = null;
|
|
||||||
var sourceMenus = null;
|
|
||||||
var helpButton = null;
|
|
||||||
var helpDocumentation = null;
|
|
||||||
|
|
||||||
// NOTE(matt): One set of markers per page. There is code to support multiple, which we may want to extend everywhere
|
|
||||||
var MarkersContainer = cinera.querySelector(".markers_container");
|
|
||||||
|
|
||||||
var views = {
|
|
||||||
REGULAR: 0,
|
|
||||||
THEATRE: 1,
|
|
||||||
SUPERTHEATRE: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
var devices = {
|
|
||||||
DESKTOP: 0,
|
|
||||||
MOBILE: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
var CineraProps = {
|
var CineraProps = {
|
||||||
C: null,
|
C: null,
|
||||||
V: views.REGULAR,
|
V: views.REGULAR,
|
||||||
|
@ -57,275 +22,19 @@ var CineraProps = {
|
||||||
};
|
};
|
||||||
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
||||||
|
|
||||||
if(titleBar)
|
|
||||||
{
|
|
||||||
quotesMenu = titleBar.querySelector(".quotes_container");
|
|
||||||
if(quotesMenu)
|
|
||||||
{
|
|
||||||
originalTextContent.TitleQuotes = quotesMenu.previousElementSibling.textContent;
|
|
||||||
menuState.push(quotesMenu);
|
|
||||||
var quoteItems = quotesMenu.querySelectorAll(".ref");
|
|
||||||
if(quoteItems)
|
|
||||||
{
|
|
||||||
for(var i = 0; i < quoteItems.length; ++i)
|
|
||||||
{
|
|
||||||
quoteItems[i].addEventListener("mouseenter", function(ev) {
|
|
||||||
mouseOverQuotes(this);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var quoteTimecodes = quotesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
|
|
||||||
for (var i = 0; i < quoteTimecodes.length; ++i) {
|
|
||||||
quoteTimecodes[i].addEventListener("click", function(ev) {
|
|
||||||
if (player) {
|
|
||||||
var time = ev.currentTarget.getAttribute("data-timestamp");
|
|
||||||
mouseSkipToTimecode(player, time, ev);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var lastFocusedQuote = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
referencesMenu = titleBar.querySelector(".references_container");
|
|
||||||
if(referencesMenu)
|
|
||||||
{
|
|
||||||
originalTextContent.TitleReferences = referencesMenu.previousElementSibling.textContent;
|
|
||||||
menuState.push(referencesMenu);
|
|
||||||
var referenceItems = referencesMenu.querySelectorAll(".ref");
|
|
||||||
if(referenceItems)
|
|
||||||
{
|
|
||||||
for(var i = 0; i < referenceItems.length; ++i)
|
|
||||||
{
|
|
||||||
referenceItems[i].addEventListener("mouseenter", function(ev) {
|
|
||||||
mouseOverReferences(this);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
var lastFocusedReference = null;
|
|
||||||
var lastFocusedIdentifier = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var refTimecodes = referencesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
|
|
||||||
for (var i = 0; i < refTimecodes.length; ++i) {
|
|
||||||
refTimecodes[i].addEventListener("click", function(ev) {
|
|
||||||
if (player) {
|
|
||||||
var time = ev.currentTarget.getAttribute("data-timestamp");
|
|
||||||
mouseSkipToTimecode(player, time, ev);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(referencesMenu || quotesMenu)
|
|
||||||
{
|
|
||||||
var refSources = titleBar.querySelectorAll(".refs .ref"); // This is for both quotes and refs
|
|
||||||
for (var i = 0; i < refSources.length; ++i) {
|
|
||||||
refSources[i].addEventListener("click", function(ev) {
|
|
||||||
if (player) {
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filterMenu = titleBar.querySelector(".filter_container");
|
|
||||||
if(filterMenu)
|
|
||||||
{
|
|
||||||
menuState.push(filterMenu);
|
|
||||||
var lastFocusedCategory = null;
|
|
||||||
var lastFocusedTopic = null;
|
|
||||||
var lastFocusedMedium = null;
|
|
||||||
|
|
||||||
var filter = filterMenu.parentNode;
|
|
||||||
|
|
||||||
var filterModeElement = filter.querySelector(".filter_mode");
|
|
||||||
filterModeElement.addEventListener("click", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
toggleFilterMode();
|
|
||||||
});
|
|
||||||
|
|
||||||
var filterMode = filterModeElement.classList[1];
|
|
||||||
var filterItems = filter.querySelectorAll(".filter_content");
|
|
||||||
|
|
||||||
var filterInitState = new Object();
|
|
||||||
var filterState = new Object();
|
|
||||||
for(var i = 0; i < filterItems.length; ++i)
|
|
||||||
{
|
|
||||||
filterItems[i].addEventListener("mouseenter", function(ev) {
|
|
||||||
navigateFilter(this);
|
|
||||||
})
|
|
||||||
|
|
||||||
filterItems[i].addEventListener("click", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
filterItemToggle(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
var filterItemName = filterItems[i].classList.item(1);
|
|
||||||
if(filterItems[i].parentNode.classList.contains("filter_topics"))
|
|
||||||
{
|
|
||||||
filterInitState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
|
|
||||||
filterState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filterInitState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
|
|
||||||
filterState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewsMenu = titleBar.querySelector(".views");
|
|
||||||
if(viewsMenu && !CineraProps.IsMobile)
|
|
||||||
{
|
|
||||||
menuState.push(viewsMenu);
|
|
||||||
var viewsContainer = viewsMenu.querySelector(".views_container");
|
|
||||||
viewsMenu.addEventListener("mouseenter", function(ev) {
|
|
||||||
handleMouseOverViewsMenu();
|
|
||||||
});
|
|
||||||
viewsMenu.addEventListener("mouseleave", function(ev) {
|
|
||||||
viewsContainer.style.display = "none";
|
|
||||||
});
|
|
||||||
|
|
||||||
var viewItems = viewsMenu.querySelectorAll(".view");
|
|
||||||
for(var i = 0; i < viewItems.length; ++i)
|
|
||||||
{
|
|
||||||
viewItems[i].addEventListener("click", function(ev) {
|
|
||||||
switch(this.getAttribute("data-id"))
|
|
||||||
{
|
|
||||||
case "regular":
|
|
||||||
case "theatre":
|
|
||||||
{
|
|
||||||
toggleTheatreMode();
|
|
||||||
} break;
|
|
||||||
case "super":
|
|
||||||
{
|
|
||||||
toggleSuperTheatreMode();
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
linkMenu = titleBar.querySelector(".link_container");
|
|
||||||
linkTimestamp = true;
|
|
||||||
if(linkMenu)
|
|
||||||
{
|
|
||||||
menuState.push(linkMenu);
|
|
||||||
|
|
||||||
var linkMode = linkMenu.querySelector("#cineraLinkMode");
|
|
||||||
var link = linkMenu.querySelector("#cineraLink");
|
|
||||||
|
|
||||||
linkMode.addEventListener("click", function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
toggleLinkMode(linkMode, link);
|
|
||||||
});
|
|
||||||
|
|
||||||
link.addEventListener("click", function(ev) {
|
|
||||||
CopyToClipboard(link);
|
|
||||||
toggleMenuVisibility(linkMenu);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
creditsMenu = titleBar.querySelector(".credits_container");
|
|
||||||
if(creditsMenu)
|
|
||||||
{
|
|
||||||
originalTextContent.TitleCredits = creditsMenu.previousElementSibling.textContent;
|
|
||||||
menuState.push(creditsMenu);
|
|
||||||
var lastFocusedCreditItem = null;
|
|
||||||
|
|
||||||
var creditItems = creditsMenu.querySelectorAll(".person, .support");
|
|
||||||
for(var i = 0; i < creditItems.length; ++i)
|
|
||||||
{
|
|
||||||
creditItems[i].addEventListener("mouseenter", function(ev) {
|
|
||||||
if(this != lastFocusedCreditItem)
|
|
||||||
{
|
|
||||||
lastFocusedCreditItem.classList.remove("focused");
|
|
||||||
unfocusSprite(lastFocusedCreditItem);
|
|
||||||
if(lastFocusedCreditItem.classList.contains("support"))
|
|
||||||
{
|
|
||||||
setSpriteLightness(lastFocusedCreditItem.firstChild);
|
|
||||||
}
|
|
||||||
lastFocusedCreditItem = this;
|
|
||||||
focusedElement = lastFocusedCreditItem;
|
|
||||||
focusedElement.classList.add("focused");
|
|
||||||
focusSprite(focusedElement);
|
|
||||||
if(focusedElement.classList.contains("support"))
|
|
||||||
{
|
|
||||||
setSpriteLightness(focusedElement.firstChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(creditItems[i].tagName == "A")
|
|
||||||
{
|
|
||||||
creditItems[i].addEventListener("click", function(ev) {
|
|
||||||
if(player)
|
|
||||||
{
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceMenus = titleBar.querySelectorAll(".menu");
|
|
||||||
|
|
||||||
helpButton = titleBar.querySelector(".cineraHelp");
|
|
||||||
helpDocumentation = helpButton.querySelector(".help_container");
|
|
||||||
BindHelp(helpButton, helpDocumentation);
|
|
||||||
}
|
|
||||||
|
|
||||||
var focusedElement = null;
|
|
||||||
var focusedIdentifier = null;
|
|
||||||
|
|
||||||
var playerContainer = cinera.querySelector(".cineraPlayerContainer")
|
|
||||||
var prevEpisode = playerContainer.querySelector(".episodeMarker.prev");
|
|
||||||
if(prevEpisode) { originalTextContent.EpisodePrev = prevEpisode.firstChild.textContent; }
|
|
||||||
var nextEpisode = playerContainer.querySelector(".episodeMarker.next");
|
|
||||||
if(nextEpisode) { originalTextContent.EpisodeNext = nextEpisode.firstChild.textContent; }
|
|
||||||
var testMarkers = playerContainer.querySelectorAll(".marker");
|
|
||||||
|
|
||||||
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
|
|
||||||
var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
|
var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
|
||||||
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector);
|
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector);
|
||||||
|
|
||||||
var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container";
|
var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container";
|
||||||
var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
|
var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
|
||||||
|
|
||||||
if(CineraProps.IsMobile)
|
var cinera = document.querySelector(".cinera");
|
||||||
{
|
var player = new Player(cinera, onRefChanged);
|
||||||
InitMobileStyle();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - 4;
|
|
||||||
MenuContainerRule.style.maxHeight = MenuMaxHeight + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
var player = new Player(playerContainer, onRefChanged);
|
|
||||||
|
|
||||||
if(CineraProps.IsMobile)
|
|
||||||
{
|
|
||||||
ConnectMobileControls(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cineraViewStorageItem = "cineraView";
|
|
||||||
|
|
||||||
if(viewsMenu && localStorage.getItem(cineraViewStorageItem))
|
|
||||||
{
|
|
||||||
toggleTheatreMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
InitScrollEventListener(cinera);
|
|
||||||
|
|
||||||
function
|
|
||||||
DelayedUpdateSize()
|
|
||||||
{
|
|
||||||
player.updateSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", function() {
|
window.addEventListener("resize", function() {
|
||||||
if(CineraProps.IsMobile)
|
if(CineraProps.IsMobile)
|
||||||
{
|
{
|
||||||
setTimeout(DelayedUpdateSize, 512);
|
setTimeout(DelayedUpdateSize, 512, player);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -333,10 +42,10 @@ window.addEventListener("resize", function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.onorientationchange = function() {
|
screen.orientation.onchange = function() {
|
||||||
if(CineraProps.IsMobile)
|
if(CineraProps.IsMobile)
|
||||||
{
|
{
|
||||||
setTimeout(DelayedUpdateSize, 512);
|
setTimeout(DelayedUpdateSize, 512, player);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -351,43 +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) {
|
{
|
||||||
handleMenuTogglerInteraction(this, ev.type);
|
CineraProps.V = views.THEATRE;
|
||||||
})
|
localStorage.setItem(player.cineraViewStorageItem, views.THEATRE);
|
||||||
sourceMenus[i].addEventListener("mouseleave", function(ev) {
|
player.updateSize();
|
||||||
handleMenuTogglerInteraction(this, ev.type);
|
}
|
||||||
})
|
});
|
||||||
sourceMenus[i].addEventListener("click", function(ev) {
|
|
||||||
handleMenuTogglerInteraction(this, ev.type);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
var colouredItems = playerContainer.querySelectorAll(".author, .member, .project");
|
|
||||||
for(i = 0; i < colouredItems.length; ++i)
|
|
||||||
{
|
|
||||||
setTextLightness(colouredItems[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var topicDots = cinera.querySelectorAll(".category");
|
|
||||||
for(var i = 0; i < topicDots.length; ++i)
|
|
||||||
{
|
|
||||||
setDotLightness(topicDots[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname;
|
|
||||||
var lastTimestamp;
|
|
||||||
if(location.hash) {
|
|
||||||
player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash);
|
|
||||||
}
|
|
||||||
else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem))
|
|
||||||
{
|
|
||||||
player.setTimeThenPlay(lastTimestamp);
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,9 @@ DeriveReliableWindowDimensions()
|
||||||
Y: null,
|
Y: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var ScrollPosX = window.scrollX;
|
||||||
|
var ScrollPosY = window.scrollY;
|
||||||
|
|
||||||
var DisplaySettings = [];
|
var DisplaySettings = [];
|
||||||
for(var i = 0; i < document.body.children.length; ++i)
|
for(var i = 0; i < document.body.children.length; ++i)
|
||||||
{
|
{
|
||||||
|
@ -40,6 +43,9 @@ DeriveReliableWindowDimensions()
|
||||||
Child.style.display = DisplaySettings.shift();
|
Child.style.display = DisplaySettings.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollTriggeredInternally = true;
|
||||||
|
window.scroll(ScrollPosX, ScrollPosY);
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +82,7 @@ function IsVisible(Element, WindowDim) {
|
||||||
function
|
function
|
||||||
GetRealOrientation(PreferredLandscape, IsMobile)
|
GetRealOrientation(PreferredLandscape, IsMobile)
|
||||||
{
|
{
|
||||||
var Result = window.orientation;
|
var Result = screen.orientation.angle;
|
||||||
var WindowDim = GetWindowDim(IsMobile);
|
var WindowDim = GetWindowDim(IsMobile);
|
||||||
if(WindowDim.Y > WindowDim.X)
|
if(WindowDim.Y > WindowDim.X)
|
||||||
{
|
{
|
||||||
|
@ -186,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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +323,7 @@ function IsInRangeEx(Min, N, Max)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto-scrolling */
|
/* Auto-scrolling */
|
||||||
|
var ScrollTriggeredInternally = false;
|
||||||
var LastScrollYPos = 0;
|
var LastScrollYPos = 0;
|
||||||
var ScrollTicking = false;
|
var ScrollTicking = false;
|
||||||
var ScrollerFunction;
|
var ScrollerFunction;
|
||||||
|
@ -376,7 +390,11 @@ function
|
||||||
InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
|
InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
|
||||||
{
|
{
|
||||||
window.addEventListener('scroll', function() {
|
window.addEventListener('scroll', function() {
|
||||||
if(ScrollCondition == undefined || ScrollCondition == true)
|
if(ScrollTriggeredInternally)
|
||||||
|
{
|
||||||
|
ScrollTriggeredInternally = false;
|
||||||
|
}
|
||||||
|
else if(ScrollCondition == undefined || ScrollCondition == true)
|
||||||
{
|
{
|
||||||
LastScrollYPos = window.scrollY;
|
LastScrollYPos = window.scrollY;
|
||||||
|
|
||||||
|
@ -503,17 +521,29 @@ IsOverflowed(Element)
|
||||||
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
|
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
SetHelpUnfocused(Button)
|
||||||
|
{
|
||||||
|
Button.firstElementChild.innerText = "¿";
|
||||||
|
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
SetHelpFocused(Button)
|
||||||
|
{
|
||||||
|
Button.firstElementChild.innerText = "?";
|
||||||
|
Button.firstElementChild.title = ""
|
||||||
|
}
|
||||||
|
|
||||||
function
|
function
|
||||||
BindHelp(Button, DocumentationContainer)
|
BindHelp(Button, DocumentationContainer)
|
||||||
{
|
{
|
||||||
window.addEventListener("blur", function(){
|
window.addEventListener("blur", function(){
|
||||||
Button.firstElementChild.innerText = "¿";
|
SetHelpUnfocused(Button);
|
||||||
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("focus", function(){
|
window.addEventListener("focus", function(){
|
||||||
Button.firstElementChild.innerText = "?";
|
SetHelpFocused(Button);
|
||||||
Button.firstElementChild.title = ""
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Button.addEventListener("click", function() {
|
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) {
|
function getBackgroundColourRGB(element) {
|
||||||
var Colour = getComputedStyle(element).getPropertyValue("background-color");
|
var Colour = getComputedStyle(element).getPropertyValue("background-color");
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && 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");
|
Colour = getComputedStyle(element).getPropertyValue("background-color");
|
||||||
++depth;
|
++depth;
|
||||||
}
|
}
|
||||||
var Staging = Colour.slice(4, -1).split(", ");
|
var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", ");
|
||||||
var Result = {
|
var Result = {
|
||||||
R: parseInt(Staging[0]),
|
R: parseInt(Staging[0]),
|
||||||
G: parseInt(Staging[1]),
|
G: parseInt(Staging[1]),
|
||||||
|
@ -580,28 +581,34 @@ function setTextLightness(textElement)
|
||||||
{
|
{
|
||||||
var textHue = textElement.getAttribute("data-hue");
|
var textHue = textElement.getAttribute("data-hue");
|
||||||
var textSaturation = textElement.getAttribute("data-saturation");
|
var textSaturation = textElement.getAttribute("data-saturation");
|
||||||
if(getBackgroundBrightness(textElement.parentNode) < 127)
|
if(textHue && textSaturation)
|
||||||
{
|
{
|
||||||
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
|
if(getBackgroundBrightness(textElement.parentNode) < 127)
|
||||||
}
|
{
|
||||||
else
|
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
|
||||||
{
|
}
|
||||||
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
|
else
|
||||||
|
{
|
||||||
|
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDotLightness(topicDot)
|
function setDotLightness(topicDot)
|
||||||
{
|
{
|
||||||
var Hue = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[0];
|
var dotHue = topicDot.getAttribute("data-hue");
|
||||||
var Saturation = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[1];
|
var dotSaturation = topicDot.getAttribute("data-saturation");
|
||||||
if(getBackgroundBrightness(topicDot.parentNode) < 127)
|
if(dotHue && dotSaturation)
|
||||||
{
|
{
|
||||||
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
|
if(getBackgroundBrightness(topicDot.parentNode) < 127)
|
||||||
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
|
{
|
||||||
}
|
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
|
||||||
else
|
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
|
||||||
{
|
}
|
||||||
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
|
else
|
||||||
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
|
{
|
||||||
|
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
|
||||||
|
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,11 +213,11 @@ function prepareToParseIndexFile(project)
|
||||||
mode = "markers";
|
mode = "markers";
|
||||||
episode.markers = [];
|
episode.markers = [];
|
||||||
} else if (mode == "markers") {
|
} else if (mode == "markers") {
|
||||||
var match = line.match(/"(\d+)": "(.+)"/);
|
var match = line.match(/"(\d+.\d+)": "(.+)"/);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
console.log(name, line);
|
console.log(name, line);
|
||||||
} else {
|
} else {
|
||||||
var totalTime = parseInt(line.slice(1));
|
var totalTime = parseFloat(line.slice(1));
|
||||||
var marker = {
|
var marker = {
|
||||||
totalTime: totalTime,
|
totalTime: totalTime,
|
||||||
prettyTime: markerTime(totalTime),
|
prettyTime: markerTime(totalTime),
|
||||||
|
@ -292,7 +292,7 @@ function markerTime(totalTime) {
|
||||||
var markTime = "(";
|
var markTime = "(";
|
||||||
var hours = Math.floor(totalTime / 60 / 60);
|
var hours = Math.floor(totalTime / 60 / 60);
|
||||||
var minutes = Math.floor(totalTime / 60) % 60;
|
var minutes = Math.floor(totalTime / 60) % 60;
|
||||||
var seconds = totalTime % 60;
|
var seconds = Math.floor(totalTime) % 60;
|
||||||
if (hours > 0) {
|
if (hours > 0) {
|
||||||
markTime += padTimeComponent(hours) + ":";
|
markTime += padTimeComponent(hours) + ":";
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ function runSearch(refresh) {
|
||||||
Search.ResultsSummary.style.display = "none";
|
Search.ResultsSummary.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + 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.";
|
Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
|
||||||
}
|
}
|
||||||
|
@ -3866,7 +3866,7 @@ InitResizeEventListener()
|
||||||
function
|
function
|
||||||
InitOrientationChangeListener()
|
InitOrientationChangeListener()
|
||||||
{
|
{
|
||||||
window.onorientationchange = function()
|
screen.orientation.onchange = function()
|
||||||
{
|
{
|
||||||
if(CineraProps.IsMobile)
|
if(CineraProps.IsMobile)
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int line;
|
int line;
|
||||||
|
|
||||||
int h, m, s;
|
int h, m, s, ms;
|
||||||
|
|
||||||
char* text;
|
char* text;
|
||||||
char* author;
|
char* author;
|
||||||
|
@ -471,7 +471,7 @@ next_attr:
|
||||||
|
|
||||||
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
{
|
{
|
||||||
unsigned int h = 0, m = 0, s = 0;
|
unsigned int h = 0, m = 0, s = 0, ms = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
|
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
|
||||||
|
|
||||||
|
@ -485,7 +485,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
if(c == ':') {
|
if(c == ':') {
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
if(sscanf(p->cursor, ":%u]%n", &tmp, &offset) != 1 || offset == 0) {
|
if(sscanf(p->cursor, ":%u%n", &tmp, &offset) != 1 || offset == 0) {
|
||||||
_hmml_err(p, "Unable to parse 3-part timecode");
|
_hmml_err(p, "Unable to parse 3-part timecode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +494,27 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
s = tmp;
|
s = tmp;
|
||||||
|
|
||||||
p->cursor += offset;
|
p->cursor += offset;
|
||||||
|
c = *p->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '.') {
|
||||||
|
unsigned int tmp;
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
int non_number_chars = 2;
|
||||||
|
int digits_in_100 = 3;
|
||||||
|
int max_chars_to_parse = non_number_chars + digits_in_100;
|
||||||
|
|
||||||
|
if(sscanf(p->cursor, ".%u]%n", &tmp, &offset) != 1 || offset == 0 || offset > max_chars_to_parse) {
|
||||||
|
_hmml_err(p, "Unable to parse %u.5-part timecode", h ? 3 : 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = offset - non_number_chars; i < digits_in_100; ++i) {
|
||||||
|
tmp *= 10;
|
||||||
|
}
|
||||||
|
ms = tmp;
|
||||||
|
|
||||||
|
p->cursor += offset;
|
||||||
|
|
||||||
} else if(c != ']') {
|
} else if(c != ']') {
|
||||||
_hmml_err(p, "Unable to parse timecode");
|
_hmml_err(p, "Unable to parse timecode");
|
||||||
|
@ -501,6 +522,10 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
++p->cursor;
|
++p->cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ms >= 1000) {
|
||||||
|
_hmml_err(p, "Milliseconds cannot exceed 999");
|
||||||
|
}
|
||||||
|
|
||||||
if(s >= 60) {
|
if(s >= 60) {
|
||||||
_hmml_err(p, "Seconds cannot exceed 59");
|
_hmml_err(p, "Seconds cannot exceed 59");
|
||||||
}
|
}
|
||||||
|
@ -512,6 +537,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
|
||||||
ts->h = h;
|
ts->h = h;
|
||||||
ts->m = m;
|
ts->m = m;
|
||||||
ts->s = s;
|
ts->s = s;
|
||||||
|
ts->ms = ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size)
|
static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size)
|
||||||
|
@ -824,7 +850,7 @@ void hmml_free(HMML_Output* out)
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct HMML_Version hmml_version = {
|
const struct HMML_Version hmml_version = {
|
||||||
2, 0, 14
|
2, 0, 15
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef HSTX
|
#undef HSTX
|
||||||
|
|
Loading…
Reference in New Issue