/**
* @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
* @returns {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 && 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 text.
* @memberof Utils#
* @param text {String} Text to trim
* @returns {String}
* @private
*/
function trim(text) {
return text === null ?
'' :
( text + '' ).replace(rtrim, '');
}
/**
* Parse any string.
* @memberof Utils#
* @param data {string} String to parse
* @returns {*}
* @private
*/
function parseJSON(data) {
var requireNonComma,
depth = null,
str = trim(data + '');
// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
// after removing valid tokens
return str && !trim(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;
}
};
/**
* 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) {
var cookies = document.cookie;
var regExp = new RegExp('(?:^| )' + name + '=([^;]+)');
var result = regExp.exec(cookies) || null;
if (result) {
storedData = result[1];
}
}
return decodeURIComponent(storedData);
}
/**
* Test value from OPT-OUT stored object
* @memberof Utils#
* @function
* @return {Boolean}
* @private
*/
function isValFromOptedOutStorage() {
var sItem = getStorageData('atoptedout');
if (sItem) {
var item = self.jsonParse(sItem) || self.jsonParse(self.Base64.decode(sItem));
if (item) {
return !!item.val;
}
}
return false;
}
/**
* 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);
};
//On ignore volontairement le deuxième paramètre qui nous est réservé :)
/**
* 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
* @returns newInst {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;
};
/**
* 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
* @returns {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
* @returns {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;
}
};
/**
* Search an element in an array. Use Array.indexOf if possible.
* @name arrayIndexOf
* @memberof Utils#
* @function
* @param arr {Array}
* @param elemToSearch {*}
* @returns {number}
* @public
*/
self.arrayIndexOf = function (arr, elemToSearch) {
if (Array.indexOf) {
return arr.indexOf(elemToSearch);
} 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
* @returns {function}
* @public
*/
self.uuid = function () {
/**
* Generate GUID with alphanumeric characters (v4 format).
* @name _v4
* @memberof Utils#
* @function
* @returns {string}
* @public
*/
function _v4() {
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 GUID with numeric characters.
* @name _num
* @memberof Utils#
* @function
* @param len {number} Length of the generated GUID
* @returns {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);
}
};
var rand = function (n) {
return Math.floor((Math.random() * 9 + 1) * Math.pow(10, n - 1));
};
return format(d.getHours()) + '' + format(d.getMinutes()) + '' + format(d.getSeconds()) + '' + rand(len - 6);
}
return {
v4: _v4,
num: _num
};
};
/**
* Get all keys from object.
* @name getObjectKeys
* @memberof Utils#
* @function
* @param object {object}
* @returns {Array}
* @public
*/
self.getObjectKeys = function (object) {
var keys = [];
for (var elem in object) {
if (object.hasOwnProperty(elem)) {
keys.push(elem);
}
}
return keys;
};
/**
* 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
* @returns {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;
};
/**
* Check if we are in Safari previewing case.
* @name isPreview
* @memberof Utils#
* @function
* @returns {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}
* @returns {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
*/
var _addEvtListener = 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
*/
var _removeEvtListener = 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
* @returns {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.
_addEvtListener(document, '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 false;
}
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.
_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.
_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) {
}
};
/****************************** OPT-OUT management *************************/
/**
* 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) {
// Init OPT-OUT mode.
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 = isValFromOptedOutStorage();
}
return !!self.optedOut;
};
/**
* OPT-OUT value
* @name optedOut
* @memberof Utils#
* @private
*/
self.optedOut = null;
/****************************** 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;
/*******************************************************************************/
// For unit tests on private elements !!!
/* @if test */
self.serialJSON = serialJSON;
self.trim = trim;
self.parseJSON = parseJSON;
self.getStorageData = getStorageData;
self.isValFromOptedOutStorage = isValFromOptedOutStorage;
/* @endif */
};
/**
* Module with utility methods.
* @name ATInternet.Utils
* @memberof ATInternet
* @type {Utils}
* @public
* @see {@link Utils}
*/
window['ATInternet']['Utils'] = new Utils();