2017-03-10 14:19:25 +00:00
|
|
|
#if 0
|
|
|
|
ctime -begin ${0%.*}.ctm
|
2017-03-23 00:34:59 +00:00
|
|
|
clang -g -Wall -Wno-unused-variable -fsanitize=address -std=c99 $0 -o ${0%.*} hmml.a
|
2017-03-10 14:19:25 +00:00
|
|
|
ctime -end ${0%.*}.ctm
|
|
|
|
exit
|
|
|
|
#endif
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
typedef unsigned int bool;
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
#define TRUE 1
|
|
|
|
#define FALSE 0
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "hmmlib.h"
|
|
|
|
|
2017-03-10 14:19:25 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *Location;
|
|
|
|
char *Ptr;
|
|
|
|
int Size;
|
|
|
|
} buffer;
|
|
|
|
|
2017-03-19 01:23:44 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *Source;
|
|
|
|
char *RefTitle;
|
|
|
|
} ref_info;
|
|
|
|
|
2017-03-16 00:56:58 +00:00
|
|
|
void
|
|
|
|
ClaimBuffer(char *MemoryArena, int *ClaimedMemory, buffer *Buffer, int Size)
|
|
|
|
{
|
|
|
|
Buffer->Location = MemoryArena + *ClaimedMemory;
|
|
|
|
Buffer->Size = Size;
|
|
|
|
*ClaimedMemory += Buffer->Size;
|
|
|
|
Buffer->Ptr = Buffer->Location;
|
|
|
|
}
|
|
|
|
|
2017-03-19 01:23:44 +00:00
|
|
|
ref_info
|
|
|
|
ParseRef(HMML_Reference RefInput)
|
|
|
|
{
|
|
|
|
ref_info Info;
|
|
|
|
if(RefInput.author)
|
|
|
|
{
|
|
|
|
Info.Source = RefInput.author;
|
|
|
|
Info.RefTitle = RefInput.title;
|
|
|
|
return Info;
|
|
|
|
}
|
|
|
|
else if(RefInput.page)
|
|
|
|
{
|
|
|
|
Info.Source = RefInput.site;
|
|
|
|
Info.RefTitle = RefInput.page;
|
|
|
|
return Info;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Info.Source = "";
|
|
|
|
Info.RefTitle = RefInput.site;
|
|
|
|
return Info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-10 14:19:25 +00:00
|
|
|
int
|
|
|
|
TimecodeToSeconds(char *Timecode)
|
|
|
|
{
|
|
|
|
int HMS[3] = { 0, 0, 0 }; // 0 == Seconds; 1 == Minutes; 2 == Hours
|
|
|
|
int Colons = 0;
|
|
|
|
while(*Timecode)
|
|
|
|
{
|
|
|
|
if((*Timecode < '0' || *Timecode > '9') && *Timecode != ':') { return FALSE; }
|
|
|
|
|
|
|
|
if(*Timecode == ':')
|
|
|
|
{
|
|
|
|
++Colons;
|
|
|
|
if(Colons > 2) { return FALSE; }
|
|
|
|
for(int i = 0; i < Colons; ++i)
|
|
|
|
{
|
|
|
|
HMS[Colons - i] = HMS[Colons - (i + 1)];
|
|
|
|
}
|
|
|
|
HMS[0] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HMS[0] = HMS[0] * 10 + *Timecode - '0';
|
|
|
|
}
|
|
|
|
|
|
|
|
++Timecode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(HMS[0] > 59 || HMS[1] > 59 || Timecode[-1] == ':') { return FALSE; }
|
|
|
|
|
|
|
|
return HMS[2] * 60 * 60 + HMS[1] * 60 + HMS[0];
|
|
|
|
}
|
|
|
|
|
2017-03-16 00:56:58 +00:00
|
|
|
void
|
|
|
|
CopyBuffer(buffer *Src, buffer *Dest)
|
|
|
|
{
|
|
|
|
Src->Ptr = Src->Location;
|
|
|
|
while(*Src->Ptr)
|
|
|
|
{
|
|
|
|
*Dest->Ptr++ = *Src->Ptr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(buffer *Dest, char *Format, ...)
|
2017-03-16 00:56:58 +00:00
|
|
|
{
|
2017-03-23 00:34:59 +00:00
|
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
|
|
int Length = vsprintf(Dest->Ptr, Format, Args);
|
|
|
|
va_end(Args);
|
|
|
|
Dest->Ptr += Length;
|
2017-03-16 00:56:58 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 01:45:16 +00:00
|
|
|
int
|
|
|
|
StringsDiffer(char *A, char *B)
|
|
|
|
{
|
2017-03-21 02:38:18 +00:00
|
|
|
while(*A && *B && *A == *B)
|
2017-03-17 01:45:16 +00:00
|
|
|
{
|
|
|
|
++A, ++B;
|
|
|
|
}
|
|
|
|
return *A - *B;
|
|
|
|
}
|
|
|
|
|
2017-03-18 02:04:13 +00:00
|
|
|
int
|
|
|
|
CharToColour(char Char)
|
|
|
|
{
|
|
|
|
if(Char >= 'a' && Char <= 'z')
|
|
|
|
{
|
|
|
|
return (((float)Char - 'a') / ('z' - 'a') * 0xFFFFFF);
|
|
|
|
}
|
|
|
|
else if(Char >= 'A' && Char <= 'Z')
|
|
|
|
{
|
|
|
|
return (((float)Char - 'A') / ('Z' - 'A') * 0xFFFFFF);
|
|
|
|
}
|
|
|
|
else if(Char >= '0' && Char <= '9')
|
|
|
|
{
|
|
|
|
return (((float)Char - '0') / ('9' - '0') * 0xFFFFFF);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0x777777;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
StringToColourHash(char *String)
|
|
|
|
{
|
|
|
|
int Result = 0;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0; String[i]; ++i)
|
|
|
|
{
|
|
|
|
Result += CharToColour(String[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result / i;
|
|
|
|
}
|
|
|
|
|
2017-03-10 14:19:25 +00:00
|
|
|
int
|
|
|
|
main(int ArgC, char **Args)
|
|
|
|
{
|
|
|
|
if(ArgC < 2)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: %s filename(s)\n", Args[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE(matt): Init MemoryArena
|
|
|
|
char *MemoryArena;
|
2017-03-16 00:56:58 +00:00
|
|
|
int ArenaSize = 1024 * 1024;
|
2017-03-10 14:19:25 +00:00
|
|
|
if(!(MemoryArena = calloc(ArenaSize, 1)))
|
|
|
|
{
|
|
|
|
perror(Args[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
int ClaimedMemory = 0;
|
|
|
|
|
|
|
|
// NOTE(matt): Setup buffers and ptrs
|
2017-03-22 02:18:31 +00:00
|
|
|
#if 1
|
|
|
|
char *InPtr;
|
|
|
|
|
|
|
|
buffer Title;
|
|
|
|
buffer QuoteMenu;
|
|
|
|
buffer ReferenceMenu;
|
|
|
|
|
|
|
|
buffer Player;
|
|
|
|
buffer Annotation;
|
|
|
|
buffer AnnotationHeader;
|
|
|
|
buffer AnnotationClass;
|
|
|
|
buffer AnnotationData;
|
|
|
|
buffer Text;
|
|
|
|
buffer Category;
|
|
|
|
|
|
|
|
buffer Master;
|
|
|
|
|
|
|
|
for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex)
|
|
|
|
{
|
|
|
|
FILE *InFile;
|
|
|
|
if(!(InFile = fopen(Args[FileIndex], "r")))
|
|
|
|
{
|
|
|
|
perror(Args[0]);
|
|
|
|
free(MemoryArena);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
HMML_Output HMML = hmml_parse_file(InFile);
|
|
|
|
fclose(InFile);
|
|
|
|
|
|
|
|
if(HMML.well_formed)
|
|
|
|
{
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Title, 1024 * 16);
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Player, 1024 * 256);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Title,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <div class=\"title\">\n"
|
|
|
|
" <span class=\"episode_name\">%s</span>\n", HMML.metadata.title);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Player,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <div class=\"player_container\">\n"
|
|
|
|
" <div class=\"video_container\" data-videoId=\"%s\"></div>\n"
|
|
|
|
" <div class=\"markers_container\">\n", HMML.metadata.id);
|
|
|
|
|
|
|
|
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
|
|
|
|
{
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationHeader, 256);
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationClass, 128);
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, 1024 * 4);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&AnnotationHeader,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <div data-timestamp=\"%d\"",
|
|
|
|
TimecodeToSeconds(HMML.annotations[AnnotationIndex].time));
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&AnnotationClass,
|
2017-03-22 02:18:31 +00:00
|
|
|
" class=\"marker");
|
2017-03-23 00:34:59 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
if(HMML.annotations[AnnotationIndex].author)
|
|
|
|
{
|
|
|
|
CopyStringToBuffer(&AnnotationClass, " authored");
|
|
|
|
CopyStringToBuffer(&Text,
|
|
|
|
"<span class=\"author\" style=\"color: #%X;\">%s</span> ",
|
|
|
|
StringToColourHash(HMML.annotations[AnnotationIndex].author),
|
|
|
|
HMML.annotations[AnnotationIndex].author);
|
|
|
|
}
|
|
|
|
#endif
|
2017-03-22 02:18:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO(matt): Replace this CopyStringToBuffer() with real stuff!
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Text, HMML.annotations[AnnotationIndex].text);
|
2017-03-22 02:18:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&AnnotationClass, "\"");
|
2017-03-22 02:18:31 +00:00
|
|
|
CopyBuffer(&AnnotationClass, &AnnotationHeader);
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&AnnotationHeader, ">\n");
|
2017-03-22 02:18:31 +00:00
|
|
|
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Annotation, 1024 * 4);
|
|
|
|
|
|
|
|
CopyBuffer(&AnnotationHeader, &Annotation);
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Annotation,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>",
|
|
|
|
HMML.annotations[AnnotationIndex].time);
|
|
|
|
|
|
|
|
CopyBuffer(&Text, &Annotation);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Annotation, "</div>\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" <div class=\"progress faded\">\n"
|
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>",
|
|
|
|
HMML.annotations[AnnotationIndex].time);
|
|
|
|
|
|
|
|
CopyBuffer(&Text, &Annotation);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Annotation, "</div>\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" </div>\n"
|
|
|
|
" <div class=\"progress main\">\n"
|
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>",
|
|
|
|
HMML.annotations[AnnotationIndex].time);
|
|
|
|
|
|
|
|
CopyBuffer(&Text, &Annotation);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Annotation, "</div>\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" </div>\n"
|
2017-03-23 00:34:59 +00:00
|
|
|
" </div>\n");
|
2017-03-22 02:18:31 +00:00
|
|
|
|
|
|
|
CopyBuffer(&Annotation, &Player);
|
|
|
|
|
|
|
|
ClaimedMemory -= Text.Size;
|
|
|
|
ClaimedMemory -= AnnotationHeader.Size;
|
|
|
|
ClaimedMemory -= AnnotationClass.Size;
|
|
|
|
ClaimedMemory -= Annotation.Size;
|
|
|
|
}
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Title,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <span class=\"annotator_container\">Annotator: <span class=\"annotator\">%s</span></span>\n"
|
|
|
|
" </div>\n", HMML.metadata.annotator);
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Player,
|
2017-03-22 02:18:31 +00:00
|
|
|
" </div>\n"
|
|
|
|
" </div>\n");
|
|
|
|
|
|
|
|
//NOTE(matt): Collate the buffers!
|
|
|
|
|
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Master, 1024 * 512);
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Master,
|
2017-03-22 02:18:31 +00:00
|
|
|
"<!DOCTYPE html>\n"
|
|
|
|
" <head>\n"
|
|
|
|
" <meta charset=\"UTF-8\">\n"
|
|
|
|
"\n"
|
|
|
|
" <!-- Load the player -->\n"
|
|
|
|
" <script type=\"text/javascript\" src=\"player.js\"></script>\n"
|
|
|
|
" <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n"
|
|
|
|
" </head>\n"
|
|
|
|
" <body>\n");
|
|
|
|
|
|
|
|
//NOTE(matt): Here is where we do all our CopyBuffer() calls
|
|
|
|
CopyBuffer(&Title, &Master);
|
|
|
|
CopyBuffer(&Player, &Master);
|
|
|
|
//
|
|
|
|
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Master,
|
2017-03-22 02:18:31 +00:00
|
|
|
" <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"
|
|
|
|
"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 onRefChanged(ref) {\n"
|
|
|
|
" var sourceMenus = document.querySelectorAll(\".refs_container\");\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");
|
|
|
|
|
|
|
|
FILE *OutFile;
|
|
|
|
if(!(OutFile = fopen("out.html", "w")))
|
|
|
|
{
|
|
|
|
perror(Args[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile);
|
|
|
|
fclose(OutFile);
|
|
|
|
|
|
|
|
ClaimedMemory -= Title.Size;
|
|
|
|
ClaimedMemory -= Master.Size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message);
|
|
|
|
}
|
|
|
|
hmml_free(&HMML);
|
|
|
|
}
|
|
|
|
#else
|
2017-03-10 14:19:25 +00:00
|
|
|
char *InPtr;
|
|
|
|
buffer Working;
|
2017-03-15 02:10:11 +00:00
|
|
|
buffer Text;
|
2017-03-10 14:19:25 +00:00
|
|
|
buffer Out;
|
|
|
|
|
2017-03-16 00:56:58 +00:00
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Out, 1024 * 512);
|
|
|
|
|
2017-03-10 14:19:25 +00:00
|
|
|
for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex)
|
|
|
|
{
|
|
|
|
FILE *InFile;
|
|
|
|
if(!(InFile = fopen(Args[FileIndex], "r")))
|
|
|
|
{
|
|
|
|
perror(Args[0]);
|
|
|
|
free(MemoryArena);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
HMML_Output HMML = hmml_parse_file(InFile);
|
|
|
|
fclose(InFile);
|
|
|
|
|
|
|
|
if(HMML.well_formed)
|
|
|
|
{
|
2017-03-18 02:04:13 +00:00
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Working, 1024 * 4);
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
sprintf(Working.Location,
|
|
|
|
"<html>\n"
|
|
|
|
" <head>\n"
|
|
|
|
" <meta charset=\"UTF-8\">\n"
|
|
|
|
"\n"
|
|
|
|
" <!-- Load the player -->\n"
|
|
|
|
" <script type=\"text/javascript\" src=\"player.js\"></script>\n"
|
|
|
|
" <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n"
|
|
|
|
" </head>\n"
|
|
|
|
" <body>\n"
|
|
|
|
" <div class=\"title\">\n"
|
|
|
|
" <span class=\"episode_name\">%s</span>\n", HMML.metadata.title);
|
2017-03-16 00:56:58 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
int AnnotationIndex = 0;
|
2017-03-15 02:10:11 +00:00
|
|
|
int ReferenceIndex = 1;
|
2017-03-10 14:19:25 +00:00
|
|
|
while(AnnotationIndex < HMML.annotation_count)
|
|
|
|
{
|
|
|
|
if(HMML.annotations[AnnotationIndex].reference_count)
|
|
|
|
{
|
|
|
|
sprintf(Working.Location,
|
2017-03-17 01:45:16 +00:00
|
|
|
" <div class=\"refs_container\">\n"
|
|
|
|
" <span>References ▼</span>\n"
|
|
|
|
" <div class=\"mouse_catcher\"></div>\n"
|
|
|
|
" <div class=\"refs\">\n");
|
2017-03-16 00:56:58 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
while(AnnotationIndex < HMML.annotation_count)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < HMML.annotations[AnnotationIndex].reference_count; ++i)
|
|
|
|
{
|
2017-03-19 01:23:44 +00:00
|
|
|
HMML_Reference *CurrentRef = HMML.annotations[AnnotationIndex].references + i;
|
2017-03-15 02:10:11 +00:00
|
|
|
// NOTE(matt): Consider removing the ref_index class if it ain't needed
|
2017-03-10 14:19:25 +00:00
|
|
|
sprintf(Working.Location,
|
2017-03-17 01:45:16 +00:00
|
|
|
" <a data-id=\"%d\" href=\"%s\" target=\"_blank\" class=\"ref\">\n"
|
|
|
|
" <span class=\"ref_content\">\n"
|
|
|
|
" <div class=\"source\">%s</div>\n"
|
|
|
|
" <div class=\"ref_title\">%s</div>\n"
|
|
|
|
" </span>\n"
|
|
|
|
// TODO(matt): Fill the div class="ref_indices" with <= 3 span
|
2017-03-15 02:10:11 +00:00
|
|
|
// class="ref_index" and ensure to put these <=3 spans on the same line without
|
|
|
|
// a space between them
|
2017-03-17 01:45:16 +00:00
|
|
|
" <div class=\"ref_indices\">\n"
|
|
|
|
" <span data-timestamp=\"%d\" class=\"timecode\"><span class=\"ref_index\">[%d]</span><span class=\"time\">%s</span></span>\n"
|
|
|
|
" </div>\n"
|
|
|
|
" </a>\n",
|
2017-03-11 02:48:11 +00:00
|
|
|
AnnotationIndex,
|
2017-03-19 01:23:44 +00:00
|
|
|
CurrentRef->url,
|
|
|
|
ParseRef(*CurrentRef).Source,
|
|
|
|
ParseRef(*CurrentRef).RefTitle,
|
2017-03-15 02:10:11 +00:00
|
|
|
TimecodeToSeconds(HMML.annotations[AnnotationIndex].time),
|
|
|
|
ReferenceIndex,
|
|
|
|
HMML.annotations[AnnotationIndex].time);
|
2017-03-16 00:56:58 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-17 01:45:16 +00:00
|
|
|
|
2017-03-15 02:10:11 +00:00
|
|
|
++ReferenceIndex;
|
2017-03-10 14:19:25 +00:00
|
|
|
}
|
|
|
|
++AnnotationIndex;
|
|
|
|
}
|
2017-03-17 01:45:16 +00:00
|
|
|
sprintf(Working.Location,
|
|
|
|
" </div>\n"
|
|
|
|
" </div>\n");
|
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-10 14:19:25 +00:00
|
|
|
}
|
|
|
|
++AnnotationIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(Working.Location,
|
2017-03-17 01:45:16 +00:00
|
|
|
" <span class=\"annotator_container\">Annotator: <span class=\"annotator\">%s</span></span>\n"
|
2017-03-10 14:19:25 +00:00
|
|
|
" </div>\n"
|
2017-03-17 01:45:16 +00:00
|
|
|
" <div class=\"player_container\">\n"
|
|
|
|
" <div class=\"video_container\" data-videoId=\"%s\"></div>\n"
|
2017-03-11 02:48:11 +00:00
|
|
|
" <div class=\"markers_container\">\n", HMML.metadata.annotator, HMML.metadata.id);
|
2017-03-16 00:56:58 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-10 14:19:25 +00:00
|
|
|
|
2017-03-15 02:10:11 +00:00
|
|
|
int DataRef = 1;
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
|
|
|
|
{
|
2017-03-17 01:45:16 +00:00
|
|
|
sprintf(Working.Location,
|
|
|
|
" <div class=\"marker");
|
|
|
|
CopyBuffer(&Working, &Out);
|
|
|
|
|
2017-03-18 02:04:13 +00:00
|
|
|
ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, 1024);
|
|
|
|
int Inc = 0;
|
|
|
|
|
|
|
|
if(HMML.annotations[AnnotationIndex].author)
|
|
|
|
{
|
|
|
|
sprintf(Working.Location, " authored");
|
|
|
|
CopyBuffer(&Working, &Out);
|
|
|
|
|
|
|
|
Inc = sprintf(Text.Ptr, "<span class=\"author\" style=\"color: #%X;\">%s</span> ",
|
|
|
|
StringToColourHash(HMML.annotations[AnnotationIndex].author), HMML.annotations[AnnotationIndex].author);
|
|
|
|
Text.Ptr += Inc;
|
|
|
|
}
|
|
|
|
|
2017-03-17 01:45:16 +00:00
|
|
|
if(HMML.annotations[AnnotationIndex].marker_count)
|
|
|
|
{
|
|
|
|
for(int MarkerIndex = 0; MarkerIndex < HMML.annotations[AnnotationIndex].marker_count; ++MarkerIndex)
|
|
|
|
{
|
2017-03-19 01:23:44 +00:00
|
|
|
if(!StringsDiffer("blackboard", HMML.annotations[AnnotationIndex].markers[MarkerIndex].marker) &&
|
2017-03-17 01:45:16 +00:00
|
|
|
HMML.annotations[AnnotationIndex].markers[MarkerIndex].type == HMML_CATEGORY)
|
|
|
|
{
|
|
|
|
sprintf(Working.Location, " blackboard");
|
|
|
|
CopyBuffer(&Working, &Out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(Working.Location, "\" data-timestamp=\"%d\"", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time));
|
|
|
|
CopyBuffer(&Working, &Out);
|
|
|
|
|
2017-03-15 02:10:11 +00:00
|
|
|
InPtr = HMML.annotations[AnnotationIndex].text;
|
|
|
|
|
2017-03-11 02:48:11 +00:00
|
|
|
if(HMML.annotations[AnnotationIndex].reference_count) // || HMML.annotations[AnnotationIndex].is_quote)
|
2017-03-10 14:19:25 +00:00
|
|
|
{
|
2017-03-17 01:45:16 +00:00
|
|
|
sprintf(Working.Location, " data-ref=\"%d\"", AnnotationIndex);
|
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-15 02:10:11 +00:00
|
|
|
|
|
|
|
for(int RefLocalIndex = 0; RefLocalIndex < HMML.annotations[AnnotationIndex].reference_count; ++RefLocalIndex)
|
|
|
|
{
|
|
|
|
while(InPtr - HMML.annotations[AnnotationIndex].text < HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset)
|
|
|
|
{
|
|
|
|
*Text.Ptr++ = *InPtr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2)
|
|
|
|
{
|
|
|
|
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].page)
|
|
|
|
{
|
|
|
|
Inc = sprintf(Text.Ptr, "%s",
|
|
|
|
HMML.annotations[AnnotationIndex].references[RefLocalIndex].page);
|
|
|
|
}
|
|
|
|
else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].site)
|
|
|
|
{
|
|
|
|
Inc = sprintf(Text.Ptr, "%s",
|
|
|
|
HMML.annotations[AnnotationIndex].references[RefLocalIndex].site);
|
|
|
|
}
|
|
|
|
else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].title)
|
|
|
|
{
|
|
|
|
Inc = sprintf(Text.Ptr, "%s",
|
|
|
|
HMML.annotations[AnnotationIndex].references[RefLocalIndex].title);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: Potentially incomplete reference at %s:%d\n",
|
|
|
|
Args[0],
|
|
|
|
Args[FileIndex],
|
|
|
|
HMML.annotations[AnnotationIndex].line);
|
|
|
|
}
|
|
|
|
Text.Ptr += Inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2 && Text.Ptr[-1] == ' ')
|
|
|
|
{
|
|
|
|
--Text.Ptr;
|
|
|
|
}
|
|
|
|
Inc = sprintf(Text.Ptr, "<sup>%d</sup>", DataRef);
|
|
|
|
Text.Ptr += Inc;
|
|
|
|
++DataRef;
|
|
|
|
}
|
2017-03-10 14:19:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 01:45:16 +00:00
|
|
|
*Out.Ptr++ = '>';
|
|
|
|
*Out.Ptr++ = '\n';
|
2017-03-23 00:34:59 +00:00
|
|
|
CopyStringToBuffer(&Text, InPtr);
|
2017-03-15 02:10:11 +00:00
|
|
|
*Text.Ptr = '\0';
|
2017-03-16 00:56:58 +00:00
|
|
|
|
2017-03-11 02:48:11 +00:00
|
|
|
sprintf(Working.Location,
|
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
|
|
|
|
" <div class=\"progress faded\">\n"
|
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
|
|
|
|
" </div>\n"
|
|
|
|
" <div class=\"progress main\">\n"
|
|
|
|
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
|
|
|
|
" </div>\n"
|
|
|
|
" </div>\n",
|
2017-03-10 14:19:25 +00:00
|
|
|
HMML.annotations[AnnotationIndex].time,
|
2017-03-15 02:10:11 +00:00
|
|
|
Text.Location,
|
2017-03-10 14:19:25 +00:00
|
|
|
HMML.annotations[AnnotationIndex].time,
|
2017-03-15 02:10:11 +00:00
|
|
|
Text.Location,
|
2017-03-10 14:19:25 +00:00
|
|
|
HMML.annotations[AnnotationIndex].time,
|
2017-03-15 02:10:11 +00:00
|
|
|
Text.Location);
|
2017-03-17 01:45:16 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-15 02:10:11 +00:00
|
|
|
|
|
|
|
ClaimedMemory -= Text.Size;
|
2017-03-10 14:19:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-11 02:48:11 +00:00
|
|
|
sprintf(Working.Location,
|
|
|
|
" </div>\n"
|
|
|
|
" </div>\n"
|
|
|
|
" <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"
|
2017-03-10 14:19:25 +00:00
|
|
|
"\n"
|
2017-03-11 02:48:11 +00:00
|
|
|
" case 'p':\n"
|
|
|
|
" case 'a':\n"
|
|
|
|
" case 'w': {\n"
|
|
|
|
" player.jumpToPrevMarker();\n"
|
|
|
|
" } break;\n"
|
2017-03-10 14:19:25 +00:00
|
|
|
" }\n"
|
|
|
|
"});\n"
|
2017-03-11 02:48:11 +00:00
|
|
|
"\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"
|
2017-03-15 02:10:11 +00:00
|
|
|
" ev.stopPropagation();\n"
|
2017-03-11 02:48:11 +00:00
|
|
|
" return false;\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 onRefChanged(ref) {\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" var sourceMenus = document.querySelectorAll(\".refs_container\");\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"
|
2017-03-15 02:10:11 +00:00
|
|
|
"\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" 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"
|
2017-03-11 02:48:11 +00:00
|
|
|
" } else {\n"
|
2017-03-22 02:18:31 +00:00
|
|
|
" 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"
|
2017-03-11 02:48:11 +00:00
|
|
|
" }\n"
|
|
|
|
" }\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
" </script>\n"
|
|
|
|
" </body>\n"
|
|
|
|
"</html>\n");
|
2017-03-16 00:56:58 +00:00
|
|
|
CopyBuffer(&Working, &Out);
|
2017-03-10 14:19:25 +00:00
|
|
|
|
|
|
|
FILE *OutFile;
|
|
|
|
//char *OutFilename;
|
|
|
|
//sprintf(OutFilename, "%s.html", Args[FileIndex]);
|
|
|
|
if(!(OutFile = fopen("out.html", "w")))
|
|
|
|
{
|
|
|
|
perror(Args[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fwrite(Out.Location, Out.Ptr - Out.Location, 1, OutFile);
|
|
|
|
fclose(OutFile);
|
|
|
|
}
|
2017-03-17 01:45:16 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message);
|
|
|
|
}
|
|
|
|
hmml_free(&HMML);
|
2017-03-10 14:19:25 +00:00
|
|
|
}
|
2017-03-22 02:18:31 +00:00
|
|
|
#endif
|
2017-03-10 14:19:25 +00:00
|
|
|
free(MemoryArena);
|
|
|
|
}
|