/**
* @class
* @classdesc Plugin to manage visitor consent
* @name Privacy
* @memberof ATInternet.Tracker.Plugins
* @type {function}
* @param tag {Object} Instance of the Tag used
* @public
*/
ATInternet.Tracker.Plugins.Privacy = function (tag) {
'use strict';
/**
* Object used to manage authority
* @name Authority
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @inner
* @constructor
* @param config {Object}
* @property {function} setVisitorOptout Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.setVisitorOptout}
* @property {function} setVisitorOptin Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.setVisitorOptin}
* @property {function} setVisitorRandomID Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.setVisitorRandomID}
* @property {function} setVisitorMode Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.setVisitorMode}
* @property {function} getAuthority Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.getAuthority}
* @property {function} getVisitorMode Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.getVisitorMode}
* @property {function} addAuthority Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.addAuthority}
* @property {function} extendIncludeStorage Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendIncludeStorage}
* @property {function} extendIncludeBuffer Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendIncludeBuffer}
* @property {function} updateStorageDuration Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.updateStorageDuration}
* @private
*/
var Authority = function (config) {
/* -------- Authority properties -------- */
var _thisAuthority = this;
var _authority = null;
var _visitorMode = null;
var _privacyParameters = {
storageParams: [],
bufferParams: []
};
var _config = config;
var DEFAULT = 'default';
var CNIL = 'cnil';
var EXEMPT = 'exempt';
var OPTIN = 'optin';
var OPTOUT = 'optout';
var RANDOM = 'random';
/* -------- Authority internal methods -------- */
/**
* Initialize Authority context
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _initAuthorityContext() {
var authorityName = tag.storage.get([_config.authorityStorageName, 'authority_name'], true);
var visitorMode = tag.storage.get([_config.authorityStorageName, 'visitor_mode'], true);
if (authorityName && visitorMode) {
if (authorityName === DEFAULT) {
if (visitorMode === OPTIN) {
_thisAuthority.setVisitorOptin();
} else if (visitorMode === OPTOUT) {
_thisAuthority.setVisitorOptout();
} else if (visitorMode === RANDOM) {
_thisAuthority.setVisitorRandomID();
} else {
_thisAuthority.setVisitorMode(authorityName, visitorMode);
}
} else {
_thisAuthority.setVisitorMode(authorityName, visitorMode);
}
}
}
/**
* Set Authority storage
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _setAuthorityStorage() {
if (_authority && _visitorMode) {
var expirationDate = new Date();
expirationDate.setTime(expirationDate.getTime() + (_visitorMode.storageDuration * 24 * 60 * 60 * 1000));
var authorityStorageValue = {
authority_name: _authority.name,
visitor_mode: _visitorMode.name
};
tag.storage.set(_config.authorityStorageName, authorityStorageValue, {
end: expirationDate,
path: '/'
});
}
}
/**
* Process Authority storage
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @param updateStorageDuration {boolean} Update storage duration
* @private
*/
function _processAuthorityStorage(updateStorageDuration) {
if (_visitorMode) {
if (_visitorMode.storageDuration > 0) {
if (updateStorageDuration) {
_setAuthorityStorage();
} else {
var authorityStorage = tag.storage.get(_config.authorityStorageName, true);
if (authorityStorage === null) {
_setAuthorityStorage();
} else if (typeof authorityStorage === 'object') {
if ((authorityStorage.authority_name !== _authority.name) || (authorityStorage.visitor_mode !== _visitorMode.name)) {
_setAuthorityStorage();
}
}
}
} else {
tag.storage.del(_config.authorityStorageName);
}
}
}
/**
* Process Tracker settings
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _processTrackerSettings() {
if (_visitorMode) {
for (var keyConfig in _visitorMode.trackerSettings) {
if (_visitorMode.trackerSettings.hasOwnProperty(keyConfig)) {
tag.setConfig(keyConfig, _visitorMode.trackerSettings[keyConfig]);
}
}
}
}
/**
* Process specific buffer parameters to add (¶m=value)
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _processParamsToAdd() {
if (_visitorMode && _visitorMode.add) {
for (var param in _visitorMode.add.buffer) {
if (_visitorMode.add.buffer.hasOwnProperty(param)) {
tag.setParam(_visitorMode.add.buffer[param].param, _visitorMode.add.buffer[param].value, {
multihit: true,
permanent: true,
hitType: ['all']
});
}
}
}
}
/**
* Process the general parameters to be considered
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _processParamsToInclude() {
if (_visitorMode && _visitorMode.include) {
var val;
for (var key in _visitorMode.include) {
if (_visitorMode.include.hasOwnProperty(key)) {
if (_visitorMode.include[key] instanceof Array) {
val = _visitorMode.include[key];
} else {
val = [_visitorMode.include[key]];
}
_privacyParameters[key + 'Params'] = val;
}
}
ATInternet.Utils.privacy.setParameters(_privacyParameters);
_processAuthorityStorage(false);
}
}
/**
* Clean storage parameters depending on general privacy
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _cleanStorageParams() {
var delCallback = function (param) {
tag.storage.del(param);
tag.storage.delPrivate(param);
};
var getCallback = function (param) {
return (tag.storage.get(param, true) || tag.storage.getPrivate(param, true));
};
var getAllCallback = function (param) {
return (tag.storage.getAll(param, true) || tag.storage.getAllPrivate(param, true));
};
ATInternet.Utils.privacy.processStorageParams(delCallback, getCallback, getAllCallback);
}
/**
* Clean buffer parameters depending on general privacy
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _cleanBufferParams() {
var delCallback = function (param) {
tag.delParam(param);
};
var getAllCallback = function () {
return tag.getParams([]);
};
var setCallback = function (param, value, options) {
tag.setParam(param, value, options);
};
ATInternet.Utils.privacy.processBufferParams(delCallback, getAllCallback, setCallback);
}
/**
* Merge parameters values
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param paramsArray {array} Parameter origin array
* @function
* @return {array}
* @private
*/
function _mergeParamsValues(paramsArray) {
var _newParamsArray = [];
var _newParamsObject = {};
for (var i = 0; i < paramsArray.length; i++) {
if (typeof paramsArray[i] === 'object') {
for (var property in paramsArray[i]) {
if (paramsArray[i].hasOwnProperty(property)) {
_newParamsObject[property] = (_newParamsObject[property] || []).concat(paramsArray[i][property]);
}
}
} else {
_newParamsArray.push(paramsArray[i]);
}
}
var obj;
for (var key in _newParamsObject) {
if (_newParamsObject.hasOwnProperty(key)) {
obj = {};
obj[key] = _newParamsObject[key];
_newParamsArray.push(obj);
}
}
return _newParamsArray;
}
/**
* Include parameter(s) to current visitor mode
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @param param {Object|string|array} Parameter(s) to add
* @param key {string} Name of the object concerned (storage, buffer)
* @param clean {function} Function to execute after processing
* @private
*/
function _extendInclude(param, key, clean) {
if (_visitorMode && _visitorMode.include) {
if (_visitorMode.include[key] instanceof Array) {
if (param instanceof Array) {
_visitorMode.include[key] = _visitorMode.include[key].concat(param);
} else if (param) {
_visitorMode.include[key].push(param);
}
} else if (_visitorMode.include[key] !== ATInternet.Utils.privacy.ALL) {
if (param instanceof Array) {
_visitorMode.include[key] = param;
} else if (param) {
_visitorMode.include[key] = [param];
}
}
_privacyParameters[key + 'Params'] = _mergeParamsValues(_visitorMode.include[key]);
ATInternet.Utils.privacy.setParameters(_privacyParameters);
clean && clean();
}
}
/**
* Add one or more parameters to be included from the current visitor mode (for all lists)
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _extendIncludeListsFromConfig() {
if (_config.parametersToInclude.length > 0) {
_thisAuthority.extendIncludeStorage(_config.parametersToInclude);
_thisAuthority.extendIncludeBuffer(_config.parametersToInclude);
}
}
/**
* Store current user ID
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _storeUserId() {
tag.clientSideUserId.store();
}
/**
* Clear current user ID
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
function _clearUserId() {
tag.clientSideUserId.clear();
}
/**
* Process the user ID according to the context
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param authority {string} Authority name
* @param visitorMode {string} Visitor mode
* @param resetUserID {boolean} Reset current user ID
* @function
* @return {boolean}
* @private
*/
function _processUserID(authority, visitorMode, resetUserID) {
var reset = true;
if (authority === CNIL && visitorMode === EXEMPT) {
reset = false;
} else if (typeof resetUserID === 'boolean') {
reset = resetUserID;
}
if (reset && _authority && _visitorMode) {
reset = (authority !== _authority.name) || (visitorMode !== _visitorMode.name);
}
return reset;
}
/**
* Process new visitor mode
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param authority {string} Authority name
* @param visitorMode {string} Visitor mode
* @param clearUserId {boolean} Clear current userid
* @param resetCurrentMode {boolean} Reset current context before applying new one
* @function
* @private
*/
function _processNewVisitorMode(authority, visitorMode, clearUserId, resetCurrentMode) {
if (_config.authorities && _config.authorities[authority] && _config.authorities[authority][visitorMode]) {
clearUserId && _clearUserId();
resetCurrentMode && _thisAuthority.setVisitorOptin();
_authority = _config.authorities[authority];
_visitorMode = _authority[visitorMode];
_processTrackerSettings();
_processParamsToAdd();
_processParamsToInclude();
_extendIncludeListsFromConfig();
_cleanStorageParams();
_cleanBufferParams();
}
}
/* -------- Authority helpers -------- */
/**
* Set OPTOUT mode
* @alias Authority.setVisitorOptout
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @public
* @example
* tag.privacy.setVisitorOptout();
*/
_thisAuthority.setVisitorOptout = function () {
ATInternet.Utils.consentReceived(true);
_processNewVisitorMode(DEFAULT, OPTOUT, false, false);
ATInternet.Utils.userOptedOut();
};
/**
* Set OPTIN mode
* @alias Authority.setVisitorOptin
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @public
* @example
* tag.privacy.setVisitorOptin();
*/
_thisAuthority.setVisitorOptin = function () {
ATInternet.Utils.consentReceived(true);
_storeUserId();
_processNewVisitorMode(DEFAULT, OPTIN, false, false);
ATInternet.Utils.userOptedIn();
};
/**
* Set random mode
* @alias Authority.setVisitorRandomID
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @public
* @example
* tag.privacy.setVisitorRandomID();
*/
_thisAuthority.setVisitorRandomID = function () {
ATInternet.Utils.consentReceived(false);
_processNewVisitorMode(DEFAULT, RANDOM, false, false);
ATInternet.Utils.userOptedIn();
};
/**
* Set visitor mode
* @alias Authority.setVisitorMode
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param authority {string} Authority name
* @param visitorMode {string} Visitor mode
* @param [resetUserID] {boolean} Reset current user ID (optional)
* @function
* @public
* @example
* tag.privacy.setVisitorMode('default', 'no-consent');
*/
_thisAuthority.setVisitorMode = function (authority, visitorMode, resetUserID) {
var clearUserId = _processUserID(authority, visitorMode, resetUserID);
_processNewVisitorMode(authority, visitorMode, clearUserId, true);
};
/**
* Get current authority
* @alias Authority.getAuthority
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @return {Object|null}
* @public
* @example
* var authority = tag.privacy.getAuthority();
*/
_thisAuthority.getAuthority = function () {
return _authority;
};
/**
* Get current visitor mode
* @alias Authority.getVisitorMode
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @return {Object|null}
* @public
* @example
* var visitorMode = tag.privacy.getVisitorMode();
*/
_thisAuthority.getVisitorMode = function () {
return _visitorMode;
};
/**
* Add new authority
* @alias Authority.addAuthority
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param authority {Object} Authority object
* @function
* @public
* @example
* tag.privacy.addAuthority(customAuthority);
*/
_thisAuthority.addAuthority = function (authority) {
if (authority && typeof authority === 'object') {
_config.authorities = _config.authorities || {};
_config.authorities[authority.name] = authority;
}
};
/**
* Include storage parameter(s) to current visitor mode
* @alias Authority.extendIncludeStorage
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param storageParam {Object|string|array} Storage properties
* @function
* @public
* @example
* tag.privacy.extendIncludeStorage('atidvistor'); // entire atidvistor storage
* tag.privacy.extendIncludeStorage({'atidvistor': ['an', 'ac']}); // only 'an' and 'ac' from atidvistor
* tag.privacy.extendIncludeStorage([{'atidvistor': ['an', 'ac']}, {'atredir'}]); // 'an' and 'ac' from atidvistor and entire atredir
* tag.privacy.extendIncludeStorage([{'atidvistor': ['an', 'ac'], 'atredir': ['an', 'ac']}]); // 'an' and 'ac' from atidvistor and atredir
*/
_thisAuthority.extendIncludeStorage = function (storageParam) {
_extendInclude(storageParam, 'storage', _cleanStorageParams);
};
/**
* Include buffer parameter(s) to current visitor mode
* @alias Authority.extendIncludeBuffer
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param bufferParam {string|array|Object} Buffer parameters
* @function
* @public
* @example
* tag.privacy.extendIncludeBuffer('an'); // buffer parameter 'an'
* tag.privacy.extendIncludeBuffer(['an', 'ac']); // buffer parameters 'an' and 'ac'
* tag.privacy.extendIncludeBuffer({stc: ['custom1', 'custom2']}); // buffer parameter 'stc', keys 'custom1' and 'custom2'
*/
_thisAuthority.extendIncludeBuffer = function (bufferParam) {
_extendInclude(bufferParam, 'buffer', _cleanBufferParams);
};
/**
* Update storage duration for authority context
* @alias Authority.updateStorageDuration
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @param storageDuration {number} Storage duration in days
* @function
* @public
* @example
* tag.privacy.updateStorageDuration(90);
*/
_thisAuthority.updateStorageDuration = function (storageDuration) {
if (_visitorMode) {
_visitorMode.storageDuration = storageDuration;
_processAuthorityStorage(true);
}
};
/* -------- Authority init -------- */
_initAuthorityContext();
// For unit tests on private elements !!!
/* @if test */
_thisAuthority._authority = _authority;
_thisAuthority._visitorMode = _visitorMode;
_thisAuthority._privacyParameters = _privacyParameters;
_thisAuthority._initAuthorityContext = _initAuthorityContext;
_thisAuthority._setAuthorityStorage = _setAuthorityStorage;
_thisAuthority._processAuthorityStorage = _processAuthorityStorage;
_thisAuthority._processTrackerSettings = _processTrackerSettings;
_thisAuthority._processParamsToAdd = _processParamsToAdd;
_thisAuthority._processParamsToInclude = _processParamsToInclude;
_thisAuthority._cleanStorageParams = _cleanStorageParams;
_thisAuthority._cleanBufferParams = _cleanBufferParams;
_thisAuthority._mergeParamsValues = _mergeParamsValues;
_thisAuthority._extendInclude = _extendInclude;
_thisAuthority._extendIncludeListsFromConfig = _extendIncludeListsFromConfig;
_thisAuthority._processNewVisitorMode = _processNewVisitorMode;
_thisAuthority._storeUserId = _storeUserId;
_thisAuthority._clearUserId = _clearUserId;
_thisAuthority._processUserID = _processUserID;
/* @endif */
};
var _authorityObject = null;
/* -------- Tag Authority helpers -------- */
/**
* [Object added by plugin {@link ATInternet.Tracker.Plugins.Privacy Privacy}] Tags to manage visitor mode context
* @name privacy
* @inner
* @type {Object}
* @memberof! ATInternet.Tracker.Tag
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.setVisitorOptout}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.setVisitorOptin}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.setVisitorRandomID}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.setVisitorMode}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.getAuthority}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.getVisitorMode}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.addAuthority}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.extendIncludeStorage}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.extendIncludeBuffer}
* @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.updateStorageDuration}
* @public
*/
tag.privacy = {
setVisitorOptout: function () {
},
setVisitorOptin: function () {
},
setVisitorRandomID: function () {
},
setVisitorMode: function () {
},
getAuthority: function () {
},
getVisitorMode: function () {
},
addAuthority: function () {
},
extendIncludeStorage: function () {
},
extendIncludeBuffer: function () {
},
updateStorageDuration: function () {
}
};
/* -------- Plugin init -------- */
/**
* Launch plugin when all dependencies are loaded.
* @memberof ATInternet.Tracker.Plugins.Privacy#
* @function
* @private
*/
var _init = function () {
var dependencies = ['Storage', 'Utils', 'ClientSideUserId'];
tag.plugins.waitForDependencies(dependencies, function () {
// Plugin configuration.
var config = null;
tag.configPlugin('Privacy', dfltPluginCfg || {}, function (newConf) {
config = ATInternet.Utils.cloneSimpleObject(newConf);
});
if (config !== null) {
_authorityObject = new Authority(config);
tag.privacy.setVisitorOptout = _authorityObject.setVisitorOptout;
tag.privacy.setVisitorOptin = _authorityObject.setVisitorOptin;
tag.privacy.setVisitorRandomID = _authorityObject.setVisitorRandomID;
tag.privacy.setVisitorMode = _authorityObject.setVisitorMode;
tag.privacy.getAuthority = _authorityObject.getAuthority;
tag.privacy.getVisitorMode = _authorityObject.getVisitorMode;
tag.privacy.addAuthority = _authorityObject.addAuthority;
tag.privacy.extendIncludeStorage = _authorityObject.extendIncludeStorage;
tag.privacy.extendIncludeBuffer = _authorityObject.extendIncludeBuffer;
tag.privacy.updateStorageDuration = _authorityObject.updateStorageDuration;
}
});
};
_init();
// For unit tests on private elements !!!
/* @if test */
this._authorityObject = _authorityObject;
this._init = _init;
/* @endif */
};
ATInternet.Tracker.addPlugin('Privacy');