"use strict"; import $ from "jquery"; import Events from "./_events"; import Consts from "./_consts"; import EventsRouter from "./_events.router"; import Spinner from "./_components/_ui.spinner"; // AJAX functionality import AjaxUI from "./_components/_ui.ajax"; import FormBasics from "./_components/_ui.form.basics"; import HeaderUI from "./_components/_ui.header-footer"; import SmoothScroll from "smooth-scroll"; const smoothScroll = SmoothScroll(); const MainUI = (($) => { // Constants const W = window; const $W = $(W); const D = document; const $Body = $("body"); const NAME = "MainUI"; console.clear(); console.info( `%cUI Kit ${UINAME} ${UIVERSION}`, "color:yellow;font-size:14px" ); console.info( `%c${UIMetaNAME} ${UIMetaVersion}`, "color:yellow;font-size:12px" ); console.info( `%chttps://github.com/a2nt/webpack-bootstrap-ui-kit by ${UIAUTHOR}`, "color:yellow;font-size:10px" ); console.groupCollapsed("Events"); Object.keys(Events).forEach((k) => { console.info(`${k}: ${Events[k]}`); }); console.groupEnd("Events"); console.groupCollapsed("Consts"); Object.keys(Consts).forEach((k) => { console.info(`${k}: ${Consts[k]}`); }); console.groupEnd("Events"); console.groupCollapsed("Init"); console.time("init"); $W.on(`${Events.LODEDANDREADY}`, () => { console.groupEnd("Init"); console.timeEnd("init"); console.time("Post-init"); console.groupCollapsed("Post-init"); }); // get browser locale //const Locale = $('html').attr('lang').substring(0, 2); const $AlertNotify = $("#AlertNotify"); const $SiteWideMessage = $("#SiteWideMessage"); // get browser window visibility preferences // Opera 12.10, Firefox >=18, Chrome >=31, IE11 const HiddenName = "hidden"; const VisibilityChangeEvent = "visibilitychange"; // update visibility state D.addEventListener(VisibilityChangeEvent, () => { if (D.visibilityState === HiddenName) { console.log(`${NAME}: Tab: hidden`); $Body.addClass("is-hidden"); $Body.trigger(Events.TABHIDDEN); $W.trigger(Events.TABHIDDEN); } else { console.log(`${NAME}: Tab: focused`); $Body.removeClass("is-hidden"); $Body.trigger(Events.TABFOCUSED); $W.trigger(Events.TABFOCUSED); } }); // session ping let pingInterval; let pingLock = false; const sessionPing = () => { if (pingLock || $Body.hasClass("is-offline")) { return; } console.log(`${NAME}: session ping`); pingLock = true; $.ajax({ sync: false, async: true, cache: false, url: "/Security/ping", global: false, type: "POST", complete: (data, datastatus) => { updateOnlineStatus(); if (datastatus !== "success") { console.warn(`${NAME}: ping failed`); clearInterval(pingInterval); pingInterval = null; } pingLock = false; }, }); }; // update online/offline state let statusLock = false; const updateOnlineStatus = () => { if (statusLock) { return; } statusLock = true; if (typeof navigator.onLine === "undefined") { return false; } if (!navigator.onLine) { console.log(`${NAME}: Offline`); clearInterval(pingInterval); pingInterval = null; $Body.addClass("is-offline"); $Body.removeClass("is-online"); $Body.trigger(Events.OFFLINE); $W.trigger(Events.OFFLINE); statusLock = false; return true; } if (!pingInterval) { pingInterval = setInterval(sessionPing, 300000); // 5 min in ms } if ($Body.hasClass("is-offline")) { sessionPing(); console.log(`${NAME}: is back online`); $Body.trigger(Events.BACKONLINE); } else { console.log(`${NAME}: Online`); } $Body.addClass("is-online"); $Body.removeClass("is-offline"); $Body.trigger(Events.ONLINE); $W.trigger(Events.ONLINE); statusLock = false; return true; }; W.addEventListener( `${Events.OFFLINE}`, () => { updateOnlineStatus(); }, false ); W.addEventListener( `${Events.ONLINE}`, () => { updateOnlineStatus(); }, false ); $W.on(`${Events.LOADED} ${Events.AJAX}`, () => { updateOnlineStatus(); }); // scrollTo const ScrollTo = (trigger, selector) => { smoothScroll.animateScroll(D.querySelector(selector), trigger, { speed: 500, offset: -20, //easing: 'easeInOutCubic', // Callback API //before: (anchor, toggle) => {}, // Callback to run before scroll //`after: (anchor, toggle) => {} // Callback to run after scroll }); }; W.URLDetails = { base: $("base").attr("href"), relative: "/", hash: "", }; let eventFired = false; const setTouchScreen = (bool) => { if (W.IsTouchScreen === bool || eventFired) { return; } eventFired = true; W.IsTouchScreen = bool; $.support.touch = W.IsTouchScreen; if (bool) { console.log(`${NAME}: Touch screen enabled`); $Body.trigger(Events.TOUCHENABLE); $W.trigger(Events.TOUCHENABLE); } else { console.log(`${NAME}: Touch screen disabled`); $Body.trigger(Events.TOUCHDISABLED); $W.trigger(Events.TOUCHDISABLED); } // prevent firing touch and mouse events together setTimeout(() => { eventFired = false; }, 200); }; setTouchScreen("ontouchstart" in window || navigator.msMaxTouchPoints > 0); // disable touch on mouse events /*D.addEventListener('mousemove', () => { setTouchScreen(false); }); D.addEventListener('mousedown', () => { setTouchScreen(false); });*/ // enable touch screen on touch events D.addEventListener("touchmove", () => { setTouchScreen(true); }); D.addEventListener("touchstart", () => { setTouchScreen(true); }); class MainUI { // Static methods static init() { const ui = this; ui.dispose(); console.log(`${NAME}: init`); // update location details ui.updateLocation(); // mark available offline areas if ("caches" in W) { $("a.offline").addClass("offline-available"); } ui.loadImages(); // detect bootstrap screen size ui.detectBootstrapScreenSize(); // mark external links $('a.external,a[rel="external"]').attr("target", "_blank"); // show encoded emails /*$(D).find('.obm').each(() => { if ($(this).attr('data-val') !== undefined) { const email = $(this).attr('data-val').split('') .reverse() .join('') .slice(0, -8) .replace(/[a-zA-Z]/g, (c) => String.fromCharCode((c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26)) .replace('#AT#', '@'); const attr = $(this).attr('data-val-append'); if (attr !== undefined && attr !== false) { $(this).append(email); } if ($(this).find('.sr-only').length > 0) { $(this).find('.sr-only').append(email); } if ($(this).attr('href') !== undefined) { $(this).attr('href', `mailto:${email}`); } } });*/ // // scroll links $(".js-scrollTo").on("click", (e) => { console.log(`${NAME}: .js-scrollTo`); e.preventDefault(); const el = e.currentTarget; const $el = $(e.currentTarget); ScrollTo(el, $el.attr("data-target")); }); // load external fonts if ($("[data-extfont]").length) { console.log(`${NAME}: loading external fonts [data-extfont]`); $.getScript( "//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js", () => { const fonts = []; $("[data-extfont]").each((i, el) => { fonts[i] = $(el).attr("data-extfont"); }); W.WebFont.load({ google: { families: fonts, }, }); } ); } // data-set links $("[data-set-target]").on("click", (e) => { console.log(`${NAME}: [data-set-target]`); const $el = $(e.currentTarget); const $target = $($el.data("set-target")); if (!$target.length) { return; } $target.each((i, targetEl) => { const $targetEl = $(targetEl); const tag = $targetEl.prop("tagName").toLowerCase(); if (tag === "input" || tag === "select") { $targetEl.val($el.data("set-val")); } else if (!$targetEl.hasClass("field")) { $targetEl.text($el.data("set-val")); } }); $el.trigger(Events.SET_TARGET_UPDATE); $target.closest("form").trigger(Events.SET_TARGET_UPDATE); }); // emulate links $(".a[data-href]").on("click", (e) => { console.log(`${NAME}: js link processing .a[data-href]`); const $el = $(e.currentTarget); const href = $el.data("href"); if (!href.length) { console.warn(`${NAME}: .a[data-href] | Missing data-href`); console.warn($el); } W.location.assign(href); }); // set attributes for mobile friendly tables $(".typography table").each((i, el) => { const $table = $(el); let $header = $table.find("thead tr:first-child"); if (!$header.length) { $header = $(el).find("tr:first-child"); } $header.addClass("d-typography-breakpoint-none"); $header.find("td").each((i, h) => { const $h = $(h); $table .find("tr") .find(`td:eq(${i})`) .each((i, el) => { const $el = $(el); if (!$el.attr("data-label")) { $el.attr("data-label", $h.text()); } }); }); }); // // hide spinner Spinner.hide(() => { $Body.addClass("loaded"); }); // fire page printing if (W.URLDetails["hash"].indexOf("printpage") > -1) { W.print(); } $Body.data(NAME, ui); $W.removeClass("lock-main-init"); } static detectBootstrapScreenSize() { const $el = $('
'); let envs = [...Consts.ENVS]; $Body.append($el); let curEnv = envs.shift(); envs = envs.reverse(); for (let i = 0; i < envs.length; ++i) { const env = envs[i]; $el.addClass(`d-${env}-none`); if ($el.is(":hidden")) { curEnv = env; break; } } $el.remove(); $Body.removeClass(envs); $Body.addClass(curEnv); let landscape = true; if ($W.width() > $W.height()) { $Body.removeClass("portrait"); $Body.addClass("landscape"); } else { landscape = false; $Body.removeClass("landscape"); $Body.addClass("portrait"); } console.log( `${NAME}: screen size detected ${curEnv} | landscape ${landscape}` ); return curEnv; } static updateLocation(url) { let location = url || W.location.href; location = location.replace(W.URLDetails["base"], "/"); const hash = location.indexOf("#"); W.URLDetails.relative = location.split("#")[0]; W.URLDetails.hash = hash >= 0 ? location.substr(location.indexOf("#")) : ""; } // show site-wide alert static alert(msg, cls) { $SiteWideMessage.fadeOut("fast"); $SiteWideMessage.html( `
×${msg}
` ); $SiteWideMessage.find(".page-alert").alert(); $SiteWideMessage.find('.close[data-dismiss="alert"]').click(() => { $SiteWideMessage.fadeOut("slow", () => { $SiteWideMessage.find(".page-alert").alert("close"); }); }); $SiteWideMessage.fadeIn("slow"); if ($AlertNotify.length) { $AlertNotify[0].play(); } $W.trigger(`${Events.ALLERTAPPEARED}`); } // hide site-wide alert static alertHide() { if ($SiteWideMessage.length !== 0) { $SiteWideMessage.fadeOut("slow", () => { $SiteWideMessage.find(".alert").alert("close"); }); } if ($AlertNotify.length && typeof $AlertNotify[0].stop !== "undefined") { $AlertNotify[0].stop(); } $W.trigger(`${Events.ALLERTREMOVED}`); } // load all images static loadImages() { const $imgs = $Body.find("img").not(".loaded"); const $imgUrls = []; const $imgLazyUrls = []; // collect image details $imgs.each((i, el) => { const $el = $(el); const src = $el.attr("src"); const lazySrc = $el.data("lazy-src"); if ($el.hasClass("loaded")) { return; } if (src && src.length) { $imgUrls.push(src); } if (lazySrc && lazySrc.length) { $imgLazyUrls.push(lazySrc); $el.addClass("loading"); AjaxUI.preload([lazySrc]).then(() => { $el.attr("src", lazySrc); $el.on(`${Events.LOADED}`, () => { $el.addClass("loaded"); $el.removeClass("loading"); $el.trigger(`${Events.LAZYIMAGEREADY}`); }); }); } }); // load lazy backgrounds $Body .find("[data-lazy-bg]") .not(".loaded") .each((i, el) => { const $el = $(el); const lazySrc = $el.data("lazy-bg"); if ($el.hasClass("loaded")) { return; } if (lazySrc && lazySrc.length) { $imgLazyUrls.push(lazySrc); $el.addClass("loading"); AjaxUI.preload([lazySrc]).then(() => { $el.css({ "background-image": `url(${lazySrc})` }); $el.addClass("loaded"); $el.removeClass("loading"); $el.trigger(`${Events.LAZYIMAGEREADY}`); }); } }); // replace img src $Body .find("[data-src-replace]") .not(".loaded") .each((i, el) => { const $el = $(el); const lazySrc = $el.data("src-replace"); if ($el.hasClass("loaded")) { return; } if (lazySrc && lazySrc.length) { $el.addClass("loaded"); $el.attr("src", lazySrc); } }); // load defined images AjaxUI.preload($imgUrls).then(() => { $W.trigger("images-loaded"); // load lazy images AjaxUI.preload($imgLazyUrls).then(() => { console.log(`${NAME}: All images are loaded!`); setTimeout(() => { $W.trigger(`${Events.LAZYIMAGESREADY}`); console.groupEnd("Post-init"); console.timeEnd("Post-init"); }, 100); }); }); } static dispose() { console.log(`${NAME}: dispose`); } } $W.on( `${Events.MAININIT} ${Events.AJAX} ${Events.AJAXMAIN} ${Events.LOADED}`, () => { if ($W.hasClass("lock-main-init")) { console.warn(`${NAME}: locked`); return; } $W.addClass("lock-main-init"); MainUI.init(); } ); $W.on(`${Events.RESIZE}`, () => { MainUI.detectBootstrapScreenSize(); }); $W.on("beforeunload unload", () => { Spinner.show(() => { $Body.removeClass("loaded"); }); }); // hide spinner on target _blank $('[target="_blank"],.external') .not('[data-toggle="lightbox"],[data-lightbox-gallery]') .on("click submit touch", (e) => { console.log(`${NAME}: External link`); setTimeout(() => { Spinner.hide(() => { $Body.addClass("loaded"); }); }, 1000); }); W.MainUI = MainUI; return MainUI; })($); export default MainUI;