// not bundled kount web client sdk at 2.2.2 /* eslint-disable no-throw-literal */ const KountSDKVersion = '2.2.2'; function kountSDK(config, sessionID, serverConfigTimeout) { const sdk = { implementationType: "module", repositoryLocation: "github", KountSDKVersion, kountClientID: null, isSinglePageApp: false, collectorURL: null, sessionID: null, kddcgid: null, FPCV_COOKIE_NAME: 'clientside-cookie', FPCV_LOCAL_STORAGE_KEY: 'clientside-local', FPCV_SESSION_STORAGE_KEY: 'kountCookie', SESSION_STORAGE_KEY_SESSION_ID: 'KountSessionID', collectBehaviorData: false, callbacks: {}, error: [], isDebugEnabled: false, LOG_PREFIX: 'k:', serverConfig: null, orchestrateTimeoutId: null, updateSDKServerConfigTimeoutInMS: serverConfigTimeout, orchestrateSemaphoreLocked: false, start(config, sessionID) { if (typeof config === 'undefined') { if (window.console && window.console.log) { console.log(`${this.LOG_PREFIX}SDK Disabled: config required.`); } return false; } this.isDebugEnabled = (typeof config.isDebugEnabled !== 'undefined') && (typeof config.isDebugEnabled === 'boolean') && config.isDebugEnabled; this.log(`SDK isDebugEnabled=${this.isDebugEnabled}`); this.log('SDK starting...'); const configuredClientID = config.clientID; if ((typeof configuredClientID === 'undefined') || (configuredClientID.length === 0)) { this.log('SDK Disabled: clientID required.'); return false; } this.kountClientID = configuredClientID; if ((typeof config.callbacks !== 'undefined')) { this.callbacks = config.callbacks; } const hostname = this._getHostname(config); if (hostname == null) { this.log(`SDK Disabled: unresolved hostname.`); return false; } this.collectorURL = `https://${hostname}`; this.log(`collectorURL=${this.collectorURL}`); const configuredIsSPA = config.isSinglePageApp; if ((typeof configuredIsSPA === 'undefined') || (configuredIsSPA !== true && configuredIsSPA !== false)) { this.log(`SDK Disabled: invalid isSinglePageApp:${configuredIsSPA}`); return false; } this.isSinglePageApp = configuredIsSPA; this.log(`isSinglePageApp=${this.isSinglePageApp}`); if ((typeof sessionID === 'undefined') || (sessionID.length === 0)) { this.log('SDK Disabled: sessionID required.'); return false; } this.sessionID = sessionID; this.kddcgid = this._newKDDCGID(); this._communicateLatestSessionData(); this._orchestrate(); this.log(`SDK Version=${this.KountSDKVersion}`); this.log(`SDK Implementation=${this.implementationType}`); this.log(`SDK Repository=${this.repositoryLocation}`); this.log('SDK started.'); return true; }, _orchestrate: async function (generateNewKDDCGID) { const functionName = "_orchestrate"; let thisFunctionOwnsSemaphoreLock = false; try { if (this.orchestrateSemaphoreLocked) { this.log(`${functionName} gated by semaphore. Skipping...`); return; } this.orchestrateSemaphoreLocked = true; thisFunctionOwnsSemaphoreLock = true; this.log(`${functionName} start...`); if (generateNewKDDCGID) { this.kddcgid = this._newKDDCGID(); } this.serverConfig = await this._getServerConfig(); if (this.serverConfig.collector.run) { this.log(`${functionName} runCollector start...`); this.runCollector(); this.log(`${functionName} runCollector end...`); } else { this.log(`${functionName} runCollector skipped...`); this.callback('collect-begin', { SessionID: this.sessionID, KountClientID: this.kountClientID }); this.callback('collect-end', { SessionID: this.sessionID, KountClientID: this.kountClientID }); } } catch(e) { let msg = `${functionName} unexpected error: ${e}`; this.log(msg); this.addError(msg); } finally { if(!thisFunctionOwnsSemaphoreLock) { return; } clearTimeout(this.orchestrateTimeoutId); this.log(`${functionName} config:${JSON.stringify(this.serverConfig)}`) let msUntilNextOrchestrate = this.serverConfig.ttlms; this.orchestrateTimeoutId = setTimeout( this._orchestrate.bind(this, true), msUntilNextOrchestrate ); this.log(`${functionName} scheduled for ${msUntilNextOrchestrate} ms`); this.log(`${functionName} end...`); this.orchestrateSemaphoreLocked = false; } }, _wrapPromiseInTimeout(msUntilTimeout, promise) { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error(`Timeout after ${msUntilTimeout}ms.`)); }, msUntilTimeout); promise .then(value => { clearTimeout(timer); resolve(value); }) .catch(reason=> { clearTimeout(timer); reject(reason); }) }) }, async _getServerConfig() { const functionName = "_getServerConfig"; var serverConfig = null try { this.log(`${functionName} start...`); let url = this.buildUrl({ base:this.collectorURL, path:"/cs/config", parameters: { m: this.kountClientID, s: this.sessionID, sv: this.KountSDKVersion, kddcgid: this.kddcgid, impl: this.implementationType, repo: this.repositoryLocation }}); const response = await this._wrapPromiseInTimeout(this.updateSDKServerConfigTimeoutInMS, fetch(url)); if (!response.ok) { throw `response not ok: ${response.status}`; } const jsonConfig = await response.json(); serverConfig = this._translateJSONToServerConfig(jsonConfig); } catch (e) { let msg = `${functionName} error caught. e:${e}`; this.log(msg); this.addError(msg); } finally { if (serverConfig == null) { serverConfig = { ttlms: 900000, collector: { run: true, featureFlags: { app: true, battery: true, browser: true, exp: true, page: true, ui: true } } } } this.log(`${functionName} config: ${JSON.stringify(serverConfig)}`); this.log(`${functionName} end...`); return serverConfig } }, _translateJSONToServerConfig(jsonConfig) { let ttlms = this._translateTTLMSConfig(jsonConfig); let collectorConfig = this._translateCollectorConfig(jsonConfig) return { ttlms: ttlms, collector: collectorConfig, } }, _translateTTLMSConfig(jsonConfig) { if (typeof jsonConfig.ttlms !== "number") { return 900000; } return jsonConfig.ttlms; }, _translateCollectorConfig(jsonConfig) { const functionName = "_translateCollectorConfig"; let collectorConfig = null try { this.log(`${functionName} start...`); if ((typeof jsonConfig.collection == 'undefined') || (typeof jsonConfig.collection.feature_flags == 'undefined')) { throw `invalid response JSON:${JSON.stringify(jsonConfig)}` } if (typeof jsonConfig.collection.collect !== "boolean") { throw `collect is not boolean: ${typeof collection.collect}`; } let runCollector = jsonConfig.collection.collect; if (runCollector) { const feature_flags = jsonConfig.collection.feature_flags if (typeof feature_flags.app !== "boolean") { throw `app feature flag is not boolean: ${typeof feature_flags.app}`; } if (typeof feature_flags.battery !== "boolean") { throw `battery feature flag is not boolean: ${typeof feature_flags.battery}`; } if (typeof feature_flags.browser !== "boolean") { throw `browser feature flag is not boolean: ${typeof feature_flags.browser}`; } if (typeof feature_flags.exp !== "boolean") { throw `exp feature flag is not boolean: ${typeof feature_flags.exp}`; } if (typeof feature_flags.page !== "boolean") { throw `page feature flag is not boolean: ${typeof feature_flags.page}`; } if (typeof feature_flags.ui !== "boolean") { throw `ui feature flag is not boolean: ${typeof feature_flags.ui}`; } if (typeof feature_flags.passLoc !== "boolean") { throw `passLoc feature flag is not boolean: ${typeof feature_flags.passLoc}`; } collectorConfig = { run: runCollector, featureFlags: jsonConfig.collection.feature_flags }; } else { collectorConfig = { run: runCollector, }; } } catch (e) { let msg = `${functionName} error caught. e:${e}`; this.log(msg); this.addError(msg); } finally { if (collectorConfig == null) { collectorConfig = { run: true, featureFlags: { app: true, battery: true, browser: true, exp: true, page: true, ui: true } } }; this.log(`${functionName} end...`); return collectorConfig; } }, _newKDDCGID() { let newKDDCGID = "invalid"; try { if ((typeof crypto != 'undefined') && (typeof crypto.randomUUID != 'undefined')) { newKDDCGID = crypto.randomUUID(); } else { const ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-1234567890"; let kddcgid = ""; for (let loopCount = 0; loopCount < 36; loopCount++) { kddcgid += ALLOWED_CHARS.charAt(Math.floor(Math.random() * ALLOWED_CHARS.length)); } newKDDCGID = kddcgid; } } catch (e) { this.log(`_newKDDCGID error:${e}`); } finally { this.log(`SDK kddcgid=${newKDDCGID}`); } return newKDDCGID; }, _getHostname(config) { let configuredHostname = config.hostname; if (typeof configuredHostname !== 'undefined') { if (this._isHostnameValid(configuredHostname)) { let configuredEnvironment = config.environment; if (configuredEnvironment){ this.log(`warning:both 'environment':${configuredEnvironment} and deprecated 'hostname':${configuredHostname} configs were specified. using hostname.`); } return configuredHostname; } this.log(`invalid configuredHostname:${configuredHostname}`); return null; } let configuredEnvironment = config.environment; if (configuredEnvironment){ configuredEnvironment = configuredEnvironment.toUpperCase(); } switch (configuredEnvironment) { case 'TEST': return "tst.kaptcha.com"; case 'PROD': return "ssl.kaptcha.com"; default: this.log(`invalid configuredEnvironment:${configuredEnvironment}`); return null; }; }, _isHostnameValid(hostname) { if (typeof hostname !== 'string') { this.log(`Invalid hostname: not a string: ${typeof hostname}`); return false; } if (hostname.length === 0) { this.log('Invalid hostname: length 0.'); return false; } const regex = /^[a-zA-Z0-9.]*$/g; if (!regex.test(hostname)) { this.log(`Invalid hostname:${hostname}`); return false; } return true; }, _communicateLatestSessionData() { try { this.log('communicateLatestSessionData running...'); const sessionIdInSessionStorage = sessionStorage.getItem(this.SESSION_STORAGE_KEY_SESSION_ID); if (sessionIdInSessionStorage === null) { this.postNewSession(this.sessionID); } else if (sessionIdInSessionStorage !== this.sessionID) { this.postChangeSession(this.sessionID, sessionIdInSessionStorage); } sessionStorage.setItem(this.SESSION_STORAGE_KEY_SESSION_ID, this.sessionID); } catch (e) { this.addError(`communicateLatestSessionData error:${e}`); } finally { this.log('communicateLatestSessionData ending...'); } }, async postNewSession(sessionID) { try { this.log('postNewSession running...'); let response = await fetch( this.buildUrl({ base: this.collectorURL, path: `/session/${sessionID}`, parameters: { kddcgid: this.kddcgid, impl:this.implementationType, repo: this.repositoryLocation, } }), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'client-id': this.kountClientID }, }); if (response.status === 200 || response.status === 201) { this.log('postNewSession success'); } else { this.addError(`postNewSession unknown response: ${response.status}`); } } catch (e) { this.addError(`postNewSession error:${e}`); } finally { this.log('postNewSession ending...'); } }, async postChangeSession(sessionID, previousSessionID) { try { this.log(`postChangeSession running: newSession: ${sessionID} prevSession: ${previousSessionID}`); let response = await fetch( this.buildUrl({ base: this.collectorURL, path: `/session/${sessionID}`, parameters: { previousSessionID: previousSessionID, kddcgid: this.kddcgid, impl:this.implementationType, repo: this.repositoryLocation, } }), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'client-id': this.kountClientID }, }); if (response.status === 200 || response.status === 201) { this.log('postChangeSession success'); const r = await response.json() } else { this.addError(`postChangeSession unknown response: ${response.status}`); } } catch (e) { this.addError(`postChangeSession error:${e}`); } finally { this.log('postChangeSession ending...'); } }, getFPCVFromLocalStorage() { try { this.log('getFPCVFromLocalStorage running...'); const value = localStorage.getItem(this.FPCV_LOCAL_STORAGE_KEY); if (value == null) { return ''; } return value; } catch (e) { this.addError(`getFPCVFromLocalStorage: error${e}`); return ''; } }, getFPCVFromCookie() { try { this.log('getFPCVFromCookie running...'); const dc = decodeURIComponent(document.cookie); const cookieList = dc.split(';'); let cookieValue = ''; for (let i = 0; i < cookieList.length; i++) { const currentCookie = cookieList[i].trim(); const elements = currentCookie.split('='); if (elements.length === 2) { if (elements[0] === this.FPCV_COOKIE_NAME) { [, cookieValue] = elements; this.log(`getFPCVFromCookie: found new first party cookie: ${cookieValue}`); break; } } } if (cookieValue === '') { const regex = /(cdn[.][a-z]+[.][0-9]+[.]ka.ck)/g; for (let i = 0; i < cookieList.length; i++) { const currentCookie = cookieList[i].trim(); if (regex.test(currentCookie) === true) { const elements = currentCookie.split('='); if (elements.length === 2) { [, cookieValue] = elements; this.log(`getFPCVFromCookie: found old first party cookie: ${cookieValue}`); this.storeFPCVInCookie(cookieValue); break; } } } } return cookieValue; } catch (e) { this.addError(`getFPCVFromCookie error:${e}`); return ''; } }, storeFPCVInLocalStore(value) { try { this.log('storeFPCVInLocalStore running...'); localStorage.setItem(this.FPCV_LOCAL_STORAGE_KEY, value); } catch (e) { this.addError(`storeFPCVInLocalStore error:${e}`); } finally { this.log('storeFPCVInLocalStore ending...'); } }, storeFPCVInCookie(value) { try { this.log('storeFPCVInCookie running...'); const expire = 365; const d = new Date(); d.setTime(d.getTime() + (expire * 24 * 60 * 60 * 1000)); const expires = `expires=${d.toUTCString()}`; let attributes = '; SameSite=None; Secure'; if (window.location.protocol !== 'https:') { attributes = '; SameSite=Lax'; } document.cookie = `${this.FPCV_COOKIE_NAME}=${value};${expires};path=/${attributes}`; } catch (e) { this.addError(`storeFPCVInCookie error:${e}`); } finally { this.log('storeFPCVInCookie ending...'); } }, storeFPCVInSession(value) { try { this.log('storeFPCVInSession running...'); sessionStorage.setItem(sdk.FPCV_SESSION_STORAGE_KEY, value); } catch (e) { this.addError(`storeFPCVInSession error:${e}`); } }, coordinateFirstPartyCookieValues() { this.log('coordinateFirstPartyCookieValues running...'); let deducedFPCV = ''; const fpcvFromCookie = this.getFPCVFromCookie(); const fpcvFromLocalStorage = this.getFPCVFromLocalStorage(); this.storeFPCVInSession(''); if ((fpcvFromCookie === '') && (fpcvFromLocalStorage === '')) { deducedFPCV = ''; } else if ((fpcvFromCookie !== '') && (fpcvFromLocalStorage === '')) { this.storeFPCVInLocalStore(fpcvFromCookie); deducedFPCV = fpcvFromCookie; } else if ((fpcvFromLocalStorage !== '') && (fpcvFromCookie === '')) { this.storeFPCVInCookie(fpcvFromLocalStorage); deducedFPCV = fpcvFromLocalStorage; } else if ((fpcvFromLocalStorage === fpcvFromCookie) && (fpcvFromLocalStorage !== '') && (fpcvFromCookie !== '')) { deducedFPCV = fpcvFromLocalStorage; } else if ((fpcvFromLocalStorage !== fpcvFromCookie) && (fpcvFromLocalStorage !== '') && (fpcvFromCookie !== '')) { this.storeFPCVInCookie(fpcvFromLocalStorage); deducedFPCV = fpcvFromLocalStorage; } if (deducedFPCV === '') { this.establishNewFPCV(); } else { this.communicateExistingFPCV(deducedFPCV); } }, async establishNewFPCV() { try { this.log('establishNewFPCV running...'); let url = this.buildUrl({ base:this.collectorURL, path:"/cs/generatecookie", parameters: { m: this.kountClientID, s: this.sessionID, sv: this.KountSDKVersion, kddcgid: this.kddcgid, impl: this.implementationType, repo: this.repositoryLocation }}); const response = await fetch(url); const json = await response.json(); if (json.value.length > 0) { let firstPartyCookieValue = json.value; this.storeFPCVInCookie(firstPartyCookieValue); this.storeFPCVInLocalStore(firstPartyCookieValue); this.storeFPCVInSession(firstPartyCookieValue); } } catch (e) { this.addError(`establishNewFPCV error:${e}`); } finally { this.log('establishNewFPCV ending...'); } }, async communicateExistingFPCV(value) { try { this.log('communicateExistingFPCV running...'); let response = await fetch( this.buildUrl({ base:this.collectorURL, path:'/cs/storecookie' }), { method: 'POST', body: this.buildParams({ m: this.kountClientID, s: this.sessionID, sv: this.KountSDKVersion, k: value, kddcgid: this.kddcgid, impl: this.implementationType, repo: this.repositoryLocation }), headers: { "Content-Type": "application/x-www-form-urlencoded" }, }); if (response.status === 500) { this.log('communicateExistingFPCV: invalid cookie'); sdk.establishNewFPCV(); } if (response.status === 200) { this.log('communicateExistingFPCV: valid cookie'); sdk.storeFPCVInSession(value); } } catch (e) { this.addError(`communicateExistingFPCV error:${e}`); } }, _passiveLoc() { const functionName = "_passiveLoc"; try { this.log(`${functionName} start...`); if (this.serverConfig.collector.featureFlags.passLoc) { if (navigator.geolocation) { navigator.permissions.query({ name: 'geolocation' }).then( (result) => { if (result.state === 'granted') { navigator.geolocation.getCurrentPosition( (position) => { this._handleCurrentPosition(position); }, (error) => { console.error("navigator.geolocation error:", error); }, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0, } ); } else { this.log("navigator.geolocation bypassed:" + result.state); } } ); } else { this.log("navigator.geolocation unavailable"); } } else { this.log("passLoc disabled"); } } catch (e) { this.addError(`${functionName} error:${e}`); } finally { this.log(`${functionName} ending...`); } }, async _handleCurrentPosition(position) { const functionName = "__handleCurrentPosition"; try { this.log(`${functionName} start...`); let url = this.buildUrl({ base:this.collectorURL, path:'/cs/location' }); let collectionTimestamp = Date.now(); let body = JSON.stringify({ merchant_id: this.kountClientID, session_id: this.sessionID, ddcGroupID: this.kddcgid, collection_timestamp: collectionTimestamp, location_data: { collection_timestamp: position.timestamp, latitude: position.coords.latitude, longitude: position.coords.longitude, location_accuracy: position.coords.accuracy, altitude: position.coords.altitude, altitude_accuracy: position.coords.altitudeAccuracy, speed: position.coords.speed, heading: position.coords.heading } }); let headers = { "Content-Type": "application/json" }; let options = { method: 'POST', body: body, headers: headers }; let response = await fetch( url, options ); if (response.status != 200) { this.log(`${functionName} ${url} had response status:${response.status}`); } } catch (e) { this.addError(`${functionName} error:${e}`); } finally { this.log(`${functionName} ending...`); } }, _createIframe() { const functionName = "_createIframe"; try { this.log(`${functionName} running...`); const iframeId = 'ibody'; const priorIframe = document.getElementById(iframeId); if (priorIframe !== null) { priorIframe.remove(); } let url = this.buildUrl({ base:this.collectorURL, path:`/logo.htm`, parameters:{ m: this.kountClientID, s: this.sessionID, sv: this.KountSDKVersion, kddcgid: this.kddcgid, impl: this.implementationType, repo: this.repositoryLocation, }, }); if (this.isDebugEnabled) { url.hash = '#console'; } const iframe = document.createElement('iframe'); iframe.id = iframeId; iframe.style.border = '0px'; iframe.style.height = '1px'; iframe.style.width = '1px'; iframe.style.position = 'absolute'; iframe.src = url.toString(); document.getElementsByTagName('body')[0].appendChild(iframe); if (typeof this.callbacks !== 'undefined') { this.callback('collect-begin', { SessionID: this.sessionID, KountClientID: this.kountClientID }); } if (window.postMessage !== 'undefined' && window.onmessage !== 'undefined') { let onMessageHandlerFunc = (event) => { try { if (event.origin !== this.collectorURL) { this.log(`window.onmessage doesn't handle event origin:[type:${event.type}, origin:${event.origin}]`); return; } if (!event.data) { throw new Error(`window.onmessage event has missing event.data:[type:${event.type}, origin:${event.origin}]`); } let data = JSON.parse(event.data); if (!this.isSinglePageApp && data.event === 'collect-end') { this.detach(window, 'unload', this.unloadHandler); } if (!data) { throw new Error(`window.onmessage event has missing data:[type:${event.type}, origin:${event.origin}]`); } if (!data.params) { throw new Error(`window.onmessage event has missing data.params:[type:${event.type}, origin:${event.origin}, data:${event.data}]`); } const params = {}; Object.keys(data.params).forEach((index) => { if (Object.prototype.hasOwnProperty.call(data.params, index)) { switch (index) { case 's': params.SessionID = data.params[index]; break; case 'm': params.KountClientID = data.params[index]; break; default: params[index] = data.params[index]; } } }); this.callback(data.event, params); this.log(`window.onmessage executed for event:[type:${event.type}, origin:${event.origin}, data:${event.data}]`); } catch (e) { this.log(`window.onmessage encountered an error: ${e}`); } }; window.onmessage = onMessageHandlerFunc; if (!this.isSinglePageApp) { this.attach(window, 'unload', this.unloadHandler); } } else { window.setTimeout(() => { this.callback('collect-end', { SessionID: this.sessionID, KountClientID: this.kountClientID }); }, 3000); } } catch (e) { this.addError(`${functionName} error:${e}`); } finally { this.log(`${functionName} ending...`); } }, runCollector() { const functionName = "runCollector"; try { this.log(`${functionName} running...`); this.coordinateFirstPartyCookieValues(); const waitForFirstParty = (timeoutInMS, intervalBetweenChecksInMS) => new Promise((resolve, reject) => { const checkForFirstParty = () => { if (sessionStorage.getItem(this.FPCV_SESSION_STORAGE_KEY) !== '') { this._passiveLoc(); this._createIframe(); resolve(); } else if ((timeoutInMS -= intervalBetweenChecksInMS) < 0) { reject(); } else { setTimeout(checkForFirstParty, intervalBetweenChecksInMS); } }; setTimeout(checkForFirstParty, intervalBetweenChecksInMS); }); (async () => { waitForFirstParty(2000, 10) .then(() => this.log(`${functionName}: Collection Initiated`)) .catch(() => this.log(`${functionName}: Invalid/Missing First Party cookie`)); })(); } catch (e) { this.addError(`${functionName} error:${e}`); } finally { this.log(`${functionName} ending...`); } }, AttachToForm(formID, options = new Map()) { this.log('AttachToForm running...'); let decisionPointField = 'kountDecisionPointUUID'; if (this.collectBehaviorData) { } this.log('AttachToForm: Attaching to form...'); const form = document.getElementById(formID); if (options != null && options.has('CustomFieldName') && options.get('CustomFieldName').length > 0) { this.log(`AttachToForm: Overriding decisionPointField name to: ${options.get('CustomFieldName')}`); decisionPointField = options.get('CustomFieldName'); } if (form != null) { if (typeof form[decisionPointField] === 'undefined') { const hiddenField = document.createElement('input'); hiddenField.setAttribute('type', 'hidden'); hiddenField.setAttribute('name', decisionPointField); hiddenField.setAttribute('value', this.sessionID); form.appendChild(hiddenField); this.log(`AttachToForm: Field ${decisionPointField} NOT found. \ Created and attached to form with value: ${this.sessionID}`); } else { this.log(`AttachToForm: Field ${decisionPointField} found, setting value to: ${this.sessionID}`); form[decisionPointField].setAttribute('value', this.sessionID); } this.log(`AttachToForm: Attached to form successfully using ${this.sessionID} \ value in ${decisionPointField} hidden field.`); } else { this.addError(`AttachToForm: FormID: ${formID} is not valid. Skipping attachment to form and collection.`); } }, NewSession(sessionID) { if (typeof sessionID === 'undefined') { this.addError("NewSession: Invalid sessionID. You must set the 'sessionID' for collection. Disabling Kount SDK"); } else { this.log(`NewSession: SessionID set to: ${sessionID}`); sessionStorage.clear(); this.sessionID = sessionID; this._communicateLatestSessionData(); this._orchestrate(true); } }, log(message) { if (this.isDebugEnabled && window.console && window.console.debug) { console.log(`${this.LOG_PREFIX}${message}`); } }, addError(error) { this.error.push(error); this.log(error); }, callback(event, params) { if (typeof this.callbacks[event] !== 'undefined') { const theCallback = this.callbacks[event]; this.callbacks[event] = undefined; theCallback(params); } }, unloadHandler(event) { const endpoint = 'fin'; const formData = { n: 'collect-end', com: 'false', et: 0, s: this.sessionID, m: this.kountClientID, }; try { const http = this.getXMLHttpRequest(endpoint, 'POST'); http.send(formData); } catch (e) {} }, getXMLHttpRequest(endpoint, method) { let http = null; const esc = encodeURIComponent || escape; const url = `${this.collectorURL}/${endpoint}`; if (window.XMLHttpRequest) { try { http = new window.XMLHttpRequest(); } catch (e) {} if ('withCredentials' in http) { http.open(method, url, false); } else if (typeof window.XDomainRequest !== 'undefined') { http = new window.XDomainRequest(); http.open(method, url); } else { http = null; } } else { http = null; } return { send(data) { if (!http) { return; } http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); let payload = ''; Object.keys(data).forEach((key) => { if (Object.prototype.hasOwnProperty.call(data, key)) { payload += `&${esc(key)}=${esc(data[key])}`; } }); payload = payload.substring(1); http.send(payload); }, }; }, buildUrl({base, path, parameters = {}}) { let params = this.buildParams(parameters); path = [...params].length == 0 ? path : path + "?" + params.toString(); let url = new URL(path, base); return url; }, buildParams(parameters = {}) { let searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(parameters)) { searchParams.append(key, value); } return searchParams; }, attach: (function attachFunc() { if (typeof document.addEventListener !== 'undefined') { return function addEventListenerFunc(element, event, callback) { element.addEventListener(event, callback, false); }; } if (typeof document.attachEvent !== 'undefined') { return function attachEventFunc(element, event, callback) { element.attachEvent(`on${event}`, callback); }; } return function noOpFunc(element, event, callback) {}; }()), detach: (function detachFunc() { if (typeof document.removeEventListener !== 'undefined') { return function removeEventListenerFunc(element, event, listener) { element.removeEventListener(event, listener, false); }; } if (typeof document.detachEvent !== 'undefined') { return function detachEventFunc(element, event, listener) { element.detach(`on${event}`, listener); }; } return function noOpFunc(element, event, listener) {}; }()), }; try { if (sdk.start(config, sessionID)) { return sdk; } } catch (e) { } return null; } // // // BEGIN Shopify // // document.body.setAttribute("class", "kaxsdc " + document.body.getAttribute("class")); document.body.setAttribute("data-event", "load"); var KountDDCIntervalID = window.setInterval(function () { KountRunDDC(); }, 1000); KountRunDDC(); function KountRunDDC() { function runDCCart() { const cartId = getCartIdCookie().slice(-32); if (cartId) { createCollectionURLAndLoadScript(cartId, "KountCart"); clearInterval(KountDDCIntervalID); } } function getCartIdCookie() { let cartId = getCookie("cart"); // some cartId's have this format encoded: :?key= // in that case, return const sub = cartId.substring(0, cartId.indexOf("?key=")); try { const decoded = atob(sub); let nk = decoded.split(":")[1]; if (nk) return nk; } catch (e) { // some cartId's have this format not encoded: ?key= // in that case, return } return sub; } function createCollectionURLAndLoadScript(id, cookieName) { if (id !== getCookie(cookieName)) { // if the cart token is different we need to fire up data collection setCookie(cookieName, id); const host = "ssl.kaptcha.com"; kountSDK({ clientID: "102907", hostname: host, isSinglePageApp: false, isDebugEnabled: false, }, id, 10000 /* tpa added */); } } function getCookie(cname) { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } function setCookie(cname, cvalue) { var d = new Date(); // 1-day expire time for the cookie /it will, however, change at every new checkout/ d.setTime(d.getTime() + (24 * 60 * 60 * 1000)); var expires = "expires=" + d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } runDCCart(); }