{
return previous_page;
}, // END getPrevPage
// -------------------------------------------------------------------------
// getLanguage()
// Returns the preferred language taken form window.navigator
// See:
// https://stackoverflow.com/questions/1043339/javascript-for-detecting-browser-language-preference
// -------------------------------------------------------------------------
getLanguage: () => {
var language = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage);
}, // END getLanguage
// -------------------------------------------------------------------------
// getTemplateVersion()
// Returns the template version taken from site config (_config.yml)
// -------------------------------------------------------------------------
getTemplateVersion: () => {
return '{{template_version}}';
}, // END getTemplateVersion
// -------------------------------------------------------------------------
// getScrollOffset()
// Calculate offset for a correct (smooth) scroll position
// -------------------------------------------------------------------------
getScrollOffset: (offsetCorrection) => {
var $pagehead = $('.attic');
var $navbar = $('#navigator_nav_navbar');
var $adblock = $('#adblock');
var navbarType = $navbar.hasClass('navbar-fixed') ? 'fixed' : 'scrolled';
var fontSize = $('body').css('font-size').replace('px','');
var f = parseInt(fontSize);
var h = $pagehead.length ? $pagehead.height() : 0;
var n = $navbar.length ? $navbar.height() : 0;
var a = $adblock.length ? $adblock.height() : 0;
// Unclear why or what element cause the need of a correction
// TODO: General revision of scrollOffset needed
// NOTE: Disabled for now
//
// offsetCorrection = navbarType == 'fixed' ? 10 : -25;
scrollOffset = navbarType == 'fixed'
? -1*(n + a + f) + offsetCorrection
: -1*(n + a + f) + h + offsetCorrection;
return scrollOffset;
}, // END getScrollOffset
// -------------------------------------------------------------------------
// scrollTo()
// Scrolls smooth to any anchor referenced by an page URL on
// e.g. a page reload. Values e.g for delay are taken from
// TOCCER module
// NOTE: crollTo() is triggered by 'onDocumentHeigthChange'
// -------------------------------------------------------------------------
scrollTo: (offset) => {
var logger = log4javascript.getLogger('j1.scrollTo');
var anchor = window.location.href.split('#')[1];
var anchor_id = (typeof anchor !== 'undefined') && (anchor != '') ? '#' + anchor : false;
var scrollDuration = {{toccer_options.scrollSmoothDuration}};
var scrollOffset = offset; // j1.getScrollOffset();
var isSlider = false;
var selector = $(anchor_id);
// skip invalid anchors|selectors
//
if (typeof anchor === 'undefined' || anchor.includes('slide') || anchor.includes('googtrans') || !$(selector).length) {
return false;
}
// Check if the anchor is an slider/gallery element
//
if (typeof anchor !== 'undefined') {
isSlider = anchor.includes('slide');
}
if (anchor_id && anchor_id !== '#' && !isSlider) {
// scroll only, if an anchor is given with an URL
selector = $(anchor_id);
if (selector.length) {
logger.info('\n' + 'scrollTo header: ' + anchor_id);
j1.core.scrollSmooth.scroll(anchor_id, {
duration: scrollDuration,
offset: scrollOffset,
callback: false
});
} else {
// scroll the page one pixel back and forth (trigger)
// to get the right position for the Toccer and adjust the
// Navigator to display the (tranparent) navbar correctly
//
$(window).scrollTop($(window).scrollTop()+1);
$(window).scrollTop($(window).scrollTop()-1);
}
} else if (anchor_id === '#') {
logger.info('\n' + 'bound click event to "#", suppress default action');
$(window).scrollTop($(window).scrollTop()+1);
$(window).scrollTop($(window).scrollTop()-1);
return false;
}
}, // END scrollTo
// -------------------------------------------------------------------------
// authEnabled()
// Returns the state of the authentication module
// -------------------------------------------------------------------------
authEnabled: () => {
var logger = log4javascript.getLogger('j1.authentication');
var authEnabled = {{authentication_options.j1_auth.enabled}};
return authEnabled;
}, // END authEnabled
// -------------------------------------------------------------------------
// appDetected()
// Returns true if a web session cookie exists
// -------------------------------------------------------------------------
appDetected: () => {
var user_session;
var cookieExists = j1.existsCookie(cookie_names.user_session);
var detected = false;
if (cookieExists) {
user_session = j1.readCookie(cookie_names.user_session);
detected = user_session.mode === 'app' ? true : false;
} else {
// detected = 'unknown';
detected = false;
}
return detected;
}, // END appDetected
// -------------------------------------------------------------------------
// loadHTML()
// Load HTML data asychronously using XHR|jQuery on an element (e.g. )
// specified by xhr_container_id, xhr_data_path (options)
// -------------------------------------------------------------------------
loadHTML: (options, mod, status) => {
var logger = log4javascript.getLogger('j1.loadHTML');
var selector = $('#' + options.xhr_container_id);
var state = status;
var observer_options = {
attributes: false,
childList: true,
characterData: false,
subtree: true
};
var observer;
var cb_load_closure = (mod, id) => {
return (responseTxt, statusTxt, xhr) => {
if (statusTxt === 'success') {
j1.setXhrDataState(id, statusTxt);
j1.setXhrDomState(id, 'pending');
logger.debug('\n' + 'data loaded successfully on id: ' +id);
state = true;
}
if ( statusTxt === 'error' ) {
// jadams, 2020-07-21: to be checked why id could be UNDEFINED
if (typeof(id) != "undefined") {
state = 'failed';
logger.debug('\n' + 'set state for ' +mod+ ' to: ' + state);
// jadams, 2020-07-21: intermediate state should DISABLED
// executeFunctionByName(mod + '.setState', window, state);
j1.setXhrDataState(id, statusTxt);
j1.setXhrDomState(id, 'pending');
logText = '\n' + 'loading data failed on id: ' +id+ ', error ' + xhr.status + ': ' + xhr.statusText;
logger.error(logText);
state = false;
}
}
};
};
// see: https://stackoverflow.com/questions/20420577/detect-added-element-to-dom-with-mutation-observer
//
var html_data_path = options.xhr_data_path + ' #' + options.xhr_data_element;
var id = '#' + options.xhr_container_id;
var container = '#' + options.xhr_container_id + '_container';
var $selector = $(id);
// NOTE: Unclear why some pages (e.g. about/site) affected (fam button).
// All pages should have FRONTMATTER defaults (by _config.yml) setting
// all relevant defaults.
// failsafe - prevent XHR load errors
if (options.xhr_data_element !== '') {
logger.debug('\n' + 'XHR data element found: ' + options.xhr_data_element);
} else {
logger.debug('\n' + 'no XHR data element found, loading data aborted');
return;
}
if ($selector.length) {
$selector.load(html_data_path, cb_load_closure( mod, id ));
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var xhrObserver = new MutationObserver (mutationHandler);
var obsConfig = {
childList: true,
characterData: false,
attributes: false,
subtree: false };
selector.each(function(){
xhrObserver.observe(this, obsConfig);
});
function mutationHandler (mutationRecords) {
mutationRecords.forEach ( function (mutation) {
if (mutation.addedNodes.length) {
logger.debug('\n' + 'XHR data loaded in the DOM: ' + id);
j1.setXhrDomState(id, 'success');
}
});
}
} else {
// jadams, 2020-07-21: To be clarified why a id is "undefined"
// failsafe - prevent XHR load errors
if (id !== '#undefined') {
j1.setXhrDataState(id, 'not loaded');
j1.setXhrDomState(id, 'not loaded')
// jadams, 2020-07-21: intermediate state should DISABLED
// executeFunctionByName(mod + '.setState', window, state);
// state = false;
}
}
return state;
}, // END loadHTML
// -------------------------------------------------------------------------
// loadJS()
// Load JS data asychronously using jQuery (XHR)
// -------------------------------------------------------------------------
loadJS: (options, mod, status) => {
var logger = log4javascript.getLogger('j1.loadJS');
var state = status;
var logText;
var cb_load_closure = function(mod, id) {
return function (responseTxt, statusTxt, xhr) {
var logger = log4javascript.getLogger('j1.loadJS');
if ( statusTxt === 'success' ) {
j1.setXhrDataState(id, statusTxt);
logText = '\n' + 'data loaded successfully for: ' +id;
logger.info(logText);
state = true;
}
if ( statusTxt === 'error' ) {
state = 'failed';
logger.info('\n' + 'set state for ' +mod+ ' to: ' + state);
j1.setXhrDataState(id, statusTxt);
logText = '\n' + 'loading data failed for: ' +id+ ', error ' + xhr.status + ': ' + xhr.statusText;
logger.error(logText);
state = false;
}
};
};
$.ajax({
url: options.xhr_data_path,
dataType: 'script',
success: cb_load_closure(mod, options.xhr_data_element)
});
return state;
}, // END loadJS
// -------------------------------------------------------------------------
// removeRessource (Vanilla JS)
// -------------------------------------------------------------------------
removeRessource: (filename, filetype) => {
// determine element type to create nodelist from
var targetelement = (filetype === "js") ? "script" : (filetype === "css") ? "link" : "none";
// determine corresponding attribute to test for
var targetattr = (filetype === "js") ? "src" : (filetype === "css") ? "href" : "none";
var allsuspects = document.getElementsByTagName(targetelement)
// search backwards within nodelist for matching elements to remove
// remove element by calling parentNode.removeChild()
for (var i=allsuspects.length; i>=0; i--) {
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i])
}
}, // END removeRessource
// -------------------------------------------------------------------------
// subdomain()
// Returns true|false if a subdomain is used for a given URL
// -------------------------------------------------------------------------
subdomain: (url) => {
// See: https://snipplr.com/view/5449/check-if-a-url-contains-a-subdomain
// IF THERE, REMOVE WHITE SPACE FROM BOTH ENDS
url = url.replace(new RegExp(/^\s+/),""); // START
url = url.replace(new RegExp(/\s+$/),""); // END
// IF FOUND, CONVERT BACK SLASHES TO FORWARD SLASHES
url = url.replace(new RegExp(/\\/g),"/");
// IF THERE, REMOVES 'http://', 'https://' or 'ftp://' FROM THE START
url = url.replace(new RegExp(/^http\:\/\/|^https\:\/\/|^ftp\:\/\//i),"");
// IF THERE, REMOVES 'www.' FROM THE START OF THE STRING
url = url.replace(new RegExp(/^www\./i),"");
// REMOVE COMPLETE STRING FROM FIRST FORWARD SLASH ON
url = url.replace(new RegExp(/\/(.*)/),"");
// REMOVES '.??.??' OR '.???.??' FROM END - e.g. '.CO.UK', '.COM.AU'
if (url.match(new RegExp(/\.[a-z]{2,3}\.[a-z]{2}$/i))) {
url = url.replace(new RegExp(/\.[a-z]{2,3}\.[a-z]{2}$/i),"");
// REMOVES '.??' or '.???' or '.????' FROM END - e.g. '.US', '.COM', '.INFO'
} else if (url.match(new RegExp(/\.[a-z]{2,4}$/i))) {
url = url.replace(new RegExp(/\.[a-z]{2,4}$/i),"");
}
// CHECK TO SEE IF THERE IS A DOT '.' LEFT IN THE STRING
var isSubDomain = (url.match(new RegExp(/\./g))) ? true : false;
return(isSubDomain);
}, // END subdomain
// -------------------------------------------------------------------------
// readCookie (Vanilla JS)
// -------------------------------------------------------------------------
readCookie: (name) => {
var data;
var data_json;
var cookieExists = j1.existsCookie(name);
if (cookieExists) {
data_json = window.atob(Cookies.get(name));
data = JSON.parse(data_json);
if (data) {
return data;
} else {
return false;
}
} else {
return false;
}
}, // END readCookie
// -------------------------------------------------------------------------
// writeCookie (Cookie lib)
// Write 'data' to a cookie 'name'. If not exists, the cookie gets
// created. Returns 'true' if cookie was written, otherwise 'false'.
// -------------------------------------------------------------------------
// NOTE:
// https://web.dev/samesite-cookies-explained/
// https://developer.mozilla.org/de/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://www.smarketer.de/blog/chrome-update-80-cookies/
// -------------------------------------------------------------------------
// SESSION Cookies:
// NOT putting an EXPIRES part in will create a session cookie.
// -------------------------------------------------------------------------
// REMOVING Cookies: Cookies get removed immediately, if the expires
// part points to a PAST date (e.g. 01 Jan 1970 00:00:00 UTC).
// -------------------------------------------------------------------------
// MAX-AGE Cookies: To leave cookies for a specific time, set the expires
// part into a FUTUTE date. FOR GDPR compliance, MAX-AGE is 365 days.
// TODO: Change attribute "Secure" to true, if HTTPS is used.
// Checks and config changes are to be done.
// -------------------------------------------------------------------------
// TODO: Handling of attribute "SameSite".
// Config to use this attribute should be configurable
// (what config file?).
// Disabled use for now in general.
//
// The SameSite attribute of the Set-Cookie HTTP response header
// allows you to declare if your cookie should be restricted to a
// first-party or same-site context. Cookies with SameSite=None
// must now also specify the Secure attribute (they require a secure
// context/HTTPS).
// -------------------------------------------------------------------------
writeCookie: (options /*name, data, [path, expires, domain, samesite, http_only, secure]*/) => {
var date = new Date();
var timestamp_now = date.toISOString();
var url = new liteURL(window.location.href);
var baseUrl = url.origin;;
var hostname = url.hostname;
var auto_domain = hostname.substring(hostname.lastIndexOf('.', hostname.lastIndexOf('.') - 1) + 1);
var auto_secure = (url.protocol.includes('https')) ? true : false;
var stringifiedAttributes = '';
var cookie_data = {};
var data_json;
var data_encoded;
var defaults = {
path: '{{cookie_options.path}}',
expires: '{{cookie_options.expires}}',
domain: ('{{cookie_options.domain}}' === 'false') ? false : '{{cookie_options.domain}}',
samesite: '{{cookie_options.same_site}}',
http_only: ('{{cookie_options.http_only}}' === 'true'),
secure: ('{{cookie_options.secure}}' === 'false') ? false : '{{cookie_options.secure}}'
};
var settings = $.extend(defaults, options);
// Failsafe: if 'None' is given for samesite in non-secure environments
// -----------------------------------------------------------------------
if (settings.samesite === 'None' && !settings.secure) {
settings.samesite = 'Lax';
}
cookie_data.timestamp = timestamp_now;
if (j1.existsCookie(settings.name)) {
cookie_data = j1.readCookie(settings.name);
cookie_data = j1.mergeData(cookie_data, settings.data);
data_json = JSON.stringify( cookie_data );
data_encoded = window.btoa(data_json);
} else {
cookie_data = settings.data;
data_json = JSON.stringify(settings.data);
data_encoded = window.btoa(data_json);
}
// collect the cookie attributes
// -----------------------------------------------------------------------
stringifiedAttributes += '; ' + 'Path=' + settings.path;
if (settings.expires > 0) {
date.setTime(date.getTime() + (settings.expires * 24 * 60 * 60 * 1000));
stringifiedAttributes += '; ' + 'Expires=' + date.toUTCString();
}
stringifiedAttributes += '; ' + 'SameSite=' + settings.samesite;
// set domain used by cookies
if (settings.domain) {
if (settings.domain === 'auto') {
stringifiedAttributes += '; ' + 'Domain=' + auto_domain;
} else {
stringifiedAttributes += '; ' + 'Domain=' + settings.domain;
}
}
// set secure attribute
if (settings.secure) {
if (settings.secure === 'auto') {
stringifiedAttributes += '; ' + 'Secure=' + auto_secure;
} else {
stringifiedAttributes += '; ' + 'Secure=' + settings.secure;
}
}
// write the cookie
// -----------------------------------------------------------------------
document.cookie = settings.name + '=' + data_encoded + stringifiedAttributes;
if (j1.existsCookie(settings.name)) {
return cookie_data;
} else {
return false;
}
}, // END writeCookie
// -------------------------------------------------------------------------
// findCookie (Vanilla JS)
// Search for cookies (by name) in the page header that matches a given
// (name) string. A cookie name can be given as full name, like 'j1.user.state',
// or as a partial like 'j1'
// Returns all names found as an array.
// -------------------------------------------------------------------------
// See: https://stackoverflow.com/questions/52287989/javascript-cookie-remove-or-delete-with-regex-regular-expression
// -------------------------------------------------------------------------
findCookie: (name) => {
var rCookie=[];
document.cookie.replace(new RegExp(name + '[^= ]*', 'g'), function(a){ rCookie.push(a.trim()); });
return rCookie;
}, // END findCookie
// -------------------------------------------------------------------------
// removeCookie (Vanilla JS)
// -------------------------------------------------------------------------
removeCookie: (options /*name, [path, domain]*/) => {
var url = new liteURL(window.location.href);
var baseUrl = url.origin;;
var hostname = url.hostname;
var auto_domain = hostname.substring(hostname.lastIndexOf('.', hostname.lastIndexOf('.') - 1) + 1);
var auto_secure = (url.protocol.includes('https')) ? true : false;
var stringifiedAttributes = '';
var defaults = {
path: '{{cookie_options.path}}',
expires: 'Thu, 01 Jan 1970 00:00:00 UTC', // clear cookies by settting the expiry date in the PAST
domain: ('{{cookie_options.domain}}' === 'false') ? false : '{{cookie_options.domain}}',
samesite: '{{cookie_options.same_site}}',
http_only: ('{{cookie_options.http_only}}' === 'true'),
secure: ('{{cookie_options.secure}}' === 'false') ? false : '{{cookie_options.secure}}'
};
var settings = $.extend(defaults, options);
// collect the cookie attributes
// -----------------------------------------------------------------------
stringifiedAttributes += '; ' + 'Path=' + settings.path;
stringifiedAttributes += '; ' + 'SameSite=' + settings.samesite;
stringifiedAttributes += '; ' + 'Expires=' + settings.expires;
// set domain used by cookies
if (settings.domain) {
if (settings.domain === 'auto') {
stringifiedAttributes += '; ' + 'Domain=' + auto_domain;
} else if (typeof settings.domain === 'string') {
stringifiedAttributes += '; ' + 'Domain=' + settings.domain;
}
}
// set secure attribute
if (settings.secure) {
if (settings.secure === 'auto') {
stringifiedAttributes += '; ' + 'Secure=' + auto_secure;
} else {
stringifiedAttributes += '; ' + 'Secure=' + settings.secure;
}
}
// clear|remove the cookie if exists
// -----------------------------------------------------------------------
if (j1.findCookie(settings.name)) {
document.cookie = settings.name + '=;' + stringifiedAttributes;
return true;
} else {
return false;
}
}, // END removeCookie
// -------------------------------------------------------------------------
// expireCookie (Vanilla JS)
// Expires given cookies by name except cookies set to httpOnly. For all
// cookies the expiry date is REMOVED. This results in cookies are set
// to 'session' for the expiry date. All session cookies are deleted
// automatically by the browser if the last session (browser tab|window)
// is closed.
// -------------------------------------------------------------------------
// expireCookie() returns 'true' if cookie is set successfully
// (to session), otherwise 'false' (e.g. NOT found)
// -------------------------------------------------------------------------
// NOTE:
// See: https://stackoverflow.com/questions/179355/clearing-all-cookies-with-javascript
//
// NOTE:
// There is NO way you could get a trace of Path, Domain and other
// attributes of cookies as they are only read by browsers and NOT shown
// to JavaScript. For that reason, attributes needs to be set explicitly
// to already KNOWN values.
//
// -------------------------------------------------------------------------
expireCookie: (options /*name [,path, samesite, secure]*/) => {
var url = new liteURL(window.location.href);
var baseUrl = url.origin;;
var hostname = url.hostname;
var auto_domain = hostname.substring(hostname.lastIndexOf('.', hostname.lastIndexOf('.') - 1) + 1);
var auto_secure = (url.protocol.includes('https')) ? true : false;
var stringifiedAttributes = '';
var defaults = {
path: '{{cookie_options.path}}',
expires: '{{cookie_options.expires}}',
domain: ('{{cookie_options.domain}}' === 'false') ? false : '{{cookie_options.domain}}',
samesite: '{{cookie_options.same_site}}',
http_only: ('{{cookie_options.http_only}}' === 'true'),
secure: ('{{cookie_options.secure}}' === 'false') ? false : '{{cookie_options.secure}}'
};
var settings = $.extend(defaults, options);
// collect the cookie attributes
// -----------------------------------------------------------------------
stringifiedAttributes += '; ' + 'Path=' + settings.path;
stringifiedAttributes += '; ' + 'SameSite=' + settings.samesite;
// set domain used by cookies
if (settings.domain) {
if (settings.domain === 'auto') {
stringifiedAttributes += '; ' + 'Domain=' + auto_domain;
} else if (typeof settings.domain == 'string') {
if (settings.domain !== 'false') {
stringifiedAttributes += '; ' + 'Domain=' + settings.domain;
}
}
}
// set secure attribute
if (settings.secure) {
if (settings.secure === 'auto') {
stringifiedAttributes += '; ' + 'Secure=' + auto_secure;
} else {
stringifiedAttributes += '; ' + 'Secure=' + settings.secure;
}
}
var dc = document.cookie; // all cookies in page
var end = dc.length; // default to end of the string
var prefix = settings.name + '='; // search string for the cookie name given
var begin = dc.indexOf('; ' + prefix);
var content = '';
// collect the cookie content
// -----------------------------------------------------------------------
// found, and not in the first position
if (begin !== -1) {
// exclude the "; "
begin += 2;
} else {
// see if cookie is in first position
begin = dc.indexOf(prefix);
// not found at all or found as a portion of another cookie name
if (begin === -1 || begin !== 0 ) return false;
}
// if ";" is found somewhere after the prefix position then "end" is
// that position, otherwise it defaults to the end of the string
if (dc.indexOf(';', begin) !== -1) {
end = dc.indexOf(';', begin);
}
// write the cookie content, expire to session
// -----------------------------------------------------------------------
content = decodeURI(dc.substring(begin + prefix.length, end) ).replace(/"/g, '');
document.cookie = settings.name + '=' + content + stringifiedAttributes;
return true;
}, // END expireCookie
// -------------------------------------------------------------------------
// existsCookie (Vanilla JS)
// returns true if a cookie of given name exists
// -------------------------------------------------------------------------
existsCookie: (name) => {
var dc = document.cookie;
var prefix = name + '=';
var begin = dc.indexOf('; ' + prefix);
var end = dc.length; // default to end of the string
var cookieExists = false;
var cookieContent = '';
// collect the cookie content
// -----------------------------------------------------------------------
// found, and not in first position
if (begin !== -1) {
// exclude the "; "
begin += 2;
} else {
//see if cookie is in first position
begin = dc.indexOf(prefix);
// not found at all or found as a portion of another cookie name
if (begin === -1 || begin !== 0 ) return false;
}
// if ";" is found somewhere after the prefix position then "end" is
// that position, otherwise it defaults to the end of the string
if (dc.indexOf(';', begin) !== -1) {
end = dc.indexOf(';', begin);
}
// check if the cookie exists
// -----------------------------------------------------------------------
cookieContent = decodeURI(dc.substring(begin + prefix.length, end) ).replace(/"/g, '');
cookieExists = cookieContent.length ? true : false;
return cookieExists;
}, // END existsCookie
// -------------------------------------------------------------------------
// Resolve MACROs
//
// See:
// https://stackoverflow.com/questions/5376431/wildcards-in-jquery-selectors
// https://stackoverflow.com/questions/16400072/jquery-each-only-affects-last-element
// https://dzone.com/articles/why-does-javascript-loop-only-use-last-value
// https://stackoverflow.com/questions/179713/how-to-change-the-href-for-a-hyperlink-using-jquery
// https://stackoverflow.com/questions/5223/length-of-a-javascript-object
// -------------------------------------------------------------------------
resolveMacros: (user_data) => {
var logger = log4javascript.getLogger('j1.resolveMacros');
var sidebarLoaded = setInterval(() => {
if ($('#sidebar_mmenu').length) {
if (Object.keys(user_data).length) {
$('[id^=macro-]').each(function() {
$('#macro-provider').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??provider', user_data.provider));
this.href = this.href.replace(/.*\/??provider-site-url/, user_data.provider_site_url);
});
$('#macro-user-name').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??user-name', user_data.user_name));
this.href = this.href.replace(/.*\/??provider_member_url/, user_data.provider_member_url);
});
$('#macro-provider-permissions').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??provider-permissions', user_data.provider_permissions));
this.href = this.href.replace(/.*\/??provider_member_url/, user_data.provider_member_url);
});
$('#macro-provider-membership').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??provider-membership', user_data.provider_membership));
this.href = this.href.replace(/.*\/??provider_member_url/, user_data.provider_member_url);
});
$('#macro-cookie-state').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??cookie-state', user_data.cookies_accepted));
this.href = this.href.replace(/.*\/??provider_privacy_url/, user_data.provider_privacy_url);
});
$('#macro-theme-name').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??theme-name', user_data.theme_name));
});
$('#macro-theme-author').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??theme-author', user_data.theme_author));
this.href = this.href.replace(/.*\/??theme-author-url/, user_data.theme_author_url);
});
$('#macro-theme-version').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace('??theme-version', user_data.template_version));
});
});
logger.debug('\n' + 'met dependencies for: sidebarLoaded');
clearInterval(sidebarLoaded);
return true;
} else {
logger.error('\n' + 'no user data provided');
clearInterval(sidebarLoaded);
return false;
}
}
}, 10);
}, // END resolveMacros
// -------------------------------------------------------------------------
// Update MACROs
// Update the values, NOT the placeholders
// -------------------------------------------------------------------------
updateMacros: (user_data) => {
var logger = log4javascript.getLogger('j1.updateMacros');
var sidebarLoaded = setInterval(() => {
if ($('#sidebar_mmenu').length) {
if (Object.keys(user_data).length) {
$('[id^=macro-]').each(function() {
$('#macro-provider').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace(/Provider:.*/, 'Provider: ' + user_data.provider));
$('#macro-provider').attr('href', user_data.provider_site_url);
});
$('#macro-user-name').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace(/User:.*/, 'User: ' + user_data.user_name));
$('#macro-user-name').attr('href', user_data.provider_member_url);
});
$('#macro-provider-permissions').each(function() {
var $this = $(this);
var $html = $this.html();
// $this.html($html.replace(/public|protected|private|blocked/, user_data.provider_permissions));
$this.html($html.replace(/public.*|protected.*|private.*|blocked.*/, user_data.provider_permissions));
$('#macro-provider-permissions').attr('href', user_data.provider_member_url);
});
$('#macro-provider-membership').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace(/guest|member/, user_data.provider_membership));
$('#macro-provider-membership').attr('href', user_data.provider_member_url);
});
$('#macro-cookie-state').each(function() {
var $this = $(this);
var $html = $this.html();
$this.html($html.replace(/accepted|declined|pending/, user_data.cookies_accepted));
$('#macro-cookie-state').attr('href', user_data.provider_privacy_url);
});
});
logger.debug('\n' + 'met dependencies for: sidebarLoaded');
clearInterval(sidebarLoaded);
return true;
} else {
logger.error('\n' + 'no user data provided');
clearInterval(sidebarLoaded);
return false;
}
}
}, 10);
}, // END updateMacros
// -------------------------------------------------------------------------
// logger
// Log a message
// -------------------------------------------------------------------------
logger: (logger, level, message) => {
var logger = log4javascript.getLogger(logger);
logger[level](message);
return true;
}, // END logger
// -------------------------------------------------------------------------
// getStyleValue:
// Returns the value of a style from a css class definition
// example: j1.getStyleValue('uno-primary', 'background-color')
getStyleValue: (className, style) => {
var elementId = 'test-' + className,
testElement = document.getElementById(elementId),
val;
if (testElement === null) {
testElement = document.createElement('div');
testElement.className = className;
testElement.style.display = 'none';
document.body.appendChild(testElement);
}
val = $(testElement).css(style);
document.body.removeChild(testElement);
return val;
}, // END getStyleValue
// -------------------------------------------------------------------------
// getStyleSheetLoaded:
// NOTE:
// EXAMPLE: getStyleSheetLoaded('bootstrap');
// -------------------------------------------------------------------------
getStyleSheetLoaded: (styleSheet) => {
var sheets = document.styleSheets, stylesheet = sheets[(sheets.length - 1)];
// find CSS file 'styleSheetName' in document
for(var i in document.styleSheets) {
if(sheets[i].href && sheets[i].href.indexOf(styleSheet) > -1) {
return true;;
}
}
}, // END getStyleSheetLoaded
// -------------------------------------------------------------------------
// Returns the names of cookies used for J1 Theme
// -------------------------------------------------------------------------
getCookieNames: () => {
return cookie_names;
}, // END getCookieNames
// -------------------------------------------------------------------------
// Set dynamic styles
// -------------------------------------------------------------------------
// setCss: function () {
// var logger = log4javascript.getLogger('j1.setCss');
// var bg_primary = j1.getStyleValue('bg-primary', 'background-color');
// var bg_secondary = j1.getStyleValue('bg-secondary', 'background-color');
//
// logger.info('\n' + 'set color scheme for selected theme');
//
// // globals
// // -----------------------------------------------------------------------
// $('head').append('');
//
// // mdi icons
// // -----------------------------------------------------------------------
// $('head').append('');
// $('head').append('');
// $('head').append('');
// $('head').append('');
//
// // asciidoc
// // -----------------------------------------------------------------------
// var admonitionblock_note_color = bg_primary;
// var admonitionblock_tip_color = j1.getStyleValue('btn-success', 'background-color');
// var admonitionblock_important_color = j1.getStyleValue('btn-info', 'background-color');
// var admonitionblock_warning_color = j1.getStyleValue('icon-warning', 'color');
// var admonitionblock_caution_color = j1.getStyleValue('btn-danger', 'background-color');
//
// $('head').append('');
// $('head').append('');
// $('head').append('');
// $('head').append('');
// $('head').append('');
//
// // bs base styles (2020-09-20: diabled. Taken for BS CSS code)
// // -----------------------------------------------------------------------
// // $('head').append('');
//
// // bs tool tips
// // -----------------------------------------------------------------------
// // $('head').append('');
// // $('head').append('');
// // $('head').append('');
// // $('head').append('');
// // $('head').append('');
//
// // asciidoc results viewer
// // -----------------------------------------------------------------------
// $('head').append('');
//
// // extended modals
// // -----------------------------------------------------------------------
// // var tabs_pills_link_color_active = j1.setColorData('md_blue'); // j1.getStyleValue('btn-info', 'background-color');
// // var tabs_pills_link_color_hover = j1.setColorData('md_gray_300'); // j1.getStyleValue('btn-secondary', 'background-color');
//
// // var tabs_pills_link_color_active = 'md-blue';
// // var tabs_pills_link_color_hover = 'md-gray-300';
//
// // nav module
// // -----------------------------------------------------------------------
// // $('head').append('');
// // $('head').append('');
//
// return true;
// },
// -------------------------------------------------------------------------
// setXhrDataState()
// Set the final (loading) state of an element (partial) loaded via Xhr
// -------------------------------------------------------------------------
setXhrDataState: (obj, stat) => {
j1.xhrDataState[obj] = stat;
}, // END setXhrDataState
// -------------------------------------------------------------------------
// getXhrDataState()
// Returns the final (loading) state of an element (partial) loaded via Xhr
// -------------------------------------------------------------------------
getXhrDataState: (obj) => {
return j1.xhrDataState[obj];
}, // END mergeData
// -------------------------------------------------------------------------
// setXhrDomState()
// Set the state of an element loaded via Xhr that is
// successfully added to the DOM
// -------------------------------------------------------------------------
setXhrDomState: (obj, stat) => {
j1.xhrDOMState[obj] = stat;
}, // END getXhrDataState
// -------------------------------------------------------------------------
// getXhrDataState()
// Returns the state of an element loaded via Xhr that is
// successfully added to the DOM
// -------------------------------------------------------------------------
getXhrDOMState: (obj) => {
return j1.xhrDOMState[obj];
}, // END getXhrDOMState
// -------------------------------------------------------------------------
// setMode()
// Set the current mode of the site (web|app)
// -------------------------------------------------------------------------
setMode: (mod) => {
mode = mod;
}, // END setMode
// -------------------------------------------------------------------------
// getMode()
// Returns the current mode of the site (web|app)
// -------------------------------------------------------------------------
getMode: () => {
return mode;
}, // END getMode
// -------------------------------------------------------------------------
// checkUserAgent()
// Returns the name (UA) of the web browser
// -------------------------------------------------------------------------
checkUserAgent: () => {
if (navigator.userAgent.search(ua_name) >= 0) {
return true;
} else {
return false;
}
}, // END checkUserAgent
// -------------------------------------------------------------------------
// generateId()
// Generate a unique (thread) id used by the logger
// -------------------------------------------------------------------------
generateId: (length) => {
var result = '';
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}, // END generateId
// -------------------------------------------------------------------------
// getTrue()
// Returns always true (for testing purposes)
// -------------------------------------------------------------------------
getTrue: () => {
return true;
}, // END getTrue
// -------------------------------------------------------------------------
// getFalse()
// Returns always false (for testing purposes)
// -------------------------------------------------------------------------
getFalse: () => {
return false;
}, // END getFalse
// -------------------------------------------------------------------------
// goHome()
// Redirect current page to the browser homepage
// -------------------------------------------------------------------------
goHome: () => {
// most browsers
if (typeof window.home == 'function') {
window.home();
} else if (document.all) {
// for IE
window.location.href = 'about:home';
} else {
window.location.href = 'about:blank';
}
}, // END goHome
// -------------------------------------------------------------------------
// goBack()
// Redirect current page to last visited page (referrer)
// -------------------------------------------------------------------------
goBack: () => {
// where visitor has come from
window.location.href = document.referrer;
}, // END goBack
// -------------------------------------------------------------------------
// scrollToAnchor()
// Scroll to an anchor in current page if given in URL
// TODO: Find a better solution for 'dynamic' pages to detect
// the content if fully loaded instead using a timeout
// -------------------------------------------------------------------------
scrollToAnchor: () => {
var logger = log4javascript.getLogger('j1.scrollToAnchor');
var scrollOffset;
var dependencies_met_page_displayed = setInterval (() => {
var pageState = $('#no_flicker').css("display");
var pageVisible = (pageState === 'block') ? true: false;
var j1CoreFinished = (j1.getState() === 'finished') ? true : false;
var pageMonitor = (j1['pageMonitor'].pageType !== 'unknown') ? true : false;
if (j1CoreFinished && pageVisible && pageMonitor) {
// TODO: Check why a timeout is required to run the
// smmoth scroller (j1.scrollTo)
//
if (j1['pageMonitor'].pageType === 'static') {
// page type static
setTimeout(() => {
var headingUrl = new URL(window.location.href);
var headingHash = headingUrl.hash;
var headingId = headingHash.replace(/#/g, '');
var scrollOffsetCorrection = -93;
var scrollDuration = 300;
logger.debug('\n' + 'scrollToAnchor: scroll page of type: static');
// scroll if headingId
//
if (headingHash === '') {
logger.debug('\n' + 'scrollToAnchor: top position detected');
} else {
logger.debug('\n' + 'scrollToAnchor: scroll to headline by id: ' + headingHash);
j1.core.scrollSmooth.scroll(headingHash, {
duration: scrollDuration,
offset: scrollOffsetCorrection,
callback: false
});
}
}, {{template_config.timeoutScrollStaticPages}});
clearInterval(dependencies_met_page_displayed);
} else if (j1['pageMonitor'].pageType === 'dynamic') {
// page type dynamic
setTimeout(() => {
var headingArray = j1.core.parseHeadings(); // collect all headings in page
var headingUrl = new URL(window.location.href);
var headingHash = headingUrl.hash;
var headingId = headingHash.replace(/#/g, '');
var scrollTop = 0
var scrollOffset = 0;
var headlineNo = 0;
var countOnce = true;
var scrollOffset = 0;
var scrollOffsetCorrection = 0;
//
// var scrollOffsetCorrection = -93;
// var scrollDuration = 300;
logger.debug('\n' + 'scrollToAnchor: scroll page of type: dynamic');
// collect top position for the active headline
//
if (headingArray !== null) {
headingArray.forEach ((heading, index) => {
if (heading.offsetTop !== undefined && heading.id == headingId && countOnce) {
scrollOffset = heading.offsetTop;
headlineNo = ++index;
// jadams, 2023-10-27: !!! TEST !!!
// calculate scrollOffsetCorrection based on AVERAGE
// height of (number of) headlines
//
if (headlineNo === 1) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 89);
} else if (headlineNo <= 3) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 30);
} else if (headlineNo > 3 && headlineNo <= 6) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 18);
} else if (headlineNo > 6 && headlineNo <= 9) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 14);
} else if (headlineNo > 9 && headlineNo <= 13) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 34);
} else if (headlineNo > 13 && headlineNo <= 16) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 22);
} else if (headlineNo > 16 && headlineNo <= 20) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 26);
} else if (headlineNo > 20) {
scrollOffsetCorrection = scrollOffset - (headlineNo * 23);
}
countOnce = false;
scrollTop = Math.round(scrollOffsetCorrection);
} // END if heading.offsetTop|heading.id|countOnce
} // END function(heading, index)
); // END forEach headingArray
} // END if headingArray !== null
// scroll to headline's top position
//
if (headingHash === '') {
headingHash = '#';
logger.debug('\n' + 'scrollToAnchor: top position detected');
} else {
logger.debug('\n' + 'scrollToAnchor: headline|no: ' + headingHash + '|' + headlineNo);
// build-in scroller
//
window.scroll ({
top: scrollTop,
left: 0,
behavior: 'smooth'
});
} // END if headingHash
}, {{template_config.timeoutScrollDynamicPages}});
clearInterval(dependencies_met_page_displayed);
} else {
// page type unknown (failsave fallback)
setTimeout(() => {
logger.debug('\n' + 'scrollToAnchor: scroll page of type: unknown');
scrollOffset = scrollOffsetCorrection - scrollOffsetBase;
j1.scrollTo(scrollOffset);
}, {{template_config.page_on_load_timeout}} );
clearInterval(dependencies_met_page_displayed);
} // END if|else j1['pageMonitor'].pageType == 'dynamic'
} // END if j1['pageMonitor'].pageType
}, 10);
}, // END scrollToAnchor
// -------------------------------------------------------------------------
// stringToBoolean()
// convert a string to boolean
// -------------------------------------------------------------------------
stringToBoolean: (string) => {
switch(string.toLowerCase().trim()) {
case "true":
case "yes":
case "1":
return true;
case "false":
case "no":
case "0":
case null:
return false;
default:
return Boolean(string);
}
}, // END stringToBoolean
// -------------------------------------------------------------------------
// registerLazyLoadCss()
// CSS loader to speed up inital rendering
//
// Requires the following config serrings:
//
// src: the 'location' of the CSS file
// selector: the 'selector' that triggers the observer
// rootMargin: the 'margin' before the load is trigged
//
// -------------------------------------------------------------------------
//
registerLazyLoadCss: () => {
console.log('register CSS files for lazy loading');
// load MDI Light CSS
//
j1.lazyCss().observe({
'src': '/assets/theme/j1/core/css/icon-fonts/mdil.min.css',
'selector': '.mdil',
'rootMargin': '150px 0px'
});
// load MDI Regular CSS
//
j1.lazyCss().observe({
'src': '/assets/theme/j1/core/css/icon-fonts/mdi.min.css',
'selector': '.mdi',
'rootMargin': '150px 0px'
});
// load FA CSS
//
j1.lazyCss().observe({
'src': '/assets/theme/j1/core/css/icon-fonts/fontawesome.min.css',
'selector': '.fa',
'rootMargin': '150px 0px'
});
// load rTable CSS
//
j1.lazyCss().observe({
'src': '/assets/theme/j1/modules/rtable/css/theme/uno/rtable.min.css',
'selector': '.rtable',
'rootMargin': '150px 0px'
});
// load CountryFlags CSS
//
j1.lazyCss().observe({
'src': '/assets/theme/j1/core/country-flags/css/theme/uno.min.css',
'selector': '.flag-icon',
'rootMargin': '150px 0px'
});
}, // END registerLazyLoadCss
// -------------------------------------------------------------------------
// registerMonitors()
//
// -------------------------------------------------------------------------
registerMonitors: () => {
const development = ('{{environment}}'.includes('prod')) ? false : true;
var cumulated_cls = 0;
var cumulated_lcp = 0;
var cls;
var lcp;
{% comment %} generate code for performance monitor if 'enabled'
------------------------------------------------------------------------ {% endcomment %}
{% if template_config.monitor.performance_observer %}
// PerformanceObserver to monitor the 'LCP' of a page load
// see: https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint
//
const performanceObserverLCP = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
// logger API used for deveöopment only
//
if (development) {
var logger = log4javascript.getLogger('j1.PerformanceObserver');
}
// Use the latest LCP candidate
const lastEntry = entries[entries.length - 1];
var lastEntryText = JSON.stringify(lastEntry, null, 2);
cumulated_lcp += lastEntry.renderTime;
var lcp_full = cumulated_lcp/1000;
lcp = lcp_full.toFixed(3);
var url = lastEntry.url;
var pathname = url.replace( /^[a-zA-Z]{3,5}\:\/{2}[a-zA-Z0-9_.:-]+\//, '' );
if (development && lastEntry.url !== '') {
logger.debug('\n' + 'Largest Contentful Paint (LCP), url:', pathname);
} else {
console.debug(`Largest Contentful Paint (LCP), url: ${pathname}`);
}
if (development) {
if (lcp > 2.5) {
logger.warn('\n' + 'Largest Contentful Paint (LCP), cumulated:', lcp);
} else {
logger.info('\n' + 'Largest Contentful Paint (LCP), cumulated:', lcp);
}
} else {
if (lcp > 2.5) {
console.warn(`Largest Contentful Paint (LCP), cumulated: ${lcp}`);
} else {
console.debug(`Largest Contentful Paint (LCP), cumulated: ${lcp}`);
}
}
}); // END performanceObserverLCP
// PerformanceObserver to monitor the 'CLS' of a page load
//
const performanceObserverCLS = new PerformanceObserver(entryList => {
var entries = entryList.getEntries() || [];
// logger API used for deveöopment only
//
if (development) {
var logger = log4javascript.getLogger('j1.PerformanceObserver');
}
entries.forEach ((entry) => {
if (entry.sources) {
// omit entries likely caused by user input
if (!entry.hadRecentInput) {
// cumulate values
cumulated_cls += entry.value;
cls = cumulated_cls.toFixed(3);
}
for (const {node, currentRect, previousRect} of entry.sources) {
var hasProperty = node.hasOwnProperty('firstElementChild');
if (hasProperty == null) {
hasProperty = false;
}
if (hasProperty) {
var id = '';
try {
id = node.firstElementChild.id;
}
catch(err) {
id = 'missing';
}
if (development && id !== 'missing' && id !== '' && cls > 0.01) {
if (cls > 0.1) {
logger.warn('\n' + 'Cumulative Layout Shift (CLS), entry id: ', id);
logger.warn('\n' + 'Cumulative Layout Shift (CLS): ', cls);
} else {
logger.info('\n' + 'Cumulative Layout Shift (CLS), entry id: ', id);
logger.info('\n' + 'Cumulative Layout Shift (CLS): ', cls);
}
}
} // END if typeof
} // END for
} // END if entry.sources
}); // END forEach entry
}); // END performanceObserverCLS
{% endif %}
// ResizeObserver to monitor the changes on page height (dynamic pages)
// see: https://stackoverflow.com/questions/14866775/detect-document-height-change
//
const resizeObserver = new ResizeObserver(entries => {
// var scrollOffsetCorrection = scrollerOptions.smoothscroll.offsetCorrection;
const body = document.body,
html = document.documentElement,
scrollOffset = scrollOffsetCorrection;
// logger API used for deveöopment only
//
if (development) {
var logger = log4javascript.getLogger('j1.ResizeObserver');
}
// get the page height from the DOM
//
var documentHeight = Math.max (
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight
);
if (j1['pageMonitor'] !== undefined && j1['pageMonitor'].eventNo !== undefined) {
j1['pageMonitor'].eventNo += 1;
}
// skip first Observer event
//
if (j1['pageMonitor'] !== undefined && j1['pageMonitor'].eventNo !== undefined && j1['pageMonitor'].eventNo === 2) {
// Set initial data from second event
//
j1['pageMonitor'].pageBaseHeight = documentHeight;
j1['pageMonitor'].currentPageHeight = document.body.scrollHeight;
j1['pageMonitor'].previousPageHeight = document.body.scrollHeight;
j1['pageMonitor'].previousGrowthRatio = 0.00;
pageBaseHeight = documentHeight;
previousGrowthRatio = 100;
growthRatio = 0.00;
} else {
// collect 'pageHeight' from 'entries'
//
previousGrowthRatio = document.body.scrollHeight / pageBaseHeight * 100;
growthRatio = document.body.scrollHeight / pageBaseHeight * 100;
if (j1['pageMonitor'] !== undefined) {
for (const entry of entries) {
pageBaseHeight = documentHeight;
if (pageBaseHeight > 0) {
// get the page height (rounded to int) from observer
//
pageHeight = Math.round(entry.contentRect.height);
j1['pageMonitor'].currentPageHeight = pageHeight;
// total growth ratio
//
pageGrowthRatio = pageHeight / pageBaseHeight * 100;
// pageGrowthRatio = pageGrowthRatio.toFixed(2);
j1['pageMonitor'].currentGrowthRatio = pageGrowthRatio;
// growthRatio = ((pageGrowthRatio / previousGrowthRatio) - 1) * 100;
// growthRatio = growthRatio.toFixed(2);
if (growthRatio !== undefined && growthRatio > 0) {
growthRatio = growthRatio.toFixed(2);
} else {
growthRatio = 0.00;
}
j1['pageMonitor'].growthRatio = growthRatio;
}
} // END for entries
} else {
pageBaseHeight = document.body.scrollHeight;
previousGrowthRatio = 100;
growthRatio = 0.00;
} // END if j1['pageMonitor'] undefined
if (j1['pageMonitor'] !== undefined) {
// detect the 'page type'
//
// if (growthRatio >= 5) {
if (growthRatio > 100) {
j1['pageMonitor'].pageType = 'dynamic';
if (development) {
logger.debug('\n' + 'growthRatio: ' + j1['pageMonitor'].growthRatio + '%');
logger.debug('\n' + 'page detected as: dynamic');
}
} else {
// set the page type to 'static' if low growth detected
//
if (typeof j1['pageMonitor'].growthRatio != 'undefined' && j1['pageMonitor'].growthRatio == 0) {
j1['pageMonitor'].pageType = 'static';
if (development) {
logger.debug('\n' + 'growthRatio: ' + j1['pageMonitor'].growthRatio + '%');
logger.debug('\n' + 'page detected as: static');
}
}
} // END if growthRatio
} // END if j1['pageMonitor'] undefined
} // END if j1['pageMonitor']
}); // END resizeObserver
// -----------------------------------------------------------------------
// run all observers for page monitoring
// -----------------------------------------------------------------------
{% comment %} generate initializers for performance monitoring if 'enabled'
------------------------------------------------------------------------ {% endcomment %}
{% if template_config.monitor.performance_observer %}
// monitor 'LCP'
//
performanceObserverLCP.observe({
type: 'largest-contentful-paint',
buffered: true
});
// monitor 'CLS'
//
performanceObserverCLS.observe({
type: 'layout-shift',
buffered: true
});
{% endif %}
// jadams, 2023-10-26: delay untion STATIC portion of a page is loaded
//
setTimeout(() => {
resizeObserver.observe(
document.querySelector('body')
);
}, 500);
// initialize event handler for window/history/back on
//
window.onkeyup = function (event) {
if (event.keyCode == 27) window.history.back();
};
$("a[href*='#']:not([href='#'])").click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
var offset = target.offset().top + scrollOffsetCorrection;
$('html,body').animate({
scrollTop: offset
}, 1000);
return false;
}
}
});
}, // END registerMonitors
// -------------------------------------------------------------------------
// int2float()
// convert an integer to float using given precision (default: 2 decimals)
// -------------------------------------------------------------------------
int2float: (number, precision = 2) => {
return number.toFixed(precision);
},
// -------------------------------------------------------------------------
// getTimeLeft()
// calulates the time left
// -------------------------------------------------------------------------
getTimeLeft: (endDate) => {
// Get the current date and time
const now = new Date();
// Get the milliseconds of both dates
const endTime = endDate.getTime();
const currentTime = now.getTime();
// Calculate the difference in milliseconds
const difference = endTime - currentTime;
// Check if the end date has passed (difference is negative)
if (difference < 0) {
return 'Time has passed!';
}
// Calculate remaining days using milliseconds in a day
const daysLeft = Math.floor(difference / (1000 * 60 * 60 * 24));
// Calculate remaining hours using milliseconds in an hour
const hoursLeft = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
// Calculate remaining minutes using milliseconds in a minute
const minutesLeft = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
// Calculate remaining seconds using milliseconds in a second
const secondsLeft = Math.floor((difference % (1000 * 60)) / 1000);
// Return a formatted string showing remaining time
return `${daysLeft} days, ${hoursLeft} hours, ${minutesLeft} minutes, ${secondsLeft} seconds left`;
}, // END getTimeLeft
// -------------------------------------------------------------------------
// getMessage()
// get a log message from the log message catalog object
// -------------------------------------------------------------------------
getMessage: (level, message, property) => {
var message = j1.messages[level][message]['message'][property];
return message;
}, // END getMessage
// -------------------------------------------------------------------------
// sendMessage()
// -------------------------------------------------------------------------
sendMessage: (sender, receiver, message) => {
var logger = log4javascript.getLogger('j1.sendMessage');
// var json_message = JSON.stringify(message, undefined, 2); // multiline
var json_message = JSON.stringify(message);
if (receiver === 'j1') {
logText = '\n' + 'send message from ' + sender + ' to' + receiver + ': ' + json_message;
logger.debug(logText);
executeFunctionByName('j1' + '.messageHandler', window, sender, message);
} else {
logText = '\n' + 'send message from ' + sender + ' to ' + receiver + ': ' + json_message;
logger.debug(logText);
//executeFunctionByName('j1.' + receiver + '.messageHandler', window, sender, message)
executeFunctionByName(receiver + '.messageHandler', window, sender, message);
}
}, // END sendMessage
// -------------------------------------------------------------------------
// messageHandler()
// manage messages send from other J1 modules
// -------------------------------------------------------------------------
messageHandler: (sender, message) => {
// var json_message = JSON.stringify(message, undefined, 2); // multiline
var json_message = JSON.stringify(message);
logText = '\n' + 'received message from ' + sender + ': ' + json_message;
logger.debug(logText);
// -----------------------------------------------------------------------
// process commands|actions
// -----------------------------------------------------------------------
if ( message.type === 'command' && message.action === 'module_initialized' ) {
_this.setState('finished');
logger.info('\n' + message.text);
}
//
// place handling of other command|action here
//
return true;
}, // END messageHandler
// -------------------------------------------------------------------------
// setState()
// det the current (processing) state of the module
// -------------------------------------------------------------------------
setState: (stat) => {
state = stat;
}, // END setState
// -------------------------------------------------------------------------
// getState()
// returns the current (processing) state of the module
// -------------------------------------------------------------------------
getState: () => {
return state;
}, // END getState
isValidCaption: (caption) => {
return (typeof caption !== 'undefined' && caption.length > 0);
}
}; // END main (return)
})(j1, window);
{%- endcapture -%}
{%- if production -%}
{{ cache|minifyJS }}
{%- else -%}
{{ cache|strip_empty_lines }}
{%- endif -%}
{%- assign cache = false -%}