This is an automated email from the ASF dual-hosted git repository.
jkevan pushed a commit to branch UNOMI-610-new-tracker
in repository https://gitbox.apache.org/repos/asf/unomi-tracker.git
The following commit(s) were added to refs/heads/UNOMI-610-new-tracker by this
push:
new 51a36a0 UNOMI-610: base tracker first draft
51a36a0 is described below
commit 51a36a00ca61e5e7d1a9e2de7c901a2cbb568a30
Author: Kevan <[email protected]>
AuthorDate: Wed Sep 7 11:54:53 2022 +0200
UNOMI-610: base tracker first draft
---
.eslintrc.json | 118 ++++++++++++++-
dist/apache-unomi-tracker.cjs.js | 319 +++++++++++++++++++++++++++++++--------
dist/apache-unomi-tracker.esm.js | 319 +++++++++++++++++++++++++++++++--------
dist/apache-unomi-tracker.umd.js | 319 +++++++++++++++++++++++++++++++--------
rollup.config.js | 22 +--
src/index.js | 2 +-
src/tracker/tracker.js | 304 +++++++++++++++++++++++++++++--------
test/spec.js | 6 +-
8 files changed, 1148 insertions(+), 261 deletions(-)
diff --git a/.eslintrc.json b/.eslintrc.json
index 959d83d..6f073a1 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,8 +1,7 @@
{
"env": {
"browser": true,
- "es2021": true,
- "node": true
+ "es2021": true
},
"extends": "eslint:recommended",
"plugins": ["json"],
@@ -12,6 +11,121 @@
"ecmaVersion": "latest",
"sourceType": "module"
},
+ "globals": {
+ "require": false
+ },
"rules": {
+ "indent": [
+ "error",
+ 4,
+ {
+ "SwitchCase": 1
+ }
+ ],
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": [
+ "error",
+ "always"
+ ],
+ "no-console": [
+ "off",
+ "allow"
+ ],
+ "no-unused-vars": [
+ "warn",
+ {
+ "vars": "all",
+ "args": "after-used",
+ "ignoreRestSiblings": false
+ }
+ ],
+ "no-global-assign": [
+ "error",
+ {
+ "exceptions": ["__webpack_public_path__"]
+ }
+ ],
+ "no-useless-escape": [
+ "warn"
+ ],
+ "keyword-spacing": [
+ "error",
+ {
+ "overrides": {
+ "import": {
+ "before": false
+ },
+ "export": {
+ "before": false
+ }
+ }
+ }
+ ],
+ "space-before-function-paren": [
+ "error",
+ {
+ "anonymous": "always",
+ "named": "never",
+ "asyncArrow": "always"
+ }
+ ],
+ "quote-props": [
+ "error",
+ "consistent-as-needed"
+ ],
+ "curly": [
+ "error",
+ "all"
+ ],
+ "object-curly-spacing": [
+ "error",
+ "always"
+ ],
+ "comma-spacing": [
+ "error",
+ {
+ "before": false,
+ "after": true
+ }
+ ],
+ "brace-style": [
+ "error",
+ "1tbs"
+ ],
+ "key-spacing": [
+ "error",
+ {
+ "beforeColon": false,
+ "afterColon": true
+ }
+ ],
+ "template-curly-spacing": [
+ "error",
+ "never"
+ ],
+ "block-spacing": [
+ "error",
+ "always"
+ ],
+ "space-in-parens": [
+ "error",
+ "never"
+ ],
+ "space-infix-ops": [
+ "error"
+ ],
+ "space-before-blocks": [
+ "error"
+ ],
+ "valid-jsdoc": [
+ "error" // we should put it back to warn once we have time to
cleanup the existing JSDoc
+ ]
}
}
diff --git a/dist/apache-unomi-tracker.cjs.js b/dist/apache-unomi-tracker.cjs.js
index 612f6fc..d8027fa 100644
--- a/dist/apache-unomi-tracker.cjs.js
+++ b/dist/apache-unomi-tracker.cjs.js
@@ -20,12 +20,13 @@ var newTracker = function newTracker() {
* This function initialize the tracker
*
* @param {object} digitalData config of the tracker
+ * @returns {undefined}
*/
initTracker: function initTracker(digitalData) {
wem.digitalData = digitalData;
- wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : "wem-profile-id";
- wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : "wem-session-id";
- wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : "";
+ wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : 'wem-profile-id';
+ wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : 'wem-session-id';
+ wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : '';
wem.disableTrackedConditionsListeners =
wem.digitalData.wemInitConfig.disableTrackedConditionsListeners;
wem.activateWem = wem.digitalData.wemInitConfig.activateWem;
var _wem$digitalData$wemI = wem.digitalData.wemInitConfig,
@@ -53,6 +54,7 @@ var newTracker = function newTracker() {
* Note: that the tracker will start once the current DOM is complete
loaded, using listener on current document: DOMContentLoaded
*
* @param {object[]} digitalDataOverrides optional, list of digitalData
extensions, they will be merged with original digitalData before context loading
+ * @returns {undefined}
*/
startTracker: function startTracker() {
var digitalDataOverrides = arguments.length > 0 && arguments[0] !==
undefined ? arguments[0] : undefined;
@@ -152,51 +154,20 @@ var newTracker = function newTracker() {
/**
* Get current session id
- * @returns {null|*}
+ * @returns {null|string} get current session id
*/
getSessionId: function getSessionId() {
return wem.sessionID;
},
- convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
- if (!searchString) {
- return null;
- }
-
- return searchString.replace(/^\?/, '') // Only trim off a single leading
interrobang.
- .split('&').reduce(function (result, next) {
- if (next === '') {
- return result;
- }
-
- var pair = next.split('=');
- var key = decodeURIComponent(pair[0]);
- var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
-
- if (Object.prototype.hasOwnProperty.call(result, key)) {
- // Check to see if this property has been met before.
- if (Array.isArray(result[key])) {
- // Is it already an array?
- result[key].push(value);
- } else {
- // Make it an array.
- result[key] = [result[key], value];
- }
- } else {
- // First time seen, just add it.
- result[key] = value;
- }
-
- return result;
- }, {});
- },
/**
* This function will register a personalization
*
- * @param {object} personalization
- * @param {object} variants
+ * @param {object} personalization the personalization object
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {function} [resultCallback]
+ * @param {function} [resultCallback] the callback to be executed after
personalization resolved
+ * @returns {undefined}
*/
registerPersonalizationObject: function
registerPersonalizationObject(personalization, variants, ajax, resultCallback) {
var target = personalization.id;
@@ -273,12 +244,13 @@ var newTracker = function newTracker() {
/**
* This function will register an optimization test or A/B test
*
- * @param {string} optimizationTestNodeId
- * @param {string} goalId
- * @param {string} containerId
- * @param {object} variants
+ * @param {string} optimizationTestNodeId the optimization test node id
+ * @param {string} goalId the associated goal Id
+ * @param {string} containerId the HTML container Id
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {object} [variantsTraffic]
+ * @param {object} [variantsTraffic] the associated traffic allocation
+ * @return {undefined}
*/
registerOptimizationTest: function
registerOptimizationTest(optimizationTestNodeId, goalId, containerId, variants,
ajax, variantsTraffic) {
// check persona panel forced variant
@@ -344,6 +316,7 @@ var newTracker = function newTracker() {
*
* @param {boolean} [skipEvents=false] Should we send the events
* @param {boolean} [invalidate=false] Should we invalidate the current
context
+ * @return {undefined}
*/
loadContext: function loadContext(skipEvents, invalidate) {
if (wem.contextLoaded) {
@@ -399,6 +372,7 @@ var newTracker = function newTracker() {
* @param {object} event The event object to send, you can build it using
wem.buildEvent(eventType, target, source)
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvent: function collectEvent(event, successCallback, errorCallback)
{
wem.collectEvents({
@@ -412,6 +386,7 @@ var newTracker = function newTracker() {
* @param {object} events Javascript object { events: [event1, event2] }
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvents: function collectEvents(events, successCallback,
errorCallback) {
if (wem.fallback) {
@@ -440,6 +415,7 @@ var newTracker = function newTracker() {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendClickEvent: function sendClickEvent(event, successCallback,
errorCallback) {
if (event.target.id || event.target.name) {
@@ -481,6 +457,7 @@ var newTracker = function newTracker() {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendVideoEvent: function sendVideoEvent(event, successCallback,
errorCallback) {
console.info('[WEM] catching video event');
@@ -506,6 +483,7 @@ var newTracker = function newTracker() {
* This function will invalidate the Apache Unomi session and profile,
* by removing the associated cookies, set the loaded context to undefined
* and set the session id cookie with a newly generated ID
+ * @return {undefined}
*/
invalidateSessionAndProfile: function invalidateSessionAndProfile() {
@@ -522,7 +500,7 @@ var newTracker = function newTracker() {
* @param {string} eventType The name of your event
* @param {object} [target] The target object for your event can be build
with wem.buildTarget(targetId, targetType, targetProperties)
* @param {object} [source] The source object for your event can be build
with wem.buildSource(sourceId, sourceType, sourceProperties)
- * @returns {{eventType: *, scope}}
+ * @returns {object} the event
*/
buildEvent: function buildEvent(eventType, target, source) {
var event = {
@@ -545,7 +523,7 @@ var newTracker = function newTracker() {
* This function return an event of type form
*
* @param {string} formName The HTML name of id of the form to use in the
target of the event
- * @returns {*|{eventType: *, scope, source: {scope, itemId: string,
itemType: string, properties: {}}, target: {scope, itemId: string, itemType:
string, properties: {}}}}
+ * @returns {object} the form event
*/
buildFormEvent: function buildFormEvent(formName) {
return wem.buildEvent('form', wem.buildTarget(formName, 'form'),
wem.buildSourcePage());
@@ -554,7 +532,7 @@ var newTracker = function newTracker() {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the target page
*/
buildTargetPage: function buildTargetPage() {
return wem.buildTarget(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -563,7 +541,7 @@ var newTracker = function newTracker() {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the source page
*/
buildSourcePage: function buildSourcePage() {
return wem.buildSource(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -575,7 +553,7 @@ var newTracker = function newTracker() {
* @param {string} targetId The ID of the target
* @param {string} targetType The type of the target
* @param {object} [targetProperties] The optional properties of the target
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the target
*/
buildTarget: function buildTarget(targetId, targetType, targetProperties) {
return wem._buildObject(targetId, targetType, targetProperties);
@@ -587,7 +565,7 @@ var newTracker = function newTracker() {
* @param {string} sourceId The ID of the source
* @param {string} sourceType The type of the source
* @param {object} [sourceProperties] The optional properties of the source
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the source
*/
buildSource: function buildSource(sourceId, sourceType, sourceProperties) {
return wem._buildObject(sourceId, sourceType, sourceProperties);
@@ -605,6 +583,7 @@ var newTracker = function newTracker() {
* @param {string} cookieName name of the cookie
* @param {string} cookieValue value of the cookie
* @param {number} [expireDays] number of days to set the expire date
+ * @return {undefined}
*/
setCookie: function setCookie(cookieName, cookieValue, expireDays) {
var expires = '';
@@ -622,7 +601,7 @@ var newTracker = function newTracker() {
* This is an utility function to get a cookie
*
* @param {string} cookieName name of the cookie to get
- * @returns {*} the value of the first cookie with the corresponding name
or null if not found
+ * @returns {string} the value of the first cookie with the corresponding
name or null if not found
*/
getCookie: function getCookie(cookieName) {
var name = cookieName + '=';
@@ -647,6 +626,7 @@ var newTracker = function newTracker() {
* This is an utility function to remove a cookie
*
* @param {string} cookieName the name of the cookie to rename
+ * @return {undefined}
*/
removeCookie: function removeCookie(cookieName) {
@@ -656,7 +636,8 @@ var newTracker = function newTracker() {
/**
* This is an utility function to execute AJAX call
*
- * @param {object} options
+ * @param {object} options options of the request
+ * @return {undefined}
*/
ajax: function ajax(options) {
var xhr = new XMLHttpRequest();
@@ -733,7 +714,7 @@ var newTracker = function newTracker() {
/**
* This is an utility function to generate a new UUID
*
- * @returns {string}
+ * @returns {string} the newly generated UUID
*/
generateGuid: function generateGuid() {
function s4() {
@@ -745,8 +726,8 @@ var newTracker = function newTracker() {
/**
* This is an utility function to check if the local storage is available
or not
- * @param type
- * @returns {boolean}
+ * @param {string} type the type of storage to test
+ * @returns {boolean} true in case storage is available, false otherwise
*/
storageAvailable: function storageAvailable(type) {
try {
@@ -759,6 +740,16 @@ var newTracker = function newTracker() {
return false;
}
},
+
+ /**
+ * Dispatch a JavaScript event in current HTML document
+ *
+ * @param {string} name the name of the event
+ * @param {boolean} canBubble does the event can bubble ?
+ * @param {boolean} cancelable is the event cancelable ?
+ * @param {*} detail event details
+ * @return {undefined}
+ */
dispatchJSEvent: function dispatchJSEvent(name, canBubble, cancelable,
detail) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, canBubble, cancelable, detail);
@@ -767,8 +758,9 @@ var newTracker = function newTracker() {
/**
* This is an utility function to get current url parameter value
- * @param name, the name of the parameter
- * @returns {string}
+ *
+ * @param {string} name, the name of the parameter
+ * @returns {string} the value of the parameter
*/
getUrlParameter: function getUrlParameter(name) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
@@ -777,11 +769,58 @@ var newTracker = function newTracker() {
return results === null ? null :
decodeURIComponent(results[1].replace(/\+/g, ' '));
},
+ /**
+ * convert the passed query string into JS object.
+ * @param {string} searchString The URL query string
+ * @returns {object} converted URL params
+ */
+ convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
+ if (!searchString) {
+ return null;
+ }
+
+ return searchString.replace(/^\?/, '') // Only trim off a single leading
interrobang.
+ .split('&').reduce(function (result, next) {
+ if (next === '') {
+ return result;
+ }
+
+ var pair = next.split('=');
+ var key = decodeURIComponent(pair[0]);
+ var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
+
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
+ // Check to see if this property has been met before.
+ if (Array.isArray(result[key])) {
+ // Is it already an array?
+ result[key].push(value);
+ } else {
+ // Make it an array.
+ result[key] = [result[key], value];
+ }
+ } else {
+ // First time seen, just add it.
+ result[key] = value;
+ }
+
+ return result;
+ }, {});
+ },
+
/*************************************/
/* Private functions under this line */
/*************************************/
+
+ /**
+ * Used to override the default digitalData values,
+ * the result will impact directly the current instance wem.digitalData
+ *
+ * @param {object[]} digitalDataOverrides list of overrides
+ * @private
+ * @return {undefined}
+ */
_handleDigitalDataOverrides: function
_handleDigitalDataOverrides(digitalDataOverrides) {
if (digitalDataOverrides && digitalDataOverrides.length > 0) {
var _iterator = _createForOfIteratorHelper(digitalDataOverrides),
@@ -799,6 +838,16 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Check for tracked conditions in the current loaded context, and attach
listeners for the known tracked condition types:
+ * - formEventCondition
+ * - videoViewEventCondition
+ * - clickOnLinkEventCondition
+ *
+ * @private
+ * @return {undefined}
+ */
_registerListenersForTrackedConditions: function
_registerListenersForTrackedConditions() {
console.info('[WEM] Check for tracked conditions and attach related HTML
listeners');
var videoNamesToWatch = [];
@@ -869,6 +918,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Check for currently registered events in wem.digitalData.events that
would be incomplete:
+ * - autocomplete the event with the current digitalData page infos for
the source
+ * @private
+ * @return {undefined}
+ */
_checkUncompleteRegisteredEvents: function
_checkUncompleteRegisteredEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator2 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -887,6 +943,12 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * dispatch JavaScript event in current HTML document for perso and opti
events contains in digitalData.events
+ * @private
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvents: function
_dispatchJSExperienceDisplayedEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator3 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -907,6 +969,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * build and dispatch JavaScript event in current HTML document for the
given Unomi event (perso/opti)
+ * @private
+ * @param {object} experienceUnomiEvent perso/opti Unomi event
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvent: function
_dispatchJSExperienceDisplayedEvent(experienceUnomiEvent) {
if (!wem.fallback && experienceUnomiEvent && experienceUnomiEvent.target
&& experienceUnomiEvent.target.properties &&
experienceUnomiEvent.target.properties.variants &&
experienceUnomiEvent.target.properties.variants.length > 0) {
var typeMapper = {
@@ -947,6 +1016,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Filter events in digitalData.events that would have the property:
event.properties.doNotSendToUnomi
+ * The effect is directly stored in a new version of wem.digitalData.events
+ * @private
+ * @return {undefined}
+ */
_filterUnomiEvents: function _filterUnomiEvents() {
if (wem.digitalData && wem.digitalData.events) {
wem.digitalData.events = wem.digitalData.events.filter(function
(event) {
@@ -960,6 +1036,15 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Check if event is incomplete and complete what is missing:
+ * - source: if missing, use the current source page
+ * - scope: if missing, use the current scope
+ * @param {object} event, the event to be checked
+ * @private
+ * @return {object} the complete event
+ */
_completeEvent: function _completeEvent(event) {
if (!event.source) {
event.source = wem.buildSourcePage();
@@ -975,6 +1060,19 @@ var newTracker = function newTracker() {
return event;
},
+
+ /**
+ * Register an event in the wem.digitalData.events.
+ * Registered event, will be sent automatically during the context loading
process.
+ *
+ * Beware this function is useless in case the context is already loaded.
+ * in case the context is already loaded (check: wem.getLoadedContext()),
then you should use: wem.collectEvent(s) functions
+ *
+ * @private
+ * @param {object} event the Unomi event to be registered
+ * @param {boolean} unshift if true, the event will be added at the
beginning of the list otherwise at the end of the list.
+ * @return {undefined}
+ */
_registerEvent: function _registerEvent(event, unshift) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -996,10 +1094,12 @@ var newTracker = function newTracker() {
/**
* This function allow for registering callback that will be executed once
the context is loaded.
- * @param onLoadCallback the callback to be executed
- * @param name optional name for the call, used mostly for logging the
execution
- * @param priority optional priority to execute the callbacks in a
specific order (default: 5, to leave room for the tracker default callback(s))
+ * @param {function} onLoadCallback the callback to be executed
+ * @param {string} name optional name for the call, used mostly for
logging the execution
+ * @param {number} priority optional priority to execute the callbacks in
a specific order
+ * (default: 5, to leave room for the tracker
default callback(s))
* @private
+ * @return {undefined}
*/
_registerCallback: function _registerCallback(onLoadCallback) {
var name = arguments.length > 1 && arguments[1] !== undefined ?
arguments[1] : undefined;
@@ -1039,6 +1139,14 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Internal function for personalization specific callbacks (used for HTML
dom manipulation once we get the context loaded)
+ * @param {object} personalization the personalization
+ * @param {function} callback the callback
+ * @private
+ * @return {undefined}
+ */
_registerPersonalizationCallback: function
_registerPersonalizationCallback(personalization, callback) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -1060,6 +1168,15 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Build a simple Unomi object
+ * @param {string} itemId the itemId of the object
+ * @param {string} itemType the itemType of the object
+ * @param {object} properties optional properties for the object
+ * @private
+ * @return {object} the built Unomi JSON object
+ */
_buildObject: function _buildObject(itemId, itemType, properties) {
var object = {
scope: wem.digitalData.scope,
@@ -1073,6 +1190,13 @@ var newTracker = function newTracker() {
return object;
},
+
+ /**
+ * Main callback used once the Ajax context request succeed
+ * @param {XMLHttpRequest} xhr the request
+ * @private
+ * @return {undefined}
+ */
_onSuccess: function _onSuccess(xhr) {
wem.cxs = JSON.parse(xhr.responseText);
@@ -1091,6 +1215,13 @@ var newTracker = function newTracker() {
window.wemLoaded = true;
},
+
+ /**
+ * Main callback used once the Ajax context request failed
+ * @param {string} logMessage the log message, to identify the place of
failure
+ * @private
+ * @return {undefined}
+ */
_executeFallback: function _executeFallback(logMessage) {
console.warn('[WEM] execute fallback' + (logMessage ? ': ' + logMessage
: '') + ', load fallback callbacks, calling now...');
wem.fallback = true;
@@ -1104,6 +1235,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Executed the registered context loaded callbacks
+ * @param {*} callbackParam param of the callbacks
+ * @private
+ * @return {undefined}
+ */
_executeLoadCallbacks: function _executeLoadCallbacks(callbackParam) {
if (wem.digitalData.loadCallbacks &&
wem.digitalData.loadCallbacks.length > 0) {
wem.digitalData.loadCallbacks.sort(function (a, b) {
@@ -1114,6 +1252,12 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Parse current HTML document referrer information to enrich the
digitalData page infos
+ * @private
+ * @return {undefined}
+ */
_processReferrer: function _processReferrer() {
var referrerURL = wem.digitalData.page.pageInfo.referringURL ||
document.referrer;
var sameDomainReferrer = false;
@@ -1157,6 +1301,16 @@ var newTracker = function newTracker() {
wem.digitalData.page.pageInfo.sameDomainReferrer = sameDomainReferrer;
},
+
+ /**
+ * Listener callback that can be attached to a specific HTML form,
+ * this listener will automatically send the form event to Unomi, by
parsing the HTML form data.
+ * (NOTE: the form listener only work for know forms to be watch due to
tracked conditions)
+ *
+ * @param {object} event the original HTML form submition event for the
watch form.
+ * @private
+ * @return {undefined}
+ */
_formSubmitEventListener: function _formSubmitEventListener(event) {
console.info('[WEM] Registering form event callback');
var form = event.target;
@@ -1197,6 +1351,14 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Utility function to extract data from an HTML form.
+ *
+ * @param {HTMLFormElement} form the HTML form element
+ * @private
+ * @return {object} the form data as JSON
+ */
_extractFormData: function _extractFormData(form) {
var params = {};
@@ -1263,6 +1425,13 @@ var newTracker = function newTracker() {
return params;
},
+
+ /**
+ * Internal function used for mapping ids when current HTML document ids
doesn't match ids stored in Unomi backend
+ * @param {string} id the id to resolve
+ * @return {string} the resolved id or the original id if not match found
+ * @private
+ */
_resolveId: function _resolveId(id) {
if (wem.digitalData.sourceLocalIdentifierMap) {
var source =
Object.keys(wem.digitalData.sourceLocalIdentifierMap).filter(function (source) {
@@ -1273,6 +1442,14 @@ var newTracker = function newTracker() {
return id;
},
+
+ /**
+ * Enable or disable tracking in current page
+ * @param {boolean} enable true will enable the tracking feature,
otherwise they will be disabled
+ * @param {function} callback an optional callback that can be used to
perform additional logic based on enabling/disabling results
+ * @private
+ * @return {undefined}
+ */
_enableWem: function _enableWem(enable, callback) {
// display fallback if wem is not enable
wem.fallback = !enable; // remove cookies, reset cxs
@@ -1297,6 +1474,14 @@ var newTracker = function newTracker() {
console.log("[WEM] successfully ".concat(enable ? 'enabled' :
'disabled', " tracking in current page"));
},
+
+ /**
+ * Utility function used to merge two JSON object together (arrays are
concat for example)
+ * @param {object} source the source object for merge
+ * @param {object} target the target object for merge
+ * @private
+ * @return {object} the merged results
+ */
_deepMergeObjects: function _deepMergeObjects(source, target) {
if (!wem._isObject(target) || !wem._isObject(source)) {
return source;
@@ -1316,9 +1501,23 @@ var newTracker = function newTracker() {
});
return target;
},
+
+ /**
+ * Utility function used to check if the given variable is a JavaScript
object.
+ * @param {*} obj the variable to check
+ * @private
+ * @return {boolean} true if the variable is an object, false otherwise
+ */
_isObject: function _isObject(obj) {
return obj && _typeof__default["default"](obj) === 'object';
},
+
+ /**
+ * Utility function used to check if the current id is contains in any
Unomi control group
+ * @param {string} id the id to check
+ * @private
+ * @return {boolean} true if the id is found in a control group, false
otherwise
+ */
_isInControlGroup: function _isInControlGroup(id) {
if (wem.cxs.profileProperties &&
wem.cxs.profileProperties.unomiControlGroups) {
var controlGroup =
wem.cxs.profileProperties.unomiControlGroups.find(function (controlGroup) {
diff --git a/dist/apache-unomi-tracker.esm.js b/dist/apache-unomi-tracker.esm.js
index 6febc91..567e119 100644
--- a/dist/apache-unomi-tracker.esm.js
+++ b/dist/apache-unomi-tracker.esm.js
@@ -12,12 +12,13 @@ var newTracker = function newTracker() {
* This function initialize the tracker
*
* @param {object} digitalData config of the tracker
+ * @returns {undefined}
*/
initTracker: function initTracker(digitalData) {
wem.digitalData = digitalData;
- wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : "wem-profile-id";
- wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : "wem-session-id";
- wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : "";
+ wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : 'wem-profile-id';
+ wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : 'wem-session-id';
+ wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : '';
wem.disableTrackedConditionsListeners =
wem.digitalData.wemInitConfig.disableTrackedConditionsListeners;
wem.activateWem = wem.digitalData.wemInitConfig.activateWem;
var _wem$digitalData$wemI = wem.digitalData.wemInitConfig,
@@ -45,6 +46,7 @@ var newTracker = function newTracker() {
* Note: that the tracker will start once the current DOM is complete
loaded, using listener on current document: DOMContentLoaded
*
* @param {object[]} digitalDataOverrides optional, list of digitalData
extensions, they will be merged with original digitalData before context loading
+ * @returns {undefined}
*/
startTracker: function startTracker() {
var digitalDataOverrides = arguments.length > 0 && arguments[0] !==
undefined ? arguments[0] : undefined;
@@ -144,51 +146,20 @@ var newTracker = function newTracker() {
/**
* Get current session id
- * @returns {null|*}
+ * @returns {null|string} get current session id
*/
getSessionId: function getSessionId() {
return wem.sessionID;
},
- convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
- if (!searchString) {
- return null;
- }
-
- return searchString.replace(/^\?/, '') // Only trim off a single leading
interrobang.
- .split('&').reduce(function (result, next) {
- if (next === '') {
- return result;
- }
-
- var pair = next.split('=');
- var key = decodeURIComponent(pair[0]);
- var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
-
- if (Object.prototype.hasOwnProperty.call(result, key)) {
- // Check to see if this property has been met before.
- if (Array.isArray(result[key])) {
- // Is it already an array?
- result[key].push(value);
- } else {
- // Make it an array.
- result[key] = [result[key], value];
- }
- } else {
- // First time seen, just add it.
- result[key] = value;
- }
-
- return result;
- }, {});
- },
/**
* This function will register a personalization
*
- * @param {object} personalization
- * @param {object} variants
+ * @param {object} personalization the personalization object
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {function} [resultCallback]
+ * @param {function} [resultCallback] the callback to be executed after
personalization resolved
+ * @returns {undefined}
*/
registerPersonalizationObject: function
registerPersonalizationObject(personalization, variants, ajax, resultCallback) {
var target = personalization.id;
@@ -265,12 +236,13 @@ var newTracker = function newTracker() {
/**
* This function will register an optimization test or A/B test
*
- * @param {string} optimizationTestNodeId
- * @param {string} goalId
- * @param {string} containerId
- * @param {object} variants
+ * @param {string} optimizationTestNodeId the optimization test node id
+ * @param {string} goalId the associated goal Id
+ * @param {string} containerId the HTML container Id
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {object} [variantsTraffic]
+ * @param {object} [variantsTraffic] the associated traffic allocation
+ * @return {undefined}
*/
registerOptimizationTest: function
registerOptimizationTest(optimizationTestNodeId, goalId, containerId, variants,
ajax, variantsTraffic) {
// check persona panel forced variant
@@ -336,6 +308,7 @@ var newTracker = function newTracker() {
*
* @param {boolean} [skipEvents=false] Should we send the events
* @param {boolean} [invalidate=false] Should we invalidate the current
context
+ * @return {undefined}
*/
loadContext: function loadContext(skipEvents, invalidate) {
if (wem.contextLoaded) {
@@ -391,6 +364,7 @@ var newTracker = function newTracker() {
* @param {object} event The event object to send, you can build it using
wem.buildEvent(eventType, target, source)
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvent: function collectEvent(event, successCallback, errorCallback)
{
wem.collectEvents({
@@ -404,6 +378,7 @@ var newTracker = function newTracker() {
* @param {object} events Javascript object { events: [event1, event2] }
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvents: function collectEvents(events, successCallback,
errorCallback) {
if (wem.fallback) {
@@ -432,6 +407,7 @@ var newTracker = function newTracker() {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendClickEvent: function sendClickEvent(event, successCallback,
errorCallback) {
if (event.target.id || event.target.name) {
@@ -473,6 +449,7 @@ var newTracker = function newTracker() {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendVideoEvent: function sendVideoEvent(event, successCallback,
errorCallback) {
console.info('[WEM] catching video event');
@@ -498,6 +475,7 @@ var newTracker = function newTracker() {
* This function will invalidate the Apache Unomi session and profile,
* by removing the associated cookies, set the loaded context to undefined
* and set the session id cookie with a newly generated ID
+ * @return {undefined}
*/
invalidateSessionAndProfile: function invalidateSessionAndProfile() {
@@ -514,7 +492,7 @@ var newTracker = function newTracker() {
* @param {string} eventType The name of your event
* @param {object} [target] The target object for your event can be build
with wem.buildTarget(targetId, targetType, targetProperties)
* @param {object} [source] The source object for your event can be build
with wem.buildSource(sourceId, sourceType, sourceProperties)
- * @returns {{eventType: *, scope}}
+ * @returns {object} the event
*/
buildEvent: function buildEvent(eventType, target, source) {
var event = {
@@ -537,7 +515,7 @@ var newTracker = function newTracker() {
* This function return an event of type form
*
* @param {string} formName The HTML name of id of the form to use in the
target of the event
- * @returns {*|{eventType: *, scope, source: {scope, itemId: string,
itemType: string, properties: {}}, target: {scope, itemId: string, itemType:
string, properties: {}}}}
+ * @returns {object} the form event
*/
buildFormEvent: function buildFormEvent(formName) {
return wem.buildEvent('form', wem.buildTarget(formName, 'form'),
wem.buildSourcePage());
@@ -546,7 +524,7 @@ var newTracker = function newTracker() {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the target page
*/
buildTargetPage: function buildTargetPage() {
return wem.buildTarget(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -555,7 +533,7 @@ var newTracker = function newTracker() {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the source page
*/
buildSourcePage: function buildSourcePage() {
return wem.buildSource(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -567,7 +545,7 @@ var newTracker = function newTracker() {
* @param {string} targetId The ID of the target
* @param {string} targetType The type of the target
* @param {object} [targetProperties] The optional properties of the target
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the target
*/
buildTarget: function buildTarget(targetId, targetType, targetProperties) {
return wem._buildObject(targetId, targetType, targetProperties);
@@ -579,7 +557,7 @@ var newTracker = function newTracker() {
* @param {string} sourceId The ID of the source
* @param {string} sourceType The type of the source
* @param {object} [sourceProperties] The optional properties of the source
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the source
*/
buildSource: function buildSource(sourceId, sourceType, sourceProperties) {
return wem._buildObject(sourceId, sourceType, sourceProperties);
@@ -597,6 +575,7 @@ var newTracker = function newTracker() {
* @param {string} cookieName name of the cookie
* @param {string} cookieValue value of the cookie
* @param {number} [expireDays] number of days to set the expire date
+ * @return {undefined}
*/
setCookie: function setCookie(cookieName, cookieValue, expireDays) {
var expires = '';
@@ -614,7 +593,7 @@ var newTracker = function newTracker() {
* This is an utility function to get a cookie
*
* @param {string} cookieName name of the cookie to get
- * @returns {*} the value of the first cookie with the corresponding name
or null if not found
+ * @returns {string} the value of the first cookie with the corresponding
name or null if not found
*/
getCookie: function getCookie(cookieName) {
var name = cookieName + '=';
@@ -639,6 +618,7 @@ var newTracker = function newTracker() {
* This is an utility function to remove a cookie
*
* @param {string} cookieName the name of the cookie to rename
+ * @return {undefined}
*/
removeCookie: function removeCookie(cookieName) {
@@ -648,7 +628,8 @@ var newTracker = function newTracker() {
/**
* This is an utility function to execute AJAX call
*
- * @param {object} options
+ * @param {object} options options of the request
+ * @return {undefined}
*/
ajax: function ajax(options) {
var xhr = new XMLHttpRequest();
@@ -725,7 +706,7 @@ var newTracker = function newTracker() {
/**
* This is an utility function to generate a new UUID
*
- * @returns {string}
+ * @returns {string} the newly generated UUID
*/
generateGuid: function generateGuid() {
function s4() {
@@ -737,8 +718,8 @@ var newTracker = function newTracker() {
/**
* This is an utility function to check if the local storage is available
or not
- * @param type
- * @returns {boolean}
+ * @param {string} type the type of storage to test
+ * @returns {boolean} true in case storage is available, false otherwise
*/
storageAvailable: function storageAvailable(type) {
try {
@@ -751,6 +732,16 @@ var newTracker = function newTracker() {
return false;
}
},
+
+ /**
+ * Dispatch a JavaScript event in current HTML document
+ *
+ * @param {string} name the name of the event
+ * @param {boolean} canBubble does the event can bubble ?
+ * @param {boolean} cancelable is the event cancelable ?
+ * @param {*} detail event details
+ * @return {undefined}
+ */
dispatchJSEvent: function dispatchJSEvent(name, canBubble, cancelable,
detail) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, canBubble, cancelable, detail);
@@ -759,8 +750,9 @@ var newTracker = function newTracker() {
/**
* This is an utility function to get current url parameter value
- * @param name, the name of the parameter
- * @returns {string}
+ *
+ * @param {string} name, the name of the parameter
+ * @returns {string} the value of the parameter
*/
getUrlParameter: function getUrlParameter(name) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
@@ -769,11 +761,58 @@ var newTracker = function newTracker() {
return results === null ? null :
decodeURIComponent(results[1].replace(/\+/g, ' '));
},
+ /**
+ * convert the passed query string into JS object.
+ * @param {string} searchString The URL query string
+ * @returns {object} converted URL params
+ */
+ convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
+ if (!searchString) {
+ return null;
+ }
+
+ return searchString.replace(/^\?/, '') // Only trim off a single leading
interrobang.
+ .split('&').reduce(function (result, next) {
+ if (next === '') {
+ return result;
+ }
+
+ var pair = next.split('=');
+ var key = decodeURIComponent(pair[0]);
+ var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
+
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
+ // Check to see if this property has been met before.
+ if (Array.isArray(result[key])) {
+ // Is it already an array?
+ result[key].push(value);
+ } else {
+ // Make it an array.
+ result[key] = [result[key], value];
+ }
+ } else {
+ // First time seen, just add it.
+ result[key] = value;
+ }
+
+ return result;
+ }, {});
+ },
+
/*************************************/
/* Private functions under this line */
/*************************************/
+
+ /**
+ * Used to override the default digitalData values,
+ * the result will impact directly the current instance wem.digitalData
+ *
+ * @param {object[]} digitalDataOverrides list of overrides
+ * @private
+ * @return {undefined}
+ */
_handleDigitalDataOverrides: function
_handleDigitalDataOverrides(digitalDataOverrides) {
if (digitalDataOverrides && digitalDataOverrides.length > 0) {
var _iterator = _createForOfIteratorHelper(digitalDataOverrides),
@@ -791,6 +830,16 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Check for tracked conditions in the current loaded context, and attach
listeners for the known tracked condition types:
+ * - formEventCondition
+ * - videoViewEventCondition
+ * - clickOnLinkEventCondition
+ *
+ * @private
+ * @return {undefined}
+ */
_registerListenersForTrackedConditions: function
_registerListenersForTrackedConditions() {
console.info('[WEM] Check for tracked conditions and attach related HTML
listeners');
var videoNamesToWatch = [];
@@ -861,6 +910,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Check for currently registered events in wem.digitalData.events that
would be incomplete:
+ * - autocomplete the event with the current digitalData page infos for
the source
+ * @private
+ * @return {undefined}
+ */
_checkUncompleteRegisteredEvents: function
_checkUncompleteRegisteredEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator2 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -879,6 +935,12 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * dispatch JavaScript event in current HTML document for perso and opti
events contains in digitalData.events
+ * @private
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvents: function
_dispatchJSExperienceDisplayedEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator3 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -899,6 +961,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * build and dispatch JavaScript event in current HTML document for the
given Unomi event (perso/opti)
+ * @private
+ * @param {object} experienceUnomiEvent perso/opti Unomi event
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvent: function
_dispatchJSExperienceDisplayedEvent(experienceUnomiEvent) {
if (!wem.fallback && experienceUnomiEvent && experienceUnomiEvent.target
&& experienceUnomiEvent.target.properties &&
experienceUnomiEvent.target.properties.variants &&
experienceUnomiEvent.target.properties.variants.length > 0) {
var typeMapper = {
@@ -939,6 +1008,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Filter events in digitalData.events that would have the property:
event.properties.doNotSendToUnomi
+ * The effect is directly stored in a new version of wem.digitalData.events
+ * @private
+ * @return {undefined}
+ */
_filterUnomiEvents: function _filterUnomiEvents() {
if (wem.digitalData && wem.digitalData.events) {
wem.digitalData.events = wem.digitalData.events.filter(function
(event) {
@@ -952,6 +1028,15 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Check if event is incomplete and complete what is missing:
+ * - source: if missing, use the current source page
+ * - scope: if missing, use the current scope
+ * @param {object} event, the event to be checked
+ * @private
+ * @return {object} the complete event
+ */
_completeEvent: function _completeEvent(event) {
if (!event.source) {
event.source = wem.buildSourcePage();
@@ -967,6 +1052,19 @@ var newTracker = function newTracker() {
return event;
},
+
+ /**
+ * Register an event in the wem.digitalData.events.
+ * Registered event, will be sent automatically during the context loading
process.
+ *
+ * Beware this function is useless in case the context is already loaded.
+ * in case the context is already loaded (check: wem.getLoadedContext()),
then you should use: wem.collectEvent(s) functions
+ *
+ * @private
+ * @param {object} event the Unomi event to be registered
+ * @param {boolean} unshift if true, the event will be added at the
beginning of the list otherwise at the end of the list.
+ * @return {undefined}
+ */
_registerEvent: function _registerEvent(event, unshift) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -988,10 +1086,12 @@ var newTracker = function newTracker() {
/**
* This function allow for registering callback that will be executed once
the context is loaded.
- * @param onLoadCallback the callback to be executed
- * @param name optional name for the call, used mostly for logging the
execution
- * @param priority optional priority to execute the callbacks in a
specific order (default: 5, to leave room for the tracker default callback(s))
+ * @param {function} onLoadCallback the callback to be executed
+ * @param {string} name optional name for the call, used mostly for
logging the execution
+ * @param {number} priority optional priority to execute the callbacks in
a specific order
+ * (default: 5, to leave room for the tracker
default callback(s))
* @private
+ * @return {undefined}
*/
_registerCallback: function _registerCallback(onLoadCallback) {
var name = arguments.length > 1 && arguments[1] !== undefined ?
arguments[1] : undefined;
@@ -1031,6 +1131,14 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Internal function for personalization specific callbacks (used for HTML
dom manipulation once we get the context loaded)
+ * @param {object} personalization the personalization
+ * @param {function} callback the callback
+ * @private
+ * @return {undefined}
+ */
_registerPersonalizationCallback: function
_registerPersonalizationCallback(personalization, callback) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -1052,6 +1160,15 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Build a simple Unomi object
+ * @param {string} itemId the itemId of the object
+ * @param {string} itemType the itemType of the object
+ * @param {object} properties optional properties for the object
+ * @private
+ * @return {object} the built Unomi JSON object
+ */
_buildObject: function _buildObject(itemId, itemType, properties) {
var object = {
scope: wem.digitalData.scope,
@@ -1065,6 +1182,13 @@ var newTracker = function newTracker() {
return object;
},
+
+ /**
+ * Main callback used once the Ajax context request succeed
+ * @param {XMLHttpRequest} xhr the request
+ * @private
+ * @return {undefined}
+ */
_onSuccess: function _onSuccess(xhr) {
wem.cxs = JSON.parse(xhr.responseText);
@@ -1083,6 +1207,13 @@ var newTracker = function newTracker() {
window.wemLoaded = true;
},
+
+ /**
+ * Main callback used once the Ajax context request failed
+ * @param {string} logMessage the log message, to identify the place of
failure
+ * @private
+ * @return {undefined}
+ */
_executeFallback: function _executeFallback(logMessage) {
console.warn('[WEM] execute fallback' + (logMessage ? ': ' + logMessage
: '') + ', load fallback callbacks, calling now...');
wem.fallback = true;
@@ -1096,6 +1227,13 @@ var newTracker = function newTracker() {
}
}
},
+
+ /**
+ * Executed the registered context loaded callbacks
+ * @param {*} callbackParam param of the callbacks
+ * @private
+ * @return {undefined}
+ */
_executeLoadCallbacks: function _executeLoadCallbacks(callbackParam) {
if (wem.digitalData.loadCallbacks &&
wem.digitalData.loadCallbacks.length > 0) {
wem.digitalData.loadCallbacks.sort(function (a, b) {
@@ -1106,6 +1244,12 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Parse current HTML document referrer information to enrich the
digitalData page infos
+ * @private
+ * @return {undefined}
+ */
_processReferrer: function _processReferrer() {
var referrerURL = wem.digitalData.page.pageInfo.referringURL ||
document.referrer;
var sameDomainReferrer = false;
@@ -1149,6 +1293,16 @@ var newTracker = function newTracker() {
wem.digitalData.page.pageInfo.sameDomainReferrer = sameDomainReferrer;
},
+
+ /**
+ * Listener callback that can be attached to a specific HTML form,
+ * this listener will automatically send the form event to Unomi, by
parsing the HTML form data.
+ * (NOTE: the form listener only work for know forms to be watch due to
tracked conditions)
+ *
+ * @param {object} event the original HTML form submition event for the
watch form.
+ * @private
+ * @return {undefined}
+ */
_formSubmitEventListener: function _formSubmitEventListener(event) {
console.info('[WEM] Registering form event callback');
var form = event.target;
@@ -1189,6 +1343,14 @@ var newTracker = function newTracker() {
});
}
},
+
+ /**
+ * Utility function to extract data from an HTML form.
+ *
+ * @param {HTMLFormElement} form the HTML form element
+ * @private
+ * @return {object} the form data as JSON
+ */
_extractFormData: function _extractFormData(form) {
var params = {};
@@ -1255,6 +1417,13 @@ var newTracker = function newTracker() {
return params;
},
+
+ /**
+ * Internal function used for mapping ids when current HTML document ids
doesn't match ids stored in Unomi backend
+ * @param {string} id the id to resolve
+ * @return {string} the resolved id or the original id if not match found
+ * @private
+ */
_resolveId: function _resolveId(id) {
if (wem.digitalData.sourceLocalIdentifierMap) {
var source =
Object.keys(wem.digitalData.sourceLocalIdentifierMap).filter(function (source) {
@@ -1265,6 +1434,14 @@ var newTracker = function newTracker() {
return id;
},
+
+ /**
+ * Enable or disable tracking in current page
+ * @param {boolean} enable true will enable the tracking feature,
otherwise they will be disabled
+ * @param {function} callback an optional callback that can be used to
perform additional logic based on enabling/disabling results
+ * @private
+ * @return {undefined}
+ */
_enableWem: function _enableWem(enable, callback) {
// display fallback if wem is not enable
wem.fallback = !enable; // remove cookies, reset cxs
@@ -1289,6 +1466,14 @@ var newTracker = function newTracker() {
console.log("[WEM] successfully ".concat(enable ? 'enabled' :
'disabled', " tracking in current page"));
},
+
+ /**
+ * Utility function used to merge two JSON object together (arrays are
concat for example)
+ * @param {object} source the source object for merge
+ * @param {object} target the target object for merge
+ * @private
+ * @return {object} the merged results
+ */
_deepMergeObjects: function _deepMergeObjects(source, target) {
if (!wem._isObject(target) || !wem._isObject(source)) {
return source;
@@ -1308,9 +1493,23 @@ var newTracker = function newTracker() {
});
return target;
},
+
+ /**
+ * Utility function used to check if the given variable is a JavaScript
object.
+ * @param {*} obj the variable to check
+ * @private
+ * @return {boolean} true if the variable is an object, false otherwise
+ */
_isObject: function _isObject(obj) {
return obj && _typeof(obj) === 'object';
},
+
+ /**
+ * Utility function used to check if the current id is contains in any
Unomi control group
+ * @param {string} id the id to check
+ * @private
+ * @return {boolean} true if the id is found in a control group, false
otherwise
+ */
_isInControlGroup: function _isInControlGroup(id) {
if (wem.cxs.profileProperties &&
wem.cxs.profileProperties.unomiControlGroups) {
var controlGroup =
wem.cxs.profileProperties.unomiControlGroups.find(function (controlGroup) {
diff --git a/dist/apache-unomi-tracker.umd.js b/dist/apache-unomi-tracker.umd.js
index f6163a8..2477420 100644
--- a/dist/apache-unomi-tracker.umd.js
+++ b/dist/apache-unomi-tracker.umd.js
@@ -1667,12 +1667,13 @@
* This function initialize the tracker
*
* @param {object} digitalData config of the tracker
+ * @returns {undefined}
*/
initTracker: function initTracker(digitalData) {
wem.digitalData = digitalData;
- wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : "wem-profile-id";
- wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : "wem-session-id";
- wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : "";
+ wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : 'wem-profile-id';
+ wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : 'wem-session-id';
+ wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : '';
wem.disableTrackedConditionsListeners =
wem.digitalData.wemInitConfig.disableTrackedConditionsListeners;
wem.activateWem = wem.digitalData.wemInitConfig.activateWem;
var _wem$digitalData$wemI = wem.digitalData.wemInitConfig,
@@ -1700,6 +1701,7 @@
* Note: that the tracker will start once the current DOM is complete
loaded, using listener on current document: DOMContentLoaded
*
* @param {object[]} digitalDataOverrides optional, list of digitalData
extensions, they will be merged with original digitalData before context loading
+ * @returns {undefined}
*/
startTracker: function startTracker() {
var digitalDataOverrides = arguments.length > 0 && arguments[0] !==
undefined ? arguments[0] : undefined;
@@ -1799,51 +1801,20 @@
/**
* Get current session id
- * @returns {null|*}
+ * @returns {null|string} get current session id
*/
getSessionId: function getSessionId() {
return wem.sessionID;
},
- convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
- if (!searchString) {
- return null;
- }
-
- return searchString.replace(/^\?/, '') // Only trim off a single
leading interrobang.
- .split('&').reduce(function (result, next) {
- if (next === '') {
- return result;
- }
-
- var pair = next.split('=');
- var key = decodeURIComponent(pair[0]);
- var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
-
- if (Object.prototype.hasOwnProperty.call(result, key)) {
- // Check to see if this property has been met before.
- if (Array.isArray(result[key])) {
- // Is it already an array?
- result[key].push(value);
- } else {
- // Make it an array.
- result[key] = [result[key], value];
- }
- } else {
- // First time seen, just add it.
- result[key] = value;
- }
-
- return result;
- }, {});
- },
/**
* This function will register a personalization
*
- * @param {object} personalization
- * @param {object} variants
+ * @param {object} personalization the personalization object
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {function} [resultCallback]
+ * @param {function} [resultCallback] the callback to be executed after
personalization resolved
+ * @returns {undefined}
*/
registerPersonalizationObject: function
registerPersonalizationObject(personalization, variants, ajax, resultCallback) {
var target = personalization.id;
@@ -1920,12 +1891,13 @@
/**
* This function will register an optimization test or A/B test
*
- * @param {string} optimizationTestNodeId
- * @param {string} goalId
- * @param {string} containerId
- * @param {object} variants
+ * @param {string} optimizationTestNodeId the optimization test node id
+ * @param {string} goalId the associated goal Id
+ * @param {string} containerId the HTML container Id
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {object} [variantsTraffic]
+ * @param {object} [variantsTraffic] the associated traffic allocation
+ * @return {undefined}
*/
registerOptimizationTest: function
registerOptimizationTest(optimizationTestNodeId, goalId, containerId, variants,
ajax, variantsTraffic) {
// check persona panel forced variant
@@ -1991,6 +1963,7 @@
*
* @param {boolean} [skipEvents=false] Should we send the events
* @param {boolean} [invalidate=false] Should we invalidate the current
context
+ * @return {undefined}
*/
loadContext: function loadContext(skipEvents, invalidate) {
if (wem.contextLoaded) {
@@ -2046,6 +2019,7 @@
* @param {object} event The event object to send, you can build it
using wem.buildEvent(eventType, target, source)
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvent: function collectEvent(event, successCallback,
errorCallback) {
wem.collectEvents({
@@ -2059,6 +2033,7 @@
* @param {object} events Javascript object { events: [event1, event2] }
* @param {function} successCallback will be executed in case of success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvents: function collectEvents(events, successCallback,
errorCallback) {
if (wem.fallback) {
@@ -2087,6 +2062,7 @@
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of
success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendClickEvent: function sendClickEvent(event, successCallback,
errorCallback) {
if (event.target.id || event.target.name) {
@@ -2128,6 +2104,7 @@
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of
success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendVideoEvent: function sendVideoEvent(event, successCallback,
errorCallback) {
console.info('[WEM] catching video event');
@@ -2153,6 +2130,7 @@
* This function will invalidate the Apache Unomi session and profile,
* by removing the associated cookies, set the loaded context to
undefined
* and set the session id cookie with a newly generated ID
+ * @return {undefined}
*/
invalidateSessionAndProfile: function invalidateSessionAndProfile() {
@@ -2169,7 +2147,7 @@
* @param {string} eventType The name of your event
* @param {object} [target] The target object for your event can be
build with wem.buildTarget(targetId, targetType, targetProperties)
* @param {object} [source] The source object for your event can be
build with wem.buildSource(sourceId, sourceType, sourceProperties)
- * @returns {{eventType: *, scope}}
+ * @returns {object} the event
*/
buildEvent: function buildEvent(eventType, target, source) {
var event = {
@@ -2192,7 +2170,7 @@
* This function return an event of type form
*
* @param {string} formName The HTML name of id of the form to use in
the target of the event
- * @returns {*|{eventType: *, scope, source: {scope, itemId: string,
itemType: string, properties: {}}, target: {scope, itemId: string, itemType:
string, properties: {}}}}
+ * @returns {object} the form event
*/
buildFormEvent: function buildFormEvent(formName) {
return wem.buildEvent('form', wem.buildTarget(formName, 'form'),
wem.buildSourcePage());
@@ -2201,7 +2179,7 @@
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the target page
*/
buildTargetPage: function buildTargetPage() {
return wem.buildTarget(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -2210,7 +2188,7 @@
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the source page
*/
buildSourcePage: function buildSourcePage() {
return wem.buildSource(wem.digitalData.page.pageInfo.pageID, 'page',
wem.digitalData.page);
@@ -2222,7 +2200,7 @@
* @param {string} targetId The ID of the target
* @param {string} targetType The type of the target
* @param {object} [targetProperties] The optional properties of the
target
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the target
*/
buildTarget: function buildTarget(targetId, targetType,
targetProperties) {
return wem._buildObject(targetId, targetType, targetProperties);
@@ -2234,7 +2212,7 @@
* @param {string} sourceId The ID of the source
* @param {string} sourceType The type of the source
* @param {object} [sourceProperties] The optional properties of the
source
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the source
*/
buildSource: function buildSource(sourceId, sourceType,
sourceProperties) {
return wem._buildObject(sourceId, sourceType, sourceProperties);
@@ -2252,6 +2230,7 @@
* @param {string} cookieName name of the cookie
* @param {string} cookieValue value of the cookie
* @param {number} [expireDays] number of days to set the expire date
+ * @return {undefined}
*/
setCookie: function setCookie(cookieName, cookieValue, expireDays) {
var expires = '';
@@ -2269,7 +2248,7 @@
* This is an utility function to get a cookie
*
* @param {string} cookieName name of the cookie to get
- * @returns {*} the value of the first cookie with the corresponding
name or null if not found
+ * @returns {string} the value of the first cookie with the
corresponding name or null if not found
*/
getCookie: function getCookie(cookieName) {
var name = cookieName + '=';
@@ -2294,6 +2273,7 @@
* This is an utility function to remove a cookie
*
* @param {string} cookieName the name of the cookie to rename
+ * @return {undefined}
*/
removeCookie: function removeCookie(cookieName) {
@@ -2303,7 +2283,8 @@
/**
* This is an utility function to execute AJAX call
*
- * @param {object} options
+ * @param {object} options options of the request
+ * @return {undefined}
*/
ajax: function ajax(options) {
var xhr = new XMLHttpRequest();
@@ -2380,7 +2361,7 @@
/**
* This is an utility function to generate a new UUID
*
- * @returns {string}
+ * @returns {string} the newly generated UUID
*/
generateGuid: function generateGuid() {
function s4() {
@@ -2392,8 +2373,8 @@
/**
* This is an utility function to check if the local storage is
available or not
- * @param type
- * @returns {boolean}
+ * @param {string} type the type of storage to test
+ * @returns {boolean} true in case storage is available, false otherwise
*/
storageAvailable: function storageAvailable(type) {
try {
@@ -2406,6 +2387,16 @@
return false;
}
},
+
+ /**
+ * Dispatch a JavaScript event in current HTML document
+ *
+ * @param {string} name the name of the event
+ * @param {boolean} canBubble does the event can bubble ?
+ * @param {boolean} cancelable is the event cancelable ?
+ * @param {*} detail event details
+ * @return {undefined}
+ */
dispatchJSEvent: function dispatchJSEvent(name, canBubble, cancelable,
detail) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, canBubble, cancelable, detail);
@@ -2414,8 +2405,9 @@
/**
* This is an utility function to get current url parameter value
- * @param name, the name of the parameter
- * @returns {string}
+ *
+ * @param {string} name, the name of the parameter
+ * @returns {string} the value of the parameter
*/
getUrlParameter: function getUrlParameter(name) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
@@ -2424,11 +2416,58 @@
return results === null ? null :
decodeURIComponent(results[1].replace(/\+/g, ' '));
},
+ /**
+ * convert the passed query string into JS object.
+ * @param {string} searchString The URL query string
+ * @returns {object} converted URL params
+ */
+ convertUrlParametersToObj: function
convertUrlParametersToObj(searchString) {
+ if (!searchString) {
+ return null;
+ }
+
+ return searchString.replace(/^\?/, '') // Only trim off a single
leading interrobang.
+ .split('&').reduce(function (result, next) {
+ if (next === '') {
+ return result;
+ }
+
+ var pair = next.split('=');
+ var key = decodeURIComponent(pair[0]);
+ var value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
+
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
+ // Check to see if this property has been met before.
+ if (Array.isArray(result[key])) {
+ // Is it already an array?
+ result[key].push(value);
+ } else {
+ // Make it an array.
+ result[key] = [result[key], value];
+ }
+ } else {
+ // First time seen, just add it.
+ result[key] = value;
+ }
+
+ return result;
+ }, {});
+ },
+
/*************************************/
/* Private functions under this line */
/*************************************/
+
+ /**
+ * Used to override the default digitalData values,
+ * the result will impact directly the current instance wem.digitalData
+ *
+ * @param {object[]} digitalDataOverrides list of overrides
+ * @private
+ * @return {undefined}
+ */
_handleDigitalDataOverrides: function
_handleDigitalDataOverrides(digitalDataOverrides) {
if (digitalDataOverrides && digitalDataOverrides.length > 0) {
var _iterator = _createForOfIteratorHelper(digitalDataOverrides),
@@ -2446,6 +2485,16 @@
}
}
},
+
+ /**
+ * Check for tracked conditions in the current loaded context, and
attach listeners for the known tracked condition types:
+ * - formEventCondition
+ * - videoViewEventCondition
+ * - clickOnLinkEventCondition
+ *
+ * @private
+ * @return {undefined}
+ */
_registerListenersForTrackedConditions: function
_registerListenersForTrackedConditions() {
console.info('[WEM] Check for tracked conditions and attach related
HTML listeners');
var videoNamesToWatch = [];
@@ -2516,6 +2565,13 @@
}
}
},
+
+ /**
+ * Check for currently registered events in wem.digitalData.events that
would be incomplete:
+ * - autocomplete the event with the current digitalData page infos for
the source
+ * @private
+ * @return {undefined}
+ */
_checkUncompleteRegisteredEvents: function
_checkUncompleteRegisteredEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator2 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -2534,6 +2590,12 @@
}
}
},
+
+ /**
+ * dispatch JavaScript event in current HTML document for perso and opti
events contains in digitalData.events
+ * @private
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvents: function
_dispatchJSExperienceDisplayedEvents() {
if (wem.digitalData && wem.digitalData.events) {
var _iterator3 = _createForOfIteratorHelper(wem.digitalData.events),
@@ -2554,6 +2616,13 @@
}
}
},
+
+ /**
+ * build and dispatch JavaScript event in current HTML document for the
given Unomi event (perso/opti)
+ * @private
+ * @param {object} experienceUnomiEvent perso/opti Unomi event
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvent: function
_dispatchJSExperienceDisplayedEvent(experienceUnomiEvent) {
if (!wem.fallback && experienceUnomiEvent &&
experienceUnomiEvent.target && experienceUnomiEvent.target.properties &&
experienceUnomiEvent.target.properties.variants &&
experienceUnomiEvent.target.properties.variants.length > 0) {
var typeMapper = {
@@ -2594,6 +2663,13 @@
}
}
},
+
+ /**
+ * Filter events in digitalData.events that would have the property:
event.properties.doNotSendToUnomi
+ * The effect is directly stored in a new version of
wem.digitalData.events
+ * @private
+ * @return {undefined}
+ */
_filterUnomiEvents: function _filterUnomiEvents() {
if (wem.digitalData && wem.digitalData.events) {
wem.digitalData.events = wem.digitalData.events.filter(function
(event) {
@@ -2607,6 +2683,15 @@
});
}
},
+
+ /**
+ * Check if event is incomplete and complete what is missing:
+ * - source: if missing, use the current source page
+ * - scope: if missing, use the current scope
+ * @param {object} event, the event to be checked
+ * @private
+ * @return {object} the complete event
+ */
_completeEvent: function _completeEvent(event) {
if (!event.source) {
event.source = wem.buildSourcePage();
@@ -2622,6 +2707,19 @@
return event;
},
+
+ /**
+ * Register an event in the wem.digitalData.events.
+ * Registered event, will be sent automatically during the context
loading process.
+ *
+ * Beware this function is useless in case the context is already loaded.
+ * in case the context is already loaded (check:
wem.getLoadedContext()), then you should use: wem.collectEvent(s) functions
+ *
+ * @private
+ * @param {object} event the Unomi event to be registered
+ * @param {boolean} unshift if true, the event will be added at the
beginning of the list otherwise at the end of the list.
+ * @return {undefined}
+ */
_registerEvent: function _registerEvent(event, unshift) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -2643,10 +2741,12 @@
/**
* This function allow for registering callback that will be executed
once the context is loaded.
- * @param onLoadCallback the callback to be executed
- * @param name optional name for the call, used mostly for logging the
execution
- * @param priority optional priority to execute the callbacks in a
specific order (default: 5, to leave room for the tracker default callback(s))
+ * @param {function} onLoadCallback the callback to be executed
+ * @param {string} name optional name for the call, used mostly for
logging the execution
+ * @param {number} priority optional priority to execute the callbacks
in a specific order
+ * (default: 5, to leave room for the tracker
default callback(s))
* @private
+ * @return {undefined}
*/
_registerCallback: function _registerCallback(onLoadCallback) {
var name = arguments.length > 1 && arguments[1] !== undefined ?
arguments[1] : undefined;
@@ -2686,6 +2786,14 @@
}
}
},
+
+ /**
+ * Internal function for personalization specific callbacks (used for
HTML dom manipulation once we get the context loaded)
+ * @param {object} personalization the personalization
+ * @param {function} callback the callback
+ * @private
+ * @return {undefined}
+ */
_registerPersonalizationCallback: function
_registerPersonalizationCallback(personalization, callback) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -2707,6 +2815,15 @@
});
}
},
+
+ /**
+ * Build a simple Unomi object
+ * @param {string} itemId the itemId of the object
+ * @param {string} itemType the itemType of the object
+ * @param {object} properties optional properties for the object
+ * @private
+ * @return {object} the built Unomi JSON object
+ */
_buildObject: function _buildObject(itemId, itemType, properties) {
var object = {
scope: wem.digitalData.scope,
@@ -2720,6 +2837,13 @@
return object;
},
+
+ /**
+ * Main callback used once the Ajax context request succeed
+ * @param {XMLHttpRequest} xhr the request
+ * @private
+ * @return {undefined}
+ */
_onSuccess: function _onSuccess(xhr) {
wem.cxs = JSON.parse(xhr.responseText);
@@ -2738,6 +2862,13 @@
window.wemLoaded = true;
},
+
+ /**
+ * Main callback used once the Ajax context request failed
+ * @param {string} logMessage the log message, to identify the place of
failure
+ * @private
+ * @return {undefined}
+ */
_executeFallback: function _executeFallback(logMessage) {
console.warn('[WEM] execute fallback' + (logMessage ? ': ' +
logMessage : '') + ', load fallback callbacks, calling now...');
wem.fallback = true;
@@ -2751,6 +2882,13 @@
}
}
},
+
+ /**
+ * Executed the registered context loaded callbacks
+ * @param {*} callbackParam param of the callbacks
+ * @private
+ * @return {undefined}
+ */
_executeLoadCallbacks: function _executeLoadCallbacks(callbackParam) {
if (wem.digitalData.loadCallbacks &&
wem.digitalData.loadCallbacks.length > 0) {
wem.digitalData.loadCallbacks.sort(function (a, b) {
@@ -2761,6 +2899,12 @@
});
}
},
+
+ /**
+ * Parse current HTML document referrer information to enrich the
digitalData page infos
+ * @private
+ * @return {undefined}
+ */
_processReferrer: function _processReferrer() {
var referrerURL = wem.digitalData.page.pageInfo.referringURL ||
document.referrer;
var sameDomainReferrer = false;
@@ -2804,6 +2948,16 @@
wem.digitalData.page.pageInfo.sameDomainReferrer = sameDomainReferrer;
},
+
+ /**
+ * Listener callback that can be attached to a specific HTML form,
+ * this listener will automatically send the form event to Unomi, by
parsing the HTML form data.
+ * (NOTE: the form listener only work for know forms to be watch due to
tracked conditions)
+ *
+ * @param {object} event the original HTML form submition event for the
watch form.
+ * @private
+ * @return {undefined}
+ */
_formSubmitEventListener: function _formSubmitEventListener(event) {
console.info('[WEM] Registering form event callback');
var form = event.target;
@@ -2844,6 +2998,14 @@
});
}
},
+
+ /**
+ * Utility function to extract data from an HTML form.
+ *
+ * @param {HTMLFormElement} form the HTML form element
+ * @private
+ * @return {object} the form data as JSON
+ */
_extractFormData: function _extractFormData(form) {
var params = {};
@@ -2910,6 +3072,13 @@
return params;
},
+
+ /**
+ * Internal function used for mapping ids when current HTML document ids
doesn't match ids stored in Unomi backend
+ * @param {string} id the id to resolve
+ * @return {string} the resolved id or the original id if not match found
+ * @private
+ */
_resolveId: function _resolveId(id) {
if (wem.digitalData.sourceLocalIdentifierMap) {
var source =
Object.keys(wem.digitalData.sourceLocalIdentifierMap).filter(function (source) {
@@ -2920,6 +3089,14 @@
return id;
},
+
+ /**
+ * Enable or disable tracking in current page
+ * @param {boolean} enable true will enable the tracking feature,
otherwise they will be disabled
+ * @param {function} callback an optional callback that can be used to
perform additional logic based on enabling/disabling results
+ * @private
+ * @return {undefined}
+ */
_enableWem: function _enableWem(enable, callback) {
// display fallback if wem is not enable
wem.fallback = !enable; // remove cookies, reset cxs
@@ -2944,6 +3121,14 @@
console.log("[WEM] successfully ".concat(enable ? 'enabled' :
'disabled', " tracking in current page"));
},
+
+ /**
+ * Utility function used to merge two JSON object together (arrays are
concat for example)
+ * @param {object} source the source object for merge
+ * @param {object} target the target object for merge
+ * @private
+ * @return {object} the merged results
+ */
_deepMergeObjects: function _deepMergeObjects(source, target) {
if (!wem._isObject(target) || !wem._isObject(source)) {
return source;
@@ -2963,9 +3148,23 @@
});
return target;
},
+
+ /**
+ * Utility function used to check if the given variable is a JavaScript
object.
+ * @param {*} obj the variable to check
+ * @private
+ * @return {boolean} true if the variable is an object, false otherwise
+ */
_isObject: function _isObject(obj) {
return obj && _typeof(obj) === 'object';
},
+
+ /**
+ * Utility function used to check if the current id is contains in any
Unomi control group
+ * @param {string} id the id to check
+ * @private
+ * @return {boolean} true if the id is found in a control group, false
otherwise
+ */
_isInControlGroup: function _isInControlGroup(id) {
if (wem.cxs.profileProperties &&
wem.cxs.profileProperties.unomiControlGroups) {
var controlGroup =
wem.cxs.profileProperties.unomiControlGroups.find(function (controlGroup) {
diff --git a/rollup.config.js b/rollup.config.js
index 0b859b9..2856e0f 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,35 +1,35 @@
-import resolve from "@rollup/plugin-node-resolve";
-import commonjs from "@rollup/plugin-commonjs";
-import babel from "@rollup/plugin-babel";
-import pkg from "./package.json";
+import resolve from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
+import babel from '@rollup/plugin-babel';
+import pkg from './package.json';
export default [
{
- input: "src/index.js",
+ input: 'src/index.js',
output: {
name: pkg.name,
file: pkg.browser,
- format: "umd",
+ format: 'umd',
},
plugins: [
resolve(),
commonjs(),
babel({
babelHelpers: 'runtime',
- exclude: ["node_modules/**"],
+ exclude: ['node_modules/**'],
}),
],
},
{
- input: "src/index.js",
+ input: 'src/index.js',
output: [
- { file: pkg.main, format: "cjs" },
- { file: pkg.module, format: "es" },
+ { file: pkg.main, format: 'cjs' },
+ { file: pkg.module, format: 'es' },
],
plugins: [
babel({
babelHelpers: 'runtime',
- exclude: ["node_modules/**"],
+ exclude: ['node_modules/**'],
}),
],
external: id => id.includes('@babel/runtime')
diff --git a/src/index.js b/src/index.js
index 6bbd7cf..6e2bb86 100644
--- a/src/index.js
+++ b/src/index.js
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import {newTracker} from "./tracker/tracker";
+import { newTracker } from './tracker/tracker';
export const useTracker = () => {
return newTracker();
diff --git a/src/tracker/tracker.js b/src/tracker/tracker.js
index 306c447..9ce78a0 100644
--- a/src/tracker/tracker.js
+++ b/src/tracker/tracker.js
@@ -6,12 +6,13 @@ export const newTracker = () => {
* This function initialize the tracker
*
* @param {object} digitalData config of the tracker
+ * @returns {undefined}
*/
initTracker: function (digitalData) {
wem.digitalData = digitalData;
- wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : "wem-profile-id";
- wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : "wem-session-id";
- wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : "";
+ wem.trackerProfileIdCookieName =
wem.digitalData.wemInitConfig.trackerProfileIdCookieName ?
wem.digitalData.wemInitConfig.trackerProfileIdCookieName : 'wem-profile-id';
+ wem.trackerSessionIdCookieName =
wem.digitalData.wemInitConfig.trackerSessionIdCookieName ?
wem.digitalData.wemInitConfig.trackerSessionIdCookieName : 'wem-session-id';
+ wem.browserGeneratedSessionSuffix =
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix ?
wem.digitalData.wemInitConfig.browserGeneratedSessionSuffix : '';
wem.disableTrackedConditionsListeners =
wem.digitalData.wemInitConfig.disableTrackedConditionsListeners;
wem.activateWem = wem.digitalData.wemInitConfig.activateWem;
@@ -36,6 +37,7 @@ export const newTracker = () => {
* Note: that the tracker will start once the current DOM is complete
loaded, using listener on current document: DOMContentLoaded
*
* @param {object[]} digitalDataOverrides optional, list of
digitalData extensions, they will be merged with original digitalData before
context loading
+ * @returns {undefined}
*/
startTracker: function (digitalDataOverrides = undefined) {
// Check before start
@@ -63,7 +65,7 @@ export const newTracker = () => {
}
if (!wem.disableTrackedConditionsListeners) {
- wem._registerListenersForTrackedConditions()
+ wem._registerListenersForTrackedConditions();
}
}, 'Default tracker callback', 0);
@@ -132,49 +134,20 @@ export const newTracker = () => {
/**
* Get current session id
- * @returns {null|*}
+ * @returns {null|string} get current session id
*/
getSessionId: function () {
return wem.sessionID;
},
- convertUrlParametersToObj: function (searchString) {
- if (!searchString) {
- return null;
- }
-
- return searchString
- .replace(/^\?/, '') // Only trim off a single leading
interrobang.
- .split('&')
- .reduce((result, next) => {
- if (next === '') {
- return result;
- }
- let pair = next.split('=');
- let key = decodeURIComponent(pair[0]);
- let value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
- if (Object.prototype.hasOwnProperty.call(result, key))
{ // Check to see if this property has been met before.
- if (Array.isArray(result[key])) { // Is it already
an array?
- result[key].push(value);
- } else { // Make it an array.
- result[key] = [result[key], value];
- }
- } else { // First time seen, just add it.
- result[key] = value;
- }
-
- return result;
- }, {}
- );
- },
-
/**
* This function will register a personalization
*
- * @param {object} personalization
- * @param {object} variants
+ * @param {object} personalization the personalization object
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {function} [resultCallback]
+ * @param {function} [resultCallback] the callback to be executed
after personalization resolved
+ * @returns {undefined}
*/
registerPersonalizationObject: function (personalization, variants,
ajax, resultCallback) {
var target = personalization.id;
@@ -242,12 +215,13 @@ export const newTracker = () => {
/**
* This function will register an optimization test or A/B test
*
- * @param {string} optimizationTestNodeId
- * @param {string} goalId
- * @param {string} containerId
- * @param {object} variants
+ * @param {string} optimizationTestNodeId the optimization test node id
+ * @param {string} goalId the associated goal Id
+ * @param {string} containerId the HTML container Id
+ * @param {object} variants the variants
* @param {boolean} [ajax] Deprecated: Ajax rendering is not supported
anymore
- * @param {object} [variantsTraffic]
+ * @param {object} [variantsTraffic] the associated traffic allocation
+ * @return {undefined}
*/
registerOptimizationTest: function (optimizationTestNodeId, goalId,
containerId, variants, ajax, variantsTraffic) {
@@ -310,6 +284,7 @@ export const newTracker = () => {
*
* @param {boolean} [skipEvents=false] Should we send the events
* @param {boolean} [invalidate=false] Should we invalidate the
current context
+ * @return {undefined}
*/
loadContext: function (skipEvents, invalidate) {
if (wem.contextLoaded) {
@@ -360,6 +335,7 @@ export const newTracker = () => {
* @param {object} event The event object to send, you can build it
using wem.buildEvent(eventType, target, source)
* @param {function} successCallback will be executed in case of
success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvent: function (event, successCallback, errorCallback) {
wem.collectEvents({ events: [event] }, successCallback,
errorCallback);
@@ -371,6 +347,7 @@ export const newTracker = () => {
* @param {object} events Javascript object { events: [event1, event2]
}
* @param {function} successCallback will be executed in case of
success
* @param {function} errorCallback will be executed in case of error
+ * @return {undefined}
*/
collectEvents: function (events, successCallback, errorCallback) {
if (wem.fallback) {
@@ -399,6 +376,7 @@ export const newTracker = () => {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of
success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendClickEvent: function (event, successCallback, errorCallback) {
if (event.target.id || event.target.name) {
@@ -443,6 +421,7 @@ export const newTracker = () => {
* @param {object} event javascript
* @param {function} [successCallback] will be executed if case of
success
* @param {function} [errorCallback] will be executed if case of error
+ * @return {undefined}
*/
sendVideoEvent: function (event, successCallback, errorCallback) {
console.info('[WEM] catching video event');
@@ -465,6 +444,7 @@ export const newTracker = () => {
* This function will invalidate the Apache Unomi session and profile,
* by removing the associated cookies, set the loaded context to
undefined
* and set the session id cookie with a newly generated ID
+ * @return {undefined}
*/
invalidateSessionAndProfile: function () {
'use strict';
@@ -481,7 +461,7 @@ export const newTracker = () => {
* @param {string} eventType The name of your event
* @param {object} [target] The target object for your event can be
build with wem.buildTarget(targetId, targetType, targetProperties)
* @param {object} [source] The source object for your event can be
build with wem.buildSource(sourceId, sourceType, sourceProperties)
- * @returns {{eventType: *, scope}}
+ * @returns {object} the event
*/
buildEvent: function (eventType, target, source) {
var event = {
@@ -504,7 +484,7 @@ export const newTracker = () => {
* This function return an event of type form
*
* @param {string} formName The HTML name of id of the form to use in
the target of the event
- * @returns {*|{eventType: *, scope, source: {scope, itemId: string,
itemType: string, properties: {}}, target: {scope, itemId: string, itemType:
string, properties: {}}}}
+ * @returns {object} the form event
*/
buildFormEvent: function (formName) {
return wem.buildEvent('form', wem.buildTarget(formName, 'form'),
wem.buildSourcePage());
@@ -513,7 +493,7 @@ export const newTracker = () => {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the target page
*/
buildTargetPage: function () {
return wem.buildTarget(wem.digitalData.page.pageInfo.pageID,
'page', wem.digitalData.page);
@@ -522,7 +502,7 @@ export const newTracker = () => {
/**
* This function return the source object for a source of type page
*
- * @returns {*|{scope, itemId: *, itemType: *}}
+ * @returns {object} the source page
*/
buildSourcePage: function () {
return wem.buildSource(wem.digitalData.page.pageInfo.pageID,
'page', wem.digitalData.page);
@@ -534,7 +514,7 @@ export const newTracker = () => {
* @param {string} targetId The ID of the target
* @param {string} targetType The type of the target
* @param {object} [targetProperties] The optional properties of the
target
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the target
*/
buildTarget: function (targetId, targetType, targetProperties) {
return wem._buildObject(targetId, targetType, targetProperties);
@@ -546,7 +526,7 @@ export const newTracker = () => {
* @param {string} sourceId The ID of the source
* @param {string} sourceType The type of the source
* @param {object} [sourceProperties] The optional properties of the
source
- * @returns {{scope, itemId: *, itemType: *}}
+ * @returns {object} the source
*/
buildSource: function (sourceId, sourceType, sourceProperties) {
return wem._buildObject(sourceId, sourceType, sourceProperties);
@@ -562,6 +542,7 @@ export const newTracker = () => {
* @param {string} cookieName name of the cookie
* @param {string} cookieValue value of the cookie
* @param {number} [expireDays] number of days to set the expire date
+ * @return {undefined}
*/
setCookie: function (cookieName, cookieValue, expireDays) {
var expires = '';
@@ -577,7 +558,7 @@ export const newTracker = () => {
* This is an utility function to get a cookie
*
* @param {string} cookieName name of the cookie to get
- * @returns {*} the value of the first cookie with the corresponding
name or null if not found
+ * @returns {string} the value of the first cookie with the
corresponding name or null if not found
*/
getCookie: function (cookieName) {
var name = cookieName + '=';
@@ -598,6 +579,7 @@ export const newTracker = () => {
* This is an utility function to remove a cookie
*
* @param {string} cookieName the name of the cookie to rename
+ * @return {undefined}
*/
removeCookie: function (cookieName) {
'use strict';
@@ -607,7 +589,8 @@ export const newTracker = () => {
/**
* This is an utility function to execute AJAX call
*
- * @param {object} options
+ * @param {object} options options of the request
+ * @return {undefined}
*/
ajax: function (options) {
var xhr = new XMLHttpRequest();
@@ -677,7 +660,7 @@ export const newTracker = () => {
/**
* This is an utility function to generate a new UUID
*
- * @returns {string}
+ * @returns {string} the newly generated UUID
*/
generateGuid: function () {
function s4() {
@@ -692,8 +675,8 @@ export const newTracker = () => {
/**
* This is an utility function to check if the local storage is
available or not
- * @param type
- * @returns {boolean}
+ * @param {string} type the type of storage to test
+ * @returns {boolean} true in case storage is available, false
otherwise
*/
storageAvailable: function (type) {
try {
@@ -707,6 +690,15 @@ export const newTracker = () => {
}
},
+ /**
+ * Dispatch a JavaScript event in current HTML document
+ *
+ * @param {string} name the name of the event
+ * @param {boolean} canBubble does the event can bubble ?
+ * @param {boolean} cancelable is the event cancelable ?
+ * @param {*} detail event details
+ * @return {undefined}
+ */
dispatchJSEvent: function (name, canBubble, cancelable, detail) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, canBubble, cancelable, detail);
@@ -715,8 +707,9 @@ export const newTracker = () => {
/**
* This is an utility function to get current url parameter value
- * @param name, the name of the parameter
- * @returns {string}
+ *
+ * @param {string} name, the name of the parameter
+ * @returns {string} the value of the parameter
*/
getUrlParameter: function (name) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
@@ -725,9 +718,52 @@ export const newTracker = () => {
return results === null ? null :
decodeURIComponent(results[1].replace(/\+/g, ' '));
},
+ /**
+ * convert the passed query string into JS object.
+ * @param {string} searchString The URL query string
+ * @returns {object} converted URL params
+ */
+ convertUrlParametersToObj: function (searchString) {
+ if (!searchString) {
+ return null;
+ }
+
+ return searchString
+ .replace(/^\?/, '') // Only trim off a single leading
interrobang.
+ .split('&')
+ .reduce((result, next) => {
+ if (next === '') {
+ return result;
+ }
+ let pair = next.split('=');
+ let key = decodeURIComponent(pair[0]);
+ let value = typeof pair[1] !== 'undefined' &&
decodeURIComponent(pair[1]) || undefined;
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
// Check to see if this property has been met before.
+ if (Array.isArray(result[key])) { // Is it already an
array?
+ result[key].push(value);
+ } else { // Make it an array.
+ result[key] = [result[key], value];
+ }
+ } else { // First time seen, just add it.
+ result[key] = value;
+ }
+
+ return result;
+ }, {}
+ );
+ },
+
/*************************************/
/* Private functions under this line */
/*************************************/
+ /**
+ * Used to override the default digitalData values,
+ * the result will impact directly the current instance wem.digitalData
+ *
+ * @param {object[]} digitalDataOverrides list of overrides
+ * @private
+ * @return {undefined}
+ */
_handleDigitalDataOverrides: function (digitalDataOverrides) {
if (digitalDataOverrides && digitalDataOverrides.length > 0) {
for (const digitalDataOverride of digitalDataOverrides) {
@@ -736,6 +772,15 @@ export const newTracker = () => {
}
},
+ /**
+ * Check for tracked conditions in the current loaded context, and
attach listeners for the known tracked condition types:
+ * - formEventCondition
+ * - videoViewEventCondition
+ * - clickOnLinkEventCondition
+ *
+ * @private
+ * @return {undefined}
+ */
_registerListenersForTrackedConditions: function () {
console.info('[WEM] Check for tracked conditions and attach
related HTML listeners');
@@ -803,6 +848,12 @@ export const newTracker = () => {
}
},
+ /**
+ * Check for currently registered events in wem.digitalData.events
that would be incomplete:
+ * - autocomplete the event with the current digitalData page infos
for the source
+ * @private
+ * @return {undefined}
+ */
_checkUncompleteRegisteredEvents: function () {
if (wem.digitalData && wem.digitalData.events) {
for (const event of wem.digitalData.events) {
@@ -811,6 +862,11 @@ export const newTracker = () => {
}
},
+ /**
+ * dispatch JavaScript event in current HTML document for perso and
opti events contains in digitalData.events
+ * @private
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvents: () => {
if (wem.digitalData && wem.digitalData.events) {
for (const event of wem.digitalData.events) {
@@ -821,6 +877,12 @@ export const newTracker = () => {
}
},
+ /**
+ * build and dispatch JavaScript event in current HTML document for
the given Unomi event (perso/opti)
+ * @private
+ * @param {object} experienceUnomiEvent perso/opti Unomi event
+ * @return {undefined}
+ */
_dispatchJSExperienceDisplayedEvent: experienceUnomiEvent => {
if (!wem.fallback &&
experienceUnomiEvent &&
@@ -858,6 +920,12 @@ export const newTracker = () => {
}
},
+ /**
+ * Filter events in digitalData.events that would have the property:
event.properties.doNotSendToUnomi
+ * The effect is directly stored in a new version of
wem.digitalData.events
+ * @private
+ * @return {undefined}
+ */
_filterUnomiEvents: () => {
if (wem.digitalData && wem.digitalData.events) {
wem.digitalData.events = wem.digitalData.events
@@ -871,6 +939,14 @@ export const newTracker = () => {
}
},
+ /**
+ * Check if event is incomplete and complete what is missing:
+ * - source: if missing, use the current source page
+ * - scope: if missing, use the current scope
+ * @param {object} event, the event to be checked
+ * @private
+ * @return {object} the complete event
+ */
_completeEvent: function (event) {
if (!event.source) {
event.source = wem.buildSourcePage();
@@ -884,6 +960,18 @@ export const newTracker = () => {
return event;
},
+ /**
+ * Register an event in the wem.digitalData.events.
+ * Registered event, will be sent automatically during the context
loading process.
+ *
+ * Beware this function is useless in case the context is already
loaded.
+ * in case the context is already loaded (check:
wem.getLoadedContext()), then you should use: wem.collectEvent(s) functions
+ *
+ * @private
+ * @param {object} event the Unomi event to be registered
+ * @param {boolean} unshift if true, the event will be added at the
beginning of the list otherwise at the end of the list.
+ * @return {undefined}
+ */
_registerEvent: function (event, unshift) {
if (wem.digitalData) {
if (wem.cxs) {
@@ -904,10 +992,12 @@ export const newTracker = () => {
/**
* This function allow for registering callback that will be executed
once the context is loaded.
- * @param onLoadCallback the callback to be executed
- * @param name optional name for the call, used mostly for logging the
execution
- * @param priority optional priority to execute the callbacks in a
specific order (default: 5, to leave room for the tracker default callback(s))
+ * @param {function} onLoadCallback the callback to be executed
+ * @param {string} name optional name for the call, used mostly for
logging the execution
+ * @param {number} priority optional priority to execute the callbacks
in a specific order
+ * (default: 5, to leave room for the tracker
default callback(s))
* @private
+ * @return {undefined}
*/
_registerCallback: function (onLoadCallback, name = undefined,
priority = 5) {
if (wem.digitalData) {
@@ -942,6 +1032,13 @@ export const newTracker = () => {
}
},
+ /**
+ * Internal function for personalization specific callbacks (used for
HTML dom manipulation once we get the context loaded)
+ * @param {object} personalization the personalization
+ * @param {function} callback the callback
+ * @private
+ * @return {undefined}
+ */
_registerPersonalizationCallback: function (personalization, callback)
{
if (wem.digitalData) {
if (wem.cxs) {
@@ -958,6 +1055,14 @@ export const newTracker = () => {
}
},
+ /**
+ * Build a simple Unomi object
+ * @param {string} itemId the itemId of the object
+ * @param {string} itemType the itemType of the object
+ * @param {object} properties optional properties for the object
+ * @private
+ * @return {object} the built Unomi JSON object
+ */
_buildObject: function (itemId, itemType, properties) {
var object = {
scope: wem.digitalData.scope,
@@ -972,6 +1077,12 @@ export const newTracker = () => {
return object;
},
+ /**
+ * Main callback used once the Ajax context request succeed
+ * @param {XMLHttpRequest} xhr the request
+ * @private
+ * @return {undefined}
+ */
_onSuccess: function (xhr) {
wem.cxs = JSON.parse(xhr.responseText);
@@ -989,6 +1100,12 @@ export const newTracker = () => {
window.wemLoaded = true;
},
+ /**
+ * Main callback used once the Ajax context request failed
+ * @param {string} logMessage the log message, to identify the place
of failure
+ * @private
+ * @return {undefined}
+ */
_executeFallback: function (logMessage) {
console.warn('[WEM] execute fallback' + (logMessage ? (': ' +
logMessage) : '') + ', load fallback callbacks, calling now...');
wem.fallback = true;
@@ -1002,17 +1119,28 @@ export const newTracker = () => {
}
},
- _executeLoadCallbacks: function(callbackParam) {
+ /**
+ * Executed the registered context loaded callbacks
+ * @param {*} callbackParam param of the callbacks
+ * @private
+ * @return {undefined}
+ */
+ _executeLoadCallbacks: function (callbackParam) {
if (wem.digitalData.loadCallbacks &&
wem.digitalData.loadCallbacks.length > 0) {
wem.digitalData.loadCallbacks
.sort((a, b) => a.priority - b.priority)
.forEach(loadCallback => {
console.info('[WEM] executing context load callback: '
+ (loadCallback.name ? loadCallback.name : 'callback without name'));
- loadCallback.execute(callbackParam)
+ loadCallback.execute(callbackParam);
});
}
},
+ /**
+ * Parse current HTML document referrer information to enrich the
digitalData page infos
+ * @private
+ * @return {undefined}
+ */
_processReferrer: function () {
var referrerURL = wem.digitalData.page.pageInfo.referringURL ||
document.referrer;
var sameDomainReferrer = false;
@@ -1053,6 +1181,15 @@ export const newTracker = () => {
wem.digitalData.page.pageInfo.sameDomainReferrer =
sameDomainReferrer;
},
+ /**
+ * Listener callback that can be attached to a specific HTML form,
+ * this listener will automatically send the form event to Unomi, by
parsing the HTML form data.
+ * (NOTE: the form listener only work for know forms to be watch due
to tracked conditions)
+ *
+ * @param {object} event the original HTML form submition event for
the watch form.
+ * @private
+ * @return {undefined}
+ */
_formSubmitEventListener: function (event) {
console.info('[WEM] Registering form event callback');
var form = event.target;
@@ -1096,6 +1233,13 @@ export const newTracker = () => {
}
},
+ /**
+ * Utility function to extract data from an HTML form.
+ *
+ * @param {HTMLFormElement} form the HTML form element
+ * @private
+ * @return {object} the form data as JSON
+ */
_extractFormData: function (form) {
var params = {};
for (var i = 0; i < form.elements.length; i++) {
@@ -1151,8 +1295,14 @@ export const newTracker = () => {
return params;
},
+ /**
+ * Internal function used for mapping ids when current HTML document
ids doesn't match ids stored in Unomi backend
+ * @param {string} id the id to resolve
+ * @return {string} the resolved id or the original id if not match
found
+ * @private
+ */
_resolveId: function (id) {
- if (wem.digitalData.sourceLocalIdentifierMap){
+ if (wem.digitalData.sourceLocalIdentifierMap) {
var source =
Object.keys(wem.digitalData.sourceLocalIdentifierMap).filter(function (source) {
return id.indexOf(source) > 0;
});
@@ -1161,6 +1311,13 @@ export const newTracker = () => {
return id;
},
+ /**
+ * Enable or disable tracking in current page
+ * @param {boolean} enable true will enable the tracking feature,
otherwise they will be disabled
+ * @param {function} callback an optional callback that can be used to
perform additional logic based on enabling/disabling results
+ * @private
+ * @return {undefined}
+ */
_enableWem: (enable, callback) => {
// display fallback if wem is not enable
wem.fallback = !enable;
@@ -1180,11 +1337,18 @@ export const newTracker = () => {
}
if (callback) {
- callback(enable)
+ callback(enable);
}
console.log(`[WEM] successfully ${enable ? 'enabled' : 'disabled'}
tracking in current page`);
},
+ /**
+ * Utility function used to merge two JSON object together (arrays are
concat for example)
+ * @param {object} source the source object for merge
+ * @param {object} target the target object for merge
+ * @private
+ * @return {object} the merged results
+ */
_deepMergeObjects: function (source, target) {
if (!wem._isObject(target) || !wem._isObject(source)) {
return source;
@@ -1207,10 +1371,22 @@ export const newTracker = () => {
return target;
},
+ /**
+ * Utility function used to check if the given variable is a
JavaScript object.
+ * @param {*} obj the variable to check
+ * @private
+ * @return {boolean} true if the variable is an object, false otherwise
+ */
_isObject: function (obj) {
return obj && typeof obj === 'object';
},
+ /**
+ * Utility function used to check if the current id is contains in any
Unomi control group
+ * @param {string} id the id to check
+ * @private
+ * @return {boolean} true if the id is found in a control group, false
otherwise
+ */
_isInControlGroup: function (id) {
if (wem.cxs.profileProperties &&
wem.cxs.profileProperties.unomiControlGroups) {
let controlGroup =
wem.cxs.profileProperties.unomiControlGroups.find(controlGroup =>
controlGroup.id === id);
@@ -1226,7 +1402,7 @@ export const newTracker = () => {
}
return false;
}
- }
+ };
return wem;
};
\ No newline at end of file
diff --git a/test/spec.js b/test/spec.js
index 760fc99..7db513e 100644
--- a/test/spec.js
+++ b/test/spec.js
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-const assert = require('assert')
+const assert = require('assert');
const unomi = require('..');
const tracker = unomi.useTracker();
-assert(tracker !== null)
-console.log(`Tests passed`);
\ No newline at end of file
+assert(tracker !== null);
+console.log('Tests passed');
\ No newline at end of file