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
{
EDITION_SINGLE = 0,
EDITION_PROJECT = 1,
EDITION_NETWORK = 2
} EDITION;
EDITION_SINGLE,
EDITION_PROJECT,
EDITION_NETWORK
};
typedef struct
{
@ -72,6 +72,12 @@ typedef struct
char WrittenText[32];
} category_info;
typedef struct
{
category_info Category[64];
int Count;
} categories;
// TODO(matt): Parse this stuff out of a config file
typedef struct
{
@ -214,6 +220,7 @@ CopyBuffer(buffer *Dest, buffer *Src)
}
*Dest->Ptr++ = *Src->Ptr++;
}
*Dest->Ptr = '\0';
}
__attribute__ ((format (printf, 2, 3)))
@ -472,10 +479,10 @@ SanitisePunctuation(char *String)
enum
{
CreditsError_NoHost = 1 << 0,
CreditsError_NoAnnotator = 1 << 1,
CreditsError_NoCredentials = 1 << 2
} CreditsErrorCodes;
CreditsError_NoHost,
CreditsError_NoAnnotator,
CreditsError_NoCredentials
};
int
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
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;
int i = 0;
for(i = 0; i < ArrayCount(CategoryMedium); ++i)
int CategoryMediumIndex;
for(CategoryMediumIndex = 0; CategoryMediumIndex < ArrayCount(CategoryMedium); ++CategoryMediumIndex)
{
if(!StringsDiffer(CategoryMedium[i].Medium, Marker))
if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, Marker))
{
IsMedium = TRUE;
*HasMedium = TRUE;
break;
}
}
int Offset;
int CategoryIndex;
if(IsMedium)
{
int j = 0;
for(j = 0; j < *UniqueMedia; ++j)
for(CategoryIndex = 0; CategoryIndex < Media->Count; ++CategoryIndex)
{
if(!StringsDiffer(CategoryMedium[i].Medium, MediaArray[j].Marker))
if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, Media->Category[CategoryIndex].Marker))
{
return;
}
if((Offset = StringsDiffer(CategoryMedium[i].WrittenName, MediaArray[j].WrittenText)) < 0)
if((StringsDiffer(CategoryMedium[CategoryMediumIndex].WrittenName, Media->Category[CategoryIndex].WrittenText)) < 0)
{
int k;
for(k = *UniqueMedia; k > j; --k)
int CategoryCount;
for(CategoryCount = Media->Count; CategoryCount > CategoryIndex; --CategoryCount)
{
CopyString(MediaArray[k].Marker, MediaArray[k-1].Marker);
CopyString(MediaArray[k].WrittenText, MediaArray[k-1].WrittenText);
CopyString(Media->Category[CategoryCount].Marker, Media->Category[CategoryCount-1].Marker);
CopyString(Media->Category[CategoryCount].WrittenText, Media->Category[CategoryCount-1].WrittenText);
}
CopyString(MediaArray[k].Marker, CategoryMedium[i].Medium);
CopyString(MediaArray[k].WrittenText, CategoryMedium[i].WrittenName);
CopyString(Media->Category[CategoryCount].Marker, CategoryMedium[CategoryMediumIndex].Medium);
CopyString(Media->Category[CategoryCount].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName);
break;
}
}
if(j == *UniqueMedia)
if(CategoryIndex == Media->Count)
{
CopyString(MediaArray[j].Marker, CategoryMedium[i].Medium);
CopyString(MediaArray[j].WrittenText, CategoryMedium[i].WrittenName);
CopyString(Media->Category[CategoryIndex].Marker, CategoryMedium[CategoryMediumIndex].Medium);
CopyString(Media->Category[CategoryIndex].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName);
}
++*UniqueMedia;
return;
++Media->Count;
}
else
{
int i = 0;
for(i = 0; i < *UniqueTopics; ++i)
*HasTopic = TRUE;
for(CategoryIndex = 0; CategoryIndex < Topics->Count; ++CategoryIndex)
{
if(!StringsDiffer(Marker, TopicsArray[i].Marker))
if(!StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker))
{
return;
}
if((Offset = StringsDiffer(Marker, TopicsArray[i].Marker)) < 0)
if((StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker)) < 0)
{
int j;
for(j = *UniqueTopics; j > i; --j)
int CategoryCount;
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;
}
}
if(i == *UniqueTopics)
if(CategoryIndex == Topics->Count)
{
CopyString(TopicsArray[i].Marker, Marker);
CopyString(Topics->Category[CategoryIndex].Marker, Marker);
}
++*UniqueTopics;
return;
++Topics->Count;
}
return;
}
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 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(LocalTopics.Count > 0)
{
if(!StringsDiffer(CategoryMedium[i].Medium, Marker))
CopyStringToBuffer(TopicDots, "<span class=\"categories\">");
for(int i = 0; i < LocalTopics.Count; ++i)
{
CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(Marker));
*HasMedium = TRUE;
++*MarkerIndex;
return;
}
}
if(*HasCategory == FALSE)
{
CopyStringToBuffer(Category, "<span class=\"categories\">");
*HasCategory = TRUE;
}
// NOTE(matt): Iterate through the Category->Location looking for "Marker", and bail if we find it
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(TopicDots, "<div title=\"%s\" class=\"category %s\"></div>",
SanitisePunctuation(LocalTopics.Category[i].Marker),
SanitisePunctuation(LocalTopics.Category[i].Marker));
CopyStringToBuffer(AnnotationClass, " cat_%s",
SanitisePunctuation(Marker));
SanitisePunctuation(LocalTopics.Category[i].Marker));
}
CopyStringToBuffer(TopicDots, "</span>");
}
++*MarkerIndex;
return;
for(int i = 0; i < LocalMedia.Count; ++i)
{
CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(LocalMedia.Category[i].Marker));
}
CopyStringToBuffer(AnnotationClass, "\"");
}
int
@ -1448,7 +1420,10 @@ PrintUsage(char *BinaryLocation, char *DefaultCSSDir, char *DefaultImagesDir, ch
" <!-- __CINERA_PLAYER__ -->\n"
" <!-- __CINERA_SCRIPT__ --> (must come after <!-- __CINERA_PLAYER__ -->)\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);
}
@ -1814,7 +1789,7 @@ main(int ArgC, char **Args)
buffer AnnotationClass;
buffer AnnotationData;
buffer Text;
buffer Category;
buffer TopicDots;
buffer Script;
buffer FilterState;
@ -1881,8 +1856,8 @@ main(int ArgC, char **Args)
ClaimBuffer(&MemoryArena, &ClaimedMemory, &FilterState, "FilterState", Kilobytes(4));
ref_info ReferencesArray[200] = { 0 };
category_info TopicsArray[56] = { 0 };
category_info MediaArray[8] = { 0 };
categories Topics = { 0 };
categories Media = { 0 };
bool HasQuoteMenu = FALSE;
bool HasReferenceMenu = FALSE;
@ -1892,8 +1867,6 @@ main(int ArgC, char **Args)
int QuoteIdentifier = 0x3b1;
int RefIdentifier = 1;
int UniqueRefs = 0;
int UniqueTopics = 0;
int UniqueMedia = 0;
CopyStringToBuffer(&Menus,
" <div class=\"title %s\">\n"
@ -1935,7 +1908,9 @@ goto Cleanup;
printf("%d\n", AnnotationIndex);
#endif
HMML_Annotation *Anno = HMML.annotations + AnnotationIndex;
bool HasCategory = FALSE;
categories LocalTopics = { 0 };
bool HasTopic = FALSE;
categories LocalMedia = { 0 };
bool HasMedium = FALSE;
bool HasQuote = FALSE;
bool HasReference = FALSE;
@ -1947,13 +1922,13 @@ goto Cleanup;
// AnnotationClass
// AnnotationData
// Text
// Category
// TopicDots
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationHeader, "AnnotationHeader", 512);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationClass, "AnnotationClass", 256);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 512);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Text, "Text", Kilobytes(4));
ClaimBuffer(&MemoryArena, &ClaimedMemory, &Category, "Category", 512);
ClaimBuffer(&MemoryArena, &ClaimedMemory, &TopicDots, "TopicDots", 512);
CopyStringToBuffer(&AnnotationHeader,
" <div data-timestamp=\"%d\"",
@ -1968,7 +1943,7 @@ goto Cleanup;
{
HasFilterMenu = TRUE;
}
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, "authored");
InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, "authored");
CopyStringToBuffer(&AnnotationClass, " authored");
hsl_colour AuthorColour;
StringToColourHash(&AuthorColour, Anno->author);
@ -2065,13 +2040,12 @@ goto Cleanup;
else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY)
{
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir);
// TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu)
{
HasFilterMenu = TRUE;
}
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, Anno->markers[MarkerIndex].marker);
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, &HasMedium, Anno->markers[MarkerIndex].marker);
InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker); // Global
InsertCategory(&LocalTopics, &LocalMedia, &HasTopic, &HasMedium, Anno->markers[MarkerIndex].marker); // Local
}
}
@ -2242,6 +2216,7 @@ goto Cleanup;
break;
default:
*Text.Ptr++ = *InPtr++;
*Text.Ptr = '\0';
break;
}
}
@ -2327,25 +2302,25 @@ goto Cleanup;
while(MarkerIndex < Anno->marker_count)
{
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker, CSSDir);
// TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu)
{
HasFilterMenu = TRUE;
}
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)
{
BuildFilter(TopicsArray, &UniqueTopics, MediaArray, &UniqueMedia, DefaultMedium);
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, &HasMedium, DefaultMedium);
InsertCategory(&Topics, &Media, &HasTopic, &HasMedium, DefaultMedium);
InsertCategory(&LocalTopics, &LocalMedia, &HasTopic, &HasMedium, DefaultMedium);
}
CopyStringToBuffer(&AnnotationClass, "\"");
BuildCategories(&AnnotationClass, &TopicDots, LocalTopics, LocalMedia, &MarkerIndex);
CopyBuffer(&AnnotationHeader, &AnnotationClass);
if(HasQuote || HasReference)
@ -2360,17 +2335,13 @@ goto Cleanup;
" <div class=\"content\"><span class=\"timecode\">%s</span>",
Anno->time);
if(HasCategory)
{
CopyStringToBuffer(&Category, "</span>");
CopyBuffer(&Text, &Category);
}
// NOTE(matt): This feels a bit janky...
*Text.Ptr = '\0';
CopyBuffer(&Annotation, &Text);
if(HasTopic)
{
CopyBuffer(&Annotation, &TopicDots);
}
CopyStringToBuffer(&Annotation, "</div>\n"
" <div class=\"progress faded\">\n"
" <div class=\"content\"><span class=\"timecode\">%s</span>",
@ -2393,13 +2364,13 @@ goto Cleanup;
CopyBuffer(&Player, &Annotation);
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// Category
// TopicDots
// Text
// AnnotationData
// AnnotationClass
// AnnotationHeader
DeclaimBuffer(&Category, &ClaimedMemory);
DeclaimBuffer(&TopicDots, &ClaimedMemory);
DeclaimBuffer(&Text, &ClaimedMemory);
DeclaimBuffer(&AnnotationData, &ClaimedMemory);
DeclaimBuffer(&AnnotationClass, &ClaimedMemory);
@ -2478,15 +2449,15 @@ goto Cleanup;
{
CopyStringToBuffer(&FilterState,
" 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",
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",
MediaArray[i].Marker, "medium");
Media.Category[i].Marker, "medium");
}
CopyStringToBuffer(&FilterState,
" };\n");
@ -2502,7 +2473,7 @@ goto Cleanup;
bool HasTopic = FALSE;
bool HasMedium = FALSE;
for(int i = 0; i < UniqueTopics; ++i)
for(int i = 0; i < Topics.Count; ++i)
{
if(!HasTopic)
{
@ -2516,12 +2487,12 @@ goto Cleanup;
" <div class=\"filter_content %s\">\n"
" <span class=\"icon category %s\"></span><span class=\"text\">%s</span>\n"
" </div>\n",
TopicsArray[i].Marker,
TopicsArray[i].Marker,
TopicsArray[i].Marker);
Topics.Category[i].Marker,
Topics.Category[i].Marker,
Topics.Category[i].Marker);
}
for(int i = 0; i < UniqueMedia; ++i)
for(int i = 0; i < Media.Count; ++i)
{
if(!HasMedium)
{
@ -2535,7 +2506,7 @@ goto Cleanup;
int 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;
}
@ -2545,7 +2516,7 @@ goto Cleanup;
" <div class=\"filter_content %s\">\n"
" <span class=\"icon\">%s</span><span class=\"text\">%s</span>\n"
" </div>\n",
MediaArray[i].Marker,
Media.Category[i].Marker,
CategoryMedium[j].Icon,
CategoryMedium[j].WrittenName
);
@ -2589,6 +2560,7 @@ goto Cleanup;
" <div class=\"help_container\">\n"
" <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n"
"\n"
/* NOTE(matt) */
" <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");
@ -2688,6 +2660,8 @@ goto Cleanup;
" </div>\n"
" <br>\n");
// NOTE(matt);
if(HasQuoteMenu)
{
CopyStringToBuffer(&Menus,
@ -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");
}
CopyStringToBuffer(&Menus, "\n");
if(HasQuoteMenu)
{
CopyStringToBuffer(&Menus,
@ -2785,6 +2761,8 @@ goto Cleanup;
}
}
CopyStringToBuffer(&Menus, "\n");
if(HasQuoteMenu || HasReferenceMenu || HasCreditsMenu)
{
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");
}
CopyStringToBuffer(&Menus, "\n");
if(HasCreditsMenu)
{
CopyStringToBuffer(&Menus,
@ -2841,19 +2821,46 @@ goto Cleanup;
// TODO(matt): Maybe do something about indentation levels
CopyStringToBuffer(&Includes,
"<meta charset=\"UTF-8\">\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.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\">",
JSDir,
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%s/cinera_topics.css\">\n"
"\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"generator\" content=\"Cinera\">\n",
CSSDir,
CSSDir,
HMML.metadata.project,
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,
" <script type=\"text/javascript\">\n"
"var menuState = [];\n");