//============================================================================== // // Inserts the virtual assistant chat into a web page // //============================================================================== //DATA VALUES var va_isMaximised = false; var va_isOpen = false; var va_isMobile = false; var va_pushTimeout; var va_chatStarted = false; var titleBarTitle = "Sam Virtual Assistant"; var headingTitle = "Ask a question"; /******* Helper functions ****************/ /** * This function sets the HTML markup for the mobile header for the virtual assistant. * @return string which contains the HTML markup for the mobile header. */ function getMobileHeader() { var mobileLargeClass = '', vaOpenClass = ''; if (va_isOpen) { mobileLargeClass = ' class="va_mobileLarge" '; vaOpenClass = ' va_open'; } var mobileStyle = '
' + '
' + '' + '
' + headingTitle + '
' + '
' + '' + '
' + '
'; return mobileStyle; } /** * This function sets the HTML markup for the desktop header for the virtual assistant. * @return string which contains the HTML markup for the desktop header. */ function getDesktopHeader() { var rightMargin = calcRightMargin(), desktopHeaderSize = "style='right:" + rightMargin + "'", vaExpanded = "", resizeIconState = "", resizeIconClass = ""; //Include these if chat is open if (va_isOpen) { vaExpanded = " va_expanded"; resizeIconState = 'style="display: block;"'; if (va_isMaximised) { desktopHeaderSize = 'style="height:490px; width:600px; right:' + rightMargin + ';"'; resizeIconClass = ' va_large'; } else { desktopHeaderSize = 'style="height:455px; width:350px; right:' + rightMargin + ';"'; } } var desktopStyle = '
' + '
' + '
' + ' ' + '

' + headingTitle + '

' + '' + '
' + '
' + '
'; return desktopStyle; } /** * This function sets the required alt text for the toggle button and resize button in different UI states. * @return string which contains the alt text to be applied. */ function getAltText(element) { if (element == "va_toggleIcon" || element == "va_mobileToggle") { if (va_isOpen) { return "Hide " + titleBarTitle; } else { return "Open " + titleBarTitle; } } else if (element == "va_resizeIcon") { if (va_isMaximised) { return "View " + titleBarTitle + " in a smaller window"; } else { return "View " + titleBarTitle + " in a larger window"; } } } /** * This function applies text to the title and aria-label attributes of an element. */ function setAltText(element) { var resizeAlt = getAltText(element); jQuery("#" + element).prop("title", resizeAlt); jQuery("#" + element).attr("aria-label", resizeAlt); } /** * This function calculates the required right margin for the virtual assistant on the page depending on the state of the UI. * @return value to be applied as the right margin. */ function calcRightMargin() { var windowWidth = jQuery(window).width(), maxContainerWidth = 960, rightMargin = "0"; if (windowWidth > 1100) { rightMargin = ((windowWidth - maxContainerWidth) / 2) - 50; rightMargin = rightMargin + "px"; } else if (windowWidth > 630) { rightMargin = "2%"; } return rightMargin; } /******* Insertion functions ****************/ /** * This function inserts a chatAnchor div to load the chat title bar in the parent page. */ function insertDiv() { var rightMargin = calcRightMargin(), chatAnchor = document.createElement('div'), appendStyle = va_isMobile == true ? getMobileHeader() : getDesktopHeader(); chatAnchor.id = 'va_chatAnchor'; chatAnchor.className = 'va_chatAnchor'; chatAnchor.zIndex = 99999; jQuery('head').append(''); document.body.appendChild(chatAnchor); jQuery('#va_chatAnchor').append(appendStyle); // console.log("initial height", window.innerHeight); // console.log("Added VA Tab"); } /** * This function inserts an iFrame inside the chatAnchor div to load the chat body into the parent page. */ function insertIframe() { var iframe = document.createElement('iframe'); iframe.id = iframe.name = 'va_iframe'; iframe.className = 'va_iframe'; iframe.style.zIndex = 9999; iframe.style.background = '#E5E5E5'; iframe.style.right = calcRightMargin(); iframe.style.overflow = "hidden"; // We are relying on a global variable in the source page called vaPath that lets us know which VA to load. //console.log("About to append VA IFrame"); jQuery('#va_chatAnchor').append(iframe); //console.log("Added VA App within IFRAME"); } function determineContext() { if (!Drupal || !Drupal.settings || ! Drupal.settings.DHSAudience) return "general"; var audience = Drupal.settings.DHSAudience; if (!audience.length) return "general"; for (var i = 0; i < audience.length; i++) { switch (audience[i].toLowerCase()) { case "students and trainees": return "student"; case "job seekers": return "jobseeker"; case "families": return "families"; default: return audience[i].toLowerCase().split(' ').join('_'); } } return "general" } function getDHSSessionId() { if (jQuery.cookie) { return jQuery.cookie('sessionId') } else { // Backup should jQuery cookie include be removed from parent site let cookie = document.cookie if (!cookie) { return null } let sessionId = cookie.split(';') .map(function(cookie){ return cookie.trim().split('=')}) .filter(function(cookie){ return cookie[0]==='sessionId'}) if (sessionId.length === 1 && sessionId[0].length === 2) { return sessionId[0][1] } return null } } /** * This function inserts the virtual assistant iFrame source. */ function insertIframeSrc() { va_chatStarted = true; var context = determineContext(); var source = "https://samv4prod-webchat.azurewebsites.net?Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJESFMiLCJleHAiOjE2MzQ2Mjc3OTUsInN1YiI6InVzZXIiLCJpYXQiOjE2MzQ2MTY5OTV9.TvzYk0nNrk9p4pLj2ZClzehj9ZMMimZikQYIILS5ihY&Context=" + context; jQuery.when(jQuery("#va_iframe") .attr("aria-live", "assertive") .attr("aria-relevant", "additions") .attr("src", source + "#/wea")).done(function () { return true; }); } function sendGoogleAnalyltics(event) { try { if (ga) { var context = determineContext(); ga('send', 'event', 'Virtual Assistant', event, context + ' VA'); } } catch (error) { // ignore } } /** * This function pops up the chat and starts a conversation if the chat is not clicked after 20 seconds. */ function addTimer() { va_pushTimeout = setTimeout(function () { sendGoogleAnalyltics('Timeout'); postMessageToIframe('timeout'); va_pushTimeout = null; if (!va_isMobile) { jQuery('#va_chatHeader').click(); } else { jQuery('#va_mobileToggle').click(); } }, parseInt("600000", 10)); } /** * This function disables the background site from scrolling. * Used in mobile only when the chat is opened. */ function disableBGScrolling() { document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; } /** * This function enables the background site for scrolling. */ function enableBGScrolling() { document.body.style.overflow = ''; document.body.style.position = ''; } /************* Mobile functions *************/ /** * This function opens the chat to full screen/closes to minimised tab for mobile devices. */ function toggleVAMobile() { var toggleWidth = va_isOpen ? '0px' : '100%', toggleHeight = va_isOpen ? '0px' : (jQuery(window).height() - 50); jQuery('#va_iframe').css({ width: toggleWidth, height: toggleHeight, right: '0px', top: '50px' }); jQuery('#va_mobileBar').toggleClass('va_mobileLarge'); jQuery('#va_mobileToggle').toggleClass('va_open'); va_isOpen = !va_isOpen; setAltText("va_mobileToggle"); if (va_isOpen) { postMessageToIframe({"type":"vaOpen","context":determineContext(),"session": getDHSSessionId()}) disableBGScrolling(); } else { enableBGScrolling(); } } /** * This function adds an on-click event listener to toggle mobile chat. */ function addOnClickMobile() { jQuery('#va_mobileToggle').on('click', function () { sendGoogleAnalyltics('Mobile click'); if (va_pushTimeout) { // If there's still a proactive push timer, clear it. clearTimeout(va_pushTimeout); va_pushTimeout = null; } if (!va_chatStarted) { insertIframeSrc(); } toggleVAMobile(); }); } /** * Determine if the browser screen size is currently in moble format. */ function isWindowMobile() { if (typeof window.matchMedia !== "undefined") { return window.matchMedia("screen and (max-width: 599px), screen and (max-height: 490px)").matches; } // If mediaQuery is not supported, just go off the window dimensions. return jQuery(window).width() < 600; } /** * This function recalculates the height and width of the iFrame in mobile. */ function responsiveResize() { var isNewScreenMobile = isWindowMobile(); var responsiveHeight = 0, responsiveWidth = 0, responsiveRight = "", responsiveTop = "", responsiveBottom = "", rightMargin = calcRightMargin(); //Resize for mobile screens. if (isNewScreenMobile) { //Remove the desktop header, replace it with the mobile header. if (!va_isMobile) { jQuery("#va_chatHeader").replaceWith(getMobileHeader()); // Desktop explicitly sets #va_iframe to "display: none" when closed. Mobile does not. // Fix to clear display style when switching to mobile. jQuery('#va_iframe').css("display",""); addOnClickMobile(); } //If the chat is open, resize it. if (va_isOpen) { disableBGScrolling(); responsiveHeight = jQuery(window).height() - 50; responsiveWidth = jQuery(window).width(); responsiveRight = "0px"; responsiveTop = "50px"; } } //Else resize for desktop screens. else { enableBGScrolling(); //Remove mobile header and add desktop header. if (va_isMobile) { jQuery("#va_mobileBar").replaceWith(getDesktopHeader()); //Add onclick listeners. addOnClickDesktop(); addOnResize(); } else { //Reposition chat header if not mobile. jQuery("#va_chatHeader").css({ right: rightMargin }) } //If the chat is open, reposition it. if (va_isOpen) { responsiveBottom = "0px"; if (va_isMaximised) { responsiveHeight = 435; responsiveWidth = 600; } else { responsiveHeight = 400; responsiveWidth = 350; } } responsiveRight = rightMargin; } jQuery('#va_iframe').css({ height: responsiveHeight, width: responsiveWidth, right: responsiveRight, top: responsiveTop, bottom: responsiveBottom }); va_isMobile = isNewScreenMobile; } /********** Desktop functions **************/ /** * This function maximises/minimises the chat in desktop view. */ function toggleVADesktop() { var ariaHidden = va_isOpen ? 'true' : 'false'; if (va_isMaximised) { var toggleHeight = va_isOpen ? '0px' : '435px', toggleWidth = va_isOpen ? '0px' : '600px', headerHeight = va_isOpen ? '55px' : '490px', headerWidth = va_isOpen ? '350px' : '600px'; } else { var toggleHeight = va_isOpen ? '0px' : '400px', toggleWidth = va_isOpen ? '0px' : '350px', headerHeight = va_isOpen ? '55px' : '455px', headerWidth = '350px'; } if (!va_isOpen) { if (va_isMaximised) { jQuery('#va_iframe').animate({ height: toggleHeight, width: toggleWidth }); if (!jQuery('#va_resizeIcon').hasClass('va_large')) { jQuery('#va_resizeIcon').addClass('va_large'); } } else { jQuery('#va_iframe').animate({ height: toggleHeight }); jQuery('#va_iframe').css({ width: toggleWidth }); } jQuery('#va_iframe').css("display","inline-block"); postMessageToIframe({"type":"vaOpen","context":determineContext(),"session": getDHSSessionId()}) } else { jQuery('#va_iframe').animate({ height: toggleHeight, width: "350px" }); setTimeout(function () { jQuery('#va_iframe').css({ width: toggleWidth, display: "none" }); }, 500); } jQuery('#va_chatHeader').animate({ height: headerHeight, width: headerWidth }, {duration: 400, queue: false}); jQuery('.va_chatBorder').animate({ width: headerWidth }, {duration: 400, queue: false}); jQuery('#va_resizeIcon').toggle(); jQuery('#va_toggleIcon').toggleClass('va_expanded'); jQuery('#va_skipToConversation').toggleClass('active'); jQuery('#va_skipToConversation').attr("aria-hidden", ariaHidden); postMessageToIframe("setAriaHiddenTags" + ariaHidden); va_isOpen = !va_isOpen; setAltText("va_toggleIcon"); return true; } /** * This function adds an on-click listener to toggle the desktop chat. */ function addOnClickDesktop() { jQuery('#va_chatHeader').on('click', function (e) { sendGoogleAnalyltics('Desktop click'); if (va_pushTimeout) { // If there's still a proactive push timer, clear it. clearTimeout(va_pushTimeout); va_pushTimeout = null; } if (!va_chatStarted) { insertIframeSrc(); } toggleVADesktop(); return false; }); //Add listener to trigger click when enter is pressed and element focused. jQuery('#va_toggleIcon').bind('keyup', function (e) { if (e.keyCode === 13) { // 13 is enter key jQuery('#va_chatHeader').click(); } }); //console.log("Added VA desktop on click event listener"); } /** * This function resizes the chat when the resize button is clicked. */ function addOnResize() { jQuery('#va_resizeIcon').on('click', function (e) { resizeVA(); //Prevent other handlers from firing. e.stopPropagation(); return false; }); //Add listener to trigger click when enter is pressed and element focused. jQuery('#va_resizeIcon').bind('keyup', function (e) { if (e.keyCode === 13) { // 13 is enter key jQuery('#va_resizeIcon').click(); e.stopPropagation(); } }); //console.log("Added VA resize event listener"); } /** * This function resizes the VA on desktop to it's large/small size */ function resizeVA() { var resizeWidth = va_isMaximised ? '350px' : '600px', resizeHeight = va_isMaximised ? '400px' : '435px', resizeHeaderHeight = va_isMaximised ? '455px' : '490px', resizeHeaderWidth = va_isMaximised ? '350px' : '600px'; jQuery('#va_chatHeader').animate({ width: resizeHeaderWidth, height: resizeHeaderHeight }, {duration: 400, queue: false}); jQuery('.va_chatBorder').animate({ width: resizeHeaderWidth }, {duration: 400, queue: false}); jQuery('#va_iframe').animate({ height: resizeHeight, width: resizeWidth }, {duration: 400, queue: false}); jQuery('#va_resizeIcon').toggleClass('va_large'); va_isMaximised = !va_isMaximised; setAltText("va_resizeIcon"); } /** TABBING AND FOCUS FUNCTIONS **/ /** * This function enables backwards tabbing through the virtual assistant tab order. */ function addOnBackwardsTabbing() { jQuery('#va_skipToConversation').unbind('keydown'); jQuery('#va_skipToConversation').bind('keydown', function (e) { if (e.shiftKey && e.keyCode === 9) { // 9 is tab postMessageToIframe("loopFocusBackward"); e.preventDefault(); } else if (e.keyCode === 13) { postMessageToIframe("skiptoConversation"); e.stopPropagation(); } }); } /** * This function loops backwards tabbing when the error message is present. */ function loopFocusBackwardWithError() { jQuery('#va_resizeIcon').bind('keydown', function (e) { if (e.shiftKey && e.keyCode === 9) { // 9 is tab + shift postMessageToIframe("loopFocusBackwardWithError"); e.preventDefault(); } }); } /** * This function adds listeners for tabbing forwards and backwards from the skiptoconversation link */ function addSkipToConversationTabListener() { jQuery('#va_skipToConversation').unbind('keydown'); jQuery('#va_skipToConversation').bind('keydown', function (e) { if (e.shiftKey && e.keyCode === 9) { // 9 is tab postMessageToIframe("loopFocusBackwardOptionButton"); e.stopPropagation(); } else if (e.keyCode === 13) { postMessageToIframe("skiptoConversation"); e.stopPropagation(); } }); } /** * This function adds an on-click event listener to the Skip To Conversation link in the parent window. */ function addOnSkipToConversation() { jQuery('#va_skipToConversation').on('click', function (e) { postMessageToIframe("skiptoConversation"); e.stopPropagation(); return false; }); addOnBackwardsTabbing(); } /** * This function adds a "Skip to..." link in the parent window. */ function addSkipToBot() { var skipLinks = jQuery('.skip-links'); var skipLinkClass = ""; if (skipLinks.length==0) { skipLinks = jQuery('.uikit-skip-link'); skipLinkClass = 'class="uikit-skip-link__link"'; } skipLinks.append('skip to ' + titleBarTitle + ''); jQuery('#va_skipToBot').on('click', function (e) { sendGoogleAnalyltics('Skip to bot'); //If VA is closed, toggle it open if (!va_isOpen) { if (va_isMobile) { toggleVAMobile(); } else { toggleVADesktop(); } } //postMessageToIframe("skiptoConversation"); jQuery("#va_chatAnchor").focus(); e.stopPropagation(); return false; }); } /** * This function hides the Skip To Conversation link. */ function hideSkipToConversation() { jQuery('#va_skipToConversation').hide(); jQuery('#va_skipToConversation').attr("aria-hidden", "true"); } /** * This function moves focus to the Skip to Conversation link element. */ function skipToSkipToConversation() { jQuery("#va_skipToConversation").focus(); } /** * This function moves focus to the Resize Icon. */ function skipToResizeIcon() { jQuery("#va_resizeIcon").focus(); } /** COMMUNICATION WITH IFRAME **/ /** * This function posts messages to the iFrame to execute functions within the iFrame */ function postMessageToIframe(message) { window.parent.document.getElementById("va_iframe").contentWindow.postMessage(message, '*'); } /** * This function adds a listener to the iFrame, which listens for messages from the iFrame and triggers appropriate functions. */ function addListenerForIframe() { window.addEventListener('message', function (e) { if (e.data && e.data.type == "error") { e.stopPropagation(); jQuery("#va_chatAnchor").hide(); } else if (e.data && e.data.type == "ready") { e.stopPropagation(); jQuery("#va_chatAnchor").show(); } else if (e.data && e.data.type == "reload") { e.stopPropagation(); if (va_isOpen) { if (va_isMobile) { toggleVAMobile(); } else { toggleVADesktop(); } } } else if (e.data == "loopFocusForward") { skipToSkipToConversation(); e.stopPropagation(); } else if (e.data == "loopFocusForwardError") { setTimeout(function () { skipToResizeIcon(); }, 100); e.stopPropagation(); } else if (e.data == "addOnBackwardsTabbing") { addOnBackwardsTabbing(); } else if (e.data == "loopFocusForwardOptionButton") { skipToSkipToConversation(); addSkipToConversationTabListener(); } else if (e.data == "errorDisplayed") { hideSkipToConversation(); loopFocusBackwardWithError(); } else if (e.data == "textboxFocus") { setTimeout(function () { scrollPageToBottom(); }, 100); } }); } /** ADD VA **/ /** * This function is used to add the virtual assistant to a page. */ function addVirtualAssistant() { //console.log("VA: addVirtualAssistant started"); va_isMobile = isWindowMobile(); //Add the tab. insertDiv(); insertIframe(); // preload insertIframeSrc(); //Event listeners. addOnResize(); addSkipToBot(); addOnSkipToConversation(); addListenerForIframe(); addOnClickDesktop(); addOnClickMobile(); addTimer(); //Add an event listener to fire responsiveResize() on every resize event. window.addEventListener("resize", responsiveResize); //console.log("VA: addVirtualAssistant complete"); } /** OTHER FUNCTIONS **/ /** * This function scrolls the page to the bottom */ function scrollPageToBottom() { window.scrollTo(0, document.body.scrollHeight); } /* * This function returns the IE version, if not IE, returns false */ function isIE() { var myNav = navigator.userAgent.toLowerCase(); return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false; } //On load, add the virtual assistant to the page. window.onload = addVirtualAssistant; console.log("VA File loaded");