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} extendRemoveStorage Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendRemoveStorage}
     * @property {function} extendRemoveBuffer Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendRemoveBuffer}
     * @property {function} extendRemoveContext Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendRemoveContext}
     * @property {function} extendRemoveProperties Authority helper, see details here {@link ATInternet.Tracker.Tag#Authority.extendRemoveProperties}
     * @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: [],
            contextParams: [],
            propertiesParams: []
        };
        var _config = config;
        var DEFAULT = 'default';
        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 buffer parameters to add
         * @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 general privacy parameters
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _processParamsToRemove() {
            if (_visitorMode && _visitorMode.remove) {
                var val;
                for (var key in _visitorMode.remove) {
                    if (_visitorMode.remove.hasOwnProperty(key)) {
                        val = [];
                        if (_visitorMode.remove[key] instanceof Array) {
                            val = _visitorMode.remove[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));
            };
            ATInternet.Utils.privacy.processStorageParams(delCallback, getCallback);
        }

        /**
         * Clean buffer parameters depending on general privacy
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _cleanBufferParams() {
            var getCallback = function (param) {
                return tag.getParam(param);
            };
            var delCallback = function (param) {
                tag.delParam(param);
            };
            var setCallback = function (param, value, options) {
                tag.setParam(param, value, options);
            };
            ATInternet.Utils.privacy.processBufferParams(getCallback, delCallback, setCallback);
        }

        /**
         * Clean context parameters depending on general privacy
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _cleanContextParams() {
            var getCallback = function (context) {
                return tag.getContext(context);
            };
            var delCallback = function (context, param) {
                tag.delContext(context, param);
            };
            var setCallback = function (context, param) {
                tag.setContext(context, param);
            };
            ATInternet.Utils.privacy.processContextParams(getCallback, delCallback, setCallback);
        }

        /**
         * Clean properties parameters depending on general privacy
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _cleanPropertiesParams() {
            var delCallback = function (param) {
                tag.delProp(param);
            };
            ATInternet.Utils.privacy.processPropertiesParams(delCallback);
        }

        /**
         * Add parameter(s) to exclude 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, context, properties)
         * @param clean {function} Function to execute after processing
         * @private
         */
        function _extendRemove(param, key, clean) {
            if (_visitorMode && _visitorMode.remove) {
                if (_visitorMode.remove[key] instanceof Array) {
                    if (param instanceof Array) {
                        _visitorMode.remove[key] = _visitorMode.remove[key].concat(param);
                    } else if (param) {
                        _visitorMode.remove[key].push(param);
                    }
                } else {
                    if (param instanceof Array) {
                        _visitorMode.remove[key] = param;
                    } else if (param) {
                        _visitorMode.remove[key] = [param];
                    }
                }
                _privacyParameters[key + 'Params'] = _visitorMode.remove[key];
                ATInternet.Utils.privacy.setParameters(_privacyParameters);
                clean && clean();
            }
        }

        /**
         * Tests if the current visitor mode is a consent mode
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _isConsentVisitorMode() {
            var _consentMode = false;
            if (_visitorMode && _visitorMode.add && _visitorMode.add.buffer && _visitorMode.add.buffer.visitorConsent) {
                _consentMode = !!_visitorMode.add.buffer.visitorConsent.value;
            }
            return _consentMode;
        }

        /**
         * Add one or more parameters to be excluded from the current visitor mode (for all lists)
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _extendRemoveListsFromConfig() {
            if (!_isConsentVisitorMode() && _config.parametersToExclude.length > 0) {
                _thisAuthority.extendRemoveStorage(_config.parametersToExclude);
                _thisAuthority.extendRemoveBuffer(_config.parametersToExclude);
                _thisAuthority.extendRemoveContext(_config.parametersToExclude);
                _thisAuthority.extendRemoveProperties(_config.parametersToExclude);
            }
        }

        /**
         * Store current userid
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _storeUserId() {
            tag.clientSideUserId.store();
        }

        /**
         * Clear current userid
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @function
         * @private
         */
        function _clearUserId() {
            tag.clientSideUserId.clear();
        }

        /**
         * 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();
                _processParamsToRemove();

                _cleanStorageParams();
                _cleanBufferParams();
                _cleanContextParams();
                _cleanPropertiesParams();

                _extendRemoveListsFromConfig();
            }
        }

        /* -------- 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 consent received  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
         * @function
         * @public
         * @example
         * tag.privacy.setVisitorMode('default', 'no-consent');
         */
        _thisAuthority.setVisitorMode = function (authority, visitorMode) {
            var clearUserId = true;
            if (_authority && _visitorMode) {
                clearUserId = (authority !== _authority.name) || (visitorMode !== _visitorMode.name);
            }
            _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;
            }
        };

        /**
         * Add storage parameter(s) to exclude to current visitor mode
         * @alias Authority.extendRemoveStorage
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @param storageParam {Object|string|array} Storage properties
         * @function
         * @public
         * @example
         * tag.privacy.extendRemoveStorage('atidvistor'); // entire atidvistor storage
         * tag.privacy.extendRemoveStorage({'atidvistor': ['an', 'ac']}); // only 'an' and 'ac' from atidvistor
         * tag.privacy.extendRemoveStorage([{'atidvistor': ['an', 'ac']}, {'atredir'}]); // 'an' and 'ac' from atidvistor and entire atredir
         * tag.privacy.extendRemoveStorage([{'atidvistor': ['an', 'ac'], 'atredir': ['an', 'ac']}]); // 'an' and 'ac' from atidvistor and atredir
         */
        _thisAuthority.extendRemoveStorage = function (storageParam) {
            _extendRemove(storageParam, 'storage', _cleanStorageParams);
        };

        /**
         * Add buffer parameter(s) to exclude to current visitor mode
         * @alias Authority.extendRemoveBuffer
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @param bufferParam {string|array|Object} Buffer parameters
         * @function
         * @public
         * @example
         * tag.privacy.extendRemoveBuffer('an'); // buffer parameter 'an'
         * tag.privacy.extendRemoveBuffer(['an', 'ac']); // buffer parameters 'an' and 'ac'
         * tag.privacy.extendRemoveBuffer({stc: ['custom1', 'custom2']}); // buffer parameter 'stc', keys 'custom1' and 'custom2'
         */
        _thisAuthority.extendRemoveBuffer = function (bufferParam) {
            _extendRemove(bufferParam, 'buffer', _cleanBufferParams);
        };

        /**
         * Add context parameter(s) to exclude to current visitor mode
         * @alias Authority.extendRemoveContext
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @param contextParam {string|array|Object} Context parameters
         * @function
         * @public
         * @example
         * tag.privacy.extendRemoveContext('campaigns'); // 'campaigns' context
         * tag.privacy.extendRemoveContext(['campaigns', 'page']); // 'campaigns' and page contexts
         * tag.privacy.extendRemoveContext({page: 'vrn'}); // vrn parameter in page context
         * tag.privacy.extendRemoveContext({page: ['vrn', 'weborama']}); // vrn and weborama parameters in page context
         */
        _thisAuthority.extendRemoveContext = function (contextParam) {
            _extendRemove(contextParam, 'context', _cleanContextParams);
        };

        /**
         * Add properties parameter(s) to exclude to current visitor mode
         * @alias Authority.extendRemoveProperties
         * @memberof ATInternet.Tracker.Plugins.Privacy#
         * @param propertiesParam {string|array} Properties parameters
         * @function
         * @public
         * @example
         * tag.privacy.extendRemoveProperties('custom'); // 'custom' property
         * tag.privacy.extendRemoveProperties(['custom1', 'custom2']); // 'custom1' and custom2 properties
         */
        _thisAuthority.extendRemoveProperties = function (propertiesParam) {
            _extendRemove(propertiesParam, 'properties', _cleanPropertiesParams);
        };

        /**
         * 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._processParamsToRemove = _processParamsToRemove;
        _thisAuthority._cleanStorageParams = _cleanStorageParams;
        _thisAuthority._cleanBufferParams = _cleanBufferParams;
        _thisAuthority._cleanContextParams = _cleanContextParams;
        _thisAuthority._cleanPropertiesParams = _cleanPropertiesParams;
        _thisAuthority._extendRemove = _extendRemove;
        _thisAuthority._isConsentVisitorMode = _isConsentVisitorMode;
        _thisAuthority._extendRemoveListsFromConfig = _extendRemoveListsFromConfig;
        _thisAuthority._processNewVisitorMode = _processNewVisitorMode;
        _thisAuthority._storeUserId = _storeUserId;
        _thisAuthority._clearUserId = _clearUserId;
        /* @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.extendRemoveStorage}
     * @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.extendRemoveBuffer}
     * @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.extendRemoveContext}
     * @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#privacy.extendRemoveProperties}
     * @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 () {
        },
        extendRemoveStorage: function () {
        },
        extendRemoveBuffer: function () {
        },
        extendRemoveContext: function () {
        },
        extendRemoveProperties: 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.extendRemoveStorage = _authorityObject.extendRemoveStorage;
                tag.privacy.extendRemoveBuffer = _authorityObject.extendRemoveBuffer;
                tag.privacy.extendRemoveContext = _authorityObject.extendRemoveContext;
                tag.privacy.extendRemoveProperties = _authorityObject.extendRemoveProperties;
                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');