strategy-absolute.js 7.01 KB
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2015 Adobe Systems Incorporated
 * All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 */
(function($, player, later) {
    'use strict';

    /**
     * A simple strategy that cycles through the items following their order in the DOM.
     *
     * @class
     *
     * @param {HTMLElement|jQuery}  el  The sequence element
     */
    var AbsoluteStrategy = function(el) {
        this.$el = $(el);
        this.el = this.$el.get(0);

        this.$el.data('screens-strategy', this);

        this.items().hide();

        this.init();
    };

    /**
     * Initialize the strategy.
     */
    AbsoluteStrategy.prototype.init = function() {
        var $items = this.items();
        var data = {};
        data.cycles = [];
        data.totalDuration = 0;

        var self = this;
        $.each($items, function(index, item) {
            var $item = $(item);

            // excluding items that are skipped
            if (item.dataset.itemSkip) {
                return;
            }
            var duration = self.duration($item);

            // items with infinite duration have to provide duration later
            if (duration === -1) {
                return;
            }

            data.totalDuration += duration;

            data.cycles.push({
                duration: duration,
                index: index,
                startTime: data.totalDuration,
                item: item
            });
        });

        data.nbCycles = data.cycles.length;

        var anchor = new Date();
        anchor.setHours(0);
        anchor.setMinutes(0);
        anchor.setSeconds(0);
        anchor.setMilliseconds(0);

        data.anchor = anchor.getTime();

        this.data = data;
    };

    /**
     * Updates the strategy.
     * @returns {boolean} true if the sequence needs restart.
     */
    AbsoluteStrategy.prototype.update = function() {
        this.init();
        return true;
    };

    /**
     * The sequence elements for this strategy.
     *
     * @return {jQuery} The strategy items
     */
    AbsoluteStrategy.prototype.items = function() {
        return $(this.el.getElementsByClassName('cq-Sequence-item'));
    };

    /**
     * Get the item which is shown when the sequence starts
     *
     * @return {jQuery} The starting item according to the strategy
     */
    AbsoluteStrategy.prototype.startItem = function() {
        return this.items().first();
    };

    /**
     * Check if a cycle is complete
     *
     * @param {jQuery} [$item] The current sequence element
     *
     * @returns {boolean} `true` if the cycle of the sequence is completed, `false` otherwise
     */
    AbsoluteStrategy.prototype.isCycleComplete = function($item) {
        var last = this.items().last();

        // cycle is complete if no item or if current $item is the last one
        return this.items().length === 0 || ($item && (last[0] === $item[0]));
    };

    AbsoluteStrategy.prototype.getNext = function() {
        var time = new Date().getTime();
        var data = this.data;

        var r = time - data.anchor;
        r %= data.totalDuration;

        var cycleBeginTime = time - r;

        // r contains the time already elapsed in the current cycle
        // need to find which item is supposed to be shown
        var index = 0,
            duration = 0,
            item;

        while (duration <= r && index < data.nbCycles) {
            var cycle = data.cycles[index++];
            item = cycle.item;
            duration += cycle.duration;
        }

        return {
            index: index,
            time: cycleBeginTime + duration,
            item: item
        };
    };

    /**
     * Get the next sequence element for this strategy.
     *
     * @return {jQuery} The next item according to the strategy
     */
    AbsoluteStrategy.prototype.next = function() {
        var info = this.getNext();
        var $nextItem = $(info.item);

        var transitionType = 'normal';

        return {
            $item: $nextItem,
            transitionType: transitionType
        };
    };

    AbsoluteStrategy.prototype.scheduleCurrent = function(callback, duration) {
        var info = this.getNext();
        var time = info.time - new Date().getTime();
        window.setTimeout(callback, time);
    };


    /**
     * Get the duration for the specified sequence element.
     *
     * @param  {jQuery} [$item] The sequence element to get the duration for
     *
     * @return {Number} The duration for the item in ms
     */
    AbsoluteStrategy.prototype.duration = function($item) {
        var item = $item.get(0);
        var duration = null;
        var el;
        if (item) {
            duration = parseInt(item.dataset.duration, 10);
            if (!duration) {
                el = item.querySelector('[data-duration]');
                duration = el ? parseInt(el.dataset.duration, 10) : null;

                // Backward compatibility case where channel was created in 6.3 GA
                // A duration of -1 means that the subsequence is responsible for determining the sequence length
                // This was defaulted in the UI after 6.3 GA but channels with subsequences created in older versions
                // may have issues. Therefore adding this default in the code as well
                if ((!duration) && (item.classList.contains('cq-Screens-subsequence'))) {
                    var iFrame = item.getElementsByTagName('iframe')[0];
                    if (iFrame) {
                        iFrame.setAttribute('data-duration', '-1');
                        duration = -1;
                    }
                }
            }
        }

        duration = parseInt(duration, 10);

        if (isNaN(duration) || duration === 0) {
            // 0 or null case, use duration of the sequence
            duration = parseInt(this.el.dataset.duration, 10) || 5000;
        }

        if (duration === -1) {
            // special case of items handling their own duration, like video comp
            // look for item-duration data. Otherwise -1 (item must trigger an update)
            el = item.querySelector('[data-item-duration]');
            duration = el && parseInt(el.dataset.itemDuration, 10) || duration;
        }

        return duration;
    };

    /**
     * Destroy the strategy.
     */
    AbsoluteStrategy.prototype.destroy = function() {
        this.$el.removeData('screens-strategy');
        delete this.$el;
        delete this.el;
    };

    player.strategies.absolute = AbsoluteStrategy;

}(window.jQuery, window.ScreensPlayer, window.later));