<commit_before>(function ($, audiojs) {
    'use strict';

    $.fn.addPlayer = function (preload) {
        if ($(this).length !== 1) {
            throw new Error('Cannot instantiate player on ' +
                            'multiple/null selector');
        }
        if (!$(this).data('js-player')) {
            $(this).data('js-player', new Player($(this), preload));
        }
        return $(this).data('js-player');
    };

    var Track = function (src, title, dur, poster_src, pod_id, last_pos, played) {
        return {
            src: src,
            title: title,
            duration: dur,
            poster: poster_src,
            podcast: Number(pod_id),
            last_pos: (last_pos) ? last_pos : 0,
            played: (played) ? played : false
        };
    };

    var TrackElement = function (track) {
        var el = $('

' +
                   '
' +
                   '  ' +
                   '

' + track.title +
                   '

');
        return el;
    };

    var PlayMode = {
        continue: 0x01,
        repeat: 0x02
    };

    var Player = function (parentElement, preload) {
        if (!preload) preload = {};
        var P = {
            _dom: $('

'),
            _mode: (preload.mode) ? preload.mode : 0,
            header: new Header(),
            audio: new AudioPlayer(),
            playlist: new PlayList(preload.playlist),

            init: function (volume) {
                this.audio.init(this, volume);
                this._dom.find('.player-list ol').click(function (e) {
                    var li = ($(e.target).is('li')) ? $(e.target) :
                            $(e.target).closest('li'),
                        index = li.index(),
                        div = ($(e.target).is('div'))? $(e.target) :
                            $(e.target).closest('div');
                    if (div.hasClass('pl-btn-del')) {
                        P.delete(index);
                    }
                    else {
                        P.load(index);
                        P.play();
                    }
                });
            },
            error: function (e) {
                console.log(e);
                this.header.loadError(this.playlist.getTrack());
                this._showState('error');
            },
            load: function (index) {
                var track = this.playlist.load(index);
                this._showState('load');
                this.audio.load(track);
                this.header.load(track);
            },
            unload: function () {
                this.header.load();
                this._showState();
            },
            add: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.add(
                    src, title, dur, poster_src, pod_id);
                if (i === 0) this.load(i);
            },
            delete: function (index) {
                var c = this.playlist._cur;
                if (c === index) {
                    this.stop();
                    if (this.playlist.count() > 1) this.nextTrack(true);
                    else this.unload();
                }
                this.playlist.delete(index);
            },
            addAndPlay: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.insert(
                    src, title, dur, poster_src, pod_id);
                this.load(i);
                this.play();
                this._dom[0].scrollIntoView();
            },
            stop: function () {
                this.pause();
                this.reset();
            },
            play: function () {
                if (this.playlist.getTrack()) {
                    this.audio.play();
                    this._showState('play');
                }
            },
            pause: function () {
                if (this.playlist.getTrack()) {
                    this.audio.pause();
                    this._showState('pause');
                }
            },
            trackFinished: function () {
                this.playlist.finished();
                if (this.isMode(PlayMode.continue)) {
                    var t = this.playlist.next();
                    if (t !== -1 || this.isMode(PlayMode.repeat)) {
                        this.nextTrack();
                    }
                }
                else if (this.isMode(PlayMode.repeat)) {
                    this.reset();
                    this.play();
                }
            },
            reset: function () {
                this.audio.reset();
            },
            nextTrack: function (noplay) {
                var t = this.playlist.next();
                if (t === -1) t = 0;
                this.load(t);
                if (!noplay) this.play();
            },
            prevTrack: function () {
                var t = this.playlist.prev();
                if (t === -1) t = this.playlist.count() - 1;
                this.load(t);
                this.play();
            },
            skipAhead: function (sec) {
                this.audio.skipAhead(sec);
            },
            skipBack: function (sec) {
                this.audio.skipBack(sec);
            },
            addMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode | mode;
            },
            remMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode & ~mode;
            },
            isMode: function () {
                var i;
                if (arguments.length < 1) return false;
                for (i = 0; i < arguments.length; i++) {
                    if (!(this._mode & arguments[i])) return false;
                }
                return true;
            },
            setVolume: function (vol) {
                this.audio.setVolume(vol);
            },
            _showState: function (type) {
                var all = 'p-play p-pause p-load p-error',
                    pp = this._dom.find('.play-pause'),
                    Types = {
                    play: 'p-play',
                    pause: 'p-pause',
                    load: 'p-load',
                    error: 'p-error'
                };
                if (!type) pp.removeClass(all);
                else if (!Types[type]) throw new Error('Cannot display state: ' + type);
                else if (!pp.hasClass(Types[type])) {
                    pp.removeClass(all);
                    pp.addClass(Types[type]);
                }
            }
        };
        P.header._dom.append(P.audio._dom);
        P._dom.append(P.header._dom);
        P._dom.append(P.playlist._dom);
        parentElement.append(P._dom);
        P.init(preload.volume);
        return P;
    };

    var Header = function () {
        var H = {
            _dom: $('
' +
                    '
No track selected.
' +
                    '
'),
            _marquee_speed: 200, // miliseconds per character

            init: function () {
                this._insertTitle(this._dom.find('.titlebar').html());
            },

            load: function (track) {
                if (!track) this._unload();
                else {
                    this._insertTitle(track.title);
                }
            },

            loadError: function (track) {

            },

            _unload: function () {
                this._insertTitle("No track selected.");
            },

            _insertTitle: function (msg) {
                this._dom.find('.titlebar').html(msg).marquee({
                    duration: msg.length * this._marquee_speed,
                    gap: 0,
                    delayBeforeStart: 0,
                    direction: 'left',
                    duplicated: false
                });
            }
        };
        return H;
    };

    var AudioPlayer = function () {
        var AP = {
            _dom: $('
<audio preload></audio>
'),
            _audio: false,

            init: function (player, volume) {
                if (!volume) volume = 0.75;
                if ($(this._dom.getUnique()).length < 1) {
                    throw new Error('Cannot initialize AudioPlayer that is ' +
                                    'not attached to the current DOM.');
                }
                this._audio = audiojs.newInstance(this._dom.find('audio')[0], {
                    trackEnded: function () {
                        player.trackFinished();
                    }
                });
                this._audio.onError = function (e) {
                    player.error(e);
                };
                this._dom.find('.play-pause').click(function () {
                    console.log('playpause: \n\t');
                    console.log($(this));
                    if ($(this).hasClass('p-play')) {
                        player.pause();
                    }
                    else if ($(this).hasClass('p-pause')) {
                        player.play();
                    }
                });
                this._dom.find('.skip.ahead').click(function () {
                    player.skipAhead(30);
                });
                this._dom.find('.skip.back').click(function () {
                    player.skipBack(15);
                });
                this._dom.find('.volume').click(function () {
                    var time = $(this).closest('.audiojs').find('.time');
                    if (time.find('span').css('display') != 'none') {
                        $(this).newTip(true, 'Close');
                        time.find('span').css('display', 'none');
                        time.find('.slider').css('display', 'block');
                        $(this).css('color', '#ccc');
                    }
                    else {
                        $(this).newTip(true, 'Adjust Volume');
                        time.find('span').css('display', 'block');
                        time.find('.slider').css('display', 'none');
                        $(this).css('color', '#777');
                    }
                });
                this._dom.find('.slider').slider({
                    range: "min",
                    min: 0,
                    max: 40,
                    value: volume * 40,
                    slide: function (event, ui) {
                        player.setVolume(ui.value/40);
                    }
                });
                player.setVolume(this._dom.find('.slider').slider('value')/40);
                this._dom.find('[title]').newTip();
            },

            load: function (src) {
                if (!src) this._unload();
                else {
                    if (typeof(src) === 'object') src = src.src;
                    this._audio.load(src);
                }
            },

            _unload: function () {
            },

            play: function () {
                this._audio.play();
            },

            pause: function () {
                this._audio.pause();
            },

            playPause: function () {
                this._audio.playPause();
            },

            skipTo: function (position) {
                var percent, a = this._dom.find('audio')[0];
                position = (position < 0) ? 0 :
                        (position > a.duration) ? a.duration : position;
                percent = Math.floor(position / a.duration * 100) / 100;
                this._audio.skipTo(percent);
            },

            skipAhead: function (seconds) {
                var a = this._dom.find('audio')[0];
                this.skipTo(a.currentTime + seconds);
            },

            skipBack: function (seconds) {
                this.skipAhead(seconds * -1);
            },

            reset: function () {
                this.skipTo(0);
            },

            isPlaying: function () {
                return this._audio.playing;
            },

            getPosition: function () {
                return Math.floor(this._dom.find('audio')[0].currentTime);
            },

            setVolume: function (vol) {
                var el = this._dom.find('.volume');
                if (vol === this._dom.find('audio')[0].volume) return;
                this._audio.setVolume(vol);
                el.find('.glyphicon').css('display', 'none');
                if (vol === 0) {
                    el.find('.glyphicon-volume-off').css('display', 'block');
                }
                else if (vol === 1) {
                    el.find('.glyphicon-volume-up').css('display', 'block');
                }
                else {
                    el.find('.glyphicon-volume-down').css('display', 'block');
                }
            },

        };
        return AP;
    };

    var PlayList = function (list) {
        var PL = {
            _dom: $('

'),
            _list: [],
            _cur: 0,

            add: function (src, title, dur, poster_src, pod_id) {
                var track, played, index, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.push(track);
                this._appendElement(el);
                index = this.count() - 1;
                if (track.played) this._markPlayed(index);
                return index;
            },

            insert: function (src, title, dur, poster_src, pod_id) {
                var track, played, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.unshift(track);
                this._insertElement(el);
                if (track.played) this._markPlayed(0);
                if (this.count() !== 1) this._cur++;
                return 0;
            },

            delete: function (index) {
                var rem, element;
                if (this._list[index]) {
                    element = $(this._dom.find('li')[index]);
                    this._list.splice(index, 1);
                    this._removeElement(element);
                }
                if (index <= this._cur) this._cur--;
            },

            load: function (index) {
                var item, element;
                if (index < 0) index = this.count() - 1;
                if (index >= this.count()) index = 0;
                if (!this.getTrack(index)) {
                    return false;
                }
                this._cur = index;
                item = this.getTrack();
                element = $(this._dom.find('li')[index]);
                item.played = false;
                $(element).parent().find('.loaded').removeClass('loaded');
                $(element).removeClass('played');
                $(element).addClass('loaded');
                return item;
            },

            getTrack: function (index) {
                index = (Number(index) === index) ? index : this._cur;
                if (this.count() === 0 || !this._list[index]) {
                    return false;
                }
                return this._list[index];
            },

            hasTrack: function (track_or_src) {
                var src = track_or_src, filter;
                if (typeof(src) !== 'string') {
                    src = src.src;
                }
                filter = this._list.filter(function (track) {
                    return track.src === src;
                })[0];
                return (typeof(filter) !== 'undefined');
            },

            next: function () {
                if (!this._cur || this._cur === this.count() - 1) return -1;
                return this._cur + 1;
            },

            prev: function () {
                if (this._cur === 0) return -1;
                return this._cur - 1;
            },

            finished: function (index) {
                if (!index) index = this._cur;
                if (this._list[index]) {
                    this._list[index].played = true;
                    this._markPlayed(index);
                }
            },

            _markPlayed: function (index) {
                if (this._list[index]) {
                    $(this._list[index].selector).addClass('played');
                }
            },

            count: function () { return this._list.length; },

            _insertElement: function (el) {
                $(this._dom).find('ol').prepend(el);
                el.animate({opacity: 1}, 500);
            },

            _appendElement: function (el) {
                $(this._dom).find('ol').append(el);
                el.animate({opacity: 1}, 500);
            },

            _removeElement: function (el) {
                el.animate({height: '0px', opacity: 0}, 250, function () {
                    el.remove();
                });
            }
        };
        while (list && list.length > 0) {
            PL.insert(list.pop(), false);
        }
        return PL;
    };

}(jQuery, audiojs));<commit_msg>Initialize header after added to DOM.<commit_after>(function ($, audiojs) {
    'use strict';

    $.fn.addPlayer = function (preload) {
        if ($(this).length !== 1) {
            throw new Error('Cannot instantiate player on ' +
                            'multiple/null selector');
        }
        if (!$(this).data('js-player')) {
            $(this).data('js-player', new Player($(this), preload));
        }
        return $(this).data('js-player');
    };

    var Track = function (src, title, dur, poster_src, pod_id, last_pos, played) {
        return {
            src: src,
            title: title,
            duration: dur,
            poster: poster_src,
            podcast: Number(pod_id),
            last_pos: (last_pos) ? last_pos : 0,
            played: (played) ? played : false
        };
    };

    var TrackElement = function (track) {
        var el = $('

' +
                   '
' +
                   '  ' +
                   '

' + track.title +
                   '

');
        return el;
    };

    var PlayMode = {
        continue: 0x01,
        repeat: 0x02
    };

    var Player = function (parentElement, preload) {
        if (!preload) preload = {};
        var P = {
            _dom: $('

'),
            _mode: (preload.mode) ? preload.mode : 0,
            header: new Header(),
            audio: new AudioPlayer(),
            playlist: new PlayList(preload.playlist),

            init: function (volume) {
                this.header.init();
                this.audio.init(this, volume);
                this._dom.find('.player-list ol').click(function (e) {
                    var li = ($(e.target).is('li')) ? $(e.target) :
                            $(e.target).closest('li'),
                        index = li.index(),
                        div = ($(e.target).is('div'))? $(e.target) :
                            $(e.target).closest('div');
                    if (div.hasClass('pl-btn-del')) {
                        P.delete(index);
                    }
                    else {
                        P.load(index);
                        P.play();
                    }
                });
            },
            error: function (e) {
                console.log(e);
                this.header.loadError(this.playlist.getTrack());
                this._showState('error');
            },
            load: function (index) {
                var track = this.playlist.load(index);
                this._showState('load');
                this.audio.load(track);
                this.header.load(track);
            },
            unload: function () {
                this.header.load();
                this._showState();
            },
            add: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.add(
                    src, title, dur, poster_src, pod_id);
                if (i === 0) this.load(i);
            },
            delete: function (index) {
                var c = this.playlist._cur;
                if (c === index) {
                    this.stop();
                    if (this.playlist.count() > 1) this.nextTrack(true);
                    else this.unload();
                }
                this.playlist.delete(index);
            },
            addAndPlay: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.insert(
                    src, title, dur, poster_src, pod_id);
                this.load(i);
                this.play();
                this._dom[0].scrollIntoView();
            },
            stop: function () {
                this.pause();
                this.reset();
            },
            play: function () {
                if (this.playlist.getTrack()) {
                    this.audio.play();
                    this._showState('play');
                }
            },
            pause: function () {
                if (this.playlist.getTrack()) {
                    this.audio.pause();
                    this._showState('pause');
                }
            },
            trackFinished: function () {
                this.playlist.finished();
                if (this.isMode(PlayMode.continue)) {
                    var t = this.playlist.next();
                    if (t !== -1 || this.isMode(PlayMode.repeat)) {
                        this.nextTrack();
                    }
                }
                else if (this.isMode(PlayMode.repeat)) {
                    this.reset();
                    this.play();
                }
            },
            reset: function () {
                this.audio.reset();
            },
            nextTrack: function (noplay) {
                var t = this.playlist.next();
                if (t === -1) t = 0;
                this.load(t);
                if (!noplay) this.play();
            },
            prevTrack: function () {
                var t = this.playlist.prev();
                if (t === -1) t = this.playlist.count() - 1;
                this.load(t);
                this.play();
            },
            skipAhead: function (sec) {
                this.audio.skipAhead(sec);
            },
            skipBack: function (sec) {
                this.audio.skipBack(sec);
            },
            addMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode | mode;
            },
            remMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode & ~mode;
            },
            isMode: function () {
                var i;
                if (arguments.length < 1) return false;
                for (i = 0; i < arguments.length; i++) {
                    if (!(this._mode & arguments[i])) return false;
                }
                return true;
            },
            setVolume: function (vol) {
                this.audio.setVolume(vol);
            },
            _showState: function (type) {
                var all = 'p-play p-pause p-load p-error',
                    pp = this._dom.find('.play-pause'),
                    Types = {
                    play: 'p-play',
                    pause: 'p-pause',
                    load: 'p-load',
                    error: 'p-error'
                };
                if (!type) pp.removeClass(all);
                else if (!Types[type]) throw new Error('Cannot display state: ' + type);
                else if (!pp.hasClass(Types[type])) {
                    pp.removeClass(all);
                    pp.addClass(Types[type]);
                }
            }
        };
        P.header._dom.append(P.audio._dom);
        P._dom.append(P.header._dom);
        P._dom.append(P.playlist._dom);
        parentElement.append(P._dom);
        P.init(preload.volume);
        return P;
    };

    var Header = function () {
        var H = {
            _dom: $('
' +
                    '
No track selected.
' +
                    '
'),
            _marquee_speed: 200, // miliseconds per character

            init: function () {
                this._insertTitle(this._dom.find('.titlebar').html());
            },

            load: function (track) {
                if (!track) this._unload();
                else {
                    this._insertTitle(track.title);
                }
            },

            loadError: function (track) {

            },

            _unload: function () {
                this._insertTitle("No track selected.");
            },

            _insertTitle: function (msg) {
                this._dom.find('.titlebar').html(msg).marquee({
                    duration: msg.length * this._marquee_speed,
                    gap: 0,
                    delayBeforeStart: 0,
                    direction: 'left',
                    duplicated: false
                });
            }
        };
        return H;
    };

    var AudioPlayer = function () {
        var AP = {
            _dom: $('
<audio preload></audio>
'),
            _audio: false,

            init: function (player, volume) {
                if (!volume) volume = 0.75;
                if ($(this._dom.getUnique()).length < 1) {
                    throw new Error('Cannot initialize AudioPlayer that is ' +
                                    'not attached to the current DOM.');
                }
                this._audio = audiojs.newInstance(this._dom.find('audio')[0], {
                    trackEnded: function () {
                        player.trackFinished();
                    }
                });
                this._audio.onError = function (e) {
                    player.error(e);
                };
                this._dom.find('.play-pause').click(function () {
                    console.log('playpause: \n\t');
                    console.log($(this));
                    if ($(this).hasClass('p-play')) {
                        player.pause();
                    }
                    else if ($(this).hasClass('p-pause')) {
                        player.play();
                    }
                });
                this._dom.find('.skip.ahead').click(function () {
                    player.skipAhead(30);
                });
                this._dom.find('.skip.back').click(function () {
                    player.skipBack(15);
                });
                this._dom.find('.volume').click(function () {
                    var time = $(this).closest('.audiojs').find('.time');
                    if (time.find('span').css('display') != 'none') {
                        $(this).newTip(true, 'Close');
                        time.find('span').css('display', 'none');
                        time.find('.slider').css('display', 'block');
                        $(this).css('color', '#ccc');
                    }
                    else {
                        $(this).newTip(true, 'Adjust Volume');
                        time.find('span').css('display', 'block');
                        time.find('.slider').css('display', 'none');
                        $(this).css('color', '#777');
                    }
                });
                this._dom.find('.slider').slider({
                    range: "min",
                    min: 0,
                    max: 40,
                    value: volume * 40,
                    slide: function (event, ui) {
                        player.setVolume(ui.value/40);
                    }
                });
                player.setVolume(this._dom.find('.slider').slider('value')/40);
                this._dom.find('[title]').newTip();
            },

            load: function (src) {
                if (!src) this._unload();
                else {
                    if (typeof(src) === 'object') src = src.src;
                    this._audio.load(src);
                }
            },

            _unload: function () {
            },

            play: function () {
                this._audio.play();
            },

            pause: function () {
                this._audio.pause();
            },

            playPause: function () {
                this._audio.playPause();
            },

            skipTo: function (position) {
                var percent, a = this._dom.find('audio')[0];
                position = (position < 0) ? 0 :
                        (position > a.duration) ? a.duration : position;
                percent = Math.floor(position / a.duration * 100) / 100;
                this._audio.skipTo(percent);
            },

            skipAhead: function (seconds) {
                var a = this._dom.find('audio')[0];
                this.skipTo(a.currentTime + seconds);
            },

            skipBack: function (seconds) {
                this.skipAhead(seconds * -1);
            },

            reset: function () {
                this.skipTo(0);
            },

            isPlaying: function () {
                return this._audio.playing;
            },

            getPosition: function () {
                return Math.floor(this._dom.find('audio')[0].currentTime);
            },

            setVolume: function (vol) {
                var el = this._dom.find('.volume');
                if (vol === this._dom.find('audio')[0].volume) return;
                this._audio.setVolume(vol);
                el.find('.glyphicon').css('display', 'none');
                if (vol === 0) {
                    el.find('.glyphicon-volume-off').css('display', 'block');
                }
                else if (vol === 1) {
                    el.find('.glyphicon-volume-up').css('display', 'block');
                }
                else {
                    el.find('.glyphicon-volume-down').css('display', 'block');
                }
            },

        };
        return AP;
    };

    var PlayList = function (list) {
        var PL = {
            _dom: $('

'),
            _list: [],
            _cur: 0,

            add: function (src, title, dur, poster_src, pod_id) {
                var track, played, index, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.push(track);
                this._appendElement(el);
                index = this.count() - 1;
                if (track.played) this._markPlayed(index);
                return index;
            },

            insert: function (src, title, dur, poster_src, pod_id) {
                var track, played, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.unshift(track);
                this._insertElement(el);
                if (track.played) this._markPlayed(0);
                if (this.count() !== 1) this._cur++;
                return 0;
            },

            delete: function (index) {
                var rem, element;
                if (this._list[index]) {
                    element = $(this._dom.find('li')[index]);
                    this._list.splice(index, 1);
                    this._removeElement(element);
                }
                if (index <= this._cur) this._cur--;
            },

            load: function (index) {
                var item, element;
                if (index < 0) index = this.count() - 1;
                if (index >= this.count()) index = 0;
                if (!this.getTrack(index)) {
                    return false;
                }
                this._cur = index;
                item = this.getTrack();
                element = $(this._dom.find('li')[index]);
                item.played = false;
                $(element).parent().find('.loaded').removeClass('loaded');
                $(element).removeClass('played');
                $(element).addClass('loaded');
                return item;
            },

            getTrack: function (index) {
                index = (Number(index) === index) ? index : this._cur;
                if (this.count() === 0 || !this._list[index]) {
                    return false;
                }
                return this._list[index];
            },

            hasTrack: function (track_or_src) {
                var src = track_or_src, filter;
                if (typeof(src) !== 'string') {
                    src = src.src;
                }
                filter = this._list.filter(function (track) {
                    return track.src === src;
                })[0];
                return (typeof(filter) !== 'undefined');
            },

            next: function () {
                if (!this._cur || this._cur === this.count() - 1) return -1;
                return this._cur + 1;
            },

            prev: function () {
                if (this._cur === 0) return -1;
                return this._cur - 1;
            },

            finished: function (index) {
                if (!index) index = this._cur;
                if (this._list[index]) {
                    this._list[index].played = true;
                    this._markPlayed(index);
                }
            },

            _markPlayed: function (index) {
                if (this._list[index]) {
                    $(this._list[index].selector).addClass('played');
                }
            },

            count: function () { return this._list.length; },

            _insertElement: function (el) {
                $(this._dom).find('ol').prepend(el);
                el.animate({opacity: 1}, 500);
            },

            _appendElement: function (el) {
                $(this._dom).find('ol').append(el);
                el.animate({opacity: 1}, 500);
            },

            _removeElement: function (el) {
                el.animate({height: '0px', opacity: 0}, 250, function () {
                    el.remove();
                });
            }
        };
        while (list && list.length > 0) {
            PL.insert(list.pop(), false);
        }
        return PL;
    };

}(jQuery, audiojs));<|endoftext|>


<commit_before>(function ($, audiojs) {
    'use strict';

    $.fn.addPlayer = function (preload) {
        if ($(this).length !== 1) {
            throw new Error('Cannot instantiate player on ' +
                            'multiple/null selector');
        }
        if (!$(this).data('js-player')) {
            $(this).data('js-player', new Player($(this), preload));
        }
        return $(this).data('js-player');
    };

    var Track = function (src, title, dur, poster_src, pod_id, last_pos, played) {
        return {
            src: src,
            title: title,
            duration: dur,
            poster: poster_src,
            podcast: Number(pod_id),
            last_pos: (last_pos) ? last_pos : 0,
            played: (played) ? played : false
        };
    };

    var TrackElement = function (track) {
        var el = $('

' +
                   '

' + track.title + '

' +
                   track.duration + '

');
        return el;
    };

    var PlayMode = {
        continue: 0x01,
        repeat: 0x02
    };

    var Player = function (parentElement, preload) {
        if (!preload) preload = {};
        var P = {
            _dom: $('

'),
            _mode: (preload.mode) ? preload.mode : 0,
            header: new Header(),
            audio: new AudioPlayer(),
            playlist: new PlayList(preload.playlist),

            init: function (volume) {
                this.audio.init(this, volume);
                this._dom.find('.player-list ol').click(function (e) {
                    var li = ($(e.target).is('li')) ? $(e.target) :
                            $(e.target).closest('li'),
                        index = li.index();
                    if ($(e.target).hasClass('pl-btn-del')) {
                        P.delete(index);
                    }
                    else {
                        P.load(index);
                    }
                });
            },
            load: function (index) {
                var track = this.playlist.load(index);
                this.audio.load(track);
                this.header.load(track);
            },
            unload: function () {
            },
            add: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.add(
                    src, title, dur, poster_src, pod_id);
                if (i === 0) this.load(i);
            },
            delete: function (index) {
                var c = this.playlist._cur;
                this.playlist.delete(index);
                if (c === index) {
                    this.stop();
                    this.trackFinished();
                }
            },
            addAndPlay: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.insert(
                    src, title, dur, poster_src, pod_id);
                this.load(i);
                this.play();
            },
            stop: function () {
                this.pause();
                this.reset();
            },
            play: function () {
                if (this.playlist.getTrack()) this.audio.play();
            },
            pause: function () {
                this.audio.pause();
            },
            trackFinished: function () {
                this.playlist.finished();
                if (this.isMode(PlayMode.continue)) {
                    var t = this.playlist.next();
                    if (t !== -1 || this.isMode(PlayMode.repeat)) {
                        this.nextTrack();
                    }
                }
                else if (this.isMode(PlayMode.repeat)) {
                    this.reset();
                    this.play();
                }
            },
            reset: function () {
                this.audio.reset();
            },
            nextTrack: function () {
                var t = this.playlist.next();
                if (t === -1) t = 0;
                this.load(t);
                this.play();
            },
            prevTrack: function () {
                var t = this.playlist.prev();
                if (t === -1) t = this.playlist.count() - 1;
                this.load(t);
                this.play();
            },
            skipAhead: function (sec) {
                this.audio.skipAhead(sec);
            },
            skipBack: function (sec) {
                this.audio.skipBack(sec);
            },
            addMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode | mode;
            },
            remMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode & ~mode;
            },
            isMode: function () {
                var i;
                if (arguments.length < 1) return false;
                for (i = 0; i < arguments.length; i++) {
                    if (!(this._mode & arguments[i])) return false;
                }
                return true;
            },
            setVolume: function (vol) {
                this.audio.setVolume(vol);
            }
        };
        P._dom.append(P.header._dom);
        P._dom.append(P.audio._dom);
        P._dom.append(P.playlist._dom);
        parentElement.append(P._dom);
        P.init(preload.volume);
        return P;
    };

    var Header = function () {
        var H = {
            _dom: $('

'),

            load: function (track) {
                if (!track) this._unload();
                else {
                }
            },

            _unload: function () {
            }
        };
        return H;
    };

    var AudioPlayer = function () {
        var AP = {
            _dom: $('
<audio preload></audio>
'),
            _audio: false,

            init: function (player, volume) {
                if (!volume) volume = 0.75;
                if ($(this._dom.getUnique()).length < 1) {
                    throw new Error('Cannot initialize AudioPlayer that is ' +
                                    'not attached to the current DOM.');
                }
                this._audio = audiojs.newInstance(this._dom.find('audio')[0], {
                    trackEnded: function () {
                        player.trackFinished();
                    }
                });
                this._dom.find('.skip.flip-glyph').click(function () {
                    player.skipBack(15);
                });
                this._dom.find('.skip.flip-glyph').click(function () {
                    player.skipAhead(30);
                });
                this._dom.find('.volume').click(function () {
                    var time = $(this).closest('.audiojs').find('.time');
                    if (time.find('span').css('display') != 'none') {
                        time.find('span').css('display', 'none');
                        time.find('.slider').css('display', 'block');
                        $(this).css('color', '#ccc');
                    }
                    else {
                        time.find('span').css('display', 'block');
                        time.find('.slider').css('display', 'none');
                        $(this).css('color', '#777');
                    }
                });
                this._dom.find('.slider').slider({
                    range: "min",
                    min: 0,
                    max: 40,
                    value: volume * 40,
                    slide: function (event, ui) {
                        player.setVolume(ui.value/40);
                    }
                });
                player.setVolume(this._dom.find('.slider').slider('value')/40);
            },

            load: function (src) {
                if (!src) this._unload();
                else {
                    if (typeof(src) === 'object') src = src.src;
                    this._audio.load(src);
                }
            },

            _unload: function () {
            },

            play: function () {
                this._audio.play();
            },

            pause: function () {
                this._audio.pause();
            },

            playPause: function () {
                this._audio.playPause();
            },

            skipTo: function (position) {
                position = (position < 0) ? 0 :
                        (position > a.duration) ? a.duration : position;
                var a = this._dom.find('audio')[0],
                    precent = position / a.duration;
                this._audio.skipTo(percent);
            },

            skipAhead: function (seconds) {
                var a = this._dom.find('audio')[0];
                this.skipTo(a.currentTime + seconds);
            },

            skipBack: function (seconds) {
                this.skipAhead(seconds * -1);
            },

            reset: function () {
                this.skipTo(0);
            },

            isPlaying: function () {
                return this._audio.playing;
            },

            getPosition: function () {
                return Math.floor(this._dom.find('audio')[0].currentTime);
            },

            setVolume: function (vol) {
                var el = this._dom.find('.volume');
                if (vol === this._dom.find('audio')[0].volume) return;
                this._audio.setVolume(vol);
                el.find('.glyphicon').css('display', 'none');
                if (vol === 0) {
                    el.find('.glyphicon-volume-off').css('display', 'block');
                }
                else if (vol === 1) {
                    el.find('.glyphicon-volume-up').css('display', 'block');
                }
                else {
                    el.find('.glyphicon-volume-down').css('display', 'block');
                }
            },

        };
        return AP;
    };

    var PlayList = function (list) {
        var PL = {
            _dom: $('

'),
            _list: [],
            _cur: 0,

            add: function (src, title, dur, poster_src, pod_id) {
                var track, played, index, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.push(track);
                this._appendElement(el);
                index = this.count() - 1;
                if (track.played) this._markPlayed(index);
                return index;
            },

            insert: function (src, title, dur, poster_src, pod_id) {
                var track, played, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dir, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.shift(track);
                this._insertElement(el);
                if (track.played) this._markPlayed(0);
                this._cur++;
                return 0;
            },

            delete: function (index) {
                var rem, element;
                if (this._list[index]) {
                    element = $(this._dom.find('li')[index]);
                    this._list.splice(index, 1);
                    this._removeElement(element);
                }
                if (index <= this._cur) this._cur--;
            },

            load: function (index) {
                var item, element;
                if (index < 0) index = this.count() - 1;
                if (index >= this.count()) index = 0;
                if (!this.getTrack(index)) {
                    return false;
                }
                this._cur = index;
                item = this.getTrack();
                element = $(this._dom.find('li')[index]);
                item.played = false;
                $(element).removeClass('played');
                $(element).addClass('loaded');
                return item;
            },

            getTrack: function (index) {
                index = (index) ? index : this._cur;
                if (this.count() === 0 || !this._list[index]) {
                    return false;
                }
                return this._list[index];
            },

            hasTrack: function (track_or_src) {
                var src = track_or_src, filter;
                if (typeof(src) !== 'string') {
                    src = src.src;
                }
                filter = this._list.filter(function (track) {
                    return track.src === src;
                })[0];
                return (typeof(filter) !== 'undefined');
            },

            next: function () {
                if (!this._cur || this._cur === this.count() - 1) return -1;
                return this._cur + 1;
            },

            prev: function () {
                if (this._cur === 0) return -1;
                return this._cur - 1;
            },

            finished: function (index) {
                if (!index) index = this._cur;
                if (this._list[index]) {
                    this._list[index].played = true;
                    this._markPlayed(index);
                }
            },

            _markPlayed: function (index) {
                if (this._list[index]) {
                    $(this._list[index].selector).addClass('played');
                }
            },

            count: function () { return this._list.length; },

            _insertElement: function (el) {
                $(this._dom).find('ol').prepend(el);
                el.animate({opacity: 1}, 500);
            },

            _appendElement: function (el) {
                $(this._dom).find('ol').append(el);
                el.animate({opacity: 1}, 500);
            },

            _removeElement: function (el) {
                el.animate({height: '0px', opacity: 0}, 250, function () {
                    el.remove();
                });
            }
        };
        while (list && list.length > 0) {
            PL.insert(list.pop(), false);
        }
        return PL;
    };

}(jQuery, audiojs));<commit_msg>[Fix] Playlist layout<commit_after>(function ($, audiojs) {
    'use strict';

    $.fn.addPlayer = function (preload) {
        if ($(this).length !== 1) {
            throw new Error('Cannot instantiate player on ' +
                            'multiple/null selector');
        }
        if (!$(this).data('js-player')) {
            $(this).data('js-player', new Player($(this), preload));
        }
        return $(this).data('js-player');
    };

    var Track = function (src, title, dur, poster_src, pod_id, last_pos, played) {
        return {
            src: src,
            title: title,
            duration: dur,
            poster: poster_src,
            podcast: Number(pod_id),
            last_pos: (last_pos) ? last_pos : 0,
            played: (played) ? played : false
        };
    };

    var TrackElement = function (track) {
        var el = $('

' +
                   '  ' +
                   '
' + track.duration + '
' +
                   '

' + track.title +
                   '

');
        return el;
    };

    var PlayMode = {
        continue: 0x01,
        repeat: 0x02
    };

    var Player = function (parentElement, preload) {
        if (!preload) preload = {};
        var P = {
            _dom: $('

'),
            _mode: (preload.mode) ? preload.mode : 0,
            header: new Header(),
            audio: new AudioPlayer(),
            playlist: new PlayList(preload.playlist),

            init: function (volume) {
                this.audio.init(this, volume);
                this._dom.find('.player-list ol').click(function (e) {
                    var li = ($(e.target).is('li')) ? $(e.target) :
                            $(e.target).closest('li'),
                        index = li.index();
                    if ($(e.target).hasClass('pl-btn-del')) {
                        P.delete(index);
                    }
                    else {
                        P.load(index);
                    }
                });
            },
            load: function (index) {
                var track = this.playlist.load(index);
                this.audio.load(track);
                this.header.load(track);
            },
            unload: function () {
            },
            add: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.add(
                    src, title, dur, poster_src, pod_id);
                if (i === 0) this.load(i);
            },
            delete: function (index) {
                var c = this.playlist._cur;
                this.playlist.delete(index);
                if (c === index) {
                    this.stop();
                    this.trackFinished();
                }
            },
            addAndPlay: function (src, title, dur, poster_src, pod_id) {
                var i = this.playlist.insert(
                    src, title, dur, poster_src, pod_id);
                this.load(i);
                this.play();
            },
            stop: function () {
                this.pause();
                this.reset();
            },
            play: function () {
                if (this.playlist.getTrack()) this.audio.play();
            },
            pause: function () {
                this.audio.pause();
            },
            trackFinished: function () {
                this.playlist.finished();
                if (this.isMode(PlayMode.continue)) {
                    var t = this.playlist.next();
                    if (t !== -1 || this.isMode(PlayMode.repeat)) {
                        this.nextTrack();
                    }
                }
                else if (this.isMode(PlayMode.repeat)) {
                    this.reset();
                    this.play();
                }
            },
            reset: function () {
                this.audio.reset();
            },
            nextTrack: function () {
                var t = this.playlist.next();
                if (t === -1) t = 0;
                this.load(t);
                this.play();
            },
            prevTrack: function () {
                var t = this.playlist.prev();
                if (t === -1) t = this.playlist.count() - 1;
                this.load(t);
                this.play();
            },
            skipAhead: function (sec) {
                this.audio.skipAhead(sec);
            },
            skipBack: function (sec) {
                this.audio.skipBack(sec);
            },
            addMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode | mode;
            },
            remMode: function (mode) {
                mode = (Number(mode) == mode) ? mode : Modes[mode];
                this._mode = this._mode & ~mode;
            },
            isMode: function () {
                var i;
                if (arguments.length < 1) return false;
                for (i = 0; i < arguments.length; i++) {
                    if (!(this._mode & arguments[i])) return false;
                }
                return true;
            },
            setVolume: function (vol) {
                this.audio.setVolume(vol);
            }
        };
        P._dom.append(P.header._dom);
        P._dom.append(P.audio._dom);
        P._dom.append(P.playlist._dom);
        parentElement.append(P._dom);
        P.init(preload.volume);
        return P;
    };

    var Header = function () {
        var H = {
            _dom: $('

'),

            load: function (track) {
                if (!track) this._unload();
                else {
                }
            },

            _unload: function () {
            }
        };
        return H;
    };

    var AudioPlayer = function () {
        var AP = {
            _dom: $('
<audio preload></audio>
'),
            _audio: false,

            init: function (player, volume) {
                if (!volume) volume = 0.75;
                if ($(this._dom.getUnique()).length < 1) {
                    throw new Error('Cannot initialize AudioPlayer that is ' +
                                    'not attached to the current DOM.');
                }
                this._audio = audiojs.newInstance(this._dom.find('audio')[0], {
                    trackEnded: function () {
                        player.trackFinished();
                    }
                });
                this._dom.find('.skip.flip-glyph').click(function () {
                    player.skipBack(15);
                });
                this._dom.find('.skip.flip-glyph').click(function () {
                    player.skipAhead(30);
                });
                this._dom.find('.volume').click(function () {
                    var time = $(this).closest('.audiojs').find('.time');
                    if (time.find('span').css('display') != 'none') {
                        time.find('span').css('display', 'none');
                        time.find('.slider').css('display', 'block');
                        $(this).css('color', '#ccc');
                    }
                    else {
                        time.find('span').css('display', 'block');
                        time.find('.slider').css('display', 'none');
                        $(this).css('color', '#777');
                    }
                });
                this._dom.find('.slider').slider({
                    range: "min",
                    min: 0,
                    max: 40,
                    value: volume * 40,
                    slide: function (event, ui) {
                        player.setVolume(ui.value/40);
                    }
                });
                player.setVolume(this._dom.find('.slider').slider('value')/40);
            },

            load: function (src) {
                if (!src) this._unload();
                else {
                    if (typeof(src) === 'object') src = src.src;
                    this._audio.load(src);
                }
            },

            _unload: function () {
            },

            play: function () {
                this._audio.play();
            },

            pause: function () {
                this._audio.pause();
            },

            playPause: function () {
                this._audio.playPause();
            },

            skipTo: function (position) {
                position = (position < 0) ? 0 :
                        (position > a.duration) ? a.duration : position;
                var a = this._dom.find('audio')[0],
                    precent = position / a.duration;
                this._audio.skipTo(percent);
            },

            skipAhead: function (seconds) {
                var a = this._dom.find('audio')[0];
                this.skipTo(a.currentTime + seconds);
            },

            skipBack: function (seconds) {
                this.skipAhead(seconds * -1);
            },

            reset: function () {
                this.skipTo(0);
            },

            isPlaying: function () {
                return this._audio.playing;
            },

            getPosition: function () {
                return Math.floor(this._dom.find('audio')[0].currentTime);
            },

            setVolume: function (vol) {
                var el = this._dom.find('.volume');
                if (vol === this._dom.find('audio')[0].volume) return;
                this._audio.setVolume(vol);
                el.find('.glyphicon').css('display', 'none');
                if (vol === 0) {
                    el.find('.glyphicon-volume-off').css('display', 'block');
                }
                else if (vol === 1) {
                    el.find('.glyphicon-volume-up').css('display', 'block');
                }
                else {
                    el.find('.glyphicon-volume-down').css('display', 'block');
                }
            },

        };
        return AP;
    };

    var PlayList = function (list) {
        var PL = {
            _dom: $('

'),
            _list: [],
            _cur: 0,

            add: function (src, title, dur, poster_src, pod_id) {
                var track, played, index, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dur, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.push(track);
                this._appendElement(el);
                index = this.count() - 1;
                if (track.played) this._markPlayed(index);
                return index;
            },

            insert: function (src, title, dur, poster_src, pod_id) {
                var track, played, el;
                if (typeof(src) === 'object') {
                    track = src;
                    autoload = title;
                }
                else {
                    track = new Track(src, title, dir, poster_src, pod_id);
                }
                if (this.hasTrack(track)) {
                    throw new Error('Track already in playlist.');
                }
                el = new TrackElement(track);
                this._list.shift(track);
                this._insertElement(el);
                if (track.played) this._markPlayed(0);
                this._cur++;
                return 0;
            },

            delete: function (index) {
                var rem, element;
                if (this._list[index]) {
                    element = $(this._dom.find('li')[index]);
                    this._list.splice(index, 1);
                    this._removeElement(element);
                }
                if (index <= this._cur) this._cur--;
            },

            load: function (index) {
                var item, element;
                if (index < 0) index = this.count() - 1;
                if (index >= this.count()) index = 0;
                if (!this.getTrack(index)) {
                    return false;
                }
                this._cur = index;
                item = this.getTrack();
                element = $(this._dom.find('li')[index]);
                item.played = false;
                $(element).removeClass('played');
                $(element).addClass('loaded');
                return item;
            },

            getTrack: function (index) {
                index = (index) ? index : this._cur;
                if (this.count() === 0 || !this._list[index]) {
                    return false;
                }
                return this._list[index];
            },

            hasTrack: function (track_or_src) {
                var src = track_or_src, filter;
                if (typeof(src) !== 'string') {
                    src = src.src;
                }
                filter = this._list.filter(function (track) {
                    return track.src === src;
                })[0];
                return (typeof(filter) !== 'undefined');
            },

            next: function () {
                if (!this._cur || this._cur === this.count() - 1) return -1;
                return this._cur + 1;
            },

            prev: function () {
                if (this._cur === 0) return -1;
                return this._cur - 1;
            },

            finished: function (index) {
                if (!index) index = this._cur;
                if (this._list[index]) {
                    this._list[index].played = true;
                    this._markPlayed(index);
                }
            },

            _markPlayed: function (index) {
                if (this._list[index]) {
                    $(this._list[index].selector).addClass('played');
                }
            },

            count: function () { return this._list.length; },

            _insertElement: function (el) {
                $(this._dom).find('ol').prepend(el);
                el.animate({opacity: 1}, 500);
            },

            _appendElement: function (el) {
                $(this._dom).find('ol').append(el);
                el.animate({opacity: 1}, 500);
            },

            _removeElement: function (el) {
                el.animate({height: '0px', opacity: 0}, 250, function () {
                    el.remove();
                });
            }
        };
        while (list && list.length > 0) {
            PL.insert(list.pop(), false);
        }
        return PL;
    };

}(jQuery, audiojs));<|endoftext|>


<commit_before>'use strict';

/*
 * Client entrypoint.
 *
 * This script contains all code necessary to bootstrap Tanura in a client.
 */

/**
 * Information about Nuve.
 */
tanura.nuve = {
    url: tanura.url + 'nuve/',
    room: null
};

/**
 * Information about Erizo.
 */
tanura.erizo = {
    localStream: null
};

/**
 * Information about the whiteboard.
 */
tanura.whiteboard = {
    url: tanura.url + 'whiteboard/',
    canvas: null
};

/**
 * Tanura init routine.
 */
tanura.run = function() {
    /**
     * Fake resize indicator.  Because of CSS bugs and limitations, Tanura will
     * sometimes have to change layout parameters manually in Js. As this
     * happens after the resize, a second resize event may be triggered to give
     * other Js code the ability to react to changes Tanura has made to the
     * layout. In this case, this variable will be set to true, to tell Tanura,
     * that the resize event was a fake resize event triggered by Tanura's own
     * code and should be ignored.
     */
    var fakeResize = false;

    /**
     * Options that should be used to stream.
     */
    var streamOpts = {
        audio: !! tanura.options.audio,
        video: !! tanura.options.video,
        data: true,
        screen: false,
    };

    /**
     * The stream options Tanura will fall back to.  These options will be used
     * when establishing a stream with the default options fails. This is
     * usually due to the user not granting the necessary permissions, the
     * browser not supporting all necessary features, or is blocking some of
     * them due to security concerns.
     */
    var fallbackStreamOpts = {
        audio: false,
        video: false,
        data: true,
        screen: false,
    };

    /**
     * This will initialize the whiteboard on the canvas from a given snapshot.
     */
    var mkCanvas = (_) => {
        tanura.whiteboard.canvas =
            LC.init(document.getElementById('whiteboard'), {snapshot: _});
        tanura
            .whiteboard
            .canvas
            .setTool(new LC.tools.Pencil(tanura.whiteboard.canvas));
        tanura.whiteboard.canvas.on('drawEnd', () =>
                tanura.erizo.localStream.sendData({
                    type: 'canvas-draw',
                    data: tanura.whiteboard.canvas.getSnapshot()
                }));
    }

    var body = document.getElementsByTagName('body')[0];

    // Manually calculate the size of some elements to work around flexbox
    // issues and limitations.
    var f = () => {
        if (!fakeResize) {

            // Wait for transitions.
            setTimeout(() => {
                // Fire the resize-event.
                tanura.eventHandler.fire('client_resized', document);

                // Pass another resize for Literallycanvas.
                fakeResize = true;
                dispatchEvent(new Event('resize'));
            }, 200);
        } else {
            fakeResize = false;
        }
    }
    tanura.throttle('resize', 'throttledResize');
    window.addEventListener('throttledResize', f);
    f();

    // Connect to Tanura.
    var connect = () => {
        tanura.eventHandler.fire('client_loaded', document);

        // Wait for Erizo to load.
        if (typeof Erizo === 'undefined' || Erizo == null) {
            setTimeout(connect, 100);
            return;
        }

        // Prepare a request for a room token.
        var req = new XMLHttpRequest();
        req.addEventListener('load', function() {
            var token = this.responseText;
            tanura.log(token);
            tanura.nuve.room = Erizo.Room({token: token});
            var join = function() {
                // Add a single stream to the DOM.
                var addStream = function(_, o) {
                    if (_.hasVideo()) {
                        var e = document.createElement('div');
                        e.setAttribute('id', 'videoEntry_' + _.getID());
                        document.getElementById('people').appendChild(e);
                        _.show('videoEntry_' + _.getID(), o);
                    }
                };

                // Subscribe to a list of streams.
                var subscribeToStreams = function(l) {
                    for (var i in l) {
                        tanura.nuve.room.subscribe(l[i]);
                        l[i].addEventListener(
                                    'bandwidth-alert',
                                    function(_) {
                                        tanura.log(
                                                'Bandwidth Alert',
                                                _.msg,
                                                _.bandwidth);
                                    });
                        l[i].addEventListener('stream-data', (_) => {
                            switch(_.msg.type) {
                                case 'canvas-clear':
                                    tanura.whiteboard.canvas.clear();
                                    tanura
                                        .eventHandler
                                        .fire(
                                                'whiteboard_cleared',
                                                tanura
                                                    .whiteboard
                                                    .canvas
                                                    .getSnapshot());
                                    break;
                                case 'canvas-draw':
                                    tanura
                                        .whiteboard
                                        .canvas
                                        .loadSnapshot(_.msg.data);
                                    tanura
                                        .eventHandler
                                        .fire('whiteboard_changed', _.msg.data);
                                    break;
                                case 'canvas-init':
                                    if (!tanura.whiteboard.canvas) {
                                        mkCanvas(_.msg.data);
                                        tanura
                                            .eventHandler
                                            .fire(
                                                    'whiteboard_loaded',
                                                    _.msg.data);
                                    }
                                    break;
                                default:
                                    tanura
                                        .eventHandler
                                        .fire('data_received', _.msg.data);
                            }
                        });
                    }
                };

                // This will run as soon as signalling is fully initialized.
                tanura
                    .nuve
                    .room
                    .addEventListener('room-connected', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_opened', tanura.nuve.room);

                        // If we are the only one in the room, add a fresh
                        // whiteboard.
                        if (_.streams.length == 0) {
                            mkCanvas();
                            tanura
                                .eventHandler
                                .fire(
                                        'whiteboard_created',
                                        tanura.whiteboard.canvas.getSnapshot());
                        }

                        // Publish the local stream.
                        tanura
                            .nuve
                            .room
                            .publish(
                                    tanura.erizo.localStream,
                                    {maxVideoBW: 300});
                        subscribeToStreams(_.streams);

                        // Init done, run callback.
                        tanura.eventHandler.fire('client_initialized', document);
                    });

                // This will run whenever the client was successfully subscribed
                // to a new stream.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-subscribed', function(_) {
                        addStream(_.stream);
                    });

                // This will run whenever a new stream was added to the room.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-added', function(_) {
                        // Attach to the new stream.
                        subscribeToStreams([_.stream]);

                        tanura.eventHandler.fire('stream_added', _.stream);

                        // If the new stream is not our own stream.
                        if (
                            tanura.erizo.localStream.getID() != _.stream.getID()
                        ) {
                            // Send the newcomer the current state of the
                            // whiteboard.  This is wasteful as the new client
                            // will get the state by each other client, but
                            // I don't know a better way to do it.  The way
                            // forward will likely be to have the server control
                            // the whiteboard.
                            tanura.erizo.localStream.sendData({
                                type: 'canvas-init',
                                data: tanura.whiteboard.canvas.getSnapshot()
                            });
                        }
                    });

                // This will run whenever a stream fails.
                tanura.nuve.room.addEventListener('stream-failed', function(_) {
                    tanura.eventHandler.fire('stream_failed', _.stream);
                });

                // This will run whenever a stream disappeared from the room.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-removed', function(_) {
                        tanura.eventHandler.fire('stream_removed', _.stream);
                        document
                            .getElementById('videoEntry_' + _.stream.getID())
                            .remove();
                    });

                // This will run whenever a streamer cannot keep up the
                // bandwidth the room requires.
                tanura
                    .nuve
                    .room
                    .addEventListener('bandwidth-alert', function(_) {
                        tanura.eventHandler.fire('stream_throttled', _.stream);
                    });

                // This will run if connecting to the room has failed.
                tanura.nuve.room.addEventListener('room-error', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_failed', tanura.nuve.room);
                    });

                // This will run when the room is disconnected.
                tanura
                    .nuve
                    .room
                    .addEventListener('room-disconnected', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_closed', tanura.nuve.room);
                    });

                // All set. Connect to the room.
                tanura.nuve.room.connect();

                // Don't attach to the local stream. For development purposes it
                // is nicer if the users own stream is attached remotely too.
                //addStream(tanura.erizo.localStream, {speaker: false});
            }

            // Get local media.
            tanura.erizo.localStream = Erizo.Stream(streamOpts);
            tanura
                .erizo
                .localStream
                .addEventListener('access-accepted', function(_) {
                    tanura.eventHandler.fire('media_granted_preferred', streamOpts);
                    join();
                });
            tanura
                .erizo
                .localStream
                .addEventListener('access-denied', function(_) {
                    tanura.eventHandler.fire('media_denied_preferred', streamOpts);
                    tanura.erizo.localStream.close();
                    tanura.erizo.localStream = Erizo.Stream(fallbackStreamOpts);
                    tanura
                        .erizo
                        .localStream
                        .addEventListener('access-accepted', function(_) {
                            tanura
                                .eventHandler
                                .fire(
                                        'media_granted_fallback',
                                        fallbackStreamOpts);
                            join();
                        });
                    tanura
                        .erizo
                        .localStream
                        .addEventListener('access-denied', function(_) {
                            tanura
                                .eventHandler
                                .fire(
                                        'media_denied_fallback',
                                        fallbackStreamOpts);
                        });
                    tanura.erizo.localStream.init();
                });
            tanura.erizo.localStream.init();
        });
        req.open('POST', tanura.nuve.url + 'createToken/', true);
        req.setRequestHeader('Content-Type', 'application/json');

        // Send the room-token request.
        req.send(JSON.stringify({username: 'user', role: 'presenter'}));
    }

    connect();
}

// Run, if initialisation is done already.
if (tanura.options) { tanura.run(); }
<commit_msg>Fire tanura.eventHandler.events.whiteboard_edited[].<commit_after>'use strict';

/*
 * Client entrypoint.
 *
 * This script contains all code necessary to bootstrap Tanura in a client.
 */

/**
 * Information about Nuve.
 */
tanura.nuve = {
    url: tanura.url + 'nuve/',
    room: null
};

/**
 * Information about Erizo.
 */
tanura.erizo = {
    localStream: null
};

/**
 * Information about the whiteboard.
 */
tanura.whiteboard = {
    url: tanura.url + 'whiteboard/',
    canvas: null
};

/**
 * Tanura init routine.
 */
tanura.run = function() {
    /**
     * Fake resize indicator.  Because of CSS bugs and limitations, Tanura will
     * sometimes have to change layout parameters manually in Js. As this
     * happens after the resize, a second resize event may be triggered to give
     * other Js code the ability to react to changes Tanura has made to the
     * layout. In this case, this variable will be set to true, to tell Tanura,
     * that the resize event was a fake resize event triggered by Tanura's own
     * code and should be ignored.
     */
    var fakeResize = false;

    /**
     * Options that should be used to stream.
     */
    var streamOpts = {
        audio: !! tanura.options.audio,
        video: !! tanura.options.video,
        data: true,
        screen: false,
    };

    /**
     * The stream options Tanura will fall back to.  These options will be used
     * when establishing a stream with the default options fails. This is
     * usually due to the user not granting the necessary permissions, the
     * browser not supporting all necessary features, or is blocking some of
     * them due to security concerns.
     */
    var fallbackStreamOpts = {
        audio: false,
        video: false,
        data: true,
        screen: false,
    };

    /**
     * This will initialize the whiteboard on the canvas from a given snapshot.
     */
    var mkCanvas = (_) => {
        tanura.whiteboard.canvas =
            LC.init(document.getElementById('whiteboard'), {snapshot: _});
        tanura
            .whiteboard
            .canvas
            .setTool(new LC.tools.Pencil(tanura.whiteboard.canvas));
        tanura.whiteboard.canvas.on('drawEnd', () => {
            tanura.erizo.localStream.sendData({
                type: 'canvas-draw',
                data: tanura.whiteboard.canvas.getSnapshot()
            });
            tanura
                .eventHandler
                .fire(
                        'whiteboard_edited',
                        tanura.whiteboard.canvas.getSnapshot());
        });
    }

    var body = document.getElementsByTagName('body')[0];

    // Manually calculate the size of some elements to work around flexbox
    // issues and limitations.
    var f = () => {
        if (!fakeResize) {

            // Wait for transitions.
            setTimeout(() => {
                // Fire the resize-event.
                tanura.eventHandler.fire('client_resized', document);

                // Pass another resize for Literallycanvas.
                fakeResize = true;
                dispatchEvent(new Event('resize'));
            }, 200);
        } else {
            fakeResize = false;
        }
    }
    tanura.throttle('resize', 'throttledResize');
    window.addEventListener('throttledResize', f);
    f();

    // Connect to Tanura.
    var connect = () => {
        tanura.eventHandler.fire('client_loaded', document);

        // Wait for Erizo to load.
        if (typeof Erizo === 'undefined' || Erizo == null) {
            setTimeout(connect, 100);
            return;
        }

        // Prepare a request for a room token.
        var req = new XMLHttpRequest();
        req.addEventListener('load', function() {
            var token = this.responseText;
            tanura.log(token);
            tanura.nuve.room = Erizo.Room({token: token});
            var join = function() {
                // Add a single stream to the DOM.
                var addStream = function(_, o) {
                    if (_.hasVideo()) {
                        var e = document.createElement('div');
                        e.setAttribute('id', 'videoEntry_' + _.getID());
                        document.getElementById('people').appendChild(e);
                        _.show('videoEntry_' + _.getID(), o);
                    }
                };

                // Subscribe to a list of streams.
                var subscribeToStreams = function(l) {
                    for (var i in l) {
                        tanura.nuve.room.subscribe(l[i]);
                        l[i].addEventListener(
                                    'bandwidth-alert',
                                    function(_) {
                                        tanura.log(
                                                'Bandwidth Alert',
                                                _.msg,
                                                _.bandwidth);
                                    });
                        l[i].addEventListener('stream-data', (_) => {
                            switch(_.msg.type) {
                                case 'canvas-clear':
                                    tanura.whiteboard.canvas.clear();
                                    tanura
                                        .eventHandler
                                        .fire(
                                                'whiteboard_cleared',
                                                tanura
                                                    .whiteboard
                                                    .canvas
                                                    .getSnapshot());
                                    break;
                                case 'canvas-draw':
                                    tanura
                                        .whiteboard
                                        .canvas
                                        .loadSnapshot(_.msg.data);
                                    tanura
                                        .eventHandler
                                        .fire('whiteboard_changed', _.msg.data);
                                    break;
                                case 'canvas-init':
                                    if (!tanura.whiteboard.canvas) {
                                        mkCanvas(_.msg.data);
                                        tanura
                                            .eventHandler
                                            .fire(
                                                    'whiteboard_loaded',
                                                    _.msg.data);
                                    }
                                    break;
                                default:
                                    tanura
                                        .eventHandler
                                        .fire('data_received', _.msg.data);
                            }
                        });
                    }
                };

                // This will run as soon as signalling is fully initialized.
                tanura
                    .nuve
                    .room
                    .addEventListener('room-connected', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_opened', tanura.nuve.room);

                        // If we are the only one in the room, add a fresh
                        // whiteboard.
                        if (_.streams.length == 0) {
                            mkCanvas();
                            tanura
                                .eventHandler
                                .fire(
                                        'whiteboard_created',
                                        tanura.whiteboard.canvas.getSnapshot());
                        }

                        // Publish the local stream.
                        tanura
                            .nuve
                            .room
                            .publish(
                                    tanura.erizo.localStream,
                                    {maxVideoBW: 300});
                        subscribeToStreams(_.streams);

                        // Init done, run callback.
                        tanura.eventHandler.fire('client_initialized', document);
                    });

                // This will run whenever the client was successfully subscribed
                // to a new stream.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-subscribed', function(_) {
                        addStream(_.stream);
                    });

                // This will run whenever a new stream was added to the room.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-added', function(_) {
                        // Attach to the new stream.
                        subscribeToStreams([_.stream]);

                        tanura.eventHandler.fire('stream_added', _.stream);

                        // If the new stream is not our own stream.
                        if (
                            tanura.erizo.localStream.getID() != _.stream.getID()
                        ) {
                            // Send the newcomer the current state of the
                            // whiteboard.  This is wasteful as the new client
                            // will get the state by each other client, but
                            // I don't know a better way to do it.  The way
                            // forward will likely be to have the server control
                            // the whiteboard.
                            tanura.erizo.localStream.sendData({
                                type: 'canvas-init',
                                data: tanura.whiteboard.canvas.getSnapshot()
                            });
                        }
                    });

                // This will run whenever a stream fails.
                tanura.nuve.room.addEventListener('stream-failed', function(_) {
                    tanura.eventHandler.fire('stream_failed', _.stream);
                });

                // This will run whenever a stream disappeared from the room.
                tanura
                    .nuve
                    .room
                    .addEventListener('stream-removed', function(_) {
                        tanura.eventHandler.fire('stream_removed', _.stream);
                        document
                            .getElementById('videoEntry_' + _.stream.getID())
                            .remove();
                    });

                // This will run whenever a streamer cannot keep up the
                // bandwidth the room requires.
                tanura
                    .nuve
                    .room
                    .addEventListener('bandwidth-alert', function(_) {
                        tanura.eventHandler.fire('stream_throttled', _.stream);
                    });

                // This will run if connecting to the room has failed.
                tanura.nuve.room.addEventListener('room-error', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_failed', tanura.nuve.room);
                    });

                // This will run when the room is disconnected.
                tanura
                    .nuve
                    .room
                    .addEventListener('room-disconnected', function(_) {
                        tanura
                            .eventHandler
                            .fire('connection_closed', tanura.nuve.room);
                    });

                // All set. Connect to the room.
                tanura.nuve.room.connect();

                // Don't attach to the local stream. For development purposes it
                // is nicer if the users own stream is attached remotely too.
                //addStream(tanura.erizo.localStream, {speaker: false});
            }

            // Get local media.
            tanura.erizo.localStream = Erizo.Stream(streamOpts);
            tanura
                .erizo
                .localStream
                .addEventListener('access-accepted', function(_) {
                    tanura.eventHandler.fire('media_granted_preferred', streamOpts);
                    join();
                });
            tanura
                .erizo
                .localStream
                .addEventListener('access-denied', function(_) {
                    tanura.eventHandler.fire('media_denied_preferred', streamOpts);
                    tanura.erizo.localStream.close();
                    tanura.erizo.localStream = Erizo.Stream(fallbackStreamOpts);
                    tanura
                        .erizo
                        .localStream
                        .addEventListener('access-accepted', function(_) {
                            tanura
                                .eventHandler
                                .fire(
                                        'media_granted_fallback',
                                        fallbackStreamOpts);
                            join();
                        });
                    tanura
                        .erizo
                        .localStream
                        .addEventListener('access-denied', function(_) {
                            tanura
                                .eventHandler
                                .fire(
                                        'media_denied_fallback',
                                        fallbackStreamOpts);
                        });
                    tanura.erizo.localStream.init();
                });
            tanura.erizo.localStream.init();
        });
        req.open('POST', tanura.nuve.url + 'createToken/', true);
        req.setRequestHeader('Content-Type', 'application/json');

        // Send the room-token request.
        req.send(JSON.stringify({username: 'user', role: 'presenter'}));
    }

    connect();
}

// Run, if initialisation is done already.
if (tanura.options) { tanura.run(); }
<|endoftext|>