From 3394daf8b0467d2dbabfd14d9753c82885d07be4 Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Fri, 10 Mar 2017 14:19:25 +0000 Subject: [PATCH] hmml_to_html.c: Partially implement refs [#15] It is missing the onRefChanged() function PPiC 051 --- hmml_to_html/hmml.a | Bin 0 -> 66588 bytes hmml_to_html/hmml_to_html.c | 331 ++++++++++++++++++++++++++++++ hmml_to_html/hmmlib.h | 87 ++++++++ hmml_to_html/out.html | 287 ++++++++++++++++++++++++++ hmml_to_html/player.js | 390 ++++++++++++++++++++++++++++++++++++ hmml_to_html/style.css | 202 +++++++++++++++++++ 6 files changed, 1297 insertions(+) create mode 100644 hmml_to_html/hmml.a create mode 100644 hmml_to_html/hmml_to_html.c create mode 100644 hmml_to_html/hmmlib.h create mode 100644 hmml_to_html/out.html create mode 100644 hmml_to_html/player.js create mode 100644 hmml_to_html/style.css diff --git a/hmml_to_html/hmml.a b/hmml_to_html/hmml.a new file mode 100644 index 0000000000000000000000000000000000000000..0d4e7510915caf5e55c4c96d081c4e6ce5c64822 GIT binary patch literal 66588 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

@D`#s8U%?#fyTc17C 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 +#include +#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, +"\n" +" \n" +" \n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +"

\n" +" %s\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, +"
\n" +" References ▼\n" +"
\n" +"
\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, +" \n" +" (%s)\n" +" \n" +"
%s
\n" +"
%s
\n" +"
\n" +"
\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, +"
\n" +"
\n" +" Annotator: %s\n" +"
\n" +"
\n" +"
\n" +"
\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, "
\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time), DataRef); + ++DataRef; + } + else + { + sprintf(Working.Location, "
\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time)); + } + + Working.Ptr = Working.Location; + while(*Working.Ptr) + { + *Out.Ptr++ = *Working.Ptr++; + } + + sprintf(Working.Location, "
%s%s
\n" + "
\n" + "
%s%s
\n" + "
\n" + "
\n" + "
%s%s
\n" + "
\n" + "
\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, "
\n" + "
\n" + "\n" + "\n" + "\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); +} diff --git a/hmml_to_html/hmmlib.h b/hmml_to_html/hmmlib.h new file mode 100644 index 0000000..0841fa3 --- /dev/null +++ b/hmml_to_html/hmmlib.h @@ -0,0 +1,87 @@ +#ifndef HMML_H_ +#define HMML_H_ +#include +#include +#include +#include + +// 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 diff --git a/hmml_to_html/out.html b/hmml_to_html/out.html new file mode 100644 index 0000000..c972ff3 --- /dev/null +++ b/hmml_to_html/out.html @@ -0,0 +1,287 @@ + + + + + + + + + + +
+
+
+
+
0:07Set the stage for the day, looking at two's complement and sign extension
+
+
0:07Set the stage for the day, looking at two's complement and sign extension
+
+
+
0:07Set the stage for the day, looking at two's complement and sign extension
+
+
+ +
+
0:41"It seems like, as you get older, the years start to go by like weeks"
+
+
0:41"It seems like, as you get older, the years start to go by like weeks"
+
+
+
0:41"It seems like, as you get older, the years start to go by like weeks"
+
+
+ +
+
1:01Recommend Code by Charles Petzold
+
+
1:01Recommend Code by Charles Petzold
+
+
+
1:01Recommend Code by Charles Petzold
+
+
+ +
+
5:03Using an electromagnet with a telegraph to communicate in Morse code
+
+
5:03Using an electromagnet with a telegraph to communicate in Morse code
+
+
+
5:03Using an electromagnet with a telegraph to communicate in Morse code
+
+
+ +
+
9:56Adder circuit
+
+
9:56Adder circuit
+
+
+
9:56Adder circuit
+
+
+ +
+
18:02Research logic gates
+
+
18:02Research logic gates
+
+
+
18:02Research logic gates
+
+
+ +
+
19:27XOR
+
+
19:27XOR
+
+
+
19:27XOR
+
+
+ +
+
25:38Redraw the tables
+
+
25:38Redraw the tables
+
+
+
25:38Redraw the tables
+
+
+ +
+
28:01What an XOR gate will do when the carry is off
+
+
28:01What an XOR gate will do when the carry is off
+
+
+
28:01What an XOR gate will do when the carry is off
+
+
+ +
+
33:27Propagating the carry through our circuit
+
+
33:27Propagating the carry through our circuit
+
+
+
33:27Propagating the carry through our circuit
+
+
+ +
+
36:58Consult Wikipedia for a ripple carry adder
+
+
36:58Consult Wikipedia for a ripple carry adder
+
+
+
36:58Consult Wikipedia for a ripple carry adder
+
+
+ +
+
38:58This circuit does 1 bit of the adder computation
+
+
38:58This circuit does 1 bit of the adder computation
+
+
+
38:58This circuit does 1 bit of the adder computation
+
+
+ +
+
41:25Subtraction circuit
+
+
41:25Subtraction circuit
+
+
+
41:25Subtraction circuit
+
+
+ +
+
44:46Agreeing on an encoding in order to communicate useful information
+
+
44:46Agreeing on an encoding in order to communicate useful information
+
+
+
44:46Agreeing on an encoding in order to communicate useful information
+
+
+ +
+
46:33"You can quote whatever you like, Miblo"
+
+
46:33"You can quote whatever you like, Miblo"
+
+
+
46:33"You can quote whatever you like, Miblo"
+
+
+ +
+
48:06Start with looking at ones' complement
+
+
48:06Start with looking at ones' complement
+
+
+
48:06Start with looking at ones' complement
+
+
+ +
+
54:28Go to two's complement
+
+
54:28Go to two's complement
+
+
+
54:28Go to two's complement
+
+
+ +
+
59:12Using both ones' complement and two's complement to enable our circuit to perform subtraction
+
+
59:12Using both ones' complement and two's complement to enable our circuit to perform subtraction
+
+
+
59:12Using both ones' complement and two's complement to enable our circuit to perform subtraction
+
+
+ +
+
1:04:43GCC's "Statements and Declarations in Expressions" Extension
+
+
1:04:43GCC's "Statements and Declarations in Expressions" Extension
+
+
+
1:04:43GCC's "Statements and Declarations in Expressions" Extension
+
+
+ +
+
1:06:12That's all for now
+
+
1:06:12That's all for now
+
+
+
1:06:12That's all for now
+
+
+ +
+
+ + + diff --git a/hmml_to_html/player.js b/hmml_to_html/player.js new file mode 100644 index 0000000..a5475ae --- /dev/null +++ b/hmml_to_html/player.js @@ -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; diff --git a/hmml_to_html/style.css b/hmml_to_html/style.css new file mode 100644 index 0000000..aa490df --- /dev/null +++ b/hmml_to_html/style.css @@ -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; +}