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 = {
.Major = 0,
.Minor = 7,
.Patch = 18
.Minor = 8,
.Patch = 0
};
#include <stdarg.h> // NOTE(matt): varargs
@ -119,6 +119,9 @@ clock_t TIMING_START;
#define MIN(A, B) A < B ? A : B
typedef int32_t hash32;
typedef hash32 asset_hash;
void
Clear(void *V, uint64_t Size)
{
@ -2442,7 +2445,7 @@ typedef struct
char BaseURL[MAX_BASE_URL_LENGTH];
char SearchLocation[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;
typedef db_entry3 db_entry4;
@ -2553,7 +2556,7 @@ typedef struct
{
char Filename[MAX_ASSET_FILENAME_LENGTH];
enum8(asset_type) Type;
int32_t Hash;
hash32 Hash;
uint32_t LandmarkCount;
uint64_t Associated:1;
uint64_t Variants:63;
@ -2639,7 +2642,7 @@ typedef struct asset
{
asset_type Type;
char Filename[MAX_ASSET_FILENAME_LENGTH];
int32_t Hash;
asset_hash Hash;
vec2 Dimensions;
sprite Sprite;
uint64_t Variants:63;
@ -2659,7 +2662,8 @@ asset BuiltinAssets[] =
{ ASSET_IMG, "cinera_icon_filter.png" },
{ ASSET_JS, "cinera_pre.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_post.js" },
};
@ -2671,7 +2675,8 @@ typedef enum
ASSET_IMG_FILTER,
ASSET_JS_CINERA_PRE,
ASSET_JS_CINERA_POST,
ASSET_JS_SEARCH,
ASSET_JS_SEARCH_PRE,
ASSET_JS_SEARCH_POST,
ASSET_JS_PLAYER_PRE,
ASSET_JS_PLAYER_POST,
BUILTIN_ASSETS_COUNT,
@ -3457,6 +3462,22 @@ CopyStringToBufferNoFormat_(int LineNumber, buffer *Dest, string String)
*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)
void
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...
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);
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;
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");
@ -4996,7 +5019,7 @@ ReadPlayerPageIntoBuffer(file *File, string BaseDir, string PlayerLocation, stri
}
rc
SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum)
SnipeChecksumIntoHTML(db_asset *Asset, asset_hash Checksum)
{
db_landmark *FirstLandmark = LocateFirstLandmark(Asset);
@ -5564,32 +5587,38 @@ UpdateAssetInDB(asset *Asset)
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;
char *Ptr = (char *)Asset;
Ptr += sizeof(*Asset);
buffer Checksum = {};
ClaimBuffer(&Checksum, BID_CHECKSUM, 16);
CopyStringToBuffer(&Checksum, "%08x", StoredAsset->Hash);
file AssetFile = {};
AssetFile.Path = ConstructAssetPath(&AssetFile, Wrap0i(StoredAsset->Filename, sizeof(StoredAsset->Filename)), StoredAsset->Type);
ResolvePath(&AssetFile.Path);
string ChecksumL = Wrap0i(Checksum.Location, Checksum.Ptr - Checksum.Location);
string Message = MakeString("sssslsss",
ColourStrings[CS_ONGOING], "Updating", ColourStrings[CS_END], " checksum ", &ChecksumL, " of ", AssetFile.Path, " in HTML files");
fprintf(stderr, "%.*s", (int)Message.Length, Message.Base);
uint64_t MessageLength = Message.Length;
FreeString(&Message);
string MessageEditType = MakeString("sss", ColourStrings[CS_ONGOING], "Updating", ColourStrings[CS_END]);
string Message = MakeString("sssssssssss",
" checksum ",
ColourStrings[CS_BLACK_BOLD], OldChecksum, ColourStrings[CS_END],
"",
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);
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");
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);
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);
@ -5985,8 +6014,6 @@ AbbreviationsClash(memory_book *Speakers)
void
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)
{
speaker *A = GetPlaceInBook(&Speakers->Speakers, i);
@ -6006,8 +6033,9 @@ SortAndAbbreviateSpeakers(speakers *Speakers)
for(int i = 0; i < Speakers->Speakers.ItemCount; ++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);
This->Abbreviation = InitialString(&Speakers->Abbreviations, This->Person->Name);
This->Abbreviation = InitialString(&Speakers->Abbreviations, Name);
}
int Attempt = 0;
@ -6016,11 +6044,12 @@ SortAndAbbreviateSpeakers(speakers *Speakers)
for(int i = 0; i < Speakers->Speakers.ItemCount; ++i)
{
speaker *This = GetPlaceInBook(&Speakers->Speakers, i);
string Name = This->Person->Name.Length > 0 ? This->Person->Name : This->Person->ID;
switch(Attempt)
{
case 0: This->Abbreviation = GetFirstSubstring(This->Person->Name); break;
case 1: This->Abbreviation = InitialAndGetFinalString(&Speakers->Abbreviations, This->Person->Name); break;
case 2: This->Abbreviation = This->Person->Name; break;
case 0: This->Abbreviation = GetFirstSubstring(Name); break;
case 1: This->Abbreviation = InitialAndGetFinalString(&Speakers->Abbreviations, Name); break;
case 2: This->Abbreviation = Name; break;
}
}
++Attempt;
@ -6405,6 +6434,8 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
CopyStringToBuffer(CreditsMenu,
" <span class=\"credit\">\n");
string Name = Actor->Name.Length > 0 ? Actor->Name : Actor->ID;
if(Actor->Homepage.Length)
{
CopyStringToBuffer(CreditsMenu,
@ -6414,7 +6445,7 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
" </a>\n",
(int)Actor->Homepage.Length, Actor->Homepage.Base,
RoleStrings[Role],
(int)Actor->Name.Length, Actor->Name.Base);
(int)Name.Length, Name.Base);
}
else
{
@ -6424,7 +6455,7 @@ PushCredentials(buffer *CreditsMenu, memory_book *Speakers, person *Actor, role
" <div class=\"name\">%.*s</div>\n"
" </div>\n",
RoleStrings[Role],
(int)Actor->Name.Length, Actor->Name.Base);
(int)Name.Length, Name.Base);
}
// TODO(matt): Handle multiple support platforms!
@ -6462,9 +6493,9 @@ ErrorCredentials(string HMMLFilepath, string Actor, role Role)
}
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)
{
PushCredentials(CreditsMenu, &Speakers->Speakers, Host, R_HOST, RequiresCineraJS);
@ -8709,61 +8740,73 @@ ExamineDB(void)
DeclaimMenuBuffers(&MenuBuffers)
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): Stack-string
char Message[128];
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);
int MessageLength = StringLength(Message);
buffer VideoAPIResponse;
ClaimBuffer(&VideoAPIResponse, BID_VIDEO_API_RESPONSE, Kilobytes(1));
if(StringsMatch(Wrap0("youtube"), VODPlatform))
{
char Message[128];
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);
int MessageLength = StringLength(Message);
buffer VideoAPIResponse;
ClaimBuffer(&VideoAPIResponse, BID_VIDEO_API_RESPONSE, Kilobytes(1));
CURL *curl = curl_easy_init();
if(curl) {
LastPrivacyCheck = time(0);
CURL *curl = curl_easy_init();
if(curl) {
LastPrivacyCheck = time(0);
#define APIKey "AIzaSyAdV2U8ivPk8PHMaPMId0gynksw_gdzr9k"
// NOTE(matt): Stack-string
char URL[1024] = {0};
CopyString(URL, sizeof(URL), "https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s", APIKey, VideoID);
CURLcode CurlReturnCode;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &VideoAPIResponse.Ptr);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlIntoBuffer);
curl_easy_setopt(curl, CURLOPT_URL, URL);
if((CurlReturnCode = curl_easy_perform(curl)))
{
fprintf(stderr, "%s\n", curl_easy_strerror(CurlReturnCode));
}
curl_easy_cleanup(curl);
// NOTE(matt): Stack-string
char URL[1024] = {0};
CopyString(URL, sizeof(URL), "https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s", APIKey, VideoID);
CURLcode CurlReturnCode;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &VideoAPIResponse.Ptr);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlIntoBuffer);
curl_easy_setopt(curl, CURLOPT_URL, URL);
// 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));
}
curl_easy_cleanup(curl);
VideoAPIResponse.Ptr = VideoAPIResponse.Location;
// TODO(matt): Parse this JSON
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER);
if(*VideoAPIResponse.Ptr == '0')
{
DeclaimBuffer(&VideoAPIResponse);
// printf("Private video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow(MessageLength);
return TRUE;
}
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
// NOTE(matt): Stack-string
char Status[16];
CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"');
if(!StringsDiffer0(Status, "public"))
{
DeclaimBuffer(&VideoAPIResponse);
ClearTerminalRow(MessageLength);
return FALSE;
VideoAPIResponse.Ptr = VideoAPIResponse.Location;
// TODO(matt): Parse this JSON
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER);
if(*VideoAPIResponse.Ptr == '0')
{
DeclaimBuffer(&VideoAPIResponse);
// printf("Private video: https://youtube.com/watch?v=%s\n", VideoID);
ClearTerminalRow(MessageLength);
return TRUE;
}
VideoAPIResponse.Ptr = VideoAPIResponse.Location;
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
// NOTE(matt): Stack-string
char Status[16];
CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"');
if(!StringsDiffer0(Status, "public"))
{
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 *
@ -9210,7 +9253,7 @@ FreeReferences(_memory_book(ref_info) *References)
rc
ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, memory_book *Strings,
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,
bool *HasQuoteMenu, bool *HasReferenceMenu, bool *HasFilterMenu, bool *RequiresCineraJS,
int *QuoteIdentifier, int *RefIdentifier,
@ -9247,7 +9290,7 @@ ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, me
CopyStringToBuffer(&IndexBuffers->Class,
" 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(Speakers->Speakers.ItemCount > 1 && Speaker && !IsCategorisedAuthored(Anno))
@ -9617,12 +9660,46 @@ ProcessTimecode(buffers *CollationBuffers, neighbourhood *N, string Filepath, me
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
HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseFilename, neighbourhood *N)
{
MEM_TEST_TOP("HMMLToBuffers");
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?
// NOTE(matt): Stack-string
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
// reregister such an entry as newly private? This test lets us avoid calling VideoIsPrivate() for all
// 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)
{
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);
}
if(!HMML.metadata.member)
person *Host = CurrentProject->Owner;
if(HMML.metadata.member)
{
IndexingError(&FilepathL, 0, S_ERROR, "The [video] node lacks a \"member\"", 0);
Result = RC_ERROR_HMML;
Host = GetPersonFromConfig(Wrap0(HMML.metadata.member));
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;
//ErrorCredentials(FilepathL, Wrap0(HMML.metadata.member), R_HOST);
//Result = RC_ERROR_HMML;
}
if(!HMML.metadata.id)
@ -9928,14 +9995,14 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
" <div class=\"markers\">\n");
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)
{
CopyStringToBuffer(&CollationBuffers->SearchEntry, "name: \"");
CopyStringToBuffer(&CollationBuffers->SearchEntry, "%.*s", (int)OutputLocation.Length, OutputLocation.Base);
CopyStringToBuffer(&CollationBuffers->SearchEntry, "location: \"%.*s\"\n", (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"
"title: \"");
CopyStringToBuffer(&CollationBuffers->SearchEntry, "title: \"");
CopyStringToBufferNoFormat(&CollationBuffers->SearchEntry, Wrap0(HMML.metadata.title));
CopyStringToBuffer(&CollationBuffers->SearchEntry, "\"\n"
"markers:\n");
@ -9964,12 +10031,12 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
if(Anno->quote.author) { Author = Wrap0(Anno->quote.author); }
else if(HMML.metadata.stream_username) { Author = Wrap0(HMML.metadata.stream_username); }
else if(CurrentProject->StreamUsername.Length > 0) { Author = CurrentProject->StreamUsername; }
else { Author = CurrentProject->Owner->ID; }
else { Author = Host->ID; }
/* */ MEM_TEST_MID("HMMLToBuffers");
/* +MEM */ Result = ProcessTimecode(CollationBuffers, N, Wrap0(Filepath), &Strings,
/* */ &MenuBuffers, &IndexBuffers, &PlayerBuffers,
/* */ DefaultMedium, &Speakers, Author, &ReferencesArray,
/* */ DefaultMedium, &Speakers, Host, Author, &ReferencesArray,
/* */ &HasQuoteMenu, &HasReferenceMenu, &HasFilterMenu, &RequiresCineraJS,
/* */ &QuoteIdentifier, &RefIdentifier,
/* */ &Topics, &Media,
@ -10167,7 +10234,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
}
CopyStringToBuffer(&PlayerBuffers.Menus,
" <div class=\"help\">\n"
" <div class=\"cineraHelp\">\n"
" <span>?</span>\n"
" <div class=\"help_container\">\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,
"<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.Minor,
CINERA_APP_VERSION.Patch);
@ -10362,7 +10430,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
for(int i = 0; i < Topics.ItemCount; ++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);
}
@ -10477,7 +10545,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&PlayerBuffers.Script,
"\"></script>");
CopyStringToBuffer(&CollationBuffers->Player, "<div>\n"
CopyStringToBuffer(&CollationBuffers->Player, "<div class=\"cinera\">\n"
" ");
CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER);
CopyStringToBuffer(&CollationBuffers->Player, "\n"
@ -10555,12 +10623,16 @@ typedef struct
html_element HTMLElements[] =
{
{ "a", FALSE },
{ "br", TRUE },
{ "div", FALSE },
{ "h1", FALSE },
{ "h2", FALSE },
{ "img", TRUE },
{ "input", TRUE },
{ "label", FALSE },
{ "li", FALSE },
{ "nav", FALSE },
{ "p", FALSE },
{ "script", FALSE },
{ "span", FALSE },
{ "ul", FALSE },
@ -10569,12 +10641,16 @@ html_element HTMLElements[] =
typedef enum
{
NODE_A,
NODE_BR,
NODE_DIV,
NODE_H1,
NODE_H2,
NODE_IMG,
NODE_INPUT,
NODE_LABEL,
NODE_LI,
NODE_NAV,
NODE_P,
NODE_SCRIPT,
NODE_SPAN,
NODE_UL,
@ -11619,10 +11695,9 @@ InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template *BespokeTempl
CycleFile(&DB.File);
}
string EntryTitle = Wrap0(CollationBuffers->Title);
if(!VideoIsPrivate || !RecheckingPrivacy)
{
string EntryTitle = Wrap0(CollationBuffers->Title);
LogEdit(EditType, CurrentProject->Lineage, BaseFilename, &EntryTitle, VideoIsPrivate);
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)
{
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, Wrap0("\" data-searchLocation=\""));
AppendStringToBuffer(Filter, P->SearchLocation);
@ -12872,6 +12947,8 @@ GenerateIndexOfProjectAndChildren(buffer *Index, db_header_project *StoredP, pro
AppendStringToBuffer(Index, P->SearchLocation);
AppendStringToBuffer(Index, Wrap0("\" data-playerLocation=\""));
AppendStringToBuffer(Index, P->PlayerLocation);
AppendStringToBuffer(Index, Wrap0("\" data-unit=\""));
AppendStringToBuffer(Index, P->Unit);
AppendStringToBuffer(Index, Wrap0("\">"));
OpenNodeNewLine(Index, &Index->IndentLevel, NODE_DIV, 0);
@ -12905,27 +12982,28 @@ GenerateIndexOfProjectAndChildren(buffer *Index, db_header_project *StoredP, pro
if(*StoredP->Unit)
{
// TODO(matt): That rigorous notion of numbering, goddammit?!
string NumberL = TrimString(Wrap0i(Entry->HMMLBaseFilename, sizeof(Entry->HMMLBaseFilename)),
P->ID.Length, 0);
char Number[NumberL.Length + 1];
ClearCopyStringNoFormat(Number, sizeof(Number), NumberL);
if(CurrentProject->NumberingScheme == NS_LINEAR)
string NumberL = GetNumberFromHMMLBaseFilename(P->ID, Wrap0i(Entry->HMMLBaseFilename, sizeof(Entry->HMMLBaseFilename)));
if(NumberL.Length > 0)
{
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, Wrap0(" "));
AppendStringToBuffer(Index, Wrap0i(Number, sizeof(Number)));
AppendStringToBuffer(Index, Wrap0(": "));
AppendStringToBuffer(Index, P->Unit);
AppendStringToBuffer(Index, Wrap0(" "));
AppendStringToBuffer(Index, Wrap0i(Number, sizeof(Number)));
AppendStringToBuffer(Index, Wrap0(": "));
}
}
// HERE
@ -13097,6 +13175,45 @@ GenerateThemeLinks(buffer *IncludesSearch, project *P)
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
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,
"<meta charset=\"UTF-8\">\n"
" <meta name=\"generator\" content=\"Cinera %d.%d.%d\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
"\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"%.*s",
CINERA_APP_VERSION.Major,
@ -13159,38 +13277,54 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
CopyStringToBuffer(&CollationBuffers->IncludesSearch,
"\">");
asset *JSSearch = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_SEARCH].Filename), ASSET_JS);
ConstructResolvedAssetURL(&URL, JSSearch, PAGE_SEARCH);
uint32_t IndentationLevel = 1;
++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;
//IndentBuffer(&CollationBuffers->Search, ++IndentationLevel);
buffer *B = &CollationBuffers->Search;
OpenNode(B, &IndentationLevel, NODE_DIV, "cineraIndexControl");
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndex");
AppendStringToBuffer(B, Wrap0(" class=\""));
AppendStringToBuffer(B, Theme);
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;"));
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;
if(StoredP)
{
@ -13221,7 +13355,7 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
AppendStringToBuffer(B, Wrap0(" class=\"cineraIndexFilter\">"));
AppendStringToBuffer(B, Wrap0(" class=\"cineraMenu cineraMenuTitle IndexFilter\">"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_SPAN, 0);
@ -13237,7 +13371,7 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
CloseNode(B, &IndentationLevel, NODE_SPAN);
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:
@ -13271,8 +13405,8 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
{
AppendLandmarkedBuffer(B, &Filter, PAGE_SEARCH);
FreeBuffer(&Filter);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenuContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraMenu IndexFilter
}
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>");
}
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);
AppendStringToBuffer(B, Wrap0(" class=\"cineraQueryContainer\">"));
@ -13320,10 +13474,82 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
IndentBuffer(B, IndentationLevel);
AppendStringToBuffer(B, Wrap0("Downloading data..."));
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // spinner
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // inputContainer
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraQueryContainer
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");
@ -13340,17 +13566,50 @@ SearchToBuffer(buffers *CollationBuffers, db_header_project *StoredP, project *P
AppendStringToBuffer(B, Wrap0("\n"));
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndex");
OpenNodeCNewLine(B, &IndentationLevel, NODE_DIV, "cineraIndexList");
AppendLandmarkedBuffer(B, &Index, PAGE_SEARCH);
FreeBuffer(&Index);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV);
CloseNodeNewLine(B, &IndentationLevel, NODE_DIV); // cineraIndexList
AppendLandmarkedBuffer(B, &Script, PAGE_SEARCH);
FreeBuffer(&Script);
OpenNodeNewLine(B, &IndentationLevel, NODE_DIV, 0);
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; }
else { return RC_SUCCESS; }

View File

@ -58,7 +58,7 @@ nav.cineraNavDropdown .cineraNavTitle {
nav.cineraNavDropdown ul.cineraNavHorizontal {
display: none;
width: 100%;
z-index: 8;
z-index: 4096;
position: absolute;
}
@ -85,12 +85,124 @@ ul.cineraNavPlain li.current > a {
/* Index */
#cineraIndexControl {
#cineraIndex
{
display: flex;
margin: 16px auto;
max-width: 1024px;
flex-direction: column;
}
#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;
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,
@ -99,15 +211,30 @@ ul.cineraNavPlain li.current > a {
display: flex;
}
.cineraIndexFilter {
.cineraMenu.cineraMenuTitle {
padding: 10px;
cursor: default;
}
.cineraIndexFilter .filter_container
.cineraMenuContainer
{
display: none;
position: absolute;
top: 100%;
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,
@ -120,7 +247,7 @@ ul.cineraNavPlain li.current > a {
}
.cineraIndexProject .cineraProjectTitle,
.cineraFilterProject
.cineraMenuItem
{
font-weight: bold;
font-size: 12px;
@ -130,7 +257,6 @@ ul.cineraNavPlain li.current > a {
flex-grow: 1;
padding-left: 16px;
display: flex;
flex-direction: horizontal;
margin: auto;
}
@ -141,19 +267,21 @@ ul.cineraNavPlain li.current > a {
}
.cineraQueryContainer .inputContainer {
display: flex;
flex-grow: 1;
position: relative;
padding: .5em;
}
.cineraQueryContainer #query {
width: 100%;
flex-grow: 1;
}
.cineraQueryContainer .inputContainer .spinner {
position: absolute;
top: 2px;
right: 5px;
color: black;
color: #000000;
height: 100%;
display: none;
}
@ -163,7 +291,7 @@ ul.cineraNavPlain li.current > a {
}
#cineraResults,
#cineraIndex {
#cineraIndexList {
margin: 0 auto;
max-width: 800px;
}
@ -173,22 +301,94 @@ ul.cineraNavPlain li.current > a {
flex-flow: column;
}
#cineraIndexControl #cineraIndexSort {
padding: 4px;
#cineraIndexList .cineraIndexEntries {
display: flex;
flex-flow: column;
width: 100%;
}
#cineraIndexControl #cineraIndexSort,
#cineraIndexControl .cineraIndexFilter .filter_container {
cursor: pointer;
#cineraIndex #cineraIndexGrid
{
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;
-moz-user-select: none;
-webkit-user-select: none;
}
#cineraIndex .cineraIndexEntries {
#cineraIndex #cineraIndexGrid .cineraButton.subdivision
{
box-sizing: border-box;
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%;
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 {
@ -203,7 +403,7 @@ ul.cineraNavPlain li.current > a {
line-height: 16px;
padding: 5px;
vertical-align: top;
width: 200px;
max-width: 200px;
}
.cineraIndexEntries div a {
@ -243,36 +443,6 @@ ul.cineraNavPlain li.current > a {
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 / Structure */
@ -285,7 +455,7 @@ ul.cineraNavPlain li.current > a {
text-decoration: none;
}
.cineraMenus > *:not(.views),
.cineraMenus > *:not(.views, .cineraHelp),
.cineraMenus > .menu > .refs .ref,
.cineraMenus > .menu > .view,
.cineraMenus > .menu > .views_container .view,
@ -299,7 +469,14 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu {
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,
@ -312,67 +489,7 @@ ul.cineraNavPlain li.current > a {
animation-iteration-count: 1;
}
.cineraMenus .help .help_container .help_key {
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 {
.cineraHelp {
cursor: pointer;
border: 1px solid;
border-radius: 4px;
@ -387,32 +504,87 @@ ul.cineraNavPlain li.current > a {
z-index: 64;
}
.cineraMenus .help .help_container .help_grid {
display: inline-flex;
flex-direction: column;
}
.cineraMenus .help .help_container {
background-color: black; /* Per project */
color: #EEE; /* Per project */
.cineraHelp .help_container {
background-color: #000000; /* Per project */
color: #EEEEEE; /* Per project */
display: none;
font-weight: normal;
line-height: 12px;
opacity: 0.9;
padding: 8px;
position: fixed;
right: 612px;
top: 42px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.cineraMenus .help .help_container.visible {
.cineraHelp .help_container.visible {
display: block;
}
#cineraIndexControl .cineraIndexFilter .filter_container
{
.cineraHelp .help_container .help_key {
font-family: Inconsolata;
font-size: 16px;
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,
@ -430,11 +602,13 @@ ul.cineraNavPlain li.current > a {
position: absolute;
right: 0;
top: 100%;
z-index: 1;
z-index: 8;
}
.cineraMenus > .menu .refs,
.cineraMenus > .menu .link_container {
.cineraMenus > .menu .filter_container,
.cineraMenus > .menu .link_container,
.cineraMenus > .menu .credits_container {
width: 350px;
}
@ -442,12 +616,9 @@ ul.cineraNavPlain li.current > a {
overflow-x: hidden;
}
.cineraMenus > .menu .filter_container {
min-width: 350px;
}
.cineraMenus > .menu .credits_container {
min-width: 240px;
.cineraMenus > .menu .sizing {
z-index: -1;
display: block;
}
.cineraMenus > .menu .visible {
@ -639,7 +810,7 @@ ul.cineraNavPlain li.current > a {
}
.cineraPlayerContainer .markers_container {
flex-shrink: 0;
flex-shrink: 1;
overflow-y: scroll;
position: relative;
}
@ -651,6 +822,16 @@ ul.cineraNavPlain li.current > a {
font-weight: bold;
}
.cineraPlayerContainer {
background-color: #000000;
}
.cineraPlayerContainer .video_container {
display: flex;
justify-content: center;
align-self: center;
}
.cineraPlayerContainer .markers_container > a.episodeMarker {
cursor: pointer;
}
@ -683,7 +864,7 @@ ul.cineraNavPlain li.current > a {
border-bottom: 1px solid;
position: relative;
cursor: pointer;
max-height: 320px;
max-height: 320px; /* NOTE(matt): Required for the transition */
transition: max-height .32s;
}
@ -709,7 +890,7 @@ ul.cineraNavPlain li.current > a {
font-size: 14px;
}
.cineraPlayerContainer .markers_container > .markers .marker.skip {
.cinera:not(.mobile) .cineraPlayerContainer .markers_container > .markers .marker.skip {
max-height: 0;
transition: max-height .32s;
overflow: hidden;
@ -781,97 +962,109 @@ ul.cineraNavPlain li.current > a {
margin-right: 8px;
}
@media (max-width: 720px), (max-height: 512px)
{
.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;
}
/* NOTE(matt): Mobile Style */
.cineraMenus {
justify-content: center;
}
.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 {
flex-direction: column;
}
#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 */
/*
@ -889,4 +1082,4 @@ Open menu: &#9662; ▾
Open link in new tab: &#10555; or &10559; or &8599; or &#11127;
Play from timecode: &#9205; (or, if &#9656;)
Playable from timecode: &#9657;
*/
*/

View File

@ -516,7 +516,7 @@ typedef struct project
asset *IconAsset;
string StreamUsername;
string VODPlatform;
string VODPlatform; // TODO(matt): Make this an enum
bool DenyBespokeTemplates;
bool SingleBrowserTab;
@ -2615,7 +2615,7 @@ string
DeriveLineageWithoutOriginOfProject(config *C, scope_tree *Project)
{
string Result = {};
memory_book StringList = {};
memory_book StringList = InitBookOfPointers(MBT_STRING_PTR, 4);
if(Project->Parent && Project->Parent->ID.Key == IDENT_PROJECT)
{
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));
Player.initializeYoutube(this.onYoutubeReady.bind(this));
this.updateSize();
var PendingMobileStyleInitialisation = true;
this.updateSize(PendingMobileStyleInitialisation);
this.resume();
}
@ -99,6 +100,11 @@ Player.prototype.setTime = function(time) {
Player.prototype.jumpToNextMarker = function() {
var targetMarkerIdx = Math.min((this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx + 1), this.markers.length-1);
var targetTime = this.markers[targetMarkerIdx].timestamp;
while(targetMarkerIdx < this.markers.length && this.markers[targetMarkerIdx].el.classList.contains("skip"))
{
++targetMarkerIdx;
targetTime = this.markers[targetMarkerIdx].timestamp;
}
this.setTime(targetTime);
this.play();
};
@ -106,35 +112,310 @@ Player.prototype.jumpToNextMarker = function() {
Player.prototype.jumpToPrevMarker = function() {
var targetMarkerIdx = Math.max(0, (this.currentMarkerIdx === null ? 0 : this.currentMarkerIdx - 1));
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.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)
{
quotesMenu.previousElementSibling.textContent = '\u{1F5E9}';
quotesMenu.style.top = menuContainerOffset + "px";
}
if(referencesMenu)
{
referencesMenu.previousElementSibling.textContent = '\u{1F4D6}';
referencesMenu.style.top = menuContainerOffset + "px";
}
if(filterMenu) { filterMenu.style.top = menuContainerOffset + "px"; }
if(linkMenu) { linkMenu.style.top = menuContainerOffset + "px"; }
if(creditsMenu) {
if(creditsMenu)
{
creditsMenu.previousElementSibling.textContent = '\u{1F46A}';
creditsMenu.style.top = menuContainerOffset + "px";
}
if(viewsMenu)
{
viewsMenu.remove();
viewsMenu = null;
}
}
function
InitMobileControls()
{
var rightmost = {};
var markersContainer = player.markersContainer;
var markersContainer = cinera.querySelector(".markers_container");
markersContainer.style.height = "auto";
var episodeMarkerFirst = markersContainer.querySelector(".episodeMarker.first");
var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev");
var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next");
var episodeMarkerLast = markersContainer.querySelector(".episodeMarker.last");
@ -143,94 +424,70 @@ function switchToMobileView(player)
if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = '\u{23ED}'; rightmost = episodeMarkerNext; }
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");
markersContainer.insertBefore(controlPrevTimestamp, markers);
var controlPrevAnnotation = document.createElement("a");
controlPrevAnnotation.classList.add("episodeMarker");
controlPrevAnnotation.classList.add("prevAnnotation");
controlPrevAnnotation.addEventListener("click", function(ev) {
player.jumpToPrevMarker();
});
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);
var controlNextTimestamp = document.createElement("a");
controlNextTimestamp.classList.add("episodeMarker");
controlNextTimestamp.classList.add("nextTimestamp");
var controlNextTimestampContent = document.createElement("div");
controlNextTimestampContent.appendChild(document.createTextNode('\u{25B6}'));
controlNextTimestamp.appendChild(controlNextTimestampContent);
if(rightmost)
{
markersContainer.insertBefore(controlNextAnnotation, rightmost);
markersContainer.insertBefore(controlNextTimestamp, rightmost);
}
else
{
markersContainer.appendChild(controlNextAnnotation);
markersContainer.appendChild(controlNextTimestamp);
}
cineraProps.D = devices.MOBILE;
}
function switchToDesktopView(player)
function InitMobileStyle()
{
if(quotesMenu)
{
quotesMenu.previousElementSibling.textContent = originalTextContent.TitleQuotes;
quotesMenu.style.top = "100%";
}
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%";
}
cinera.classList.add("mobile");
IconifyMenuTogglers();
InitMobileControls();
ApplyMobileStyle();
}
function
ConnectMobileControls(player)
{
var markersContainer = player.markersContainer;
var episodeMarkerPrev = markersContainer.querySelector(".episodeMarker.prev");
if(episodeMarkerPrev) { episodeMarkerPrev.firstChild.textContent = originalTextContent.EpisodePrev; }
var episodeMarkerNext = markersContainer.querySelector(".episodeMarker.next");
if(episodeMarkerNext) { episodeMarkerNext.firstChild.textContent = originalTextContent.EpisodeNext; }
var prevAnnotation = markersContainer.querySelector(".episodeMarker.prevAnnotation");
markersContainer.removeChild(prevAnnotation);
var nextAnnotation = markersContainer.querySelector(".episodeMarker.nextAnnotation");
markersContainer.removeChild(nextAnnotation);
cineraProps.D = devices.DESKTOP;
var ControlPrevTimestamp = markersContainer.querySelector(".episodeMarker.prevTimestamp");
ControlPrevTimestamp.addEventListener("click", function(ev) {
player.jumpToPrevMarker();
});
var ControlNextTimestamp = markersContainer.querySelector(".episodeMarker.nextTimestamp");
ControlNextTimestamp.addEventListener("click", function(ev) {
player.jumpToNextMarker();
});
}
// Call this after changing the size of the video container in order to update the youtube player.
Player.prototype.updateSize = function() {
var width = this.videoContainer.offsetWidth;
var height = width / 16 * 9;
if(window.innerHeight > 512 && window.innerWidth > 720)
var width = 0;
var height = 0;
if(cineraProps.D === devices.DESKTOP)
{
if(cineraProps.D == devices.MOBILE)
{
switchToDesktopView(this);
}
this.markersContainer.style.height = height + "px"; // NOTE(matt): This was the original line here
width = this.videoContainer.offsetWidth;
height = width / 16 * 9; // TODO(matt): Get the aspect ratio from the video itself?
this.markersContainer.style.height = height + "px";
}
else
{
if(cineraProps.D == devices.DESKTOP)
{
switchToMobileView(this);
}
ApplyMobileStyle();
width = this.videoContainer.offsetWidth;
height = this.videoContainer.offsetHeight;
}
if (this.youtubePlayerReady) {
@ -288,25 +545,10 @@ Player.prototype.onMarkerClick = function(marker, ev) {
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) {
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) {
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);
if (this.currentMarker.hoverx === null) {
var pixelWidth = progress * totalWidth;
@ -364,9 +607,6 @@ Player.prototype.updateProgress = function() {
this.currentMarker.el.classList.add("current");
this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0;
this.scrollPosition = this.markersContainer.scrollTop;
}
if (this.currentMarker) {
this.refsCallback(this.currentMarker.ref, this.currentMarker.el, this);
} else if (prevMarker && prevMarker.ref) {
this.refsCallback(null);
@ -445,6 +685,7 @@ Player.prototype.onYoutubeReady = function() {
videoId: this.videoContainer.getAttribute("data-videoId"),
width: this.videoContainer.offsetWidth,
height: this.videoContainer.offsetWidth / 16 * 9,
playerVars: { 'playsinline': 1 },
//playerVars: { disablekb: 1 },
events: {
"onReady": this.onYoutubePlayerReady.bind(this),
@ -636,13 +877,13 @@ function enterFullScreen_()
{
if(!document.mozFullScreen && !document.webkitFullScreen)
{
if(cinera.mozRequestFullScreen)
if(document.mozRequestFullScreen)
{
cinera.mozRequestFullScreen();
document.mozRequestFullScreen();
}
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.mH = cinera.style.maxHeight;
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.zIndex = 64;
@ -683,6 +929,9 @@ function toggleTheatreMode() {
cinera.style.height = "100%";
cinera.style.maxHeight = "100%";
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("title", "Regular mode");
@ -703,6 +952,15 @@ function toggleTheatreMode() {
cinera.style.height = cineraProps.H;
cinera.style.maxHeight = cineraProps.mH;
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("title", "Theatre mode");
@ -859,10 +1117,12 @@ function handleKey(key) {
if(focusedElement.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.previousElementSibling;
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("references_container"))
@ -870,15 +1130,19 @@ function handleKey(key) {
if(focusedElement.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.previousElementSibling;
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
@ -887,10 +1151,12 @@ function handleKey(key) {
focusedElement.previousElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.previousElementSibling;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
@ -898,6 +1164,7 @@ function handleKey(key) {
if(focusedElement.parentNode.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.previousElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support"))
{
@ -905,13 +1172,14 @@ function handleKey(key) {
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
setSpriteLightness(focusedElement.firstChild);
focusSprite(focusedElement);
}
else
{
lastFocusedCreditItem = focusedElement.parentNode.previousElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
@ -926,10 +1194,12 @@ function handleKey(key) {
if(focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedQuote = focusedElement.nextElementSibling;
focusedElement = lastFocusedQuote;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("references_container"))
@ -937,15 +1207,19 @@ function handleKey(key) {
if(focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedReference = focusedElement.nextElementSibling;
focusedElement = lastFocusedReference;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
lastFocusedIdentifier = focusedElement.querySelector(".ref_indices").firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.parentNode.parentNode.classList.contains("filters"))
@ -954,10 +1228,12 @@ function handleKey(key) {
focusedElement.nextElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCategory = focusedElement.nextElementSibling;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
@ -965,6 +1241,7 @@ function handleKey(key) {
if(focusedElement.parentNode.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.nextElementSibling.querySelector(".support") &&
focusedElement.classList.contains("support"))
{
@ -972,13 +1249,14 @@ function handleKey(key) {
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".support");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
setSpriteLightness(focusedElement.firstChild);
focusSprite(focusedElement);
}
else
{
lastFocusedCreditItem = focusedElement.parentNode.nextElementSibling.querySelector(".person");
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
@ -993,16 +1271,20 @@ function handleKey(key) {
if(focusedIdentifier.previousElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.previousElementSibling;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
else if(focusedIdentifier.parentNode.previousElementSibling.classList.contains("ref_indices"))
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.previousElementSibling.lastElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.classList.contains("filter_content"))
@ -1011,6 +1293,7 @@ function handleKey(key) {
focusedElement.parentNode.previousElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedMedium = focusedElement;
if(!lastFocusedTopic)
@ -1020,6 +1303,7 @@ function handleKey(key) {
lastFocusedCategory = lastFocusedTopic;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
@ -1027,14 +1311,14 @@ function handleKey(key) {
if(focusedElement.classList.contains("support"))
{
focusedElement.classList.remove("focused");
console.log(focusedElement);
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.previousElementSibling;
if(focusedElement.firstChild.classList.contains("cineraSprite"))
{
setSpriteLightness(focusedElement.firstChild);
}
setSpriteLightness(focusedElement.firstChild);
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
}
@ -1048,17 +1332,21 @@ function handleKey(key) {
if(focusedIdentifier.nextElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.nextElementSibling;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
else if(focusedIdentifier.parentNode.nextElementSibling)
{
focusedIdentifier.classList.remove("focused");
unfocusSprite(focusedIdentifier);
lastFocusedIdentifier = focusedIdentifier.parentNode.nextElementSibling.firstElementChild;
focusedIdentifier = lastFocusedIdentifier;
focusedIdentifier.classList.add("focused");
focusSprite(focusedIdentifier);
}
}
else if(focusedElement.classList.contains("filter_content"))
@ -1067,6 +1355,7 @@ function handleKey(key) {
focusedElement.parentNode.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedTopic = focusedElement;
if(!lastFocusedMedium)
@ -1076,6 +1365,7 @@ function handleKey(key) {
lastFocusedCategory = lastFocusedMedium;
focusedElement = lastFocusedCategory;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
else if(focusedElement.parentNode.classList.contains("credit"))
@ -1084,14 +1374,12 @@ function handleKey(key) {
focusedElement.nextElementSibling)
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
lastFocusedCreditItem = focusedElement.nextElementSibling;
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
if(focusedElement.firstChild.classList.contains("cineraSprite"))
{
setSpriteLightness(focusedElement.firstChild);
}
focusSprite(focusedElement);
}
}
}
@ -1105,6 +1393,7 @@ function handleKey(key) {
focusedElement.nextElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics"))
{
lastFocusedTopic = focusedElement.nextElementSibling;
@ -1118,6 +1407,7 @@ function handleKey(key) {
lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
} break;
@ -1130,6 +1420,7 @@ function handleKey(key) {
focusedElement.previousElementSibling.classList.contains("filter_content"))
{
focusedElement.classList.remove("focused");
unfocusSprite(focusedElement);
if(focusedElement.parentNode.classList.contains("filter_topics"))
{
lastFocusedTopic = focusedElement.previousElementSibling;
@ -1143,6 +1434,7 @@ function handleKey(key) {
lastFocusedElement = lastFocusedCategory;
focusedElement = lastFocusedElement;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
}
}
} break;
@ -1163,7 +1455,10 @@ function handleKey(key) {
} break;
case "?": {
helpDocumentation.classList.toggle("visible");
if(helpDocumentation)
{
helpDocumentation.classList.toggle("visible");
}
} break;
case 'N':
@ -1319,7 +1614,7 @@ function filterItemToggle(filterItem) {
filterItem.querySelector(".icon").style.backgroundColor = getComputedStyle(filterItem.querySelector(".icon")).getPropertyValue("border-color");
}
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)
{
if(filterState[selectedCategory].type == "topic")
@ -1461,35 +1756,36 @@ function onRefChanged(ref, element, player) {
{
player.jumpToNextMarker();
}
return;
}
for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)
else
{
var SetMenu = 0;
if (ref !== undefined && ref !== null) {
var refElements = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
var refs = ref.split(",");
for (var MenuIndex = 0; MenuIndex < sourceMenus.length; ++MenuIndex)
{
var SetMenu = 0;
if (ref !== undefined && ref !== null) {
var refElements = sourceMenus[MenuIndex].querySelectorAll(".refs .ref");
var refs = ref.split(",");
for (var i = 0; i < refElements.length; ++i) {
if (refs.includes(refElements[i].getAttribute("data-id"))) {
refElements[i].classList.add("current");
SetMenu = 1;
} else {
refElements[i].classList.remove("current");
for (var i = 0; i < refElements.length; ++i) {
if (refs.includes(refElements[i].getAttribute("data-id"))) {
refElements[i].classList.add("current");
SetMenu = 1;
} else {
refElements[i].classList.remove("current");
}
}
}
if(SetMenu) {
sourceMenus[MenuIndex].classList.add("current");
if(SetMenu) {
sourceMenus[MenuIndex].classList.add("current");
} else {
sourceMenus[MenuIndex].classList.remove("current");
}
} else {
sourceMenus[MenuIndex].classList.remove("current");
}
} else {
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");
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;
}
function handleMouseOverMenu(menu, eventType)
function handleMenuTogglerInteraction(menu, eventType)
{
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"))
{
@ -1600,10 +1897,6 @@ function handleMouseOverMenu(menu, eventType)
toggleMenuVisibility(creditsMenu);
}
}
if(eventType == "click" && menu.classList.contains("help"))
{
helpDocumentation.classList.toggle("visible");
}
}
function RGBtoHSL(colour)
@ -1648,7 +1941,6 @@ function getBackgroundBrightness(element) {
var result = Math.sqrt(rgb[0] * rgb[0] * .241 +
rgb[1] * rgb[1] * .691 +
rgb[2] * rgb[2] * .068);
console.log(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) {
var colour = getComputedStyle(element).getPropertyValue("background-color");
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