hmml_to_html.c: Generate the Filter [#21]

This commit is contained in:
Matt Mascarenhas 2017-05-21 07:35:16 +01:00
parent 27143e291f
commit 3c32b5904b
3 changed files with 621 additions and 74 deletions

View File

@ -5,6 +5,10 @@ ctime -end ${0%.*}.ctm
exit exit
#endif #endif
#define DEBUG 1
// TODO(matt): Fully investigate the ClaimedMemory situation
typedef unsigned int bool; typedef unsigned int bool;
#define TRUE 1 #define TRUE 1
@ -19,9 +23,18 @@ typedef struct
{ {
char *Location; char *Location;
char *Ptr; char *Ptr;
char *ID;
int Size; int Size;
} buffer; } buffer;
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes
typedef struct
{
char Date[32];
char Text[512];
} quote_info;
typedef struct typedef struct
{ {
char Timecode[8]; char Timecode[8];
@ -42,19 +55,36 @@ typedef struct
typedef struct typedef struct
{ {
char Date[32]; char Category[32];
char Text[512]; bool IsMedium;
} quote_info; } category_info;
#define ArrayCount(A) sizeof(A)/sizeof(*(A)) #define ArrayCount(A) sizeof(A)/sizeof(*(A))
void void
ClaimBuffer(char *MemoryArena, int *ClaimedMemory, buffer *Buffer, int Size) ClaimBuffer(char *MemoryArena, int *ClaimedMemory, buffer *Buffer, char *ID, int Size)
{ {
Buffer->Location = MemoryArena + *ClaimedMemory; Buffer->Location = MemoryArena + *ClaimedMemory;
Buffer->Size = Size; Buffer->Size = Size;
Buffer->ID = ID;
*ClaimedMemory += Buffer->Size; *ClaimedMemory += Buffer->Size;
*Buffer->Location = '\0';
Buffer->Ptr = Buffer->Location; Buffer->Ptr = Buffer->Location;
#if DEBUG
printf(" Claimed: %s: %d\n"
" Total ClaimedMemory: %d\n\n", Buffer->ID, Buffer->Size, *ClaimedMemory);
#endif
}
void
DeclaimBuffer(buffer *Buffer, int *ClaimedMemory)
{
*Buffer->Location = '\0';
*ClaimedMemory -= Buffer->Size;
#if DEBUG
printf("Declaimed: %s\n"
" Total ClaimedMemory: %d\n\n", Buffer->ID, *ClaimedMemory);
#endif
} }
int int
@ -95,6 +125,15 @@ CopyBuffer(buffer *Dest, buffer *Src)
Src->Ptr = Src->Location; Src->Ptr = Src->Location;
while(*Src->Ptr) while(*Src->Ptr)
{ {
// TODO(matt)
{
if(Dest->Ptr - Dest->Location >= Dest->Size)
{
printf("Too big! Too big!\n");
__asm__("int3");
}
}
*Dest->Ptr++ = *Src->Ptr++; *Dest->Ptr++ = *Src->Ptr++;
} }
} }
@ -113,10 +152,28 @@ __attribute__ ((format (printf, 2, 3)))
void void
CopyStringToBuffer(buffer *Dest, char *Format, ...) CopyStringToBuffer(buffer *Dest, char *Format, ...)
{ {
// TODO(matt):
{
if(Dest->Ptr - Dest->Location >= Dest->Size)
{
printf("Too big! Too big!\n");
__asm__("int3");
}
}
va_list Args; va_list Args;
va_start(Args, Format); va_start(Args, Format);
int Length = vsprintf(Dest->Ptr, Format, Args); int Length = vsprintf(Dest->Ptr, Format, Args);
va_end(Args); va_end(Args);
// TODO(matt):
{
if(Length + (Dest->Ptr - Dest->Location) >= Dest->Size)
{
printf("Too big! Too big!\n");
__asm__("int3");
}
}
Dest->Ptr += Length; Dest->Ptr += Length;
} }
@ -235,15 +292,6 @@ SanitisePunctuation(char *String)
return String; return String;
} }
char *CategoryMedium[] =
{
"blackboard",
"owl",
"rant",
"research",
"run",
};
int int
BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMML_Reference Ref, HMML_Annotation Anno) BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMML_Reference Ref, HMML_Annotation Anno)
{ {
@ -280,6 +328,7 @@ BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMM
CopyString(ReferencesArray[UniqueRefs].ID, Ref.isbn); CopyString(ReferencesArray[UniqueRefs].ID, Ref.isbn);
CopyString(ReferencesArray[UniqueRefs].Source, Ref.author); CopyString(ReferencesArray[UniqueRefs].Source, Ref.author);
CopyString(ReferencesArray[UniqueRefs].RefTitle, Ref.title); CopyString(ReferencesArray[UniqueRefs].RefTitle, Ref.title);
//TODO(matt): Look into finding the best ISBN searcher the web has to offer
CopyString(ReferencesArray[UniqueRefs].URL, "http://www.isbnsearch.org/isbn/%s", Ref.isbn); CopyString(ReferencesArray[UniqueRefs].URL, "http://www.isbnsearch.org/isbn/%s", Ref.isbn);
} }
else if(Ref.url && Ref.article && Ref.author) else if(Ref.url && Ref.article && Ref.author)
@ -318,12 +367,48 @@ BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMM
return 0; return 0;
} }
char *CategoryMedium[][3] =
{
// category icon written name
{ "authored", "&#128490", "Chat Comment"}, // TODO(matt): Conditionally handle Chat vs Guest Comments
{ "blackboard", "&#128396", "Blackboard"},
{ "owl", "&#129417", "Owl of Shame"},
{ "default", "&#128430", "Programming"}, // TODO(matt): Potentially make this configurable per project
{ "rant", "&#128162", "Rant"},
{ "research", "&#128214", "Research"},
{ "run", "&#127939", "In-Game"} // TODO(matt): Potentially make this configurable per project
};
void
BuildFilter(category_info *CategoriesArray, int *UniqueCategories, char *Marker)
{
int i = 0;
for(i = 0; i < *UniqueCategories; ++i)
{
if(!StringsDiffer(Marker, CategoriesArray[i].Category))
{
return;
}
}
++*UniqueCategories;
CopyString(CategoriesArray[i].Category, Marker);
for(int j = 0; j < ArrayCount(CategoryMedium); ++j)
{
if(!StringsDiffer(CategoryMedium[j][0], Marker))
{
CategoriesArray[i].IsMedium = TRUE;
}
}
// TODO(matt): Sort the CategoriesArray
}
void void
BuildCategories(buffer *AnnotationClass, buffer *Category, int *MarkerIndex, bool *HasCategory, char *Marker) BuildCategories(buffer *AnnotationClass, buffer *Category, int *MarkerIndex, bool *HasCategory, char *Marker)
{ {
for(int i = 0; i < ArrayCount(CategoryMedium); ++i) for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{ {
if(!StringsDiffer(CategoryMedium[i], Marker)) if(!StringsDiffer(CategoryMedium[i][0], Marker))
{ {
CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(Marker)); CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(Marker));
++*MarkerIndex; ++*MarkerIndex;
@ -360,6 +445,11 @@ StringToInt(char *String)
int int
BuildQuote(quote_info *Info, char *Speaker, int ID) BuildQuote(quote_info *Info, char *Speaker, int ID)
{ {
// TODO(matt): Pull these paths from the config
// Also notable that there are different paths for different projects
//
// TODO(matt): Handle csv's double quote escaping stuff
char *QuoteDir = "/home/matt/git/GitHub/insofaras/25fc16d58a297a486334"; char *QuoteDir = "/home/matt/git/GitHub/insofaras/25fc16d58a297a486334";
if(!StringsDiffer(Speaker, "handmade_hero")) if(!StringsDiffer(Speaker, "handmade_hero"))
{ {
@ -385,12 +475,14 @@ BuildQuote(quote_info *Info, char *Speaker, int ID)
} }
fread(Buffer, Length, 1, File); fread(Buffer, Length, 1, File);
fclose(File); fclose(File);
// TODO(matt): Search the quote store in reverse
char *InPtr = Buffer; char *InPtr = Buffer;
while(InPtr - Buffer < Length) while(InPtr - Buffer < Length)
{ {
char InID[4] = {0};
char InID[4] = { 0 };
char *OutPtr = InID; char *OutPtr = InID;
while(*InPtr != ',') while(*InPtr != ',')
{ {
@ -442,7 +534,7 @@ GenerateTopicColours(buffer *Colour, char *Topic)
{ {
for(int i = 0; i < ArrayCount(CategoryMedium); ++i) for(int i = 0; i < ArrayCount(CategoryMedium); ++i)
{ {
if(!StringsDiffer(Topic, CategoryMedium[i])) if(!StringsDiffer(Topic, CategoryMedium[i][0]))
{ {
return; return;
} }
@ -450,6 +542,7 @@ GenerateTopicColours(buffer *Colour, char *Topic)
FILE *TopicsFile; FILE *TopicsFile;
char *TopicsBuffer; char *TopicsBuffer;
// TODO(matt): Consider (optionally) pulling this path from the config
if((TopicsFile = fopen("topics.css", "a+"))) if((TopicsFile = fopen("topics.css", "a+")))
{ {
fseek(TopicsFile, 0, SEEK_END); fseek(TopicsFile, 0, SEEK_END);
@ -772,7 +865,7 @@ main(int ArgC, char **Args)
// NOTE(matt): Init MemoryArena // NOTE(matt): Init MemoryArena
char *MemoryArena; char *MemoryArena;
int ArenaSize = 1024 * 1024; int ArenaSize = 1024 * 1024 * 4;
if(!(MemoryArena = calloc(ArenaSize, 1))) if(!(MemoryArena = calloc(ArenaSize, 1)))
{ {
perror(Args[0]); perror(Args[0]);
@ -790,6 +883,9 @@ main(int ArgC, char **Args)
buffer Title; buffer Title;
buffer QuoteMenu; buffer QuoteMenu;
buffer ReferenceMenu; buffer ReferenceMenu;
buffer FilterMenu;
buffer FilterTopics;
buffer FilterMedia;
buffer Player; buffer Player;
buffer Annotation; buffer Annotation;
@ -800,6 +896,8 @@ main(int ArgC, char **Args)
buffer Category; buffer Category;
buffer Colour; buffer Colour;
buffer FilterState;
buffer Master; buffer Master;
for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex) for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex)
@ -816,7 +914,7 @@ main(int ArgC, char **Args)
} }
#if CONFIG #if CONFIG
ClaimBuffer(MemoryArena, &ClaimedMemory, &Config, 1024); ClaimBuffer(MemoryArena, &ClaimedMemory, &Config, "Config", 1024);
#endif #endif
HMML_Output HMML = hmml_parse_file(InFile); HMML_Output HMML = hmml_parse_file(InFile);
@ -824,16 +922,28 @@ main(int ArgC, char **Args)
if(HMML.well_formed) if(HMML.well_formed)
{ {
ClaimBuffer(MemoryArena, &ClaimedMemory, &Title, 1024 * 16); ClaimBuffer(MemoryArena, &ClaimedMemory, &Master, "Master", 1024 * 512);
ClaimBuffer(MemoryArena, &ClaimedMemory, &Player, "Player", 1024 * 256);
ClaimBuffer(MemoryArena, &ClaimedMemory, &Title, "Title", 1024 * 16);
ClaimBuffer(MemoryArena, &ClaimedMemory, &FilterMenu, "FilterMenu", 1024 * 16);
ClaimBuffer(MemoryArena, &ClaimedMemory, &FilterTopics, "FilterTopics", 1024 * 8);
ClaimBuffer(MemoryArena, &ClaimedMemory, &FilterMedia, "FilterMedia", 1024 * 8);
ClaimBuffer(MemoryArena, &ClaimedMemory, &FilterState, "FilterState", 1024 * 4);
ClaimBuffer(MemoryArena, &ClaimedMemory, &ReferenceMenu, "ReferenceMenu", 1024 * 16);
ClaimBuffer(MemoryArena, &ClaimedMemory, &QuoteMenu, "QuoteMenu", 1024 * 16);
ref_info ReferencesArray[200] = { 0 }; ref_info ReferencesArray[200] = { 0 };
ClaimBuffer(MemoryArena, &ClaimedMemory, &Player, 1024 * 256); category_info CategoriesArray[64] = { 0 };
bool HasQuoteMenu = FALSE; bool HasQuoteMenu = FALSE;
bool HasReferenceMenu = FALSE; bool HasReferenceMenu = FALSE;
bool HasFilterMenu = FALSE;
int QuoteIdentifier = 0x3b1; int QuoteIdentifier = 0x3b1;
int RefIdentifier = 1; int RefIdentifier = 1;
int UniqueRefs = 0; int UniqueRefs = 0;
int UniqueCategories = 0;
CopyStringToBuffer(&Title, CopyStringToBuffer(&Title,
" <div class=\"title %s\">\n" " <div class=\"title %s\">\n"
@ -846,6 +956,9 @@ main(int ArgC, char **Args)
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
{ {
#if DEBUG
printf("%d\n", AnnotationIndex);
#endif
HMML_Annotation *Anno = HMML.annotations + AnnotationIndex; HMML_Annotation *Anno = HMML.annotations + AnnotationIndex;
bool HasCategory = FALSE; bool HasCategory = FALSE;
bool HasQuote = FALSE; bool HasQuote = FALSE;
@ -853,11 +966,10 @@ main(int ArgC, char **Args)
quote_info QuoteInfo = { 0 }; quote_info QuoteInfo = { 0 };
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationHeader, 512); ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationHeader, "AnnotationHeader", 512);
ClaimBuffer(MemoryArena, &ClaimedMemory, &Category, 256); ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationClass, "AnnotationClass", 256);
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationClass, 256); ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, "Text", 1024 * 4);
ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, 1024 * 4); ClaimBuffer(MemoryArena, &ClaimedMemory, &Colour, "Colour", 32);
ClaimBuffer(MemoryArena, &ClaimedMemory, &Colour, 32);
CopyStringToBuffer(&AnnotationHeader, CopyStringToBuffer(&AnnotationHeader,
" <div data-timestamp=\"%d\"", " <div data-timestamp=\"%d\"",
@ -868,6 +980,11 @@ TimecodeToSeconds(Anno->time));
if(Anno->author) if(Anno->author)
{ {
if(!HasFilterMenu)
{
HasFilterMenu = TRUE;
}
BuildFilter(CategoriesArray, &UniqueCategories, "authored");
CopyStringToBuffer(&AnnotationClass, " authored"); CopyStringToBuffer(&AnnotationClass, " authored");
CopyStringToBuffer(&Text, CopyStringToBuffer(&Text,
"<span class=\"author\" style=\"color: %s;\">%s</span> ", "<span class=\"author\" style=\"color: %s;\">%s</span> ",
@ -890,6 +1007,7 @@ Anno->author);
{ {
CopyStringToBuffer(&Text, CopyStringToBuffer(&Text,
// TODO(matt): Hoverbox // TODO(matt): Hoverbox
// We should get instructions on how to get this info in the config
"<a href=\"https://handmade.network/m/%s\" target=\"blank\" style=\"color: %s; text-decoration: none\">%.*s</a>", "<a href=\"https://handmade.network/m/%s\" target=\"blank\" style=\"color: %s; text-decoration: none\">%.*s</a>",
Anno->markers[MarkerIndex].marker, Anno->markers[MarkerIndex].marker,
StringToColourHash(&Colour, Anno->markers[MarkerIndex].marker), StringToColourHash(&Colour, Anno->markers[MarkerIndex].marker),
@ -901,6 +1019,7 @@ StringLength(Readable), InPtr);
{ {
CopyStringToBuffer(&Text, CopyStringToBuffer(&Text,
// TODO(matt): Hoverbox // TODO(matt): Hoverbox
// We should get instructions on how to get this info in the config
"<a href=\"https://%s.handmade.network/\" target=\"blank\" style=\"color: %s; text-decoration: none\">%s</a>", "<a href=\"https://%s.handmade.network/\" target=\"blank\" style=\"color: %s; text-decoration: none\">%s</a>",
Anno->markers[MarkerIndex].marker, Anno->markers[MarkerIndex].marker,
StringToColourHash(&Colour, Anno->markers[MarkerIndex].marker), StringToColourHash(&Colour, Anno->markers[MarkerIndex].marker),
@ -911,6 +1030,16 @@ Readable);
else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY) else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY)
{ {
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker); GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker);
// TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu)
{
HasFilterMenu = TRUE;
}
BuildFilter(CategoriesArray, &UniqueCategories, Anno->markers[MarkerIndex].marker);
if(!HasCategory)
{
ClaimBuffer(MemoryArena, &ClaimedMemory, &Category, "Category", 256);
}
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, Anno->markers[MarkerIndex].marker); BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, Anno->markers[MarkerIndex].marker);
} }
} }
@ -921,8 +1050,6 @@ Readable);
HMML_Reference *CurrentRef = Anno->references + RefIndex; HMML_Reference *CurrentRef = Anno->references + RefIndex;
if(!HasReferenceMenu) if(!HasReferenceMenu)
{ {
ClaimBuffer(MemoryArena, &ClaimedMemory, &ReferenceMenu, 1024 * 16);
CopyStringToBuffer(&ReferenceMenu, CopyStringToBuffer(&ReferenceMenu,
" <div class=\"menu\">\n" " <div class=\"menu\">\n"
" <span>References &#9660;</span>\n" " <span>References &#9660;</span>\n"
@ -987,7 +1114,7 @@ Readable);
AppendedIdentifier: AppendedIdentifier:
if(!HasReference) if(!HasReference)
{ {
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationData, 128); ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 128);
if(CurrentRef->isbn) if(CurrentRef->isbn)
{ {
CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->isbn); CopyStringToBuffer(&AnnotationData, " data-ref=\"%s", CurrentRef->isbn);
@ -1041,7 +1168,6 @@ AppendedIdentifier:
{ {
if(!HasQuoteMenu) if(!HasQuoteMenu)
{ {
ClaimBuffer(MemoryArena, &ClaimedMemory, &QuoteMenu, 1024 * 16);
CopyStringToBuffer(&QuoteMenu, CopyStringToBuffer(&QuoteMenu,
" <div class=\"menu\">\n" " <div class=\"menu\">\n"
" <span>Quotes &#9660;</span>\n" " <span>Quotes &#9660;</span>\n"
@ -1053,7 +1179,7 @@ AppendedIdentifier:
if(!HasReference) if(!HasReference)
{ {
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationData, 128); ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationData, "AnnotationData", 128);
CopyStringToBuffer(&AnnotationData, " data-ref=\"&#%d;", QuoteIdentifier); CopyStringToBuffer(&AnnotationData, " data-ref=\"&#%d;", QuoteIdentifier);
} }
else else
@ -1106,35 +1232,55 @@ Anno->time);
while(MarkerIndex < Anno->marker_count) while(MarkerIndex < Anno->marker_count)
{ {
GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker); GenerateTopicColours(&Colour, Anno->markers[MarkerIndex].marker);
ClaimedMemory -= Colour.Size; // TODO(matt): Maybe stuff this into BuildCategories
if(!HasFilterMenu)
{
HasFilterMenu = TRUE;
}
if(Anno->markers[MarkerIndex].marker)
{
BuildFilter(CategoriesArray, &UniqueCategories, Anno->markers[MarkerIndex].marker);
}
if(!HasCategory)
{
ClaimBuffer(MemoryArena, &ClaimedMemory, &Category, "Category", 256);
}
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, Anno->markers[MarkerIndex].marker); BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, Anno->markers[MarkerIndex].marker);
} }
CopyStringToBuffer(&AnnotationClass, "\""); CopyStringToBuffer(&AnnotationClass, "\"");
CopyBuffer(&AnnotationHeader, &AnnotationClass); CopyBuffer(&AnnotationHeader, &AnnotationClass);
ClaimedMemory -= AnnotationClass.Size;
if(HasQuote || HasReference) if(HasQuote || HasReference)
{ {
CopyStringToBuffer(&AnnotationData, "\""); CopyStringToBuffer(&AnnotationData, "\"");
CopyBuffer(&AnnotationHeader, &AnnotationData); CopyBuffer(&AnnotationHeader, &AnnotationData);
ClaimedMemory -= AnnotationData.Size; DeclaimBuffer(&AnnotationData, &ClaimedMemory);
} }
CopyStringToBuffer(&AnnotationHeader, ">\n"); CopyStringToBuffer(&AnnotationHeader, ">\n");
ClaimBuffer(MemoryArena, &ClaimedMemory, &Annotation, 1024 * 4); ClaimBuffer(MemoryArena, &ClaimedMemory, &Annotation, "Annotation", 1024 * 8);
CopyBuffer(&Annotation, &AnnotationHeader); CopyBuffer(&Annotation, &AnnotationHeader);
ClaimedMemory -= AnnotationHeader.Size;
CopyStringToBuffer(&Annotation, CopyStringToBuffer(&Annotation,
" <div class=\"content\"><span class=\"timecode\">%s</span>", " <div class=\"content\"><span class=\"timecode\">%s</span>",
Anno->time); Anno->time);
#if 0
// TODO(matt): Handle special-cases, i.e. default media, and possibly other things
if(!HasCategory)
{
ClaimBuffer(MemoryArena, &ClaimedMemory, &Category, "Category", 256);
BuildFilter(CategoriesArray, &UniqueCategories, "default");
BuildCategories(&AnnotationClass, &Category, &MarkerIndex, &HasCategory, "default");
}
#endif
if(HasCategory) if(HasCategory)
{ {
CopyStringToBuffer(&Category, "</span>"); CopyStringToBuffer(&Category, "</span>");
CopyBuffer(&Text, &Category); CopyBuffer(&Text, &Category);
ClaimedMemory -= Category.Size; DeclaimBuffer(&Category, &ClaimedMemory);
} }
*Text.Ptr = '\0'; *Text.Ptr = '\0';
@ -1155,7 +1301,6 @@ Anno->time);
Anno->time); Anno->time);
CopyBuffer(&Annotation, &Text); CopyBuffer(&Annotation, &Text);
ClaimedMemory -= Text.Size;
CopyStringToBuffer(&Annotation, "</div>\n" CopyStringToBuffer(&Annotation, "</div>\n"
" </div>\n" " </div>\n"
@ -1163,16 +1308,22 @@ Anno->time);
CopyBuffer(&Player, &Annotation); CopyBuffer(&Player, &Annotation);
ClaimedMemory -= Annotation.Size; DeclaimBuffer(&Annotation, &ClaimedMemory);
DeclaimBuffer(&Colour, &ClaimedMemory);
DeclaimBuffer(&Text, &ClaimedMemory);
DeclaimBuffer(&AnnotationClass, &ClaimedMemory);
DeclaimBuffer(&AnnotationHeader, &ClaimedMemory);
} }
#if DEBUG
printf("EOA\n\n");
#endif
if(HasQuoteMenu) if(HasQuoteMenu)
{ {
CopyStringToBuffer(&QuoteMenu, CopyStringToBuffer(&QuoteMenu,
" </div>\n" " </div>\n"
" </div>\n"); " </div>\n");
CopyBuffer(&Title, &QuoteMenu); CopyBuffer(&Title, &QuoteMenu);
ClaimedMemory -= QuoteMenu.Size;
} }
if(HasReferenceMenu) if(HasReferenceMenu)
@ -1226,9 +1377,108 @@ ReferencesArray[i].Identifier[j].Timecode);
" </div>\n" " </div>\n"
" </div>\n"); " </div>\n");
CopyBuffer(&Title, &ReferenceMenu); CopyBuffer(&Title, &ReferenceMenu);
ClaimedMemory -= ReferenceMenu.Size;
} }
if(HasFilterMenu)
{
CopyStringToBuffer(&FilterState, "var filterState = {\n");
for(int i = 0; i < UniqueCategories; ++i)
{
CopyStringToBuffer(&FilterState, "\"%s\":\t{ \"type\": \"%s\",\t\"off\": false },\n",
CategoriesArray[i].Category, CategoriesArray[i].IsMedium ? "medium" : "topic");
}
CopyStringToBuffer(&FilterState, "};\n"
"\n");
CopyStringToBuffer(&FilterMenu,
" <div class=\"menu filter\">\n"
" <span><img src=\"hues_HCL.png\"></span>\n"
" <div class=\"filter_container\">\n"
" <div class=\"filter_mode inclusive\">Filter mode: </div>\n"
" <div class=\"filters\">\n");
{
bool HasTopic = FALSE;
bool HasMedium = FALSE;
for(int i = 0; i < UniqueCategories; ++i)
{
if(CategoriesArray[i].IsMedium)
{
if(!HasMedium)
{
CopyStringToBuffer(&FilterMedia,
" <div class=\"filter_media\">\n"
" <div class=\"filter_title\">Media</div>\n");
HasMedium = TRUE;
}
int j;
for(j = 0; j < ArrayCount(CategoryMedium); ++j)
{
if(!StringsDiffer(CategoriesArray[i].Category, CategoryMedium[j][0]))
{
break;
}
}
CopyStringToBuffer(&FilterMedia,
" <div class=\"filter_content %s\">\n"
" <span class=\"icon\">%s</span><span class=\"text\">%s</span>\n"
" </div>\n",
CategoriesArray[i].Category,
CategoryMedium[j][1],
CategoryMedium[j][2]
);
/*
</div>
*/
}
else
{
if(!HasTopic)
{
CopyStringToBuffer(&FilterMenu,
" <div class=\"filter_topics\">\n"
" <div class=\"filter_title\">Topics</div>\n");
HasTopic = TRUE;
}
CopyStringToBuffer(&FilterTopics,
" <div class=\"filter_content %s\">\n"
" <span class=\"icon category %s\"></span><span class=\"text\">%s</span>\n"
" </div>\n",
CategoriesArray[i].Category,
CategoriesArray[i].Category,
CategoriesArray[i].Category);
}
}
if(HasTopic)
{
CopyStringToBuffer(&FilterTopics,
" </div>\n");
CopyBuffer(&FilterMenu, &FilterTopics);
}
if(HasMedium)
{
CopyStringToBuffer(&FilterMedia,
" </div>\n");
CopyBuffer(&FilterMenu, &FilterMedia);
}
}
CopyStringToBuffer(&FilterMenu,
" </div>\n"
" </div>\n"
" </div>\n");
}
CopyBuffer(&Title, &FilterMenu);
#if CONFIG #if CONFIG
// TODO(matt): Here is where I test ParseConfig // TODO(matt): Here is where I test ParseConfig
ParseConfig(&Config, HMML.metadata.annotator); ParseConfig(&Config, HMML.metadata.annotator);
@ -1243,8 +1493,10 @@ HMML.metadata.annotator);
" </div>\n"); " </div>\n");
//NOTE(matt): Collate the buffers! //NOTE(matt): Collate the buffers!
#if DEBUG
printf("Buffer Collation\n\n");
#endif
ClaimBuffer(MemoryArena, &ClaimedMemory, &Master, 1024 * 512);
CopyStringToBuffer(&Master, CopyStringToBuffer(&Master,
"<html>\n" "<html>\n"
" <head>\n" " <head>\n"
@ -1260,9 +1512,7 @@ HMML.metadata.annotator);
//NOTE(matt): Here is where we do all our CopyBuffer() calls //NOTE(matt): Here is where we do all our CopyBuffer() calls
CopyBuffer(&Master, &Title); CopyBuffer(&Master, &Title);
ClaimedMemory -= Title.Size;
CopyBuffer(&Master, &Player); CopyBuffer(&Master, &Player);
ClaimedMemory -= Player.Size;
// //
CopyStringToBuffer(&Master, CopyStringToBuffer(&Master,
@ -1299,6 +1549,292 @@ HMML.metadata.annotator);
" });\n" " });\n"
"}\n" "}\n"
"\n" "\n"
"var filter = document.querySelector(\".filter\");\n"
"var filterModeElement = filter.querySelector(\".filter_mode\");\n"
"var filterMode = filterModeElement.classList[1];\n");
if(HasFilterMenu)
{
CopyBuffer(&Master, &FilterState);
}
DeclaimBuffer(&QuoteMenu, &ClaimedMemory);
DeclaimBuffer(&ReferenceMenu, &ClaimedMemory);
DeclaimBuffer(&FilterState, &ClaimedMemory);
DeclaimBuffer(&FilterMedia, &ClaimedMemory);
DeclaimBuffer(&FilterTopics, &ClaimedMemory);
DeclaimBuffer(&FilterMenu, &ClaimedMemory);
DeclaimBuffer(&Title, &ClaimedMemory);
DeclaimBuffer(&Player, &ClaimedMemory);
CopyStringToBuffer(&Master,
"// Filter Mode Toggle\n"
"var testMarkers = document.querySelectorAll(\".marker\");\n"
"filterModeElement.addEventListener(\"click\", function(ev) {\n"
" if(filterMode == \"inclusive\")\n"
" {\n"
" filterModeElement.classList.remove(\"inclusive\");\n"
" filterModeElement.classList.add(\"exclusive\");\n"
" filterMode = \"exclusive\";\n"
"\n"
" for(var i = 0; i < testMarkers.length; ++i)\n"
" {\n"
" var testCategories = testMarkers[i].classList;\n"
" for(var j = 0; j < testCategories.length; ++j)\n"
" {\n"
" if((testCategories[j].startsWith(\"off_\")) && !testMarkers[i].classList.contains(\"skip\"))\n"
" {\n"
" testMarkers[i].classList.add(\"skip\");\n"
" }\n"
" }\n"
" }\n"
" }\n"
" else\n"
" {\n"
" filterModeElement.classList.remove(\"exclusive\");\n"
" filterModeElement.classList.add(\"inclusive\");\n"
" filterMode = \"inclusive\";\n"
"\n"
" for(var i = 0; i < testMarkers.length; ++i)\n"
" {\n"
" var testCategories = testMarkers[i].classList;\n"
" for(var j = 0; j < testCategories.length; ++j)\n"
" {\n"
" if((testCategories[j] in filterState || testCategories[j].startsWith(\"cat_\")) && testMarkers[i].classList.contains(\"skip\"))\n"
" {\n"
" testMarkers[i].classList.remove(\"skip\");\n"
" }\n"
" }\n"
" }\n"
" }\n"
"});\n"
"\n"
"// Filter Toggle\n"
"var filterCategories = filter.querySelectorAll(\".filter_topics .filter_content,.filter_media .filter_content\");\n"
"for(var i = 0; i < filterCategories.length; ++i)\n"
"{\n"
" filterCategories[i].addEventListener(\"click\", function(ev) {\n"
" var selectedCategory = this.classList[1];\n"
" filterState[selectedCategory].off = !filterState[selectedCategory].off;\n"
"\n"
" if(filterState[selectedCategory].off)\n"
" {\n"
" this.classList.add(\"off\");\n"
" var testMarkers = document.querySelectorAll(\".marker.\" + selectedCategory + \", .marker.cat_\" + selectedCategory);\n"
" for(var j = 0; j < testMarkers.length; ++j)\n"
" {\n"
" if(filterState[selectedCategory].type == \"topic\")\n"
" {\n"
" testMarkers[j].classList.remove(\"cat_\" + selectedCategory);\n"
" testMarkers[j].classList.add(\"off_\" + selectedCategory);\n"
" var markerCategories = testMarkers[j].querySelectorAll(\".category.\" + selectedCategory);\n"
" for(var k = 0; k < markerCategories.length; ++k)\n"
" {\n"
" if(markerCategories[k].classList.contains(selectedCategory))\n"
" {\n"
" markerCategories[k].classList.add(\"off\");\n"
" }\n"
" }\n"
" }\n"
" else\n"
" {\n"
" testMarkers[j].classList.remove(selectedCategory);\n"
" testMarkers[j].classList.add(\"off_\" + selectedCategory);\n"
" }\n"
"\n"
" Skipping = 1;\n"
" if(filterMode == \"exclusive\")\n"
" {\n"
" testMarkers[j].classList.add(\"skip\");\n"
" }\n"
" else\n"
" {\n"
" var markerClasses = testMarkers[j].classList;\n"
" for(var k = 0; k < markerClasses.length; ++k)\n"
" {\n"
" if(markerClasses[k] in filterState || markerClasses[k].replace(/^cat_/, \"\") in filterState)\n"
" {\n"
" Skipping = 0;\n"
" }\n"
" }\n"
" if(Skipping)\n"
" {\n"
" testMarkers[j].classList.add(\"skip\");\n"
" }\n"
" }\n"
"\n"
" }\n"
" }\n"
" else\n"
" {\n"
" this.classList.remove(\"off\");\n"
" var testMarkers = document.querySelectorAll(\".marker.off_\" + selectedCategory);\n"
" for(var j = 0; j < testMarkers.length; ++j)\n"
" {\n"
" if(filterState[selectedCategory].type == \"topic\")\n"
" {\n"
" testMarkers[j].classList.remove(\"off_\" + selectedCategory);\n"
" testMarkers[j].classList.add(\"cat_\" + selectedCategory);\n"
" var markerCategories = testMarkers[j].querySelectorAll(\".category.\" + selectedCategory);\n"
" for(var k = 0; k < markerCategories.length; ++k)\n"
" {\n"
" if(markerCategories[k].classList.contains(selectedCategory))\n"
" {\n"
" markerCategories[k].classList.remove(\"off\");\n"
" }\n"
" }\n"
" }\n"
" else\n"
" {\n"
" testMarkers[j].classList.remove(\"off_\" + selectedCategory);\n"
" testMarkers[j].classList.add(selectedCategory);\n"
" }\n"
"\n"
" Skipping = 0;\n"
" if(filterMode == \"inclusive\")\n"
" {\n"
" testMarkers[j].classList.remove(\"skip\");\n"
" }\n"
" else\n"
" {\n"
" var markerClasses = testMarkers[j].classList;\n"
" for(var k = 0; k < markerClasses.length; ++k)\n"
" {\n"
" if(markerClasses[k].startsWith(\"off_\"))\n"
" {\n"
" Skipping = 1;\n"
" }\n"
" }\n"
" if(!Skipping)\n"
" {\n"
" testMarkers[j].classList.remove(\"skip\");\n"
" }\n"
" }\n"
" }\n"
" }\n"
" });\n"
"}\n"
"\n"
"var refSources = document.querySelectorAll(\".refs .ref\");\n"
"for (var i = 0; i < refSources.length; ++i) {\n"
" refSources[i].addEventListener(\"click\", function(ev) {\n"
" if (player) {\n"
" player.pause();\n"
" }\n"
" });\n"
"}\n"
"\n"
"function resetFade()\n"
"{\n"
" filter.classList.remove(\"responsible\");\n"
" filter.querySelector(\".filter_mode\").classList.remove(\"responsible\");\n"
" var responsibleCategories = filter.querySelectorAll(\".filter_content.responsible\");\n"
" for(var i = 0; i < responsibleCategories.length; ++i)\n"
" {\n"
" responsibleCategories[i].classList.remove(\"responsible\");\n"
" }\n"
"}\n"
"\n"
"var sourceMenus = document.querySelectorAll(\".menu\");\n"
"function onRefChanged(ref, element) {\n"
" if(element.classList.contains(\"skip\"))\n"
" {\n"
" if(!filter.classList.contains(\"responsible\"))\n"
" {\n"
" filter.classList.add(\"responsible\");\n"
" }\n"
"\n"
" for(var selector = 0; selector < element.classList.length; ++selector)\n"
" {\n"
" if(element.classList[selector].startsWith(\"off_\"))\n"
" {\n"
" if(!filter.querySelector(\".filter_content.\" + element.classList[selector].replace(/^off_/, \"\")).classList.contains(\"responsible\"))\n"
" {\n"
" filter.querySelector(\".filter_content.\" + element.classList[selector].replace(/^off_/, \"\")).classList.add(\"responsible\");\n"
" }\n"
" }\n"
" if((element.classList[selector].startsWith(\"cat_\") || element.classList[selector] in filterState))\n"
" {\n"
" if(!filter.querySelector(\".filter_mode\").classList.add(\"responsible\"))\n"
" {\n"
" filter.querySelector(\".filter_mode\").classList.add(\"responsible\");\n"
" }\n"
" }\n"
" setTimeout(resetFade, 8000);\n"
" }\n"
" player.jumpToNextMarker();\n"
" return;\n"
" }\n"
"\n"
" for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)\n"
" {\n"
" var SetMenu = 0;\n"
" if (ref !== undefined && ref !== null) {\n"
" var refElements = sourceMenus[MenuIndex].querySelectorAll(\".refs .ref\");\n"
" var refs = ref.split(\",\");\n"
"\n"
" for (var i = 0; i < refElements.length; ++i) {\n"
" if (refs.includes(refElements[i].getAttribute(\"data-id\"))) {\n"
" refElements[i].classList.add(\"current\");\n"
" SetMenu = 1;\n"
" } else {\n"
" refElements[i].classList.remove(\"current\");\n"
" }\n"
" }\n"
" if(SetMenu) {\n"
" sourceMenus[MenuIndex].classList.add(\"current\");\n"
" } else {\n"
" sourceMenus[MenuIndex].classList.remove(\"current\");\n"
" }\n"
"\n"
" } else {\n"
" sourceMenus[MenuIndex].classList.remove(\"current\");\n"
" var refs = sourceMenus[MenuIndex].querySelectorAll(\".refs .ref\");\n"
" for (var i = 0; i < refs.length; ++i) {\n"
" refs[i].classList.remove(\"current\");\n"
" }\n"
" }\n"
" }\n"
"}\n"
" </script>\n"
" </body>\n"
"</html>\n");
#if 0
CopyStringToBuffer(&Master,
" <script>\n"
" var player = new Player(document.querySelector(\".player_container\"), onRefChanged);\n"
" window.addEventListener(\"resize\", function() { player.updateSize(); });\n"
" document.addEventListener(\"keypress\", function(ev) {\n"
" switch (ev.key) {\n"
" case 'n':\n"
" case 'd':\n"
" case 's': {\n"
" player.jumpToNextMarker();\n"
" } break;\n"
"\n"
" case 'p':\n"
" case 'a':\n"
" case 'w': {\n"
" player.jumpToPrevMarker();\n"
" } break;\n"
" }\n"
"});\n"
"\n"
"var refTimecodes = document.querySelectorAll(\".refs .ref .timecode\");\n"
"for (var i = 0; i < refTimecodes.length; ++i) {\n"
" refTimecodes[i].addEventListener(\"click\", function(ev) {\n"
" if (player) {\n"
" var time = ev.currentTarget.getAttribute(\"data-timestamp\");\n"
" player.setTime(parseInt(time, 10));\n"
" player.play();\n"
" ev.preventDefault();\n"
" ev.stopPropagation();\n"
" return false;\n"
" }\n"
" });\n"
"}\n"
"\n"
"var refSources = document.querySelectorAll(\".refs .ref\");\n" "var refSources = document.querySelectorAll(\".refs .ref\");\n"
"for (var i = 0; i < refSources.length; ++i) {\n" "for (var i = 0; i < refSources.length; ++i) {\n"
" refSources[i].addEventListener(\"click\", function(ev) {\n" " refSources[i].addEventListener(\"click\", function(ev) {\n"
@ -1343,6 +1879,7 @@ HMML.metadata.annotator);
" </script>\n" " </script>\n"
" </body>\n" " </body>\n"
"</html>\n"); "</html>\n");
#endif
FILE *OutFile; FILE *OutFile;
if(!(OutFile = fopen("out.html", "w"))) if(!(OutFile = fopen("out.html", "w")))
@ -1355,7 +1892,7 @@ HMML.metadata.annotator);
fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile);
fclose(OutFile); fclose(OutFile);
ClaimedMemory -= Master.Size; DeclaimBuffer(&Master, &ClaimedMemory);
} }
else else
{ {

View File

@ -1,8 +1,8 @@
.title.riscy, .title.riscy,
.title.riscy > .menu .refs, .title.riscy > .menu .refs,
.title.riscy > .menu .filter, .title.riscy > .menu .filter_container,
.title.riscy > .menu > .refs .ref, .title.riscy > .menu > .refs .ref,
.title.riscy > .menu > .filter .filter_mode, .title.riscy > .menu > .filter_container .filter_mode,
.markers_container.riscy, .markers_container.riscy,
.markers_container.riscy > .marker { .markers_container.riscy > .marker {
background-color: #EEE; background-color: #EEE;
@ -23,8 +23,8 @@
.title.riscy > .menu:hover, .title.riscy > .menu:hover,
.title.riscy > .menu > .refs .ref:hover, .title.riscy > .menu > .refs .ref:hover,
.title.riscy > .menu > .filter .filter_mode:hover, .title.riscy > .menu > .filter_container .filter_mode:hover,
.title.riscy > .menu > .filter .filter_content:hover, .title.riscy > .menu > .filter_container .filter_content:hover,
.markers_container.riscy > .marker:hover > .content { .markers_container.riscy > .marker:hover > .content {
background-color: #FFF8E7; background-color: #FFF8E7;
} }
@ -46,7 +46,7 @@
.title.riscy > .menu > .refs .ref .source, .title.riscy > .menu > .refs .ref .source,
.title.riscy > .menu > .refs .ref .quote_byline, .title.riscy > .menu > .refs .ref .quote_byline,
.title > .menu > .filter .filter_content.off .text { .title > .menu > .filter_container .filter_content.off .text {
color: #888; color: #888;
} }
@ -96,7 +96,11 @@
* .markers_container.riscy > marker.run * .markers_container.riscy > marker.run
*/ */
/* TODO(matt): Actually style this */ @keyframes riscy_fade_mode {
0% { color: #FFF; }
100% { color: #000; }
}
@keyframes riscy_fade_text { @keyframes riscy_fade_text {
0% { color: #FFF; } 0% { color: #FFF; }
100% { color: #888; } 100% { color: #888; }
@ -107,12 +111,18 @@
100% { background-color: #EEE; } 100% { background-color: #EEE; }
} }
/* TODO(matt): Get this to work! */
.title.riscy > .menu > .filter_container .filter_mode.responsible {
animation-name: riscy_fade_mode;
}
.title.riscy .filter_content.responsible .text { .title.riscy .filter_content.responsible .text {
animation-name: riscy_fade_text; animation-name: riscy_fade_text;
} }
.title.riscy > .menu.filter.responsible, .title.riscy > .menu.filter.responsible,
.title.riscy .filter_content.responsible, .title.riscy .filter_content.responsible,
.title.riscy > .menu > .filter .filter_mode.responsible { .title.riscy > .menu > .filter_container .filter_mode.responsible {
animation-name: riscy_fade_background; animation-name: riscy_fade_background;
} }

View File

@ -29,14 +29,14 @@
.title > .menu.filter.responsible, .title > .menu.filter.responsible,
.title .filter_content.responsible, .title .filter_content.responsible,
.title .filter_content.responsible .text, .title .filter_content.responsible .text,
.title > .menu > .filter .filter_mode.responsible { .title > .menu > .filter_container .filter_mode.responsible {
animation-duration: 8s; animation-duration: 8s;
animation-timing-function: ease-out; animation-timing-function: ease-out;
animation-iteration-count: 1; animation-iteration-count: 1;
} }
.title > .menu .refs, .title > .menu .refs,
.title > .menu .filter { .title > .menu .filter_container {
border: 1px solid; border: 1px solid;
border-top: none; border-top: none;
display: none; display: none;
@ -51,12 +51,12 @@
} }
.title > .menu:hover .refs, .title > .menu:hover .refs,
.title > .menu:hover .filter { .title > .menu:hover .filter_container {
display: block; display: block;
} }
.title > .menu > .refs .ref, .title > .menu > .refs .ref,
.title > .menu > .refs .filter { /* TODO(matt): See what this is! */ .title > .menu > .refs .filter_container { /* TODO(matt): See what this is! */
border-bottom: 1px solid; border-bottom: 1px solid;
padding: 10px; padding: 10px;
display: flex; display: flex;
@ -66,7 +66,7 @@
} }
.title > .menu > .refs .ref:last-child, .title > .menu > .refs .ref:last-child,
.title > .menu > .refs .filter:last-child { /* TODO(matt): See what this is! */ .title > .menu > .refs .filter_container:last-child { /* TODO(matt): See what this is! */
border: none; border: none;
} }
@ -82,7 +82,7 @@
} }
.title > .menu > .refs .ref .ref_content, .title > .menu > .refs .ref .ref_content,
.title > .menu > .refs .ref .filter_content { .title > .menu > .filter_content {
margin-bottom: 8px; margin-bottom: 8px;
width: 100%; width: 100%;
} }
@ -120,60 +120,60 @@
margin-right: 4px; margin-right: 4px;
} }
.title > .menu > .filter .filter_mode { .title > .menu > .filter_container .filter_mode {
cursor: pointer; cursor: pointer;
border-bottom: 1px solid; border-bottom: 1px solid;
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
} }
.title > .menu > .filter .filter_mode.exclusive:after { .title > .menu > .filter_container .filter_mode.exclusive:after {
content: "exclusive"; content: "exclusive";
} }
.title > .menu > .filter .filter_mode.inclusive:after { .title > .menu > .filter_container .filter_mode.inclusive:after {
content: "inclusive"; content: "inclusive";
} }
.title > .menu > .filter .filter_title { .title > .menu > .filter_container .filter_title {
font-size: 9px; font-size: 9px;
text-align: center; text-align: center;
} }
.title > .menu > .filter .filters { .title > .menu > .filter_container .filters {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
} }
.title > .menu > .filter .filters > * { .title > .menu > .filter_container .filters > * {
width: 50%; width: 50%;
} }
.title > .menu > .filter .filter_content { .title > .menu > .filter_container .filter_content {
cursor: pointer; cursor: pointer;
} }
.title > .menu > .filter .filter_content .icon { .title > .menu > .filter_container .filter_content .icon {
margin: 0 4px; margin: 0 4px;
} }
.title > .menu > .filter > .filter_media .filter_content .icon { .title > .menu > .filter_container > .filter_media .filter_content .icon {
margin: 0 .5em 0 4px; margin: 0 .5em 0 4px;
} }
.title > .menu > .filter .filter_content.off .icon { .title > .menu > .filter_container .filter_content.off .icon {
background: transparent; background: transparent;
} }
.title > .menu > .filter .filter_content.rant .icon { .title > .menu > .filter_container .filter_content.rant .icon {
color: #F00; color: #F00;
} }
.title > .menu > .filter .filter_media .filter_content.off .icon { .title > .menu > .filter_container .filter_media .filter_content.off .icon {
opacity: 0.32; opacity: 0.32;
} }
.title > .menu > .filter .filter_content.rant .text, .title > .menu > .filter_container .filter_content.rant .text,
.markers_container > .marker.rant .content { .markers_container > .marker.rant .content {
font-variant: small-caps; font-variant: small-caps;
} }
@ -254,18 +254,18 @@
margin: 4px; margin: 4px;
} }
.title > .menu > .filter .filter_content { .title > .menu > .filter_container .filter_content {
display: flex; display: flex;
} }
.title > .menu > .filter .filter_content .category, .title > .menu > .filter_container .filter_content .category,
.markers_container > .marker .content .categories .category { .markers_container > .marker .content .categories .category {
border-radius: 50%; border-radius: 50%;
height: 5px; height: 5px;
width: 5px; width: 5px;
} }
.title > .menu > .filter .filter_content .category.off, .title > .menu > .filter_container .filter_content .category.off,
.markers_container > .marker .content .categories .category.off { .markers_container > .marker .content .categories .category.off {
background: transparent; background: transparent;
} }
@ -274,7 +274,7 @@
margin-left: 2px; margin-left: 2px;
} }
.title > .menu > .filter .filter_content .icon { .title > .menu > .filter_container .filter_content .icon {
margin-right: 8px; margin-right: 8px;
} }