/**
* @class
* @classdesc Main Tag Object.
* @name ATInternet.Tracker.Tag
* @memberof ATInternet.Tracker
* @type {function}
* @param config {object} Configuration of the Tag
* @param context {object} Context of the Tag
* @param trackerCallback {function} Executed when tracker is ready
* @public
*/
var Tag = function (config, context, trackerCallback) {
'use strict';
context = context || {};
var self = this;
/**
* Version of the tracker.
* @name version
* @memberof ATInternet.Tracker.Tag
* @inner
* @type {string}
* @public
*/
self.version = '#VERSION#';
/**
* Contains tagging configuration: context variable (like 'page', 'level2', ...)
* @type {object}
* @private
*/
var _context = ATInternet.Utils.cloneSimpleObject(context);
//instantiation of all sub objects
/**
* Module for triggers management.
* @name triggers
* @memberof ATInternet.Tracker.Tag#
* @type {TriggersManager}
* @public
* @see {@link TriggersManager}
*/
self.triggers = new TriggersManager(self);
/**
* Use to trigger event.
* @name emit
* @memberof ATInternet.Tracker.Tag#
* @function
* @param trigger {string} Trigger to emit
* @param data {object} Data to transmit to listeners
* @public
* @example
* <pre><code class="javascript">tag.emit('RichMedia:richMedia:remove', {
* lvl: 'DEBUG',
* msg: 'method ended',
* details: {playerId: plyr, media: mediaObject}
* });
* </code></pre>
* @see {@link TriggersManager#emit}
*/
self.emit = self.triggers.emit;
/**
* Use to emit debug triggers.
* @memberof ATInternet.Tracker.Tag#
* @name debug
* @function
* @param trigger {string} Trigger that you want to emit
* @param level {string} DEBUG, ERROR, WARNING
* @param message {string}
* @param details {*} Data you want to transmit to listeners
* @public
* @example
* <pre><code class="javascript">tag.debug('RichMedia:richMedia:remove', 'DEBUG', 'method ended', {playerId: plyr, media: mediaObject});
* </code></pre>
*/
/* @if debug */
self.debug = function (trigger, level, message, details) {
self.emit(trigger, {
lvl: level,
msg: message,
details: details
});
};
/* @endif */
/**
* Use to add a trigger listener.
* @name onTrigger
* @memberof ATInternet.Tracker.Tag#
* @function
* @param trigger {string} Trigger you want to subscribe
* @param callback {function} Method that you want to be triggered
* @returns {number} Callback ID
* @public
* @example
* <pre><code class="javascript">// It returns the ID of the callback so you can delete it if needed.
* var idCallback = tag.onTrigger('myTrigger', callback(trig, data, idCallbackBis){});
* </code></pre>
* @see {@link TriggersManager#on}
*/
self.onTrigger = self.triggers.on;
/**
* Internal configuration object.
* @type {object}
* @private
*/
var _conf = ATInternet.Utils.cloneSimpleObject(dfltGlobalCfg) || {};
//Overloading of the configuration with the one given in parameter
for (var k in config) {
if (config.hasOwnProperty(k)) {
_conf[k] = config[k];
}
}
/**
* Get configuration.
* @name getConfig
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Configuration property name
* @returns {*} Configuration property value if exist, undefined if not
* @public
*/
self.getConfig = function (key) {
return _conf[key];
};
/**
* Set configuration.
* @name setConfig
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Configuration property name
* @param value {*} Configuration property value
* @param ifNotExist {boolean} If true, the property will only be set if the configuration property doesn't exist
* @returns {*}
* @public
*/
self.setConfig = function (key, value, ifNotExist) {
if (_conf[key] === undefined || !ifNotExist) {
self.emit('Tracker:Config:Set:' + key, {lvl: 'INFO', details: {bef: _conf[key], aft: value}});
_conf[key] = value;
}
};
/**
* Set the specific configuration of a plugin.
* If the configuration already exists, set only the undefined properties.
* @name configPlugin
* @memberof ATInternet.Tracker.Tag#
* @function
* @param plg {string} Name of the plugin concerned
* @param cfg {object} Value of the plugin configuration
* @param cbk {function} Function which be executed if the plugin configuration change
* @returns {object}
* @public
*/
self.configPlugin = function (plg, cfg, cbk) {
_conf[plg] = _conf[plg] || {};
for (var key in cfg) {
if (cfg.hasOwnProperty(key)) {
if (_conf[plg][key] === undefined) {
_conf[plg][key] = cfg[key];
}
}
}
if (cbk) {
cbk(_conf[plg]);
self.onTrigger('Tracker:Config:Set:' + plg, function (trig, data) {
cbk(data.details.aft);
});
}
return _conf[plg];
};
/**
* Get all context.
* @name getAllContext
* @memberof ATInternet.Tracker.Tag#
* @function
* @returns {*}
* @public
*/
self.getAllContext = function () {
return _context;
};
/**
* Get a context variable.
* @name getContext
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Context variable name
* @returns {*}
* @public
*/
self.getContext = function (key) {
return _context[key];
};
/**
* Set a context variable.
* @name setContext
* @memberof ATInternet.Tracker.Tag#
* @function
* @param {string} key context variable name
* @param {*} value
* @public
*/
self.setContext = function (key, value) {
self.emit('Tracker:Context:Set:' + key, {lvl: 'INFO', details: {bef: _context[key], aft: value}});
_context[key] = value;
};
/**
* Delete context value or parameter value in context(s).
* @name delContext
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key1 {string} Context name
* @param key2 {string} Context parameter name (first level)
* @public
* @example
* <pre><code class="javascript">tag.delContext('myContext'); //=> Delete 'myContext' content
* tag.delContext('myContext', 'param'); //=> Delete 'param' content in 'myContext'
* tag.delContext(undefined, 'param'); //=> Delete 'param' content in all contexts
* </code></pre>
*/
self.delContext = function (key1, key2) {
self.emit('Tracker:Context:Deleted:' + key1 + ':' + key2, {lvl: 'INFO', details: {key1: key1, key2: key2}});
if (key1) {
if (_context.hasOwnProperty(key1)) {
if (key2) {
if (_context[key1] && _context[key1].hasOwnProperty(key2)) {
_context[key1][key2] = undefined;
}
} else {
_context[key1] = undefined;
}
}
} else if (key2) {
var key;
for (key in _context) {
if (_context.hasOwnProperty(key)) {
if (_context[key] && _context[key].hasOwnProperty(key2)) {
_context[key][key2] = undefined;
}
}
}
}
};
/**
* Module for plugins management.
* @name plugins
* @memberof ATInternet.Tracker.Tag#
* @type {PluginsManager}
* @public
* @see {@link PluginsManager}
*/
self.plugins = new PluginsManager(self);
/**
* Module for buffer management.
* @name buffer
* @memberof ATInternet.Tracker.Tag#
* @type {BufferManager}
* @public
* @see {@link BufferManager}
*/
self.buffer = new BufferManager(self);
/**
* Set value for a hit variable (overrides if present).
* @name setParam
* @memberof ATInternet.Tracker.Tag#
* @function
* @param name {string} Name of the hit variable
* @param value {string|number|function|Array} value of the hit variable
* @param options {object} Configuration of the variable, if no hitType defined, it will be "page" by default
* @public
* @example
* <pre><code class="javascript">tag.setParam('test1', 'val1', {hitType: ['test', 'click'], permanent: true, encode: true});
* </code></pre>
* @see {@link BufferManager}
*/
self.setParam = self.buffer.set;
/**
* Use to get the collection of hit parameters stored (or a value if a parameter name is given).
* @name getParams
* @memberof ATInternet.Tracker.Tag#
* @function
* @param param {string} Parameter name (optional)
* @returns {string|object}
* @public
* @see {@link BufferManager}
*/
self.getParams = function (param) {
return self.buffer.get(param, false);
};
/**
* Get variables from the buffer using the filter given, with possibility of returning options or not.
* @name getParam
* @memberof ATInternet.Tracker.Tag#
* @function
* @param filterList {Array} List of key/value(s) in an array. (ex : [[key1,value1],[key2,[value2A,value2B]]]) Filter on variable's options
* @param withOptions {boolean} If true only returns value else returns object with value and options
* @returns {string|object}
* @public
* @example
* <pre><code class="javascript">// This filter will get variables with hitType 'page' OR 'all', AND with permanent true
* var filter = [
* ['hitType',['page','all']],
* ['permanent',true]
* ]
*
* var dataObj = tag.getParam(filter, true); // true to get options
* dataObj = {
* 'variableExample' : {
* _value:'value',
* _options: {
* hitType:['page'],
* permanent:true
* }
* }
* }
*
* var dataObj = tag.getParam(filter); // no option in results
* dataObj = {
* 'variableExample':'value'
* }
* </code></pre>
* @see {@link BufferManager}
*/
self.getParam = self.buffer.get;
/**
* Use to delete a stored parameter.
* @name delParam
* @memberof ATInternet.Tracker.Tag#
* @function
* @public
* @see {@link BufferManager}
*/
self.delParam = self.buffer.del;
/**
* Module for hit builder.
* @name builder
* @memberof ATInternet.Tracker.Tag#
* @type {BuildManager}
* @public
* @see {@link BuildManager}
*/
self.builder = new BuildManager(self);
/**
* Send single hit from complete url. An event will be sent thanks to {@link TriggersManager} :
* <br />- <b>"Tracker:Hit:Sent:Ok"</b> with the hit as data if succeed,
* <br />- <b>"Tracker:Hit:Sent:Error"</b> with error as data otherwise.
* @name sendUrl
* @memberof ATInternet.Tracker.Tag#
* @function
* @param hit {string} Url to send
* @public
* @see {@link BuildManager}
*/
self.sendUrl = self.builder.sendUrl;
/**
* Module for callback management.
* @name callbacks
* @memberof ATInternet.Tracker.Tag#
* @type {CallbacksManager}
* @public
* @see {@link CallbacksManager}
*/
self.callbacks = new CallbacksManager(self);
/**
* Module for properties management.
* @name properties
* @memberof ATInternet.Tracker.Tag#
* @type {PropertiesManager}
* @public
* @see {@link PropertiesManager}
*/
self.properties = new PropertiesManager(self);
/**
* Set a property (overrides if present)
* @name setProp
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Key name of the property
* @param value {string|number|Array} value of the property
* @param permanent {boolean} Permanence of property
* @public
*/
self.setProp = self.properties.setProp;
/**
* Set multiple properties
* @name setProps
* @memberof ATInternet.Tracker.Tag#
* @function
* @param props {object} Object to be added
* @param permanent {boolean} Permanence of object properties
* @public
*/
self.setProps = self.properties.setProps;
/**
* Delete/remove a property
* @name delProp
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Key name of the property
* @public
*/
self.delProp = self.properties.delProp;
/**
* Delete/remove all properties
* @name delProps
* @memberof ATInternet.Tracker.Tag#
* @function
* @public
*/
self.delProps = self.properties.delProps;
/**
* Get a property
* @name getProp
* @memberof ATInternet.Tracker.Tag#
* @function
* @param key {string} Key name of the property
* @public
*/
self.getProp = self.properties.getProp;
/**
* Get all properties
* @name getProp
* @memberof ATInternet.Tracker.Tag#
* @function
* @return {object}
* @public
*/
self.getProps = self.properties.getProps;
/**
* Send the hit and call the callback if present
* The hit will integrate parameters stored
* @name sendHit
* @memberof ATInternet.Tracker.Tag#
* @function
* @param customParams {object} Object which contains some hit parameters that you would like to send specifically (they are given priority over the current buffer)
* @param filters {Array} List of buffer filters
* @param callback {function} Callback to execute
* @param requestMethod {string} Overloading the global method of sending hits (GET|POST)
* @param elementType {string} Element type (mailto, form, redirection)
* @public
* @see {@link BuildManager}
*/
self.sendHit = function (customParams, filters, callback, requestMethod, elementType) {
var properties = self.getProps();
var value;
for (var key in properties) {
if (properties.hasOwnProperty(key)) {
value = properties[key].value;
if (properties[key].persistent) {
self.setParam(key.toLowerCase(), value, {permanent: true, hitType: ['all'], encode: true});
} else {
if (ATInternet.Utils.isObject(customParams)) {
customParams[key.toLowerCase()] = {
_value: value,
_options: {
hitType: ['all'],
encode: true
}
};
} else {
self.setParam(key.toLowerCase(), value, {hitType: ['all'], encode: true});
}
self.delProp(key, true);
}
}
}
self.builder.send(customParams, filters, callback, requestMethod, elementType);
};
// Reset privacy context parameters
ATInternet.Utils.privacy.resetParameters();
// Ajout du timestamp dans le hit pour le différencier d'un autre
self.setParam('ts', function () {
return new Date().getTime()
}, {permanent: true, hitType: ['all']});
// Ajout de l'idclient dans le cas où le stockage est désactivé
if (self.getConfig('disableCookie') || self.getConfig('disableStorage')) {
self.setParam('idclient', ATInternet.Utils.privacy.CONSENTNO, {permanent: true, hitType: ['all']});
}
// Ajout du paramètre medium (ex: fia pour Facebook) si déclaré dans le marqueur
if (self.getConfig('medium')) {
self.setParam('medium', self.getConfig('medium'), {permanent: true, hitType: ['all']});
}
// Ajout du paramètre page_url aux hits et aux events qui contient l'url courante
if (self.getConfig('urlPropertyAuto') && typeof window !== 'undefined' && typeof window.location !== 'undefined') {
var currentUrl = (self.getConfig('urlPropertyQueryString')? window.location.href : window.location.protocol + '//' + window.location.host + window.location.pathname).replace(/[<>]/g, "").substring(0, 1600).replace(/&/g, '$');
var pageContext = self.getContext('page') || {};
pageContext.url = window.encodeURIComponent(currentUrl);
// value différente entre legacy et col=2 ? (à cause des "&" qui sont décodés/etc par le récupligne ?) ou on dégage ce caractère partout comme pour le referrer ?
self.setContext('page', pageContext); // page context is retrieved automatically for events (carefull, all properties are not retrieved !)
self.setParam('page_url', currentUrl, {permanent: true, hitType: ['page','click','publisher','selfPromotion','onSiteAdsClick','onSiteAdsImpression','InternalSearch','mvtesting','richmedia']});
}
/* Init */
self.plugins.init();
self.callbacks.init();
self.emit('Tracker:Ready', {
lvl: 'INFO',
msg: 'Tracker initialized',
details: {
tracker: self,
args: {
config: config,
context: context,
callback: trackerCallback
}
}
});
trackerCallback && trackerCallback(self);
// Ajout de l'instance dans la collection
ATInternet.Tracker.instances.push(self);
};
ATInternet.Tracker.Tag = Tag;
/**
* Reference all instances of Tag.
* @name instances
* @memberof ATInternet.Tracker
* @type {Array}
* @public
*/
ATInternet.Tracker.instances = [];
/**
* Reference all plugins loaded.
* @name pluginProtos
* @memberof ATInternet.Tracker
* @type {object}
* @public
*/
ATInternet.Tracker.pluginProtos = {};
/**
* This method loads a plugin in all instances of Tag.
* @name addPlugin
* @memberof ATInternet.Tracker
* @function
* @param name {string} Plugin's name to add
* @param obj {object} Plugin to add
* @public
*/
ATInternet.Tracker.addPlugin = function (name, obj) {
'use strict';
obj = obj || ATInternet.Tracker.Plugins[name];
//Ajout de son proto dans la collection (si pas déjà présent).
if (!ATInternet.Tracker.pluginProtos[name]) {
ATInternet.Tracker.pluginProtos[name] = obj;
//création d'une instance du plugin dans toutes les instances du tracker
for (var i = 0; i < ATInternet.Tracker.instances.length; i++) {
ATInternet.Tracker.instances[i].plugins.load(name, obj);
}
}
};
/**
* This method deletes a plugin loaded in all instances of Tag.
* @name delPlugin
* @memberof ATInternet.Tracker
* @function
* @param name {string} plugin's name to delete
* @public
*/
ATInternet.Tracker.delPlugin = function (name) {
'use strict';
//Ajout de son proto dans la collection (si pas déjà présent)
if (ATInternet.Tracker.pluginProtos[name]) {
ATInternet.Tracker.pluginProtos[name] = undefined;
//Suppression de l'instance du plugin dans toutes les instances du tracker
for (var i = 0; i < ATInternet.Tracker.instances.length; i++) {
ATInternet.Tracker.instances[i].plugins.unload(name);
}
}
};
/**
* Reference all callbacks loaded.
* @name callbackProtos
* @memberof ATInternet.Tracker
* @type {object}
* @public
*/
ATInternet.Tracker.callbackProtos = {};