cinera: Improve keyboard controls and scrolling

This commit adds keyboard navigation of the indices, documented in the
"Help" text box. It also improves scrolling of menus to follow progress
through the video, with the ability to centre the scrolling around a
range of references. Finally in this UI work, it enables the keyboard
and mouse to work more cooperatively.

Other changes:

• Added a "Clear" so the player's initial sizing happens invisibly.
• Fixed getBackgroundColourRGB() to handle both rgb() and rgba().
• Deduplicated code, including spurious querySelectorAll() calls.
• Moved global variables into the Player object.
This commit is contained in:
Matt Mascarenhas 2023-03-18 01:27:05 +00:00
parent c9bf96c7aa
commit a7694d4c3b
5 changed files with 1643 additions and 1219 deletions

View File

@ -23,7 +23,7 @@ typedef struct
version CINERA_APP_VERSION = {
.Major = 0,
.Minor = 10,
.Patch = 24
.Patch = 25
};
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
@ -11098,14 +11098,14 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
if(Result == RC_SUCCESS)
{
CopyStringToBuffer(&MenuBuffers->Quote,
" <a target=\"_blank\" class=\"ref\" href=\"https://dev.abaines.me.uk/quotes/%.*s/%d\">\n"
" <span data-id=\"&#%d;\">\n"
" <a target=\"_blank\" data-id=\"&#%d;\" class=\"ref\" href=\"https://dev.abaines.me.uk/quotes/%.*s/%d\">\n"
" <span>\n"
" <span class=\"ref_content\">\n"
" <div class=\"source\">Quote %d</div>\n"
" <div class=\"ref_title\">",
*QuoteIdentifier,
(int)QuoteUsername.Length, QuoteUsername.Base,
Timestamp->quote.id,
*QuoteIdentifier,
Timestamp->quote.id);
CopyStringToBufferHTMLSafe(&MenuBuffers->Quote, QuoteInfo.Text);
@ -11952,14 +11952,30 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&MenuBuffers.Filter,
"\"></span>\n"
" <div class=\"filter_container\">\n"
" <div class=\"filter_header\">\n"
" <div class=\"filter_mode exclusive\">Filter mode: </div>\n"
" <div class=\"filter_titles\">\n");
if(Topics.ItemCount > 0)
{
CopyStringToBuffer(&MenuBuffers.Filter,
" <div class=\"filter_title\">Topics</div>\n");
}
if(Media.ItemCount > 0)
{
CopyStringToBuffer(&MenuBuffers.Filter,
" <div class=\"filter_title\">Media</div>\n");
}
CopyStringToBuffer(&MenuBuffers.Filter,
" </div>\n"
" </div>\n"
" <div class=\"filters\">\n");
if(Topics.ItemCount > 0)
{
CopyStringToBuffer(&MenuBuffers.Filter,
" <div class=\"filter_topics\">\n"
" <div class=\"filter_title\">Topics</div>\n");
" <div class=\"filter_topics\">\n");
for(int i = 0; i < Topics.ItemCount; ++i)
{
category_info *This = GetPlaceInBook(&Topics, i);
@ -11988,8 +12004,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
if(Media.ItemCount > 0)
{
CopyStringToBuffer(&MenuBuffers.FilterMedia,
" <div class=\"filter_media\">\n"
" <div class=\"filter_title\">Media</div>\n");
" <div class=\"filter_media\">\n");
for(int i = 0; i < Media.ItemCount; ++i)
{
category_info *This = GetPlaceInBook(&Media, i);
@ -12056,7 +12071,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
"\n"
" <h2>Global Keys</h2>\n"
" <span class=\"help_key\">[</span>, <span class=\"help_key\">&lt;</span> / <span class=\"help_key\">]</span>, <span class=\"help_key\">&gt;</span> <span class=\"help_text\">Jump to previous / next episode</span><br>\n"
" <span class=\"help_key\">W</span>, <span class=\"help_key\">K</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">J</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next marker</span><br>\n"
" <span class=\"help_key\">W</span>, <span class=\"help_key\">K</span>, <span class=\"help_key\">P</span> / <span class=\"help_key\">S</span>, <span class=\"help_key\">J</span>, <span class=\"help_key\">N</span> <span class=\"help_text\">Jump to previous / next timestamp</span><br>\n"
" <span class=\"help_key\">t</span> / <span class=\"help_key\">T</span> <span class=\"help_text\">Toggle theatre / SUPERtheatre mode</span><br>\n"
" <span class=\"help_key%s\">V</span> <span class=\"help_text%s\">Revert filter to original state</span> <span class=\"help_key\">Y</span> <span class=\"help_text\">Select link (requires manual Ctrl-c)</span>\n",
@ -12078,15 +12093,15 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&PlayerBuffers.Menus,
"\n"
" <h2>In-Menu Movement</h2>\n"
" <h2>In-Menu and <span class=\"help_title_key help_custom_index\">Index</span> Controls</h2>\n"
" <div class=\"help_paragraph\">\n"
" <div class=\"key_block\">\n"
" <div>\n"
" <span class=\"help_key\">a</span>\n"
" </div>\n"
" <div>\n"
" <span class=\"help_key\">w</span><br>\n"
" <span class=\"help_key\">s</span>\n"
" <span class=\"help_key help_custom_index\">w</span><br>\n"
" <span class=\"help_key help_custom_index\">s</span>\n"
" </div>\n"
" <div>\n"
" <span class=\"help_key\">d</span>\n"
@ -12094,8 +12109,8 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
" </div>\n"
" <div class=\"key_block\">\n"
" <span class=\"help_key\">h</span>\n"
" <span class=\"help_key\">j</span>\n"
" <span class=\"help_key\">k</span>\n"
" <span class=\"help_key help_custom_index\">j</span>\n"
" <span class=\"help_key help_custom_index\">k</span>\n"
" <span class=\"help_key\">l</span>\n"
" </div>\n"
" <div class=\"key_block\">\n"
@ -12110,25 +12125,29 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
" <span class=\"help_key\">→</span>\n"
" </div>\n"
" </div>\n"
" </div><br>\n"
" <div class=\"help_paragraph\">\n"
" <span class=\"help_key help_custom_index\">Esc</span> <span class=\"help_text\">Close menu / unfocus timestamp</span>\n"
" </div>\n"
" <br>\n");
CopyStringToBuffer(&PlayerBuffers.Menus,
" <h2>%sQuotes %sand%s References%s Menus%s</h2>\n"
" <span class=\"help_key word%s\">Enter</span> <span class=\"help_text%s\">Jump to timecode</span><br>\n",
" <h2>%sQuotes %sand%s References%s Menus and%s Index</h2>\n"
" <span class=\"help_key word\">Enter</span> <span class=\"help_text\">Jump to timestamp</span><br>\n",
// Q R
//
// 0 0 <h2><span off>Quotes and References Menus</span></h2>
// 0 1 <h2><span off>Quotes and</span> References Menus</h2>
// 1 0 <h2>Quotes <span off>and References</span> Menus</h2>
// 1 1 <h2>Quotes and References Menus</h2>
// 0 0 <h2><span off>Quotes and References Menus and</span> Index</h2>
// 0 1 <h2><span off>Quotes and</span> References Menus and Index</h2>
// 1 0 <h2>Quotes <span off>and References</span> Menus and Index</h2>
// 1 1 <h2>Quotes and References Menus and Index</h2>
HasQuoteMenu ? "" : "<span class=\"unavailable\">",
HasQuoteMenu && !HasReferenceMenu ? "<span class=\"unavailable\">" : "",
!HasQuoteMenu && HasReferenceMenu ? "</span>" : "",
HasQuoteMenu && !HasReferenceMenu ? "</span>" : "",
!HasQuoteMenu && !HasReferenceMenu ? "</span>" : "",
HasQuoteMenu || HasReferenceMenu ? "" : " unavailable", HasQuoteMenu || HasReferenceMenu ? "" : " unavailable");
!HasQuoteMenu && !HasReferenceMenu ? "</span>" : "");
//HasQuoteMenu || HasReferenceMenu ? "" : " unavailable",
//HasQuoteMenu || HasReferenceMenu ? "" : " unavailable");
CopyStringToBuffer(&PlayerBuffers.Menus,
"\n"
@ -12371,6 +12390,19 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
CopyStringToBuffer(&CollationBuffers->Player, "<div class=\"cinera\">\n"
" ");
asset *JSClear = GetAsset(Wrap0(BuiltinAssets[ASSET_JS_CLEAR].Filename), ASSET_JS);
ConstructResolvedAssetURL(&URL, JSClear, PAGE_PLAYER);
CopyStringToBuffer(&CollationBuffers->Player,
"<script type=\"text/javascript\" src=\"%s",
URL.Location);
DeclaimBuffer(&URL);
PushAssetLandmark(&CollationBuffers->Player, JSClear, PAGE_PLAYER, 0);
CopyStringToBuffer(&CollationBuffers->Player,
"\"></script>");
CopyStringToBuffer(&CollationBuffers->Player, "\n"
" ");
CopyLandmarkedBuffer(&CollationBuffers->Player, &PlayerBuffers.Menus, 0, PAGE_PLAYER);
CopyStringToBuffer(&CollationBuffers->Player, "\n"
" ");

View File

@ -540,6 +540,16 @@ ul.cineraNavPlain li.current > a {
display: block;
}
.cineraHelp .help_container .help_custom_index {
background-color: #159 !important;
}
.cineraHelp .help_container h2 .help_title_key {
padding: .2em;
border: 1px solid;
border-radius: 4px;
}
.cineraHelp .help_container .help_key {
box-sizing: content-box;
font-family: Inconsolata;
@ -614,12 +624,11 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu .credits_container {
border: 1px solid;
border-top: none;
display: none;
z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */
overflow-y: auto;
position: absolute;
right: 0;
top: 100%;
z-index: 8;
}
.cineraMenus > .menu .refs,
@ -639,7 +648,7 @@ ul.cineraNavPlain li.current > a {
}
.cineraMenus > .menu .visible {
display: block;
z-index: 8;
}
.cineraMenus > .menu > .refs .ref {
@ -737,6 +746,11 @@ ul.cineraNavPlain li.current > a {
margin-right: 4px;
}
.cineraMenus > .menu > .filter_container .filter_header {
position: sticky;
top: 0;
}
.cineraMenus > .menu > .filter_container .filter_mode,
.cineraMenus > .menu > .link_container #cineraLinkMode {
cursor: pointer;
@ -756,11 +770,13 @@ ul.cineraNavPlain li.current > a {
text-align: center;
}
.cineraMenus > .menu > .filter_container .filter_header .filter_titles,
.cineraMenus > .menu > .filter_container .filters {
display: flex;
flex-flow: row nowrap;
}
.cineraMenus > .menu > .filter_container .filter_header .filter_titles > *,
.cineraMenus > .menu > .filter_container .filters > * {
width: 50%;
flex-grow: 1;

View File

@ -1,40 +1,5 @@
var cinera = document.querySelector(".cinera");
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
var originalTextContent = {
TitleQuotes: null,
TitleReferences: null,
TitleCredits: null,
EpisodePrev: null,
EpisodeNext: null,
};
var menuState = [];
var titleBar = cinera.querySelector(".cineraMenus");
var quotesMenu = null;
var referencesMenu = null;
var filterMenu = null;
var viewsMenu = null;
var linkMenu = null;
var creditsMenu = null;
var sourceMenus = null;
var helpButton = null;
var helpDocumentation = null;
// NOTE(matt): One set of markers per page. There is code to support multiple, which we may want to extend everywhere
var MarkersContainer = cinera.querySelector(".markers_container");
var views = {
REGULAR: 0,
THEATRE: 1,
SUPERTHEATRE: 2,
};
var devices = {
DESKTOP: 0,
MOBILE: 1,
};
var CineraProps = {
C: null,
V: views.REGULAR,
@ -57,275 +22,19 @@ var CineraProps = {
};
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(titleBar)
{
quotesMenu = titleBar.querySelector(".quotes_container");
if(quotesMenu)
{
originalTextContent.TitleQuotes = quotesMenu.previousElementSibling.textContent;
menuState.push(quotesMenu);
var quoteItems = quotesMenu.querySelectorAll(".ref");
if(quoteItems)
{
for(var i = 0; i < quoteItems.length; ++i)
{
quoteItems[i].addEventListener("mouseenter", function(ev) {
mouseOverQuotes(this);
})
};
}
var quoteTimecodes = quotesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < quoteTimecodes.length; ++i) {
quoteTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
var lastFocusedQuote = null;
}
referencesMenu = titleBar.querySelector(".references_container");
if(referencesMenu)
{
originalTextContent.TitleReferences = referencesMenu.previousElementSibling.textContent;
menuState.push(referencesMenu);
var referenceItems = referencesMenu.querySelectorAll(".ref");
if(referenceItems)
{
for(var i = 0; i < referenceItems.length; ++i)
{
referenceItems[i].addEventListener("mouseenter", function(ev) {
mouseOverReferences(this);
})
};
var lastFocusedReference = null;
var lastFocusedIdentifier = null;
}
var refTimecodes = referencesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < refTimecodes.length; ++i) {
refTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
}
if(referencesMenu || quotesMenu)
{
var refSources = titleBar.querySelectorAll(".refs .ref"); // This is for both quotes and refs
for (var i = 0; i < refSources.length; ++i) {
refSources[i].addEventListener("click", function(ev) {
if (player) {
player.pause();
}
});
}
}
filterMenu = titleBar.querySelector(".filter_container");
if(filterMenu)
{
menuState.push(filterMenu);
var lastFocusedCategory = null;
var lastFocusedTopic = null;
var lastFocusedMedium = null;
var filter = filterMenu.parentNode;
var filterModeElement = filter.querySelector(".filter_mode");
filterModeElement.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleFilterMode();
});
var filterMode = filterModeElement.classList[1];
var filterItems = filter.querySelectorAll(".filter_content");
var filterInitState = new Object();
var filterState = new Object();
for(var i = 0; i < filterItems.length; ++i)
{
filterItems[i].addEventListener("mouseenter", function(ev) {
navigateFilter(this);
})
filterItems[i].addEventListener("click", function(ev) {
ev.stopPropagation();
filterItemToggle(this);
});
var filterItemName = filterItems[i].classList.item(1);
if(filterItems[i].parentNode.classList.contains("filter_topics"))
{
filterInitState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
}
else
{
filterInitState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
}
}
}
viewsMenu = titleBar.querySelector(".views");
if(viewsMenu && !CineraProps.IsMobile)
{
menuState.push(viewsMenu);
var viewsContainer = viewsMenu.querySelector(".views_container");
viewsMenu.addEventListener("mouseenter", function(ev) {
handleMouseOverViewsMenu();
});
viewsMenu.addEventListener("mouseleave", function(ev) {
viewsContainer.style.display = "none";
});
var viewItems = viewsMenu.querySelectorAll(".view");
for(var i = 0; i < viewItems.length; ++i)
{
viewItems[i].addEventListener("click", function(ev) {
switch(this.getAttribute("data-id"))
{
case "regular":
case "theatre":
{
toggleTheatreMode();
} break;
case "super":
{
toggleSuperTheatreMode();
} break;
}
});
}
}
linkMenu = titleBar.querySelector(".link_container");
linkTimestamp = true;
if(linkMenu)
{
menuState.push(linkMenu);
var linkMode = linkMenu.querySelector("#cineraLinkMode");
var link = linkMenu.querySelector("#cineraLink");
linkMode.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleLinkMode(linkMode, link);
});
link.addEventListener("click", function(ev) {
CopyToClipboard(link);
toggleMenuVisibility(linkMenu);
});
}
creditsMenu = titleBar.querySelector(".credits_container");
if(creditsMenu)
{
originalTextContent.TitleCredits = creditsMenu.previousElementSibling.textContent;
menuState.push(creditsMenu);
var lastFocusedCreditItem = null;
var creditItems = creditsMenu.querySelectorAll(".person, .support");
for(var i = 0; i < creditItems.length; ++i)
{
creditItems[i].addEventListener("mouseenter", function(ev) {
if(this != lastFocusedCreditItem)
{
lastFocusedCreditItem.classList.remove("focused");
unfocusSprite(lastFocusedCreditItem);
if(lastFocusedCreditItem.classList.contains("support"))
{
setSpriteLightness(lastFocusedCreditItem.firstChild);
}
lastFocusedCreditItem = this;
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
if(focusedElement.classList.contains("support"))
{
setSpriteLightness(focusedElement.firstChild);
}
}
});
if(creditItems[i].tagName == "A")
{
creditItems[i].addEventListener("click", function(ev) {
if(player)
{
player.pause();
}
});
}
}
}
sourceMenus = titleBar.querySelectorAll(".menu");
helpButton = titleBar.querySelector(".cineraHelp");
helpDocumentation = helpButton.querySelector(".help_container");
BindHelp(helpButton, helpDocumentation);
}
var focusedElement = null;
var focusedIdentifier = null;
var playerContainer = cinera.querySelector(".cineraPlayerContainer")
var prevEpisode = playerContainer.querySelector(".episodeMarker.prev");
if(prevEpisode) { originalTextContent.EpisodePrev = prevEpisode.firstChild.textContent; }
var nextEpisode = playerContainer.querySelector(".episodeMarker.next");
if(nextEpisode) { originalTextContent.EpisodeNext = nextEpisode.firstChild.textContent; }
var testMarkers = playerContainer.querySelectorAll(".marker");
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector);
var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container";
var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
if(CineraProps.IsMobile)
{
InitMobileStyle();
}
else
{
var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - 4;
MenuContainerRule.style.maxHeight = MenuMaxHeight + "px";
}
var player = new Player(playerContainer, onRefChanged);
if(CineraProps.IsMobile)
{
ConnectMobileControls(player);
}
var cineraViewStorageItem = "cineraView";
if(viewsMenu && localStorage.getItem(cineraViewStorageItem))
{
toggleTheatreMode();
}
InitScrollEventListener(cinera);
function
DelayedUpdateSize()
{
player.updateSize();
}
var cinera = document.querySelector(".cinera");
var player = new Player(cinera, onRefChanged);
window.addEventListener("resize", function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512);
setTimeout(DelayedUpdateSize, 512, player);
}
else
{
@ -336,7 +45,7 @@ window.addEventListener("resize", function() {
window.onorientationchange = function() {
if(CineraProps.IsMobile)
{
setTimeout(DelayedUpdateSize, 512);
setTimeout(DelayedUpdateSize, 512, player);
}
else
{
@ -351,43 +60,8 @@ document.addEventListener("keydown", function(ev) {
key = "capitalSpace";
}
if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement)
if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item)
{
ev.preventDefault();
}
});
for(var i = 0; i < sourceMenus.length; ++i)
{
sourceMenus[i].addEventListener("mouseenter", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
sourceMenus[i].addEventListener("mouseleave", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
sourceMenus[i].addEventListener("click", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
};
var colouredItems = playerContainer.querySelectorAll(".author, .member, .project");
for(i = 0; i < colouredItems.length; ++i)
{
setTextLightness(colouredItems[i]);
}
var topicDots = cinera.querySelectorAll(".category");
for(var i = 0; i < topicDots.length; ++i)
{
setDotLightness(topicDots[i]);
}
var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname;
var lastTimestamp;
if(location.hash) {
player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash);
}
else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem))
{
player.setTimeThenPlay(lastTimestamp);
}

File diff suppressed because it is too large Load Diff

View File

@ -186,6 +186,12 @@ function enableSprite(Element)
function disableSprite(Element)
{
if(Element.classList.contains("focused"))
{
focusSprite(Element);
}
else
{
if(Element.classList.contains("cineraSprite"))
{
setSpriteLightness(Element);
@ -195,6 +201,7 @@ function disableSprite(Element)
{
disableSprite(Element.children[i]);
}
}
}
function unfocusSprite(Element)
@ -503,17 +510,29 @@ IsOverflowed(Element)
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
}
function
SetHelpUnfocused(Button)
{
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";
}
function
SetHelpFocused(Button)
{
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
}
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";
SetHelpUnfocused(Button);
});
window.addEventListener("focus", function(){
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
SetHelpFocused(Button);
});
Button.addEventListener("click", function() {
@ -553,13 +572,13 @@ function RGBtoHSL(colour)
function getBackgroundColourRGB(element) {
var Colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0;
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && depth <= 4)
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && element.parentElement && depth <= 4)
{
element = element.parentNode;
element = element.parentElement;
Colour = getComputedStyle(element).getPropertyValue("background-color");
++depth;
}
var Staging = Colour.slice(4, -1).split(", ");
var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", ");
var Result = {
R: parseInt(Staging[0]),
G: parseInt(Staging[1]),