/**
* @class
* @classdesc Utility methods.
* @name Utils
* @public
*/
var Utils = function () {
var self = this;
var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
var whitespace = "[\\x20\\t\\r\\n\\f]";
var rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g");
/**
* Serialize any element.
* @memberof Utils#
* @param obj {Object} Object to serialize
* @return {String}
* @private
*/
function serialJSON(obj) {
var t = typeof obj;
if (t !== 'object' || obj === null) {
if (t === 'string') {
obj = '"' + obj + '"';
}
return String(obj);
} else {
var n, v, json = [],
arr = (obj.constructor === Array);
for (n in obj) {
if (obj.hasOwnProperty(n)) {
v = obj[n];
t = typeof (v);
if (t !== 'function' && t !== 'undefined') {
if (t === 'string') {
v = '"' + v.replace(/[^\\]"/g, '\\"') + '"';
} else if (t === 'object' && v !== null) {
v = serialJSON(v);
}
json.push((arr ? '' : '"' + n + '":') + String(v));
}
}
}
return (arr ? '[' : '{') + String(json) + (arr ? ']' : '}');
}
}
/**
* Trim JSON string.
* @memberof Utils#
* @param text {String} Text to trim
* @return {String}
* @private
*/
function trimJSONString(text) {
return text === null ?
'' :
(text + '').replace(rtrim, '');
}
/**
* Parse any string.
* @memberof Utils#
* @param data {string} String to parse
* @return {*}
* @private
*/
function parseJSON(data) {
var requireNonComma,
depth = null,
str = trimJSONString(data + '');
// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
// after removing valid tokens
return str && !trimJSONString(str.replace(rvalidtokens, function (token, comma, open, close) {
// Force termination if we see a misplaced comma
if (requireNonComma && comma) {
depth = 0;
}
// Perform no more replacements after returning to outermost depth
if (depth === 0) {
return token;
}
// Commas must not follow "[", "{", or ","
requireNonComma = open || comma;
// Determine new depth
// array/object open ("[" or "{"): depth += true - false (increment)
// array/object close ("]" or "}"): depth += false - true (decrement)
// other cases ("," or primitive): depth += true - true (numeric cast)
depth += !close - !open;
// Remove this token
return '';
})) ?
(Function('return ' + str))() :
null;
}
/**
* Check if local storage is both supported and available
* @name isLocalStorageAvailable
* @memberof Utils#
* @return {boolean}
* @public
*/
self.isLocalStorageAvailable = function () {
try {
var storage = localStorage,
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return false;
}
};
/**
* Check if beacon method is supported and available
* @name isBeaconMethodAvailable
* @memberof Utils#
* @return {boolean}
* @public
*/
self.isBeaconMethodAvailable = function () {
return (window.navigator && typeof window.navigator.sendBeacon === "function");
};
/**
* Get a stored value from name
* @name getStorageData
* @memberof Utils#
* @function
* @param name {String} Name of the stored data
* @return {*} Return null if the stored data does not exist
* @private
*/
function getStorageData(name) {
var storedData = null;
if (self.isLocalStorageAvailable()) {
storedData = localStorage.getItem(name)
}
if (storedData === null) {
var cookies = document.cookie;
var regExp = new RegExp('(?:^| )' + name + '=([^;]+)');
var result = regExp.exec(cookies);
if (result !== null) {
storedData = result[1];
}
}
if (storedData !== null) {
try {
storedData = decodeURIComponent(storedData);
} catch (e) {
}
}
return storedData;
}
/**
* Base64 encode / decode
* *http://www.webtoolkit.info/
* @memberof Utils#
* @Object
* @public
*/
self.Base64 = {
// private property
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode: function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = self.Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = self.Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode: function (string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode: function (utftext) {
var string = "";
var i = 0;
var c, c2, c3;
c = c2 = c3 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
};
/**
* Load a script.
* @name loadScript
* @memberof Utils#
* @function
* @param scriptObj {object} Object containing script properties (url)
* @param callback {function} Function to call on success or on error
* @public
*/
self.loadScript = function (scriptObj, callback) {
var newScript;
callback = callback || function () {
};
var errorCallback = function (event) {
newScript.onload = newScript.onreadystatechange = newScript.onerror = null;
callback({msg: 'script not loaded', event: event}); // first param not null means an error has occured
};
var onloadCallback = function (event) {
event = event || window.event;
// Check for different events in IEs
if (event.type === "load" || (/loaded|complete/.test(newScript.readyState) && (!document.documentMode || document.documentMode < 9))) {
newScript.onload = newScript.onreadystatechange = newScript.onerror = null;
callback(null, event);
}
};
newScript = document.createElement("script");
newScript.type = "text/javascript";
newScript.src = scriptObj.url;
newScript.async = false;
newScript.defer = false;
newScript.onload = newScript.onreadystatechange = onloadCallback;
newScript.onerror = errorCallback;
var head = document.head || document.getElementsByTagName("head")[0];
head.insertBefore(newScript, head.lastChild);
};
// We deliberately ignore the second parameter reserved for us :)
/**
* Copy an object which doesnt' contain functions/objects.
* @name cloneSimpleObject
* @memberof Utils#
* @inner
* @function
* @param inst {Object} Item that you want to clone
* @param delUndefined {boolean} If true, undefined properties are not cloned
* @return {Object} clone of the instance
* @public
*/
self.cloneSimpleObject = function (inst, delUndefined) {
/*Si l'instance source n'est pas un objet ou qu'elle ne vaut rien c'est une feuille donc on la retourne*/
if (typeof (inst) !== 'object' || inst === null || inst instanceof Date) {
return inst;
}
/*On appelle le constructeur de l'instance source pour créer une nouvelle instance de la même classe*/
var newInst = new inst.constructor;
/*On parcourt les propriétés de l'objet et on les recopie dans la nouvelle instance*/
for (var i in inst) {
if (inst.hasOwnProperty(i)) {
//On gère le cas où il a été demandé (delUndefined) de ne pas copier les propriétés undefined
if (i !== undefined && (!delUndefined || inst[i] !== undefined)) {
newInst[i] = self.cloneSimpleObject(inst[i]);
}
}
}
/*On retourne la nouvelle instance*/
return newInst;
};
/**
* Check if object is empty.
* @name isEmptyObject
* @memberof Utils#
* @function
* @param tagObject {Object}
* @return {boolean}
* @public
*/
self.isEmptyObject = function (tagObject) {
for (var p in tagObject) {
if (tagObject.hasOwnProperty(p)) {
return false;
}
}
return true;
};
/**
* Check if parameter is an object.
* @name isObject
* @memberof Utils#
* @function
* @param contextualObject {*}
* @return {boolean}
* @public
*/
self.isObject = function (contextualObject) {
return ((contextualObject !== null) && (typeof contextualObject === 'object') && !(contextualObject instanceof Array));
};
// Constants to manage flattening
self.ATVALUE = '_ATVALUE';
self.ATPREFIX = '_ATPREFIX';
/**
* Adding flatten property with value from source object into destination.
* @memberof Utils#
* @function
* @param source {Object} Source object
* @param parentPath {string|null} Path from parent
* @param destination {Object} Final object
* @param parentPrefix {string|null} Prefix from parent
* @param toLower {boolean} Put the key to the property in lower case
* @public
*/
self.object2Flatten = function (source, parentPath, destination, parentPrefix, toLower) {
var splittedObject = {};
var prefix = '';
var path = '';
var levels = [];
var newPath = '';
var i = 0;
for (var sourceKey in source) {
if (source.hasOwnProperty(sourceKey)) {
splittedObject = self.splitProtocolAndKey(sourceKey, toLower);
prefix = splittedObject.prefix || parentPrefix || '';
path = (parentPath ? parentPath + '_' : '') + splittedObject.key;
if (self.isObject(source[sourceKey])) {
self.object2Flatten(source[sourceKey], path, destination, prefix, toLower);
} else {
levels = path.split('_');
newPath = '';
for (i = 0; i < levels.length; i++) {
splittedObject = self.splitProtocolAndKey(levels[i], toLower);
prefix = splittedObject.prefix || prefix;
newPath += splittedObject.key + ((i < levels.length - 1) ? '_' : '');
}
path = newPath || path;
destination[path] = destination[path] || {};
destination[path][self.ATVALUE] = source[sourceKey];
destination[path][self.ATPREFIX] = prefix;
}
}
}
};
/**
* Create object from flatten property.
* @memberof Utils#
* @function
* @param destination {Object} Final object
* @param sourceKey {string} Key to process
* @param sourceValue {Object} Value to set
* @public
*/
self.flatten2Object = function (destination, sourceKey, sourceValue) {
var levels = sourceKey.split('_');
var parentObject = destination;
var currentLevel;
// Scrolling through data object
var i;
for (i = 0; i < levels.length - 1; i++) {
currentLevel = levels[i];
if (!parentObject[currentLevel]) {
parentObject[currentLevel] = {};
}
parentObject = parentObject[currentLevel];
}
// Update parent object if necessary
if (parentObject.hasOwnProperty(self.ATVALUE)) {
var parentValue = parentObject[self.ATVALUE];
var parentPrefix = parentObject[self.ATPREFIX];
delete parentObject[self.ATVALUE];
delete parentObject[self.ATPREFIX];
parentObject['$'] = {};
parentObject['$'][self.ATVALUE] = parentValue;
parentObject['$'][self.ATPREFIX] = parentPrefix;
}
// Adding current value
var sourceCopyValue = self.cloneSimpleObject(sourceValue);
if (parentObject[levels[i]]) {
parentObject[levels[i]]['$'] = sourceCopyValue;
} else {
parentObject[levels[i]] = sourceCopyValue;
}
};
/**
* Get formatted content object from flatten source.
* @memberof ATInternet.Tracker.Plugins.AvInsights#
* @function
* @param source {Object} Source object
* @public
*/
self.getFormattedObject = function (source) {
var content = {};
var newKey;
for (var key in source) {
if (source.hasOwnProperty(key)) {
if (!source[key].hasOwnProperty(self.ATVALUE)) {
content[key] = self.getFormattedObject(source[key]);
} else {
newKey = source[key][self.ATPREFIX] ? source[key][self.ATPREFIX] + ':' + key : key;
content[newKey] = source[key][self.ATVALUE];
}
}
}
return content;
};
/**
* Complete the first level of an object with another.
* @name completeFstLevelObj
* @memberof Utils#
* @function
* @param target {object}
* @param source {object}
* @param overload {boolean} If true, properties of the target will be overloaded by source ones if they exist
* @return {object}
* @public
*/
self.completeFstLevelObj = function (target, source, overload) {
if (target) {
if (source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
if (!target[key] || overload) {
target[key] = source[key];
}
}
}
}
} else {
target = source;
}
return target;
};
/**
* Get all keys from object.
* @name getObjectKeys
* @memberof Utils#
* @function
* @param object {object}
* @return {Array}
* @public
*/
self.getObjectKeys = function (object) {
var keys = [];
for (var elem in object) {
if (object.hasOwnProperty(elem)) {
keys.push(elem);
}
}
return keys;
};
/**
* Change the keys of object to lowercase
* @name objectToLowercase
* @memberof Utils#
* @function
* @param tagObject {Object} Object from tag
* @return {Object}
* @public
*/
self.objectToLowercase = function (tagObject) {
var objUtil = {};
for (var key in tagObject) {
if (tagObject.hasOwnProperty(key)) {
if (self.isObject(tagObject[key])) {
objUtil[key.toLowerCase()] = self.objectToLowercase(tagObject[key]);
} else {
objUtil[key.toLowerCase()] = tagObject[key];
}
}
}
return objUtil;
};
/**
* Process event key.
* @name splitProtocolAndKey
* @memberof Utils#
* @function
* @param key {String} Event key to split
* @param toLower {boolean} Put the key to the property in lower case
* @return {Object}
* @public
*/
self.splitProtocolAndKey = function (key, toLower) {
var _prefix, _key;
if ((key.length < 2) || (key[1] !== ':')) {
_prefix = '';
_key = key;
} else if ((key.length < 4) || (key[3] !== ':')) {
_prefix = key.substring(0, 1);
_key = key.substring(2, key.length);
} else {
_prefix = key.substring(0, 3);
_key = key.substring(4, key.length);
}
if (toLower) {
_prefix = _prefix.toLowerCase();
_key = _key.toLowerCase();
}
return {
'prefix': _prefix,
'key': _key
};
};
/**
* Serialize any element. Use JSON.stringify if possible.
* @name jsonSerialize
* @memberof Utils#
* @function
* @param obj {Object} Javascript object that you want to serialize to JSON
* @return {Object|String} Serialized JSON
* @public
*/
self.jsonSerialize = function (obj) {
try {
if (typeof JSON !== 'undefined' && JSON.stringify) {
return JSON.stringify(obj);
} else {
return serialJSON(obj);
}
} catch (e) {
return null;
}
};
/**
* Parse a string. Use JSON.parse if possible.
* @name jsonParse
* @memberof Utils#
* @function
* @param str {String} JSON string that you want to parse to Javascript object
* @return {Object}
* @public
*/
self.jsonParse = function (str) {
try {
if (typeof JSON !== 'undefined' && JSON.parse) {
return JSON.parse(str + '');
} else {
return parseJSON(str);
}
} catch (e) {
return null;
}
};
/**
* Trim a string. Use String.prototype.trim if possible.
* @name trim
* @memberof Utils#
* @function
* @param str {String} string to trim
* @return {String}
* @public
*/
self.trim = function (str) {
try {
if (!String.prototype.trim) {
// Polyfill
return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
return str.trim();
} catch (e) {
return str;
}
};
/**
* Search an element in an array. Use Array.indexOf if possible.
* @name arrayIndexOf
* @memberof Utils#
* @function
* @param arr {Array}
* @param elemToSearch {*}
* @return {number}
* @public
*/
self.arrayIndexOf = function (arr, elemToSearch) {
if (Array.prototype.indexOf) {
var index = -1;
if (typeof arr.indexOf(elemToSearch) !== 'undefined') {
index = arr.indexOf(elemToSearch)
}
return index;
} else {
return (function (searchElement) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}).apply(arr, [elemToSearch]);
}
};
/**
* Generate UUID.
* @name uuid
* @memberof Utils#
* @function
* @return {function}
* @public
*/
self.uuid = function () {
var _cryptoObj = window.crypto || window.msCrypto; // IE11
var _isCryptoAvailable = (_cryptoObj !== null && typeof _cryptoObj === 'object');
/* @if test */
if (typeof self.isCryptoAvailable === 'boolean') {
_isCryptoAvailable = self.isCryptoAvailable;
}
/* @endif */
/**
* Generate GUID with alphanumeric characters (v4 format) with Crypto or Math random.
* @name _crypto
* @memberof Utils#
* @function
* @return {string}
* @public
*/
function _v4() {
try {
if (_isCryptoAvailable) {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (c) {
return (c ^ _cryptoObj.getRandomValues(new Uint32Array(1))[0] & 15 >> c / 4).toString(16);
});
}
} catch (e) {
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* Generate a random number of n digits with Crypto or Math random.
* @name _mathRand
* @memberof Utils#
* @function
* @return {number}
* @public
*/
function _rand(n) {
var rand = Math.random();
try {
if (_isCryptoAvailable) {
rand = _cryptoObj.getRandomValues(new Uint32Array(1))[0] / Math.pow(2, 32);
}
} catch (e) {
}
return Math.floor((rand * 9 + 1) * Math.pow(10, n - 1));
}
/**
* Generate GUID with numeric characters.
* @name _num
* @memberof Utils#
* @function
* @param len {number} Length of the generated GUID
* @return {string}
* @public
*/
function _num(len) {
var d = new Date();
var format = function (a) {
a = a - Math.floor(a / 100) * 100;
if (a < 10) {
return '0' + a;
} else {
return String(a);
}
};
return format(d.getHours()) + '' + format(d.getMinutes()) + '' + format(d.getSeconds()) + '' + _rand(len - 6);
}
return {
v4: _v4,
num: _num
};
};
/**
* Check if we are in Safari previewing case.
* @name isPreview
* @memberof Utils#
* @function
* @return {boolean}
* @public
*/
self.isPreview = function () {
return (window.navigator && window.navigator.loadPurpose === 'preview');
};
/**
* Check if we are in Chrome or IE prerendering case.
* @name isPrerender
* @memberof Utils#
* @function
* @param callback {function}
* @return {boolean}
* @public
*/
self.isPrerender = function (callback) {
var visibilityChangeEvent;
var isPrerender = false;
//Prefixes: Chrome, IE
var prefixes = ['webkit', 'ms'];
if (document.visibilityState === 'prerender') {
//Opera 12.10 and Firefox 18 and later support
visibilityChangeEvent = 'visibilitychange';
} else {
for (var i = 0; i < prefixes.length; i++) {
if (document[prefixes[i] + 'VisibilityState'] === 'prerender') {
visibilityChangeEvent = prefixes[i] + 'visibilitychange';
}
}
}
if (typeof visibilityChangeEvent !== 'undefined') {
var _manageCallback = function (event) {
callback(event);
self.removeEvtListener(document, visibilityChangeEvent, _manageCallback);
};
self.addEvtListener(document, visibilityChangeEvent, _manageCallback);
isPrerender = true;
}
return isPrerender;
};
/**
* Add an event listener to an object.
* @name addEvtListener
* @memberof Utils#
* @function
* @param obj {Object} DOM Element on which you want to add the event listener
* @param event {string} Event you need to listen
* @param callback {function} When event is triggered, it takes one parameter which is the event object
* @public
*/
self.addEvtListener = function (obj, event, callback) {
if (obj.addEventListener) {
obj.addEventListener(event, callback, false);
} else if (obj.attachEvent) {
obj.attachEvent('on' + event, callback);
}
};
/**
* Remove an event listener from an object.
* @name removeEvtListener
* @memberof Utils#
* @function
* @param obj {Object} DOM Element on which you want to add the event listener
* @param event {string} Event you need to listen
* @param callback {function}
* @public
*/
self.removeEvtListener = function (obj, event, callback) {
if (obj.removeEventListener) {
obj.removeEventListener(event, callback, false);
} else if (obj.detachEvent) {
obj.detachEvent('on' + event, callback);
}
};
/**
* Make a unique number with a given string.
* @name hashcode
* @memberof Utils#
* @function
* @param str
* @return {number}
* @public
*/
self.hashcode = function (str) {
var hash = 0;
if (str.length === 0) return hash;
for (var i = 0; i < str.length; i++) {
var character = str.charCodeAt(i);
hash = ((hash << 5) - hash) + character;
hash |= 0;
}
return hash;
};
/**
* Force document location.
* @name setLocation
* @memberof Utils#
* @function
* @param contextObj {Object} Redirection's url and target (properties location & target)
* @public
*/
self.setLocation = function (contextObj) {
var loc = contextObj['location'];
var obj = window[contextObj['target']] || window;
if (loc) {
obj.location.href = loc;
}
};
/****************************** Callback management *************************/
/**
* Dispatch event for callbacks
* @name dispatchCallbackEvent
* @memberof Utils#
* @function
* @param name {string} Callback's name
* @public
*/
self.dispatchCallbackEvent = function (name) {
// Create the event.
var event;
if (typeof window.Event === 'function') {
event = new Event('ATCallbackEvent');
} else {
try {
event = document.createEvent('Event');
// Define that the event name is 'ATCallbackEvent'.
// Deprecated.
event.initEvent && event.initEvent('ATCallbackEvent', true, true);
} catch (e) {
}
}
if (event && (typeof document.dispatchEvent === 'function')) {
event.name = name;
document.dispatchEvent(event);
}
};
/**
* Add callback event
* @name addCallbackEvent
* @memberof Utils#
* @function
* @param func {function} function to execute
* @public
*/
self.addCallbackEvent = function (func) {
// Listen to the event.
self.addEvtListener(document, 'ATCallbackEvent', func);
};
/**
* Remove callback event
* @name removeCallbackEvent
* @memberof Utils#
* @param func {function} function to remove
* @function
* @public
*/
self.removeCallbackEvent = function (func) {
self.removeEvent('ATCallbackEvent', func);
};
/****************************** Custom Event management *************************/
/**
* Polyfill the CustomEvent() constructor functionality in Internet Explorer 9 and higher
* @memberof Utils#
* @function
* @private
*/
(function () {
if (typeof window.CustomEvent === 'function') {
window.ATCustomEvent = window.CustomEvent;
return;
}
function ATCustomEvent(evt, params) {
params = params || {bubbles: false, cancelable: false, detail: undefined};
var event;
try {
event = document.createEvent('CustomEvent');
event.initCustomEvent(evt, params.bubbles, params.cancelable, params.detail);
} catch (e) {
}
return event;
}
if (typeof window.Event === "function") {
ATCustomEvent.prototype = window.Event.prototype;
}
window.ATCustomEvent = ATCustomEvent;
})();
/**
* Add custom event
* @name addEvent
* @memberof Utils#
* @param eventName {string} event name
* @param detailName {string} detail name
* @param detailID {number} detail uuid
* @param func {function} function to execute
* @function
* @public
*/
self.addEvent = function (eventName, detailName, detailID, func) {
self[eventName] = new ATCustomEvent(eventName, {
detail: {
name: detailName,
id: detailID
}
});
// Listen to the event.
self.addEvtListener(document, eventName, func);
};
/**
* Remove custom event
* @name removeEvent
* @memberof Utils#
* @param eventName {string} event name
* @param func {function} function to execute
* @function
* @public
*/
self.removeEvent = function (eventName, func) {
// Remove the event listener.
self.removeEvtListener(document, eventName, func);
};
/**
* Dispatch custom event
* @name dispatchEvent
* @memberof Utils#
* @param eventName {string} event name
* @param detailName {string} detail name
* @function
* @public
*/
self.dispatchEvent = function (eventName, detailName) {
// Init the event.
self[eventName] = self[eventName] || new ATCustomEvent(eventName, {
detail: {
name: detailName,
id: -1
}
});
try {
document.dispatchEvent(self[eventName]);
} catch (e) {
}
};
/****************************** Privacy management *************************/
/**
* Object used to manage authority
* @name Privacy
* @memberof Utils#
* @inner
* @constructor
* @property {function} processStorageParam Authority helper, see details here {@link Utils#Privacy.processStorageParam}
* @property {function} processBufferParam Authority helper, see details here {@link Utils#Privacy.processBufferParam}
* @property {function} setParameters Authority helper, see details here {@link Utils#Privacy.setParameters}
* @property {function} getParameters Authority helper, see details here {@link Utils#Privacy.getParameters}
* @property {function} resetParameters Authority helper, see details here {@link Utils#Privacy.resetParameters}
* @private
*/
var Privacy = function () {
/* -------- Privacy properties -------- */
var _thisPrivacy = this;
var _privacyParameters = {
storageParams: null,
bufferParams: null
};
_thisPrivacy.CONSENTNO = 'Consent-NO';
_thisPrivacy.ALL = '*';
/* -------- Privacy internal methods -------- */
/**
* Test if parameter can be stored
* @memberof Utils#
* @param param {string} Parameter to store
* @param prop {string} Property to store
* @param toInclude {Object} Object containing parameters and properties to be included
* @function
* @return {Object}
* @private
* @example
* // param
* // "atredir"
* //
* // prop
* // 'at'
* //
* // toInclude
* // {"atredir":["at"]}
*/
function _testStorageParamObject(param, prop, toInclude) {
var _storageParamsArray;
for (var storageObjectProperty in toInclude) {
if (toInclude.hasOwnProperty(storageObjectProperty)) {
if (param === storageObjectProperty) {
if (!prop) {
return {"toSetInStorage": true};
}
_storageParamsArray = [];
if (toInclude[storageObjectProperty] instanceof Array) {
_storageParamsArray = toInclude[storageObjectProperty];
} else {
_storageParamsArray.push(toInclude[storageObjectProperty]);
}
for (var j = 0; j < _storageParamsArray.length; j++) {
if (_storageParamsArray[j] === prop) {
return {"toSetInStorage": true};
}
}
}
}
}
return {"toSetInStorage": false};
}
/**
* Process storage parameters
* @memberof Utils#
* @param param {string} Parameter to store
* @param value {Object} Parameter value to process
* @param toInclude {string|Array} Properties to be included
* @param delCallback {function} Callback to delete in storage
* @param getCallback {function} Callback to get from storage
* @function
* @private
* @example
* // param :
* // "atidvisitor"
* //
* // value :
* // {
* // "vrn": "-410501--123456-",
* // "at": "abcd123",
* // "ac": "21"
* // }
* //
* // toInclude :
* // "an"
* // ["an", "at"]
*/
function _processStorageParamObject(param, value, toInclude, delCallback, getCallback) {
if (typeof toInclude !== 'undefined') {
var _toIncludeArray = [];
if (toInclude instanceof Array) {
_toIncludeArray = toInclude;
} else {
_toIncludeArray.push(toInclude);
}
// We browse the list of keys contained in the value
for (var key in value) {
// An entry is deleted if it is not part of the inclusion list
if (value.hasOwnProperty(key) && self.arrayIndexOf(_toIncludeArray, key) === -1) {
delCallback && delCallback([param, key]);
}
}
// A parameter is completely deleted if its value is empty
if (delCallback && getCallback && self.isEmptyObject(getCallback(param))) {
delCallback(param);
}
}
}
/**
* Get a cleaned array
* @memberof Utils#
* @param value {Array} Parameter value to process
* @param toInclude {Array} Parameters to include
* @function
* @return {Array}
* @private
*/
function _getCleanedArray(value, toInclude) {
var _newArray = [];
var _flattenObject;
var _temporaryObject;
var _finalObject = {};
for (var i = 0; i < value.length; i++) {
_flattenObject = {};
self.object2Flatten(value[i], null, _flattenObject, null, true);
// We browse the list of keys contained in the value
for (var flattenObjectKey1 in _flattenObject) {
// An entry is deleted if it is not part of the inclusion list
if (_flattenObject.hasOwnProperty(flattenObjectKey1) && self.arrayIndexOf(toInclude, flattenObjectKey1) === -1) {
delete _flattenObject[flattenObjectKey1];
}
}
// A parameter cannot be set if its value is empty
if (!self.isEmptyObject(_flattenObject)) {
_temporaryObject = {};
for (var flattenObjectKey2 in _flattenObject) {
if (_flattenObject.hasOwnProperty(flattenObjectKey2)) {
self.flatten2Object(_temporaryObject, flattenObjectKey2, _flattenObject[flattenObjectKey2]);
}
}
_finalObject = self.getFormattedObject(_temporaryObject);
_newArray.push(_finalObject);
}
}
return _newArray;
}
/**
* Test if parameter can be can be saved in the buffer
* @memberof Utils#
* @param param {string} Parameter to buffer
* @param value {string} Parameter value to process
* @param toInclude {Object} Object containing parameters and properties to be included
* @function
* @return {Object}
* @private
* @example
* // param
* // "stc"
* //
* // value
* // '"{\"key1\":\"val1\",\"key2\":\"val2\"}"'
* // '"[\"key1\",\"key2\"]"'
* //
* // toInclude
* // {"stc":["key1"]}
*/
function _testBufferParamObject(param, value, toInclude) {
if (self.isObject(toInclude)) {
var _value;
var _valueArray = [];
var _isArray = false;
var _cleanedArray;
var _newValue;
for (var bufferProperty in toInclude) {
if (toInclude.hasOwnProperty(bufferProperty) && (param === bufferProperty)) {
_value = value;
if (typeof _value === 'string') {
_value = self.jsonParse(_value) || _value;
}
if (typeof _value === 'object') {
if (_value instanceof Array) {
_valueArray = _value;
_isArray = true
} else {
_valueArray.push(_value);
}
_cleanedArray = _getCleanedArray(_valueArray, toInclude[bufferProperty]);
// A parameter cannot be set if its value is empty
if (_cleanedArray.length === 0) {
return {"toSetInBuffer": false};
} else {
// Otherwise we update its value
_newValue = _isArray ? _cleanedArray : _cleanedArray[0];
return {"toSetInBuffer": true, "value": self.jsonSerialize(_newValue)};
}
}
}
}
}
return {"toSetInBuffer": false};
}
/**
* Process buffer parameters
* @memberof Utils#
* @param param {Object} Parameter to buffer
* @param value {Object} Parameter value to process
* @param toInclude {string|Array} Properties to be included
* @param delCallback {function} Callback to delete in buffer
* @param setCallback {function} Callback to set in buffer
* @function
* @private
* @example
* // param :
* // "stc"
* //
* // value :
* // {
* // "_value": "{\"key1\":\"val1\",\"key2\":\"val2\"}",
* // "_options": {
* // "hitType": ["page"],
* // "encode": true,
* // "separator": ",",
* // "truncate": true
* // }
* // }
* //
* // toInclude :
* // "key1"
* // ["key1", "key2"]
*/
function _processBufferParamObject(param, value, toInclude, delCallback, setCallback) {
if (typeof toInclude !== 'undefined') {
var _toIncludeArray = [];
var _value = value._value;
var _valueArray = [];
var _isArray = false;
var _cleanedArray;
var _newValue;
if (toInclude instanceof Array) {
_toIncludeArray = toInclude;
} else {
_toIncludeArray.push(toInclude);
}
if (typeof _value === 'string') {
_value = self.jsonParse(_value) || _value;
}
if (typeof _value === 'object') {
if (_value instanceof Array) {
_valueArray = _value;
_isArray = true
} else {
_valueArray.push(_value);
}
_cleanedArray = _getCleanedArray(_valueArray, _toIncludeArray);
// A parameter cannot be set if its value is empty
if (_cleanedArray.length === 0) {
delCallback && delCallback(param);
} else {
// Otherwise we update its value
_newValue = _isArray ? _cleanedArray : _cleanedArray[0];
setCallback && setCallback(param, self.jsonSerialize(_newValue), value._options);
}
}
}
}
/**
* Get keys and values from parameters
* @memberof Utils#
* @param privacyParams {Array} Parameters to include
* @function
* @return {Object}
* @private
*/
function _getParamKeysAndValues(privacyParams) {
// Listing of the main keys of the privacy table
var _paramsMainKeys = [];
// Listing of the values of the objects in the privacy table
var _paramsMainObjectValues = {};
for (var i = 0; i < privacyParams.length; i++) {
if (typeof privacyParams[i] === 'string') {
_paramsMainKeys.push(privacyParams[i]);
} else {
for (var key in privacyParams[i]) {
if (privacyParams[i].hasOwnProperty(key)) {
_paramsMainKeys.push(key);
_paramsMainObjectValues[key] = (_paramsMainObjectValues[key] || []).concat(privacyParams[i][key]);
}
}
}
}
return {
"keys": _paramsMainKeys,
"values": _paramsMainObjectValues
};
}
/* -------- Privacy helpers -------- */
/**
* Test if parameter can be stored
* @alias Privacy.testStorageParam
* @memberof Utils#
* @param param {string} Parameter to store
* @param prop {string} Property to store
* @function
* @return {Object}
* @public
* @example
* ATInternet.Utils.privacy.testStorageParam(param, prop);
* // param
* // "atredir"
* //
* // prop
* // 'at'
* //
* // _privacyParameters.storageParams
* // ["atidvisitor",{"atredir":["at"]}]
*/
_thisPrivacy.testStorageParam = function (param, prop) {
if (_privacyParameters.storageParams instanceof Array) {
var _storageParam, _toSetInStorageObject;
for (var i = _privacyParameters.storageParams.length - 1; i >= 0; i--) {
_storageParam = _privacyParameters.storageParams[i];
if (typeof _storageParam === 'string') {
if (_storageParam === param || _storageParam === _thisPrivacy.ALL) {
return {"toSetInStorage": true};
}
} else {
_toSetInStorageObject = _testStorageParamObject(param, prop, _storageParam);
if (_toSetInStorageObject.toSetInStorage) {
return {"toSetInStorage": true};
}
}
}
return {"toSetInStorage": false};
}
return {"toSetInStorage": true};
};
/**
* Process storage parameters
* @alias Privacy.processStorageParams
* @memberof Utils#
* @param delCallback {function} Callback to delete in storage
* @param getCallback {function} Callback to get from storage
* @param getAllCallback {function} Callback to get all from storage
* @function
* @return {Object}
* @public
* @example
* ATInternet.Utils.privacy.processStorageParams(delCallback, getCallback, getAllCallback);
* // _allParams :
* // {
* // "atidvisitor": {
* // "name": "atidvisitor",
* // "val": {
* // "vrn": "-410501--123456-",
* // "at": "abcd123",
* // "ac": "21"
* // },
* // "options": {
* // "path": "/",
* // "session": 15724800,
* // "end": 15724800
* // }
* // }
* // }
* //
* // "_privacyParameters.storageParams": [
* // "atuserid",
* // {
* // "atidvisitor": ["an", "at"]
* // }
* // ]
*/
_thisPrivacy.processStorageParams = function (delCallback, getCallback, getAllCallback) {
if (getAllCallback) {
// Listing of parameters stored in cookie or localStorage
var _allParams = getAllCallback();
// Listing of parameters and values to be included
var _params = _getParamKeysAndValues(_privacyParameters.storageParams);
if (_params.keys[0] !== _thisPrivacy.ALL) {
for (var param in _allParams) {
if (_allParams.hasOwnProperty(param)) {
// If the parameter is not part of the inclusion list then it is deleted.
if (self.arrayIndexOf(_params.keys, param) === -1) {
delCallback && delCallback(param);
} else if (self.isObject(_allParams[param])) {
// Otherwise you check its keys
_processStorageParamObject(param, _allParams[param].val, _params.values[param], delCallback, getCallback);
}
}
}
}
}
};
/**
* Test if parameter can be can be saved in the buffer
* @alias Privacy.testBufferParam
* @memberof Utils#
* @param param {string} Parameter to buffer
* @param value {string} Parameter value to process
* @function
* @return {Object}
* @public
* @example
* ATInternet.Utils.privacy.testBufferParam(param, value);
* // param
* // "stc"
* //
* // value
* // '"{\"key1\":\"val1\",\"key2\":\"val2\"}"'
* // '"[\"key1\",\"key2\"]"'
*/
_thisPrivacy.testBufferParam = function (param, value) {
if (_privacyParameters.bufferParams instanceof Array) {
// _privacyParameters.bufferParams
// ["custom",{"stc":["key1"]}]
var _bufferParam, _toSetInBufferObject;
for (var i = _privacyParameters.bufferParams.length - 1; i >= 0; i--) {
_bufferParam = _privacyParameters.bufferParams[i];
if (typeof _bufferParam === 'string') {
if (_bufferParam === param || _bufferParam === _thisPrivacy.ALL) {
return {"toSetInBuffer": true, "value": value};
}
} else {
_toSetInBufferObject = _testBufferParamObject(param, value, _bufferParam);
if (_toSetInBufferObject.toSetInBuffer) {
return {"toSetInBuffer": true, "value": _toSetInBufferObject.value};
}
}
}
return {"toSetInBuffer": false};
}
return {"toSetInBuffer": true, "value": value};
};
/**
* Process buffer parameters
* @alias Privacy.processBufferParams
* @memberof Utils#
* @param delCallback {function} Callback to delete in buffer
* @param getAllCallback {function} Callback to get all from buffer
* @param setCallback {function} Callback to set in buffer
* @function
* @public
* @example
* ATInternet.Utils.privacy.processBufferParams(delCallback, getAllCallback, setCallback);
* // _allParams :
* // {
* // "stc": {
* // "_value": "{\"key1\":\"val1\",\"key2\":\"val2\"}",
* // "_options": {
* // "hitType": ["page"],
* // "encode": true,
* // "separator": ",",
* // "truncate": true
* // }
* // }
* //
* // "_privacyParameters.bufferParams": [
* // "an",
* // {
* // "stc": [
* // "key1",
* // "key2"
* // ]
* // }
* // ]
*/
_thisPrivacy.processBufferParams = function (delCallback, getAllCallback, setCallback) {
if (getAllCallback) {
// Listing of parameters stored in the buffer
var _allParams = getAllCallback();
// Listing of parameters and values to be included
var _params = _getParamKeysAndValues(_privacyParameters.bufferParams);
if (_params.keys[0] !== _thisPrivacy.ALL) {
for (var param in _allParams) {
if (_allParams.hasOwnProperty(param)) {
// If the parameter is not part of the inclusion list then it is deleted.
if (self.arrayIndexOf(_params.keys, param) === -1) {
delCallback && delCallback(param);
} else {
// Otherwise you check its keys
_processBufferParamObject(param, _allParams[param], _params.values[param], delCallback, setCallback);
}
}
}
}
}
};
/**
* Set privacy parameters values
* @alias Privacy.setParameters
* @memberof Utils#
* @param privacyParameters {Object} Privacy parameters to set
* @function
* @public
* @example
* ATInternet.Utils.privacy.setParameters(privacyParameters);
*/
_thisPrivacy.setParameters = function (privacyParameters) {
_privacyParameters = privacyParameters;
};
/**
* Get privacy parameters values
* @alias Privacy.getParameters
* @memberof Utils#
* @function
* @return {Object}
* @public
* @example
* ATInternet.Utils.privacy.getParameters();
*/
_thisPrivacy.getParameters = function () {
return _privacyParameters;
};
/**
* Reset privacy parameters values
* @alias Privacy.resetParameters
* @memberof Utils#
* @function
* @public
* @example
* ATInternet.Utils.privacy.resetParameters();
*/
_thisPrivacy.resetParameters = function () {
_privacyParameters = {
storageParams: null,
bufferParams: null
};
};
// For unit tests on private elements !!!
/* @if test */
_thisPrivacy._getPrivacyParameters = function () {
return _privacyParameters;
};
_thisPrivacy._setPrivacyParameters = function (privacyParameters) {
_privacyParameters = privacyParameters;
};
_thisPrivacy._testStorageParamObject = _testStorageParamObject;
_thisPrivacy._processStorageParamObject = _processStorageParamObject;
_thisPrivacy._getCleanedArray = _getCleanedArray;
_thisPrivacy._testBufferParamObject = _testBufferParamObject;
_thisPrivacy._processBufferParamObject = _processBufferParamObject;
_thisPrivacy._getParamKeysAndValues = _getParamKeysAndValues;
/* @endif */
};
/**
* Privacy object
* @name privacy
* @memberof Utils#
* @public
*/
self.privacy = new Privacy();
/****************************** OPT-OUT management *************************/
/**
* OPT-OUT value
* @name optedOut
* @memberof Utils#
* @private
*/
self.optedOut = null;
/**
* Test value from OPT-OUT stored object
* @memberof Utils#
* @function
* @return {Boolean}
* @private
*/
function isOptoutInStorage() {
var sItem = getStorageData('atuserid');
if (sItem) {
var item = self.jsonParse(sItem) || self.jsonParse(self.Base64.decode(sItem));
if (item !== null) {
return item.val === 'OPT-OUT';
}
}
return false;
}
/**
* Add OPT-OUT event
* @name addOptOutEvent
* @memberof Utils#
* @param eventID {number} uuid
* @param func {function} function to associate
* @function
* @public
*/
self.addOptOutEvent = function (eventID, func) {
self.addEvent('ATOptOutEvent', 'clientsideuserid', eventID, func);
};
/**
* Remove OPT-OUT event
* @name removeOptOutEvent
* @memberof Utils#
* @param func {function} function to remove
* @function
* @public
*/
self.removeOptOutEvent = function (func) {
self.removeEvent('ATOptOutEvent', func);
};
/**
* Dispatch event for OPT-OUT
* @name dispatchOptOutEvent
* @memberof Utils#
* @param active {boolean} Activate or deactivate OPT-OUT
* @function
* @private
*/
self.dispatchOptOutEvent = function (active) {
self.optedOut = active;
self.dispatchEvent('ATOptOutEvent', 'clientsideuserid');
};
/**
* Activate OPT-OUT mode adding OPT-OUT value in hit and storage for idclient parameter
* @name userOptedOut
* @memberof Utils#
* @function
* @public
*/
self.userOptedOut = function () {
self.dispatchOptOutEvent(true);
};
/**
* Deactivate OPT-OUT mode
* @name userOptedIn
* @memberof Utils#
* @function
* @public
*/
self.userOptedIn = function () {
self.dispatchOptOutEvent(false);
};
/**
* Get OPTOUT activation status
* @name isOptedOut
* @memberof Utils#
* @function
* @public
*/
self.isOptedOut = function () {
if (self.optedOut === null) {
self.optedOut = isOptoutInStorage();
}
return !!self.optedOut;
};
/****************************** Consent Page management *************************/
/**
* Specify whether consent has been obtained preventing or allowing data to be stored in cookies or local storage
* @name consentReceived
* @memberof Utils#
* @param active {boolean} false to prevent data to be stored, true to allow data to be stored
* @function
* @public
*/
self.consentReceived = function (active) {
self.consent = !!active;
};
/**
* Consent page option value
* @name consent
* @memberof Utils#
* @private
*/
self.consent = true;
/****************************** Click event management *************************/
/**
* Check if a key allowing a tab opening is pressed during a click
* @name isTabOpeningAction
* @memberof Utils#
* @param event {Object} event object
* @function
* @public
* @return {boolean}
*/
self.isTabOpeningAction = function (event) {
var isTabOpening = false;
if (event && (event.ctrlKey ||
event.shiftKey ||
event.metaKey ||
(event.button && event.button === 1))
) {
isTabOpening = true;
}
return isTabOpening;
};
// Constants for managing click actions according to the DOM Element
self.CLICKS_REDIRECTION = 'redirection';
self.CLICKS_FORM = 'form';
self.CLICKS_MAILTO = 'mailto';
/*******************************************************************************/
// For unit tests on private elements !!!
/* @if test */
self.serialJSON = serialJSON;
self.parseJSON = parseJSON;
self.getStorageData = getStorageData;
self.isOptoutInStorage = isOptoutInStorage;
/* @endif */
};
/**
* Module with utility methods.
* @name ATInternet.Utils
* @memberof ATInternet
* @type {Utils}
* @public
* @see {@link Utils}
*/
ATInternet.Utils = new Utils();