let uneeqFrame; let uneeqSessionLive = false; let uneeqState = 'Initial'; let uneeqFrameReady = false; window.addEventListener('load', (event) => { uneeqAddFrame(); // Setup message event handler so we can get messages passed up from the iframe to the client's web page if (window.addEventListener) { window.addEventListener('message', uneeqOnMessage, false); } }); /** * Adds iFrame for DH */ function uneeqAddFrame() { if ( window.uneeqInteractionsOptions && isShareIdValid(window.uneeqInteractionsOptions.personaShareId) ) { uneeqFrame = document.createElement('iframe'); uneeqFrame.id = 'uneeqFrame'; uneeqFrame.src = `https://interactions.us.uneeq.io/interactions/`; const iFrameStyle = uneeqFrame.style; iFrameStyle.position = 'fixed'; iFrameStyle.bottom = '0'; if (uneeqIsMobileLayoutModeEnabled() || (window.uneeqInteractionsOptions.layoutMode !== 'splitScreen' && window.uneeqInteractionsOptions.position !== 'left')) { // Only necessary for Overlay & splitScreen in left positions, or mobile iFrameStyle.right = '0'; } iFrameStyle.width = '0px'; iFrameStyle.height = '0px'; iFrameStyle.zIndex = '99999'; iFrameStyle.border = 'none'; iFrameStyle.maxWidth = '100%'; uneeqFrame.sandbox = 'allow-scripts allow-same-origin allow-modals allow-top-navigation allow-popups ' + 'allow-presentation allow-popups-to-escape-sandbox'; uneeqFrame.allow = 'camera;microphone;autoplay;'; document.body.appendChild(uneeqFrame); // Add view port tag if it doesn't exist - for mobile layout support if ( document.querySelector('meta[name="viewport"]') === null ) { const viewportTag = document.createElement('meta'); viewportTag.name = 'viewport'; viewportTag.content = 'width=device-width, initial-scale=1.0'; document.head.appendChild(viewportTag); } } else { console.error('UneeQ Interactions experience could not be loaded: window.uneeqInteractionsOptions.personaShareId not valid.' + ' Please add the following script to the head of your page:'); const codeHint = ''; console.error(codeHint); } } /** * uneeqPostToFrame * @param {string} type The message type * @param {any} value The value to be passed with the message */ function uneeqPostToFrame(type, value) { if ( uneeqFrame ) { uneeqFrame.contentWindow.postMessage({ type: type, value: value, }, '*'); } } /** * Handles events from the iFrame * @param {event} event passed from the iFrame */ function uneeqOnMessage(event) { if ( !event || !event.data || !event.data.msg || !event.data.msg.uneeqMessageType ) { // the message is not for UneeQ return; } const uneeqMsg = event.data.msg; switch (uneeqMsg.uneeqMessageType) { case 'FrameReady': uneeqFrameReadyHandler(); break; case 'FrameResize': uneeqFrame = document.getElementById('uneeqFrame'); Object.assign(uneeqFrame.style, uneeqMsg.style); if (uneeqMsg.document) Object.assign(document.body.style, uneeqMsg.document.style); break; case 'SessionStateUpdate': uneeqSessionLive = uneeqMsg.live; uneeqState = uneeqMsg.state; const uneeqLiveUpdateMessage = new CustomEvent('uneeqSessionStateUpdate', {detail: {live: uneeqSessionLive, state: uneeqState}} ); window.dispatchEvent(uneeqLiveUpdateMessage); // to be deprecated uneeqSendEventMessage({ uneeqMessageType: 'SessionStateUpdate', live: uneeqSessionLive, state: uneeqState, timestamp: uneeqMsg.timestamp, }); break; case 'SessionId': const uneeqSessionIdMsg = new CustomEvent('uneeqSessionId', {detail: {uneeQSessionId: uneeqMsg.sessionId}}); window.dispatchEvent(uneeqSessionIdMsg); // to be deprecated uneeqSendEventMessage(uneeqMsg); break; case 'Notification': alert(uneeqMsg.msg); break; case 'WebRtcData': // NO OP: consume message so it is not send through to parent page break; default: const uneeqEventMsg = new CustomEvent('uneeqEvent', {detail: uneeqMsg}); window.dispatchEvent(uneeqEventMsg); // to be deprecated uneeqSendEventMessage(uneeqMsg); break; } } /** * Handler for when UneeQ frame is ready to process messages. */ function uneeqFrameReadyHandler() { uneeqFrameReady = true; // Pass up all uneeqInteractionsOptions uneeqPostToFrame('uneeqInteractionsOptions', window.uneeqInteractionsOptions); uneeqPostToFrame('deployComplete', true); uneeqInitResizeHandler(); uneeqHandleClientResize(); const uneeqEventMsg = new CustomEvent('uneeqEvent', {detail: {uneeqMessageType: 'Ready'}}); window.dispatchEvent(uneeqEventMsg); } /** * @param {CustomEventInit} eventDetail */ function uneeqSendEventMessage(eventDetail) { const verboseLoggingEnabled = (window.uneeqInteractionsOptions && window.uneeqInteractionsOptions.verboseLogging === undefined) ? false : window.uneeqInteractionsOptions.verboseLogging; if (verboseLoggingEnabled) { console.debug('UneeQ Message: ', eventDetail); } const event = new CustomEvent('UneeqMessage', {detail: eventDetail}); window.dispatchEvent(event); } /** * uneeqIsMobileLayoutModeEnabled * * Initialised Mobile Layout if not configured * Checks if client's pixels width is less than configured mobile width breakpoint * @return {boolean} */ function uneeqIsMobileLayoutModeEnabled() { if (!window.uneeqInteractionsOptions.mobileViewWidthBreakpoint) { window.uneeqInteractionsOptions.mobileViewWidthBreakpoint = 900; } return document.body.clientWidth <= window.uneeqInteractionsOptions.mobileViewWidthBreakpoint; } /** * uneeqHandleClientResize */ function uneeqHandleClientResize() { if (uneeqFrameReady) { uneeqPostToFrame('setMobileLayoutMode', uneeqIsMobileLayoutModeEnabled()); } } /** * uneeqInitResizeHandler */ function uneeqInitResizeHandler() { uneeqIsMobileLayoutModeEnabled(); let resizeTimeoutId = false; // holder for timeout id const resizeDebounceMs = 250; // delay after event is "complete" to run callback window.addEventListener('resize', () => { // clear the timeout clearTimeout(resizeTimeoutId); // start timing for event "completion" resizeTimeoutId = setTimeout(uneeqHandleClientResize, resizeDebounceMs); }); } /** * Ask a question * @param {question} question intent to be sent to the NLP */ function uneeqAsk(question) { uneeqPostToFrame('askQuestion', question); } /** * Start a session (must be used after user click/key event */ function uneeqStartSession() { uneeqPostToFrame('startSession', null); } /** * Enable the users microphone for audio recording */ function uneeqEnableMicrophone() { uneeqPostToFrame('enableMicrophone', null); } /** * Sets the call to action text * @param {callToActionText} callToActionText */ function uneeqSetCallToActionText(callToActionText) { uneeqPostToFrame('setCallToActionText', callToActionText); } /** * Opens the Start Session Popup to allow a user to start a session * @param {popupProperties} popupProperties */ function uneeqOpenStartSessionPopup(popupProperties) { uneeqPostToFrame('openStartSessionPopup', popupProperties); } /** * End Session */ function uneeqEndSession() { uneeqPostToFrame('endSession', null); } /** * Stop Speaking */ function uneeqStopSpeaking() { uneeqPostToFrame('stopSpeaking', null); } /** * Show the voice recording UI [deprecated] * @param {placeHolder} placeHolder for the input text box */ function uneeqShowVoiceInput(placeHolder) { // TODO upon deprecation also remove setTextInputPlaceholder message handling from Interactions app. if ( placeHolder !== null || placeHolder !== undefined ) { uneeqPostToFrame('setTextInputPlaceholder', {placeholder: placeHolder}); } uneeqSetShowUserInputInterface(true); } /** * Hide the voice recording UI [deprecated] */ function uneeqHideVoiceInput() { uneeqSetShowUserInputInterface(false); } /** * Show or hide the voice/text input interface * @param {boolean} show */ function uneeqSetShowUserInputInterface(show) { uneeqPostToFrame('setShowUserInputInterface', {show: show}); } /** * Set the layout mode ( 'overlay' | 'splitScreen' | 'fullScreen' | 'contained' ) * @param {string} layoutMode */ function uneeqSetLayoutMode(layoutMode) { uneeqPostToFrame('setLayoutMode', {layoutMode: layoutMode}); } /** * Start recording voice */ function uneeqStartRecording() { uneeqPostToFrame('startRecording', null); } /** * Stop recording voice */ function uneeqStopRecording() { uneeqPostToFrame('stopRecording', null); } /** Validates a shareId * @param {personaShareId} personaShareId to be validated * @return {boolean} */ function isShareIdValid(personaShareId) { const validShareId = RegExp(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/).test(personaShareId); return validShareId; }