hmml_to_html.c: Partially implement refs [#15]
It is missing the onRefChanged() function PPiC 051
This commit is contained in:
		
							parent
							
								
									ce2feef7f6
								
							
						
					
					
						commit
						3394daf8b0
					
				
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -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 ▼</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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -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 ▼</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>
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue