Source: Privacy/privacy.js

/**
 * @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 (&param=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');