hmml_to_html.c: Add keywords and sort topic dots

This commit is contained in:
Matt Mascarenhas 2017-08-19 22:41:51 +01:00
parent 6e1f6a4faa
commit f3899c2f35
1 changed files with 191 additions and 184 deletions

View File

@ -27,10 +27,10 @@ typedef unsigned int bool;
enum enum
{ {
EDITION_SINGLE = 0, EDITION_SINGLE,
EDITION_PROJECT = 1, EDITION_PROJECT,
EDITION_NETWORK = 2 EDITION_NETWORK
} EDITION; };
typedef struct typedef struct
{ {
@ -72,6 +72,12 @@ typedef struct
char WrittenText[32]; char WrittenText[32];
} category_info; } category_info;
typedef struct
{
category_info Category[64];
int Count;
} categories;
// TODO(matt): Parse this stuff out of a config file // TODO(matt): Parse this stuff out of a config file
typedef struct typedef struct
{ {
@ -214,6 +220,7 @@ CopyBuffer(buffer *Dest, buffer *Src)
} }
*Dest->Ptr++ = *Src->Ptr++; *Dest->Ptr++ = *Src->Ptr++;
} }
*Dest->Ptr = '\0';
} }
__attribute__ ((format (printf, 2, 3))) __attribute__ ((format (printf, 2, 3)))
@ -472,10 +479,10 @@ SanitisePunctuation(char *String)
enum enum
{ {
CreditsError_NoHost = 1 << 0, CreditsError_NoHost,
CreditsError_NoAnnotator = 1 << 1, CreditsError_NoAnnotator,
CreditsError_NoCredentials = 1 << 2 CreditsError_NoCredentials
} CreditsErrorCodes; };
int int
SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role) SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role)
@ -730,145 +737,110 @@ BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMM
} }
void void
BuildFilter(category_info *TopicsArray, int *UniqueTopics, category_info *MediaArray, int *UniqueMedia, char *Marker) InsertCategory(categories *Topics, categories *Media, bool *HasTopic, bool *HasMedium, char *Marker)
{ {
bool IsMedium = FALSE; bool IsMedium = FALSE;
int i = 0; int CategoryMediumIndex;
for(i = 0; i < ArrayCount(CategoryMedium); ++i) for(CategoryMediumIndex = 0; CategoryMediumIndex < ArrayCount(CategoryMedium); ++CategoryMediumIndex)
{ {
if(!StringsDiffer(CategoryMedium[i].Medium, Marker)) if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, Marker))
{ {
IsMedium = TRUE; IsMedium = TRUE;
*HasMedium = TRUE;
break; break;
} }
} }
int Offset; int CategoryIndex;
if(IsMedium) if(IsMedium)
{ {
int j = 0; for(CategoryIndex = 0; CategoryIndex < Media->Count; ++CategoryIndex)
for(j = 0; j < *UniqueMedia; ++j)
{ {
if(!StringsDiffer(CategoryMedium[i].Medium, MediaArray[j].Marker)) if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, Media->Category[CategoryIndex].Marker))
{ {
return; return;
} }
if((Offset = StringsDiffer(CategoryMedium[i].WrittenName, MediaArray[j].WrittenText)) < 0) if((StringsDiffer(CategoryMedium[CategoryMediumIndex].WrittenName, Media->Category[CategoryIndex].WrittenText)) < 0)
{ {
int k; int CategoryCount;
for(k = *UniqueMedia; k > j; --k) for(CategoryCount = Media->Count; CategoryCount > CategoryIndex; --CategoryCount)
{ {
CopyString(MediaArray[k].Marker, MediaArray[k-1].Marker); CopyString(Media->Category[CategoryCount].Marker, Media->Category[CategoryCount-1].Marker);
CopyString(MediaArray[k].WrittenText, MediaArray[k-1].WrittenText); CopyString(Media->Category[CategoryCount].WrittenText, Media->Category[CategoryCount-1].WrittenText);
} }
CopyString(MediaArray[k].Marker, CategoryMedium[i].Medium); CopyString(Media->Category[CategoryCount].Marker, CategoryMedium[CategoryMediumIndex].Medium);
CopyString(MediaArray[k].WrittenText, CategoryMedium[i].WrittenName); CopyString(Media->Category[CategoryCount].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName);
break; break;
} }
} }
if(j == *UniqueMedia) if(CategoryIndex == Media->Count)
{ {
CopyString(MediaArray[j].Marker, CategoryMedium[i].Medium); CopyString(Media->Category[CategoryIndex].Marker, CategoryMedium[CategoryMediumIndex].Medium);
CopyString(MediaArray[j].WrittenText, CategoryMedium[i].WrittenName); CopyString(Media->Category[CategoryIndex].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName);
} }
++*UniqueMedia; ++Media->Count;
return;
} }
else else
{ {
int i = 0; *HasTopic = TRUE;
for(i = 0; i < *UniqueTopics; ++i) for(CategoryIndex = 0; CategoryIndex < Topics->Count; ++CategoryIndex)
{ {
if(!StringsDiffer(Marker, TopicsArray[i].Marker)) if(!StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker))
{ {
return; return;
} }
if((Offset = StringsDiffer(Marker, TopicsArray[i].Marker)) < 0) if((StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker)) < 0)
{ {
int j; int CategoryCount;
for(j = *UniqueTopics; j > i; --j) for(CategoryCount = Topics->Count; CategoryCount > CategoryIndex; --CategoryCount)
{ {
CopyString(TopicsArray[j].Marker, TopicsArray[j-1].Marker); CopyString(Topics->Category[CategoryCount].Marker, Topics->Category[CategoryCount-1].Marker);
} }
CopyString(TopicsArray[j].Marker, Marker); CopyString(Topics->Category[CategoryCount].Marker, Marker);
break; break;
} }
} }
if(i == *UniqueTopics) if(CategoryIndex == Topics->Count)
{ {
CopyString(TopicsArray[i].Marker, Marker); CopyString(Topics->Category[CategoryIndex].Marker, Marker);
} }
++*UniqueTopics; ++Topics->Count;
return;
} }
return;
} }
void void
BuildCategories(buffer *AnnotationClass, buffer *Category, int *MarkerIndex, bool *HasCategory, bool *HasMedium, char *Marker) BuildCategories(buffer *AnnotationClass, buffer *TopicDots, categories LocalTopics, categories LocalMedia, int *MarkerIndex)
{ {
// NOTE(matt): This guy could also sort, so that the dots appear in a consistent order in the annotations if(LocalTopics.Count > 0)
// If so, the Category buffer would have to only contain the category names and no more until collation time
// BuildCategories() would have to parse the Category.Location out to an array, sort that array and write it back in
// The code in the "annotation loop" would then have to write both the head and tail of the category stuff
for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{ {
if(!StringsDiffer(CategoryMedium[i].Medium, Marker)) CopyStringToBuffer(TopicDots, "<span class=\"categories\">");
for(int i = 0; i < LocalTopics.Count; ++i)
{ {
CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(Marker)); CopyStringToBuffer(TopicDots, "<div title=\"%s\" class=\"category %s\"></div>",
*HasMedium = TRUE; SanitisePunctuation(LocalTopics.Category[i].Marker),
++*MarkerIndex; SanitisePunctuation(LocalTopics.Category[i].Marker));
return;
CopyStringToBuffer(AnnotationClass, " cat_%s",
SanitisePunctuation(LocalTopics.Category[i].Marker));
} }
CopyStringToBuffer(TopicDots, "</span>");
} }
if(*HasCategory == FALSE)
for(int i = 0; i < LocalMedia.Count; ++i)
{ {
CopyStringToBuffer(Category, "<span class=\"categories\">"); CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(LocalMedia.Category[i].Marker));
*HasCategory = TRUE;
} }
// NOTE(matt): Iterate through the Category->Location looking for "Marker", and bail if we find it CopyStringToBuffer(AnnotationClass, "\"");
char *Ptr = Category->Location;
bool Found = FALSE;
while(*Ptr)
{
if(*Ptr == '\"')
{
++Ptr;
if(!StringsDifferT(SanitisePunctuation(Marker), Ptr, ' '))
{
Found = TRUE;
break;
}
else
{
while(*Ptr != '\"')
{
++Ptr;
}
}
}
++Ptr;
}
if(Found == FALSE)
{
CopyStringToBuffer(Category, "<div title=\"%s\" class=\"category %s\"></div>",
SanitisePunctuation(Marker),
SanitisePunctuation(Marker));
CopyStringToBuffer(AnnotationClass, " cat_%s",
SanitisePunctuation(Marker));
}
++*MarkerIndex;
return;
} }
int int
@ -1448,7 +1420,10 @@ PrintUsage(char *BinaryLocation, char *DefaultCSSDir, char *DefaultImagesDir, ch
" <!-- __CINERA_PLAYER__ -->\n" " <!-- __CINERA_PLAYER__ -->\n"
" <!-- __CINERA_SCRIPT__ --> (must come after <!-- __CINERA_PLAYER__ -->)\n" " <!-- __CINERA_SCRIPT__ --> (must come after <!-- __CINERA_PLAYER__ -->)\n"
" Other available tags include:\n" " Other available tags include:\n"
" <!-- __CINERA_TITLE__ -->\n", " <!-- __CINERA_TITLE__ -->\n"
"\n"
"HMML Specification:\n"
" https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec\n",
BinaryLocation, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultTemplateLocation); BinaryLocation, DefaultCSSDir, DefaultImagesDir, DefaultJSDir, DefaultDefaultMedium, DefaultOutLocation, DefaultTemplateLocation);
} }
@ -1814,7 +1789,7 @@ main(int ArgC, char **Args)
buffer AnnotationClass; buffer AnnotationClass;
buffer AnnotationData; buffer AnnotationData;
buffer Text; buffer Text;
buffer Category; buffer TopicDots;
buffer Script; buffer Script;
buffer FilterState; buffer FilterState;
@ -1881,8 +1856,8 @@ main(int ArgC, char **Args)
ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterState, "FilterState", Kilobytes(4)); ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterState, "FilterState", Kilobytes(4));
ref_info ReferencesArray[200] = { 0 }; ref_info ReferencesArray[200] = { 0 };
category_info TopicsArray[56] = { 0 }; categories Topics = { 0 };
category_info MediaArray[8] = { 0 }; categories Media = { 0 };
bool HasQuoteMenu = FALSE; bool HasQuoteMenu = FALSE;
bool HasReferenceMenu = FALSE; bool HasReferenceMenu = FALSE;
@ -1892,8 +1867,6 @@ main(int ArgC, char **Args)
int QuoteIdentifier = 0x3b1; int QuoteIdentifier = 0x3b1;
int RefIdentifier = 1; int RefIdentifier = 1;
int UniqueRefs = 0; int UniqueRefs = 0;
int UniqueTopics = 0;
int UniqueMedia = 0;
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <div class=\"title %s\">\n" " <div class=\"title %s\">\n"
@ -1935,7 +1908,9 @@ goto Cleanup;
printf("%d\n", AnnotationIndex); printf("%d\n", AnnotationIndex);
#endif #endif
HMML_Annotation *Anno = HMML.annotations + AnnotationIndex; HMML_Annotation *Anno = HMML.annotations + AnnotationIndex;
bool HasCategory = FALSE; categories LocalTopics = { 0 };
bool HasTopic = FALSE;
categories LocalMedia = { 0 };
bool HasMedium = FALSE; bool HasMedium = FALSE;
bool HasQuote = FALSE; bool HasQuote = FALSE;
bool HasReference = FALSE; bool HasReference = FALSE;
@ -1947,13 +1922,13 @@ goto Cleanup;
// AnnotationClass // AnnotationClass
// AnnotationData // AnnotationData
// Text // Text
// Category // TopicDots
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationHeader, "AnnotationHeader", 512); ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationHeader, "AnnotationHeader", 512);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationClass, "AnnotationClass", 256); ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationClass, "AnnotationClass", 256);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 512); ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 512);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Text, "Text", Kilobytes(4)); ClaimBuffer(&MemoryArena, &ClaimedMemory, &Text, "Text", Kilobytes(4));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Category, "Category", 512); ClaimBuffer(&MemoryArena, &ClaimedMemory, &TopicDots, "TopicDots", 512);
CopyStringToBuffer(&AnnotationHeader, CopyStringToBuffer(&AnnotationHeader,
" <div data-timestamp=\"%d\"", " <div data-timestamp=\"%d\"",
@ -1968,7 +1943,7 @@ goto Cleanup;
{ {
HasFilterMenu = TRUE; HasFilterMenu = TRUE;
} }
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, "authored"); InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, "authored");
CopyStringToBuffer(&AnnotationClass, " authored"); CopyStringToBuffer(&AnnotationClass, " authored");
hsl_colour AuthorColour; hsl_colour AuthorColour;
StringToColourHash(&AuthorColour, Anno->author); StringToColourHash(&AuthorColour, Anno->author);
@ -2065,13 +2040,12 @@ goto Cleanup;
else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY) else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY)
{ {
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir); GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir);
// TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu) if(!HasFilterMenu)
{ {
HasFilterMenu = TRUE; HasFilterMenu = TRUE;
} }
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, Anno->markers[MarkerIndex].marker); InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker); // Global
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, &HasMedium, Anno->markers[MarkerIndex].marker); InsertCategory(&LocalTopics, &LocalMedia, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker); // Local
} }
} }
@ -2242,6 +2216,7 @@ goto Cleanup;
break; break;
default: default:
*Text.Ptr++ = *InPtr++; *Text.Ptr++ = *InPtr++;
*Text.Ptr = '\0';
break; break;
} }
} }
@ -2327,25 +2302,25 @@ goto Cleanup;
while(MarkerIndex < Anno->marker_count) while(MarkerIndex < Anno->marker_count)
{ {
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir); GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir);
// TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu) if(!HasFilterMenu)
{ {
HasFilterMenu = TRUE; HasFilterMenu = TRUE;
} }
if(Anno->markers[MarkerIndex].marker) if(Anno->markers[MarkerIndex].marker)
{ {
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, Anno->markers[MarkerIndex].marker); InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker);
} }
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, &HasMedium, Anno->markers[MarkerIndex].marker); InsertCategory(&LocalTopics, &LocalMedia, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker);
++MarkerIndex;
} }
if(!HasMedium) if(!HasMedium)
{ {
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, DefaultMedium); InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, DefaultMedium);
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, &HasMedium, DefaultMedium); InsertCategory(&LocalTopics, &LocalMedia, &HasTopic, &HasMedium, DefaultMedium);
} }
CopyStringToBuffer(&AnnotationClass, "\""); BuildCategories(&AnnotationClass, &TopicDots, LocalTopics, LocalMedia, &MarkerIndex);
CopyBuffer(&AnnotationHeader, &AnnotationClass); CopyBuffer(&AnnotationHeader, &AnnotationClass);
if(HasQuote || HasReference) if(HasQuote || HasReference)
@ -2360,17 +2335,13 @@ goto Cleanup;
" <div class=\"content\"><span class=\"timecode\">%s</span>", " <div class=\"content\"><span class=\"timecode\">%s</span>",
Anno->time); Anno->time);
if(HasCategory)
{
CopyStringToBuffer(&Category, "</span>");
CopyBuffer(&Text, &Category);
}
// NOTE(matt): This feels a bit janky...
*Text.Ptr = '\0';
CopyBuffer(&Annotation, &Text); CopyBuffer(&Annotation, &Text);
if(HasTopic)
{
CopyBuffer(&Annotation, &TopicDots);
}
CopyStringToBuffer(&Annotation, "</div>\n" CopyStringToBuffer(&Annotation, "</div>\n"
" <div class=\"progress faded\">\n" " <div class=\"progress faded\">\n"
" <div class=\"content\"><span class=\"timecode\">%s</span>", " <div class=\"content\"><span class=\"timecode\">%s</span>",
@ -2393,13 +2364,13 @@ goto Cleanup;
CopyBuffer(&Player, &Annotation); CopyBuffer(&Player, &Annotation);
// NOTE(matt): Tree structure of "annotation local" buffer dependencies // NOTE(matt): Tree structure of "annotation local" buffer dependencies
// Category // TopicDots
// Text // Text
// AnnotationData // AnnotationData
// AnnotationClass // AnnotationClass
// AnnotationHeader // AnnotationHeader
DeclaimBuffer(&Category, &ClaimedMemory); DeclaimBuffer(&TopicDots, &ClaimedMemory);
DeclaimBuffer(&Text, &ClaimedMemory); DeclaimBuffer(&Text, &ClaimedMemory);
DeclaimBuffer(&AnnotationData, &ClaimedMemory); DeclaimBuffer(&AnnotationData, &ClaimedMemory);
DeclaimBuffer(&AnnotationClass, &ClaimedMemory); DeclaimBuffer(&AnnotationClass, &ClaimedMemory);
@ -2478,15 +2449,15 @@ goto Cleanup;
{ {
CopyStringToBuffer(&FilterState, CopyStringToBuffer(&FilterState,
" var filterState = {\n"); " var filterState = {\n");
for(int i = 0; i < UniqueTopics; ++i) for(int i = 0; i < Topics.Count; ++i)
{ {
CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n",
TopicsArray[i].Marker, "topic"); Topics.Category[i].Marker, "topic");
} }
for(int i = 0; i < UniqueMedia; ++i) for(int i = 0; i < Media.Count; ++i)
{ {
CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n", CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n",
MediaArray[i].Marker, "medium"); Media.Category[i].Marker, "medium");
} }
CopyStringToBuffer(&FilterState, CopyStringToBuffer(&FilterState,
" };\n"); " };\n");
@ -2502,7 +2473,7 @@ goto Cleanup;
bool HasTopic = FALSE; bool HasTopic = FALSE;
bool HasMedium = FALSE; bool HasMedium = FALSE;
for(int i = 0; i < UniqueTopics; ++i) for(int i = 0; i < Topics.Count; ++i)
{ {
if(!HasTopic) if(!HasTopic)
{ {
@ -2516,12 +2487,12 @@ goto Cleanup;
" <div class=\"filter_content %s\">\n" " <div class=\"filter_content %s\">\n"
" <span class=\"icon category %s\"></span><span class=\"text\">%s</span>\n" " <span class=\"icon category %s\"></span><span class=\"text\">%s</span>\n"
" </div>\n", " </div>\n",
TopicsArray[i].Marker, Topics.Category[i].Marker,
TopicsArray[i].Marker, Topics.Category[i].Marker,
TopicsArray[i].Marker); Topics.Category[i].Marker);
} }
for(int i = 0; i < UniqueMedia; ++i) for(int i = 0; i < Media.Count; ++i)
{ {
if(!HasMedium) if(!HasMedium)
{ {
@ -2535,7 +2506,7 @@ goto Cleanup;
int j; int j;
for(j = 0; j < ArrayCount(CategoryMedium); ++j) for(j = 0; j < ArrayCount(CategoryMedium); ++j)
{ {
if(!StringsDiffer(MediaArray[i].Marker, CategoryMedium[j].Medium)) if(!StringsDiffer(Media.Category[i].Marker, CategoryMedium[j].Medium))
{ {
break; break;
} }
@ -2545,7 +2516,7 @@ goto Cleanup;
" <div class=\"filter_content %s\">\n" " <div class=\"filter_content %s\">\n"
" <span class=\"icon\">%s</span><span class=\"text\">%s</span>\n" " <span class=\"icon\">%s</span><span class=\"text\">%s</span>\n"
" </div>\n", " </div>\n",
MediaArray[i].Marker, Media.Category[i].Marker,
CategoryMedium[j].Icon, CategoryMedium[j].Icon,
CategoryMedium[j].WrittenName CategoryMedium[j].WrittenName
); );
@ -2589,104 +2560,107 @@ goto Cleanup;
" <div class=\"help_container\">\n" " <div class=\"help_container\">\n"
" <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n" " <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n"
"\n" "\n"
" <h2>Global Keys</h2>\n" /* NOTE(matt) */
" <span class=\"help_key\">W</span>, <span class=\"help_key\">A</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">D</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next marker</span><br>\n"); " <h2>Global Keys</h2>\n"
" <span class=\"help_key\">W</span>, <span class=\"help_key\">A</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">D</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next marker</span><br>\n");
if(HasFilterMenu) if(HasFilterMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key\">z</span> <span class=\"help_text\">Toggle filter mode</span> <span class=\"help_key\">V</span> <span class=\"help_text\">Revert filter to original state</span>\n"); " <span class=\"help_key\">z</span> <span class=\"help_text\">Toggle filter mode</span> <span class=\"help_key\">V</span> <span class=\"help_text\">Revert filter to original state</span>\n");
} }
else else
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key unavailable\">z</span> <span class=\"help_text unavailable\">Toggle filter mode</span> <span class=\"help_key unavailable\">V</span> <span class=\"help_text unavailable\">Revert filter to original state</span>\n"); " <span class=\"help_key unavailable\">z</span> <span class=\"help_text unavailable\">Toggle filter mode</span> <span class=\"help_key unavailable\">V</span> <span class=\"help_text unavailable\">Revert filter to original state</span>\n");
} }
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
"\n" "\n"
" <h2>Menu toggling</h2>\n"); " <h2>Menu toggling</h2>\n");
if(HasQuoteMenu) if(HasQuoteMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key\">q</span> <span class=\"help_text\">Quotes</span>\n"); " <span class=\"help_key\">q</span> <span class=\"help_text\">Quotes</span>\n");
} }
else else
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key unavailable\">q</span> <span class=\"help_text unavailable\">Quotes</span>\n"); " <span class=\"help_key unavailable\">q</span> <span class=\"help_text unavailable\">Quotes</span>\n");
} }
if(HasReferenceMenu) if(HasReferenceMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key\">r</span> <span class=\"help_text\">References</span>\n"); " <span class=\"help_key\">r</span> <span class=\"help_text\">References</span>\n");
} }
else else
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key unavailable\">r</span> <span class=\"help_text unavailable\">References</span>\n"); " <span class=\"help_key unavailable\">r</span> <span class=\"help_text unavailable\">References</span>\n");
} }
if(HasFilterMenu) if(HasFilterMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key\">f</span> <span class=\"help_text\">Filter</span>\n"); " <span class=\"help_key\">f</span> <span class=\"help_text\">Filter</span>\n");
} }
else else
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key unavailable\">f</span> <span class=\"help_text unavailable\">Filter</span>\n"); " <span class=\"help_key unavailable\">f</span> <span class=\"help_text unavailable\">Filter</span>\n");
} }
if(HasCreditsMenu) if(HasCreditsMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key\">c</span> <span class=\"help_text\">Credits</span>\n"); " <span class=\"help_key\">c</span> <span class=\"help_text\">Credits</span>\n");
} }
else else
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
" <span class=\"help_key unavailable\">c</span> <span class=\"help_text unavailable\">Credits</span>\n"); " <span class=\"help_key unavailable\">c</span> <span class=\"help_text unavailable\">Credits</span>\n");
} }
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
"\n" "\n"
" <h2>Movement</h2>\n" " <h2>Movement</h2>\n"
" <div class=\"help_paragraph\">\n" " <div class=\"help_paragraph\">\n"
" <div class=\"key_block\">\n" " <div class=\"key_block\">\n"
" <div class=\"key_column\" style=\"flex-grow: 1\">\n" " <div class=\"key_column\" style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">a</span>\n" " <span class=\"help_key\">a</span>\n"
" </div>\n"
" <div class=\"key_column\" style=\"flex-grow: 2\">\n"
" <span class=\"help_key\">w</span><br>\n"
" <span class=\"help_key\">s</span>\n"
" </div>\n"
" <div class=\"key_column\" style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">d</span>\n"
" </div>\n"
" </div>\n"
" <div class=\"key_block\">\n"
" <span class=\"help_key\">h</span>\n"
" <span class=\"help_key\">j</span>\n"
" <span class=\"help_key\">k</span>\n"
" <span class=\"help_key\">l</span>\n"
" </div>\n"
" <div class=\"key_block\">\n"
" <div style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">←</span>\n"
" </div>\n"
" <div style=\"flex-grow: 2\">\n"
" <span class=\"help_key\">↑</span><br>\n"
" <span class=\"help_key\">↓</span>\n"
" </div>\n"
" <div style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">→</span>\n"
" </div>\n"
" </div>\n"
" </div>\n" " </div>\n"
" <div class=\"key_column\" style=\"flex-grow: 2\">\n" " <br>\n");
" <span class=\"help_key\">w</span><br>\n"
" <span class=\"help_key\">s</span>\n" // NOTE(matt);
" </div>\n"
" <div class=\"key_column\" style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">d</span>\n"
" </div>\n"
" </div>\n"
" <div class=\"key_block\">\n"
" <span class=\"help_key\">h</span>\n"
" <span class=\"help_key\">j</span>\n"
" <span class=\"help_key\">k</span>\n"
" <span class=\"help_key\">l</span>\n"
" </div>\n"
" <div class=\"key_block\">\n"
" <div style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">←</span>\n"
" </div>\n"
" <div style=\"flex-grow: 2\">\n"
" <span class=\"help_key\">↑</span><br>\n"
" <span class=\"help_key\">↓</span>\n"
" </div>\n"
" <div style=\"flex-grow: 1\">\n"
" <span class=\"help_key\">→</span>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" <br>\n");
if(HasQuoteMenu) if(HasQuoteMenu)
{ {
@ -2726,6 +2700,8 @@ goto Cleanup;
" <span style=\"width: auto\" class=\"help_key unavailable\">Enter</span> <span class=\"help_text unavailable\">Jump to timecode</span><br>\n"); " <span style=\"width: auto\" class=\"help_key unavailable\">Enter</span> <span class=\"help_text unavailable\">Jump to timecode</span><br>\n");
} }
CopyStringToBuffer(&Menus, "\n");
if(HasQuoteMenu) if(HasQuoteMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
@ -2785,6 +2761,8 @@ goto Cleanup;
} }
} }
CopyStringToBuffer(&Menus, "\n");
if(HasQuoteMenu || HasReferenceMenu || HasCreditsMenu) if(HasQuoteMenu || HasReferenceMenu || HasCreditsMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
@ -2816,6 +2794,8 @@ goto Cleanup;
" <span class=\"help_key unavailable\">v</span> <span class=\"help_text unavailable\">Invert topics / media as per focus</span>\n"); " <span class=\"help_key unavailable\">v</span> <span class=\"help_text unavailable\">Invert topics / media as per focus</span>\n");
} }
CopyStringToBuffer(&Menus, "\n");
if(HasCreditsMenu) if(HasCreditsMenu)
{ {
CopyStringToBuffer(&Menus, CopyStringToBuffer(&Menus,
@ -2841,19 +2821,46 @@ goto Cleanup;
// TODO(matt): Maybe do something about indentation levels // TODO(matt): Maybe do something about indentation levels
CopyStringToBuffer(&Includes, CopyStringToBuffer(&Includes,
"<meta charset=\"UTF-8\">\n" "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera.css\">\n"
"\n"
" <!-- Load the player -->\n"
" <script type=\"text/javascript\" src=\"%s/cinera.js\"></script>\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera.css\">\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera__%s.css\">\n" " <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera__%s.css\">\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera_topics.css\">", " <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera_topics.css\">\n"
JSDir, "\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"generator\" content=\"Cinera\">\n",
CSSDir, CSSDir,
CSSDir, CSSDir,
HMML.metadata.project, HMML.metadata.project,
CSSDir); CSSDir);
if(Topics.Count || Media.Count)
{
CopyStringToBuffer(&Includes,
" <meta name=\"keywords\" content=\"");
if(Topics.Count > 0)
{
for(int i = 0; i < Topics.Count; ++i)
{
CopyStringToBuffer(&Includes, "%s, ", Topics.Category[i].Marker);
}
}
if(Media.Count > 0)
{
for(int i = 0; i < Media.Count; ++i)
{
CopyStringToBuffer(&Includes, "%s, ", Media.Category[i].WrittenText);
}
}
Includes.Ptr -= 2;
CopyStringToBuffer(&Includes, "\">\n\n");
}
CopyStringToBuffer(&Includes,
" <script type=\"text/javascript\" src=\"%s/cinera.js\"></script>\n",
JSDir);
CopyStringToBuffer(&Script, CopyStringToBuffer(&Script,
" <script type=\"text/javascript\">\n" " <script type=\"text/javascript\">\n"
"var menuState = [];\n"); "var menuState = [];\n");