hmml_to_html.c: Partially implement refs [#15]

It is missing the onRefChanged() function

PPiC 051
This commit is contained in:
Matt Mascarenhas 2017-03-10 14:19:25 +00:00
parent ce2feef7f6
commit 3394daf8b0
6 changed files with 1297 additions and 0 deletions

BIN
hmml_to_html/hmml.a Normal file

Binary file not shown.

331
hmml_to_html/hmml_to_html.c Normal file
View File

@ -0,0 +1,331 @@
#if 0
ctime -begin ${0%.*}.ctm
gcc -g -fsanitize=address $0 -o ${0%.*} hmml.a
ctime -end ${0%.*}.ctm
exit
#endif
#include <stdio.h>
#include <stdlib.h>
#include "hmmlib.h"
#ifndef bool
typedef bool unsigned int;
#endif
#define TRUE 1
#define FALSE 0
typedef struct
{
char *Location;
char *Ptr;
int Size;
} buffer;
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];
}
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;
int ArenaSize = 1024 * 64;
if(!(MemoryArena = calloc(ArenaSize, 1)))
{
perror(Args[0]);
return 1;
}
int ClaimedMemory = 0;
// NOTE(matt): Setup buffers and ptrs
char *InPtr;
buffer Template;
buffer Working;
buffer Out;
FILE *TemplateFile;
if(!(TemplateFile = fopen("style.css", "r")))
{
perror(Args[0]);
return 1;
}
fseek(TemplateFile, 0, SEEK_END);
Template.Size = ftell(TemplateFile);
fseek(TemplateFile, 0, SEEK_SET);
Template.Location = MemoryArena + ClaimedMemory;
ClaimedMemory += Template.Size;
fread(Template.Location, Template.Size, 1, TemplateFile);
fclose(TemplateFile);
Out.Location = MemoryArena + ClaimedMemory;
Out.Size = 1024 * 32;
ClaimedMemory += Out.Size;
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)
{
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 351;
ClaimedMemory += Working.Size;
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);
Working.Ptr = Working.Location;
Out.Ptr = Out.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
Working.Location = '\0';
ClaimedMemory -= Working.Size;
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 1024;
ClaimedMemory += Working.Size;
int AnnotationIndex = 0;
while(AnnotationIndex < HMML.annotation_count)
{
if(HMML.annotations[AnnotationIndex].reference_count)
{
sprintf(Working.Location,
" <div class=\"refs_container\">\n"
" <span>References &#9660;</span>\n"
" <div class=\"mouse_catcher\"></div>\n"
" <div class=\"refs\">\n");
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
while(AnnotationIndex < HMML.annotation_count)
{
for(int i = 0; i < HMML.annotations[AnnotationIndex].reference_count; ++i)
{
sprintf(Working.Location,
" <a href=\"%s\" target=\"_blank\" class=\"ref\">\n"
" <span data-timestamp=\"%d\" class=\"timecode\">(<span class=\"time\">%s</span>)</span>\n"
" <span class=\"ref_content\">\n"
" <div class=\"source\">%s</div>\n"
" <div class=\"ref_title\">%s</div>\n"
" </span>\n"
" </a>\n",
HMML.annotations[AnnotationIndex].references[i].url,
TimecodeToSeconds(HMML.annotations[AnnotationIndex].time),
HMML.annotations[AnnotationIndex].time,
HMML.annotations[AnnotationIndex].references[i].site ? HMML.annotations[AnnotationIndex].references[i].site : HMML.annotations[AnnotationIndex].references[i].author,
HMML.annotations[AnnotationIndex].references[i].page ? HMML.annotations[AnnotationIndex].references[i].page : HMML.annotations[AnnotationIndex].references[i].title);
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
}
++AnnotationIndex;
}
}
++AnnotationIndex;
}
Working.Location = '\0';
ClaimedMemory -= Working.Size;
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 256;
ClaimedMemory += Working.Size;
Working.Location = '\0';
ClaimedMemory -= Working.Size;
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 1024;
ClaimedMemory += Working.Size;
sprintf(Working.Location,
" </div>\n"
" </div>\n"
" <span class=\"annotator_container\">Annotator: <span class=\"annotator\">%s</span></span>\n"
"</div>\n"
"<div class=\"player_container\">\n"
" <div class=\"video_container\" data-videoId=\"%s\"></div>\n"
" <div class=\"markers_container\">\n", HMML.metadata.annotator, HMML.metadata.id);
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
Working.Location = '\0';
ClaimedMemory -= Working.Size;
int DataRef = 0;
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
{
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 1024;
ClaimedMemory += Working.Size;
if(HMML.annotations[AnnotationIndex].reference_count || HMML.annotations[AnnotationIndex].is_quote)
{
sprintf(Working.Location, "<div class=\"marker\" data-timestamp=\"%d\" data-ref=\"%d\">\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time), DataRef);
++DataRef;
}
else
{
sprintf(Working.Location, "<div class=\"marker\" data-timestamp=\"%d\">\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time));
}
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
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",
HMML.annotations[AnnotationIndex].time,
HMML.annotations[AnnotationIndex].text,
HMML.annotations[AnnotationIndex].time,
HMML.annotations[AnnotationIndex].text,
HMML.annotations[AnnotationIndex].time,
HMML.annotations[AnnotationIndex].text);
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
Working.Location = '\0';
ClaimedMemory -= Working.Size;
*Out.Ptr++ = '\n';
}
Working.Location = MemoryArena + ClaimedMemory;
Working.Size = 256;
ClaimedMemory += Working.Size;
sprintf(Working.Location, "</div>\n"
"</div>\n"
"<script>\n"
"var player = new Player(document.querySelector(\".player_container\"));\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"
"</script>\n"
"</body>\n"
"</html>\n");
Working.Ptr = Working.Location;
while(*Working.Ptr)
{
*Out.Ptr++ = *Working.Ptr++;
}
hmml_free(&HMML);
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);
}
}
free(MemoryArena);
}

87
hmml_to_html/hmmlib.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef HMML_H_
#define HMML_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
// Data structures
typedef struct {
char* member;
char* twitch;
char* project;
char* title;
char* platform;
char* id;
char* annotator;
} HMML_VideoMetaData;
typedef struct {
char* site;
char* page;
char* url;
char* title;
char* article;
char* author;
char* editor;
char* publisher;
char* isbn;
int offset;
} HMML_Reference;
typedef enum {
HMML_CATEGORY,
HMML_MEMBER,
HMML_PROJECT,
HMML_MARKER_COUNT,
} HMML_MarkerType;
typedef struct {
HMML_MarkerType type;
char* text;
int offset;
} HMML_Marker;
typedef struct {
int id;
char* author;
} HMML_Quote;
typedef struct {
int line;
char* time;
char* text;
char* author;
HMML_Reference* references;
size_t reference_count;
HMML_Marker* markers;
size_t marker_count;
HMML_Quote quote;
bool is_quote;
} HMML_Annotation;
typedef struct {
int line;
char* message;
} HMML_Error;
typedef struct {
bool well_formed;
HMML_VideoMetaData metadata;
HMML_Annotation* annotations;
size_t annotation_count;
HMML_Error error;
} HMML_Output;
// Functions
HMML_Output hmml_parse_file (FILE* file);
void hmml_dump (HMML_Output* output);
void hmml_free (HMML_Output* output);
#endif

287
hmml_to_html/out.html Normal file
View File

@ -0,0 +1,287 @@
<html>
<head>
<meta charset="UTF-8">
<!-- Load the player -->
<script type="text/javascript" src="player.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="title">
<span class="episode_name">Ripple Carry Adders & Two's Complement</span>
<div class="refs_container">
<span>References &#9660;</span>
<div class="mouse_catcher"></div>
<div class="refs">
<a href="http://www.charlespetzold.com/code/" target="_blank" class="ref">
<span data-timestamp="61" class="timecode">(<span class="time">1:01</span>)</span>
<span class="ref_content">
<div class="source">Charles Petzold</div>
<div class="ref_title">Code: The Hidden Language of Computer Hardware and Software</div>
</span>
</a>
<a href="https://en.wikipedia.org/wiki/Logic_gate" target="_blank" class="ref">
<span data-timestamp="1082" class="timecode">(<span class="time">18:02</span>)</span>
<span class="ref_content">
<div class="source">Wikipedia</div>
<div class="ref_title">Logic gate</div>
</span>
</a>
<a href="https://en.wikipedia.org/wiki/Adder_(electronics)" target="_blank" class="ref">
<span data-timestamp="2218" class="timecode">(<span class="time">36:58</span>)</span>
<span class="ref_content">
<div class="source">Wikipedia</div>
<div class="ref_title">Adder (electronics)</div>
</span>
</a>
<a href="https://en.wikipedia.org/wiki/Ones'_complement" target="_blank" class="ref">
<span data-timestamp="2886" class="timecode">(<span class="time">48:06</span>)</span>
<span class="ref_content">
<div class="source">Wikipedia</div>
<div class="ref_title">Ones' complement</div>
</span>
</a>
<a href="https://en.wikipedia.org/wiki/Two's_complement" target="_blank" class="ref">
<span data-timestamp="3268" class="timecode">(<span class="time">54:28</span>)</span>
<span class="ref_content">
<div class="source">Wikipedia</div>
<div class="ref_title">Two's complement</div>
</span>
</a>
<a href="https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html" target="_blank" class="ref">
<span data-timestamp="3883" class="timecode">(<span class="time">1:04:43</span>)</span>
<span class="ref_content">
<div class="source">GCC, the GNU Compiler Collection</div>
<div class="ref_title">Statements and Declarations in Expressions</div>
</span>
</a>
</div>
</div>
<span class="annotator_container">Annotator: <span class="annotator">Miblo</span></span>
</div>
<div class="player_container">
<div class="video_container" data-videoId="BObABjzvVPw"></div>
<div class="markers_container">
<div class="marker" data-timestamp="7">
<div class="content"><span class="timecode">0:07</span>Set the stage for the day, looking at two's complement and sign extension</div>
<div class="progress faded">
<div class="content"><span class="timecode">0:07</span>Set the stage for the day, looking at two's complement and sign extension</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">0:07</span>Set the stage for the day, looking at two's complement and sign extension</div>
</div>
</div>
<div class="marker" data-timestamp="41" data-ref="0">
<div class="content"><span class="timecode">0:41</span>"It seems like, as you get older, the years start to go by like weeks"</div>
<div class="progress faded">
<div class="content"><span class="timecode">0:41</span>"It seems like, as you get older, the years start to go by like weeks"</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">0:41</span>"It seems like, as you get older, the years start to go by like weeks"</div>
</div>
</div>
<div class="marker" data-timestamp="61" data-ref="1">
<div class="content"><span class="timecode">1:01</span>Recommend Code by Charles Petzold</div>
<div class="progress faded">
<div class="content"><span class="timecode">1:01</span>Recommend Code by Charles Petzold</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">1:01</span>Recommend Code by Charles Petzold</div>
</div>
</div>
<div class="marker" data-timestamp="303">
<div class="content"><span class="timecode">5:03</span>Using an electromagnet with a telegraph to communicate in Morse code</div>
<div class="progress faded">
<div class="content"><span class="timecode">5:03</span>Using an electromagnet with a telegraph to communicate in Morse code</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">5:03</span>Using an electromagnet with a telegraph to communicate in Morse code</div>
</div>
</div>
<div class="marker" data-timestamp="596">
<div class="content"><span class="timecode">9:56</span>Adder circuit</div>
<div class="progress faded">
<div class="content"><span class="timecode">9:56</span>Adder circuit</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">9:56</span>Adder circuit</div>
</div>
</div>
<div class="marker" data-timestamp="1082" data-ref="2">
<div class="content"><span class="timecode">18:02</span>Research logic gates</div>
<div class="progress faded">
<div class="content"><span class="timecode">18:02</span>Research logic gates</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">18:02</span>Research logic gates</div>
</div>
</div>
<div class="marker" data-timestamp="1167">
<div class="content"><span class="timecode">19:27</span>XOR</div>
<div class="progress faded">
<div class="content"><span class="timecode">19:27</span>XOR</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">19:27</span>XOR</div>
</div>
</div>
<div class="marker" data-timestamp="1538">
<div class="content"><span class="timecode">25:38</span>Redraw the tables</div>
<div class="progress faded">
<div class="content"><span class="timecode">25:38</span>Redraw the tables</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">25:38</span>Redraw the tables</div>
</div>
</div>
<div class="marker" data-timestamp="1681">
<div class="content"><span class="timecode">28:01</span>What an XOR gate will do when the carry is off</div>
<div class="progress faded">
<div class="content"><span class="timecode">28:01</span>What an XOR gate will do when the carry is off</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">28:01</span>What an XOR gate will do when the carry is off</div>
</div>
</div>
<div class="marker" data-timestamp="2007">
<div class="content"><span class="timecode">33:27</span>Propagating the carry through our circuit</div>
<div class="progress faded">
<div class="content"><span class="timecode">33:27</span>Propagating the carry through our circuit</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">33:27</span>Propagating the carry through our circuit</div>
</div>
</div>
<div class="marker" data-timestamp="2218" data-ref="3">
<div class="content"><span class="timecode">36:58</span>Consult Wikipedia for a ripple carry adder</div>
<div class="progress faded">
<div class="content"><span class="timecode">36:58</span>Consult Wikipedia for a ripple carry adder</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">36:58</span>Consult Wikipedia for a ripple carry adder</div>
</div>
</div>
<div class="marker" data-timestamp="2338">
<div class="content"><span class="timecode">38:58</span>This circuit does 1 bit of the adder computation</div>
<div class="progress faded">
<div class="content"><span class="timecode">38:58</span>This circuit does 1 bit of the adder computation</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">38:58</span>This circuit does 1 bit of the adder computation</div>
</div>
</div>
<div class="marker" data-timestamp="2485">
<div class="content"><span class="timecode">41:25</span>Subtraction circuit</div>
<div class="progress faded">
<div class="content"><span class="timecode">41:25</span>Subtraction circuit</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">41:25</span>Subtraction circuit</div>
</div>
</div>
<div class="marker" data-timestamp="2686">
<div class="content"><span class="timecode">44:46</span>Agreeing on an encoding in order to communicate useful information</div>
<div class="progress faded">
<div class="content"><span class="timecode">44:46</span>Agreeing on an encoding in order to communicate useful information</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">44:46</span>Agreeing on an encoding in order to communicate useful information</div>
</div>
</div>
<div class="marker" data-timestamp="2793" data-ref="4">
<div class="content"><span class="timecode">46:33</span>"You can quote whatever you like, Miblo"</div>
<div class="progress faded">
<div class="content"><span class="timecode">46:33</span>"You can quote whatever you like, Miblo"</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">46:33</span>"You can quote whatever you like, Miblo"</div>
</div>
</div>
<div class="marker" data-timestamp="2886" data-ref="5">
<div class="content"><span class="timecode">48:06</span>Start with looking at ones' complement</div>
<div class="progress faded">
<div class="content"><span class="timecode">48:06</span>Start with looking at ones' complement</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">48:06</span>Start with looking at ones' complement</div>
</div>
</div>
<div class="marker" data-timestamp="3268" data-ref="6">
<div class="content"><span class="timecode">54:28</span>Go to two's complement</div>
<div class="progress faded">
<div class="content"><span class="timecode">54:28</span>Go to two's complement</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">54:28</span>Go to two's complement</div>
</div>
</div>
<div class="marker" data-timestamp="3552">
<div class="content"><span class="timecode">59:12</span>Using both ones' complement and two's complement to enable our circuit to perform subtraction</div>
<div class="progress faded">
<div class="content"><span class="timecode">59:12</span>Using both ones' complement and two's complement to enable our circuit to perform subtraction</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">59:12</span>Using both ones' complement and two's complement to enable our circuit to perform subtraction</div>
</div>
</div>
<div class="marker" data-timestamp="3883" data-ref="7">
<div class="content"><span class="timecode">1:04:43</span>GCC's "Statements and Declarations in Expressions" Extension</div>
<div class="progress faded">
<div class="content"><span class="timecode">1:04:43</span>GCC's "Statements and Declarations in Expressions" Extension</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">1:04:43</span>GCC's "Statements and Declarations in Expressions" Extension</div>
</div>
</div>
<div class="marker" data-timestamp="3972">
<div class="content"><span class="timecode">1:06:12</span>That's all for now</div>
<div class="progress faded">
<div class="content"><span class="timecode">1:06:12</span>That's all for now</div>
</div>
<div class="progress main">
<div class="content"><span class="timecode">1:06:12</span>That's all for now</div>
</div>
</div>
</div>
</div>
<script>
var player = new Player(document.querySelector(".player_container"));
window.addEventListener("resize", function() { player.updateSize(); });
document.addEventListener("keypress", function(ev) {
switch (ev.key) {
case 'n':
case 'd':
case 's': {
player.jumpToNextMarker();
} break;
case 'p':
case 'a':
case 'w': {
player.jumpToPrevMarker();
} break;
}
});
</script>
</body>
</html>

390
hmml_to_html/player.js Normal file
View File

@ -0,0 +1,390 @@
// refsCallback: (optional)
// Will be called when the player enters a marker that has a `data-ref` attribute. The value of `data-ref` will be passed to the function.
// When leaving a marker that a `data-ref` attribute, and entering a marker without one (or not entering a new marker at all), the function will be called with `null`.
function Player(htmlContainer, refsCallback) {
this.container = htmlContainer;
this.markersContainer = this.container.querySelector(".markers_container");
this.videoContainer = this.container.querySelector(".video_container");
this.refsCallback = refsCallback || function() {};
if (!this.videoContainer.getAttribute("data-videoId")) {
console.error("Expected to find data-videoId attribute on", this.videoContainer, "for player initialized on", this.container);
throw new Error("Missing data-videoId attribute.");
}
this.markers = [];
var markerEls = this.markersContainer.querySelectorAll(".marker");
if (markerEls.length == 0) {
console.error("No markers found in", this.markersContainer, "for player initialized on", this.container);
throw new Error("Missing markers.");
}
for (var i = 0; i < markerEls.length; ++i) {
var marker = {
timestamp: parseInt(markerEls[i].getAttribute("data-timestamp"), 10),
ref: markerEls[i].getAttribute("data-ref"),
endTime: (i < markerEls.length - 1 ? parseInt(markerEls[i+1].getAttribute("data-timestamp"), 10) : null),
el: markerEls[i],
fadedProgress: markerEls[i].querySelector(".progress.faded"),
progress: markerEls[i].querySelector(".progress.main"),
hoverx: null
};
marker.el.addEventListener("click", this.onMarkerClick.bind(this, marker));
marker.el.addEventListener("mousemove", this.onMarkerMouseMove.bind(this, marker));
marker.el.addEventListener("mouseleave", this.onMarkerMouseLeave.bind(this, marker));
this.markers.push(marker);
}
this.currentMarker = null;
this.currentMarkerIdx = null;
this.youtubePlayer = null;
this.youtubePlayerReady = false;
this.playing = false;
this.shouldPlay = false;
this.speed = 1;
this.currentTime = 0;
this.lastFrameTime = 0;
this.scrollTo = -1;
this.scrollPosition = 0;
this.nextFrame = null;
this.looping = false;
this.markersContainer.addEventListener("wheel", function(ev) {
this.scrollTo = -1;
}.bind(this));
Player.initializeYoutube(this.onYoutubeReady.bind(this));
this.updateSize();
this.resume();
}
// Start playing the video from the current position.
// If the player hasn't loaded yet, it will autoplay when ready.
Player.prototype.play = function() {
if (this.youtubePlayerReady) {
if (!this.playing) {
this.youtubePlayer.playVideo();
}
} else {
this.shouldPlay = true;
}
};
// Pause the video at the current position.
// If the player hasn't loaded yet, it will not autoplay when ready. (This is the default)
Player.prototype.pause = function() {
if (this.youtubePlayerReady) {
if (this.playing) {
this.youtubePlayer.pauseVideo();
}
} else {
this.shouldPlay = false;
}
};
// Sets the current time. Does not affect play status.
// If the player hasn't loaded yet, it will seek to this time when ready.
Player.prototype.setTime = function(time) {
this.currentTime = time;
if (this.youtubePlayerReady) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.youtubePlayer.getDuration()));
this.youtubePlayer.seekTo(this.currentTime);
}
this.updateProgress();
};
Player.prototype.jumpToNextMarker = function() {
var targetMarkerIdx = Math.min((this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx + 1), this.markers.length-1);
var targetTime = this.markers[targetMarkerIdx].timestamp;
this.setTime(targetTime);
this.play();
};
Player.prototype.jumpToPrevMarker = function() {
var targetMarkerIdx = Math.max(0, (this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx - 1));
var targetTime = this.markers[targetMarkerIdx].timestamp;
this.setTime(targetTime);
this.play();
};
// Call this after changing the size of the video container in order to update the youtube player.
Player.prototype.updateSize = function() {
var width = this.videoContainer.offsetWidth;
var height = width / 16 * 9;
this.markersContainer.style.height = height;
if (this.youtubePlayerReady) {
this.youtubePlayer.setSize(Math.floor(width), Math.floor(height));
}
}
// Stops the per-frame work that the player does. Call when you want to hide or get rid of the player.
Player.prototype.halt = function() {
this.pause();
this.looping = false;
if (this.nextFrame) {
cancelAnimationFrame(this.nextFrame);
this.nextFrame = null;
}
}
// Resumes the per-frame work that the player does. Call when you want to show the player again after hiding.
Player.prototype.resume = function() {
this.looping = true;
if (!this.nextFrame) {
this.doFrame();
}
}
Player.createHTMLSkeleton = function(videoId, markers) {
};
// timestamp can be either an int (number of seconds from beginning of the video) or a string ([hh:][mm:]ss)
Player.createHTMLMarker = function(timestamp, text, ref) {
if (typeof(timestamp) == "string") {
var timeParts = timestamp.split(":");
var hours = (timeParts.length == 3 ? parseInt(timeParts[0], 10) : 0);
var minutes = (timeParts.length > 1 ? parseInt(timeParts[timeParts.length-2], 10) : 0);
var seconds = parseInt(timeParts[timeParts.length-1], 10);
timestamp = hours * 60 * 60 + minutes * 60 + seconds;
}
var marker = document.createElement("DIV");
marker.classList.add("marker");
marker.setAttribute("data-timestamp", timestamp);
if (ref !== undefined && ref !== null) {
marker.setAttribute("data-ref", ref);
}
var content = document.createElement("DIV");
content.classList.add("content");
var markerTime = "(";
var hours = Math.floor(timestamp / 60 / 60);
var minutes = Math.floor(timestamp / 60) % 60;
var seconds = timestamp % 60;
if (hours > 0) {
markerTime += (hours < 10 ? "0" + hours : hours) + ":";
}
markerTime += (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds) + ")";
content.textContent = markerTime + " " + text;
marker.appendChild(content);
var fadedProgress = document.createElement("DIV");
fadedProgress.classList.add("progress");
fadedProgress.classList.add("faded");
fadedProgress.appendChild(content.cloneNode(true));
marker.appendChild(fadedProgress);
var progress = document.createElement("DIV");
progress.classList.add("progress");
progress.classList.add("main");
progress.appendChild(content.cloneNode(true));
marker.appendChild(progress);
return marker;
};
Player.parseHMHAnnotation = function(text) {
var annotation = {
title: null,
videoId: null,
author: null,
markers: []
};
var lines = text.split("\n");
var mode = "none";
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
if (line == "---") {
mode = "none";
} else if (line.startsWith("title:")) {
annotation.title = line.slice(7).replace(/"/g, "");
} else if (line.startsWith("videoId:")) {
annotation.videoId = line.slice(9).replace(/"/g, "");
} else if (line.startsWith("author:")) {
annotation.author = line.slice(8).replace(/"/g, ""); // Miblo, where's the author field?
} else if (line.startsWith("markers")) {
mode = "markers";
} else if (mode == "markers") {
var match = line.match(/"((\d+):)?(\d+):(\d+)": "(.+)"/);
var marker = {
timestamp: (match[2] ? parseInt(match[2], 10) : 0) * 60 * 60 + parseInt(match[3], 10) * 60 + parseInt(match[4], 10),
text: match[5].replace(/\\"/g, "\"")
}
annotation.markers.push(marker);
}
}
return annotation;
};
Player.parseAnnotation = function(text) {
};
Player.initializeYoutube = function(callback) {
if (window.APYoutubeAPIReady === undefined) {
window.APYoutubeAPIReady = false;
window.APCallbacks = (callback ? [callback] : []);
window.onYouTubeIframeAPIReady = function() {
window.APYoutubeAPIReady = true;
for (var i = 0; i < APCallbacks.length; ++i) {
APCallbacks[i]();
}
};
var scriptTag = document.createElement("SCRIPT");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", "https://www.youtube.com/iframe_api");
document.body.appendChild(scriptTag);
} else if (window.APYoutubeAPIReady === false) {
window.APCallbacks.push(callback);
} else if (window.APYoutubeAPIReady === true) {
callback();
}
}
// END PUBLIC INTERFACE
Player.prototype.onMarkerClick = function(marker, ev) {
var time = marker.timestamp;
if (this.currentMarker == marker && marker.hoverx !== null) {
time += (marker.endTime - marker.timestamp) * marker.hoverx;
}
this.setTime(time);
this.play();
};
Player.prototype.onMarkerMouseMove = function(marker, ev) {
if (this.currentMarker == marker) {
marker.hoverx = (ev.offsetX - marker.el.offsetLeft) / marker.el.offsetWidth;
}
};
Player.prototype.onMarkerMouseLeave = function(marker, ev) {
marker.hoverx = null;
};
Player.prototype.updateProgress = function() {
var prevMarker = this.currentMarker;
this.currentMarker = null;
this.currentMarkerIdx = null;
for (var i = 0; i < this.markers.length; ++i) {
var marker = this.markers[i];
if (marker.timestamp <= this.currentTime && this.currentTime < marker.endTime) {
this.currentMarker = marker;
this.currentMarkerIdx = i;
break;
}
}
if (this.currentMarker) {
var totalWidth = this.currentMarker.el.offsetWidth;
var progress = (this.currentTime - this.currentMarker.timestamp) / (this.currentMarker.endTime - this.currentMarker.timestamp);
if (this.currentMarker.hoverx === null) {
var pixelWidth = progress * totalWidth;
this.currentMarker.fadedProgress.style.width = Math.ceil(pixelWidth) + "px";
this.currentMarker.fadedProgress.style.opacity = pixelWidth - Math.floor(pixelWidth);
this.currentMarker.progress.style.width = Math.floor(pixelWidth) + "px";
} else {
this.currentMarker.fadedProgress.style.opacity = 1;
this.currentMarker.progress.style.width = Math.floor(Math.min(this.currentMarker.hoverx, progress) * totalWidth) + "px";
this.currentMarker.fadedProgress.style.width = Math.floor(Math.max(this.currentMarker.hoverx, progress) * totalWidth) + "px";
}
}
if (this.currentMarker != prevMarker) {
if (prevMarker) {
prevMarker.el.classList.remove("current");
prevMarker.fadedProgress.style.width = "0px";
prevMarker.progress.style.width = "0px";
prevMarker.hoverx = null;
}
if (this.currentMarker) {
this.currentMarker.el.classList.add("current");
this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0;
this.scrollPosition = this.markersContainer.scrollTop;
}
if (this.currentMarker && this.currentMarker.ref) {
this.refsCallback(this.currentMarker.ref);
} else if (prevMarker && prevMarker.ref) {
this.refsCallback(null);
}
}
};
Player.prototype.doFrame = function() {
var now = performance.now();
var delta = (now - this.lastFrameTime) / 1000.0;
this.lastFrameTime = now;
if (this.playing) {
this.currentTime += delta * this.speed;
}
this.updateProgress();
if (this.scrollTo >= 0) {
var targetPosition = this.scrollTo - this.markersContainer.offsetHeight/2.0;
targetPosition = Math.max(0, Math.min(targetPosition, this.markersContainer.scrollHeight - this.markersContainer.offsetHeight));
this.scrollPosition += (targetPosition - this.scrollPosition) * 0.1;
if (Math.abs(this.scrollPosition - targetPosition) < 1.0) {
this.markersContainer.scrollTop = targetPosition;
this.scrollTo = -1;
} else {
this.markersContainer.scrollTop = this.scrollPosition;
}
}
this.nextFrame = requestAnimationFrame(this.doFrame.bind(this));
};
Player.prototype.onYoutubePlayerReady = function() {
this.youtubePlayerReady = true;
this.markers[this.markers.length-1].endTime = this.youtubePlayer.getDuration();
this.updateSize();
this.youtubePlayer.setPlaybackQuality("hd1080");
if (this.currentTime > 0) {
this.currentTime = Math.max(0, Math.min(this.currentTime, this.youtubePlayer.getDuration()));
this.youtubePlayer.seekTo(this.currentTime, true);
}
if (this.shouldPlay) {
this.youtubePlayer.playVideo();
}
};
Player.prototype.onYoutubePlayerStateChange = function(ev) {
if (ev.data == YT.PlayerState.PLAYING) {
this.playing = true;
this.currentTime = this.youtubePlayer.getCurrentTime();
} else if (ev.data == YT.PlayerState.PAUSED || ev.data == YT.PlayerState.BUFFERING) {
this.playing = false;
this.currentTime = this.youtubePlayer.getCurrentTime();
this.updateProgress();
} else {
this.playing = false;
}
};
Player.prototype.onYoutubePlayerPlaybackRateChange = function(ev) {
this.speed = ev.data;
};
Player.prototype.onYoutubeReady = function() {
var youtubePlayerDiv = document.createElement("DIV");
youtubePlayerDiv.id = "youtube_player_" + Player.youtubePlayerCount++;
this.videoContainer.appendChild(youtubePlayerDiv);
this.youtubePlayer = new YT.Player(youtubePlayerDiv.id, {
videoId: this.videoContainer.getAttribute("data-videoId"),
width: this.videoContainer.offsetWidth,
height: this.videoContainer.offsetWidth / 16 * 9,
events: {
"onReady": this.onYoutubePlayerReady.bind(this),
"onStateChange": this.onYoutubePlayerStateChange.bind(this),
"onPlaybackRateChange": this.onYoutubePlayerPlaybackRateChange.bind(this)
}
});
};
Player.youtubePlayerCount = 0;

202
hmml_to_html/style.css Normal file
View File

@ -0,0 +1,202 @@
/* USER-DEFINED */
.marker .content {
width: 320px;
padding: 5px;
font-size: 14px;
}
.timecode {
font-size: 9px;
padding-right: 8px;
position: relative;
top: -2px;
}
.marker {
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.marker:hover > .content {
background-color: #222;
}
.marker:hover .faded .content {
background-color: rgba(139, 61, 35, 0.7);
color: black;
}
.marker > .content {
background-color: #161616;
color: #8A877D;
}
.marker.current > .content {
color: #B57714;
}
.marker .progress .content {
background-color: #8B3D23;
color: black;
}
/* MANDATORY */
.player_container {
display: flex;
flex-direction: row;
}
.video_container {
flex-grow: 1;
overflow: hidden;
}
.markers_container {
overflow-y: scroll;
position: relative;
}
.marker {
position: relative;
cursor: pointer;
}
.marker .content {
display: block;
box-sizing: border-box;
word-wrap: break-word;
}
.marker .progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0px;
overflow: hidden;
}
/* CUSTOM PAGE STYLE */
body {
background-color: #222;
font-family: sans-serif;
color: white;
margin: 0;
padding: 0;
}
.title {
display: flex;
flex-direction: row;
background-color: #444;
}
.title > * {
padding: 10px;
}
.title .episode_name {
flex: 1 1;
}
.title > a {
color: rgba(38, 139, 210, 1);
text-decoration: none;
}
.title > a:visited {
color: rgba(38, 139, 210, 1);
}
.title > a:hover {
text-decoration: underline;
}
.title .refs_container {
position: relative;
transition: box-shadow 800ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: inset 0 0 0 #B57714;
}
.title .refs_container:hover {
background-color: #666;
}
.title .refs_container.current {
box-shadow: inset 0px 0px 30px #B57714;
}
.title .refs_container .mouse_catcher {
position: absolute;
width: 100%;
height: 100%;
top: 0;
right: 0;
}
.title .refs_container:hover .mouse_catcher {
width: 300px;
}
.title .refs_container .refs {
position: absolute;
top: 100%;
right: 0;
width: 350px;
background-color: black;
border: 3px solid #444;
border-top: none;
z-index: 1;
display: none;
}
.title .refs_container:hover .refs {
display: block;
}
.refs .ref {
padding: 10px;
border-bottom: 1px solid rgb(51, 51, 51);
display: flex;
flex-direction: row;
align-items: center;
text-decoration: none;
color: white;
}
.refs .ref.current {
background-color: #8B3D23;
color: black;
}
.refs .ref:hover {
background-color: #222;
}
.refs .ref.current:hover {
background-color: rgba(139, 61, 35, 0.7);
}
.refs .ref:last-child {
border: none;
}
.refs .ref .timecode {
display: inline-block;
min-width: 55px;
font-size: 12px;
padding-right: 10px;
text-align: right;
}
.refs .ref .timecode:hover .time {
text-decoration: underline;
}
.refs .ref .source {
font-size: 10px;
color: #888;
line-height: 8px;
}