cinera v0.8.0: Mobile-friendly layout

Major features:
    Search page: Subdivision grid layout
    Player page: Device orientation-specific layout

Fixes:
    Strip slashes of the URLs: base_url, etc.
    Fix SnipeChecksumAndCloseFile() to not null-terminate the checksum string
    Fix hover background colouration of medium icons of current timestamp
    Fix DeriveLineageWithoutOriginOfProject() to call InitBookOfPointers()
    Fix SortAndAbbreviateSpeakers() to use the person's ID if Name is blank
    More directly display the "unit", if set, in the search results
    Fix VideoIsPrivate() to find the apparently relocated privacyStatus
    Make VideoIsPrivate() default to TRUE for non-youtube
    Output as keywords all topics that do not match "nullTopic"

Deleted asset:
    cinera_search.js

New assets:
    cinera_search_pre.js
    cinera_search_post.js
This commit is contained in:
Matt Mascarenhas 2021-01-25 18:09:30 +00:00
parent 6eeb588adf
commit 3945ac883c
9 changed files with 5493 additions and 1880 deletions

View File

@ -22,8 +22,8 @@ typedef struct
version CINERA_APP_VERSION = { version CINERA_APP_VERSION = {
.Major = 0, .Major = 0,
.Minor = 7, .Minor = 8,
.Patch = 18 .Patch = 0
}; };
#include <stdarg.h> // NOTE(matt): varargs #include <stdarg.h> // NOTE(matt): varargs
@ -119,6 +119,9 @@ clock_t TIMING_START;
#define MIN(A, B) A < B ? A : B #define MIN(A, B) A < B ? A : B
typedef int32_t hash32;
typedef hash32 asset_hash;
void void
Clear(void *V, uint64_t Size) Clear(void *V, uint64_t Size)
{ {
@ -2442,7 +2445,7 @@ typedef struct
char BaseURL[MAX_BASE_URL_LENGTH]; char BaseURL[MAX_BASE_URL_LENGTH];
char SearchLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH]; char SearchLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH];
char PlayerLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH]; char PlayerLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH];
char PlayerURLPrefix[MAX_PLAYER_URL_PREFIX_LENGTH]; // TODO(matt): Replace this with the OutputPath, when we add that char PlayerURLPrefix[MAX_PLAYER_URL_PREFIX_LENGTH];
} db_header_entries4; } db_header_entries4;
typedef db_entry3 db_entry4; typedef db_entry3 db_entry4;
@ -2553,7 +2556,7 @@ typedef struct
{ {
char Filename[MAX_ASSET_FILENAME_LENGTH]; char Filename[MAX_ASSET_FILENAME_LENGTH];
enum8(asset_type) Type; enum8(asset_type) Type;
int32_t Hash; hash32 Hash;
uint32_t LandmarkCount; uint32_t LandmarkCount;
uint64_t Associated:1; uint64_t Associated:1;
uint64_t Variants:63; uint64_t Variants:63;
@ -2639,7 +2642,7 @@ typedef struct asset
{ {
asset_type Type; asset_type Type;
char Filename[MAX_ASSET_FILENAME_LENGTH]; char Filename[MAX_ASSET_FILENAME_LENGTH];
int32_t Hash; asset_hash Hash;
vec2 Dimensions; vec2 Dimensions;
sprite Sprite; sprite Sprite;
uint64_t Variants:63; uint64_t Variants:63;
@ -2659,7 +2662,8 @@ asset BuiltinAssets[] =
{ ASSET_IMG, "cinera_icon_filter.png" }, { ASSET_IMG, "cinera_icon_filter.png" },
{ ASSET_JS, "cinera_pre.js" }, { ASSET_JS, "cinera_pre.js" },
{ ASSET_JS, "cinera_post.js" }, { ASSET_JS, "cinera_post.js" },
{ ASSET_JS, "cinera_search.js" }, { ASSET_JS, "cinera_search_pre.js" },
{ ASSET_JS, "cinera_search_post.js" },
{ ASSET_JS, "cinera_player_pre.js" }, { ASSET_JS, "cinera_player_pre.js" },
{ ASSET_JS, "cinera_player_post.js" }, { ASSET_JS, "cinera_player_post.js" },
}; };
@ -2671,7 +2675,8 @@ typedef enum
ASSET_IMG_FILTER, ASSET_IMG_FILTER,
ASSET_JS_CINERA_PRE, ASSET_JS_CINERA_PRE,
ASSET_JS_CINERA_POST, ASSET_JS_CINERA_POST,
ASSET_JS_SEARCH, ASSET_JS_SEARCH_PRE,
ASSET_JS_SEARCH_POST,
ASSET_JS_PLAYER_PRE, ASSET_JS_PLAYER_PRE,
ASSET_JS_PLAYER_POST, ASSET_JS_PLAYER_POST,
BUILTIN_ASSETS_COUNT, BUILTIN_ASSETS_COUNT,
@ -3457,6 +3462,22 @@ CopyStringToBufferNoFormat_(int LineNumber, buffer *Dest, string String)
*Dest->Ptr = '\0'; *Dest->Ptr = '\0';
} }
#define CopyStringToBufferNoTerminate(Dest, Src) CopyStringToBufferNoTerminate_(__LINE__, Dest, Src)
void
CopyStringToBufferNoTerminate_(int LineNumber, buffer *Dest, string Src)
{
if((Dest->Ptr - Dest->Location + Src.Length) > Dest->Size)
{
fprintf(stderr, "CopyStringToBufferNoTerminate(%s) call on line %d cannot accommodate %ld-character string:\n"
"%.*s\n", BufferIDStrings[Dest->ID], LineNumber, Src.Length, (int)Src.Length, Src.Base);
__asm__("int3");
}
for(int i = 0; i < Src.Length; ++i)
{
*Dest->Ptr++ = Src.Base[i];
}
}
#define CopyStringToBufferNoFormatL(Dest, Length, String) CopyStringToBufferNoFormatL_(__LINE__, Dest, Length, String) #define CopyStringToBufferNoFormatL(Dest, Length, String) CopyStringToBufferNoFormatL_(__LINE__, Dest, Length, String)
void void
CopyStringToBufferNoFormatL_(int LineNumber, buffer *Dest, int Length, char *String) CopyStringToBufferNoFormatL_(int LineNumber, buffer *Dest, int Length, char *String)
@ -4569,7 +4590,7 @@ PrintAssetAndLandmarks(db_asset *A, uint16_t *Index)
// TODO(matt): Almost definitely redo this using Locate*() functions... // TODO(matt): Almost definitely redo this using Locate*() functions...
void void
SnipeChecksumAndCloseFile(file *HTMLFile, db_asset *Asset, int LandmarksInFile, buffer *Checksum, int64_t *RunningLandmarkIndex) SnipeChecksumAndCloseFile(file *HTMLFile, db_asset *Asset, int LandmarksInFile, asset_hash Checksum, int64_t *RunningLandmarkIndex)
{ {
db_landmark *FirstLandmark = LocateFirstLandmark(Asset); db_landmark *FirstLandmark = LocateFirstLandmark(Asset);
for(int j = 0; j < LandmarksInFile; ++j, ++*RunningLandmarkIndex) for(int j = 0; j < LandmarksInFile; ++j, ++*RunningLandmarkIndex)
@ -4577,7 +4598,9 @@ SnipeChecksumAndCloseFile(file *HTMLFile, db_asset *Asset, int LandmarksInFile,
db_landmark *Landmark = FirstLandmark + *RunningLandmarkIndex; db_landmark *Landmark = FirstLandmark + *RunningLandmarkIndex;
HTMLFile->Buffer.Ptr = HTMLFile->Buffer.Location + Landmark->Position; HTMLFile->Buffer.Ptr = HTMLFile->Buffer.Location + Landmark->Position;
CopyBufferSized(&HTMLFile->Buffer, Checksum, Checksum->Ptr - Checksum->Location); char ChecksumString[16];
ClearCopyString(ChecksumString, sizeof(ChecksumString), "%08x", Checksum);
CopyStringToBufferNoTerminate(&HTMLFile->Buffer, Wrap0(ChecksumString));
} }
HTMLFile->Handle = fopen(HTMLFile->Path, "w"); HTMLFile->Handle = fopen(HTMLFile->Path, "w");
@ -4996,7 +5019,7 @@ ReadPlayerPageIntoBuffer(file *File, string BaseDir, string PlayerLocation, stri
} }
rc rc
SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum) SnipeChecksumIntoHTML(db_asset *Asset, asset_hash Checksum)
{ {
db_landmark *FirstLandmark = LocateFirstLandmark(Asset); db_landmark *FirstLandmark = LocateFirstLandmark(Asset);
@ -5564,32 +5587,38 @@ UpdateAssetInDB(asset *Asset)
if(StoredAsset->Hash != Asset->Hash) if(StoredAsset->Hash != Asset->Hash)
{ {
char OldChecksum[16];
ClearCopyString(OldChecksum, sizeof(OldChecksum), "%08x", StoredAsset->Hash);
char NewChecksum[16];
ClearCopyString(NewChecksum, sizeof(NewChecksum), "%08x", Asset->Hash);
StoredAsset->Hash = Asset->Hash; StoredAsset->Hash = Asset->Hash;
char *Ptr = (char *)Asset; char *Ptr = (char *)Asset;
Ptr += sizeof(*Asset); Ptr += sizeof(*Asset);
buffer Checksum = {};
ClaimBuffer(&Checksum, BID_CHECKSUM, 16);
CopyStringToBuffer(&Checksum, "%08x", StoredAsset->Hash);
file AssetFile = {}; file AssetFile = {};
AssetFile.Path = ConstructAssetPath(&AssetFile, Wrap0i(StoredAsset->Filename, sizeof(StoredAsset->Filename)), StoredAsset->Type); AssetFile.Path = ConstructAssetPath(&AssetFile, Wrap0i(StoredAsset->Filename, sizeof(StoredAsset->Filename)), StoredAsset->Type);
ResolvePath(&AssetFile.Path); ResolvePath(&AssetFile.Path);
string ChecksumL = Wrap0i(Checksum.Location, Checksum.Ptr - Checksum.Location); string MessageEditType = MakeString("sss", ColourStrings[CS_ONGOING], "Updating", ColourStrings[CS_END]);
string Message = MakeString("sssslsss", string Message = MakeString("sssssssssss",
ColourStrings[CS_ONGOING], "Updating", ColourStrings[CS_END], " checksum ", &ChecksumL, " of ", AssetFile.Path, " in HTML files"); " checksum ",
fprintf(stderr, "%.*s", (int)Message.Length, Message.Base); ColourStrings[CS_BLACK_BOLD], OldChecksum, ColourStrings[CS_END],
uint64_t MessageLength = Message.Length; "",
FreeString(&Message); ColourStrings[CS_BLUE_BOLD], NewChecksum, ColourStrings[CS_END],
" of ", AssetFile.Path, " in HTML files");
fprintf(stderr, "%.*s%.*s", (int)MessageEditType.Length, MessageEditType.Base, (int)Message.Length, Message.Base);
uint64_t MessageLength = MessageEditType.Length + Message.Length;
if(SnipeChecksumIntoHTML(StoredAsset, &Checksum) == RC_SUCCESS) if(SnipeChecksumIntoHTML(StoredAsset, Asset->Hash) == RC_SUCCESS)
{ {
ClearTerminalRow(MessageLength); ClearTerminalRow(MessageLength);
fprintf(stderr, "%sUpdated%s checksum %.*s of %s\n", ColourStrings[CS_REINSERTION], ColourStrings[CS_END], (int)ChecksumL.Length, ChecksumL.Base, AssetFile.Path); PrintC(CS_REINSERTION, "Updated");
fprintf(stderr, "%.*s\n", (int)Message.Length, Message.Base);
} }
DeclaimBuffer(&Checksum); FreeString(&Message);
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"); DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle); fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
@ -5625,7 +5654,7 @@ UpdateAssetInDB(asset *Asset)
fwrite(&StoredAsset, sizeof(StoredAsset), 1, DB.Metadata.File.Handle); fwrite(&StoredAsset, sizeof(StoredAsset), 1, DB.Metadata.File.Handle);
AccumulateFileEditSize(&DB.Metadata, sizeof(StoredAsset)); AccumulateFileEditSize(&DB.Metadata, sizeof(StoredAsset));
printf("%sAppended%s %s asset: %s [%08x]\n", ColourStrings[CS_ADDITION], ColourStrings[CS_END], AssetTypeNames[StoredAsset.Type], StoredAsset.Filename, StoredAsset.Hash); fprintf(stderr, "%sAppended%s %s asset: %s [%08x]\n", ColourStrings[CS_ADDITION], ColourStrings[CS_END], AssetTypeNames[StoredAsset.Type], StoredAsset.Filename, StoredAsset.Hash);
fwrite(DB.Metadata.File.Buffer.Location + BytesIntoFile, DB.Metadata.File.Buffer.Size - BytesIntoFile, 1, DB.Metadata.File.Handle); fwrite(DB.Metadata.File.Buffer.Location + BytesIntoFile, DB.Metadata.File.Buffer.Size - BytesIntoFile, 1, DB.Metadata.File.Handle);
@ -5985,8 +6014,6 @@ AbbreviationsClash(memory_book *Speakers)
void void
SortAndAbbreviateSpeakers(speakers *Speakers) SortAndAbbreviateSpeakers(speakers *Speakers)
{ {
// TODO(matt): Handle Abbreviation in its new form as a char *, rather than a fixed-sized char[], so probably doing
// MakeString0() or ExpandString0() or something
for(int i = 0; i < Speakers->Speakers.ItemCount; ++i) for(int i = 0; i < Speakers->Speakers.ItemCount; ++i)
{ {
speaker *A = GetPlaceInBook(&Speakers->Speakers, i); speaker *A = GetPlaceInBook(&Speakers->Speakers, i);
@ -6006,8 +6033,9 @@ SortAndAbbreviateSpeakers(speakers *Speakers)
for(int i = 0; i < Speakers->Speakers.ItemCount; ++i) for(int i = 0; i < Speakers->Speakers.ItemCount; ++i)
{ {
speaker *This = GetPlaceInBook(&Speakers->Speakers, i); speaker *This = GetPlaceInBook(&Speakers->Speakers, i);
string Name = This->Person->Name.Length > 0 ? This->Person->Name : This->Person->ID;
StringToColourHash(&This->Colour, This->Person->ID); StringToColourHash(&This->Colour, This->Person->ID);
This->Abbreviation = InitialString(&Speakers->Abbreviations, This->Person->Name); This->Abbreviation = InitialString(&Speakers->Abbreviations, Name);
} }
int Attempt = 0; int Attempt = 0;
@ -6016,11 +6044,12 @@ SortAndAbbreviateSpeakers(speakers *Speakers)
for(int i = 0; i < Speakers->Speakers.ItemCount; ++i) for(int i = 0; i < Speakers->Speakers.ItemCount; ++i)
{ {
speaker *This = GetPlaceInBook(&Speakers->Speakers, i); speaker *This = GetPlaceInBook(&Speakers->Speakers, i);
string Name = This->Person->Name.Length > 0 ? This->Person->Name : This->Person->ID;
switch(Attempt) switch(Attempt)
{ {
case 0: This->Abbreviation = GetFirstSubstring(This->Person->Name); break; case 0: This->Abbreviation = GetFirstSubstring(Name); break;
case 1: This->Abbreviation = InitialAndGetFinalString(&Speakers->Abbreviations, This->Person->Name); break; case 1: This->Abbreviation = InitialAndGetFinalString(&Speakers->Abbreviations, Name); break;
case 2: This->Abbreviation = This->Person->Name; break; case 2: This->Abbreviation = Name; break;
} }
} }
++Attempt; ++Attempt;
@ -6405,6 +6434,8 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
CopyStringToBuffer(CreditsMenu, CopyStringToBuffer(CreditsMenu,
" <span class=\"credit\">\n"); " <span class=\"credit\">\n");
string Name = Actor->Name.Length > 0 ? Actor->Name : Actor->ID;
if(Actor->Homepage.Length) if(Actor->Homepage.Length)
{ {
CopyStringToBuffer(CreditsMenu, CopyStringToBuffer(CreditsMenu,
@ -6414,7 +6445,7 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
" </a>\n", " </a>\n",
(int)Actor->Homepage.Length, Actor->Homepage.Base, (int)Actor->Homepage.Length, Actor->Homepage.Base,
RoleStrings[Role], RoleStrings[Role],
(int)Actor->Name.Length, Actor->Name.Base); (int)Name.Length, Name.Base);
} }
else else
{ {
@ -6424,7 +6455,7 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
" <div class=\"name\">%.*s</div>\n" " <div class=\"name\">%.*s</div>\n"
" </div>\n", " </div>\n",
RoleStrings[Role], RoleStrings[Role],
(int)Actor->Name.Length, Actor->Name.Base); (int)Name.Length, Name.Base);
} }
// TODO(matt): Handle multiple support platforms! // TODO(matt): Handle multiple support platforms!
@ -6462,9 +6493,9 @@ ErrorCredentials(string HMMLFilepath, string Actor, role Role)
} }
int int
BuildCredits(string HMMLFilepath, buffer *CreditsMenu, HMML_VideoMetaData *Metadata, speakers *Speakers, bool *RequiresCineraJS) BuildCredits(string HMMLFilepath, buffer *CreditsMenu, HMML_VideoMetaData *Metadata, person *Host, speakers *Speakers, bool *RequiresCineraJS)
{ {
person *Host = GetPersonFromConfig(Wrap0(Metadata->member)); //person *Host = GetPersonFromConfig(Wrap0(Metadata->member));
if(Host) if(Host)
{ {
PushCredentials(CreditsMenu, &Speakers->Speakers, Host, R_HOST, RequiresCineraJS); PushCredentials(CreditsMenu, &Speakers->Speakers, Host, R_HOST, RequiresCineraJS);
@ -8709,61 +8740,73 @@ ExamineDB(void)
DeclaimMenuBuffers(&MenuBuffers) DeclaimMenuBuffers(&MenuBuffers)
bool bool
VideoIsPrivate(char *VideoID) VideoIsPrivate(string VODPlatform, char *VideoID)
{ {
// TODO(matt): Redo this to only return once, at the end
// NOTE(matt): Currently only supports YouTube // NOTE(matt): Currently only supports YouTube
// NOTE(matt): Stack-string // NOTE(matt): Stack-string
char Message[128]; if(StringsMatch(Wrap0("youtube"), VODPlatform))
CopyString(Message, sizeof(Message), "%sChecking%s privacy status of: https://youtube.com/watch?v=%s", ColourStrings[CS_ONGOING], ColourStrings[CS_END], VideoID); {
fprintf(stderr, "%s", Message); char Message[128];
int MessageLength = StringLength(Message); CopyString(Message, sizeof(Message), "%sChecking%s privacy status of: https://youtube.com/watch?v=%s", ColourStrings[CS_ONGOING], ColourStrings[CS_END], VideoID);
buffer VideoAPIResponse; fprintf(stderr, "%s", Message);
ClaimBuffer(&VideoAPIResponse, BID_VIDEO_API_RESPONSE, Kilobytes(1)); int MessageLength = StringLength(Message);
buffer VideoAPIResponse;
ClaimBuffer(&VideoAPIResponse, BID_VIDEO_API_RESPONSE, Kilobytes(1));
CURL *curl = curl_easy_init(); CURL *curl = curl_easy_init();
if(curl) { if(curl) {
LastPrivacyCheck = time(0); LastPrivacyCheck = time(0);
#define APIKey "AIzaSyAdV2U8ivPk8PHMaPMId0gynksw_gdzr9k" #define APIKey "AIzaSyAdV2U8ivPk8PHMaPMId0gynksw_gdzr9k"
// NOTE(matt): Stack-string // NOTE(matt): Stack-string
char URL[1024] = {0}; char URL[1024] = {0};
CopyString(URL, sizeof(URL), "https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s", APIKey, VideoID); CopyString(URL, sizeof(URL), "https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s", APIKey, VideoID);
CURLcode CurlReturnCode; CURLcode CurlReturnCode;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &VideoAPIResponse.Ptr); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &VideoAPIResponse.Ptr);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlIntoBuffer); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlIntoBuffer);
curl_easy_setopt(curl, CURLOPT_URL, URL); curl_easy_setopt(curl, CURLOPT_URL, URL);
if((CurlReturnCode = curl_easy_perform(curl))) // TODO(matt): Handle the case when our API quota has depleted / expired
{ if((CurlReturnCode = curl_easy_perform(curl)))
fprintf(stderr, "%s\n", curl_easy_strerror(CurlReturnCode)); {
} fprintf(stderr, "%s\n", curl_easy_strerror(CurlReturnCode));
curl_easy_cleanup(curl); }
curl_easy_cleanup(curl);
VideoAPIResponse.Ptr = VideoAPIResponse.Location; VideoAPIResponse.Ptr = VideoAPIResponse.Location;
// TODO(matt): Parse this JSON // TODO(matt): Parse this JSON
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER); SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER); SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER);
if(*VideoAPIResponse.Ptr == '0') if(*VideoAPIResponse.Ptr == '0')
{ {
DeclaimBuffer(&VideoAPIResponse); DeclaimBuffer(&VideoAPIResponse);
// printf("Private video: https://youtube.com/watch?v=%s\n", VideoID); // printf("Private video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow(MessageLength); ClearTerminalRow(MessageLength);
return TRUE; return TRUE;
} }
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER); VideoAPIResponse.Ptr = VideoAPIResponse.Location;
// NOTE(matt): Stack-string SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
char Status[16]; SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"'); // NOTE(matt): Stack-string
if(!StringsDiffer0(Status, "public")) char Status[16];
{ CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"');
DeclaimBuffer(&VideoAPIResponse); if(!StringsDiffer0(Status, "public"))
ClearTerminalRow(MessageLength); {
return FALSE; DeclaimBuffer(&VideoAPIResponse);
ClearTerminalRow(MessageLength);
return FALSE;
}
} }
DeclaimBuffer(&VideoAPIResponse);
// printf("Unlisted video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow(MessageLength);
return TRUE;
}
else
{
return FALSE;
} }
DeclaimBuffer(&VideoAPIResponse);
// printf("Unlisted video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow(MessageLength);
return TRUE;
} }
speaker * speaker *
@ -9210,7 +9253,7 @@ FreeReferences(_memory_book(ref_info) *References)
rc rc
ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, memory_book *Strings, ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, memory_book *Strings,
menu_buffers *MenuBuffers, index_buffers *IndexBuffers, player_buffers *PlayerBuffers, menu_buffers *MenuBuffers, index_buffers *IndexBuffers, player_buffers *PlayerBuffers,
medium *DefaultMedium, speakers *Speakers, string Author, medium *DefaultMedium, speakers *Speakers, person *Host, string Author,
_memory_book(ref_info) *ReferencesArray, _memory_book(ref_info) *ReferencesArray,
bool *HasQuoteMenu, bool *HasReferenceMenu, bool *HasFilterMenu, bool *RequiresCineraJS, bool *HasQuoteMenu, bool *HasReferenceMenu, bool *HasFilterMenu, bool *RequiresCineraJS,
int *QuoteIdentifier, int *RefIdentifier, int *QuoteIdentifier, int *RefIdentifier,
@ -9247,7 +9290,7 @@ ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, me
CopyStringToBuffer(&IndexBuffers->Class, CopyStringToBuffer(&IndexBuffers->Class,
" class=\"marker"); " class=\"marker");
speaker *Speaker = GetSpeaker(&Speakers->Speakers, Anno->author ? Wrap0(Anno->author) : CurrentProject->StreamUsername.Length > 0 ? CurrentProject->StreamUsername : CurrentProject->Owner->ID); speaker *Speaker = GetSpeaker(&Speakers->Speakers, Anno->author ? Wrap0(Anno->author) : CurrentProject->StreamUsername.Length > 0 ? CurrentProject->StreamUsername : Host->ID);
if(!IsCategorisedAFK(Anno)) if(!IsCategorisedAFK(Anno))
{ {
if(Speakers->Speakers.ItemCount > 1 && Speaker && !IsCategorisedAuthored(Anno)) if(Speakers->Speakers.ItemCount > 1 && Speaker && !IsCategorisedAuthored(Anno))
@ -9617,12 +9660,46 @@ ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, me
return Result; return Result;
} }
string
GetNumberFromHMMLBaseFilename(string ProjectID, string HMMLBaseFilename)
{
// TODO(matt): That rigorous notion of numbering, goddammit?!
string Result = {};
if(HMMLBaseFilename.Length > ProjectID.Length)
{
Result = TrimString(HMMLBaseFilename, ProjectID.Length, 0);
}
return Result;
}
rc rc
HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseFilename, neighbourhood *N) HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseFilename, neighbourhood *N)
{ {
MEM_TEST_TOP("HMMLToBuffers"); MEM_TEST_TOP("HMMLToBuffers");
rc Result = RC_SUCCESS; rc Result = RC_SUCCESS;
RewindCollationBuffers(CollationBuffers);
Clear(CollationBuffers->Custom0, sizeof(CollationBuffers->Custom0));
Clear(CollationBuffers->Custom1, sizeof(CollationBuffers->Custom1));
Clear(CollationBuffers->Custom2, sizeof(CollationBuffers->Custom2));
Clear(CollationBuffers->Custom3, sizeof(CollationBuffers->Custom3));
Clear(CollationBuffers->Custom4, sizeof(CollationBuffers->Custom4));
Clear(CollationBuffers->Custom5, sizeof(CollationBuffers->Custom5));
Clear(CollationBuffers->Custom6, sizeof(CollationBuffers->Custom6));
Clear(CollationBuffers->Custom7, sizeof(CollationBuffers->Custom7));
Clear(CollationBuffers->Custom8, sizeof(CollationBuffers->Custom8));
Clear(CollationBuffers->Custom9, sizeof(CollationBuffers->Custom9));
Clear(CollationBuffers->Custom10, sizeof(CollationBuffers->Custom10));
Clear(CollationBuffers->Custom11, sizeof(CollationBuffers->Custom11));
Clear(CollationBuffers->Custom12, sizeof(CollationBuffers->Custom12));
Clear(CollationBuffers->Custom13, sizeof(CollationBuffers->Custom13));
Clear(CollationBuffers->Custom14, sizeof(CollationBuffers->Custom14));
Clear(CollationBuffers->Custom15, sizeof(CollationBuffers->Custom15));
Clear(CollationBuffers->Title, sizeof(CollationBuffers->Title));
Clear(CollationBuffers->URLPlayer, sizeof(CollationBuffers->URLPlayer));
Clear(CollationBuffers->URLSearch, sizeof(CollationBuffers->URLSearch));
Clear(CollationBuffers->VODPlatform, sizeof(CollationBuffers->VODPlatform));
// TODO(matt): A "MakeString0OnStack()" sort of function? // TODO(matt): A "MakeString0OnStack()" sort of function?
// NOTE(matt): Stack-string // NOTE(matt): Stack-string
int NullTerminationBytes = 1; int NullTerminationBytes = 1;
@ -9656,30 +9733,11 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
// NOTE(matt): WorkingThis.Size > 0 means that this entry was previously processed as non-private. Would we rather // NOTE(matt): WorkingThis.Size > 0 means that this entry was previously processed as non-private. Would we rather
// reregister such an entry as newly private? This test lets us avoid calling VideoIsPrivate() for all // reregister such an entry as newly private? This test lets us avoid calling VideoIsPrivate() for all
// entries, on every cinera invocation, when !CurrentProject->IgnorePrivacy // entries, on every cinera invocation, when !CurrentProject->IgnorePrivacy
if(N->WorkingThis.Size > 0 || CurrentProject->IgnorePrivacy || !VideoIsPrivate(HMML.metadata.id))
{
RewindCollationBuffers(CollationBuffers);
Clear(CollationBuffers->Custom0, sizeof(CollationBuffers->Custom0));
Clear(CollationBuffers->Custom1, sizeof(CollationBuffers->Custom1));
Clear(CollationBuffers->Custom2, sizeof(CollationBuffers->Custom2));
Clear(CollationBuffers->Custom3, sizeof(CollationBuffers->Custom3));
Clear(CollationBuffers->Custom4, sizeof(CollationBuffers->Custom4));
Clear(CollationBuffers->Custom5, sizeof(CollationBuffers->Custom5));
Clear(CollationBuffers->Custom6, sizeof(CollationBuffers->Custom6));
Clear(CollationBuffers->Custom7, sizeof(CollationBuffers->Custom7));
Clear(CollationBuffers->Custom8, sizeof(CollationBuffers->Custom8));
Clear(CollationBuffers->Custom9, sizeof(CollationBuffers->Custom9));
Clear(CollationBuffers->Custom10, sizeof(CollationBuffers->Custom10));
Clear(CollationBuffers->Custom11, sizeof(CollationBuffers->Custom11));
Clear(CollationBuffers->Custom12, sizeof(CollationBuffers->Custom12));
Clear(CollationBuffers->Custom13, sizeof(CollationBuffers->Custom13));
Clear(CollationBuffers->Custom14, sizeof(CollationBuffers->Custom14));
Clear(CollationBuffers->Custom15, sizeof(CollationBuffers->Custom15));
Clear(CollationBuffers->Title, sizeof(CollationBuffers->Title));
Clear(CollationBuffers->URLPlayer, sizeof(CollationBuffers->URLPlayer));
Clear(CollationBuffers->URLSearch, sizeof(CollationBuffers->URLSearch));
Clear(CollationBuffers->VODPlatform, sizeof(CollationBuffers->VODPlatform));
if(N->WorkingThis.Size > 0 || CurrentProject->IgnorePrivacy || !VideoIsPrivate(CurrentProject->VODPlatform, HMML.metadata.id))
{
// TODO(matt): Do a catch-all function that checks for missing info at the head, such as everything from here
// to the loop and additionally an indexer
if(BaseFilename.Length > MAX_BASE_FILENAME_LENGTH) if(BaseFilename.Length > MAX_BASE_FILENAME_LENGTH)
{ {
IndexingErrorSizing(&FilepathL, 0, "Base filename", BaseFilename, MAX_BASE_FILENAME_LENGTH); IndexingErrorSizing(&FilepathL, 0, "Base filename", BaseFilename, MAX_BASE_FILENAME_LENGTH);
@ -9702,15 +9760,24 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
Title = Wrap0(HMML.metadata.title); Title = Wrap0(HMML.metadata.title);
} }
if(!HMML.metadata.member) person *Host = CurrentProject->Owner;
if(HMML.metadata.member)
{ {
IndexingError(&FilepathL, 0, S_ERROR, "The [video] node lacks a \"member\"", 0); Host = GetPersonFromConfig(Wrap0(HMML.metadata.member));
Result = RC_ERROR_HMML; if(!Host && CurrentProject->Owner)
{
fprintf(stderr, "Falling back to the owner set in the config: ");
PrintStringCN(CS_MAGENTA_BOLD, CurrentProject->Owner->ID, FALSE, TRUE);
Host = CurrentProject->Owner;
}
} }
else if(!GetPersonFromConfig(Wrap0(HMML.metadata.member)))
if(!Host)
{ {
ErrorCredentials(FilepathL, Wrap0(HMML.metadata.member), R_HOST); IndexingError(&FilepathL, 0, S_ERROR, "No known owner set in the config, or member in the [video] node", 0);
Result = RC_ERROR_HMML; Result = RC_ERROR_HMML;
//ErrorCredentials(FilepathL, Wrap0(HMML.metadata.member), R_HOST);
//Result = RC_ERROR_HMML;
} }
if(!HMML.metadata.id) if(!HMML.metadata.id)
@ -9928,14 +9995,14 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
" <div class=\"markers\">\n"); " <div class=\"markers\">\n");
bool RequiresCineraJS = FALSE; bool RequiresCineraJS = FALSE;
Result = BuildCredits(FilepathL, &MenuBuffers.Credits, &HMML.metadata, &Speakers, &RequiresCineraJS); Result = BuildCredits(FilepathL, &MenuBuffers.Credits, &HMML.metadata, Host, &Speakers, &RequiresCineraJS);
if(Result == RC_SUCCESS) if(Result == RC_SUCCESS)
{ {
CopyStringToBuffer(&CollationBuffers->SearchEntry, "name: \""); CopyStringToBuffer(&CollationBuffers->SearchEntry, "location: \"%.*s\"\n", (int)OutputLocation.Length, OutputLocation.Base);
CopyStringToBuffer(&CollationBuffers->SearchEntry, "%.*s", (int)OutputLocation.Length, OutputLocation.Base); string Number = GetNumberFromHMMLBaseFilename(Wrap0i(N->Project->ID, sizeof(N->Project->ID)), BaseFilename);
CopyStringToBuffer(&CollationBuffers->SearchEntry, "number: \"%.*s\"\n", (int)Number.Length, Number.Base);
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"\n" CopyStringToBuffer(&CollationBuffers->SearchEntry, "title: \"");
"title: \"");
CopyStringToBufferNoFormat(&CollationBuffers->SearchEntry, Wrap0(HMML.metadata.title)); CopyStringToBufferNoFormat(&CollationBuffers->SearchEntry, Wrap0(HMML.metadata.title));
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"\n" CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"\n"
"markers:\n"); "markers:\n");
@ -9964,12 +10031,12 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
if(Anno->quote.author) { Author = Wrap0(Anno->quote.author); } if(Anno->quote.author) { Author = Wrap0(Anno->quote.author); }
else if(HMML.metadata.stream_username) { Author = Wrap0(HMML.metadata.stream_username); } else if(HMML.metadata.stream_username) { Author = Wrap0(HMML.metadata.stream_username); }
else if(CurrentProject->StreamUsername.Length > 0) { Author = CurrentProject->StreamUsername; } else if(CurrentProject->StreamUsername.Length > 0) { Author = CurrentProject->StreamUsername; }
else { Author = CurrentProject->Owner->ID; } else { Author = Host->ID; }
/* */ MEM_TEST_MID("HMMLToBuffers"); /* */ MEM_TEST_MID("HMMLToBuffers");
/* +MEM */ Result = ProcessTimecode(CollationBuffers, N, Wrap0(Filepath), &Strings, /* +MEM */ Result = ProcessTimecode(CollationBuffers, N, Wrap0(Filepath), &Strings,
/* */ &MenuBuffers, &IndexBuffers, &PlayerBuffers, /* */ &MenuBuffers, &IndexBuffers, &PlayerBuffers,
/* */ DefaultMedium, &Speakers, Author, &ReferencesArray, /* */ DefaultMedium, &Speakers, Host, Author, &ReferencesArray,
/* */ &HasQuoteMenu, &HasReferenceMenu, &HasFilterMenu, &RequiresCineraJS, /* */ &HasQuoteMenu, &HasReferenceMenu, &HasFilterMenu, &RequiresCineraJS,
/* */ &QuoteIdentifier, &RefIdentifier, /* */ &QuoteIdentifier, &RefIdentifier,
/* */ &Topics, &Media, /* */ &Topics, &Media,
@ -10167,7 +10234,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
} }
CopyStringToBuffer(&PlayerBuffers.Menus, CopyStringToBuffer(&PlayerBuffers.Menus,
" <div class=\"help\">\n" " <div class=\"cineraHelp\">\n"
" <span>?</span>\n" " <span>?</span>\n"
" <div class=\"help_container\">\n" " <div class=\"help_container\">\n"
" <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n" " <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n"
@ -10347,7 +10414,8 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&CollationBuffers->IncludesPlayer, CopyStringToBuffer(&CollationBuffers->IncludesPlayer,
"<meta charset=\"UTF-8\">\n" "<meta charset=\"UTF-8\">\n"
" <meta name=\"generator\" content=\"Cinera %d.%d.%d\">\n", " <meta name=\"generator\" content=\"Cinera %d.%d.%d\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n",
CINERA_APP_VERSION.Major, CINERA_APP_VERSION.Major,
CINERA_APP_VERSION.Minor, CINERA_APP_VERSION.Minor,
CINERA_APP_VERSION.Patch); CINERA_APP_VERSION.Patch);
@ -10362,7 +10430,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
for(int i = 0; i < Topics.ItemCount; ++i) for(int i = 0; i < Topics.ItemCount; ++i)
{ {
category_info *This = GetPlaceInBook(&Topics, i); category_info *This = GetPlaceInBook(&Topics, i);
if(StringsMatch(This->Marker, Wrap0("nullTopic"))) if(!StringsMatch(This->Marker, Wrap0("nullTopic")))
{ {
CopyStringToBuffer(&CollationBuffers->IncludesPlayer, "%.*s, ", (int)This->Marker.Length, This->Marker.Base); CopyStringToBuffer(&CollationBuffers->IncludesPlayer, "%.*s, ", (int)This->Marker.Length, This->Marker.Base);
} }
@ -10477,7 +10545,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&PlayerBuffers.Script, CopyStringToBuffer(&PlayerBuffers.Script,
"\"></script>"); "\"></script>");
CopyStringToBuffer(&CollationBuffers->Player, "<div>\n" CopyStringToBuffer(&CollationBuffers->Player, "<div class=\"cinera\">\n"
" "); " ");
CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER); CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER);
CopyStringToBuffer(&CollationBuffers->Player, "\n" CopyStringToBuffer(&CollationBuffers->Player, "\n"
@ -10555,12 +10623,16 @@ typedef struct
html_element HTMLElements[] = html_element HTMLElements[] =
{ {
{ "a", FALSE }, { "a", FALSE },
{ "br", TRUE },
{ "div", FALSE }, { "div", FALSE },
{ "h1", FALSE },
{ "h2", FALSE },
{ "img", TRUE }, { "img", TRUE },
{ "input", TRUE }, { "input", TRUE },
{ "label", FALSE }, { "label", FALSE },
{ "li", FALSE }, { "li", FALSE },
{ "nav", FALSE }, { "nav", FALSE },
{ "p", FALSE },
{ "script", FALSE }, { "script", FALSE },
{ "span", FALSE }, { "span", FALSE },
{ "ul", FALSE }, { "ul", FALSE },
@ -10569,12 +10641,16 @@ html_element HTMLElements[] =
typedef enum typedef enum
{ {
NODE_A, NODE_A,
NODE_BR,
NODE_DIV, NODE_DIV,
NODE_H1,
NODE_H2,
NODE_IMG, NODE_IMG,
NODE_INPUT, NODE_INPUT,
NODE_LABEL, NODE_LABEL,
NODE_LI, NODE_LI,
NODE_NAV, NODE_NAV,
NODE_P,
NODE_SCRIPT, NODE_SCRIPT,
NODE_SPAN, NODE_SPAN,
NODE_UL, NODE_UL,
@ -11619,10 +11695,9 @@ InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template *BespokeTempl
CycleFile(&DB.File); CycleFile(&DB.File);
} }
string EntryTitle = Wrap0(CollationBuffers->Title);
if(!VideoIsPrivate || !RecheckingPrivacy) if(!VideoIsPrivate || !RecheckingPrivacy)
{ {
string EntryTitle = Wrap0(CollationBuffers->Title);
LogEdit(EditType, CurrentProject->Lineage, BaseFilename, &EntryTitle, VideoIsPrivate); LogEdit(EditType, CurrentProject->Lineage, BaseFilename, &EntryTitle, VideoIsPrivate);
PrintEdit(EditType, CurrentProject->Lineage, BaseFilename, &EntryTitle, VideoIsPrivate, TRUE); PrintEdit(EditType, CurrentProject->Lineage, BaseFilename, &EntryTitle, VideoIsPrivate, TRUE);
} }
@ -12825,7 +12900,7 @@ GenerateFilterOfProjectAndChildren(buffer *Filter, db_header_project *StoredP, p
if(!TopLevel || StoredP->EntryCount > 0) if(!TopLevel || StoredP->EntryCount > 0)
{ {
OpenNodeNewLine(Filter, &Filter->IndentLevel, NODE_DIV, 0); OpenNodeNewLine(Filter, &Filter->IndentLevel, NODE_DIV, 0);
AppendStringToBuffer(Filter, Wrap0(" class=\"cineraFilterProject\" data-baseURL=\"")); AppendStringToBuffer(Filter, Wrap0(" class=\"cineraMenuItem cineraFilterProject\" data-baseURL=\""));
AppendStringToBuffer(Filter, P->BaseURL); AppendStringToBuffer(Filter, P->BaseURL);
AppendStringToBuffer(Filter, Wrap0("\" data-searchLocation=\"")); AppendStringToBuffer(Filter, Wrap0("\" data-searchLocation=\""));
AppendStringToBuffer(Filter, P->SearchLocation); AppendStringToBuffer(Filter, P->SearchLocation);
@ -12872,6 +12947,8 @@ GenerateIndexOfProjectAndChildren(buffer *Index, db_header_project *StoredP, pro
AppendStringToBuffer(Index, P->SearchLocation); AppendStringToBuffer(Index, P->SearchLocation);
AppendStringToBuffer(Index, Wrap0("\" data-playerLocation=\"")); AppendStringToBuffer(Index, Wrap0("\" data-playerLocation=\""));
AppendStringToBuffer(Index, P->PlayerLocation); AppendStringToBuffer(Index, P->PlayerLocation);
AppendStringToBuffer(Index, Wrap0("\" data-unit=\""));
AppendStringToBuffer(Index, P->Unit);
AppendStringToBuffer(Index, Wrap0("\">")); AppendStringToBuffer(Index, Wrap0("\">"));
OpenNodeNewLine(Index, &Index->IndentLevel, NODE_DIV, 0); OpenNodeNewLine(Index, &Index->IndentLevel, NODE_DIV, 0);
@ -12905,27 +12982,28 @@ GenerateIndexOfProjectAndChildren(buffer *Index, db_header_project *StoredP, pro
if(*StoredP->Unit) if(*StoredP->Unit)
{ {
// TODO(matt): That rigorous notion of numbering, goddammit?! string NumberL = GetNumberFromHMMLBaseFilename(P->ID, Wrap0i(Entry->HMMLBaseFilename, sizeof(Entry->HMMLBaseFilename)));
string NumberL = TrimString(Wrap0i(Entry->HMMLBaseFilename, sizeof(Entry->HMMLBaseFilename)), if(NumberL.Length > 0)
P->ID.Length, 0);
char Number[NumberL.Length + 1];
ClearCopyStringNoFormat(Number, sizeof(Number), NumberL);
if(CurrentProject->NumberingScheme == NS_LINEAR)
{ {
for(int i = 0; Number[i]; ++i) char Number[NumberL.Length + 1];
ClearCopyStringNoFormat(Number, sizeof(Number), NumberL);
if(CurrentProject->NumberingScheme == NS_LINEAR)
{ {
if(Number[i] == '_') for(int i = 0; Number[i]; ++i)
{ {
Number[i] = '.'; if(Number[i] == '_')
{
Number[i] = '.';
}
} }
} }
}
AppendStringToBuffer(Index, P->Unit); AppendStringToBuffer(Index, P->Unit);
AppendStringToBuffer(Index, Wrap0(" ")); AppendStringToBuffer(Index, Wrap0(" "));
AppendStringToBuffer(Index, Wrap0i(Number, sizeof(Number))); AppendStringToBuffer(Index, Wrap0i(Number, sizeof(Number)));
AppendStringToBuffer(Index, Wrap0(": ")); AppendStringToBuffer(Index, Wrap0(": "));
}
} }
// HERE // HERE
@ -13097,6 +13175,45 @@ GenerateThemeLinks(buffer *IncludesSearch, project *P)
FreeBook(&UniqueThemes); FreeBook(&UniqueThemes);
} }
void
AppendHelpKeyToBuffer(buffer *B, uint32_t *IndentationLevel, string Key, bool Available)
{
OpenNodeNewLine(B, IndentationLevel, NODE_SPAN, 0);
AppendStringToBuffer(B, Wrap0(" class=\"help_key"));
if(!Available)
{
AppendStringToBuffer(B, Wrap0(" unavailable"));
}
AppendStringToBuffer(B, Wrap0("\">"));
AppendStringToBuffer(B, Key);
CloseNode(B, IndentationLevel, NODE_SPAN);
}
void
AppendHelpKeyToBufferNewLine(buffer *B, uint32_t *IndentationLevel, string Key, bool Available)
{
AppendHelpKeyToBuffer(B, IndentationLevel, Key, Available);
OpenNodeC(B, IndentationLevel, NODE_BR, 0);
}
void
AppendHelpKeyAndTextToBuffer(buffer *B, uint32_t *IndentationLevel, string Key, string Text, bool Available)
{
AppendHelpKeyToBuffer(B, IndentationLevel, Key, Available);
AppendStringToBuffer(B, Wrap0(" "));
OpenNodeNewLine(B, IndentationLevel, NODE_SPAN, 0);
AppendStringToBuffer(B, Wrap0(" class=\"help_text"));
if(!Available)
{
AppendStringToBuffer(B, Wrap0(" unavailable"));
}
AppendStringToBuffer(B, Wrap0("\">"));
AppendStringToBuffer(B, Text);
CloseNode(B, IndentationLevel, NODE_SPAN);
AppendStringToBuffer(B, Wrap0("\n"));
}
int int
SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P, string Theme, bool RequiresCineraJS) // NOTE(matt): This guy malloc's CollationBuffers->Search SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P, string Theme, bool RequiresCineraJS) // NOTE(matt): This guy malloc's CollationBuffers->Search
{ {
@ -13134,6 +13251,7 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
CopyStringToBuffer(&CollationBuffers->IncludesSearch, CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"<meta charset=\"UTF-8\">\n" "<meta charset=\"UTF-8\">\n"
" <meta name=\"generator\" content=\"Cinera %d.%d.%d\">\n" " <meta name=\"generator\" content=\"Cinera %d.%d.%d\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
"\n" "\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%.*s", " <link rel=\"stylesheet\" type=\"text/css\" href=\"%.*s",
CINERA_APP_VERSION.Major, CINERA_APP_VERSION.Major,
@ -13159,38 +13277,54 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
CopyStringToBuffer(&CollationBuffers->IncludesSearch, CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\">"); "\">");
asset *JSSearch = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_SEARCH].Filename), ASSET_JS);
ConstructResolvedAssetURL(&URL, JSSearch, PAGE_SEARCH);
uint32_t IndentationLevel = 1; uint32_t IndentationLevel = 1;
++IndentationLevel; ++IndentationLevel;
buffer Script = {};
Script.ID = BID_SCRIPT;
OpenNodeNewLine(&Script, &IndentationLevel, NODE_SCRIPT, 0);
AppendStringToBuffer(&Script, Wrap0(" type=\"text/javascript\" src=\""));
AppendStringToBuffer(&Script, Wrap0i(URL.Location, URL.Ptr - URL.Location));
DeclaimBuffer(&URL);
PushAssetLandmark(&Script, JSSearch, PAGE_SEARCH, TRUE);
AppendStringToBuffer(&Script, Wrap0("\">"));
CloseNode(&Script, &IndentationLevel, NODE_SCRIPT);
bool SearchRequired = FALSE; bool SearchRequired = FALSE;
//IndentBuffer(&CollationBuffers->Search, ++IndentationLevel); //IndentBuffer(&CollationBuffers->Search, ++IndentationLevel);
buffer *B = &CollationBuffers->Search; buffer *B = &CollationBuffers->Search;
OpenNode(B, &IndentationLevel, NODE_DIV, "cineraIndexControl"); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndex");
AppendStringToBuffer(B, Wrap0(" class=\"")); AppendStringToBuffer(B, Wrap0(" class=\""));
AppendStringToBuffer(B, Theme); AppendStringToBuffer(B, Theme);
AppendStringToBuffer(B, Wrap0("\">")); AppendStringToBuffer(B, Wrap0("\">"));
OpenNodeC(B, &IndentationLevel, NODE_DIV, "cineraIndexControl");
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndexSort"); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenu cineraMenuTitle ViewSettings\">"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_SPAN, 0);
AppendStringToBuffer(B, Wrap0("Settings"));
CloseNodeNewLine(B, &IndentationLevel, NODE_SPAN);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuContainer\">"));
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuItem sort\">"));
AppendStringToBuffer(B, Wrap0("Sort: Old to New &#9206;")); AppendStringToBuffer(B, Wrap0("Sort: Old to New &#9206;"));
CloseNode(B, &IndentationLevel, NODE_DIV); CloseNode(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuItem view\">"));
AppendStringToBuffer(B, Wrap0("View: Grid"));
CloseNode(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuItem anim\">"));
AppendStringToBuffer(B, Wrap0("Animations: ✔"));
CloseNode(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuItem save\">"));
AppendStringToBuffer(B, Wrap0("Save Settings: ✔"));
CloseNode(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenuContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenu ViewSettings
bool FilterRequired = FALSE; bool FilterRequired = FALSE;
if(StoredP) if(StoredP)
{ {
@ -13221,7 +13355,7 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraIndexFilter\">")); AppendStringToBuffer(B, Wrap0(" class=\"cineraMenu cineraMenuTitle IndexFilter\">"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_SPAN, 0); OpenNodeCNewLine(B, &IndentationLevel, NODE_SPAN, 0);
@ -13237,7 +13371,7 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
CloseNode(B, &IndentationLevel, NODE_SPAN); CloseNode(B, &IndentationLevel, NODE_SPAN);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"filter_container\">")); AppendStringToBuffer(B, Wrap0(" class=\"cineraMenuContainer\">"));
} }
// NOTE(matt): What I think the rules should be are: // NOTE(matt): What I think the rules should be are:
@ -13271,8 +13405,8 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
{ {
AppendLandmarkedBuffer(B, &Filter, PAGE_SEARCH); AppendLandmarkedBuffer(B, &Filter, PAGE_SEARCH);
FreeBuffer(&Filter); FreeBuffer(&Filter);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenuContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenu IndexFilter
} }
asset *JSCineraPre = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_CINERA_PRE].Filename), ASSET_JS); asset *JSCineraPre = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_CINERA_PRE].Filename), ASSET_JS);
@ -13298,6 +13432,26 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
"\" defer></script>"); "\" defer></script>");
} }
asset *JSSearchPre = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_SEARCH_PRE].Filename), ASSET_JS);
ConstructResolvedAssetURL(&URL, JSSearchPre, PAGE_SEARCH);
CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\n <script type=\"text/javascript\" src=\"%s",
URL.Location);
DeclaimBuffer(&URL);
PushAssetLandmark(&CollationBuffers->IncludesSearch, JSSearchPre, PAGE_SEARCH, 0);
CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\"></script>");
asset *JSSearchPost = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_SEARCH_POST].Filename), ASSET_JS);
ConstructResolvedAssetURL(&URL, JSSearchPost, PAGE_SEARCH);
CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\n <script type=\"text/javascript\" src=\"%s",
URL.Location);
DeclaimBuffer(&URL);
PushAssetLandmark(&CollationBuffers->IncludesSearch, JSSearchPost, PAGE_SEARCH, 0);
CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\" defer></script>");
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraQueryContainer\">")); AppendStringToBuffer(B, Wrap0(" class=\"cineraQueryContainer\">"));
@ -13320,10 +13474,82 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
IndentBuffer(B, IndentationLevel); IndentBuffer(B, IndentationLevel);
AppendStringToBuffer(B, Wrap0("Downloading data...")); AppendStringToBuffer(B, Wrap0("Downloading data..."));
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // spinner
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // inputContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraQueryContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraHelp\">"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_SPAN, 0);
AppendStringToBuffer(B, Wrap0("?"));
CloseNode(B, &IndentationLevel, NODE_SPAN);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"help_container\">"));
AppendHelpKeyToBuffer(B, &IndentationLevel, Wrap0("?"), TRUE);
OpenNodeC(B, &IndentationLevel, NODE_H1, 0);
AppendStringToBuffer(B, Wrap0("Keyboard Navigation"));
CloseNode(B, &IndentationLevel, NODE_H1);
OpenNodeC(B, &IndentationLevel, NODE_H2, 0);
AppendStringToBuffer(B, Wrap0("Grid Traversal"));
CloseNode(B, &IndentationLevel, NODE_H2);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("h"), Wrap0("Shift leftwards"), TRUE);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("k"), Wrap0("Ascend (zoom out)"), TRUE);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("l"), Wrap0("Shift rightwards"), TRUE);
OpenNodeCNewLine(B, &IndentationLevel, NODE_BR, 0);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"help_paragraph\">"));
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"key_block\">"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("1"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("q"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("a"), TRUE);
AppendHelpKeyToBuffer(B, &IndentationLevel, Wrap0("z"), TRUE);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("2"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("w"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("s"), TRUE);
AppendHelpKeyToBuffer(B, &IndentationLevel, Wrap0("x"), TRUE);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("3"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("e"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("d"), TRUE);
AppendHelpKeyToBuffer(B, &IndentationLevel, Wrap0("c"), TRUE);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("4"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("r"), TRUE);
AppendHelpKeyToBufferNewLine(B, &IndentationLevel, Wrap0("f"), TRUE);
AppendHelpKeyToBuffer(B, &IndentationLevel, Wrap0("v"), TRUE);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // key_block
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // help_paragraph
OpenNodeC(B, &IndentationLevel, NODE_H2, 0);
AppendStringToBuffer(B, Wrap0("Settings"));
CloseNode(B, &IndentationLevel, NODE_H2);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("t"), Wrap0("Toggle sort order"), TRUE);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("y"), Wrap0("Toggle view style"), TRUE);
AppendHelpKeyAndTextToBuffer(B, &IndentationLevel, Wrap0("m"), Wrap0("Toggle animations"), TRUE);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // help_container
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraHelp
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraIndexControl
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraResultsSummary"); OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraResultsSummary");
@ -13340,17 +13566,50 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
AppendStringToBuffer(B, Wrap0("\n")); AppendStringToBuffer(B, Wrap0("\n"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndex"); OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndexList");
AppendLandmarkedBuffer(B, &Index, PAGE_SEARCH); AppendLandmarkedBuffer(B, &Index, PAGE_SEARCH);
FreeBuffer(&Index); FreeBuffer(&Index);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraIndexList
AppendLandmarkedBuffer(B, &Script, PAGE_SEARCH); OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
FreeBuffer(&Script); AppendStringToBuffer(B, Wrap0(" class=\"cineraIndexGridContainer\">"));
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraTraversalContainer\">"));
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraTraversal\">"));
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraButton prev\">"));
OpenNodeC(B, &IndentationLevel, NODE_P, 0);
AppendStringToBuffer(B, Wrap0(""));
CloseNode(B, &IndentationLevel, NODE_P);
CloseNode(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraButton ascension\">"));
OpenNodeC(B, &IndentationLevel, NODE_P, 0);
AppendStringToBuffer(B, Wrap0(""));
CloseNode(B, &IndentationLevel, NODE_P);
CloseNode(B, &IndentationLevel, NODE_DIV);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraButton next\">"));
OpenNodeC(B, &IndentationLevel, NODE_P, 0);
AppendStringToBuffer(B, Wrap0(""));
CloseNode(B, &IndentationLevel, NODE_P);
CloseNode(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraTraversal
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraTraversalContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraIndexGridContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraIndex
if(!SearchRequired) { return RC_NOOP; } if(!SearchRequired) { return RC_NOOP; }
else { return RC_SUCCESS; } else { return RC_SUCCESS; }

View File

@ -58,7 +58,7 @@ nav.cineraNavDropdown .cineraNavTitle {
nav.cineraNavDropdown ul.cineraNavHorizontal { nav.cineraNavDropdown ul.cineraNavHorizontal {
display: none; display: none;
width: 100%; width: 100%;
z-index: 8; z-index: 4096;
position: absolute; position: absolute;
} }
@ -85,12 +85,124 @@ ul.cineraNavPlain li.current > a {
/* Index */ /* Index */
#cineraIndexControl { #cineraIndex
{
display: flex; display: flex;
margin: 16px auto; flex-direction: column;
max-width: 1024px; }
#cineraIndex #cineraIndexControl
{
display: flex;
position: -webkit-sticky;
position: sticky;
top: 0px;
z-index: 64;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
#cineraIndex .cineraIndexGridContainer
{
display: flex;
flex-direction: column;
align-items: center; align-items: center;
padding: 8px; justify-content: center;
}
#cineraIndex .cineraIndexGridContainer.Portrait
{
flex-direction: column-reverse;
}
#cineraIndex .cineraIndexGridContainer.Landscape.Left
{
flex-direction: row-reverse;
}
#cineraIndex .cineraIndexGridContainer.Landscape.Right
{
flex-direction: row;
}
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal
{
flex-direction: column;
}
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal .cineraButton.prev
{
order: 1;
}
#cineraIndex .cineraIndexGridContainer.Landscape .cineraTraversal .cineraButton.next
{
order: 2;
}
#cineraIndex .cineraTraversal .cineraButton
{
/* TODO(matt): Do the cineraTraversal as a flex box, to auto-size these buttons? */
height: 42px;
width: 42px;
padding: 2px;
margin: 2px;
display: inline-flex;
align-items: center;
justify-content: center;
}
#cineraIndex.anim .cineraTraversal,
#cineraIndex.anim .cineraTraversal * p
{
transition: transform .32s;
}
#cineraIndex .cineraTraversalContainer
{
overflow: hidden;
}
#cineraIndex .cineraTraversal
{
display: flex;
justify-content: center;
transform: rotate(0deg);
}
#cineraIndex.reversed .cineraTraversal
{
transform: rotate(180deg);
}
#cineraIndex .cineraTraversal * p
{
transform: rotate(0deg);
}
#cineraIndex.reversed .cineraTraversal .ascension p
{
transform: rotate(-180deg);
}
#cineraIndex.reversed .cineraTraversal .prev p,
#cineraIndex.reversed .cineraTraversal .next p
{
transform: rotate(-360deg);
}
#cineraIndex .cineraTraversal .next.ascension p
{
transform: rotate(-45deg);
}
#cineraIndex.reversed .cineraTraversal .next.ascension p
{
transform: rotate(-315deg);
} }
.cineraFilterProject .cineraText, .cineraFilterProject .cineraText,
@ -99,15 +211,30 @@ ul.cineraNavPlain li.current > a {
display: flex; display: flex;
} }
.cineraIndexFilter { .cineraMenu.cineraMenuTitle {
padding: 10px; padding: 10px;
cursor: default;
} }
.cineraIndexFilter .filter_container .cineraMenuContainer
{ {
display: none; display: none;
position: absolute; position: absolute;
top: 100%;
z-index: 64; z-index: 64;
cursor: pointer;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
border: 1px solid;
border-top: none;
}
.cineraMenuContainer.visible
{
display: block;
} }
.cineraFilterProject, .cineraFilterProject,
@ -120,7 +247,7 @@ ul.cineraNavPlain li.current > a {
} }
.cineraIndexProject .cineraProjectTitle, .cineraIndexProject .cineraProjectTitle,
.cineraFilterProject .cineraMenuItem
{ {
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
@ -130,7 +257,6 @@ ul.cineraNavPlain li.current > a {
flex-grow: 1; flex-grow: 1;
padding-left: 16px; padding-left: 16px;
display: flex; display: flex;
flex-direction: horizontal;
margin: auto; margin: auto;
} }
@ -141,19 +267,21 @@ ul.cineraNavPlain li.current > a {
} }
.cineraQueryContainer .inputContainer { .cineraQueryContainer .inputContainer {
display: flex;
flex-grow: 1; flex-grow: 1;
position: relative; position: relative;
padding: .5em;
} }
.cineraQueryContainer #query { .cineraQueryContainer #query {
width: 100%; flex-grow: 1;
} }
.cineraQueryContainer .inputContainer .spinner { .cineraQueryContainer .inputContainer .spinner {
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 5px; right: 5px;
color: black; color: #000000;
height: 100%; height: 100%;
display: none; display: none;
} }
@ -163,7 +291,7 @@ ul.cineraNavPlain li.current > a {
} }
#cineraResults, #cineraResults,
#cineraIndex { #cineraIndexList {
margin: 0 auto; margin: 0 auto;
max-width: 800px; max-width: 800px;
} }
@ -173,22 +301,94 @@ ul.cineraNavPlain li.current > a {
flex-flow: column; flex-flow: column;
} }
#cineraIndexControl #cineraIndexSort { #cineraIndexList .cineraIndexEntries {
padding: 4px; display: flex;
flex-flow: column;
width: 100%;
} }
#cineraIndexControl #cineraIndexSort, #cineraIndex #cineraIndexGrid
#cineraIndexControl .cineraIndexFilter .filter_container { {
cursor: pointer; display: flex;
overflow: hidden;
position: relative;
perspective-origin: center;
-webkit-perspective-origin: center;
}
#cineraIndex #cineraIndexGrid .cineraButtons
{
flex-shrink: 0;
display: grid;
grid-gap: 2px;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
#cineraIndex .cineraButton
{
border: 1px solid;
text-align: center;
font-size: 16px;
font-weight: bold;
flex-direction: column;
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
} }
#cineraIndex .cineraIndexEntries { #cineraIndex #cineraIndexGrid .cineraButton.subdivision
{
box-sizing: border-box;
display: flex; display: flex;
flex-flow: column; backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .head-item,
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .tail-item
{
width: 100%; width: 100%;
height: 50%;
display: flex;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf
{
justify-content: center;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf a
{
text-decoration: none;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision.leaf a:hover {
cursor: default;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .head-item,
#cineraIndex.reversed #cineraIndexGrid .cineraButton.subdivision .tail-item
{
align-items: flex-start;
justify-content: flex-start;
text-align: left;
}
#cineraIndex #cineraIndexGrid .cineraButton.subdivision .tail-item,
#cineraIndex.reversed #cineraIndexGrid .cineraButton.subdivision .head-item
{
align-items: flex-end;
justify-content: flex-end;
text-align: right;
}
#cineraIndexList.hidden,
#cineraIndex .cineraIndexGridContainer.hidden
{
display: none;
} }
#cineraResults .dayContainer { #cineraResults .dayContainer {
@ -203,7 +403,7 @@ ul.cineraNavPlain li.current > a {
line-height: 16px; line-height: 16px;
padding: 5px; padding: 5px;
vertical-align: top; vertical-align: top;
width: 200px; max-width: 200px;
} }
.cineraIndexEntries div a { .cineraIndexEntries div a {
@ -243,36 +443,6 @@ ul.cineraNavPlain li.current > a {
display: none; display: none;
} }
@media (max-width: 720px), (max-height: 512px)
{
#cineraIndexControl {
margin: 4px auto;
}
#cineraIndexControl,
#cineraResultsSummary {
font-size: 64%;
}
#cineraResults .dayContainer {
flex-direction: column;
}
#cineraResults .dayContainer .dayName {
font-weight: bold;
text-align: center;
width: 100%;
}
#cineraResults .dayContainer .markerList {
max-width: 100%;
}
.cineraIndexEntries div a {
font-size: 80%;
}
}
/* Player */ /* Player */
/* Player / Structure */ /* Player / Structure */
@ -285,7 +455,7 @@ ul.cineraNavPlain li.current > a {
text-decoration: none; text-decoration: none;
} }
.cineraMenus > *:not(.views), .cineraMenus > *:not(.views, .cineraHelp),
.cineraMenus > .menu > .refs .ref, .cineraMenus > .menu > .refs .ref,
.cineraMenus > .menu > .view, .cineraMenus > .menu > .view,
.cineraMenus > .menu > .views_container .view, .cineraMenus > .menu > .views_container .view,
@ -299,7 +469,14 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu { .cineraMenus > .menu {
position: relative; position: relative;
align-self: center; min-height: 1em;
min-width: 1em;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
} }
.cineraMenus > .menu.filter.responsible, .cineraMenus > .menu.filter.responsible,
@ -312,67 +489,7 @@ ul.cineraNavPlain li.current > a {
animation-iteration-count: 1; animation-iteration-count: 1;
} }
.cineraMenus .help .help_container .help_key { .cineraHelp {
font-family: Inconsolata;
font-size: 16px;
border: 1px solid;
display: inline-block;
background-color: #111; /* Per project */
border-radius: 4px;
height: 16px;
width: 16px;
padding: 4px;
line-height: 16px;
margin: 2px;
}
.cineraMenus .help .help_container .help_key.word {
width: auto;
}
.cineraMenus .help .help_container .help_key.modifer {
margin-right: 0;
}
.cineraMenus .help .help_container .help_key.unavailable,
.cineraMenus .help .help_container .help_text.unavailable,
.cineraMenus .help .help_container h2 .unavailable {
opacity: 0.32;
}
.cineraMenus .help .help_container .key_block {
display: inline-flex;
align-items: flex-end;
flex-direction: row;
margin: 8px;
}
.cineraMenus .help .help_container .help_text {
margin: 0 8px 0 2px;
}
.cineraMenus .help .help_container h1 {
display: inline;
margin-left: 4px;
}
.cineraMenus .help .help_container h1:after {
content: "\a";
}
.cineraMenus .help .help_container h2 {
font-size: 16px;
margin-bottom: 8px;
}
.cineraMenus .help .help_container .help_paragraph {
display: inline-flex;
align-items: center;
}
.cineraMenus > .help {
cursor: pointer; cursor: pointer;
border: 1px solid; border: 1px solid;
border-radius: 4px; border-radius: 4px;
@ -387,32 +504,87 @@ ul.cineraNavPlain li.current > a {
z-index: 64; z-index: 64;
} }
.cineraMenus .help .help_container .help_grid { .cineraHelp .help_container {
display: inline-flex; background-color: #000000; /* Per project */
flex-direction: column; color: #EEEEEE; /* Per project */
}
.cineraMenus .help .help_container {
background-color: black; /* Per project */
color: #EEE; /* Per project */
display: none; display: none;
font-weight: normal; font-weight: normal;
line-height: 12px; line-height: 12px;
opacity: 0.9; opacity: 0.9;
padding: 8px; padding: 8px;
position: fixed; position: fixed;
right: 612px; top: 50%;
top: 42px; left: 50%;
transform: translate(-50%, -50%);
} }
.cineraMenus .help .help_container.visible { .cineraHelp .help_container.visible {
display: block; display: block;
} }
#cineraIndexControl .cineraIndexFilter .filter_container .cineraHelp .help_container .help_key {
{ font-family: Inconsolata;
font-size: 16px;
border: 1px solid; border: 1px solid;
border-top: none; display: inline-block;
background-color: #111111; /* Per project */
border-radius: 4px;
height: 16px;
width: 16px;
padding: 4px;
line-height: 16px;
margin: 2px;
}
.cineraHelp .help_container .help_key.word {
width: auto;
}
.cineraHelp .help_container .help_key.modifer {
margin-right: 0;
}
.cineraHelp .help_container .help_key.unavailable,
.cineraHelp .help_container .help_text.unavailable,
.cineraHelp .help_container h2 .unavailable {
opacity: 0.32;
}
.cineraHelp .help_container .key_block {
display: inline-flex;
align-items: flex-end;
flex-direction: row;
margin: 8px;
}
.cineraHelp .help_container .help_text {
margin: 0 8px 0 2px;
}
.cineraHelp .help_container h1 {
display: inline;
margin-left: 4px;
}
.cineraHelp .help_container h1:after {
content: "\a";
}
.cineraHelp .help_container h2 {
font-size: 16px;
margin-bottom: 8px;
}
.cineraHelp .help_container .help_paragraph {
display: inline-flex;
align-items: center;
}
.cineraHelp .help_container .help_grid {
display: inline-flex;
flex-direction: column;
} }
.cineraMenus > .menu .quotes_container, .cineraMenus > .menu .quotes_container,
@ -430,11 +602,13 @@ ul.cineraNavPlain li.current > a {
position: absolute; position: absolute;
right: 0; right: 0;
top: 100%; top: 100%;
z-index: 1; z-index: 8;
} }
.cineraMenus > .menu .refs, .cineraMenus > .menu .refs,
.cineraMenus > .menu .link_container { .cineraMenus > .menu .filter_container,
.cineraMenus > .menu .link_container,
.cineraMenus > .menu .credits_container {
width: 350px; width: 350px;
} }
@ -442,12 +616,9 @@ ul.cineraNavPlain li.current > a {
overflow-x: hidden; overflow-x: hidden;
} }
.cineraMenus > .menu .filter_container { .cineraMenus > .menu .sizing {
min-width: 350px; z-index: -1;
} display: block;
.cineraMenus > .menu .credits_container {
min-width: 240px;
} }
.cineraMenus > .menu .visible { .cineraMenus > .menu .visible {
@ -639,7 +810,7 @@ ul.cineraNavPlain li.current > a {
} }
.cineraPlayerContainer .markers_container { .cineraPlayerContainer .markers_container {
flex-shrink: 0; flex-shrink: 1;
overflow-y: scroll; overflow-y: scroll;
position: relative; position: relative;
} }
@ -651,6 +822,16 @@ ul.cineraNavPlain li.current > a {
font-weight: bold; font-weight: bold;
} }
.cineraPlayerContainer {
background-color: #000000;
}
.cineraPlayerContainer .video_container {
display: flex;
justify-content: center;
align-self: center;
}
.cineraPlayerContainer .markers_container > a.episodeMarker { .cineraPlayerContainer .markers_container > a.episodeMarker {
cursor: pointer; cursor: pointer;
} }
@ -683,7 +864,7 @@ ul.cineraNavPlain li.current > a {
border-bottom: 1px solid; border-bottom: 1px solid;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
max-height: 320px; max-height: 320px; /* NOTE(matt): Required for the transition */
transition: max-height .32s; transition: max-height .32s;
} }
@ -709,7 +890,7 @@ ul.cineraNavPlain li.current > a {
font-size: 14px; font-size: 14px;
} }
.cineraPlayerContainer .markers_container > .markers .marker.skip { .cinera:not(.mobile) .cineraPlayerContainer .markers_container > .markers .marker.skip {
max-height: 0; max-height: 0;
transition: max-height .32s; transition: max-height .32s;
overflow: hidden; overflow: hidden;
@ -781,97 +962,109 @@ ul.cineraNavPlain li.current > a {
margin-right: 8px; margin-right: 8px;
} }
@media (max-width: 720px), (max-height: 512px) /* NOTE(matt): Mobile Style */
{
.cineraMenus .episode_name,
.cineraMenus > .menu > .view,
.cineraMenus > .menu > .views_container .view,
.cineraMenus > .help,
.markers_container > .episodeMarker div:nth-child(2),
.markers_container > .episodeMarker div:nth-child(3),
.markers_container > .markers .marker:not(.current) {
display: none;
}
.cineraMenus { #cineraIndex.mobile #cineraResults .dayContainer {
justify-content: center; flex-direction: column;
}
.cineraMenus .menu .quotes_container,
.cineraMenus .menu .references_container,
.cineraMenus .menu .filter_container,
.cineraMenus .menu .link_container,
.cineraMenus .menu .credits_container {
position: fixed;
left: 0;
margin: auto;
max-height: 80%;
max-width: 97%;
}
.cineraPlayerContainer {
display: flex;
flex-direction: column;
}
.cineraPlayerContainer .markers_container {
display: flex;
flex-flow: row;
overflow-y: auto;
}
.cineraPlayerContainer .markers_container > .episodeMarker {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
width: 456px;
}
@media (max-width: 580px)
{
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
width: 320px;
}
}
@media (max-width: 450px)
{
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
width: 256px;
}
}
@media (max-width: 375px)
{
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
width: 180px;
}
}
.cineraPlayerContainer .markers_container > .episodeMarker.first,
.cineraPlayerContainer .markers_container > .episodeMarker.prev,
.cineraPlayerContainer .markers_container > .markers .marker,
.cineraPlayerContainer .markers_container > .episodeMarker.last,
.cineraPlayerContainer .markers_container > .episodeMarker.next {
border: 0;
}
.cineraPlayerContainer .markers_container > .episodeMarker.first,
.cineraPlayerContainer .markers_container > .episodeMarker.prev {
border-right: 3px double;
}
.cineraPlayerContainer .markers_container > .episodeMarker.last,
.cineraPlayerContainer .markers_container > .episodeMarker.next {
border-left: 3px double;
}
} }
#cineraIndex.mobile #cineraResults .dayContainer .dayName {
font-weight: bold;
text-align: center;
max-width: 100%;
}
#cineraIndex.mobile #cineraResults .dayContainer .markerList {
max-width: 100%;
}
.cinera.mobile
{
display: flex;
flex-direction: column;
flex-grow: 0;
-webkit-text-size-adjust: none;
}
.cinera.mobile .cineraMenus {
justify-content: center;
display: flex;
flex-direction: row;
position: relative;
border: 0;
}
.cinera.mobile .cineraMenus,
.cinera.mobile .player_container {
flex-grow: 0;
}
.cinera.mobile .cineraMenus .episode_name,
.cinera.mobile .cineraMenus > .menu > .view,
.cinera.mobile .cineraMenus > .menu > .views_container .view,
.cinera.mobile .cineraHelp,
#cineraIndex.mobile .cineraHelp,
.cinera.mobile .markers_container > .episodeMarker div:nth-child(2),
.cinera.mobile .markers_container > .episodeMarker div:nth-child(3),
.cinera.mobile .markers_container > .markers .marker:not(.current) {
display: none;
}
.cinera.mobile .cineraMenus .menu {
position: static;
}
.cinera.mobile .cineraMenus .menu .quotes_container,
.cinera.mobile .cineraMenus .menu .references_container,
.cinera.mobile .cineraMenus .menu .filter_container,
.cinera.mobile .cineraMenus .menu .link_container,
.cinera.mobile .cineraMenus .menu .credits_container {
box-sizing: border-box;
position: absolute;
}
.cinera.mobile .cineraPlayerContainer {
display: flex;
flex-direction: column;
}
.cinera.mobile .cineraPlayerContainer .markers_container {
display: flex;
flex-flow: row;
overflow-y: hidden;
}
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker {
width: 32px;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.cinera.mobile .cineraPlayerContainer .markers_container > .markers {
flex-grow: 1;
}
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.first,
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.prev,
.cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker,
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.last,
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.next {
border: 0;
}
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.first,
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.prev {
border-right: 3px double;
}
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.last,
.cinera.mobile .cineraPlayerContainer .markers_container > .episodeMarker.next {
border-left: 3px double;
}
/* Mobile Style End */
/* CUSTOM PAGE STYLE */ /* CUSTOM PAGE STYLE */
/* /*
@ -889,4 +1082,4 @@ Open menu: &#9662; ▾
Open link in new tab: &#10555; or &10559; or &8599; or &#11127; Open link in new tab: &#10555; or &10559; or &8599; or &#11127;
Play from timecode: &#9205; (or, if &#9656;) Play from timecode: &#9205; (or, if &#9656;)
Playable from timecode: &#9657; Playable from timecode: &#9657;
*/ */

View File

@ -516,7 +516,7 @@ typedef struct project
asset *IconAsset; asset *IconAsset;
string StreamUsername; string StreamUsername;
string VODPlatform; string VODPlatform; // TODO(matt): Make this an enum
bool DenyBespokeTemplates; bool DenyBespokeTemplates;
bool SingleBrowserTab; bool SingleBrowserTab;
@ -2615,7 +2615,7 @@ string
DeriveLineageWithoutOriginOfProject(config *C, scope_tree *Project) DeriveLineageWithoutOriginOfProject(config *C, scope_tree *Project)
{ {
string Result = {}; string Result = {};
memory_book StringList = {}; memory_book StringList = InitBookOfPointers(MBT_STRING_PTR, 4);
if(Project->Parent && Project->Parent->ID.Key == IDENT_PROJECT) if(Project->Parent && Project->Parent->ID.Key == IDENT_PROJECT)
{ {
string **Writer = MakeSpaceInBook(&StringList); string **Writer = MakeSpaceInBook(&StringList);

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,8 @@ function Player(htmlContainer, refsCallback) {
}.bind(this)); }.bind(this));
Player.initializeYoutube(this.onYoutubeReady.bind(this)); Player.initializeYoutube(this.onYoutubeReady.bind(this));
this.updateSize(); var PendingMobileStyleInitialisation = true;
this.updateSize(PendingMobileStyleInitialisation);
this.resume(); this.resume();
} }
@ -99,6 +100,11 @@ Player.prototype.setTime = function(time) {
Player.prototype.jumpToNextMarker = function() { Player.prototype.jumpToNextMarker = function() {
var targetMarkerIdx = Math.min((this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx + 1), this.markers.length-1); var targetMarkerIdx = Math.min((this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx + 1), this.markers.length-1);
var targetTime = this.markers[targetMarkerIdx].timestamp; var targetTime = this.markers[targetMarkerIdx].timestamp;
while(targetMarkerIdx < this.markers.length && this.markers[targetMarkerIdx].el.classList.contains("skip"))
{
++targetMarkerIdx;
targetTime = this.markers[targetMarkerIdx].timestamp;
}
this.setTime(targetTime); this.setTime(targetTime);
this.play(); this.play();
}; };
@ -106,35 +112,310 @@ Player.prototype.jumpToNextMarker = function() {
Player.prototype.jumpToPrevMarker = function() { Player.prototype.jumpToPrevMarker = function() {
var targetMarkerIdx = Math.max(0, (this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx - 1)); var targetMarkerIdx = Math.max(0, (this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx - 1));
var targetTime = this.markers[targetMarkerIdx].timestamp; var targetTime = this.markers[targetMarkerIdx].timestamp;
while(targetMarkerIdx >= 0 && this.markers[targetMarkerIdx].el.classList.contains("skip"))
{
--targetMarkerIdx;
targetTime = this.markers[targetMarkerIdx].timestamp;
}
this.setTime(targetTime); this.setTime(targetTime);
this.play(); this.play();
}; };
function switchToMobileView(player) function
GetHeightOfHideableElement(Element, UnhidingClass)
{
var Result = 0;
if(Element.classList.contains(UnhidingClass))
{
Result = Element.offsetHeight;
}
else
{
var ZOffset = Element.style.zOffset;
Element.style.zOffset = -1;
Element.classList.add(UnhidingClass);
Result = Element.offsetHeight;
Element.classList.remove(UnhidingClass);
Element.style.zOffset = ZOffset;
}
return Result;
}
function
GetWidthOfHideableElement(Element, UnhidingClass)
{
var Result = 0;
if(Element.classList.contains(UnhidingClass))
{
Result = Element.offsetWidth;
}
else
{
var ZOffset = Element.style.zOffset;
Element.style.zOffset = -1;
Element.classList.add(UnhidingClass);
Result = Element.offsetWidth;
Element.classList.remove(UnhidingClass);
Element.style.zOffset = ZOffset;
}
return Result;
}
function
ComputeVerticalOffsetForMenu(Menu, Toggler, VideoContainerDimY)
{
console.log("ComputeVerticalOffsetForMenu()");
var Result = 0;
var MenuHeight = GetHeightOfHideableElement(Menu, "visible");
var TogglerHeight = Toggler.offsetHeight;
var TogglerOffset = Toggler.offsetTop;
var Result = TogglerOffset + (TogglerHeight / 2) - (MenuHeight / 2);
console.log("Lower Point (0): " + (Result + MenuHeight));
console.log("VideoContainerDimY (0): " + VideoContainerDimY);
var LowerProtrusion = MenuHeight + Result - VideoContainerDimY;
if(LowerProtrusion > 0)
{
Result -= LowerProtrusion;
}
console.log("Lower Point (1): " + (Result + MenuHeight));
console.log("VideoContainerDimY (1): " + VideoContainerDimY);
if(Result < 0)
{
Result = 0;
}
return Result;
}
function sizeAndPositionMenuContainer(TitleBar, SizerElement, Menu)
{
if(Menu)
{
var Toggler = Menu.parentElement;
var TitleBarDimX = TitleBar.offsetWidth;
var TitleBarDimY = TitleBar.offsetHeight;
var SizerElementDimX = SizerElement.offsetWidth;
var SizerElementDimY = SizerElement.offsetHeight;
var ContainerDimX = SizerElementDimX;
var ContainerDimY = SizerElementDimY;
switch(cineraProps.O)
{
case orientations.PORTRAIT:
{
Menu.style.borderTopWidth = 0;
Menu.style.borderTopStyle = "none";
Menu.style.borderRightWidth = "1px";
Menu.style.borderRightStyle = "solid";
Menu.style.borderLeftWidth = "1px";
Menu.style.borderLeftStyle = "solid";
Menu.style.maxWidth = ContainerDimX + "px";
ContainerDimY -= TitleBarDimY;
Menu.style.maxHeight = ContainerDimY + "px";
Menu.style.top = TitleBarDimY + "px";
Menu.style.left = 0 + "px";
} break;
case orientations.LANDSCAPE_LEFT:
{
Menu.style.borderTopWidth = "1px";
Menu.style.borderTopStyle = "solid";
Menu.style.borderRightWidth = "1px";
Menu.style.borderRightStyle = "solid";
Menu.style.borderLeftWidth = 0;
Menu.style.borderLeftStyle = "none";
ContainerDimX -= TitleBarDimX;
Menu.style.maxWidth = ContainerDimX + "px";
Menu.style.maxHeight = ContainerDimY + "px";
var MenuVerticalOffset = ComputeVerticalOffsetForMenu(Menu, Toggler, SizerElementDimY);
Menu.style.top = MenuVerticalOffset + "px";
Menu.style.left = TitleBarDimX + "px";
} break;
case orientations.LANDSCAPE_RIGHT:
{
Menu.style.borderTopWidth = "1px";
Menu.style.borderTopStyle = "solid";
Menu.style.borderRightWidth = 0;
Menu.style.borderRightStyle = "none";
Menu.style.borderLeftWidth = "1px";
Menu.style.borderLeftStyle = "solid";
ContainerDimX -= TitleBarDimX;
Menu.style.maxWidth = ContainerDimX + "px";
Menu.style.maxHeight = ContainerDimY + "px";
var MenuVerticalOffset = ComputeVerticalOffsetForMenu(Menu, Toggler, SizerElementDimY);
Menu.style.top = MenuVerticalOffset + "px";
var MenuWidth = GetWidthOfHideableElement(Menu, "visible");
Menu.style.left = -MenuWidth + "px";
} break;
}
}
}
function
ComputeTallest(Elements, UnhidingClass)
{
var Result = null;
for(var i = 0; i < Elements.length; ++i)
{
var This = Elements[i];
var Height = UnhidingClass ? GetHeightOfHideableElement(This, UnhidingClass) : This.offsetHeight;
if(Height > Result)
{
Result = Height;
}
}
return Result;
}
function
ComputeAndSetTallest(Selector, Elements, UnhidingClass)
{
Selector.style.height = "unset";
Selector.style.height = ComputeTallest(Elements, UnhidingClass) + "px";
}
function ApplyMobileStyle(player)
{
var MaxWidth = MaxWidthOfElement(cinera);
var MaxHeight = MaxHeightOfElement(cinera);
var IndicesBar = playerContainer.querySelector(".markers_container");
var Markers = IndicesBar.querySelector(".markers");
var CineraContentWidth = MaxWidth;
var EpisodeMarkers = IndicesBar.querySelectorAll(".episodeMarker");
for(var i = 0; i < EpisodeMarkers.length; ++i)
{
CineraContentWidth -= EpisodeMarkers[i].offsetWidth;
}
switch(cineraProps.O)
{
case orientations.PORTRAIT:
{
cinera.style.flexDirection = "column";
titleBar.style.flexDirection = "row";
} break;
case orientations.LANDSCAPE_LEFT:
{
cinera.style.flexDirection = "row";
titleBar.style.flexDirection = "column-reverse";
CineraContentWidth -= titleBar.offsetWidth;
} break;
case orientations.LANDSCAPE_RIGHT:
{
cinera.style.flexDirection = "row-reverse";
titleBar.style.flexDirection = "column";
CineraContentWidth -= titleBar.offsetWidth;
} break;
}
if(MobileCineraContentRule !== undefined)
{
MobileCineraContentRule.style.width = CineraContentWidth + "px";
var MarkerList = Markers.querySelectorAll(".marker");
ComputeAndSetTallest(MobileCineraContentRule, MarkerList, "current");
}
var VideoMaxDimX = null;
var VideoMaxDimY = null;
switch(cineraProps.O)
{
case orientations.PORTRAIT:
{
VideoMaxDimX = MaxWidth;
VideoMaxDimY = MaxHeight - IndicesBar.offsetHeight - titleBar.offsetHeight;
} break;
case orientations.LANDSCAPE_LEFT:
case orientations.LANDSCAPE_RIGHT:
{
VideoMaxDimX = MaxWidth - titleBar.offsetWidth;
VideoMaxDimY = MaxHeight - IndicesBar.offsetHeight;
} break;
}
var VideoDimYFromMaxX = VideoMaxDimX * 9 / 16;
var VideoDimXFromMaxY = VideoMaxDimY * 16 / 9;
var VideoDimX = 0;
var VideoDimY = 0;
if(VideoDimXFromMaxY > VideoMaxDimX)
{
VideoDimX = Math.floor(VideoMaxDimX);
VideoDimY = Math.floor(VideoDimYFromMaxX);
}
else if(VideoDimYFromMaxX > VideoMaxDimY)
{
VideoDimY = Math.floor(VideoMaxDimY);
VideoDimX = Math.floor(VideoDimXFromMaxY);
}
else
{
VideoDimX = Math.floor(VideoMaxDimX);
VideoDimY = Math.floor(VideoDimYFromMaxX);
}
var VideoContainer = cinera.querySelector(".video_container");
VideoContainer.style.width = VideoDimX + "px";
VideoContainer.style.height = VideoDimY + "px";
sizeAndPositionMenuContainer(titleBar, cinera, quotesMenu);
sizeAndPositionMenuContainer(titleBar, cinera, referencesMenu);
sizeAndPositionMenuContainer(titleBar, cinera, filterMenu);
sizeAndPositionMenuContainer(titleBar, cinera, linkMenu);
sizeAndPositionMenuContainer(titleBar, cinera, creditsMenu);
}
function
IconifyMenuTogglers()
{ {
var menuContainerOffset = getElementYOffsetFromPage(titleBar) + parseInt(window.getComputedStyle(titleBar).height);
if(quotesMenu) if(quotesMenu)
{ {
quotesMenu.previousElementSibling.textContent = '\u{1F5E9}'; quotesMenu.previousElementSibling.textContent = '\u{1F5E9}';
quotesMenu.style.top = menuContainerOffset + "px";
} }
if(referencesMenu) if(referencesMenu)
{ {
referencesMenu.previousElementSibling.textContent = '\u{1F4D6}'; referencesMenu.previousElementSibling.textContent = '\u{1F4D6}';
referencesMenu.style.top = menuContainerOffset + "px";
} }
if(filterMenu) { filterMenu.style.top = menuContainerOffset + "px"; } if(creditsMenu)
if(linkMenu) { linkMenu.style.top = menuContainerOffset + "px"; } {
if(creditsMenu) {
creditsMenu.previousElementSibling.textContent = '\u{1F46A}'; creditsMenu.previousElementSibling.textContent = '\u{1F46A}';
creditsMenu.style.top = menuContainerOffset + "px";
} }
if(viewsMenu)
{
viewsMenu.remove();
viewsMenu = null;
}
}
function
InitMobileControls()
{
var rightmost = {}; var rightmost = {};
var markersContainer = player.markersContainer; var markersContainer = cinera.querySelector(".markers_container");
markersContainer.style.height = "auto"; markersContainer.style.height = "auto";
var episodeMarkerFirst = markersContainer.querySelector(".episodeMarker.first");
var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev"); var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev");
var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next"); var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next");
var episodeMarkerLast = markersContainer.querySelector(".episodeMarker.last"); var episodeMarkerLast = markersContainer.querySelector(".episodeMarker.last");
@ -143,94 +424,70 @@ function switchToMobileView(player)
if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = '\u{23ED}'; rightmost = episodeMarkerNext; } if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = '\u{23ED}'; rightmost = episodeMarkerNext; }
else if (episodeMarkerLast) { rightmost = episodeMarkerLast; } else if (episodeMarkerLast) { rightmost = episodeMarkerLast; }
var controlPrevTimestamp = document.createElement("a");
controlPrevTimestamp.classList.add("episodeMarker");
controlPrevTimestamp.classList.add("prevTimestamp");
var controlPrevTimestampContent = document.createElement("div");
controlPrevTimestampContent.appendChild(document.createTextNode('\u{25C0}'));
controlPrevTimestamp.appendChild(controlPrevTimestampContent);
var markers = markersContainer.querySelector(".markers"); var markers = markersContainer.querySelector(".markers");
markersContainer.insertBefore(controlPrevTimestamp, markers);
var controlPrevAnnotation = document.createElement("a"); var controlNextTimestamp = document.createElement("a");
controlPrevAnnotation.classList.add("episodeMarker"); controlNextTimestamp.classList.add("episodeMarker");
controlPrevAnnotation.classList.add("prevAnnotation"); controlNextTimestamp.classList.add("nextTimestamp");
controlPrevAnnotation.addEventListener("click", function(ev) { var controlNextTimestampContent = document.createElement("div");
player.jumpToPrevMarker(); controlNextTimestampContent.appendChild(document.createTextNode('\u{25B6}'));
}); controlNextTimestamp.appendChild(controlNextTimestampContent);
var controlPrevAnnotationContent = document.createElement("div");
controlPrevAnnotationContent.appendChild(document.createTextNode('\u{23F4}'));
controlPrevAnnotation.appendChild(controlPrevAnnotationContent);
markersContainer.insertBefore(controlPrevAnnotation, markers);
var controlNextAnnotation = document.createElement("a");
controlNextAnnotation.classList.add("episodeMarker");
controlNextAnnotation.classList.add("nextAnnotation");
controlNextAnnotation.addEventListener("click", function(ev) {
player.jumpToNextMarker();
});
var controlNextAnnotationContent = document.createElement("div");
controlNextAnnotationContent.appendChild(document.createTextNode('\u{23F5}'));
controlNextAnnotation.appendChild(controlNextAnnotationContent);
if(rightmost) if(rightmost)
{ {
markersContainer.insertBefore(controlNextAnnotation, rightmost); markersContainer.insertBefore(controlNextTimestamp, rightmost);
} }
else else
{ {
markersContainer.appendChild(controlNextAnnotation); markersContainer.appendChild(controlNextTimestamp);
} }
cineraProps.D = devices.MOBILE;
} }
function switchToDesktopView(player) function InitMobileStyle()
{ {
if(quotesMenu) cinera.classList.add("mobile");
{ IconifyMenuTogglers();
quotesMenu.previousElementSibling.textContent = originalTextContent.TitleQuotes; InitMobileControls();
quotesMenu.style.top = "100%"; ApplyMobileStyle();
} }
if(referencesMenu)
{
referencesMenu.previousElementSibling.textContent = originalTextContent.TitleReferences;
referencesMenu.style.top = "100%";
}
if(filterMenu) { filterMenu.style.top = "100%"; }
if(linkMenu) { linkMenu.style.top = "100%"; }
if(creditsMenu)
{
creditsMenu.previousElementSibling.textContent = originalTextContent.TitleCredits;
creditsMenu.style.top = "100%";
}
function
ConnectMobileControls(player)
{
var markersContainer = player.markersContainer; var markersContainer = player.markersContainer;
var ControlPrevTimestamp = markersContainer.querySelector(".episodeMarker.prevTimestamp");
var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev"); ControlPrevTimestamp.addEventListener("click", function(ev) {
if(episodeMarkerPrev) { episodeMarkerPrev.firstChild.textContent = originalTextContent.EpisodePrev; } player.jumpToPrevMarker();
var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next"); });
if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = originalTextContent.EpisodeNext; } var ControlNextTimestamp = markersContainer.querySelector(".episodeMarker.nextTimestamp");
ControlNextTimestamp.addEventListener("click", function(ev) {
var prevAnnotation = markersContainer.querySelector(".episodeMarker.prevAnnotation"); player.jumpToNextMarker();
markersContainer.removeChild(prevAnnotation); });
var nextAnnotation = markersContainer.querySelector(".episodeMarker.nextAnnotation");
markersContainer.removeChild(nextAnnotation);
cineraProps.D = devices.DESKTOP;
} }
// Call this after changing the size of the video container in order to update the youtube player. // Call this after changing the size of the video container in order to update the youtube player.
Player.prototype.updateSize = function() { Player.prototype.updateSize = function() {
var width = this.videoContainer.offsetWidth; var width = 0;
var height = width / 16 * 9; var height = 0;
if(window.innerHeight > 512 && window.innerWidth > 720) if(cineraProps.D === devices.DESKTOP)
{ {
if(cineraProps.D == devices.MOBILE) width = this.videoContainer.offsetWidth;
{ height = width / 16 * 9; // TODO(matt): Get the aspect ratio from the video itself?
switchToDesktopView(this); this.markersContainer.style.height = height + "px";
}
this.markersContainer.style.height = height + "px"; // NOTE(matt): This was the original line here
} }
else else
{ {
if(cineraProps.D == devices.DESKTOP) ApplyMobileStyle();
{ width = this.videoContainer.offsetWidth;
switchToMobileView(this); height = this.videoContainer.offsetHeight;
}
} }
if (this.youtubePlayerReady) { if (this.youtubePlayerReady) {
@ -288,25 +545,10 @@ Player.prototype.onMarkerClick = function(marker, ev) {
this.play(); this.play();
}; };
function getElementXOffsetFromPage(el) {
var left = 0;
do {
left += el.offsetLeft;
} while (el = el.offsetParent);
return left;
}
function getElementYOffsetFromPage(el) {
var top = 0;
do {
top += el.offsetTop;
} while (el = el.offsetParent);
return top;
}
Player.prototype.onMarkerMouseMove = function(marker, ev) { Player.prototype.onMarkerMouseMove = function(marker, ev) {
if (this.currentMarker == marker) { if (this.currentMarker == marker) {
marker.hoverx = (ev.pageX - getElementXOffsetFromPage(marker.el)) / marker.el.offsetWidth; var CineraContent = this.currentMarker.el.querySelector(".cineraContent");
marker.hoverx = (ev.pageX - getElementXOffsetFromPage(CineraContent)) / CineraContent.offsetWidth;
} }
}; };
@ -329,7 +571,8 @@ Player.prototype.updateProgress = function() {
} }
if (this.currentMarker) { if (this.currentMarker) {
var totalWidth = this.currentMarker.el.offsetWidth; var CineraContent = this.currentMarker.el.querySelector(".cineraContent");
var totalWidth = CineraContent.offsetWidth;
var progress = (this.currentTime - this.currentMarker.timestamp) / (this.currentMarker.endTime - this.currentMarker.timestamp); var progress = (this.currentTime - this.currentMarker.timestamp) / (this.currentMarker.endTime - this.currentMarker.timestamp);
if (this.currentMarker.hoverx === null) { if (this.currentMarker.hoverx === null) {
var pixelWidth = progress * totalWidth; var pixelWidth = progress * totalWidth;
@ -364,9 +607,6 @@ Player.prototype.updateProgress = function() {
this.currentMarker.el.classList.add("current"); this.currentMarker.el.classList.add("current");
this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0; this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0;
this.scrollPosition = this.markersContainer.scrollTop; this.scrollPosition = this.markersContainer.scrollTop;
}
if (this.currentMarker) {
this.refsCallback(this.currentMarker.ref, this.currentMarker.el, this); this.refsCallback(this.currentMarker.ref, this.currentMarker.el, this);
} else if (prevMarker && prevMarker.ref) { } else if (prevMarker && prevMarker.ref) {
this.refsCallback(null); this.refsCallback(null);
@ -445,6 +685,7 @@ Player.prototype.onYoutubeReady = function() {
videoId: this.videoContainer.getAttribute("data-videoId"), videoId: this.videoContainer.getAttribute("data-videoId"),
width: this.videoContainer.offsetWidth, width: this.videoContainer.offsetWidth,
height: this.videoContainer.offsetWidth / 16 * 9, height: this.videoContainer.offsetWidth / 16 * 9,
playerVars: { 'playsinline': 1 },
//playerVars: { disablekb: 1 }, //playerVars: { disablekb: 1 },
events: { events: {
"onReady": this.onYoutubePlayerReady.bind(this), "onReady": this.onYoutubePlayerReady.bind(this),
@ -636,13 +877,13 @@ function enterFullScreen_()
{ {
if(!document.mozFullScreen && !document.webkitFullScreen) if(!document.mozFullScreen && !document.webkitFullScreen)
{ {
if(cinera.mozRequestFullScreen) if(document.mozRequestFullScreen)
{ {
cinera.mozRequestFullScreen(); document.mozRequestFullScreen();
} }
else else
{ {
cinera.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); document.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} }
} }
} }
@ -673,6 +914,11 @@ function toggleTheatreMode() {
cineraProps.H = cinera.style.height; cineraProps.H = cinera.style.height;
cineraProps.mH = cinera.style.maxHeight; cineraProps.mH = cinera.style.maxHeight;
cineraProps.P = cinera.style.position; cineraProps.P = cinera.style.position;
cineraProps.Display = cinera.style.display;
cineraProps.FlexDirection = cinera.style.flexDirection;
cineraProps.JustifyContent = cinera.style.justifyContent;
cineraProps.ScrollX = window.scrollX;
cineraProps.ScrollY = window.scrollY;
cinera.style.backgroundColor = "#000"; cinera.style.backgroundColor = "#000";
cinera.style.zIndex = 64; cinera.style.zIndex = 64;
@ -683,6 +929,9 @@ function toggleTheatreMode() {
cinera.style.height = "100%"; cinera.style.height = "100%";
cinera.style.maxHeight = "100%"; cinera.style.maxHeight = "100%";
cinera.style.position = "fixed"; cinera.style.position = "fixed";
cinera.style.display = "flex";
cinera.style.flexDirection = "column";
cinera.style.justifyContent = "center";
viewItems[0].setAttribute("data-id", "regular"); viewItems[0].setAttribute("data-id", "regular");
viewItems[0].setAttribute("title", "Regular mode"); viewItems[0].setAttribute("title", "Regular mode");
@ -703,6 +952,15 @@ function toggleTheatreMode() {
cinera.style.height = cineraProps.H; cinera.style.height = cineraProps.H;
cinera.style.maxHeight = cineraProps.mH; cinera.style.maxHeight = cineraProps.mH;
cinera.style.position = cineraProps.P; cinera.style.position = cineraProps.P;
cinera.style.display = cineraProps.Display;
cinera.style.flexDirection = cineraProps.FlexDirection;
cinera.style.justifyContent = cineraProps.JustifyContent;
window.scroll(
{
top: cineraProps.ScrollY,
left: cineraProps.ScrollX
}
);
viewItems[0].setAttribute("data-id", "theatre"); viewItems[0].setAttribute("data-id", "theatre");
viewItems[0].setAttribute("title", "Theatre mode"); viewItems[0].setAttribute("title", "Theatre mode");
@ -859,10 +1117,12 @@ function handleKey(key) {
if(focusedElement.previousElementSibling) if(focusedElement.previousElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.previousElementSibling; lastFocusedQuote = focusedElement.previousElementSibling;
focusedElement = lastFocusedQuote; focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("references_container")) else if(focusedElement.parentNode.classList.contains("references_container"))
@ -870,15 +1130,19 @@ function handleKey(key) {
if(focusedElement.previousElementSibling) if(focusedElement.previousElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.previousElementSibling; lastFocusedReference = focusedElement.previousElementSibling;
focusedElement = lastFocusedReference; focusedElement = lastFocusedReference;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild; lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
} }
else if(focusedElement.parentNode.parentNode.classList.contains("filters")) else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
@ -887,10 +1151,12 @@ function handleKey(key) {
focusedElement.previousElementSibling.classList.contains("filter_content")) focusedElement.previousElementSibling.classList.contains("filter_content"))
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.previousElementSibling; lastFocusedCategory = focusedElement.previousElementSibling;
focusedElement = lastFocusedCategory; focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("credit")) else if(focusedElement.parentNode.classList.contains("credit"))
@ -898,6 +1164,7 @@ function handleKey(key) {
if(focusedElement.parentNode.previousElementSibling) if(focusedElement.parentNode.previousElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.previousElementSibling.querySelector(".support") && if(focusedElement.parentNode.previousElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support")) focusedElement.classList.contains("support"))
{ {
@ -905,13 +1172,14 @@ function handleKey(key) {
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support"); lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
setSpriteLightness(focusedElement.firstChild); focusSprite(focusedElement);
} }
else else
{ {
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person"); lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
} }
@ -926,10 +1194,12 @@ function handleKey(key) {
if(focusedElement.nextElementSibling) if(focusedElement.nextElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.nextElementSibling; lastFocusedQuote = focusedElement.nextElementSibling;
focusedElement = lastFocusedQuote; focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("references_container")) else if(focusedElement.parentNode.classList.contains("references_container"))
@ -937,15 +1207,19 @@ function handleKey(key) {
if(focusedElement.nextElementSibling) if(focusedElement.nextElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.nextElementSibling; lastFocusedReference = focusedElement.nextElementSibling;
focusedElement = lastFocusedReference; focusedElement = lastFocusedReference;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild; lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
} }
else if(focusedElement.parentNode.parentNode.classList.contains("filters")) else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
@ -954,10 +1228,12 @@ function handleKey(key) {
focusedElement.nextElementSibling.classList.contains("filter_content")) focusedElement.nextElementSibling.classList.contains("filter_content"))
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.nextElementSibling; lastFocusedCategory = focusedElement.nextElementSibling;
focusedElement = lastFocusedCategory; focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("credit")) else if(focusedElement.parentNode.classList.contains("credit"))
@ -965,6 +1241,7 @@ function handleKey(key) {
if(focusedElement.parentNode.nextElementSibling) if(focusedElement.parentNode.nextElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.nextElementSibling.querySelector(".support") && if(focusedElement.parentNode.nextElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support")) focusedElement.classList.contains("support"))
{ {
@ -972,13 +1249,14 @@ function handleKey(key) {
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support"); lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
setSpriteLightness(focusedElement.firstChild); focusSprite(focusedElement);
} }
else else
{ {
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person"); lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
} }
@ -993,16 +1271,20 @@ function handleKey(key) {
if(focusedIdentifier.previousElementSibling) if(focusedIdentifier.previousElementSibling)
{ {
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.previousElementSibling; lastFocusedIdentifier = focusedIdentifier.previousElementSibling;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
else if(focusedIdentifier.parentNode.previousElementSibling.classList.contains("ref_indices")) else if(focusedIdentifier.parentNode.previousElementSibling.classList.contains("ref_indices"))
{ {
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.previousElementSibling.lastElementChild; lastFocusedIdentifier = focusedIdentifier.parentNode.previousElementSibling.lastElementChild;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
} }
else if(focusedElement.classList.contains("filter_content")) else if(focusedElement.classList.contains("filter_content"))
@ -1011,6 +1293,7 @@ function handleKey(key) {
focusedElement.parentNode.previousElementSibling) focusedElement.parentNode.previousElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedMedium = focusedElement; lastFocusedMedium = focusedElement;
if(!lastFocusedTopic) if(!lastFocusedTopic)
@ -1020,6 +1303,7 @@ function handleKey(key) {
lastFocusedCategory = lastFocusedTopic; lastFocusedCategory = lastFocusedTopic;
focusedElement = lastFocusedCategory; focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("credit")) else if(focusedElement.parentNode.classList.contains("credit"))
@ -1027,14 +1311,14 @@ function handleKey(key) {
if(focusedElement.classList.contains("support")) if(focusedElement.classList.contains("support"))
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
console.log(focusedElement);
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.previousElementSibling; lastFocusedCreditItem = focusedElement.previousElementSibling;
if(focusedElement.firstChild.classList.contains("cineraSprite")) setSpriteLightness(focusedElement.firstChild);
{
setSpriteLightness(focusedElement.firstChild);
}
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
} }
@ -1048,17 +1332,21 @@ function handleKey(key) {
if(focusedIdentifier.nextElementSibling) if(focusedIdentifier.nextElementSibling)
{ {
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.nextElementSibling; lastFocusedIdentifier = focusedIdentifier.nextElementSibling;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
else if(focusedIdentifier.parentNode.nextElementSibling) else if(focusedIdentifier.parentNode.nextElementSibling)
{ {
focusedIdentifier.classList.remove("focused"); focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.nextElementSibling.firstElementChild; lastFocusedIdentifier = focusedIdentifier.parentNode.nextElementSibling.firstElementChild;
focusedIdentifier = lastFocusedIdentifier; focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused"); focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
} }
} }
else if(focusedElement.classList.contains("filter_content")) else if(focusedElement.classList.contains("filter_content"))
@ -1067,6 +1355,7 @@ function handleKey(key) {
focusedElement.parentNode.nextElementSibling) focusedElement.parentNode.nextElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedTopic = focusedElement; lastFocusedTopic = focusedElement;
if(!lastFocusedMedium) if(!lastFocusedMedium)
@ -1076,6 +1365,7 @@ function handleKey(key) {
lastFocusedCategory = lastFocusedMedium; lastFocusedCategory = lastFocusedMedium;
focusedElement = lastFocusedCategory; focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
else if(focusedElement.parentNode.classList.contains("credit")) else if(focusedElement.parentNode.classList.contains("credit"))
@ -1084,14 +1374,12 @@ function handleKey(key) {
focusedElement.nextElementSibling) focusedElement.nextElementSibling)
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.nextElementSibling; lastFocusedCreditItem = focusedElement.nextElementSibling;
focusedElement = lastFocusedCreditItem; focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
if(focusedElement.firstChild.classList.contains("cineraSprite")) focusSprite(focusedElement);
{
setSpriteLightness(focusedElement.firstChild);
}
} }
} }
} }
@ -1105,6 +1393,7 @@ function handleKey(key) {
focusedElement.nextElementSibling.classList.contains("filter_content")) focusedElement.nextElementSibling.classList.contains("filter_content"))
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics")) if(focusedElement.parentNode.classList.contains("filter_topics"))
{ {
lastFocusedTopic = focusedElement.nextElementSibling; lastFocusedTopic = focusedElement.nextElementSibling;
@ -1118,6 +1407,7 @@ function handleKey(key) {
lastFocusedElement = lastFocusedCategory; lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement; focusedElement = lastFocusedElement;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
} break; } break;
@ -1130,6 +1420,7 @@ function handleKey(key) {
focusedElement.previousElementSibling.classList.contains("filter_content")) focusedElement.previousElementSibling.classList.contains("filter_content"))
{ {
focusedElement.classList.remove("focused"); focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics")) if(focusedElement.parentNode.classList.contains("filter_topics"))
{ {
lastFocusedTopic = focusedElement.previousElementSibling; lastFocusedTopic = focusedElement.previousElementSibling;
@ -1143,6 +1434,7 @@ function handleKey(key) {
lastFocusedElement = lastFocusedCategory; lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement; focusedElement = lastFocusedElement;
focusedElement.classList.add("focused"); focusedElement.classList.add("focused");
focusSprite(focusedElement);
} }
} }
} break; } break;
@ -1163,7 +1455,10 @@ function handleKey(key) {
} break; } break;
case "?": { case "?": {
helpDocumentation.classList.toggle("visible"); if(helpDocumentation)
{
helpDocumentation.classList.toggle("visible");
}
} break; } break;
case 'N': case 'N':
@ -1319,7 +1614,7 @@ function filterItemToggle(filterItem) {
filterItem.querySelector(".icon").style.backgroundColor = getComputedStyle(filterItem.querySelector(".icon")).getPropertyValue("border-color"); filterItem.querySelector(".icon").style.backgroundColor = getComputedStyle(filterItem.querySelector(".icon")).getPropertyValue("border-color");
} }
setDotLightness(filterItem.querySelector(".icon")); setDotLightness(filterItem.querySelector(".icon"));
var testMarkers = document.querySelectorAll(".marker.off_" + selectedCategory); var testMarkers = cinera.querySelectorAll(".marker.off_" + selectedCategory);
for(var j = 0; j < testMarkers.length; ++j) for(var j = 0; j < testMarkers.length; ++j)
{ {
if(filterState[selectedCategory].type == "topic") if(filterState[selectedCategory].type == "topic")
@ -1461,35 +1756,36 @@ function onRefChanged(ref, element, player) {
{ {
player.jumpToNextMarker(); player.jumpToNextMarker();
} }
return;
} }
else
for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)
{ {
var SetMenu = 0; for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)
if (ref !== undefined && ref !== null) { {
var refElements = sourceMenus[MenuIndex].querySelectorAll(".refs .ref"); var SetMenu = 0;
var refs = ref.split(","); if (ref !== undefined && ref !== null) {
var refElements = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
var refs = ref.split(",");
for (var i = 0; i < refElements.length; ++i) { for (var i = 0; i < refElements.length; ++i) {
if (refs.includes(refElements[i].getAttribute("data-id"))) { if (refs.includes(refElements[i].getAttribute("data-id"))) {
refElements[i].classList.add("current"); refElements[i].classList.add("current");
SetMenu = 1; SetMenu = 1;
} else { } else {
refElements[i].classList.remove("current"); refElements[i].classList.remove("current");
}
} }
} if(SetMenu) {
if(SetMenu) { sourceMenus[MenuIndex].classList.add("current");
sourceMenus[MenuIndex].classList.add("current"); } else {
sourceMenus[MenuIndex].classList.remove("current");
}
} else { } else {
sourceMenus[MenuIndex].classList.remove("current"); sourceMenus[MenuIndex].classList.remove("current");
} var refs = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
for (var i = 0; i < refs.length; ++i) {
} else { refs[i].classList.remove("current");
sourceMenus[MenuIndex].classList.remove("current"); }
var refs = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
for (var i = 0; i < refs.length; ++i) {
refs[i].classList.remove("current");
} }
} }
} }
@ -1574,10 +1870,11 @@ function mouseSkipToTimecode(player, time, ev)
return false; return false;
} }
function handleMouseOverMenu(menu, eventType) function handleMenuTogglerInteraction(menu, eventType)
{ {
if(!(menu.classList.contains("visible")) && eventType == "mouseenter" || if(!(menu.classList.contains("visible")) && eventType == "mouseenter" ||
menu.classList.contains("visible") && eventType == "mouseleave") menu.classList.contains("visible") && eventType == "mouseleave" ||
(eventType == "click" && !menu.classList.contains("cineraHelp")))
{ {
if(menu.classList.contains("quotes")) if(menu.classList.contains("quotes"))
{ {
@ -1600,10 +1897,6 @@ function handleMouseOverMenu(menu, eventType)
toggleMenuVisibility(creditsMenu); toggleMenuVisibility(creditsMenu);
} }
} }
if(eventType == "click" && menu.classList.contains("help"))
{
helpDocumentation.classList.toggle("visible");
}
} }
function RGBtoHSL(colour) function RGBtoHSL(colour)
@ -1648,7 +1941,6 @@ function getBackgroundBrightness(element) {
var result = Math.sqrt(rgb[0] * rgb[0] * .241 + var result = Math.sqrt(rgb[0] * rgb[0] * .241 +
rgb[1] * rgb[1] * .691 + rgb[1] * rgb[1] * .691 +
rgb[2] * rgb[2] * .068); rgb[2] * rgb[2] * .068);
console.log(result);
return result; return result;
} }

View File

@ -1,3 +1,24 @@
var orientations = {
PORTRAIT: 0,
LANDSCAPE_LEFT: 90,
LANDSCAPE_RIGHT: -90,
};
var DebugConsoleMessageCount = 0;
function Say(Message)
{
var DebugConsole = document.getElementById("debug-console");
if(DebugConsole)
{
DebugConsole.textContent += DebugConsoleMessageCount++ + ": " + Message + "\n";
DebugConsole.scrollTo(
{
top: DebugConsole.scrollHeight,
behavior: "smooth"
});
}
}
function getBackgroundBrightness(element) { function getBackgroundBrightness(element) {
var colour = getComputedStyle(element).getPropertyValue("background-color"); var colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0; var depth = 0;
@ -110,3 +131,233 @@ function unfocusSprite(Element)
} }
} }
} }
function IsMobile() {
// NOTE(matt): From https://medium.com/simplejs/detect-the-users-device-type-with-a-simple-javascript-check-4fc656b735e1
var Identifier = navigator.userAgent||navigator.vendor||window.opera;
var Result = (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(Identifier)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(Identifier.substr(0,4)));
return Result;
};
function GetRule(SelectorText)
{
// NOTE(matt): Modifying CSS style
// from https://stackoverflow.com/a/566445
// https://usefulangle.com/post/39/adding-css-to-stylesheet-with-javascript
var Result = undefined;
var StyleSheets = document.styleSheets;
var cssRuleCode = document.all ? 'rules' : 'cssRules'; // account for IE and FF
for(var StyleSheetIndex = StyleSheets.length - 1; StyleSheetIndex >= 0; --StyleSheetIndex)
{
var ThisSheet = StyleSheets[StyleSheetIndex];
var Rules = ThisSheet[cssRuleCode];
for(var RuleIndex = Rules.length - 1; RuleIndex >= 0; --RuleIndex)
{
var ThisRule = Rules[RuleIndex];
if(SelectorText === ThisRule.selectorText)
{
Result = ThisRule;
break;
}
}
if(Result !== undefined) { break; }
}
return Result;
}
function
GetRulesOfStyleSheetIndex(Index)
{
var Result = undefined;
var StyleSheets = document.styleSheets;
var cssRuleCode = document.all ? 'rules' : 'cssRules'; // account for IE and FF
var StyleSheet = StyleSheets[Index];
if(StyleSheet)
{
Result = StyleSheet[cssRuleCode];
}
return Result;
}
function
GetOrSetRule(SelectorText)
{
var Result = GetRule(SelectorText);
if(Result === undefined)
{
var StyleSheet = document.styleSheets[0];
var RuleIndex = StyleSheet.insertRule(SelectorText + "{}", StyleSheet.length - 1);
var Rules = GetRulesOfStyleSheetIndex(0);
Result = Rules[RuleIndex];
}
return Result;
}
/* Auto-scrolling */
var LastScrollYPos = 0;
var ScrollTicking = false;
var ScrollerFunction;
var ScrollCondition;
function ScrollTo(Element, ScrollPos) {
var BoundingRect = Element.getBoundingClientRect();
var Height = BoundingRect.height;
var PercentageOfInView = 89;
var GatherableHeight = Height * (1 - PercentageOfInView / 100);
var ScrollY = BoundingRect.top;
var YOffsetFromPage = getElementYOffsetFromPage(Element);
var DesiredScroll = null;
if(ScrollY < 0)
{
ScrollY = ~ScrollY;
if(ScrollY <= GatherableHeight)
{
DesiredScroll = YOffsetFromPage;
}
}
else
{
var LowerProtrusion = ScrollY - (window.innerHeight - Height);
if(LowerProtrusion > 0 && LowerProtrusion <= GatherableHeight)
{
DesiredScroll = ScrollPos + LowerProtrusion;
}
}
if(DesiredScroll !== null)
{
window.scrollTo({
top: DesiredScroll,
behavior: "smooth"
});
}
}
function
InitScrollEventListener(Element)
{
window.addEventListener('scroll', function() {
if(ScrollCondition == undefined || ScrollCondition == true)
{
LastScrollYPos = window.scrollY;
if (!ScrollTicking) {
window.requestAnimationFrame(function() {
clearTimeout(ScrollerFunction);
ScrollerFunction = setTimeout(ScrollTo, 2000, Element, LastScrollYPos);
ScrollTicking = false;
});
ScrollTicking = true;
}
}
});
}
/* /Auto-scrolling */
function getElementXOffsetFromPage(el) {
var left = 0;
do {
left += el.offsetLeft;
} while (el = el.offsetParent);
return left;
}
function getElementYOffsetFromPage(el) {
var top = 0;
do {
top += el.offsetTop;
} while (el = el.offsetParent);
return top;
}
function
MaxWidthOfElement(Element)
{
var Result = 0;
var OriginalWidth = Element.style.width;
Element.style.width = "100%";
var Max = parseInt(window.getComputedStyle(Element).width);
Element.style.width = "unset";
var Default = parseInt(window.getComputedStyle(Element).width);
Element.style.width = OriginalWidth;
if(Max > window.innerWidth || Max == Default)
{
Result = window.innerWidth;
}
else
{
Result = Max;
}
return Result;
}
function
MaxHeightOfElement(Element)
{
var Result = 0;
var OriginalHeight = Element.style.height;
Element.style.height = "100%";
var Max = parseInt(window.getComputedStyle(Element).height);
Element.style.height = "unset";
var Default = parseInt(window.getComputedStyle(Element).height);
Element.style.height = OriginalHeight;
if(Max > window.innerHeight || Max == Default)
{
Result = window.innerHeight;
}
else
{
Result = Max;
}
return Result;
}
function
Clamp(EndA, N, EndB)
{
var Min = EndA < EndB ? EndA : EndB;
var Max = EndA > EndB ? EndA : EndB;
return N < Min ? Min : N > Max ? Max : N;
}
function
Clamp01(N)
{
return N < 0 ? 0 : N > 1 ? 1 : N;
}
function
Lerp(A, t, B)
{
return (1-t)*A + t*B;
}
function
IsOverflowed(Element)
{
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
}
function
BindHelp(Button, DocumentationContainer)
{
window.addEventListener("blur", function(){
Button.firstElementChild.innerText = "¿";
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
});
window.addEventListener("focus", function(){
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
});
Button.addEventListener("click", function() {
DocumentationContainer.classList.toggle("visible");
})
}

View File

@ -1,642 +0,0 @@
document.body.style.overflowY = "scroll";
if (location.hash && location.hash.length > 0) {
var initialQuery = location.hash;
if (initialQuery[0] == "#") {
initialQuery = initialQuery.slice(1);
}
document.getElementById("query").value = decodeURIComponent(initialQuery);
}
var indexControl = document.getElementById("cineraIndexControl");
var indexSort = indexControl.querySelector("#cineraIndexSort");
var indexSortChronological = true;
var filterMenu = indexControl.querySelector(".cineraIndexFilter");
if(filterMenu)
{
var filterContainer = filterMenu.querySelector(".filter_container");
//menuState.push(linkMenu);
filterMenu.addEventListener("mouseenter", function(ev) {
filterContainer.style.display = "block";
});
filterMenu.addEventListener("mouseleave", function(ev) {
filterContainer.style.display = "none";
});
}
function hideEntriesOfProject(ProjectElement)
{
if(!ProjectElement.classList.contains("off"))
{
ProjectElement.classList.add("off");
}
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
{
ThisProject.filteredOut = true;
if(ThisProject.entriesContainer != null)
{
ThisProject.entriesContainer.style.display = "none";
disableSprite(ThisProject.entriesContainer.parentElement);
}
}
}
}
function showEntriesOfProject(ProjectElement)
{
if(ProjectElement.classList.contains("off"))
{
ProjectElement.classList.remove("off");
}
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
{
ThisProject.filteredOut = false;
if(ThisProject.entriesContainer != null)
{
ThisProject.entriesContainer.style.display = "flex";
enableSprite(ThisProject.entriesContainer.parentElement);
}
}
}
}
function hideProjectSearchResults(baseURL, searchLocation, playerLocation)
{
var cineraResults = document.getElementById("cineraResults");
if(cineraResults)
{
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
for(var i = 0; i < cineraResultsProjects.length; ++i)
{
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
{
cineraResultsProjects[i].style.display = "none";
return;
}
}
}
}
function showProjectSearchResults(baseURL, searchLocation, playerLocation)
{
var cineraResults = document.getElementById("cineraResults");
if(cineraResults)
{
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
for(var i = 0; i < cineraResultsProjects.length; ++i)
{
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
{
cineraResultsProjects[i].style.display = "flex";
return;
}
}
}
}
function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
{
var baseURL = ProjectFilterElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectFilterElement.attributes.getNamedItem("data-searchLocation").value;
var playerLocation = ProjectFilterElement.attributes.getNamedItem("data-playerLocation").value;
var shouldShow = ProjectFilterElement.classList.contains("off");
if(shouldShow)
{
ProjectFilterElement.classList.remove("off");
enableSprite(ProjectFilterElement);
}
else
{
ProjectFilterElement.classList.add("off");
disableSprite(ProjectFilterElement);
}
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
{
if(shouldShow)
{
ThisProject.filteredOut = false;
enableSprite(ThisProject.projectTitleElement.parentElement);
if(ThisProject.entriesContainer != null)
{
ThisProject.entriesContainer.style.display = "flex";
}
showProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
}
else
{
ThisProject.filteredOut = true;
disableSprite(ThisProject.projectTitleElement.parentElement);
if(ThisProject.entriesContainer != null)
{
ThisProject.entriesContainer.style.display = "none";
}
hideProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
}
}
}
var indexChildFilterProjects = ProjectFilterElement.querySelectorAll(".cineraFilterProject");
for(var j = 0; j < indexChildFilterProjects.length; ++j)
{
var ThisElement = indexChildFilterProjects[j];
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ThisElement.attributes.getNamedItem("data-searchLocation").value;
var playerLocation = ThisElement.attributes.getNamedItem("data-playerLocation").value;
if(shouldShow)
{
showEntriesOfProject(ThisElement);
showProjectSearchResults(baseURL, searchLocation, playerLocation);
}
else
{
hideEntriesOfProject(ThisElement);
hideProjectSearchResults(baseURL, searchLocation, playerLocation);
}
}
}
var indexFilter = indexControl.querySelector(".cineraIndexFilter");
if(indexFilter)
{
var indexFilterProjects = indexFilter.querySelectorAll(".cineraFilterProject");
for(var i = 0; i < indexFilterProjects.length; ++i)
{
indexFilterProjects[i].addEventListener("mouseover", function(ev) {
ev.stopPropagation();
this.classList.add("focused");
focusSprite(this);
});
indexFilterProjects[i].addEventListener("mouseout", function(ev) {
ev.stopPropagation();
this.classList.remove("focused");
unfocusSprite(this);
});
indexFilterProjects[i].addEventListener("click", function(ev) {
ev.stopPropagation();
toggleEntriesOfProjectAndChildren(this);
});
}
}
var resultsSummary = document.getElementById("cineraResultsSummary");
var resultsContainer = document.getElementById("cineraResults");
var indexContainer = document.getElementById("cineraIndex");
var projectsContainer = indexContainer.querySelectorAll(".cineraIndexProject");
var projectContainerPrototype = document.createElement("DIV");
projectContainerPrototype.classList.add("projectContainer");
var dayContainerPrototype = document.createElement("DIV");
dayContainerPrototype.classList.add("dayContainer");
var dayNamePrototype = document.createElement("SPAN");
dayNamePrototype.classList.add("dayName");
dayContainerPrototype.appendChild(dayNamePrototype);
var markerListPrototype = document.createElement("DIV");
markerListPrototype.classList.add("markerList");
dayContainerPrototype.appendChild(markerListPrototype);
var markerPrototype = document.createElement("A");
markerPrototype.classList.add("marker");
if(resultsContainer.getAttribute("data-single") == 0)
{
markerPrototype.setAttribute("target", "_blank");
}
function prepareToParseIndexFile(project)
{
project.xhr.addEventListener("load", function() {
var contents = project.xhr.response;
var lines = contents.split("\n");
var mode = "none";
var episode = null;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
if (line.trim().length == 0) { continue; }
if (line == "---") {
if (episode != null && episode.name != null && episode.title != null) {
episode.filename = episode.name;
episode.day = getEpisodeName(episode.filename + ".html.md");
episode.dayContainerPrototype = project.dayContainerPrototype;
episode.markerPrototype = markerPrototype;
episode.playerURLPrefix = project.playerURLPrefix;
project.episodes.push(episode);
}
episode = {};
mode = "none";
} else if (line.startsWith("name:")) {
episode.name = line.slice(6);
} else if (line.startsWith("title:")) {
episode.title = line.slice(7).trim().slice(1, -1);
} else if (line.startsWith("markers")) {
mode = "markers";
episode.markers = [];
} else if (mode == "markers") {
var match = line.match(/"(\d+)": "(.+)"/);
if (match == null) {
console.log(name, line);
} else {
var totalTime = parseInt(line.slice(1));
var marker = {
totalTime: totalTime,
prettyTime: markerTime(totalTime),
text: match[2].replace(/\\"/g, "\"")
}
episode.markers.push(marker);
}
}
}
document.querySelector(".spinner").classList.remove("show");
project.parsed = true;
runSearch(true);
});
project.xhr.addEventListener("error", function() {
console.error("Failed to load content");
});
}
var projects = [];
function prepareProjects()
{
for(var i = 0; i < projectsContainer.length; ++i)
{
var ID = projectsContainer[i].attributes.getNamedItem("data-project").value;
var baseURL = projectsContainer[i].attributes.getNamedItem("data-baseURL").value;
var searchLocation = projectsContainer[i].attributes.getNamedItem("data-searchLocation").value;
var playerLocation = projectsContainer[i].attributes.getNamedItem("data-playerLocation").value;
var theme = projectsContainer[i].classList.item(1);
projects[i] =
{
baseURL: baseURL,
searchLocation: searchLocation,
playerLocation: playerLocation,
playerURLPrefix: (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : ""),
indexLocation: (baseURL ? baseURL + "/" : "") + (searchLocation ? searchLocation + "/" : "") + ID + ".index",
projectTitleElement: projectsContainer[i].querySelector(":scope > .cineraProjectTitle"),
entriesContainer: projectsContainer[i].querySelector(":scope > .cineraIndexEntries"),
dayContainerPrototype: dayContainerPrototype.cloneNode(true),
filteredOut: false,
parsed: false,
searched: false,
resultsToRender: [],
resultsIndex: 0,
theme: theme,
episodes: [],
xhr: new XMLHttpRequest(),
}
projects[i].dayContainerPrototype.classList.add(theme);
projects[i].dayContainerPrototype.children[1].classList.add(theme);
document.querySelector(".spinner").classList.add("show");
projects[i].xhr.open("GET", projects[i].indexLocation);
projects[i].xhr.setRequestHeader("Content-Type", "text/plain");
projects[i].xhr.send();
prepareToParseIndexFile(projects[i]);
}
}
prepareProjects();
indexSort.addEventListener("click", function(ev) {
if(indexSortChronological)
{
this.firstChild.nodeValue = "Sort: New to Old ⏷"
for(var i = 0; i < projects.length; ++i)
{
if(projects[i].entriesContainer)
{
projects[i].entriesContainer.style.flexFlow = "column-reverse";
}
}
}
else
{
this.firstChild.nodeValue = "Sort: Old to New ⏶"
for(var i = 0; i < projects.length; ++i)
{
if(projects[i].entriesContainer)
{
projects[i].entriesContainer.style.flexFlow = "column";
}
}
}
indexSortChronological = !indexSortChronological;
runSearch(true);
});
var lastQuery = null;
var markerList = null;
var projectContainer = null;
var resultsMarkerIndex = -1;
var rendering = false;
var highlightPrototype = document.createElement("B");
function getEpisodeName(filename) {
var day = filename;
var dayParts = day.match(/([a-zA-Z_-]+)([0-9]+)?([a-zA-Z]+)?/);
day = dayParts[1].slice(0, 1).toUpperCase() + dayParts[1].slice(1) + (dayParts[2] ? " " + dayParts[2] : "") + (dayParts[3] ? " " + dayParts[3].toUpperCase() : "");
return day;
}
function markerTime(totalTime) {
var markTime = "(";
var hours = Math.floor(totalTime / 60 / 60);
var minutes = Math.floor(totalTime / 60) % 60;
var seconds = totalTime % 60;
if (hours > 0) {
markTime += padTimeComponent(hours) + ":";
}
markTime += padTimeComponent(minutes) + ":" + padTimeComponent(seconds) + ")";
return markTime;
}
function padTimeComponent(component) {
return (component < 10 ? "0" + component : component);
}
function resetProjectsForSearch()
{
for(var i = 0; i < projects.length; ++i)
{
var project = projects[i];
project.searched = false;
project.resultsToRender = [];
}
}
var renderHandle;
function runSearch(refresh) {
var queryStr = document.getElementById("query").value;
if (refresh || lastQuery != queryStr) {
var oldResultsContainer = resultsContainer;
resultsContainer = oldResultsContainer.cloneNode(false);
oldResultsContainer.parentNode.insertBefore(resultsContainer, oldResultsContainer);
oldResultsContainer.remove();
for(var i = 0; i < projects.length; ++i)
{
projects[i].resultsIndex = 0;
}
resultsMarkerIndex = -1;
}
lastQuery = queryStr;
resetProjectsForSearch();
var numEpisodes = 0;
var numMarkers = 0;
var totalSeconds = 0;
// NOTE(matt): Function defined within runSearch() so that we can modify numEpisodes, numMarkers and totalSeconds
function runSearchInterior(resultsToRender, query, episode)
{
var matches = [];
for (var k = 0; k < episode.markers.length; ++k) {
query.lastIndex = 0;
var result = query.exec(episode.markers[k].text);
if (result && result[0].length > 0) {
numMarkers++;
matches.push(episode.markers[k]);
if (k < episode.markers.length-1) {
totalSeconds += episode.markers[k+1].totalTime - episode.markers[k].totalTime;
}
}
}
if (matches.length > 0) {
numEpisodes++;
resultsToRender.push({
query: query,
episode: episode,
matches: matches
});
}
}
if (queryStr && queryStr.length > 0) {
indexContainer.style.display = "none";
resultsSummary.style.display = "block";
var shouldRender = false;
var query = new RegExp(queryStr.replace("(", "\\(").replace(")", "\\)").replace(/\|+/, "\|").replace(/\|$/, "").replace(/(^|[^\\])\\$/, "$1"), "gi");
// Visible
for(var i = 0; i < projects.length; ++i)
{
var project = projects[i];
if(project.parsed && !project.filteredOut && project.episodes.length > 0) {
if(indexSortChronological)
{
for(var j = 0; j < project.episodes.length; ++j) {
var episode = project.episodes[j];
runSearchInterior(project.resultsToRender, query, episode);
}
}
else
{
for(var j = project.episodes.length; j > 0; --j) {
var episode = project.episodes[j - 1];
runSearchInterior(project.resultsToRender, query, episode);
}
}
shouldRender = true;
project.searched = true;
}
}
// Invisible
for(var i = 0; i < projects.length; ++i)
{
var project = projects[i];
if(project.parsed && project.filteredOut && !project.searched && project.episodes.length > 0) {
if(indexSortChronological)
{
for(var j = 0; j < project.episodes.length; ++j) {
var episode = project.episodes[j];
runSearchInterior(project.resultsToRender, query, episode);
}
}
else
{
for(var j = project.episodes.length; j > 0; --j) {
var episode = project.episodes[j - 1];
runSearchInterior(project.resultsToRender, query, episode);
}
}
shouldRender = true;
project.searched = true;
}
}
if(shouldRender)
{
if (rendering) {
clearTimeout(renderHandle);
}
renderResults();
}
}
else
{
indexContainer.style.display = "block";
resultsSummary.style.display = "none";
}
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
resultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
}
function renderResults() {
var maxItems = 42;
var numItems = 0;
for(var i = 0; i < projects.length; ++i)
{
var project = projects[i];
if (project.resultsIndex < project.resultsToRender.length) {
rendering = true;
while (numItems < maxItems && project.resultsIndex < project.resultsToRender.length) {
var query = project.resultsToRender[project.resultsIndex].query;
var episode = project.resultsToRender[project.resultsIndex].episode;
var matches = project.resultsToRender[project.resultsIndex].matches;
if (resultsMarkerIndex == -1) {
if(project.resultsIndex == 0 || project.resultsToRender[project.resultsIndex - 1].episode.playerURLPrefix != episode.playerURLPrefix)
{
projectContainer = projectContainerPrototype.cloneNode(true);
for(var i = 0; i < projects.length; ++i)
{
if(projects[i].playerURLPrefix === episode.playerURLPrefix)
{
projectContainer.setAttribute("data-baseURL", projects[i].baseURL);
projectContainer.setAttribute("data-searchLocation", projects[i].searchLocation);
projectContainer.setAttribute("data-playerLocation", projects[i].playerLocation);
if(projects[i].filteredOut)
{
projectContainer.style.display = "none";
}
}
}
resultsContainer.appendChild(projectContainer);
}
else
{
projectContainer = resultsContainer.lastElementChild;
}
var dayContainer = episode.dayContainerPrototype.cloneNode(true);
var dayName = dayContainer.children[0];
markerList = dayContainer.children[1];
dayName.textContent = episode.day + ": " + episode.title;
projectContainer.appendChild(dayContainer);
resultsMarkerIndex = 0;
numItems++;
}
while (numItems < maxItems && resultsMarkerIndex < matches.length) {
var match = matches[resultsMarkerIndex];
var marker = episode.markerPrototype.cloneNode(true);
marker.setAttribute("href", episode.playerURLPrefix + episode.filename.replace(/"/g, "") + "/#" + match.totalTime);
query.lastIndex = 0;
var cursor = 0;
var text = match.text;
var result = null;
marker.appendChild(document.createTextNode(match.prettyTime + " "));
while (result = query.exec(text)) {
if (result.index > cursor) {
marker.appendChild(document.createTextNode(text.slice(cursor, result.index)));
}
var highlightEl = highlightPrototype.cloneNode();
highlightEl.textContent = result[0];
marker.appendChild(highlightEl);
cursor = result.index + result[0].length;
}
if (cursor < text.length) {
marker.appendChild(document.createTextNode(text.slice(cursor, text.length)));
}
markerList.appendChild(marker);
numItems++;
resultsMarkerIndex++;
}
if (resultsMarkerIndex == matches.length) {
resultsMarkerIndex = -1;
project.resultsIndex++;
}
}
renderHandle = setTimeout(renderResults, 0);
} else {
rendering = false;
}
}
}
function IsVisible(el) {
var xPos = 0;
var yPos = 0;
var Height = parseInt(getComputedStyle(el).height);
while (el) {
if (el.tagName == "BODY") {
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
var yScroll = el.scrollTop || document.documentElement.scrollTop;
xPos += (el.offsetLeft - xScroll + el.clientLeft)
yPos += (el.offsetTop - yScroll + el.clientTop)
} else {
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
}
el = el.offsetParent;
}
return ((xPos > 0 && xPos < window.innerWidth) && (yPos > 0 && yPos + Height < window.innerHeight));
}
var queryEl = document.getElementById("query");
if(document.hasFocus() && IsVisible(queryEl)) { queryEl.focus(); }
queryEl.addEventListener("input", function(ev) {
history.replaceState(null, null, "#" + encodeURIComponent(queryEl.value));
runSearch();
});
runSearch();
// Testing

View File

@ -0,0 +1,62 @@
document.body.style.overflowY = "scroll";
// Element Selection
//
Nav.Nexus = document.getElementById("cineraIndex");
Nav.Controls.Header = document.getElementById("cineraIndexControl");
Nav.Controls.Sort = Nav.Controls.Header.querySelector(".cineraMenuItem.sort");
Nav.Controls.View = Nav.Controls.Header.querySelector(".cineraMenuItem.view");
Nav.Controls.Anim = Nav.Controls.Header.querySelector(".cineraMenuItem.anim");
Nav.Controls.Save = Nav.Controls.Header.querySelector(".cineraMenuItem.save");
Nav.Controls.Help = Nav.Nexus.querySelector(".cineraHelp");
Nav.Controls.HelpDocumentation = Nav.Controls.Help.querySelector(".help_container");
Nav.GridContainer = Nav.Nexus.querySelector(".cineraIndexGridContainer");
Nav.Controls.GridTraversal.Header = Nav.GridContainer.querySelector(".cineraTraversal");
Nav.Controls.GridTraversal.Ascend = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.ascension");
Nav.Controls.GridTraversal.Prev = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.prev");
Nav.Controls.GridTraversal.Next = Nav.Controls.GridTraversal.Header.querySelector(".cineraButton.next");
Search.QueryElement = document.getElementById("query");
Search.ResultsSummary = document.getElementById("cineraResultsSummary");
Search.ResultsContainer = document.getElementById("cineraResults");
Search.IndexContainer = document.getElementById("cineraIndexList");
Search.ProjectsContainer = Search.IndexContainer.querySelectorAll(".cineraIndexProject");
//
///
// NOTE(matt): Initialisation
//
if(CineraProps.IsMobile)
{
Nav.Nexus.classList.add("mobile");
}
InitTraversalStack();
InitNexus();
InitHelpKeys(Nav.Controls.HelpDocumentation);
Nav.GridSize = ComputeOptimalGridSize();
SetHelpKeyAvailability(Nav.GridSize);
InitButtons(); // NOTE(matt): Also does "keydown" listeners, needed before UpdateButtons()
UpdateButtons();
InitQuery(Search.QueryElement);
InitPrototypes(Search.ResultsContainer);
prepareProjects();
SyncNavState();
//
////
// NOTE(matt): Listeners
//
BindControls();
InitResizeEventListener();
InitOrientationChangeListener();
InitScrollEventListener(Nav.Nexus);
//
////
// NOTE(matt): On-load Execution
//
runSearch();
//
////

3654
cinera/cinera_search_pre.js Normal file

File diff suppressed because it is too large Load Diff