Source: RichMedia/richmedia.js

/**
 * @class
 * @classdesc Plugin to measure media.
 * @name RichMedia
 * @memberof ATInternet.Tracker.Plugins
 * @type {function}
 * @param parent {object} Instance of the Tag used
 * @public
 */
window['ATInternet']['Tracker']['Plugins']['RichMedia'] = function (parent) {

    'use strict';

    var self = this;

    var _debug = {
        level: 'DEBUG',
        messageEnd: 'method ended'
    };

    /**
     * Manage refresh values.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param val {string} Refresh value to process
     * @param min {number} Minimum value to set
     * @return {number}
     * @private
     */
    var _filterValue = function (val, min) {
        var _val = parseInt(val, 10);
        if (!!_val)
            return Math.max(_val, min);
        return 0;
    };

    /**
     * Manage local Media Object.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @private
     */
    var Media = function () {
        this.media = function () {
            this.type = undefined;
            this.plyr = 0;
            this.s2 = undefined;
            this.clnk = undefined;
            this.p = '';
            this.a = undefined;
            this.buf = undefined;
            this.rfsh = undefined;
            this.m1 = undefined;
            this.m5 = undefined;
            this.m6 = undefined;
            this.m9 = undefined;
        };
        this.mediaAll = {};
        this.setMediaValue = function (playerId, mediaLabel, param, value) {
            if (typeof value !== 'undefined') {
                this.mediaAll[playerId] = this.mediaAll[playerId] || {};
                this.mediaAll[playerId][mediaLabel] = this.mediaAll[playerId][mediaLabel] || new this.media();
                this.mediaAll[playerId][mediaLabel][param] = value;
            }
        };
        this.getMediaValue = function (playerId, mediaLabel, param) {
            if (this.mediaAll[playerId] && this.mediaAll[playerId][mediaLabel]) {
                return (this.mediaAll[playerId][mediaLabel][param]);
            }
            return undefined;
        };
        this.removePlayer = function (playerId) {
            this.mediaAll[playerId] = {};
        };
        this.removeAll = function () {
            this.mediaAll = {};
        };
    };

    // Local object to manage all media.
    var mediaObject = new Media();

    /**
     * Manage Timeout Object.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @private
     */
    var Timeout = function () {
        this.timeout = {};
        this.setTimeout = function (playerId, mediaLabel, refreshValue) {
            this.timeout[playerId] = this.timeout[playerId] || {};
            if (this.timeout[playerId][mediaLabel]) {
                window.clearTimeout(this.timeout[playerId][mediaLabel]);
            }
            this.timeout[playerId][mediaLabel] = window.setTimeout(function () {
                parent.richMedia.send({action: 'refresh', playerId: playerId, mediaLabel: mediaLabel});
            }, refreshValue * 1e3);
            // Example of generated object
            /* this.timeout[playerId][mediaLabel] = window.setTimeout(function () {
                parent.richMedia.send({action: 'refresh', playerId: plyr, mediaLabel: key});
                }, 5 * 1e3); */
        };
        this.setTimeoutObject = function (playerId, mediaLabel, refreshObject) {
            this.timeout[playerId] = this.timeout[playerId] || {};
            if (typeof this.timeout[playerId][mediaLabel] === 'undefined') {
                var refreshTab = [];
                for (var val in refreshObject) {
                    if (refreshObject.hasOwnProperty(val)) {
                        refreshTab.push({
                            delay: _filterValue(val, 0),
                            refresh: _filterValue(refreshObject[val], 5)
                        });
                    }
                }
                refreshTab.sort(function (a, b) {
                    if (a.delay < b.delay) {
                        return -1;
                    }
                    if (a.delay > b.delay) {
                        return 1;
                    }
                    return 0;
                });
                this.timeout[playerId][mediaLabel] = {
                    refreshTab: refreshTab,
                    backupRefreshTab: ATInternet.Utils.cloneSimpleObject(refreshTab),
                    delayConfiguration: {}
                };
            }

            // Process refresh objects
            var timeout = this.timeout[playerId][mediaLabel];
            if (timeout.refreshTab.length > 0) {

                var delay = timeout.refreshTab[0].delay;
                var refresh = timeout.refreshTab[0].refresh;

                if ((typeof delay === 'number') && (typeof refresh === 'number') && (refresh > 0)) {

                    timeout.delayConfiguration[delay] = timeout.delayConfiguration[delay] || {};

                    // Get next delay
                    var nextDelay = undefined;
                    if (typeof timeout.refreshTab[1] !== 'undefined') {
                        nextDelay = timeout.refreshTab[1].delay;
                    }

                    //Get the number of remaining sendings for the current delay
                    var number = 0;
                    if (typeof nextDelay === 'undefined') {
                        number = 1;
                    }
                    else if (typeof timeout.delayConfiguration[delay].number === 'number') {
                        var action = mediaObject.getMediaValue(playerId, mediaLabel, 'a');
                        if (action === 'refresh') {
                            number = Math.max(timeout.delayConfiguration[delay].number - 1, 0);
                        }
                        else {
                            number = timeout.delayConfiguration[delay].number;
                        }
                    }
                    else if (typeof nextDelay === 'number') {
                        number = Math.floor((nextDelay - delay) * 60 / refresh) - 1;
                    }
                    timeout.delayConfiguration[delay].number = number;

                    //Send refresh hits and manage window timeout
                    if (timeout.delayConfiguration[delay].timeout) {
                        window.clearTimeout(timeout.delayConfiguration[delay].timeout);
                    }
                    if (number > 0) {
                        timeout.delayConfiguration[delay].timeout = window.setTimeout(function () {
                            parent.richMedia.send({action: 'refresh', playerId: playerId, mediaLabel: mediaLabel});
                        }, refresh * 1e3);
                    }
                    else {
                        timeout.delayConfiguration[delay].number = undefined;
                        timeout.delayConfiguration[delay].timeout = undefined;
                        timeout.refreshTab.splice(0, 1);
                        window.setTimeout(function () {
                            parent.richMedia.send({action: 'refresh', playerId: playerId, mediaLabel: mediaLabel});
                        }, refresh * 1e3);
                    }
                    this.timeout[playerId][mediaLabel] = timeout;
                    // Example of final generated object
                    /* this.timeout[playerId][mediaLabel] = {
                        refreshTab: [
                            {delay: 0, refresh: 5},
                            {delay: 1, refresh: 15},
                            {delay: 5, refresh: 60},
                        ],
                        backupRefreshTab: [
                            {delay: 0, refresh: 5},
                            {delay: 1, refresh: 15},
                            {delay: 5, refresh: 60},
                        ],
                        delayConfiguration: {
                            0: {
                                number: 11,
                                timeout: window.setTimeout(function () {
                                    parent.richMedia.send({action: 'refresh', playerId: plyr, mediaLabel: key});
                                }, 5 * 1e3)
                            }
                        }
                    }; */
                }
            }
        };
        this.clearTimeout = function (playerId, mediaLabel, resetDelay) {
            this.timeout[playerId] = this.timeout[playerId] || {};
            var timeout = this.timeout[playerId][mediaLabel];
            if (typeof timeout === 'object') {
                if (typeof timeout.delayConfiguration === 'object') {
                    var number;
                    for (var delay in timeout.delayConfiguration) {
                        if (timeout.delayConfiguration.hasOwnProperty(delay)) {
                            number = timeout.delayConfiguration[delay].number;
                            if (typeof number !== 'undefined' && number > 0) {
                                if (timeout.delayConfiguration[delay].timeout) {
                                    window.clearTimeout(timeout.delayConfiguration[delay].timeout);
                                }
                                timeout.delayConfiguration[delay].timeout = undefined;
                            }
                        }
                    }
                    if (resetDelay) {
                        timeout.refreshTab = ATInternet.Utils.cloneSimpleObject(timeout.backupRefreshTab);
                    }
                    this.timeout[playerId][mediaLabel] = timeout;
                }
            }
            else if (timeout) {
                window.clearTimeout(timeout);
            }
        };
        this.removePlayer = function (playerId) {
            for (var mediaLabel in this.timeout[playerId]) {
                if (this.timeout[playerId].hasOwnProperty(mediaLabel)) {
                    this.clearTimeout(playerId, mediaLabel, false);
                    var action = mediaObject.getMediaValue(playerId, mediaLabel, 'a');
                    if (typeof this.timeout[playerId][mediaLabel] !== 'undefined' && action !== 'stop') {
                        parent.richMedia.send({action: "stop", playerId: playerId, mediaLabel: mediaLabel});
                    }
                }
            }
            this.timeout[playerId] = {};
        };
        this.removeAll = function () {
            for (var playerId in this.timeout) {
                if (this.timeout.hasOwnProperty(playerId)) {
                    this.removePlayer(playerId);
                }
            }
            this.timeout = {};
        };
    };

    // Local object to manage timers.
    var timeoutObject = new Timeout();

    /**
     * Get "prich" label with chapters from page context.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param tagObject {object} object containing properties to use
     * @param type {string} property to use for chapters/mediathemes
     * @param label {string} property to use for page/media label
     * @return {string}
     * @private
     */
    var _getFullName = function (tagObject, type, label) {
        return parent.utils.manageChapters(tagObject, type, 3) + (tagObject[label] ? tagObject[label] : '');
    };

    /**
     * Set default hit values for boolean parameters (isBuffering & isEmbedded).
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param tagObject {object} Madia object containing properties to use
     * @param key {string} Name of the tag property to test
     * @param defaultValue {string} Default value to set
     * @param value {string} Value to set when property is true
     * @return {string}
     * @private
     */
    var _setBoolean = function (tagObject, key, defaultValue, value) {
        var val = tagObject[key];
        if (typeof tagObject[key] === 'boolean') {
            if (tagObject[key]) {
                val = value;
            }
            else {
                val = defaultValue;
            }
        }
        else if (tagObject[key]) {
            /* @if debug */
            parent.debug('RichMedia:richmedia:' + key + ':Error', 'ERROR', 'Not a boolean', {'value': tagObject[key]});
            /* @endif */
        }
        return val;
    };

    /**
     * Filter player ID value.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param value {string} Value of "playerId" property from tag
     * @return {number}
     * @private
     */
    var _filterPlyrId = function (value) {
        var val = 0;
        if (/^(\-|\+)?([0-9]+)$/.test(value)) {
            val = Number(value);
        }
        else if (value) {
            /* @if debug */
            parent.debug('RichMedia:richMedia:playerId:Error', 'ERROR', 'Not an integer', {value: value});
            /* @endif */
        }
        return val;
    };

    /**
     * Set buffer value with media data.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param buffer {object} object containing properties to use
     * @param playerId {number} Player ID
     * @param mediaLabel {string} media label
     * @param param {string} Name of the parameter
     * @param encode {boolean} Encode value if true
     * @private
     */
    var _setBufferParamFromMediaObject = function (buffer, playerId, mediaLabel, param, encode) {
        var value = mediaObject.getMediaValue(playerId, mediaLabel, param);
        if (typeof value !== 'undefined') {
            buffer[param] = encode ? encodeURIComponent(value) : value;
        }
    };

    /**
     * Set buffer value with tag data.
     * @memberof ATInternet.Tracker.Plugins.RichMedia#
     * @function
     * @param buffer {object} object containing properties to use
     * @param param {string} Name of the parameter
     * @param value {string|number} Tag value
     * @private
     */
    var _setBufferParamFromTagObject = function (buffer, param, value) {
        if (typeof value !== 'undefined') {
            buffer[param] = value;
        }
    };

    /**
     * [Object added by plugin {@link ATInternet.Tracker.Plugins.RichMedia RichMedia}] Tags to manage media measurement.
     * @name richMedia
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @type {object}
     * @property {function} add Tag helper, see details here {@link ATInternet.Tracker.Tag#richMedia.add}
     * @property {function} remove Tag helper, see details here {@link ATInternet.Tracker.Tag#richMedia.remove}
     * @property {function} removeAll Tag helper, see details here {@link ATInternet.Tracker.Tag#richMedia.removeAll}
     * @property {function} send Tag helper, see details here {@link ATInternet.Tracker.Tag#richMedia.send}
     * @public
     */
    parent.richMedia = {};

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.RichMedia RichMedia}] Add media object.
     * @alias richMedia.add
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @param tagObject {object} 10 properties : mediaType, playerId, mediaLevel2, mediaLabel,
     * linkedContent, refreshDuration, duration, isEmbedded, broadcastMode, webdomain
     * @example
     * <pre><code class="javascript">tag.richMedia.add({
     *      mediaType: "video",
     *      playerId: 333,
     *      mediaLevel2: "mediaLevel2",
     *      mediaLabel: "mediaLabel",
     *      linkedContent: "linkedContent",
     *      refreshDuration: 5,
     *      duration: 20,
     *      isEmbedded: true,
     *      broadcastMode: "clip",
     *      webdomain: "http://www.atinternet.com"
     *  });
     * </code></pre>
     * @public
     */
    parent.richMedia.add = function (tagObject) {

        tagObject = tagObject || {};
        var playerId = _filterPlyrId(tagObject['playerId']);
        var mediaLabel = _getFullName(tagObject, 'mediaTheme', 'mediaLabel');
        var isEmbedded = _setBoolean(tagObject, 'isEmbedded', 'int', 'ext');

        // Complete media object with tag data
        mediaObject.setMediaValue(playerId, mediaLabel, 'plyr', playerId);
        mediaObject.setMediaValue(playerId, mediaLabel, 'type', tagObject.mediaType);
        mediaObject.setMediaValue(playerId, mediaLabel, 's2', tagObject.mediaLevel2);
        mediaObject.setMediaValue(playerId, mediaLabel, 'p', mediaLabel);
        // We keep 'previousMedia' property for legacy purpose
        mediaObject.setMediaValue(playerId, mediaLabel, 'clnk', tagObject.linkedContent || tagObject.previousMedia);
        mediaObject.setMediaValue(playerId, mediaLabel, 'a', tagObject.action);
        mediaObject.setMediaValue(playerId, mediaLabel, 'rfsh', tagObject.refreshDuration);
        mediaObject.setMediaValue(playerId, mediaLabel, 'm1', tagObject.duration);
        mediaObject.setMediaValue(playerId, mediaLabel, 'm5', isEmbedded);
        mediaObject.setMediaValue(playerId, mediaLabel, 'm6', tagObject.broadcastMode);
        mediaObject.setMediaValue(playerId, mediaLabel, 'm9', tagObject.webdomain);

        /* @if debug */
        parent.debug('RichMedia:richMedia:add', _debug.level, _debug.messageEnd, {
            player: playerId,
            media: mediaObject,
            tagData: tagObject
        });
        /* @endif */
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.RichMedia RichMedia}] Send a media action ("play", "pause", "stop", "info").
     * @alias richMedia.send
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @param tagObject {object} 4 properties : action, playerId, mediaLabel, isBuffering
     * @example
     * <pre><code class="javascript">tag.richMedia.send({
     *      action: "info",
     *      playerId: 333,
     *      mediaLabel: "mediaLabel",
     *      isBuffering: true
     *  });
     * </code></pre>
     * @public
     */
    parent.richMedia.send = function (tagObject) {

        tagObject = tagObject || {};
        var playerId = _filterPlyrId(tagObject.playerId);
        var mediaLabel = _getFullName(tagObject, 'mediaTheme', 'mediaLabel');
        var action = tagObject.action;

        // Update media action
        mediaObject.setMediaValue(playerId, mediaLabel, 'a', action);

        // Complete buffer object with tag data
        var buffer = {
            'plyr': playerId,
            'p': mediaLabel
        };
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'a', false);
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'type', false);
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 's2', false);
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'm1', false);
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'm5', false);
        _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'm6', false);

        // Manage additional parameters for actions 'play' or 'info'
        if ((action === 'play') || (action === 'info')) {
            var buf = _setBoolean(tagObject, 'isBuffering', '0', '1');
            var contextPageObject = parent.getContext('page') || {};
            var prich = _getFullName(contextPageObject, 'chapter', 'name') || undefined;
            var s2rich = contextPageObject.level2 || undefined;
            _setBufferParamFromTagObject(buffer, 'buf', buf);
            _setBufferParamFromTagObject(buffer, 'prich', prich);
            _setBufferParamFromTagObject(buffer, 's2rich', s2rich);
            _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'clnk', false);
            _setBufferParamFromMediaObject(buffer, playerId, mediaLabel, 'm9', true);
        }

        // Send hit
        parent.sendHit(buffer, [['hitType', ['richmedia']]], null, null, null);

        // Manage timeout clearing for actions 'pause' or 'stop'
        if (action === 'pause') {
            timeoutObject.clearTimeout(playerId, mediaLabel, false);
        } else if (action === 'stop') {
            timeoutObject.clearTimeout(playerId, mediaLabel, true);
        }

        // Manage refresh value
        if ((action === 'play') || (action === 'refresh')) {
            var refreshValue = mediaObject.getMediaValue(playerId, mediaLabel, 'rfsh');
            if (typeof refreshValue === 'object' && refreshValue !== null) {
                timeoutObject.setTimeoutObject(playerId, mediaLabel, refreshValue);
            } else {
                refreshValue = _filterValue(refreshValue, 5);
                if (refreshValue !== 0) {
                    timeoutObject.setTimeout(playerId, mediaLabel, refreshValue);
                }
            }
        }

        /* @if debug */
        if (action === 'info' && (typeof tagObject.isBuffering !== 'boolean')) {
            parent.debug('RichMedia:richMedia:send:Error', 'WARNING', '"isBuffering" parameter is mandatory for "info"-type actions, please add it to "tag.richMedia.send()" method with correct format', {
                action: action,
                isBuffering: tagObject.isBuffering
            });
        }
        var mediaType = mediaObject.getMediaValue(playerId, mediaLabel, 'type');
        var broadcastMode = mediaObject.getMediaValue(playerId, mediaLabel, 'm6');
        var duration = mediaObject.getMediaValue(playerId, mediaLabel, 'm1');
        if ((typeof mediaType === 'undefined') || (typeof broadcastMode === 'undefined') || (broadcastMode === 'clip' && (typeof duration === 'undefined'))) {
            parent.debug('RichMedia:richMedia:send:Error', 'WARNING', 'Some required parameters are missing, please use "tag.richMedia.add()" method to set', {
                mediaType: mediaType,
                broadcastMode: broadcastMode,
                duration: duration
            });
        }
        parent.debug('RichMedia:richMedia:send', _debug.level, _debug.messageEnd, {
            player: playerId,
            media: mediaObject,
            buffer: buffer,
            tagData: tagObject
        });
        /* @endif */
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.RichMedia RichMedia}] Remove a player with all associated media.
     * @alias richMedia.remove
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @param playerId {number} Player ID
     * @example
     * <pre><code class="javascript">tag.richMedia.remove(333);
     * </code></pre>
     * @public
     */
    parent.richMedia.remove = function (playerId) {
        timeoutObject.removePlayer(playerId);
        mediaObject.removePlayer(playerId);
        /* @if debug */
        parent.debug('RichMedia:richMedia:remove', _debug.level, _debug.messageEnd, {playerId: playerId, media: mediaObject});
        /* @endif */
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.RichMedia RichMedia}] Remove all players and media.
     * @alias richMedia.removeAll
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @example
     * <pre><code class="javascript">tag.richMedia.removeAll();
     * </code></pre>
     * @public
     */
    parent.richMedia.removeAll = function () {
        timeoutObject.removeAll();
        mediaObject.removeAll();
        /* @if debug */
        parent.debug('RichMedia:richMedia:removeAll', _debug.level, _debug.messageEnd, {media: mediaObject});
        /* @endif */
    };

    /* @if test */
    self._filterValue = _filterValue;
    self.Timeout = Timeout;
    self.Media = Media;
    self._getFullName = _getFullName;
    self._setBoolean = _setBoolean;
    self._filterPlyrId = _filterPlyrId;
    self._setBufferParamFromMediaObject = _setBufferParamFromMediaObject;
    self._setBufferParamFromTagObject = _setBufferParamFromTagObject;
    /* @endif */

};
window['ATInternet']['Tracker']['addPlugin']('RichMedia');