<commit_before>/*jslint browser: true, for: true, node: true */
/*global window */

/*!

   hlsjs engine plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes hls.js
   Copyright (c) 2015 Dailymotion (http://www.dailymotion.com)
   https://github.com/dailymotion/hls.js/blob/master/LICENSE

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - hls.js https://github.com/dailymotion/hls.js
   revision: $GIT_ID$

*/

(function (flowplayer, Hls) {
    "use strict";
    var engineName = "hlsjs",
        hlsconf,
        common = flowplayer.common,
        extend = flowplayer.extend,
        support = flowplayer.support,
        version = flowplayer.version,

        isHlsType = function (typ) {
            return typ.toLowerCase().indexOf("mpegurl") > -1;
        },
        hlsQualitiesSupport = function (conf) {
            var hlsQualities = conf.clip.hlsQualities || conf.hlsQualities;

            return support.inlineVideo &&
                    (hlsQualities === true ||
                    (hlsQualities && hlsQualities.length));
        },

        engineImpl = function hlsjsEngine(player, root) {
            var bean = flowplayer.bean,
                videoTag,
                hls,
                recover,

                // pre 6.0.4 poster detection
                bc,
                has_bg,

                getStartLevelConf = function () {
                    var value = hlsconf.startLevel;

                    switch (value) {
                    case "auto":
                        value = -1;
                        break;
                    case "firstLevel":
                        value = undefined;
                        break;
                    }
                    return value;
                },

                disableAutoLevel = 0,
                setStickyLevel = function () {
                    hls.nextLevel = hls[disableAutoLevel];
                    disableAutoLevel = 0;
                },

                qActive = "active",
                dataQuality = function (quality) {
                    // e.g. "Level 1" -> "level1"
                    if (!quality) {
                        quality = player.quality;
                    }
                    return quality.toLowerCase().replace(/\ /g, "");
                },
                removeAllQualityClasses = function () {
                    var qualities = player.qualities;

                    if (!qualities || !qualities.length) {
                        return;
                    }
                    common.removeClass(root, "quality-abr");
                    qualities.forEach(function (quality) {
                        common.removeClass(root, "quality-" + dataQuality(quality));
                    });
                },
                qClean = function () {
                    delete player.hlsQualities;
                    removeAllQualityClasses();
                    common.find(".fp-quality-selector", root).forEach(common.removeNode);
                },
                qIndex = function () {
                    return player.hlsQualities[player.qualities.indexOf(player.quality) + 1];
                },
                initQualitySelection = function (hlsQualitiesConf, data) {
                    var levels = data.levels,
                        levelIndex = 0,
                        selector;

                    qClean();
                    player.hlsQualities = [];
                    levels.forEach(function (level) {
                        if ((hlsQualitiesConf === true || hlsQualitiesConf.indexOf(levelIndex) > -1) &&
                                level.videoCodec &&
                                window.MediaSource.isTypeSupported('video/mp4;codecs=' + level.videoCodec)) {
                            // do not check audioCodec,
                            // as e.g. HE_AAC is decoded as LC_AAC by hls.js on Android
                            player.hlsQualities.push(levelIndex);
                        }
                        levelIndex += 1;
                    });
                    if (player.hlsQualities.length < 2) {
                        return;
                    }

                    player.qualities = [];
                    player.hlsQualities.forEach(function (levelIndex) {
                        var level = levels[levelIndex],
                            width = level.width,
                            height = level.height,
                            label = (width && height)
                                ? Math.min(width, height) + "p"
                                : "Level " + (levelIndex + 1);

                        player.qualities.push(label);
                    });

                    selector = common.createElement("ul", {
                        "class": "fp-quality-selector"
                    });
                    common.find(".fp-ui", root)[0].appendChild(selector);

                    player.hlsQualities.unshift(-1);

                    if (!player.quality || player.qualities.indexOf(player.quality) < 0) {
                        hls.startLevel = getStartLevelConf();
                        player.quality = "abr";
                    } else {
                        hls.startLevel = qIndex();
                        disableAutoLevel = "startLevel";
                    }

                    selector.appendChild(common.createElement("li", {
                        "data-quality": "abr"
                    }, "Auto"));
                    player.qualities.forEach(function (q) {
                        selector.appendChild(common.createElement("li", {
                            "data-quality": dataQuality(q)
                        }, q));
                    });

                    common.addClass(root, "quality-" + dataQuality());

                    bean.on(root, "click." + engineName, ".fp-quality-selector li", function (e) {
                        var choice = e.currentTarget,
                            selectors,
                            active,
                            paused,
                            i;

                        if (common.hasClass(choice, qActive)) {
                            return;
                        }

                        selectors = common.find(".fp-quality-selector li", root);

                        for (i = 0; i < selectors.length; i += 1) {
                            active = selectors[i] === choice;
                            if (active) {
                                player.quality = i > 0
                                    ? player.qualities[i - 1]
                                    : "abr";
                                paused = player.paused;
                                if (paused) {
                                    player.resume();
                                }
                                if (hlsconf.smoothSwitching && !paused) {
                                    hls.nextLevel = qIndex();
                                } else {
                                    hls.currentLevel = qIndex();
                                }
                                common.addClass(choice, qActive);
                            }
                            common.toggleClass(selectors[i], qActive, active);
                        }
                        removeAllQualityClasses();
                        common.addClass(root, "quality-" + dataQuality());
                    });
                },

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isHlsType(source.type)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !hls,
                            conf = player.conf,
                            EVENTS = {
                                ended: "finish",
                                loadeddata: "ready",
                                pause: "pause",
                                play: "resume",
                                progress: "buffer",
                                ratechange: "speed",
                                seeked: "seek",
                                timeupdate: "progress",
                                volumechange: "volume"
                            },
                            autoplay = !!video.autoplay || conf.autoplay,
                            posterClass = "is-poster",
                            hlsQualitiesConf = video.hlsQualities || conf.hlsQualities,
                            hlsClientConf = extend({}, hlsconf),
                            hlsParams = [
                                "autoLevelCapping", "startLevel",
                                "adaptOnStartOnly", "smoothSwitching",
                                "anamorphic", "recover", "strict"
                            ];

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                className: "fp-engine " + engineName + "-engine",
                                autoplay: autoplay
                                    ? "autoplay"
                                    : false
                            });
                            videoTag.setAttribute("x-webkit-airplay", "allow");

                        } else {
                            hls.destroy();
                        }

                        Object.keys(EVENTS).forEach(function (key) {
                            var flow = EVENTS[key],
                                type = key + "." + engineName,
                                arg;

                            bean.on(videoTag, type, function (e) {
                                if (conf.debug && flow.indexOf("progress") < 0) {
                                    console.log(type, "->", flow, e.originalEvent);
                                }
                                if (!player.ready && flow.indexOf("ready") < 0) {
                                    return;
                                }

                                var ct,
                                    buffered,
                                    buffer = 0,
                                    buffend = 0,
                                    i,
                                    quality = player.quality,
                                    selectorIndex;

                                switch (flow) {
                                case "ready":
                                    arg = extend(video, {
                                        duration: videoTag.duration,
                                        seekable: videoTag.seekable.end(null),
                                        width: videoTag.videoWidth,
                                        height: videoTag.videoHeight,
                                        url: videoTag.currentSrc
                                    });
                                    break;
                                case "resume":
                                    if (player.poster) {
                                        player.poster = false;
                                        common.removeClass(root, posterClass);
                                    }
                                    break;
                                case "seek":
                                case "progress":
                                    arg = videoTag.currentTime;
                                    break;
                                case "speed":
                                    arg = videoTag.playbackRate;
                                    break;
                                case "volume":
                                    arg = videoTag.volume;
                                    break;
                                case "buffer":
                                    try {
                                        ct = videoTag.currentTime;
                                        if (ct) {
                                            // cycle through time ranges to obtain buffer
                                            // nearest current time
                                            buffered = videoTag.buffered;
                                            for (i = buffered.length - 1; i > -1; i -= 1) {
                                                buffend = buffered.end(i);
                                                if (buffend >= ct) {
                                                    buffer = buffend;
                                                }
                                            }
                                        }
                                    } catch (ignore) {}
                                    video.buffer = buffer;
                                    arg = e;
                                    break;
                                }

                                player.trigger(flow, [player, arg]);

                                if (flow === "ready" && quality) {
                                    selectorIndex = quality === "abr"
                                        ? 0
                                        : player.qualities.indexOf(quality) + 1;
                                    common.addClass(common.find(".fp-quality-selector li", root)[selectorIndex],
                                            qActive);
                                }
                            });
                        });

                        if (conf.poster) {
                            // engine too late, poster already removed
                            // abuse timeupdate to re-instate poster
                            player.on("stop." + engineName, function () {
                                bean.one(videoTag, "timeupdate." + engineName, function () {
                                    player.poster = true;
                                    common.addClass(root, posterClass);
                                });
                            });
                        }

                        player.on("error." + engineName, function () {
                            if (hls) {
                                hls.destroy();
                                hls = 0;
                            }
                        });

                        hlsParams.forEach(function (key) {
                            var value = hlsconf[key];

                            delete hlsClientConf[key];

                            switch (key) {
                            case "adaptOnStartOnly":
                                if (value) {
                                    hlsClientConf.startLevel = -1;
                                    disableAutoLevel = "currentLevel";
                                }
                                break;
                            case "autoLevelCapping":
                                if (value === false) {
                                    value = -1;
                                }
                                break;
                            case "recover":
                                recover = hlsconf.strict
                                    ? 0
                                    : value;
                                break;
                            case "startLevel":
                                value = (player.hlsQualities && player.quality)
                                    ? qIndex()
                                    : getStartLevelConf();
                                break;
                            }

                            if ((key === "autoLevelCapping" || key === "startLevel") && value !== undefined) {
                                hlsClientConf[key] = value;
                            }
                        });

                        hlsClientConf.autoStartLoad = false;

                        hls = new Hls(hlsClientConf);
                        player.engine[engineName] = hls;

                        Object.keys(Hls.Events).forEach(function (key) {
                            hls.on(Hls.Events[key], function (etype, data) {
                                var fperr,
                                    errobj = {};

                                switch (key) {
                                case "MANIFEST_PARSED":
                                    if (hlsQualitiesSupport(conf)) {
                                        initQualitySelection(hlsQualitiesConf, data);
                                    } else {
                                        delete player.quality;
                                    }
                                    hls.startLoad();
                                    break;

                                case "FRAG_LOADED":
                                    if (disableAutoLevel === "startLevel") {
                                        // qsel
                                        setStickyLevel();
                                    }
                                    break;

                                case "FRAG_CHANGED":
                                    if (disableAutoLevel === "currentLevel" && player.ready) {
                                        // adaptOnStartOnly
                                        setStickyLevel();
                                    }
                                    break;

                                case "ERROR":
                                    if (data.fatal || hlsconf.strict > 0) {
                                        switch (data.type) {
                                        case Hls.ErrorTypes.NETWORK_ERROR:
                                            if (recover) {
                                                hls.startLoad();
                                                if (recover > 0) {
                                                    recover -= 1;
                                                }
                                            } else if (data.frag && data.frag.url) {
                                                errobj.url = data.frag.url;
                                                fperr = 2;
                                            } else {
                                                fperr = 4;
                                            }
                                            break;
                                        case Hls.ErrorTypes.MEDIA_ERROR:
                                            if (recover) {
                                                hls.recoverMediaError();
                                                if (recover > 0) {
                                                    recover -= 1;
                                                }
                                            } else {
                                                fperr = 3;
                                            }
                                            break;
                                        default:
                                            fperr = 5;
                                        }

                                        if (fperr !== undefined) {
                                            errobj.code = fperr;
                                            if (fperr > 2) {
                                                errobj.video = extend(video, {
                                                    src: video.src,
                                                    url: data.url || video.src
                                                });
                                            }
                                            player.trigger("error", [player, errobj]);
                                        }
                                    }
                                    // log non fatals?
                                    break;
                                }

                                player.trigger(etype, [player, data]);
                            });
                        });

                        if (init) {
                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                        }

                        hls.attachMedia(videoTag);
                        hls.loadSource(video.src);

                        if (videoTag.paused && autoplay) {
                            videoTag.play();
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        if (hls) {
                            bean.off(root, "." + engineName);
                            player.off("." + engineName);
                            qClean();
                            hls.destroy();
                            hls = 0;
                            bean.off(videoTag, "." + engineName);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            // pre 6.0.4: no boolean api.conf.poster and no poster with autoplay
            if (/^6\.0\.[0-3]$/.test(version) &&
                    !player.conf.splash && !player.conf.poster && !player.conf.autoplay) {
                bc = common.css(root, 'backgroundColor');
                // spaces in rgba arg mandatory for recognition
                has_bg = common.css(root, 'backgroundImage') !== "none" ||
                        (bc && bc !== "rgba(0, 0, 0, 0)" && bc !== "transparent");
                if (has_bg) {
                    player.conf.poster = true;
                }
            }

            return engine;
        };

    if (Hls.isSupported() && version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            var b = support.browser,
                wn = window.navigator,
                IE11 = wn.userAgent.indexOf("Trident/7") > -1;

            if (conf[engineName] === false || conf.clip[engineName] === false) {
                // engine disabled for player or clip
                return false;
            }

            // merge hlsjs clip config at earliest opportunity
            hlsconf = extend({
                smoothSwitching: true,
                recover: 0
            }, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            if (isHlsType(type)) {
                // allow all browsers for hlsjs debugging
                if (hlsconf.debug) {
                    return true;
                }
                // https://bugzilla.mozilla.org/show_bug.cgi?id=1244294
                if (hlsconf.anamorphic &&
                        wn.platform.indexOf("Win") === 0 && b.mozilla && b.version.indexOf("44.") === 0) {
                    return false;
                }

                // https://github.com/dailymotion/hls.js/issues/9
                return IE11 || !b.safari;
            }
            return false;
        };

        // put on top of engine stack
        // so hlsjs is tested before html5 video hls and flash hls
        flowplayer.engines.unshift(engineImpl);

        flowplayer(function (api) {
            // to take precedence over VOD quality selector
            api.pluginQualitySelectorEnabled = hlsQualitiesSupport(api.conf) &&
                    engineImpl.canPlay("application/x-mpegurl", api.conf);
        });

    }

}.apply(null, (typeof module === 'object' && module.exports)
    ? [require('flowplayer'), require('hls.js')]
    : [window.flowplayer, window.Hls]));
<commit_msg>Make video tag creation code more consistent<commit_after>/*jslint browser: true, for: true, node: true */
/*global window */

/*!

   hlsjs engine plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes hls.js
   Copyright (c) 2015 Dailymotion (http://www.dailymotion.com)
   https://github.com/dailymotion/hls.js/blob/master/LICENSE

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - hls.js https://github.com/dailymotion/hls.js
   revision: $GIT_ID$

*/

(function (flowplayer, Hls) {
    "use strict";
    var engineName = "hlsjs",
        hlsconf,
        common = flowplayer.common,
        extend = flowplayer.extend,
        support = flowplayer.support,
        version = flowplayer.version,

        isHlsType = function (typ) {
            return typ.toLowerCase().indexOf("mpegurl") > -1;
        },
        hlsQualitiesSupport = function (conf) {
            var hlsQualities = conf.clip.hlsQualities || conf.hlsQualities;

            return support.inlineVideo &&
                    (hlsQualities === true ||
                    (hlsQualities && hlsQualities.length));
        },

        engineImpl = function hlsjsEngine(player, root) {
            var bean = flowplayer.bean,
                videoTag,
                hls,
                recover,

                // pre 6.0.4 poster detection
                bc,
                has_bg,

                getStartLevelConf = function () {
                    var value = hlsconf.startLevel;

                    switch (value) {
                    case "auto":
                        value = -1;
                        break;
                    case "firstLevel":
                        value = undefined;
                        break;
                    }
                    return value;
                },

                disableAutoLevel = 0,
                setStickyLevel = function () {
                    hls.nextLevel = hls[disableAutoLevel];
                    disableAutoLevel = 0;
                },

                qActive = "active",
                dataQuality = function (quality) {
                    // e.g. "Level 1" -> "level1"
                    if (!quality) {
                        quality = player.quality;
                    }
                    return quality.toLowerCase().replace(/\ /g, "");
                },
                removeAllQualityClasses = function () {
                    var qualities = player.qualities;

                    if (!qualities || !qualities.length) {
                        return;
                    }
                    common.removeClass(root, "quality-abr");
                    qualities.forEach(function (quality) {
                        common.removeClass(root, "quality-" + dataQuality(quality));
                    });
                },
                qClean = function () {
                    delete player.hlsQualities;
                    removeAllQualityClasses();
                    common.find(".fp-quality-selector", root).forEach(common.removeNode);
                },
                qIndex = function () {
                    return player.hlsQualities[player.qualities.indexOf(player.quality) + 1];
                },
                initQualitySelection = function (hlsQualitiesConf, data) {
                    var levels = data.levels,
                        levelIndex = 0,
                        selector;

                    qClean();
                    player.hlsQualities = [];
                    levels.forEach(function (level) {
                        if ((hlsQualitiesConf === true || hlsQualitiesConf.indexOf(levelIndex) > -1) &&
                                level.videoCodec &&
                                window.MediaSource.isTypeSupported('video/mp4;codecs=' + level.videoCodec)) {
                            // do not check audioCodec,
                            // as e.g. HE_AAC is decoded as LC_AAC by hls.js on Android
                            player.hlsQualities.push(levelIndex);
                        }
                        levelIndex += 1;
                    });
                    if (player.hlsQualities.length < 2) {
                        return;
                    }

                    player.qualities = [];
                    player.hlsQualities.forEach(function (levelIndex) {
                        var level = levels[levelIndex],
                            width = level.width,
                            height = level.height,
                            label = (width && height)
                                ? Math.min(width, height) + "p"
                                : "Level " + (levelIndex + 1);

                        player.qualities.push(label);
                    });

                    selector = common.createElement("ul", {
                        "class": "fp-quality-selector"
                    });
                    common.find(".fp-ui", root)[0].appendChild(selector);

                    player.hlsQualities.unshift(-1);

                    if (!player.quality || player.qualities.indexOf(player.quality) < 0) {
                        hls.startLevel = getStartLevelConf();
                        player.quality = "abr";
                    } else {
                        hls.startLevel = qIndex();
                        disableAutoLevel = "startLevel";
                    }

                    selector.appendChild(common.createElement("li", {
                        "data-quality": "abr"
                    }, "Auto"));
                    player.qualities.forEach(function (q) {
                        selector.appendChild(common.createElement("li", {
                            "data-quality": dataQuality(q)
                        }, q));
                    });

                    common.addClass(root, "quality-" + dataQuality());

                    bean.on(root, "click." + engineName, ".fp-quality-selector li", function (e) {
                        var choice = e.currentTarget,
                            selectors,
                            active,
                            paused,
                            i;

                        if (common.hasClass(choice, qActive)) {
                            return;
                        }

                        selectors = common.find(".fp-quality-selector li", root);

                        for (i = 0; i < selectors.length; i += 1) {
                            active = selectors[i] === choice;
                            if (active) {
                                player.quality = i > 0
                                    ? player.qualities[i - 1]
                                    : "abr";
                                paused = player.paused;
                                if (paused) {
                                    player.resume();
                                }
                                if (hlsconf.smoothSwitching && !paused) {
                                    hls.nextLevel = qIndex();
                                } else {
                                    hls.currentLevel = qIndex();
                                }
                                common.addClass(choice, qActive);
                            }
                            common.toggleClass(selectors[i], qActive, active);
                        }
                        removeAllQualityClasses();
                        common.addClass(root, "quality-" + dataQuality());
                    });
                },

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isHlsType(source.type)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !hls,
                            conf = player.conf,
                            EVENTS = {
                                ended: "finish",
                                loadeddata: "ready",
                                pause: "pause",
                                play: "resume",
                                progress: "buffer",
                                ratechange: "speed",
                                seeked: "seek",
                                timeupdate: "progress",
                                volumechange: "volume"
                            },
                            autoplay = !!video.autoplay || !!conf.autoplay,
                            posterClass = "is-poster",
                            hlsQualitiesConf = video.hlsQualities || conf.hlsQualities,
                            hlsClientConf = extend({}, hlsconf),
                            hlsParams = [
                                "autoLevelCapping", "startLevel",
                                "adaptOnStartOnly", "smoothSwitching",
                                "anamorphic", "recover", "strict"
                            ];

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                "class": "fp-engine " + engineName + "-engine",
                                "autoplay": autoplay
                                    ? "autoplay"
                                    : false,
                                "preload": conf.clip.preload || "metadata",
                                "x-webkit-airplay": "allow"
                            });

                        } else {
                            hls.destroy();
                        }

                        Object.keys(EVENTS).forEach(function (key) {
                            var flow = EVENTS[key],
                                type = key + "." + engineName,
                                arg;

                            bean.on(videoTag, type, function (e) {
                                if (conf.debug && flow.indexOf("progress") < 0) {
                                    console.log(type, "->", flow, e.originalEvent);
                                }
                                if (!player.ready && flow.indexOf("ready") < 0) {
                                    return;
                                }

                                var ct,
                                    buffered,
                                    buffer = 0,
                                    buffend = 0,
                                    i,
                                    quality = player.quality,
                                    selectorIndex;

                                switch (flow) {
                                case "ready":
                                    arg = extend(video, {
                                        duration: videoTag.duration,
                                        seekable: videoTag.seekable.end(null),
                                        width: videoTag.videoWidth,
                                        height: videoTag.videoHeight,
                                        url: videoTag.currentSrc
                                    });
                                    break;
                                case "resume":
                                    if (player.poster) {
                                        player.poster = false;
                                        common.removeClass(root, posterClass);
                                    }
                                    break;
                                case "seek":
                                case "progress":
                                    arg = videoTag.currentTime;
                                    break;
                                case "speed":
                                    arg = videoTag.playbackRate;
                                    break;
                                case "volume":
                                    arg = videoTag.volume;
                                    break;
                                case "buffer":
                                    try {
                                        ct = videoTag.currentTime;
                                        if (ct) {
                                            // cycle through time ranges to obtain buffer
                                            // nearest current time
                                            buffered = videoTag.buffered;
                                            for (i = buffered.length - 1; i > -1; i -= 1) {
                                                buffend = buffered.end(i);
                                                if (buffend >= ct) {
                                                    buffer = buffend;
                                                }
                                            }
                                        }
                                    } catch (ignore) {}
                                    video.buffer = buffer;
                                    arg = e;
                                    break;
                                }

                                player.trigger(flow, [player, arg]);

                                if (flow === "ready" && quality) {
                                    selectorIndex = quality === "abr"
                                        ? 0
                                        : player.qualities.indexOf(quality) + 1;
                                    common.addClass(common.find(".fp-quality-selector li", root)[selectorIndex],
                                            qActive);
                                }
                            });
                        });

                        if (conf.poster) {
                            // engine too late, poster already removed
                            // abuse timeupdate to re-instate poster
                            player.on("stop." + engineName, function () {
                                bean.one(videoTag, "timeupdate." + engineName, function () {
                                    player.poster = true;
                                    common.addClass(root, posterClass);
                                });
                            });
                        }

                        player.on("error." + engineName, function () {
                            if (hls) {
                                hls.destroy();
                                hls = 0;
                            }
                        });

                        hlsParams.forEach(function (key) {
                            var value = hlsconf[key];

                            delete hlsClientConf[key];

                            switch (key) {
                            case "adaptOnStartOnly":
                                if (value) {
                                    hlsClientConf.startLevel = -1;
                                    disableAutoLevel = "currentLevel";
                                }
                                break;
                            case "autoLevelCapping":
                                if (value === false) {
                                    value = -1;
                                }
                                break;
                            case "recover":
                                recover = hlsconf.strict
                                    ? 0
                                    : value;
                                break;
                            case "startLevel":
                                value = (player.hlsQualities && player.quality)
                                    ? qIndex()
                                    : getStartLevelConf();
                                break;
                            }

                            if ((key === "autoLevelCapping" || key === "startLevel") && value !== undefined) {
                                hlsClientConf[key] = value;
                            }
                        });

                        hlsClientConf.autoStartLoad = false;

                        hls = new Hls(hlsClientConf);
                        player.engine[engineName] = hls;

                        Object.keys(Hls.Events).forEach(function (key) {
                            hls.on(Hls.Events[key], function (etype, data) {
                                var fperr,
                                    errobj = {};

                                switch (key) {
                                case "MANIFEST_PARSED":
                                    if (hlsQualitiesSupport(conf)) {
                                        initQualitySelection(hlsQualitiesConf, data);
                                    } else {
                                        delete player.quality;
                                    }
                                    hls.startLoad();
                                    break;

                                case "FRAG_LOADED":
                                    if (disableAutoLevel === "startLevel") {
                                        // qsel
                                        setStickyLevel();
                                    }
                                    break;

                                case "FRAG_CHANGED":
                                    if (disableAutoLevel === "currentLevel" && player.ready) {
                                        // adaptOnStartOnly
                                        setStickyLevel();
                                    }
                                    break;

                                case "ERROR":
                                    if (data.fatal || hlsconf.strict > 0) {
                                        switch (data.type) {
                                        case Hls.ErrorTypes.NETWORK_ERROR:
                                            if (recover) {
                                                hls.startLoad();
                                                if (recover > 0) {
                                                    recover -= 1;
                                                }
                                            } else if (data.frag && data.frag.url) {
                                                errobj.url = data.frag.url;
                                                fperr = 2;
                                            } else {
                                                fperr = 4;
                                            }
                                            break;
                                        case Hls.ErrorTypes.MEDIA_ERROR:
                                            if (recover) {
                                                hls.recoverMediaError();
                                                if (recover > 0) {
                                                    recover -= 1;
                                                }
                                            } else {
                                                fperr = 3;
                                            }
                                            break;
                                        default:
                                            fperr = 5;
                                        }

                                        if (fperr !== undefined) {
                                            errobj.code = fperr;
                                            if (fperr > 2) {
                                                errobj.video = extend(video, {
                                                    src: video.src,
                                                    url: data.url || video.src
                                                });
                                            }
                                            player.trigger("error", [player, errobj]);
                                        }
                                    }
                                    // log non fatals?
                                    break;
                                }

                                player.trigger(etype, [player, data]);
                            });
                        });

                        if (init) {
                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                        }

                        hls.attachMedia(videoTag);
                        hls.loadSource(video.src);

                        if (videoTag.paused && autoplay) {
                            videoTag.play();
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        if (hls) {
                            bean.off(root, "." + engineName);
                            player.off("." + engineName);
                            qClean();
                            hls.destroy();
                            hls = 0;
                            bean.off(videoTag, "." + engineName);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            // pre 6.0.4: no boolean api.conf.poster and no poster with autoplay
            if (/^6\.0\.[0-3]$/.test(version) &&
                    !player.conf.splash && !player.conf.poster && !player.conf.autoplay) {
                bc = common.css(root, 'backgroundColor');
                // spaces in rgba arg mandatory for recognition
                has_bg = common.css(root, 'backgroundImage') !== "none" ||
                        (bc && bc !== "rgba(0, 0, 0, 0)" && bc !== "transparent");
                if (has_bg) {
                    player.conf.poster = true;
                }
            }

            return engine;
        };

    if (Hls.isSupported() && version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            var b = support.browser,
                wn = window.navigator,
                IE11 = wn.userAgent.indexOf("Trident/7") > -1;

            if (conf[engineName] === false || conf.clip[engineName] === false) {
                // engine disabled for player or clip
                return false;
            }

            // merge hlsjs clip config at earliest opportunity
            hlsconf = extend({
                smoothSwitching: true,
                recover: 0
            }, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            if (isHlsType(type)) {
                // allow all browsers for hlsjs debugging
                if (hlsconf.debug) {
                    return true;
                }
                // https://bugzilla.mozilla.org/show_bug.cgi?id=1244294
                if (hlsconf.anamorphic &&
                        wn.platform.indexOf("Win") === 0 && b.mozilla && b.version.indexOf("44.") === 0) {
                    return false;
                }

                // https://github.com/dailymotion/hls.js/issues/9
                return IE11 || !b.safari;
            }
            return false;
        };

        // put on top of engine stack
        // so hlsjs is tested before html5 video hls and flash hls
        flowplayer.engines.unshift(engineImpl);

        flowplayer(function (api) {
            // to take precedence over VOD quality selector
            api.pluginQualitySelectorEnabled = hlsQualitiesSupport(api.conf) &&
                    engineImpl.canPlay("application/x-mpegurl", api.conf);
        });

    }

}.apply(null, (typeof module === 'object' && module.exports)
    ? [require('flowplayer'), require('hls.js')]
    : [window.flowplayer, window.Hls]));
<|endoftext|>


<commit_before><commit_msg>Update sidebar<commit_after><|endoftext|>


<commit_before>/* eslint no-undef: 0 */
/* eslint no-implicit-globals: 0 */
/* eslint camelcase: 0 */
/* eslint unicorn/prefer-string-trim-start-end: 0 */

(function () {
  'use strict'

  window.addEventListener('tac.root_available', function () {
    var tac = document.getElementById('tarteaucitron')

    tac.querySelectorAll('.tarteaucitronAllow').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'btn-success', 'my-2', 'my-lg-0')
    })

    tac.querySelectorAll('.tarteaucitronDeny').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'btn-danger', 'my-2', 'my-lg-0', 'ml-lg-2')
    })

    document.getElementById('tarteaucitronClosePanel').classList.add('btn-close')
  }, { once: true })

  window.addEventListener('tac.open_alert', function () {
    var alert = document.getElementById('tarteaucitronAlertBig')

    document.getElementById('tarteaucitronCloseAlert').classList.add('btn', 'btn-sm', 'ml-lg-2')
    alert.querySelector('.tarteaucitronAllow').classList.add('btn', 'btn-sm', 'btn-success', 'mx-sm-2', 'ml-lg-auto', 'my-2', 'my-lg-0')
    alert.querySelector('.tarteaucitronDeny').classList.add('btn', 'btn-sm', 'btn-danger', 'mx-sm-2', 'my-2', 'my-lg-0')
    alert.querySelector('.tarteaucitronDeny').classList.add('btn', 'btn-sm', 'btn-danger', 'mx-sm-2', 'my-2', 'my-lg-0')
  }, { once: true })

  window.addEventListener('tac.open_panel', function () {
    document.querySelectorAll('#tarteaucitronServices_api button').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'ml-2')
    })
  }, { once: true })

  document.addEventListener('googletagmanager_added', function () {
    var ask = document.querySelector('#googletagmanagerLine .tarteaucitronAsk')
    var choiceEvent = 'tarteaucitron.userInterface.respond(document.getElementById(\'googletagmanagerAllowed\'),document.getElementById(\'googletagmanagerAllowed\').checked);'
    var toggle = document.createRange().createContextualFragment('
<input class="form-check-input ml-0" type="checkbox" id="googletagmanagerAllowed" aria-describedby="tacCLgoogletagmanager" onchange="' + choiceEvent + '">Google Tag Manager
')
    ask.innerHTML = ''
    ask.append(toggle)
  }, { once: true })
})()

if (typeof tarteaucitron !== 'undefined') {
  tarteaucitron.init({
    hashtag: '#cookie-consent',
    cookieName: 'cookie-consent',
    orientation: 'bottom',
    bodyPosition: 'top',
    cookieslist: true,
    highPrivacy: false,
    showIcon: false,
    handleBrowserDNTRequest: true,
    useExternalCss: true,
    mandatory: false
  })

  tarteaucitron.user.googletagmanagerId = 'GTM-P6H78BQ';
  (tarteaucitron.job = tarteaucitron.job || []).push('googletagmanager')
  dataLayer = [{
    site_name: 'accessibility-boosted',
    phase: 'prod',
    page_type: 'doc',
    page_title: document.title
  }]
}
<commit_msg>fix(#677): improve cookie consent panel rendering<commit_after>/* eslint no-undef: 0 */
/* eslint no-implicit-globals: 0 */
/* eslint camelcase: 0 */
/* eslint unicorn/prefer-string-trim-start-end: 0 */

(function () {
  'use strict'

  window.addEventListener('tac.root_available', function () {
    var tac = document.getElementById('tarteaucitron')

    tac.querySelectorAll('.tarteaucitronAllow').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'btn-success', 'my-2', 'my-lg-0')
    })

    tac.querySelectorAll('.tarteaucitronDeny').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'btn-danger', 'my-2', 'my-lg-0', 'ms-lg-2')
    })

    document.getElementById('tarteaucitron').classList.add('p-3')

    document.getElementById('tarteaucitronClosePanel').classList.add('d-flex', 'align-content-end', 'position-relative', 'btn-close')
    document.getElementById('tarteaucitronClosePanel').innerHTML =
      '' +
      document.getElementById('tarteaucitronClosePanel').innerHTML +
      ''
  }, { once: true })

  window.addEventListener('tac.open_alert', function () {
    var alert = document.getElementById('tarteaucitronAlertBig')

    document.getElementById('tarteaucitronCloseAlert').classList.add('btn', 'btn-sm', 'ms-lg-2')
    alert.querySelector('.tarteaucitronAllow').classList.add('btn', 'btn-sm', 'btn-success', 'mx-sm-2', 'ms-lg-auto', 'my-2', 'my-lg-0')
    alert.querySelector('.tarteaucitronDeny').classList.add('btn', 'btn-sm', 'btn-danger', 'mx-sm-2', 'my-2', 'my-lg-0')
    alert.querySelector('.tarteaucitronDeny').classList.add('btn', 'btn-sm', 'btn-danger', 'mx-sm-2', 'my-2', 'my-lg-0')
  }, { once: true })

  window.addEventListener('tac.open_panel', function () {
    document.querySelectorAll('#tarteaucitronServices_api button').forEach(function (button) {
      button.classList.add('btn', 'btn-sm', 'ms-2')
    })
  }, { once: true })

  document.addEventListener('googletagmanager_added', function () {
    var ask = document.querySelector('#googletagmanagerLine .tarteaucitronAsk')
    var choiceEvent = 'tarteaucitron.userInterface.respond(document.getElementById(\'googletagmanagerAllowed\'),document.getElementById(\'googletagmanagerAllowed\').checked);'
    var toggle = document.createRange().createContextualFragment('
<input class="form-check-input ms-0" type="checkbox" id="googletagmanagerAllowed" aria-describedby="tacCLgoogletagmanager" onchange="' + choiceEvent + '">Google Tag Manager
')
    ask.innerHTML = ''
    ask.append(toggle)
  }, { once: true })
})()

if (typeof tarteaucitron !== 'undefined') {
  tarteaucitron.init({
    hashtag: '#cookie-consent',
    cookieName: 'cookie-consent',
    orientation: 'bottom',
    bodyPosition: 'top',
    cookieslist: true,
    highPrivacy: false,
    showIcon: false,
    handleBrowserDNTRequest: true,
    useExternalCss: true,
    mandatory: false
  })

  tarteaucitron.user.googletagmanagerId = 'GTM-P6H78BQ';
  (tarteaucitron.job = tarteaucitron.job || []).push('googletagmanager')
  dataLayer = [{
    site_name: 'accessibility-boosted',
    phase: 'prod',
    page_type: 'doc',
    page_title: document.title
  }]
}
<|endoftext|>


<commit_before>'use strict';

angular.module('WidgetApp').controller('WidgetsViewCtrl', function($scope, WidgetsService, $controller, $log, $location, $routeParams, WidgetThemes  ){

    $scope.includePath = 'views/widgets/themes/loading.html';

    if ( !!$routeParams.widgetKey ){
        WidgetsService.getWidgetByKey( $routeParams.widgetKey).then(function(result){
            $scope.widget = result.data;

            $scope.widget.data = JSON.parse(result.data.data);
            $scope.includePath = 'views/widgets/themes/' + ( $scope.widget.data.theme || WidgetThemes.getDefault().id) + '.html';
            $log.info('includePath is', $scope.includePath);
            $controller('WidgetCtrl', {$scope: $scope}); //This works
        });
    }

});<commit_msg>fixing<commit_after>'use strict';

angular.module('WidgetApp').controller('WidgetsViewCtrl', function($scope, WidgetsService, $controller, $log, $location, $routeParams, WidgetThemes  ){

    $scope.includePath = 'views/widgets/themes/loading.html';

    if ( !!$routeParams.widgetKey ){
        WidgetsService.getWidgetByKey( $routeParams.widgetKey).then(function(result){
            $scope.widget = result.data;

            $scope.widget.data = JSON.parse(result.data.data);
            $scope.includePath = 'views/widgets/themes/' + ( ( $scope.widget.data && $scope.widget.data.theme ) || WidgetThemes.getDefault().id) + '.html';
            $log.info('includePath is', $scope.includePath);
            $controller('WidgetCtrl', {$scope: $scope}); //This works
        });
    }

});<|endoftext|>


<commit_before>'use strict';
var ko = require('knockout');

var $osf = require('js/osfHelpers');
var Raven = require('raven-js');

var ViewModel = function() {
    var self = this;
    self.instNames = ko.observableArray([]);
    self.selectedInst = ko.observable();
    self.insts = {};
    self.fetchInstitutions = function() {
        var url = window.contextVars.apiV2Prefix + 'institutions/';
        return $osf.ajaxJSON(
            'GET',
            url,
            {
                isCors: true
            }
        ).done(function (response) {
            self.instNames(
                response.data.filter(function (item) {
                    return item.attributes.auth_url;
                }).map(function(item){
                    return item.attributes.name;
                })
            );
            response.data.forEach(function(item){
                if (item.attributes.auth_url){
                    var name = item.attributes.name;
                    self.insts[name] = item.attributes.auth_url + '&target=' + encodeURIComponent(window.contextVars.institution_redirect);
                    self.insts[item.id] = name;
                }
            });
            var instRedirect = decodeURIComponent(decodeURIComponent(window.contextVars.institution_redirect));
            if (instRedirect){
                var instId = instRedirect.split('institutions')[1];
                instId = instId ? instId.split('/')[1] : false;
                if (instId){
                    self.selectedInst(self.insts[instId]);
                }
            }
        }).fail(function (xhr, status, error) {
            Raven.captureMessage('Unable to fetch institutions', {
                url: url,
                status: status,
                error: error
            });
        });
    };

    self.instLogin = function(){
        window.location = self.insts[self.selectedInst()];
    };
};

var InstitutionSignIn = function(selector) {
    this.viewModel = new ViewModel();
    $osf.applyBindings(this.viewModel, selector);
    this.viewModel.fetchInstitutions();
};

module.exports = InstitutionSignIn;

<commit_msg>no longer filter twice for institutions with auth_url<commit_after>'use strict';
var ko = require('knockout');

var $osf = require('js/osfHelpers');
var Raven = require('raven-js');

var ViewModel = function() {
    var self = this;
    self.instNames = ko.observableArray([]);
    self.selectedInst = ko.observable();
    self.insts = {};
    self.fetchInstitutions = function() {
        var url = window.contextVars.apiV2Prefix + 'institutions/';
        return $osf.ajaxJSON(
            'GET',
            url,
            {
                isCors: true
            }
        ).done(function (response) {
            var validInsts = response.data.filter(function(item){
                return item.attributes.auth_url;
            });
            self.instNames(
                validInsts.map(function(item){
                    return item.attributes.name;
                })
            );
            validInsts.forEach(function(item){
                var name = item.attributes.name;
                self.insts[name] = item.attributes.auth_url + '&target=' + encodeURIComponent(window.contextVars.institution_redirect);
                self.insts[item.id] = name;
            });
            var instRedirect = decodeURIComponent(decodeURIComponent(window.contextVars.institution_redirect));
            if (instRedirect){
                var instId = instRedirect.split('institutions')[1];
                instId = instId ? instId.split('/')[1] : false;
                if (instId){
                    self.selectedInst(self.insts[instId]);
                }
            }
        }).fail(function (xhr, status, error) {
            Raven.captureMessage('Unable to fetch institutions', {
                url: url,
                status: status,
                error: error
            });
        });
    };

    self.instLogin = function(){
        window.location = self.insts[self.selectedInst()];
    };
};

var InstitutionSignIn = function(selector) {
    this.viewModel = new ViewModel();
    $osf.applyBindings(this.viewModel, selector);
    this.viewModel.fetchInstitutions();
};

module.exports = InstitutionSignIn;

<|endoftext|>


<commit_before>
/*
 *
 * poloniex-unofficial
 * https://git.io/polonode
 *
 * Yet another unofficial Node.js wrapper for the Poloniex cryptocurrency
 * exchange APIs.
 *
 * Copyright (c) 2016 Tyler Filla
 *
 * This software may be modified and distributed under the terms of the MIT
 * license. See the LICENSE file for details.
 *
 */

/*
 * NOTICE: This file needs documentation revisions.
 */

// Localize wrappers
var PushWrapper = module.parent.exports.PushWrapper;
var PublicWrapper = module.parent.exports.PublicWrapper;

// Do not continue without these wrappers
if (!PushWrapper || !PublicWrapper) {
    throw "Failed to localize wrappers (OrderBook.js should not be require()'d externally)";
}

/*
 * Order book utility constructor.
 */
function OrderBook(currencyPair) {
    this._currencyPair = currencyPair;

    this._wrapperPush = null;
    this._wrapperPublic = null;

    this._running = false;

    this._callbackSetStart = new Array();
    this._callbackSetStop = new Array();
    this._callbackSetSync = new Array();
    this._callbackSetLoseSync = new Array();
}

/*
 *
 * function getWrapperPush()
 *
 * Get the push wrapper instance.
 *
 */
OrderBook.prototype.getWrapperPush = function() {
    return this._wrapperPush;
};

/*
 *
 * function setWrapperPush(wrapperPush)
 *
 * Set the push wrapper instance.
 *
 */
OrderBook.prototype.setWrapperPush = function(wrapperPush) {
    // Sanity check
    if (!(wrapperPush instanceof PushWrapper)) {
        throw new TypeError("wrapperPush is not an instance of PushWrapper");
    }

    this._wrapperPush = wrapperPush;
};

/*
 *
 * function getWrapperPublic()
 *
 * Get the public wrapper instance.
 *
 */
OrderBook.prototype.getWrapperPublic = function() {
    return this._wrapperPublic;
};

/*
 *
 * function setWrapperPublic(wrapperPublic)
 *
 * Set the public wrapper instance.
 *
 */
OrderBook.prototype.setWrapperPublic = function(wrapperPublic) {
    // Sanity check
    if (!(wrapperPublic instanceof PublicWrapper)) {
        throw new TypeError("wrapperPublic is not an instance of PublicWrapper");
    }

    this._wrapperPublic = wrapperPublic;
};

/*
 *
 * function getCurrencyPair()
 *
 * Get the target currency pair.
 *
 */
OrderBook.prototype.getCurrencyPair = function(currencyPair) {
    return this._currencyPair;
};

/*
 *
 * function setCurrencyPair(currencyPair)
 *
 * Set the target currency pair.
 *
 */
OrderBook.prototype.setCurrencyPair = function(currencyPair) {
    this._currencyPair = currencyPair;
};

/*
 *
 * function useDefaultWrappers()
 *
 * Create new push and public wrappers and use them.
 *
 */
OrderBook.prototype.useDefaultWrappers = function() {
    this.setWrapperPush(new PushWrapper());
    this.setWrapperPublic(new PublicWrapper());
};

/*
 *
 * function start()
 *
 * Start tracking the target currency pair's order book.
 *
 */
OrderBook.prototype.start = function() {
    // Sanity check
    if (this._running) {
        throw "Attempt to start, but already running";
    }

    // TODO: Start tracker

    // Set running flag
    this._running = true;
};

/*
 *
 * function start()
 *
 * Stop tracking the target currency pair's order book.
 *
 */
OrderBook.prototype.stop = function() {
    // Sanity check
    if (this._running) {
        throw "Attempt to stop, but not running";
    }

    // TODO: Stop tracker

    // Clear running flag
    this._running = false;
};

/*
 *
 * function onStart(callback)
 *
 * Register a callback to receive start events.
 *
 */
OrderBook.prototype.onStart = function(callback) {
    // Push to start callback set
    this._callbackSetStart.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from start callback set
            this._callbackSetStart.splice(this._callbackSetStart.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onStop(callback)
 *
 * Register a callback to receive stop events.
 *
 */
OrderBook.prototype.onStop = function(callback) {
    // Push to stop callback set
    this._callbackSetStop.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from stop callback set
            this._callbackSetStop.splice(this._callbackSetStop.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onSync(callback)
 *
 * Register a callback to receive sync events.
 *
 */
OrderBook.prototype.onSync = function(callback) {
    // Push to sync callback set
    this._callbackSetSync.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from sync callback set
            this._callbackSetSync.splice(this._callbackSetSync.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onLoseSync(callback)
 *
 * Register a callback to receive lose sync events.
 *
 */
OrderBook.prototype.onLoseSync = function(callback) {
    // Push to lose sync callback set
    this._callbackSetLoseSync.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from lose sync callback set
            this._callbackSetLoseSync.splice(this._callbackSetLoseSync.indexOf(callback), 1);
        }
    };
};

module.exports = OrderBook;
<commit_msg>Add handler structure and a few more sanity checks<commit_after>
/*
 *
 * poloniex-unofficial
 * https://git.io/polonode
 *
 * Yet another unofficial Node.js wrapper for the Poloniex cryptocurrency
 * exchange APIs.
 *
 * Copyright (c) 2016 Tyler Filla
 *
 * This software may be modified and distributed under the terms of the MIT
 * license. See the LICENSE file for details.
 *
 */

/*
 * NOTICE: This file needs documentation revisions.
 */

// Localize wrappers
var PushWrapper = module.parent.exports.PushWrapper;
var PublicWrapper = module.parent.exports.PublicWrapper;

// Do not continue without these wrappers
if (!PushWrapper || !PublicWrapper) {
    throw "Failed to localize wrappers (OrderBook.js should not be require()'d externally)";
}

/*
 * Order book utility constructor.
 */
function OrderBook(currencyPair) {
    this._currencyPair = currencyPair;

    this._wrapperPush = null;
    this._wrapperPublic = null;

    this._running = false;

    this._callbackSetStart = new Array();
    this._callbackSetStop = new Array();
    this._callbackSetSync = new Array();
    this._callbackSetLoseSync = new Array();
}

/*
 *
 * function getWrapperPush()
 *
 * Get the push wrapper instance.
 *
 */
OrderBook.prototype.getWrapperPush = function() {
    return this._wrapperPush;
};

/*
 *
 * function setWrapperPush(wrapperPush)
 *
 * Set the push wrapper instance.
 *
 */
OrderBook.prototype.setWrapperPush = function(wrapperPush) {
    // Lock while running
    if (this._running) {
        throw "Push wrapper instance cannot be changed while running";
    }

    // Sanity check
    if (!(wrapperPush instanceof PushWrapper)) {
        throw new TypeError("wrapperPush is not an instance of PushWrapper");
    }

    this._wrapperPush = wrapperPush;
};

/*
 *
 * function getWrapperPublic()
 *
 * Get the public wrapper instance.
 *
 */
OrderBook.prototype.getWrapperPublic = function() {
    return this._wrapperPublic;
};

/*
 *
 * function setWrapperPublic(wrapperPublic)
 *
 * Set the public wrapper instance.
 *
 */
OrderBook.prototype.setWrapperPublic = function(wrapperPublic) {
    // Lock while running
    if (this._running) {
        throw "Public wrapper instance cannot be changed while running";
    }

    // Sanity check
    if (!(wrapperPublic instanceof PublicWrapper)) {
        throw new TypeError("wrapperPublic is not an instance of PublicWrapper");
    }

    this._wrapperPublic = wrapperPublic;
};

/*
 *
 * function getCurrencyPair()
 *
 * Get the target currency pair.
 *
 */
OrderBook.prototype.getCurrencyPair = function() {
    return this._currencyPair;
};

/*
 *
 * function setCurrencyPair(currencyPair)
 *
 * Set the target currency pair.
 *
 */
OrderBook.prototype.setCurrencyPair = function(currencyPair) {
    // Lock while running
    if (this._running) {
        throw "Currency pair cannot be changed while running";
    }

    this._currencyPair = currencyPair;
};

/*
 *
 * function useDefaultWrappers()
 *
 * Create new push and public wrappers and use them.
 *
 */
OrderBook.prototype.useDefaultWrappers = function() {
    this.setWrapperPush(new PushWrapper());
    this.setWrapperPublic(new PublicWrapper());
};

/*
 *
 * function start()
 *
 * Start tracking the target currency pair's order book.
 *
 */
OrderBook.prototype.start = function() {
    // Sanity check
    if (this._running) {
        throw "Attempt to start, but already running";
    }

    // Start listening to the push API
    this._wrapperPush.orderTrade(this.currencyPair, (err, response) => {
        // Disconnect as soon as running flag is cleared
        if (!this._running) {
            return true;
        }

        // If an error occurred
        if (err) {
            // TODO: Handle error
        }

        // Handle update
        this._handleUpdate(response);
    });

    // Set running flag
    this._running = true;
};

/*
 *
 * function start()
 *
 * Stop tracking the target currency pair's order book.
 *
 */
OrderBook.prototype.stop = function() {
    // Sanity check
    if (!this._running) {
        throw "Attempt to stop, but not running";
    }

    // Clear running flag
    this._running = false;

    // TODO: Do any necessary cleanup
};

/*
 *
 * function onStart(callback)
 *
 * Register a callback to receive start events.
 *
 */
OrderBook.prototype.onStart = function(callback) {
    // Push to start callback set
    this._callbackSetStart.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from start callback set
            this._callbackSetStart.splice(this._callbackSetStart.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onStop(callback)
 *
 * Register a callback to receive stop events.
 *
 */
OrderBook.prototype.onStop = function(callback) {
    // Push to stop callback set
    this._callbackSetStop.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from stop callback set
            this._callbackSetStop.splice(this._callbackSetStop.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onSync(callback)
 *
 * Register a callback to receive sync events.
 *
 */
OrderBook.prototype.onSync = function(callback) {
    // Push to sync callback set
    this._callbackSetSync.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from sync callback set
            this._callbackSetSync.splice(this._callbackSetSync.indexOf(callback), 1);
        }
    };
};

/*
 *
 * function onLoseSync(callback)
 *
 * Register a callback to receive lose sync events.
 *
 */
OrderBook.prototype.onLoseSync = function(callback) {
    // Push to lose sync callback set
    this._callbackSetLoseSync.push(callback);

    // Give user the option to remove it
    return {
        "remove": () => {
            // Remove from lose sync callback set
            this._callbackSetLoseSync.splice(this._callbackSetLoseSync.indexOf(callback), 1);
        }
    };
};

/*
 * Internal function to handle order book updates.
 */
OrderBook.prototype._handleUpdate = function(update) {
    console.log("Received update with seq #" + update.seq);
};

module.exports = OrderBook;
<|endoftext|>


<commit_before><commit_msg>confirm before remove accession from population<commit_after><|endoftext|>


<commit_before>Array.prototype.distSq = function (b) {
  return this.reduce((p,c,i,a) => p + Math.pow(a[i]-b[i],2), 0)
}

Array.prototype.dist = function (b) { return Math.sqrt(this.distSq(b)) }

Array.prototype.magSq = function (b) {
  return this.reduce((p,c,i,a) => p + (c*c), 0)
}

Array.prototype.mag = function (b) { return Math.sqrt(this.magSq(b)) }

Array.prototype.add = function (b) { return this.map((c,i,a) => a[i] + b[i]) }
Array.prototype.sub = function (b) { return this.map((c,i,a) => a[i] - b[i]) }
Array.prototype.mul = function (b) { return this.map((c,i,a) => a[i] * b) }
Array.prototype.div = function (b) { return this.map((c,i,a) => a[i] / b) }

Array.prototype.x = function (v) { return v?(this[0] = v):this[0] }
Array.prototype.y = function (v) { return v?(this[1] = v):this[1] }
Array.prototype.z = function (v) { return v?(this[2] = v):this[2] }

;['abs','ceil','floor','max','min','round'].forEach(function (f) {
  Array.prototype[f] = function (b) { return this.map((c,i,a) => Math[f](c,b)) }
})<commit_msg>Cleaner syntax<commit_after>Array.prototype.distSq = function (b) {
  return this.reduce((p,c,i,a) => p + Math.pow(a[i]-b[i],2), 0)
}

Array.prototype.dist = function (b) { return Math.sqrt(this.distSq(b)) }

Array.prototype.magSq = function (b) {
  return this.reduce((p,c,i,a) => p + (c*c), 0)
}

Array.prototype.mag = function (b) { return Math.sqrt(this.magSq(b)) }

Array.prototype.add = function (b) { return this.map((c,i,a) => a[i] + b[i]) }
Array.prototype.sub = function (b) { return this.map((c,i,a) => a[i] - b[i]) }
Array.prototype.mul = function (b) { return this.map((c,i,a) => a[i] * b) }
Array.prototype.div = function (b) { return this.map((c,i,a) => a[i] / b) }

Array.prototype.x = function (v) {
  if (v) this[0] = v
  return this[0]
}
Array.prototype.y = function (v) {
  if (v) this[1] = v
  return this[1]
}
Array.prototype.z = function (v) {
  if (v) this[2] = v
  return this[2]
}

;['abs','ceil','floor','max','min','round'].forEach(function (f) {
  Array.prototype[f] = function (b) { return this.map((c,i,a) => Math[f](c,b)) }
})<|endoftext|>


<commit_before>'use strict';

define([
    'jquery',
    'underscore',
    'backbone',
    'cal-heatmap',
    'utils/Partial',
    'collections/home/GithubContribCollection',
    'text!templates/home/githubContribTemplate.html'
], function(
    $, _, Backbone, CalHeatMap, Partial, GithubContribCollection, githubContribTemplate
) {
    var ResumeView = Backbone.View.extend({
        tagName: 'div',
        className: 'tetris-view-github-contrib',

        xhr: null,

        cal: null,
        start: new Date(),
        data: {},

        initialize: function(options) {
            this.app = options.app;
            this.tube = options.tube;

            $(window).on('resize.github-contrib-view', _.bind(this.handleWindowResize, this));
        },

        handleWindowResize: function() {
            this.createNewCalHeatMap();
        },

        createNewCalHeatMap: function() {
            if (_.isEmpty(this.data)) {
                return;
            }

            if (this.cal !== null) {
                this.cal = this.cal.destroy();
            }

            this.cal = new CalHeatMap();
            this.cal.init({
                itemSelector: this.$('.tetris-heatmap')[0],
                domain: 'month',
                data: this.data,
                start: this.start,
                cellSize: 15,
                range: this.getApplicableRange(),
                legend: [2, 4, 6, 8],
                itemName: ['contribution', 'contributions'],
                tooltip: true,
                considerMissingDataAsZero: true
            });
        },

        getApplicableRange: function() {
            return 12;
        },

        render: function() {
            var that = this;

            this.collection = new GithubContribCollection();
            this.xhr = this.collection.fetch({
                success: function(collection, response) {
                    var data = collection.models[0].toJSON();
                    var i = 0;
                    var timestamp;

                    that.$el.html(_.template(githubContribTemplate)({
                        _: _
                    }));

                    for (timestamp in data) {
                        if (i > 0) {
                            break;
                        }
                        i++;
                    }

                    that.start = timestamp ? new Date(timestamp*1000) : new Date();
                    that.data = data;
                    that.range = 12;
                    that.createNewCalHeatMap();

                    that.xhr = null;
                }
            });

            this.$el.html(_.template(Partial.template.loader)(Partial.preload));

            return this;
        },

        close: function() {
            if (this.xhr !== null) {
                this.xhr.abort();
            }
            _.each(this.subViews, function(view) {
                view.remove();
            });
            this.remove();

            $(window).off('window.github-contrib-view');
        }
    });

    return ResumeView;
});
<commit_msg>Made github cal-heatmap range automatically adjustable<commit_after>'use strict';

define([
    'jquery',
    'underscore',
    'backbone',
    'cal-heatmap',
    'utils/Partial',
    'collections/home/GithubContribCollection',
    'text!templates/home/githubContribTemplate.html'
], function(
    $, _, Backbone, CalHeatMap, Partial, GithubContribCollection, githubContribTemplate
) {
    var ResumeView = Backbone.View.extend({
        tagName: 'div',
        className: 'tetris-view-github-contrib',

        xhr: null,

        cal: null,
        start: new Date(),
        data: {},

        initialize: function(options) {
            this.app = options.app;
            this.tube = options.tube;

            $(window).on('resize.github-contrib-view', _.bind(this.handleWindowResize, this));
        },

        handleWindowResize: function() {
            this.createNewCalHeatMap();
        },

        createNewCalHeatMap: function() {
            if (_.isEmpty(this.data)) {
                return;
            }

            if (this.cal !== null) {
                this.cal = this.cal.destroy();
            }

            this.cal = new CalHeatMap();
            this.cal.init({
                itemSelector: this.$('.tetris-heatmap')[0],
                domain: 'month',
                data: this.data,
                start: this.getApplicableStart(),
                cellSize: 15,
                range: this.getApplicableRange(),
                legend: [2, 4, 6, 8],
                itemName: ['contribution', 'contributions'],
                tooltip: true,
                considerMissingDataAsZero: true,
                animationDuration: 0
            });
        },

        getApplicableRange: function() {
            return Math.ceil(
                this.$el.width() / 104
            );
        },

        getApplicableStart: function() {
            var date = new Date();
            date.setMonth(
                date.getMonth() - this.getApplicableRange() + 1
            );
            return date;
        },

        render: function() {
            var that = this;

            this.collection = new GithubContribCollection();
            this.xhr = this.collection.fetch({
                success: function(collection, response) {
                    that.$el.html(_.template(githubContribTemplate)({
                        _: _
                    }));

                    that.data = collection.models[0].toJSON();
                    that.createNewCalHeatMap();

                    that.xhr = null;
                }
            });

            this.$el.html(_.template(Partial.template.loader)(Partial.preload));

            return this;
        },

        close: function() {
            if (this.xhr !== null) {
                this.xhr.abort();
            }
            _.each(this.subViews, function(view) {
                view.remove();
            });
            this.remove();

            $(window).off('window.github-contrib-view');
        }
    });

    return ResumeView;
});
<|endoftext|>


<commit_before>import { combineResolvers } from 'graphql-resolvers'
import mergeDeep from 'merge-deep'
import { set } from 'object-path-immutable'
import { get } from 'object-path'

const defaultOptions = {
  rootKeys: {
    query: 'RootQuery',
    mutation: 'RootMutation',
    subscription: 'RootSubscription',
  }
}

// Helper to flatten a deeply nested array.
const flatten = (result, subject) => result.concat(Array.isArray(subject) ? subject.reduce(flatten, []) : subject)
// Helper to compose functions left-to-right.
const compose = (...fns) => x => fns.reduce((v, f) => f(v), x)
// Helper to rename an object's property.
const renameProp = (from, to) => ({ [from]: value, ...rest }) => Object.assign(rest, value ? { [to]: value } : {})

const processedModules = []

const processModule = module => {
  // Recursion security meassure.
  if (processedModules.indexOf(module) !== -1) return []
  processedModules.push(module)

  // 1 - Factory format.
  if (module && module.constructor && module.call && module.apply) {
    return processModule(module())
  }

  // 2 - Array of modules format.
  if (Array.isArray(module)) {
    return module.map(processModule)
  }

  // 3 - Available submodules/dependencies.
  const dependencies = module.modules || []
  delete module.modules

  return [module].concat(dependencies.map(processModule))
}

const reduceResolvers = (result, root) => Object.keys(root).reduce(
  (result, type) => Object.keys(root[type]).reduce(
    (result, field) => set(result, [type, field], combineResolvers(
      get(result, [type, field], () => {}),
      get(root, [type, field])
    )), result
  ), result
)

/**
 * Compiles modules into schema definitions and resolvers as expected by Apollo server.
 *
 * @todo: Add warning for inconsistent data (e.g. resolver without definition).
 * @todo: Add simplified support for custom scalars.
 *
 * @param {Object} modules An array of modules. Each module can be one of the following:
 *                         1 - An object as described in the other params.
 *                         2 - An array of the previous option.
 *                         3 - A factory (function) that returns one of the previous.
 * @param {Array} modules[].modules An array of submodules/dependencies.
 * @param {String} modules[].schema Schema definition string.
 * @param {String} modules[].queries Query definition string.
 * @param {String} modules[].mutations Mutation definition string.
 * @param {String} modules[].subscriptions Subscription definition string.
 * @param {Object} modules[].resolvers Resolver definition object.
 * @param {Object} modules[].resolvers.queries Query resolver map.
 * @param {Object} modules[].resolvers.queries.[key] Query resolver function.
 * @param {Object} modules[].resolvers.mutations Mutation resolver map.
 * @param {Object} modules[].resolvers.mutations.[key] Mutation resolver function.
 * @param {Object} modules[].resolvers.subscriptions Subscription resolver map.
 * @param {Object} modules[].resolvers.subscriptions.[key] Subscription resolver function.
 * @param {Function} modules[].alter A function to alter the resulting configuration object
 *                                 after it's being created.
 * @param {Object} options An object of options.
 * @param {Object} options.rootKeys A map of root query keys.
 * @param {String} [options.rootKeys.query="RootQuery"] The root key for queries.
 * @param {String} [options.rootKeys.mutation="RootMutation"] The root key for mutations.
 * @param {String} [options.rootKeys.subscription="RootSubscription"] The root key for subscriptions.
 *
 * @return {Object} Options as expected by http://dev.apollodata.com/tools/graphql-tools/generate-schema.html#makeExecutableSchema.
 */
export default (modules = [], options = {}) => {
  options = mergeDeep({}, defaultOptions, options)
  modules = modules.reduce((modules, module) => modules.concat(processModule(module)), []).reduce(flatten, [])

  const schema = modules.map(module => module.schema || '').filter(Boolean).join(`\n`)
  const queries = modules.map(module => module.queries || '').filter(Boolean).join(`\n`)
  const mutations = modules.map(module => module.mutations || '').filter(Boolean).join(`\n`)
  const subscriptions = modules.map(module => module.subscriptions || '').filter(Boolean).join(`\n`)

  const resolvers = compose(
    modules => modules.map(module => module.resolvers || {}),
    modules => modules.reduce(reduceResolvers, {}),
    renameProp('subscriptions', options.rootKeys.subscription),
    renameProp('mutations', options.rootKeys.mutation),
    renameProp('queries', options.rootKeys.query),
  )(modules)

  const RootQuery = queries.length ? `
    type ${options.rootKeys.query} {
      ${queries}
    }
  ` : ''

  const RootMutation = mutations.length ? `
    type ${options.rootKeys.mutation} {
      ${mutations}
    }
  ` : ''

  const RootSubscription = subscriptions.length ? `
    type ${options.rootKeys.subscription} {
      ${subscriptions}
    }
  ` : ''

  const typeDefs = `
    ${schema}
    ${RootQuery}
    ${RootMutation}
    ${RootSubscription}

    schema {
      ${RootQuery && 'query: ' + options.rootKeys.query}
      ${RootMutation && 'mutation: ' + options.rootKeys.mutation}
      ${RootSubscription && 'subscription: ' + options.rootKeys.subscription}
    }
  `

  // typeDefs,
  // resolvers,
  // logger,
  // allowUndefinedInResolve = false,
  // resolverValidationOptions = {},

  // Cleanup processedModules after bundling.
  processedModules.splice(0, processedModules.length)

  const config = { typeDefs, resolvers }

  return modules.reduce((config, { alter = obj => obj }) => alter(config), config)
}
<commit_msg>Added opt-out and out per default from combineResolvers.<commit_after>import { combineResolvers } from 'graphql-resolvers'
import mergeDeep from 'merge-deep'
import { set } from 'object-path-immutable'
import { get } from 'object-path'

const defaultOptions = {
  combine: true,
  rootKeys: {
    query: 'RootQuery',
    mutation: 'RootMutation',
    subscription: 'RootSubscription',
  }
}

// Helper to flatten a deeply nested array.
const flatten = (result, subject) => result.concat(Array.isArray(subject) ? subject.reduce(flatten, []) : subject)
// Helper to compose functions left-to-right.
const compose = (...fns) => x => fns.reduce((v, f) => f(v), x)
// Helper to rename an object's property.
const renameProp = (from, to) => ({ [from]: value, ...rest }) => Object.assign(rest, value ? { [to]: value } : {})

const processedModules = []

const processModule = module => {
  // Recursion security meassure.
  if (processedModules.indexOf(module) !== -1) return []
  processedModules.push(module)

  // 1 - Factory format.
  if (module && module.constructor && module.call && module.apply) {
    return processModule(module())
  }

  // 2 - Array of modules format.
  if (Array.isArray(module)) {
    return module.map(processModule)
  }

  // 3 - Available submodules/dependencies.
  const dependencies = module.modules || []
  delete module.modules

  return [module].concat(dependencies.map(processModule))
}

/**
 * Compiles modules into schema definitions and resolvers as expected by Apollo server.
 *
 * @todo: Add warning for inconsistent data (e.g. resolver without definition).
 * @todo: Add simplified support for custom scalars.
 *
 * @param {Object} modules An array of modules. Each module can be one of the following:
 *                         1 - An object as described in the other params.
 *                         2 - An array of the previous option.
 *                         3 - A factory (function) that returns one of the previous.
 * @param {Array} modules[].modules An array of submodules/dependencies.
 * @param {String} modules[].schema Schema definition string.
 * @param {String} modules[].queries Query definition string.
 * @param {String} modules[].mutations Mutation definition string.
 * @param {String} modules[].subscriptions Subscription definition string.
 * @param {Object} modules[].resolvers Resolver definition object.
 * @param {Object} modules[].resolvers.queries Query resolver map.
 * @param {Object} modules[].resolvers.queries.[key] Query resolver function.
 * @param {Object} modules[].resolvers.mutations Mutation resolver map.
 * @param {Object} modules[].resolvers.mutations.[key] Mutation resolver function.
 * @param {Object} modules[].resolvers.subscriptions Subscription resolver map.
 * @param {Object} modules[].resolvers.subscriptions.[key] Subscription resolver function.
 * @param {Function} modules[].alter A function to alter the resulting configuration object
 *                                 after it's being created.
 * @param {Object} options An object of options.
 * @param {Object} options.rootKeys A map of root query keys.
 * @param {String} [options.rootKeys.query="RootQuery"] The root key for queries.
 * @param {String} [options.rootKeys.mutation="RootMutation"] The root key for mutations.
 * @param {String} [options.rootKeys.subscription="RootSubscription"] The root key for subscriptions.
 *
 * @return {Object} Options as expected by http://dev.apollodata.com/tools/graphql-tools/generate-schema.html#makeExecutableSchema.
 */
export default (modules = [], options = {}) => {
  options = mergeDeep({}, defaultOptions, options)
  modules = modules.reduce((modules, module) => modules.concat(processModule(module)), []).reduce(flatten, [])

  const schema = modules.map(module => module.schema || '').filter(Boolean).join(`\n`)
  const queries = modules.map(module => module.queries || '').filter(Boolean).join(`\n`)
  const mutations = modules.map(module => module.mutations || '').filter(Boolean).join(`\n`)
  const subscriptions = modules.map(module => module.subscriptions || '').filter(Boolean).join(`\n`)

  const reduceResolvers = (result, root) => Object.keys(root).reduce(
    (result, type) => Object.keys(root[type]).reduce(
      (result, field) => set(result, [type, field], !options.combine || !result[type] || !result[type][field]
        ? get(root, [type, field])
        : combineResolvers(
          get(result, [type, field], () => {}),
          get(root, [type, field])
        )), result
    ), result
  )

  const resolvers = compose(
    modules => modules.map(module => module.resolvers || {}),
    modules => modules.reduce(reduceResolvers, {}),
    renameProp('subscriptions', options.rootKeys.subscription),
    renameProp('mutations', options.rootKeys.mutation),
    renameProp('queries', options.rootKeys.query),
  )(modules)

  const RootQuery = queries.length ? `
    type ${options.rootKeys.query} {
      ${queries}
    }
  ` : ''

  const RootMutation = mutations.length ? `
    type ${options.rootKeys.mutation} {
      ${mutations}
    }
  ` : ''

  const RootSubscription = subscriptions.length ? `
    type ${options.rootKeys.subscription} {
      ${subscriptions}
    }
  ` : ''

  const typeDefs = `
    ${schema}
    ${RootQuery}
    ${RootMutation}
    ${RootSubscription}

    schema {
      ${RootQuery && 'query: ' + options.rootKeys.query}
      ${RootMutation && 'mutation: ' + options.rootKeys.mutation}
      ${RootSubscription && 'subscription: ' + options.rootKeys.subscription}
    }
  `

  // typeDefs,
  // resolvers,
  // logger,
  // allowUndefinedInResolve = false,
  // resolverValidationOptions = {},

  // Cleanup processedModules after bundling.
  processedModules.splice(0, processedModules.length)

  const config = { typeDefs, resolvers }

  return modules.reduce((config, { alter = obj => obj }) => alter(config), config)
}
<|endoftext|>