|
|
|
|
|
|
|
|
|
|
|
/***************************/ |
|
|
|
|
|
/* INITIALIZATION REGISTRY */ |
|
|
|
|
|
/***************************/ |
|
|
|
|
|
|
|
|
|
|
|
/* Polyfill for requestIdleCallback in Apple and Microsoft browsers. */ |
|
|
|
|
|
if (!window.requestIdleCallback) { |
|
|
|
|
|
window.requestIdleCallback = (fn) => { setTimeout(fn, 0); }; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* TBC. */ |
|
|
|
|
|
GW.initializersDone = { }; |
|
|
|
|
|
GW.initializers = { }; |
|
|
|
|
|
function registerInitializer(name, tryEarly, precondition, fn) { |
|
|
|
|
|
GW.initializersDone[name] = false; |
|
|
|
|
|
GW.initializers[name] = fn; |
|
|
|
|
|
let wrapper = function () { |
|
|
|
|
|
if (GW.initializersDone[name]) return; |
|
|
|
|
|
if (!precondition()) { |
|
|
|
|
|
if (tryEarly) { |
|
|
|
|
|
setTimeout(() => requestIdleCallback(wrapper, { timeout: 1000 }), 50); |
|
|
|
|
|
} else { |
|
|
|
|
|
document.addEventListener("readystatechange", wrapper, { once: true }); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
GW.initializersDone[name] = true; |
|
|
|
|
|
fn(); |
|
|
|
|
|
}; |
|
|
|
|
|
if (tryEarly) { |
|
|
|
|
|
requestIdleCallback(wrapper, { timeout: 1000 }); |
|
|
|
|
|
} else { |
|
|
|
|
|
document.addEventListener("readystatechange", wrapper, { once: true }); |
|
|
|
|
|
requestIdleCallback(wrapper); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
function forceInitializer(name) { |
|
|
|
|
|
if (GW.initializersDone[name]) return; |
|
|
|
|
|
GW.initializersDone[name] = true; |
|
|
|
|
|
GW.initializers[name](); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/***********/ |
|
|
|
|
|
/* COOKIES */ |
|
|
|
|
|
/***********/ |
|
|
|
|
|
|
|
|
|
|
|
/* Sets a cookie. */ |
|
|
|
|
|
function setCookie(name, value, days) { |
|
|
|
|
|
var expires = ""; |
|
|
|
|
|
if (!days) days = 36500; |
|
|
|
|
|
if (days) { |
|
|
|
|
|
var date = new Date(); |
|
|
|
|
|
date.setTime(date.getTime() + (days*24*60*60*1000)); |
|
|
|
|
|
expires = "; expires=" + date.toUTCString(); |
|
|
|
|
|
} |
|
|
|
|
|
document.cookie = name + "=" + (value || "") + expires + "; path=/"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Reads the value of named cookie. |
|
|
|
|
|
Returns the cookie as a string, or null if no such cookie exists. */ |
|
|
|
|
|
function readCookie(name) { |
|
|
|
|
|
var nameEQ = name + "="; |
|
|
|
|
|
var ca = document.cookie.split(';'); |
|
|
|
|
|
for(var i = 0; i < ca.length; i++) { |
|
|
|
|
|
var c = ca[i]; |
|
|
|
|
|
while (c.charAt(0)==' ') c = c.substring(1, c.length); |
|
|
|
|
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); |
|
|
|
|
|
} |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/****************************************************/ |
|
|
|
|
|
/* CSS CLASS MANIPULATION (polyfill for .classList) */ |
|
|
|
|
|
/****************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
Element.prototype.addClass = function(className) { |
|
|
|
|
|
if (!this.hasClass(className)) |
|
|
|
|
|
this.className = (this.className + " " + className).trim(); |
|
|
|
|
|
} |
|
|
|
|
|
Element.prototype.addClasses = function(classNames) { |
|
|
|
|
|
let elementClassNames = this.className.trim().split(/\s/); |
|
|
|
|
|
|
|
|
|
|
|
classNames.forEach(className => { |
|
|
|
|
|
if (!this.hasClass(className)) |
|
|
|
|
|
elementClassNames.push(className); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
this.className = elementClassNames.join(" "); |
|
|
|
|
|
} |
|
|
|
|
|
Element.prototype.removeClass = function(className) { |
|
|
|
|
|
this.className = this.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), "$1").trim(); |
|
|
|
|
|
if (this.className == "") this.removeAttribute("class"); |
|
|
|
|
|
} |
|
|
|
|
|
Element.prototype.removeClasses = function(classNames) { |
|
|
|
|
|
classNames.forEach(className => { |
|
|
|
|
|
this.className = this.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), "$1").trim(); |
|
|
|
|
|
}); |
|
|
|
|
|
if (this.className == "") this.removeAttribute("class"); |
|
|
|
|
|
} |
|
|
|
|
|
Element.prototype.hasClass = function(className) { |
|
|
|
|
|
return (new RegExp("(^|\\s+)" + className + "(\\s+|$)")).test(this.className); |
|
|
|
|
|
} |
|
|
|
|
|
Element.prototype.toggleClass = function(className, on) { |
|
|
|
|
|
if ((typeof on == "undefined" && !this.hasClass(className)) || |
|
|
|
|
|
on == true) { |
|
|
|
|
|
this.addClass(className); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.removeClass(className); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/********************/ |
|
|
|
|
|
/* QUERYING THE DOM */ |
|
|
|
|
|
/********************/ |
|
|
|
|
|
|
|
|
|
|
|
function queryAll(selector, context) { |
|
|
|
|
|
context = context || document; |
|
|
|
|
|
// Redirect simple selectors to the more performant function |
|
|
|
|
|
if (/^(#?[\w-]+|\.[\w-.]+)$/.test(selector)) { |
|
|
|
|
|
switch (selector.charAt(0)) { |
|
|
|
|
|
case '#': |
|
|
|
|
|
// Handle ID-based selectors |
|
|
|
|
|
let element = document.getElementById(selector.substr(1)); |
|
|
|
|
|
return element ? [ element ] : [ ]; |
|
|
|
|
|
case '.': |
|
|
|
|
|
// Handle class-based selectors |
|
|
|
|
|
// Query by multiple classes by converting the selector |
|
|
|
|
|
// string into single spaced class names |
|
|
|
|
|
var classes = selector.substr(1).replace(/\./g, ' '); |
|
|
|
|
|
return [].slice.call(context.getElementsByClassName(classes)); |
|
|
|
|
|
default: |
|
|
|
|
|
// Handle tag-based selectors |
|
|
|
|
|
return [].slice.call(context.getElementsByTagName(selector)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Default to `querySelectorAll` |
|
|
|
|
|
return [].slice.call(context.querySelectorAll(selector)); |
|
|
|
|
|
} |
|
|
|
|
|
function query(selector, context) { |
|
|
|
|
|
let all = queryAll(selector, context); |
|
|
|
|
|
return (all.length > 0) ? all[0] : null; |
|
|
|
|
|
} |
|
|
|
|
|
Object.prototype.queryAll = function (selector) { |
|
|
|
|
|
return queryAll(selector, this); |
|
|
|
|
|
} |
|
|
|
|
|
Object.prototype.query = function (selector) { |
|
|
|
|
|
return query(selector, this); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*******************************/ |
|
|
|
|
|
/* EVENT LISTENER MANIPULATION */ |
|
|
|
|
|
/*******************************/ |
|
|
|
|
|
|
|
|
|
|
|
/* Adds an event listener to a button (or other clickable element), attaching |
|
|
|
|
|
it to both ‘click’ and ‘keyup’ events (for use with keyboard navigation). |
|
|
|
|
|
Optionally also attaches the listener to the ‘mousedown’ event, making the |
|
|
|
|
|
element activate on mouse down instead of mouse up. */ |
|
|
|
|
|
Element.prototype.addActivateEvent = function(func, includeMouseDown) { |
|
|
|
|
|
let ael = this.activateEventListener = (event) => { if (event.button === 0 || event.key === ' ') func(event) }; |
|
|
|
|
|
if (includeMouseDown) this.addEventListener("mousedown", ael); |
|
|
|
|
|
this.addEventListener("click", ael); |
|
|
|
|
|
this.addEventListener("keyup", ael); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Removes event listener from a clickable element, automatically detaching it |
|
|
|
|
|
from all relevant event types. */ |
|
|
|
|
|
Element.prototype.removeActivateEvent = function() { |
|
|
|
|
|
let ael = this.activateEventListener; |
|
|
|
|
|
this.removeEventListener("mousedown", ael); |
|
|
|
|
|
this.removeEventListener("click", ael); |
|
|
|
|
|
this.removeEventListener("keyup", ael); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Adds a scroll event listener to the page. */ |
|
|
|
|
|
function addScrollListener(fn, name) { |
|
|
|
|
|
let wrapper = (event) => { |
|
|
|
|
|
requestAnimationFrame(() => { |
|
|
|
|
|
fn(event); |
|
|
|
|
|
document.addEventListener("scroll", wrapper, { once: true, passive: true }); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
document.addEventListener("scroll", wrapper, { once: true, passive: true }); |
|
|
|
|
|
|
|
|
|
|
|
// Retain a reference to the scroll listener, if a name is provided. |
|
|
|
|
|
if (typeof name != "undefined") |
|
|
|
|
|
GW[name] = wrapper; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/************************/ |
|
|
|
|
|
/* ACTIVE MEDIA QUERIES */ |
|
|
|
|
|
/************************/ |
|
|
|
|
|
|
|
|
|
|
|
/* This function provides two slightly different versions of its functionality, |
|
|
|
|
|
depending on how many arguments it gets. |
|
|
|
|
|
|
|
|
|
|
|
If one function is given (in addition to the media query and its name), it |
|
|
|
|
|
is called whenever the media query changes (in either direction). |
|
|
|
|
|
|
|
|
|
|
|
If two functions are given (in addition to the media query and its name), |
|
|
|
|
|
then the first function is called whenever the media query starts matching, |
|
|
|
|
|
and the second function is called whenever the media query stops matching. |
|
|
|
|
|
|
|
|
|
|
|
If you want to call a function for a change in one direction only, pass an |
|
|
|
|
|
empty closure (NOT null!) as one of the function arguments. |
|
|
|
|
|
|
|
|
|
|
|
There is also an optional fifth argument. This should be a function to be |
|
|
|
|
|
called when the active media query is canceled. |
|
|
|
|
|
*/ |
|
|
|
|
|
function doWhenMatchMedia(mediaQuery, name, ifMatchesOrAlwaysDo, otherwiseDo = null, whenCanceledDo = null) { |
|
|
|
|
|
if (typeof GW.mediaQueryResponders == "undefined") |
|
|
|
|
|
GW.mediaQueryResponders = { }; |
|
|
|
|
|
|
|
|
|
|
|
let mediaQueryResponder = (event, canceling = false) => { |
|
|
|
|
|
if (canceling) { |
|
|
|
|
|
GWLog(`Canceling media query “${name}”`); |
|
|
|
|
|
|
|
|
|
|
|
if (whenCanceledDo != null) |
|
|
|
|
|
whenCanceledDo(mediaQuery); |
|
|
|
|
|
} else { |
|
|
|
|
|
let matches = (typeof event == "undefined") ? mediaQuery.matches : event.matches; |
|
|
|
|
|
|
|
|
|
|
|
GWLog(`Media query “${name}” triggered (matches: ${matches ? "YES" : "NO"})`); |
|
|
|
|
|
|
|
|
|
|
|
if (otherwiseDo == null || matches) ifMatchesOrAlwaysDo(mediaQuery); |
|
|
|
|
|
else otherwiseDo(mediaQuery); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
mediaQueryResponder(); |
|
|
|
|
|
mediaQuery.addListener(mediaQueryResponder); |
|
|
|
|
|
|
|
|
|
|
|
GW.mediaQueryResponders[name] = mediaQueryResponder; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Deactivates and discards an active media query, after calling the function |
|
|
|
|
|
that was passed as the whenCanceledDo parameter when the media query was |
|
|
|
|
|
added. |
|
|
|
|
|
*/ |
|
|
|
|
|
function cancelDoWhenMatchMedia(name) { |
|
|
|
|
|
GW.mediaQueryResponders[name](null, true); |
|
|
|
|
|
|
|
|
|
|
|
for ([ key, mediaQuery ] of Object.entries(GW.mediaQueries)) |
|
|
|
|
|
mediaQuery.removeListener(GW.mediaQueryResponders[name]); |
|
|
|
|
|
|
|
|
|
|
|
GW.mediaQueryResponders[name] = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/****************/ |
|
|
|
|
|
/* MISC HELPERS */ |
|
|
|
|
|
/****************/ |
|
|
|
|
|
|
|
|
|
|
|
/* Returns the passed object if it’s truthy, or a newly created HTMLElement. |
|
|
|
|
|
Æ(x) is the element analogue of (x||{}). |
|
|
|
|
|
*/ |
|
|
|
|
|
function Æ(x) { |
|
|
|
|
|
return x || document.createElement(null); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* If top of element is not at or above the top of the screen, scroll it into |
|
|
|
|
|
view. */ |
|
|
|
|
|
Element.prototype.scrollIntoViewIfNeeded = function() { |
|
|
|
|
|
GWLog("scrollIntoViewIfNeeded"); |
|
|
|
|
|
let rect = this.getBoundingClientRect(); |
|
|
|
|
|
if ((rect.bottom > window.innerHeight && rect.top > 0) || |
|
|
|
|
|
rect.top < 0) { |
|
|
|
|
|
this.scrollIntoView(true); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Return the currently selected text, as HTML (rather than unstyled text). |
|
|
|
|
|
*/ |
|
|
|
|
|
function getSelectionHTML() { |
|
|
|
|
|
var container = document.createElement("div"); |
|
|
|
|
|
container.appendChild(window.getSelection().getRangeAt(0).cloneContents()); |
|
|
|
|
|
return container.innerHTML; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Return the value of a GET (i.e., URL) parameter. |
|
|
|
|
|
*/ |
|
|
|
|
|
function getQueryVariable(variable) { |
|
|
|
|
|
var query = window.location.search.substring(1); |
|
|
|
|
|
var vars = query.split("&"); |
|
|
|
|
|
for (var i = 0; i < vars.length; i++) { |
|
|
|
|
|
var pair = vars[i].split("="); |
|
|
|
|
|
if (pair[0] == variable) |
|
|
|
|
|
return pair[1]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Given an element or a selector, removes that element (or the element |
|
|
|
|
|
identified by the selector). |
|
|
|
|
|
|
|
|
|
|
|
If multiple elements match the selector, only the first is removed. |
|
|
|
|
|
*/ |
|
|
|
|
|
function removeElement(elementOrSelector, ancestor = document) { |
|
|
|
|
|
if (typeof elementOrSelector == "string") elementOrSelector = ancestor.query(elementOrSelector); |
|
|
|
|
|
if (elementOrSelector) elementOrSelector.parentElement.removeChild(elementOrSelector); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Returns true if the string begins with the given prefix. |
|
|
|
|
|
*/ |
|
|
|
|
|
String.prototype.hasPrefix = function (prefix) { |
|
|
|
|
|
return (this.lastIndexOf(prefix, 0) === 0); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Toggles whether the page is scrollable. |
|
|
|
|
|
*/ |
|
|
|
|
|
function togglePageScrolling(enable) { |
|
|
|
|
|
if (!enable) { |
|
|
|
|
|
window.addEventListener('keydown', GW.scrollingDisabledKeyDown = (event) => { |
|
|
|
|
|
let forbiddenKeys = [ " ", "Spacebar", "ArrowUp", "ArrowDown", "Up", "Down" ]; |
|
|
|
|
|
if (forbiddenKeys.contains(event.key) && |
|
|
|
|
|
event.target == document.body) { |
|
|
|
|
|
event.preventDefault(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
window.removeEventListener('keydown', GW.scrollingDisabledKeyDown); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Copies a string to the clipboard. |
|
|
|
|
|
*/ |
|
|
|
|
|
function copyTextToClipboard(string) { |
|
|
|
|
|
let scratchpad = query("#scratchpad"); |
|
|
|
|
|
scratchpad.value = string; |
|
|
|
|
|
scratchpad.select(); |
|
|
|
|
|
document.execCommand("copy"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Returns the next element sibling of the element, wrapping around to the |
|
|
|
|
|
first child of the parent if the element is the last child. |
|
|
|
|
|
Returns the element itself, if it has no siblings or no parent. |
|
|
|
|
|
*/ |
|
|
|
|
|
Element.prototype.nextElementSiblingCyclical = function() { |
|
|
|
|
|
if (this.parentElement == null) return this; |
|
|
|
|
|
|
|
|
|
|
|
return this.parentElement.children[(Array.prototype.indexOf.call(this.parentElement.children, this) + 1) % this.parentElement.children.length]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Returns the previous element sibling of the element, wrapping around to the |
|
|
|
|
|
last child of the parent if the element is the first child. |
|
|
|
|
|
Returns the element itself, if it has no siblings or no parent. |
|
|
|
|
|
*/ |
|
|
|
|
|
Element.prototype.previousElementSiblingCyclical = function() { |
|
|
|
|
|
if (this.parentElement == null) return this; |
|
|
|
|
|
|
|
|
|
|
|
return this.parentElement.children[(Array.prototype.indexOf.call(this.parentElement.children, this) - 1) % this.parentElement.children.length]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/********************/ |
|
|
|
|
|
/* DEBUGGING OUTPUT */ |
|
|
|
|
|
/********************/ |
|
|
|
|
|
|
|
|
|
|
|
function GWLog (string) { |
|
|
|
|
|
if (GW.loggingEnabled == true || (GW.loggingEnabled == null && localStorage.getItem("logging-enabled") == "true")) |
|
|
|
|
|
console.log(string); |
|
|
|
|
|
} |
|
|
|
|
|
GW.enableLogging = (permanently = false) => { |
|
|
|
|
|
if (permanently) |
|
|
|
|
|
localStorage.setItem("logging-enabled", "true"); |
|
|
|
|
|
else |
|
|
|
|
|
GW.loggingEnabled = true; |
|
|
|
|
|
}; |
|
|
|
|
|
GW.disableLogging = (permanently = false) => { |
|
|
|
|
|
if (permanently) |
|
|
|
|
|
localStorage.removeItem("logging-enabled"); |
|
|
|
|
|
else |
|
|
|
|
|
GW.loggingEnabled = false; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/**********/ |
|
|
|
|
|
/* NAV UI */ |
|
|
|
|
|
/**********/ |
|
|
|
|
|
|
|
|
|
|
|
/* Hide the site nav UI on scroll down; show it on scroll up. |
|
|
|
|
|
|
|
|
|
|
|
Called by the ‘updateSiteNavUIStateScrollListener’ scroll listener. |
|
|
|
|
|
*/ |
|
|
|
|
|
function updateSiteNavUIState(event) { |
|
|
|
|
|
GWLog("updateSiteNavUIState"); |
|
|
|
|
|
|
|
|
|
|
|
let newScrollTop = window.pageYOffset || document.documentElement.scrollTop; |
|
|
|
|
|
GW.scrollState.unbrokenDownScrollDistance = (newScrollTop > GW.scrollState.lastScrollTop) ? |
|
|
|
|
|
(GW.scrollState.unbrokenDownScrollDistance + newScrollTop - GW.scrollState.lastScrollTop) : |
|
|
|
|
|
0; |
|
|
|
|
|
GW.scrollState.unbrokenUpScrollDistance = (newScrollTop < GW.scrollState.lastScrollTop) ? |
|
|
|
|
|
(GW.scrollState.unbrokenUpScrollDistance + GW.scrollState.lastScrollTop - newScrollTop) : |
|
|
|
|
|
0; |
|
|
|
|
|
GW.scrollState.lastScrollTop = newScrollTop; |
|
|
|
|
|
|
|
|
|
|
|
// Hide site nav UI when scrolling a full page down. |
|
|
|
|
|
if (GW.scrollState.unbrokenDownScrollDistance > window.innerHeight) { |
|
|
|
|
|
if (!GW.scrollState.siteNavUI[0].hasClass("hidden")) toggleSiteNavUI(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// On mobile, make site nav UI translucent on ANY scroll down. |
|
|
|
|
|
// if (GW.mediaQueries.mobileNarrow.matches) |
|
|
|
|
|
GW.scrollState.siteNavUI.forEach(element => { |
|
|
|
|
|
if (GW.scrollState.unbrokenDownScrollDistance > 0) element.addClass("translucent-on-scroll"); |
|
|
|
|
|
else element.removeClass("hidden"); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Show site nav UI when scrolling a full page up, or to the top. |
|
|
|
|
|
if ((GW.scrollState.unbrokenUpScrollDistance > window.innerHeight || |
|
|
|
|
|
GW.scrollState.lastScrollTop == 0)) showSiteNavUI(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function toggleSiteNavUI() { |
|
|
|
|
|
GWLog("toggleSiteNavUI"); |
|
|
|
|
|
|
|
|
|
|
|
GW.scrollState.siteNavUI.forEach(element => { |
|
|
|
|
|
element.toggleClass("hidden"); |
|
|
|
|
|
element.removeClass("translucent-on-scroll"); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function showSiteNavUI() { |
|
|
|
|
|
GWLog("showSiteNavUI"); |
|
|
|
|
|
|
|
|
|
|
|
GW.scrollState.siteNavUI.forEach(element => { |
|
|
|
|
|
element.removeClass("hidden"); |
|
|
|
|
|
element.removeClass("translucent-on-scroll"); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/******************/ |
|
|
|
|
|
/* INITIALIZATION */ |
|
|
|
|
|
/******************/ |
|
|
|
|
|
|
|
|
|
|
|
registerInitializer('earlyInitialize', true, () => (query("#content") != null), () => { |
|
|
|
|
|
GWLog("INITIALIZER earlyInitialize"); |
|
|
|
|
|
// Check to see whether we’re on a mobile device (which we define as a touchscreen^W narrow viewport). |
|
|
|
|
|
// GW.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); |
|
|
|
|
|
// GW.isMobile = ('ontouchstart' in document.documentElement); |
|
|
|
|
|
GW.mediaQueries = { |
|
|
|
|
|
mobileNarrow: matchMedia("(max-width: 520px)"), |
|
|
|
|
|
mobileWide: matchMedia("(max-width: 900px)"), |
|
|
|
|
|
mobileMax: matchMedia("(max-width: 960px)"), |
|
|
|
|
|
hover: matchMedia("only screen and (hover: hover) and (pointer: fine)") |
|
|
|
|
|
}; |
|
|
|
|
|
GW.isMobile = GW.mediaQueries.mobileMax.matches; |
|
|
|
|
|
GW.isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; |
|
|
|
|
|
|
|
|
|
|
|
GW.siteNavUISelector = "main nav"; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
/*************************/ |
|
|
|
|
|
/* POST-LOAD ADJUSTMENTS */ |
|
|
|
|
|
/*************************/ |
|
|
|
|
|
|
|
|
|
|
|
registerInitializer('pageLayoutFinished', false, () => (document.readyState == "complete"), () => { |
|
|
|
|
|
GWLog("INITIALIZER pageLayoutFinished"); |
|
|
|
|
|
|
|
|
|
|
|
forceInitializer('earlyInitialize'); |
|
|
|
|
|
|
|
|
|
|
|
// We pre-query the relevant elements, so we don’t have to run queryAll on |
|
|
|
|
|
// every firing of the scroll listener. |
|
|
|
|
|
GW.scrollState = { |
|
|
|
|
|
"lastScrollTop": window.pageYOffset || document.documentElement.scrollTop, |
|
|
|
|
|
"unbrokenDownScrollDistance": 0, |
|
|
|
|
|
"unbrokenUpScrollDistance": 0, |
|
|
|
|
|
"siteNavUI": queryAll(GW.siteNavUISelector), |
|
|
|
|
|
}; |
|
|
|
|
|
addScrollListener(updateSiteNavUIState, "updateSiteNavUIStateScrollListener"); |
|
|
|
|
|
}); |
|
|
|
|
|
|