UNPKG

amazon-connect-streams

Version:
1,493 lines (1,327 loc) 1.03 MB
/******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 94 () { /* * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ (function () { var global = this || globalThis; var connect = global.connect || {}; global.connect = connect; global.lily = connect; var userAgent = navigator.userAgent; var ONE_DAY_MILLIS = 24 * 60 * 60 * 1000; var DEFAULT_POPUP_HEIGHT = 578; var DEFAULT_POPUP_WIDTH = 433; var COPYABLE_EVENT_FIELDS = ["bubbles", "cancelBubble", "cancelable", "composed", "data", "defaultPrevented", "eventPhase", "isTrusted", "lastEventId", "origin", "returnValue", "timeStamp", "type"]; /** * Unpollute sprintf functions from the global namespace. */ connect.sprintf = global.sprintf; connect.vsprintf = global.vsprintf; delete global.sprintf; delete global.vsprintf; connect.HTTP_STATUS_CODES = { SUCCESS: 200, UNAUTHORIZED: 401, ACCESS_DENIED: 403, TOO_MANY_REQUESTS: 429, INTERNAL_SERVER_ERROR: 500 }; connect.TRANSPORT_TYPES = { CHAT_TOKEN: "chat_token", WEB_SOCKET: "web_socket", AGENT_DISCOVERY: "agent_discovery", WEB_RTC: "web_rtc" }; /** * Binds the given instance object as the context for * the method provided. * * @param scope The instance object to be set as the scope * of the function. * @param method The method to be encapsulated. * * All other arguments, if any, are bound to the method * invocation inside the closure. * * @return A closure encapsulating the invocation of the * method provided in context of the given instance. */ connect.hitch = function () { var args = Array.prototype.slice.call(arguments); var scope = args.shift(); var method = args.shift(); connect.assertNotNull(scope, 'scope'); connect.assertNotNull(method, 'method'); connect.assertTrue(connect.isFunction(method), 'method must be a function'); return function () { var closureArgs = Array.prototype.slice.call(arguments); return method.apply(scope, args.concat(closureArgs)); }; }; /** * Determine if the given value is a callable function type. * Borrowed from Underscore.js. */ connect.isFunction = function (obj) { return !!(obj && obj.constructor && obj.call && obj.apply); }; /** * Determine if the given value is an array. */ connect.isArray = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; /** * Get a list of keys from a Javascript object used * as a hash map. */ connect.keys = function (map) { var keys = []; connect.assertNotNull(map, 'map'); for (var k in map) { keys.push(k); } return keys; }; /** * Get a list of values from a Javascript object used * as a hash map. */ connect.values = function (map) { var values = []; connect.assertNotNull(map, 'map'); for (var k in map) { values.push(map[k]); } return values; }; /** * Get a list of key/value pairs from the given map. */ connect.entries = function (map) { var entries = []; for (var k in map) { entries.push({ key: k, value: map[k] }); } return entries; }; /** * Merge two or more maps together into a new map, * or simply copy a single map. */ connect.merge = function () { var argMaps = Array.prototype.slice.call(arguments, 0); var resultMap = {}; argMaps.forEach(function (map) { connect.entries(map).forEach(function (kv) { resultMap[kv.key] = kv.value; }); }); return resultMap; }; connect.now = function () { return new Date().getTime(); }; connect.find = function (array, predicate) { for (var x = 0; x < array.length; x++) { if (predicate(array[x])) { return array[x]; } } return null; }; connect.contains = function (obj, value) { if (obj instanceof Array) { return connect.find(obj, function (v) { return v === value; }) != null; } else { return (value in obj); } }; connect.containsValue = function (obj, value) { if (obj instanceof Array) { return connect.find(obj, function (v) { return v === value; }) != null; } else { return connect.find(connect.values(obj), function (v) { return v === value; }) != null; } }; /** * Generate a random ID consisting of the current timestamp * and a random base-36 number based on Math.random(). */ connect.randomId = function () { return connect.sprintf("%s-%s", connect.now(), Math.random().toString(36).slice(2)); }; /** * Generate an enum from the given list of lower-case enum values, * where the enum keys will be upper case. * * Conversion from pascal case based on code from here: * http://stackoverflow.com/questions/30521224 */ connect.makeEnum = function (values) { var enumObj = {}; values.forEach(function (value) { var key = value.replace(/\.?([a-z]+)_?/g, function (x, y) { return y.toUpperCase() + "_"; }) .replace(/_$/, ""); enumObj[key] = value; }); return enumObj; }; connect.makeNamespacedEnum = function (prefix, values) { var enumObj = connect.makeEnum(values); connect.keys(enumObj).forEach(function (key) { enumObj[key] = connect.sprintf("%s::%s", prefix, enumObj[key]); }); return enumObj; }; connect.makeGenericNamespacedEnum = function (prefix, values, delimiter) { var enumObj = connect.makeEnum(values); connect.keys(enumObj).forEach(function (key) { enumObj[key] = connect.sprintf("%s"+delimiter+"%s", prefix, enumObj[key]); }); return enumObj; }; /** * Methods to determine browser type and versions, used for softphone initialization. */ /* This will also return True for Edge and Opera since they include Chrome in the user agent string, as they are built off of Chrome. */ connect.isChromeBrowser = function () { return userAgent.indexOf("Chrome") !== -1; }; connect.isFirefoxBrowser = function () { return userAgent.indexOf("Firefox") !== -1; }; connect.isOperaBrowser = function () { return userAgent.indexOf("Opera") !== -1; }; connect.isEdgeBrowser = function () { return userAgent.indexOf("Edg") !== -1; }; connect.getChromeBrowserVersion = function () { var chromeVersion = userAgent.substring(userAgent.indexOf("Chrome") + 7); if (chromeVersion) { return parseFloat(chromeVersion); } else { return -1; } }; connect.getFirefoxBrowserVersion = function () { var firefoxVersion = userAgent.substring(userAgent.indexOf("Firefox") + 8); if (firefoxVersion) { return parseFloat(firefoxVersion); } else { return -1; } }; connect.isValidLocale = function (locale) { var languages = [ { id: 'en_US', label: 'English' }, { id: 'de_DE', label: 'Deutsch' }, { id: 'es_ES', label: 'Español' }, { id: 'fr_FR', label: 'Français' }, { id: 'ja_JP', label: '日本語' }, { id: 'it_IT', label: 'Italiano' }, { id: 'ko_KR', label: '한국어' }, { id: 'pt_BR', label: 'Português' }, { id: 'zh_CN', label: '中文(简体)' }, { id: 'zh_TW', label: '中文(繁體)' } ]; return languages.map(function(language){ return language.id}).includes(locale); } connect.getOperaBrowserVersion = function () { var versionOffset = userAgent.indexOf("Opera"); var operaVersion = (userAgent.indexOf("Version") !== -1) ? userAgent.substring(versionOffset + 8) : userAgent.substring(versionOffset + 6); if (operaVersion) { return parseFloat(operaVersion); } else { return -1; } }; /** * Return a map of items in the given list indexed by * keys determined by the closure provided. * * @param iterable A list-like object. * @param closure A closure to determine the index for the * items in the iterable. * @return A map from index to item for each item in the iterable. */ connect.index = function (iterable, closure) { var map = {}; iterable.forEach(function (item) { map[closure(item)] = item; }); return map; }; /** * Converts the given array into a map as a set, * where elements in the array are mapped to 1. */ connect.set = function (arrayIn) { var setMap = {}; arrayIn.forEach(function (key) { setMap[key] = 1; }); return setMap; }; /** * Returns a map for each key in mapB which * is NOT in mapA. */ connect.relativeComplement = function (mapA, mapB) { var compMap = {}; connect.keys(mapB).forEach(function (key) { if (!(key in mapA)) { compMap[key] = mapB[key]; } }); return compMap; }; /** * Asserts that a premise is true. */ connect.assertTrue = function (premise, message) { if (!premise) { throw new connect.ValueError(message); } }; /** * Asserts that a value is not null or undefined. */ connect.assertNotNull = function (value, name) { connect.assertTrue(value != null && typeof value !== undefined, connect.sprintf("%s must be provided", name || 'A value')); return value; }; connect.deepcopy = function (src) { return JSON.parse(JSON.stringify(src)); }; connect.deepcopyCrossOriginEvent = function(event) { const obj = {}; const listOfAcceptableKeys = COPYABLE_EVENT_FIELDS; listOfAcceptableKeys.forEach((key) => { try { obj[key] = event[key]; } catch(e) { connect.getLog().info("deepcopyCrossOriginEvent failed on key: ", key).sendInternalLogToServer(); } }); return connect.deepcopy(obj); } /** * Get the current base url of the open page, e.g. if the page is * https://example.com:9494/oranges, this will be "https://example.com:9494". */ connect.getBaseUrl = function () { var location = global.location; return connect.sprintf("%s//%s:%s", location.protocol, location.hostname, location.port); }; connect.getUrlWithProtocol = function(url) { var protocol = global.location.protocol; if (url.substr(0, protocol.length) !== protocol) { return connect.sprintf("%s//%s", protocol, url); } return url; } /** * Determine if the current window is in an iframe. * Courtesy: http://stackoverflow.com/questions/326069/ */ connect.isFramed = function () { try { return window.self !== window.top; } catch (e) { return true; } }; connect.hasOtherConnectedCCPs = function () { return connect.numberOfConnectedCCPs > 1; } connect.fetch = function (endpoint, options, milliInterval, maxRetry) { maxRetry = maxRetry || 5; milliInterval = milliInterval || 1000; options = options || {}; return new Promise(function (resolve, reject) { function fetchData(maxRetry) { fetch(endpoint, options).then(function (res) { if (res.status === connect.HTTP_STATUS_CODES.SUCCESS) { res.json().then(json => resolve(json)).catch(() => resolve({})); } else if (maxRetry !== 1 && (res.status >= connect.HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR || res.status === connect.HTTP_STATUS_CODES.TOO_MANY_REQUESTS)) { setTimeout(function () { fetchData(--maxRetry); }, milliInterval); } else { reject(res); } }).catch(function (e) { reject(e); }); } fetchData(maxRetry); }); }; connect.fetchWithTimeout = async function(endpoint, timeoutMs, options, milliInterval, maxRetry) { options = options || {}; if (!timeoutMs) { return connect.fetch(endpoint, options, milliInterval, maxRetry); } const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeoutMs); const response = await connect.fetch(endpoint, { ...options, signal: controller.signal }, milliInterval, maxRetry); clearTimeout(id); return response; } /** * Calling a function with exponential backoff with full jitter retry strategy * It will retry calling the function for maximum maxRetry times if it fails. * Success callback will be called if the function succeeded. * Failure callback will be called only if the last try failed. */ connect.backoff = function (func, milliInterval, maxRetry, callbacks) { connect.assertTrue(connect.isFunction(func), "func must be a Function"); var self = this; var ratio = 2; func({ success: function (data) { if (callbacks && callbacks.success) { callbacks.success(data); } }, failure: function (err, data) { if (maxRetry > 0) { var interval = milliInterval * 2 * Math.random(); global.setTimeout(function () { self.backoff(func, interval * ratio, --maxRetry, callbacks); }, interval); } else { if (callbacks && callbacks.failure) { callbacks.failure(err, data); } } } }); }; connect.publishMetric = function (metricData) { connect.core.getUpstream().sendUpstream(connect.EventType.BROADCAST, { event: connect.EventType.CLIENT_METRIC, data: metricData }); }; connect.publishSoftphoneStats = function(stats) { connect.core.getUpstream().sendUpstream(connect.EventType.BROADCAST, { event: connect.EventType.SOFTPHONE_STATS, data: stats }); }; connect.publishSoftphoneReport = function(report) { connect.core.getUpstream().sendUpstream(connect.EventType.BROADCAST, { event: connect.EventType.SOFTPHONE_REPORT, data: report }); }; connect.publishClickStreamData = function(report) { connect.core.getUpstream().sendUpstream(connect.EventType.BROADCAST, { event: connect.EventType.CLICK_STREAM_DATA, data: report }); }; connect.publishClientSideLogs = function(logs) { var bus = connect.core.getEventBus(); bus.trigger(connect.EventType.CLIENT_SIDE_LOGS, logs); }; connect.addNamespaceToLogs = function(namespace) { const methods = ['log', 'error', 'warn', 'info', 'debug']; methods.forEach((method) => { const consoleMethod = window.console[method]; window.console[method] = function () { const args = Array.from(arguments); args.unshift(`[${namespace}]`); consoleMethod.apply(window.console, args); }; }); }; /** * A wrapper around Window.open() for managing single instance popups. * Tracks window references to enable window reuse without reload and provides security. */ connect.PopupManager = function () { this.windows = {}; this.windowUrls = {}; }; connect.PopupManager.prototype.open = function (url, name, options) { var features = ''; if (options) { // default values are chosen to provide a minimum height without scrolling // and a uniform margin based on the css of the ccp login page var height = options.height || DEFAULT_POPUP_HEIGHT; var width = options.width || DEFAULT_POPUP_WIDTH; var top = options.top || 0; var left = options.left || 0; features = "width="+width+", height="+height+", top="+top+", left="+left; } var cachedWin = this.windows[name]; var cachedUrl = this.windowUrls[name]; // Check if window exists and is still open if (cachedWin) { try { if (!cachedWin.closed) { if (cachedUrl === url) { // Same URL - reuse existing window without reload try { cachedWin.focus(); } catch (e) { // Focus may fail due to browser restrictions, this is acceptable } connect.getLog().info("[PopupManager] Reusing existing popup window").sendInternalLogToServer(); return cachedWin; } } else { // Window was closed, clean up tracking delete this.windows[name]; delete this.windowUrls[name]; } } catch (e) { // Can't access window (likely cross-origin), assume closed and clean up delete this.windows[name]; delete this.windowUrls[name]; } } // Open new window directly to URL var win = window.open(url, name, features); if (win) { // Check if window is already closed (edge case in testing scenarios) try { if (!win.closed) { this.windows[name] = win; this.windowUrls[name] = url; connect.getLog().info("[PopupManager] Opened popup window").withObject({url: url, name: name}).sendInternalLogToServer(); } } catch (e) { // Can't access window properties, but still return it } } return win; }; connect.PopupManager.prototype.clear = function (name) { // Clean up window tracking delete this.windows[name]; delete this.windowUrls[name]; // Clean up localStorage var key = this._getLocalStorageKey(name); global.localStorage.removeItem(key); }; connect.PopupManager.prototype._getLastOpenedTimestamp = function (name) { var key = this._getLocalStorageKey(name); var value = global.localStorage.getItem(key); if (value) { return parseInt(value, 10); } else { return 0; } }; connect.PopupManager.prototype._setLastOpenedTimestamp = function (name, ts) { var key = this._getLocalStorageKey(name); global.localStorage.setItem(key, '' + ts); }; connect.PopupManager.prototype._getLocalStorageKey = function (name) { return "connectPopupManager::" + name; }; /** * An enumeration of the HTML5 notification permission values. */ var NotificationPermission = connect.makeEnum([ 'granted', 'denied', 'default' ]); /** * A simple engine for showing notification popups. */ connect.NotificationManager = function () { this.queue = []; this.permission = NotificationPermission.DEFAULT; }; connect.NotificationManager.prototype.requestPermission = function () { var self = this; if (!("Notification" in global)) { connect.getLog().warn("This browser doesn't support notifications.").sendInternalLogToServer(); this.permission = NotificationPermission.DENIED; } else if (global.Notification.permission === NotificationPermission.DENIED) { connect.getLog().warn("The user has requested to not receive notifications.").sendInternalLogToServer(); this.permission = NotificationPermission.DENIED; } else if (this.permission !== NotificationPermission.GRANTED) { global.Notification.requestPermission().then(function (permission) { self.permission = permission; if (permission === NotificationPermission.GRANTED) { self._showQueued(); } else { self.queue = []; } }); } }; connect.NotificationManager.prototype.show = function (title, options) { if (this.permission === NotificationPermission.GRANTED) { return this._showImpl({ title: title, options: options }); } else if (this.permission === NotificationPermission.DENIED) { connect.getLog().warn("Unable to show notification.") .sendInternalLogToServer() .withObject({ title: title, options: options }); } else { var params = { title: title, options: options }; connect.getLog().warn("Deferring notification until user decides to allow or deny.") .withObject(params) .sendInternalLogToServer(); this.queue.push(params); } }; connect.NotificationManager.prototype._showQueued = function () { var self = this; var notifications = this.queue.map(function (params) { return self._showImpl(params); }); this.queue = []; return notifications; }; connect.NotificationManager.prototype._showImpl = function (params) { var notification = new global.Notification(params.title, params.options); if (params.options.clicked) { notification.onclick = function () { params.options.clicked.call(notification); }; } return notification; }; connect.ValueError = function () { var args = Array.prototype.slice.call(arguments, 0); var format = args.shift(); var instance = new Error(connect.vsprintf(format, args)); Object.setPrototypeOf(instance, connect.ValueError.prototype); return instance; }; Object.setPrototypeOf(connect.ValueError.prototype, Error.prototype); Object.setPrototypeOf(connect.ValueError, Error); connect.ValueError.prototype.name = 'ValueError'; connect.NotImplementedError = function () { var args = Array.prototype.slice.call(arguments, 0); var format = args.shift(); var instance = new Error(connect.vsprintf(format, args)); Object.setPrototypeOf(instance, connect.NotImplementedError.prototype); return instance; }; Object.setPrototypeOf(connect.NotImplementedError.prototype, Error.prototype); Object.setPrototypeOf(connect.NotImplementedError, Error); connect.NotImplementedError.prototype.name = 'NotImplementedError'; connect.StateError = function () { var args = Array.prototype.slice.call(arguments, 0); var format = args.shift(); var instance = new Error(connect.vsprintf(format, args)); Object.setPrototypeOf(instance, connect.StateError.prototype); return instance; } Object.setPrototypeOf(connect.StateError.prototype, Error.prototype); Object.setPrototypeOf(connect.StateError, Error); connect.StateError.prototype.name = 'StateError'; connect.VoiceIdError = function(type, message, err){ var error = {}; error.type = type; error.message = message; error.stack = Error(message).stack; error.err = err; return error; } // internal use only connect.isCCP = function () { if (!connect.core.upstream) { return false; } var conduit = connect.core.getUpstream(); return conduit.name === 'ConnectSharedWorkerConduit'; } connect.isSharedWorker = function () { return connect.worker && !!connect.worker.clientEngine; } connect.isCRM = function () { if (!connect.core.upstream) { return false; } return connect.core.getUpstream() instanceof connect.IFrameConduit || connect.core.getUpstream() instanceof connect.GRProxyIframeConduit; } // internal use only connect.isActiveConduit = function (conduit) { const grProxyConduit = connect.core.getUpstream(); if (grProxyConduit instanceof connect.GRProxyIframeConduit) { return conduit.name === grProxyConduit.activeRegionUrl; } else { connect.getLog().debug('connect.isActiveConduit is called but there is no GR proxy conduit').sendInternalLogToServer(); return true; } } })(); /***/ }, /***/ 96 () { /* * Copyright 2014-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 * * Note: load utils before core.js */ (function () { var global = this || globalThis; var connect = global.connect || {}; var globalConnect = global.globalConnect || {}; global.connect = connect; global.globalConnect = globalConnect; global.lily = connect; globalConnect.core = {}; var IFRAME_STYLE = "margin: 0; border: 0; padding:0px;width: 0px;height: 0px"; var GLOBALIFRAME_STYLE = "margin: 0; border: 0; padding:0px;width: 100%;height: 100%"; var GLOBALIFRAME_ID = "globalCCP"; var DIV_DEFAULT_HEIGHT = { height: "465px", }; globalConnect.extractCcpRegionParams = function (globalContainerDiv, paramsIn) { connect.assertNotNull(paramsIn.standByRegion, "ccpBackupResource"); connect.assertNotNull(paramsIn.standByRegion.ccpUrl, "ccpUrl"); connect.assertNotNull(paramsIn.standByRegion.region, "region"); if (paramsIn.pollForFailover) { connect.assertNotNull(paramsIn.loginUrl, "loginUrl"); connect.assertNotNull(paramsIn.instanceArn, "primary ARN"); paramsIn.otherArn = connect.assertNotNull(paramsIn.standByRegion.instanceArn, "backup ARN"); } var regionAParams = paramsIn; var regionBParams = Object.assign({}, paramsIn, { ccpUrl: paramsIn.standByRegion.ccpUrl, region: paramsIn.standByRegion.region, instanceArn: paramsIn.standByRegion.instanceArn, otherArn: paramsIn.instanceArn, loginPopup: false }); var divStyle = extractDivStyle(globalContainerDiv); if (parseInt(divStyle.height) <= 0) { globalContainerDiv.style.height = DIV_DEFAULT_HEIGHT.height; // populating ccp divStyle.height = DIV_DEFAULT_HEIGHT.height; } return [regionAParams, regionBParams].map(function (params) { connect.assertNotNull(params.ccpUrl, "ccpUrl"); connect.assertNotNull(params.region, "region"); delete params.standByRegion; //signal CCP as part of a disaster recovery fleet params.disasterRecoveryOn = true; params.iframe_style = IFRAME_STYLE; params.height = divStyle.height; return params; }); }; var extractDivStyle = function (globalContainerDiv) { var style = window.getComputedStyle(globalContainerDiv); return { height: style.getPropertyValue("height"), width: style.getPropertyValue("width"), display: style.getPropertyValue("display"), }; }; var validateRegion = function (region, availableRegions) { connect.assertTrue( typeof region == "string", "Region provided " + region + " is not a valid string" ); var regions = availableRegions || globalConnect.core.regions; if (!regions.hasOwnProperty(region)) { var message = "Region provided " + region + " is not found!"; throw new connect.ValueError(message); } }; /** * Particular to DR, the getPrimaryRegion paramsIn field is new, and required. It returns a promise that is resolved once the CCP and namespace are successfully initialized. * It is recommended that you do not attempt to use the connect object before this promise is resolved. * @param {object} globalContainerDiv -- The container div for the active and secondary CCPs for use with DR. * @param {object} paramsIn -- Identical to connect.core.initCCP's paramsIn, save for one additional param. * @param {getPrimaryRegionCallback} paramsIn.getPrimaryRegion -- Required. A callback function that returns a promise that is resolved once the CCP and namespace are successfully initialized. */ globalConnect.core.initCCP = function (globalContainerDiv, paramsIn) { connect.assertNotNull(paramsIn.getPrimaryRegion, "getPrimaryRegion"); connect.assertTrue( connect.isFunction(paramsIn.getPrimaryRegion), "getPrimaryRegion must be a function" ); var getPrimaryRegionFunc = paramsIn.getPrimaryRegion; delete paramsIn.getPrimaryRegion; delete paramsIn.provider; delete paramsIn.plugins; var dualCcpResources = globalConnect.extractCcpRegionParams(globalContainerDiv, paramsIn); getPrimaryRegionFunc( function (primaryRegion) { return new Promise(resolve => { var initialRegion; if (paramsIn.pollForFailover) { // allow getPrimaryRegion function to pass a missing/untruthy region, if polling is enabled initialRegion = primaryRegion || paramsIn.region; } else { initialRegion = connect.assertNotNull(primaryRegion); } globalConnect.core.regions = dualCcpResources.reduce(function (obj, resource) { obj[resource.region] = { ccpParams: resource }; return obj; }, {}); var arnToRegionMap = dualCcpResources.reduce(function (obj, resource) { obj[resource.instanceArn] = resource.region; return obj; }, {}); validateRegion(initialRegion, globalConnect.core.regions); globalConnect.core.primaryRegion = initialRegion; globalConnect.core.secondaryRegion = Object.keys(globalConnect.core.regions).find(function ( region ) { return region != globalConnect.core.primaryRegion; }); var containers = dualCcpResources.map(function (resource) { if (resource.region === globalConnect.core.primaryRegion) { resource.isPrimary = true; } return new globalConnect.Container(resource); }); //Create global Iframe and attach ccp containers var ccpIframes = containers.map(function (container) { return container.ccp.outerHTML; }); var globalIframe = document.createElement("iframe"); globalIframe.style = GLOBALIFRAME_STYLE; globalIframe.allow = "microphone; camera; autoplay; clipboard-write; identity-credentials-get"; globalIframe.id = GLOBALIFRAME_ID; globalIframe.scrolling = "no"; // surface single instance connect api in main window globalIframe.onload = function () { activateUI(globalConnect.core.primaryRegion); deactivateUI(globalConnect.core.secondaryRegion); containers.map(function (container) { var regionalFrame = globalIframe.contentDocument.getElementById(container.id); var contentDocument = regionalFrame.contentDocument; var contentWindow = regionalFrame.contentWindow; // inject additionalScripts if specified if (paramsIn.additionalScripts && Array.isArray(paramsIn.additionalScripts)) { paramsIn.additionalScripts.forEach(script => { var scriptElt = contentDocument.createElement('script'); scriptElt.src = script; contentDocument.body.appendChild(scriptElt); }); } // trigger initCCP contentWindow.init(); var regionalConnect = contentWindow.connect; globalConnect.core.regions[container.id].connect = regionalConnect; //listen to failover state change from other window regionalConnect.core.getUpstream().onUpstream(regionalConnect.DisasterRecoveryEvents.FAILOVER, function (data) { if (data.nextActiveArn && globalConnect.core.secondaryRegion === arnToRegionMap[data.nextActiveArn]) { switchDisplayedRegion(arnToRegionMap[data.nextActiveArn], globalConnect.core.primaryRegion); } else if (data.isPrimary === false && container.id === globalConnect.core.primaryRegion) { switchDisplayedRegion(globalConnect.core.secondaryRegion, container.id); } else { return; // failover request ignored } delete globalConnect._failoverPending; globalConnect._triggerFailoverCompleteHandlers({ activeRegion: globalConnect.core.primaryRegion, activeCcpUrl: globalConnect.core.regions[globalConnect.core.primaryRegion].ccpParams.ccpUrl }); }); regionalConnect.core.getUpstream().onUpstream(regionalConnect.DisasterRecoveryEvents.FAILOVER_PENDING, function ({nextActiveArn}) { if (!globalConnect._failoverPending) { globalConnect._triggerFailoverPendingHandlers({nextActiveArn}); globalConnect._failoverPending = true; } }); if (container.id === globalConnect.core.primaryRegion) { window.connect = regionalConnect; } globalConnect._triggerInitHandlers(regionalConnect, container.id); }); resolve(globalConnect.core.regions[globalConnect.core.primaryRegion].connect); }; globalIframe.srcdoc = [ "<!DOCTYPE html>", "<meta charset='UTF-8'>", "<html>", "<head>", "<style>", "html, body { width: 100%;height: 100%;margin: 0px;padding: 0px; border: 0px;}", "</style>", "</head>", "<body>", ccpIframes.join(""), "</body>", "</html>", ].join(''); globalContainerDiv.appendChild(globalIframe); }); }, function (callback) { console.error( "[Disaster Recovery] An error occured, while attempting to retrieve your primary region;" ); callback(); } ); }; var switchDisplayedRegion = function(newPrimaryRegionId, newSecondaryRegionId) { globalConnect.core.primaryRegion = newPrimaryRegionId; globalConnect.core.secondaryRegion = newSecondaryRegionId; window.connect = globalConnect.core.regions[newPrimaryRegionId].connect; globalConnect.core.activate(newPrimaryRegionId); activateUI(newPrimaryRegionId); deactivateUI(newSecondaryRegionId); } var deactivateUI = function (regionID) { var renderedGlobalIframe = document.getElementById(GLOBALIFRAME_ID); renderedGlobalIframe.contentDocument.getElementById(regionID).style = "height: 0; width: 0; border: 0px"; }; var activateUI = function (regionID) { var renderedGlobalIframe = document.getElementById(GLOBALIFRAME_ID); renderedGlobalIframe.contentDocument.getElementById(regionID).style = "height:100%;width:100%;border:0px"; }; /** * Register a function to be triggered after globalConnect.core.initCCP() is invoked, and once the * Global Resiliency setup has been successfully initialized on the page and agents are able to begin * taking contacts. If you wish, you can set up hooks using this function before calling globalConnect.core.initCCP(). * * @param f A function that will be triggered when the Global Resiliency setup has been * successfully initialized on the page and agents are able to begin taking contacts. * The function will be called twice (once for each Connect instance in the setup), with two parameters: * 1. The Streams API object (the connect object) for one of the Connect instances in the Global Resiliency setup * 2. A string parameter with the AWS region associated with the Connect instance whose Streams API object * was provided in the first parameter. * * @returns A function that can be called if you wish to deregister the trigger. */ globalConnect.core.onInit = function(f) { globalConnect.core._onInitHandlers = globalConnect.core._onInitHandlers || {}; const subId = connect.randomId(); globalConnect.core._onInitHandlers[subId] = f; return () => delete globalConnect.core._onInitHandlers[subId]; } globalConnect._triggerInitHandlers = function(connect, region) { const handlers = globalConnect.core._onInitHandlers; if (handlers) { Object.values(handlers).forEach(f => f(connect, region)); } } /** * Register a function to be triggered when the UI changes to display a different region, and agents are able to * begin taking contacts in the new CCP region. If automatic region selection is in use for this multi-region setup, * this function will also be triggered when CCP is initialized and ready for use, if the region provided to the `getPrimaryRegion` * callback is not the currently active region for the agent. * * @param f A function that will be triggered when the UI changes to show CCP for a different region. * The function will be called with an Object parameter with two properties: * 1. `activeRegion`: the string name of the AWS region for the newly-active CCP instance * 2. `activeCcpUrl`: the value of the `ccpUrl` parameter for the newly-active instance, * as originally provided in the `initCCP()` parameters * * @returns A function that can be called if you wish to deregister the trigger. */ globalConnect.core.onFailoverComplete = function(f) { globalConnect.core._failoverCompleteHandlers = globalConnect.core._failoverCompleteHandlers || {}; const subId = connect.randomId(); globalConnect.core._failoverCompleteHandlers[subId] = f; return () => delete globalConnect.core._failoverCompleteHandlers[subId]; } globalConnect._triggerFailoverCompleteHandlers = function(data) { const handlers = globalConnect.core._failoverCompleteHandlers; if (handlers) { Object.values(handlers).forEach(f => f(data)); } } /** * Register a function to be triggered when an active region change has been detected, when soft failover is enabled and * a voice contact is active. The UI will wait to change over to the new region until the active voice contact is destroyed. * * @param f A function that will be triggered when a soft failover has been scheduled to occur when the active voice contact is destroyed. * The function will be called with an Object parameter with one property: * 1. `nextActiveArn`: the ARN of the Connect instance that will become active in the UI once the active voice contact is destroyed. * * @returns A function that can be called if you wish to deregister the trigger. */ globalConnect.core.onFailoverPending = function(f) { globalConnect.core._failoverPendingHandlers = globalConnect.core._failoverPendingHandlers || {}; const subId = connect.randomId(); globalConnect.core._failoverPendingHandlers[subId] = f; return () => delete globalConnect.core._failoverPendingHandlers[subId]; } globalConnect._triggerFailoverPendingHandlers = function(data) { const handlers = globalConnect.core._failoverPendingHandlers; if (handlers) { Object.values(handlers).forEach(f => f(data)); } } /** * Download CCP agent logs from the CCP instances in this multi-region setup. A separate log file * will be produced for each instance. The options are the same as for `connect.getLog().download()`, * except each log name will be prefixed with the AWS region associated with that log's Connect instance. * * @param options Optional parameter of type Object, providing Download options: * { logName: 'agent-log', // (the default name) * filterByLogLevel: false // download all logs (the default) * } * e.g. in a multi-region setup with one CCP instance in us-west-2 and another in us-east-1, this will * download two files: us-west-2-agent-log.txt and us-east-1-agent-log.txt. */ globalConnect.core.downloadLogs = function(options) { if (globalConnect.core.regions && globalConnect.core.regions[globalConnect.core.primaryRegion] && globalConnect.core.regions[globalConnect.core.primaryRegion].connect) { Object.entries(globalConnect.core.regions).forEach(([region, {connect}]) => { const logName = `${region}-${options && options.logName || 'agent-log'}`; connect.getLog().download({logName, filterByLogLevel: options && options.filterByLogLevel}); }); } else { throw new Error("CCP is not initialized yet. Please call initCCP() first and wait until the getPrimaryRegion promise resolves."); } }; globalConnect.core.failover = function (useSoftFailover) { globalConnect.core.failoverTo(globalConnect.core.secondaryRegion, useSoftFailover); }; globalConnect.core.failoverTo = function (electedNewPrimaryRegion, useSoftFailover) { validateRegion(electedNewPrimaryRegion); if (electedNewPrimaryRegion === globalConnect.core.primaryRegion) { connect .getLog() .info(`[Disaster Recovery] Ignoring request to fail over to region ${electedNewPrimaryRegion} since it is already the currently active region.`) .sendInternalLogToServer(); } else { globalConnect.core.deactivate(globalConnect.core.primaryRegion, useSoftFailover); } }; /**------------------------------------------------------------------------- * Deactivates a region */ globalConnect.core.deactivate = function (region, useSoftFailover) { var connect = globalConnect.core.regions[region].connect; connect .getLog() .info("[Disaster Recovery] Deactivating %s region.", region) .sendInternalLogToServer(); // call this to suppress contacts if (connect.core.suppressContacts && connect.core.forceOffline) { connect.core.suppressContacts(true); connect.core.forceOffline({softFailover: useSoftFailover}); } else { connect.getLog().error("[Disaster Recovery] CCP did not load successfully for region %s; unable to deactivate region", region); } }; /**------------------------------------------------------------------------- * Activates Stand-by region on failover using suppress==false event */ globalConnect.core.activate = function (region) { var connect = globalConnect.core.regions[region].connect; connect .getLog() .info("[Disaster Recovery] Activating %s region.", region) .sendInternalLogToServer(); if (connect.core.suppressContacts) { connect.core.suppressContacts(false); } else { connect.getLog().error("[Disaster Recovery] CCP did not load successfully for region %s; unable to activate region", region); } }; })(); /***/ }, /***/ 236 () { /* * JavaScript MD5 * https://github.com/blueimp/JavaScript-MD5 * * Copyright 2011, Sebastian Tschan * https://blueimp.net * * Licensed under the MIT license: * https://opensource.org/licenses/MIT * * Based on * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for more info. */ /* global define */ /* eslint-disable strict */ ;(function ($) { var ctx = this || globalThis; /** * Add integers, wrapping at 2^32. * This uses 16-bit operations internally to work around bugs in interpreters. * * @param {number} x First integer * @param {number} y Second integer * @returns {number} Sum */ function safeAdd(x, y) { var lsw = (x & 0xffff) + (y & 0xffff) var msw = (x >> 16) + (y >> 16) + (lsw >> 16) return (msw << 16) | (lsw & 0xffff) } /** * Bitwise rotate a 32-bit number to the left. * * @param {number} num 32-bit number * @param {number} cnt Rotation count * @returns {number} Rotated number */ function bitRotateLeft(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)) } /** * Basic operation the algorithm uses. * * @param {number} q q * @param {number} a a * @param {number} b b * @param {number} x x * @param {number} s s * @param {number} t t * @returns {number} Result */ function md5cmn(q, a, b, x, s, t) { return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) } /** * Basic operation the algorithm uses. * * @param {number} a a * @param {number} b b * @param {number} c c * @param {number} d d * @param {number} x x * @param {number} s s * @param {number} t t * @returns {number} Result */ function md5ff(a, b, c, d, x, s, t) { return md5cmn((b & c) | (~b & d), a, b, x, s, t) } /** * Basic operation the algorithm uses. * * @param {number} a a * @param {number} b b * @param {number} c c * @param {number} d d * @param {number} x x * @param {number} s s * @param {number} t t * @returns {number} Result */ function md5gg(a, b, c, d, x, s, t) { return md5cmn((b & d) | (c & ~d), a, b, x, s, t) } /** * Basic operation the algorithm uses. * * @param {number} a a * @param {number} b b * @param {number} c c * @param {number} d d * @param {number} x x * @param {number} s s * @param {number} t t * @returns {number} Result */ function md5hh(a, b, c, d, x, s, t) { return md5cmn(b ^ c ^ d, a, b, x, s, t) } /** * Basic operation the algorithm uses. * * @param {number} a a * @param {number} b b * @param {number} c c * @param {number} d d * @param {number} x x * @param {number} s s * @param {number} t t * @returns {number} Result */ function md5ii(a, b, c, d, x, s, t) { return md5cmn(c ^ (b | ~d), a, b, x, s, t) } /** * Calculate the MD5 of an array of little-endian words, and a bit length. * * @param {Array} x Array of little-endian words * @param {number} len Bit length * @returns {Array<number>} MD5 Array */ function binlMD5(x, len) { /* append padding */ x[len >> 5] |= 0x80 << len % 32 x[(((len + 64) >>> 9) << 4) + 14] = len var i var olda var oldb var oldc var oldd var a = 1732584193 var b = -271733879 var c = -1732584194 var d = 271733878 for (i = 0; i < x.length; i += 16) { olda = a oldb = b oldc = c oldd = d a = md5ff(a, b, c, d, x[i], 7, -680876936) d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) c = md5ff(c, d, a, b, x[i + 10], 17, -42063) b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290) b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) b = md5gg(b, c, d, a, x[i], 20, -373897302) a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) a = md5hh(a, b, c, d, x[i + 5], 4, -378558) d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) d = md5hh(d, a, b, c, x[i], 11, -358537222) c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) a = md5ii(a, b, c, d, x[i], 6, -198630844) d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) a = safeAdd(a, olda) b = safeAdd(b, oldb) c = safeAdd(c, oldc) d = safeAdd(d, oldd) } return [a, b, c, d] } /** * Convert an array of little-endian words to a string * * @param {Array<number>} input MD5 Array * @returns {string} MD5 string */ function binl2rstr(input) { var i var output = '' var length32 = input.length * 32 for (i = 0; i < length32; i += 8) { output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff) } return output } /** * Convert a raw string to an array of little-endian words * Characters >255 have their high-byte silently ignored. * * @param {string} input Raw input string * @returns {Array<number>} Array of little-endian words */ function rstr2binl(input) { var i var output = [] output[(input.length >> 2) - 1] = undefined for (i = 0; i < output.length; i += 1) { output[i] = 0 } var length8 = input.length * 8 for (i = 0; i < length8; i += 8) { output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32 } return output