<commit_before>//
//  File: skin.js
//  Project: R7.Epsilon
//
//  Author: Roman M. Yagodin <roman.yagodin@gmail.com>
//
//  Copyright (c) 2015-2019 Roman M. Yagodin, R7.Labs
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import A11y from "./a11y";
import Cookies from "js-cookie";

const supportedBrowsers = require ("./supportedBrowsers");

// TODO: Move global functions to eplilon object

window.skinGoogleTranslatePage = function (fromLang) {
    window.open ("http://translate.google.com/translate?hl=en&sl=" + fromLang + "&u=" + encodeURI (document.location));
};

window.skinOpenFeedback = function (e, button, $, feedbackModuleId) {
    e.preventDefault ();
    const selection = encodeURIComponent (rangy.getSelection ().toString ().replace (/(\n|\r)/gm," ").replace (/\s+/g, " ").replace (/\"/g, "").trim ().substring (0,100));
    const baseFeedbackUrl = $(button).data ("feedback-url");
    const feedbackParams = "returntabid=" + epsilon.queryParams ["TabId"] + "&feedbackmid=" + feedbackModuleId;

    const feedbackSelection = ((!!selection)? "&feedbackselection=" + selection : "");

    if (epsilon.enablePopups && window.skinA11y.getPopupsDisabled () === false && $(button).data ("feedback-open-in-popup") === true) {
        const popupFeedbackUrl = baseFeedbackUrl + "/mid/" + feedbackModuleId + "?" + feedbackParams + "&popup=true" + feedbackSelection;
        dnnModal.show (popupFeedbackUrl, false, 550, 950, false, "");
    }
    else {
        const rawFeedbackUrl = baseFeedbackUrl + "?" + feedbackParams + "&" + feedbackSelection;
        window.open (rawFeedbackUrl, "_blank");
    }
};

window.skinSearchExternalClick = function (e, link) {
    const searchText = $("input[id$='_dnnSearch_txtSearch']").val ();
    const urlFormat = $(link).data ("url-format");
    $(link).attr ("href", urlFormat.replace ("{website}", encodeURIComponent (epsilon.portalAlias)).replace ("{searchText}", encodeURIComponent (searchText)));
};

window.skinCookiesAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "CookiesAlert", {expires: 14});
    $(e.target).closest (".toast").toast ("hide");
};

(function ($, window, document) {

    function initUpButton (offset, duration) {
        $(window).scroll(function() {
            if ($(this).scrollTop() > offset) {
                $('.skn-float-btn-up').fadeIn(duration);
            } else {
                $('.skn-float-btn-up').fadeOut(duration);
            }
        });

        $('.skn-float-btn-up').on ("click.epsilon", function (e) {
            e.preventDefault();
            $(this).tooltip ('hide');
            $('html, body').animate({scrollTop: 0}, duration);
            return false;
        });
    }

    function emptyLayoutRows () {
        $('.row').each (function () {
            if ($(this).children ().length ===
                $(this).children ('.DNNEmptyPane').not ('.dnnSortable').length) {
                $(this).addClass ('hidden');
            }
        });
    }

    function alterLanguage () {
        $(".skn-languages .language-object a").each (function () {
            const lang = $(this).parent (".Language").attr ("title");
            const langCode = $(this).closest (".language-object").find ("option").filter (function () { return $(this).text() == lang; }).val ();
            $(this).addClass ("dropdown-item")
                .html ("" + langCode + " " + lang)
                .attr ("hreflang", langCode);
        });
    }

    function initBootstrapTooltips () {
        if (typeof ($.fn.tooltip) !== "undefined") {
            $("[data-toggle='tooltip']").tooltip ();
        }
    }

    function initBootstrapPopovers () {
        if (typeof ($.fn.popover) !== "undefined") {
            $("[data-toggle='popover']").popover ();
        }
    }

    function initBootstrapToasts () {
        if (typeof ($.fn.toast) !== "undefined") {
            $(".toast").toast ();
        }
    }

    function alterLogin () {
        $(".skn-login li.userDisplayName > a").addClass ("dropdown-item");
        $(".skn-login div.loginGroup > a").addClass ("dropdown-item");
        $(".skn-login li.userMessages > a").addClass ("dropdown-item");
        $(".skn-login li.userNotifications > a").addClass ("dropdown-item");
        $(".skn-login li.userProfileImg > a").addClass ("dropdown-item");

        const loginGroup = $(".skn-login .loginGroup").first ().detach ();
        const divider = "

";

        const profileImg = $(".skn-login li.userProfileImg > a > img");
        if (profileImg.length > 0) {
            profileImg.attr ("src", profileImg.attr ("src").replace ("h=32&w=32", "h=64&w=64"));

            const profileLinkBlock = $(".skn-login li.userDisplayName").first ().detach ();
            const profileImgBlock = $(".skn-login li.userProfileImg").first ().detach ();

            $(".skn-login ul.buttonGroup")
                .prepend (divider)
                .prepend (profileLinkBlock)
                .prepend (profileImgBlock)
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));

                $(".skn-login li.userMessages > a > span").addClass ("badge badge-primary");
                $(".skn-login li.userNotifications > a > span").addClass ("badge badge-secondary");
        }
        else {
            $(".skn-login ul.buttonGroup")
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));
        }
    }

    function initCustomContent () {
        // TODO: Also check for superusers and admins
        if (epsilon.isEditMode) {
            $(".skn-custom-content").each (function () {
                $(this).prepend ("
"
                                + "
"
                                // TODO: Localize label
                                // TODO: Generate actual edit URL
                                + "

"
                                + "
"
                                + "
");
            });
        }
    }

    function initSearch () {
        $("#searchModal").on ("shown.bs.modal", function (e) {
            $("#searchModal input[id$='_dnnSearch_txtSearch']").focus ();
        });
    }

    function initClipboard () {
        epsilon.clipboard = new ClipboardJS('.btn.btn-clipboard');
    }

    function initTags () {
        $(".skn-tags ul.categories > li > a").addClass ("badge badge-secondary");
    }

    function initMainMenu () {
        $(".skn-main-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skn-main-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });

        // TODO: How this will work with event namespaces?
        $(".skn-main-menu .skn-submenu.collapse")
            .on ("shown.bs.collapse", function (e) {
                const parentMenuItem = $(".skn-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab or Down
                    if ((e.keyCode === 9 && !e.shiftKey) || e.keyCode === 40) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        firstMenuItem.focus ();
                    }
                });
                firstMenuItem.on ("keydown.epsilon", function (e) {
                    // Shift-Tab or Up
                    if ((e.keyCode === 9 && e.shiftKey) || e.keyCode === 38) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        parentMenuItem.focus ();
                    }
                });
                lastMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab
                    if (e.keyCode === 9 && !e.shiftKey) {
                        const nextParentMenuItem = parentMenuItem.parent ().next ().children (".nav-link");
                        if (nextParentMenuItem.length > 0) {
                            e.preventDefault ();
                            e.stopPropagation ();
                            nextParentMenuItem.focus ();
                        }
                    }
                });
            })
            .on ("hidden.bs.collapse", function () {
                const parentMenuItem = $(".skn-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.off ("keydown.epsilon");
                firstMenuItem.off ("keydown.epsilon");
                lastMenuItem.off ("keydown.epsilon");
            });
    }

    function initBreadcrumb () {
        $(".skn-breadcrumb-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skn-breadcrumb-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });
    }

    function isCookiesEnabled () {
        // borrowed from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js
        try {
            document.cookie = epsilon.cookiePrefix + "CookieTest=1;"
            var isCookiesEnabled = document.cookie.indexOf (epsilon.cookiePrefix + "CookieTest=") !== -1;
            document.cookie = epsilon.cookiePrefix + "CookieTest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
            return isCookiesEnabled;
          }
          catch (e) {
            return false;
          }
    }

    function showToasts () {
        if (! supportedBrowsers.test (navigator.userAgent)) {
            $("#skin_toastBrowserAlert").toast ("show");
        }

        if (!isCookiesEnabled ()) {
            $("#skin_toastCookiesDisabledAlert").toast ("show");
        }
        else if (typeof Cookies.get (epsilon.cookiePrefix + "CookiesAlert") === "undefined") {
            $("#skin_toastCookiesAlert").toast ("show");
        }
    }

    $(function () {
        initBootstrapTooltips ();
        initBootstrapPopovers ();

        if (! epsilon.inPopup) {
            initBootstrapToasts ();
            emptyLayoutRows ();
            initUpButton (320, 500);
            initCustomContent ();
            initSearch ();
            initClipboard ();
            initTags ();
            initMainMenu ();
            initBreadcrumb ();
            alterLanguage ();
            alterLogin ();
            showToasts ();
            window.skinA11y = new A11y ().init ();
        }
    });

}) (jQuery, window, document);
<commit_msg>Remove code task<commit_after>//
//  File: skin.js
//  Project: R7.Epsilon
//
//  Author: Roman M. Yagodin <roman.yagodin@gmail.com>
//
//  Copyright (c) 2015-2019 Roman M. Yagodin, R7.Labs
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import A11y from "./a11y";
import Cookies from "js-cookie";

const supportedBrowsers = require ("./supportedBrowsers");

// TODO: Move global functions to eplilon object

window.skinGoogleTranslatePage = function (fromLang) {
    window.open ("http://translate.google.com/translate?hl=en&sl=" + fromLang + "&u=" + encodeURI (document.location));
};

window.skinOpenFeedback = function (e, button, $, feedbackModuleId) {
    e.preventDefault ();
    const selection = encodeURIComponent (rangy.getSelection ().toString ().replace (/(\n|\r)/gm," ").replace (/\s+/g, " ").replace (/\"/g, "").trim ().substring (0,100));
    const baseFeedbackUrl = $(button).data ("feedback-url");
    const feedbackParams = "returntabid=" + epsilon.queryParams ["TabId"] + "&feedbackmid=" + feedbackModuleId;

    const feedbackSelection = ((!!selection)? "&feedbackselection=" + selection : "");

    if (epsilon.enablePopups && window.skinA11y.getPopupsDisabled () === false && $(button).data ("feedback-open-in-popup") === true) {
        const popupFeedbackUrl = baseFeedbackUrl + "/mid/" + feedbackModuleId + "?" + feedbackParams + "&popup=true" + feedbackSelection;
        dnnModal.show (popupFeedbackUrl, false, 550, 950, false, "");
    }
    else {
        const rawFeedbackUrl = baseFeedbackUrl + "?" + feedbackParams + "&" + feedbackSelection;
        window.open (rawFeedbackUrl, "_blank");
    }
};

window.skinSearchExternalClick = function (e, link) {
    const searchText = $("input[id$='_dnnSearch_txtSearch']").val ();
    const urlFormat = $(link).data ("url-format");
    $(link).attr ("href", urlFormat.replace ("{website}", encodeURIComponent (epsilon.portalAlias)).replace ("{searchText}", encodeURIComponent (searchText)));
};

window.skinCookiesAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "CookiesAlert", {expires: 14});
    $(e.target).closest (".toast").toast ("hide");
};

(function ($, window, document) {

    function initUpButton (offset, duration) {
        $(window).scroll(function() {
            if ($(this).scrollTop() > offset) {
                $('.skn-float-btn-up').fadeIn(duration);
            } else {
                $('.skn-float-btn-up').fadeOut(duration);
            }
        });

        $('.skn-float-btn-up').on ("click.epsilon", function (e) {
            e.preventDefault();
            $(this).tooltip ('hide');
            $('html, body').animate({scrollTop: 0}, duration);
            return false;
        });
    }

    function emptyLayoutRows () {
        $('.row').each (function () {
            if ($(this).children ().length ===
                $(this).children ('.DNNEmptyPane').not ('.dnnSortable').length) {
                $(this).addClass ('hidden');
            }
        });
    }

    function alterLanguage () {
        $(".skn-languages .language-object a").each (function () {
            const lang = $(this).parent (".Language").attr ("title");
            const langCode = $(this).closest (".language-object").find ("option").filter (function () { return $(this).text() == lang; }).val ();
            $(this).addClass ("dropdown-item")
                .html ("" + langCode + " " + lang)
                .attr ("hreflang", langCode);
        });
    }

    function initBootstrapTooltips () {
        if (typeof ($.fn.tooltip) !== "undefined") {
            $("[data-toggle='tooltip']").tooltip ();
        }
    }

    function initBootstrapPopovers () {
        if (typeof ($.fn.popover) !== "undefined") {
            $("[data-toggle='popover']").popover ();
        }
    }

    function initBootstrapToasts () {
        if (typeof ($.fn.toast) !== "undefined") {
            $(".toast").toast ();
        }
    }

    function alterLogin () {
        $(".skn-login li.userDisplayName > a").addClass ("dropdown-item");
        $(".skn-login div.loginGroup > a").addClass ("dropdown-item");
        $(".skn-login li.userMessages > a").addClass ("dropdown-item");
        $(".skn-login li.userNotifications > a").addClass ("dropdown-item");
        $(".skn-login li.userProfileImg > a").addClass ("dropdown-item");

        const loginGroup = $(".skn-login .loginGroup").first ().detach ();
        const divider = "

";

        const profileImg = $(".skn-login li.userProfileImg > a > img");
        if (profileImg.length > 0) {
            profileImg.attr ("src", profileImg.attr ("src").replace ("h=32&w=32", "h=64&w=64"));

            const profileLinkBlock = $(".skn-login li.userDisplayName").first ().detach ();
            const profileImgBlock = $(".skn-login li.userProfileImg").first ().detach ();

            $(".skn-login ul.buttonGroup")
                .prepend (divider)
                .prepend (profileLinkBlock)
                .prepend (profileImgBlock)
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));

                $(".skn-login li.userMessages > a > span").addClass ("badge badge-primary");
                $(".skn-login li.userNotifications > a > span").addClass ("badge badge-secondary");
        }
        else {
            $(".skn-login ul.buttonGroup")
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));
        }
    }

    function initCustomContent () {
        // TODO: Also check for superusers and admins
        if (epsilon.isEditMode) {
            $(".skn-custom-content").each (function () {
                $(this).prepend ("
"
                                + "
"
                                // TODO: Localize label
                                // TODO: Generate actual edit URL
                                + "

"
                                + "
"
                                + "
");
            });
        }
    }

    function initSearch () {
        $("#searchModal").on ("shown.bs.modal", function (e) {
            $("#searchModal input[id$='_dnnSearch_txtSearch']").focus ();
        });
    }

    function initClipboard () {
        epsilon.clipboard = new ClipboardJS('.btn.btn-clipboard');
    }

    function initTags () {
        $(".skn-tags ul.categories > li > a").addClass ("badge badge-secondary");
    }

    function initMainMenu () {
        $(".skn-main-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skn-main-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });

        $(".skn-main-menu .skn-submenu.collapse")
            .on ("shown.bs.collapse", function (e) {
                const parentMenuItem = $(".skn-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab or Down
                    if ((e.keyCode === 9 && !e.shiftKey) || e.keyCode === 40) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        firstMenuItem.focus ();
                    }
                });
                firstMenuItem.on ("keydown.epsilon", function (e) {
                    // Shift-Tab or Up
                    if ((e.keyCode === 9 && e.shiftKey) || e.keyCode === 38) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        parentMenuItem.focus ();
                    }
                });
                lastMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab
                    if (e.keyCode === 9 && !e.shiftKey) {
                        const nextParentMenuItem = parentMenuItem.parent ().next ().children (".nav-link");
                        if (nextParentMenuItem.length > 0) {
                            e.preventDefault ();
                            e.stopPropagation ();
                            nextParentMenuItem.focus ();
                        }
                    }
                });
            })
            .on ("hidden.bs.collapse", function () {
                const parentMenuItem = $(".skn-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.off ("keydown.epsilon");
                firstMenuItem.off ("keydown.epsilon");
                lastMenuItem.off ("keydown.epsilon");
            });
    }

    function initBreadcrumb () {
        $(".skn-breadcrumb-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skn-breadcrumb-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });
    }

    function isCookiesEnabled () {
        // borrowed from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js
        try {
            document.cookie = epsilon.cookiePrefix + "CookieTest=1;"
            var isCookiesEnabled = document.cookie.indexOf (epsilon.cookiePrefix + "CookieTest=") !== -1;
            document.cookie = epsilon.cookiePrefix + "CookieTest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
            return isCookiesEnabled;
          }
          catch (e) {
            return false;
          }
    }

    function showToasts () {
        if (! supportedBrowsers.test (navigator.userAgent)) {
            $("#skin_toastBrowserAlert").toast ("show");
        }

        if (!isCookiesEnabled ()) {
            $("#skin_toastCookiesDisabledAlert").toast ("show");
        }
        else if (typeof Cookies.get (epsilon.cookiePrefix + "CookiesAlert") === "undefined") {
            $("#skin_toastCookiesAlert").toast ("show");
        }
    }

    $(function () {
        initBootstrapTooltips ();
        initBootstrapPopovers ();

        if (! epsilon.inPopup) {
            initBootstrapToasts ();
            emptyLayoutRows ();
            initUpButton (320, 500);
            initCustomContent ();
            initSearch ();
            initClipboard ();
            initTags ();
            initMainMenu ();
            initBreadcrumb ();
            alterLanguage ();
            alterLogin ();
            showToasts ();
            window.skinA11y = new A11y ().init ();
        }
    });

}) (jQuery, window, document);
<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2010 Marat Nepomnyashy    maratbn@gmail
 *  All rights reserved.
 *
 *  Module:         dojo_dcodeview/DCodeView.js
 *
 *  Description:    A Dojo-based widget for embedding arbitrary source code
 *                  views inside web pages.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of the <organization> nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

dojo.provide('dcodeview.DCodeView');

dojo.require('dijit._Templated');
dojo.require('dijit._Widget');

dojo.declare(
    'dcodeview.DCodeView',
    [dijit._Widget, dijit._Templated],
    {
        templateString:     [   "
",
                                    "
",
                                        "",
                                            "

",
                                        "	",
                                        "",
                                            "

",
                                        "	",
                                    "
",
                                "
"
                            ].join(""),

        postCreate:         function() {
                                this.inherited(arguments);

                                var strCode = this.srcNodeRef.innerHTML || "";
                                var arrLinesCode = strCode.split(/\r\n|\r|\n/);

                                var arrOutputLines = [], arrOutputCode = [];
                                for (var i = 0; i < arrLinesCode.length; i++) {
                                    arrOutputLines.push(i);
                                    arrOutputCode.push(arrLinesCode[i]);
                                }

                                var strOutputLines = arrOutputLines.join('\r\n');
                                var strOutputCode = arrOutputCode.join('\r\n');

                                if (dojo.isIE) {
                                    this._preLineNumbers.innerText = strOutputLines;
                                    this._preCode.innerText = strOutputCode;
                                } else {
                                    this._preLineNumbers.innerHTML = strOutputLines;
                                    this._preCode.innerHTML = strOutputCode;
                                }
                            }
    });<commit_msg>Separating the line numbers from the code by a margin and a red line.<commit_after>/**
 *  Copyright (c) 2010 Marat Nepomnyashy    maratbn@gmail
 *  All rights reserved.
 *
 *  Module:         dojo_dcodeview/DCodeView.js
 *
 *  Description:    A Dojo-based widget for embedding arbitrary source code
 *                  views inside web pages.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of the <organization> nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

dojo.provide('dcodeview.DCodeView');

dojo.require('dijit._Templated');
dojo.require('dijit._Widget');

dojo.declare(
    'dcodeview.DCodeView',
    [dijit._Widget, dijit._Templated],
    {
        templateString:     [   "
",
                                    "
",
                                        "",
                                            "

",
                                        "	",
                                        "",
                                            "

",
                                        "	",
                                    "
",
                                "
"
                            ].join(""),

        postCreate:         function() {
                                this.inherited(arguments);

                                var strCode = this.srcNodeRef.innerHTML || "";
                                var arrLinesCode = strCode.split(/\r\n|\r|\n/);

                                var arrOutputLines = [], arrOutputCode = [];
                                for (var i = 0; i < arrLinesCode.length; i++) {
                                    arrOutputLines.push(i);
                                    arrOutputCode.push(arrLinesCode[i]);
                                }

                                var strOutputLines = arrOutputLines.join('\r\n');
                                var strOutputCode = arrOutputCode.join('\r\n');

                                if (dojo.isIE) {
                                    this._preLineNumbers.innerText = strOutputLines;
                                    this._preCode.innerText = strOutputCode;
                                } else {
                                    this._preLineNumbers.innerHTML = strOutputLines;
                                    this._preCode.innerHTML = strOutputCode;
                                }
                            }
    });<|endoftext|>


<commit_before>/* global describe */
/* global it */

const should = require('should')
const sw = require('../lib/stopword.js')

describe('general stopwordiness:', function () {
  it('should remove stopwords, should default to english, should preserve case', function () {
    const oldString = 'a really Interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString)
    should.exist(sw.removeStopwords(oldString))
    newString.should.eql([ 'really', 'Interesting', 'string', 'words' ])
  })

  it('should remove custom stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, ['interesting'])
    newString.should.eql([ 'a', 'really', 'string', 'with', 'some', 'words' ])
  })

  it('should not remove any stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, [])
    newString.should.eql([ 'a', 'really', 'interesting', 'string', 'with', 'some', 'words' ])
  })

  it('should remove stopwords that have a non standard separator', function () {
    const oldString = 'a.really,interesting string.with,some.words'.split(/[\\., ]+/)
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'words' ])
  })

  it('should specify a custom input separator', function () {
    const oldString = 'a-really-interesting-string-with-some words'.split('-')
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'some words' ])
  })

  it('should remove norwegian stopwords', function () {
    const oldString = 'dette er en tekst som har nørske tegn øæåø øæåø æææ'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.no)
    newString.should.eql([ 'tekst', 'nørske', 'tegn', 'øæåø', 'øæåø', 'æææ' ])
  })

  it('should remove swedish stopwords and preserve case', function () {
    const oldString = 'Trädgårdsägare är beredda att pröva vad som helst för att bli av med de hatade mördarsniglarna åäö'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.sv)
    newString.should.eql([ 'Trädgårdsägare', 'beredda', 'pröva', 'helst', 'hatade', 'mördarsniglarna', 'åäö' ])
  })

  it('should remove danish stopwords', function () {
    const oldString = 'gæsterne i musikhuset i aarhus bør fremover sende en venlig tanke til den afdøde købmand herman salling når de skal til koncert eller se teater i det aarhusianske kulturhus æøå'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.da)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

  it('should remove hindu stopwords', function () {
    const oldString = 'केंद्र सरकार पर्यावरण के माकूल घरों 'ग्रीन होम्स' को बढ़ावा देने की दिशा में गंभीरता से सोच रही है। ग्रीन हाउसिंग सोसायटी डिवेलप करने के लिए सरकार सस्ते लोन और कम रजिस्ट्रेशन फीस जैसी सुविधाएं देगी। क्लाइमेट चेंज से लड़ने की दिशा में केंद्र इस तरह की रिहायशी कॉलोनियां विकसित करने की दिशा में सोच रही है। बता दें कि ग्रीन होम्स वे घर हैं, जो पर्यावरण को ध्यान में रखकर विकसित किए जाते हैं। इनमें पर्यावरण के नजरिए से एनर्जी, जल संसाधन और बिल्डिंग मटीरियल्स का प्रभावशाली इस्तेमाल किया जाता है।'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.hi)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

})

describe('error handling', function () {
  it('should return throw an error if specified language is not supported', function () {
    (function () {
      sw.getStopwords('xx')
    }).should.throw()
  })
})
<commit_msg>Update test.js<commit_after>/* global describe */
/* global it */

const should = require('should')
const sw = require('../lib/stopword.js')

describe('general stopwordiness:', function () {
  it('should remove stopwords, should default to english, should preserve case', function () {
    const oldString = 'a really Interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString)
    should.exist(sw.removeStopwords(oldString))
    newString.should.eql([ 'really', 'Interesting', 'string', 'words' ])
  })

  it('should remove custom stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, ['interesting'])
    newString.should.eql([ 'a', 'really', 'string', 'with', 'some', 'words' ])
  })

  it('should not remove any stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, [])
    newString.should.eql([ 'a', 'really', 'interesting', 'string', 'with', 'some', 'words' ])
  })

  it('should remove stopwords that have a non standard separator', function () {
    const oldString = 'a.really,interesting string.with,some.words'.split(/[\\., ]+/)
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'words' ])
  })

  it('should specify a custom input separator', function () {
    const oldString = 'a-really-interesting-string-with-some words'.split('-')
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'some words' ])
  })

  it('should remove norwegian stopwords', function () {
    const oldString = 'dette er en tekst som har nørske tegn øæåø øæåø æææ'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.no)
    newString.should.eql([ 'tekst', 'nørske', 'tegn', 'øæåø', 'øæåø', 'æææ' ])
  })

  it('should remove swedish stopwords and preserve case', function () {
    const oldString = 'Trädgårdsägare är beredda att pröva vad som helst för att bli av med de hatade mördarsniglarna åäö'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.sv)
    newString.should.eql([ 'Trädgårdsägare', 'beredda', 'pröva', 'helst', 'hatade', 'mördarsniglarna', 'åäö' ])
  })

  it('should remove danish stopwords', function () {
    const oldString = 'gæsterne i musikhuset i aarhus bør fremover sende en venlig tanke til den afdøde købmand herman salling når de skal til koncert eller se teater i det aarhusianske kulturhus æøå'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.da)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

  it('should remove hindu stopwords', function () {
    const oldString = 'केंद्र सरकार पर्यावरण के माकूल घरों ग्रीन होम्स को बढ़ावा देने की दिशा में गंभीरता से सोच रही है। ग्रीन हाउसिंग सोसायटी डिवेलप करने के लिए सरकार सस्ते लोन और कम रजिस्ट्रेशन फीस जैसी सुविधाएं देगी। क्लाइमेट चेंज से लड़ने की दिशा में केंद्र इस तरह की रिहायशी कॉलोनियां विकसित करने की दिशा में सोच रही है। बता दें कि ग्रीन होम्स वे घर हैं, जो पर्यावरण को ध्यान में रखकर विकसित किए जाते हैं। इनमें पर्यावरण के नजरिए से एनर्जी, जल संसाधन और बिल्डिंग मटीरियल्स का प्रभावशाली इस्तेमाल किया जाता है।'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.hi)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

})

describe('error handling', function () {
  it('should return throw an error if specified language is not supported', function () {
    (function () {
      sw.getStopwords('xx')
    }).should.throw()
  })
})
<|endoftext|>


<commit_before>'use strict';

var q = require('q'),
    fs = require('q-io/fs'),
    path = require('path'),
    Handlebars = require('handlebars'),
    Image = require('../../image');

var REPORT_DIR = 'gemini-report',
    REPORT_INDEX = path.join(REPORT_DIR, 'index.html'),
    REPORT_ATTACHMENTS = path.join(REPORT_DIR, 'attach'),

    REPORT_JS_NAME = 'report.js',
    REPORT_CSS_NAME = 'report.css',
    REPORT_TPL_SRC = path.join(__dirname, 'report.hbs'),
    SUITE_TPL_SRC = path.join(__dirname, 'suite.hbs'),
    REPORT_JS_SRC = path.join(__dirname, REPORT_JS_NAME),
    REPORT_CSS_SRC = path.join(__dirname, REPORT_CSS_NAME),

    REPORT_JS_DST = path.join(REPORT_DIR, REPORT_JS_NAME),
    REPORT_CSS_DST = path.join(REPORT_DIR, REPORT_CSS_NAME);

function suitePath(suite) {
    var result = '';
    while (suite) {
        result = path.join(suite.name, result);
        suite = suite.parent;
    }
    return result;
}

function attachmentStateDir(suite, stateName) {
    return path.join('attach', suitePath(suite), stateName);
}

// Path to attachments relative to root report
function attachmentRelPath(suite, result, kind) {
    return path.join(attachmentStateDir(suite, result.stateName),
        result.browserId + '~' + kind + '.png');
}

Handlebars.registerHelper('status', function() {
    if (this.skipped) {
        return 'section_status_skip';
    }
    return isOK(this)? 'section_status_success' : 'section_status_fail';
});

Handlebars.registerHelper('has-fails', function() {
    return this.failed > 0? 'summary__key_has-fails' : '';
});

Handlebars.registerHelper('image', function(kind) {
    return new Handlebars.SafeString(
            '');
});

Handlebars.registerHelper('stacktrace', function() {
    var error = this.error.originalError || this.error;
    return error.stack || error.message || error;
});

Handlebars.registerHelper('collapse', function() {
    return isOK(this) ? 'section_collapsed' : '';
});

function isOK(section) {
    if ('equal' in section) {
        return section.equal;
    }

    if ('skipped' in section) {
        return true;
    }

    if ('error' in section) {
        return false;
    }

    var result = true;
    if (section.children) {
        result = result && section.children.every(isOK);
    }

    if (section.states) {
        result = result && section.states.every(isOK);
    }

    if (section.browsers) {
        result = result && section.browsers.every(isOK);
    }
    return result;
}

module.exports = function htmlReporter(tester) {
    var currentSuite,
        attachmentsQueue,
        failed,
        passed,
        skipped;

    function createState(name) {
        var state = {
            name: name,
            browsers: []
        };

        currentSuite.states.push(state);
        currentSuite.statesByName[name] = state;

        createStateDir(state);

        return state;
    }

    function createStateDir(state) {
        attachmentsQueue.then(function() {
            var dir = path.resolve(REPORT_DIR, attachmentStateDir(currentSuite, state.name));
            return fs.makeTree(dir);
        });
    }

    tester.on('begin', function() {
        failed = passed = skipped = 0;
        currentSuite = {
            name: '',
            children: [],
            parent: null,
        };

        attachmentsQueue = fs.makeTree(REPORT_ATTACHMENTS);
    });

    tester.on('beginSuite', function(data) {
        var suite = {
            name: data.suiteName,
            states: [],
            statesByName: {},
            children: [],
            parent: currentSuite
        };
        currentSuite.children.push(suite);
        currentSuite = suite;
    });

    tester.on('endSuite', function(suiteName) {
        currentSuite = currentSuite.parent;
    });

    tester.on('beginState', function(suiteName, stateName) {
        if (currentSuite.statesByName[stateName]) {
            return;
        }

        createState(stateName);
    });

    tester.on('skipState', function(suiteName, stateName, browserId) {
        var state = currentSuite.statesByName[stateName];
        if (!state) {
            state = createState(stateName);
        }

        state.browsers.push({
            name: browserId,
            skipped: true
        });
        skipped++;
    });

    tester.on('endTest', function(result) {
        var state = currentSuite.statesByName[result.stateName],
            reportData = {
                name: result.browserId,
                tested: true,
                equal: result.equal,
                currentPath: attachmentRelPath(currentSuite, result, 'current'),
            };

        if (!result.equal) {
            failed++;
            reportData.referencePath = attachmentRelPath(currentSuite, result, 'ref');
            reportData.diffPath = attachmentRelPath(currentSuite, result, 'diff');
        } else {
            passed++;
        }

        state.browsers.push(reportData);

        attachmentsQueue.then(function() {
            var copyCurrent = fs.copy(result.currentPath, path.resolve(REPORT_DIR, reportData.currentPath));
            if (result.equal) {
                return copyCurrent;
            }
            return q.all([
                copyCurrent,
                fs.copy(result.referencePath, path.resolve(REPORT_DIR, reportData.referencePath)),
                Image.buildDiff(result.referencePath, result.currentPath,
                        path.resolve(REPORT_DIR, reportData.diffPath))
            ]);
        });
    });

    tester.on('error', function(error) {
        var state = currentSuite.statesByName[error.stateName];

        state.browsers.push({
            name: error.browserId,
            error: error
        });
        failed++;
    });

    tester.on('end', function () {
        attachmentsQueue
            .then(function() {
                return fs.read(SUITE_TPL_SRC);
            })
            .then(function(suiteTpl) {
                Handlebars.registerPartial('suite', suiteTpl);
                return fs.read(REPORT_TPL_SRC);
            })
            .then(function(tpl) {
                var tplFunc = Handlebars.compile(tpl);
                return tplFunc({
                    suites: currentSuite.children,
                    stats: {
                        total: failed + passed + skipped,
                        failed: failed,
                        passed: passed,
                        skipped: skipped
                    }
                });
            })
            .then(function(index) {
                return q.all([
                    fs.write(REPORT_INDEX, index),
                    fs.copy(REPORT_JS_SRC, path.resolve(REPORT_JS_DST)),
                    fs.copy(REPORT_CSS_SRC, path.resolve(REPORT_CSS_DST))
                ]);
            })
            .fail(function(e) {
                console.log(e.stack);
            })
            .done();
    });
};
<commit_msg>HTML reporter working<commit_after>'use strict';

var q = require('q'),
    fs = require('q-io/fs'),
    path = require('path'),
    Handlebars = require('handlebars'),
    Image = require('../../image');

var REPORT_DIR = 'gemini-report',
    REPORT_INDEX = path.join(REPORT_DIR, 'index.html'),
    REPORT_IMAGES = path.join(REPORT_DIR, 'images'),

    REPORT_JS_NAME = 'report.js',
    REPORT_CSS_NAME = 'report.css',
    REPORT_TPL_SRC = path.join(__dirname, 'report.hbs'),
    SUITE_TPL_SRC = path.join(__dirname, 'suite.hbs'),
    REPORT_JS_SRC = path.join(__dirname, REPORT_JS_NAME),
    REPORT_CSS_SRC = path.join(__dirname, REPORT_CSS_NAME),

    REPORT_JS_DST = path.join(REPORT_DIR, REPORT_JS_NAME),
    REPORT_CSS_DST = path.join(REPORT_DIR, REPORT_CSS_NAME);

function suitePath(suite) {
    var result = '';
    while (suite) {
        result = path.join(suite.name, result);
        suite = suite.parent;
    }
    return result;
}

function imageStateDir(suite, stateName) {
    return path.join('images', suitePath(suite), stateName);
}

// Path to images relative to root report
function imageRelPath(suite, result, kind) {
    return path.join(imageStateDir(suite, result.stateName),
        result.browserId + '~' + kind + '.png');
}

Handlebars.registerHelper('status', function() {
    if (this.skipped) {
        return 'section_status_skip';
    }
    return isOK(this)? 'section_status_success' : 'section_status_fail';
});

Handlebars.registerHelper('has-fails', function() {
    return this.failed > 0? 'summary__key_has-fails' : '';
});

Handlebars.registerHelper('image', function(kind) {
    return new Handlebars.SafeString(
            '');
});

Handlebars.registerHelper('stacktrace', function() {
    var error = this.error.originalError || this.error;
    return error.stack || error.message || error;
});

Handlebars.registerHelper('collapse', function() {
    return isOK(this) ? 'section_collapsed' : '';
});

function isOK(section) {
    if ('equal' in section) {
        return section.equal;
    }

    if ('skipped' in section) {
        return true;
    }

    if ('error' in section) {
        return false;
    }

    var result = true;
    if (section.children) {
        result = result && section.children.every(isOK);
    }

    if (section.states) {
        result = result && section.states.every(isOK);
    }

    if (section.browsers) {
        result = result && section.browsers.every(isOK);
    }
    return result;
}

module.exports = function htmlReporter(tester) {
    var rootSuite,
        browserSuites,
        imageCopyQueue,
        failed,
        passed,
        skipped;

    function createState(suite, name) {
        var state = {
            name: name,
            browsers: []
        };

        suite.states.push(state);
        suite.statesByName[name] = state;

        createStateDir(suite, state);

        return state;
    }

    function createStateDir(suite, state) {
        imageCopyQueue.then(function() {
            var dir = path.resolve(REPORT_DIR, imageStateDir(suite, state.name));
            return fs.makeTree(dir);
        });
    }

    tester.on('begin', function(data) {
        failed = passed = skipped = 0;
        rootSuite = {
            name: '',
            children: [],
            childrenByName: {},
            parent: null,
        };

        browserSuites = {};
        data.browserIds.forEach(function(browserId) {
            browserSuites[browserId] = rootSuite;
        });

        imageCopyQueue = fs.makeTree(REPORT_IMAGES);
    });

    tester.on('beginSuite', function(data) {
        var currentSuite = browserSuites[data.browserId],
            nextSuite = currentSuite.childrenByName[data.suiteName];

        if (!nextSuite) {
            nextSuite = {
                name: data.suiteName,
                states: [],
                statesByName: {},
                childrenByName: {},
                children: [],
                parent: currentSuite
            };
            currentSuite.children.push(nextSuite);
            currentSuite.childrenByName[nextSuite.name] = nextSuite;
        }
        browserSuites[data.browserId] = nextSuite;
    });

    tester.on('endSuite', function(data) {
        browserSuites[data.browserId] = browserSuites[data.browserId].parent;
    });

    tester.on('beginState', function(data) {
        var currentSuite = browserSuites[data.browserId];
        if (currentSuite.statesByName[data.stateName]) {
            return;
        }

        createState(currentSuite, data.stateName);

    });

    tester.on('skipState', function(data) {
        var currentSuite = browserSuites[data.browserId],
            state = currentSuite.statesByName[data.stateName];

        if (!state) {
            state = createState(currentSuite, data.stateName);
        }

        state.browsers.push({
            name: data.browserId,
            skipped: true
        });
        skipped++;
    });

    tester.on('endTest', function(result) {
        var currentSuite = browserSuites[result.browserId],
            state = currentSuite.statesByName[result.stateName],
            reportData = {
                name: result.browserId,
                tested: true,
                equal: result.equal,
                currentPath: imageRelPath(currentSuite, result, 'current'),
            };

        if (!result.equal) {
            failed++;
            reportData.referencePath = imageRelPath(currentSuite, result, 'ref');
            reportData.diffPath = imageRelPath(currentSuite, result, 'diff');
        } else {
            passed++;
        }

        state.browsers.push(reportData);

        imageCopyQueue.then(function() {
            var copyCurrent = fs.copy(result.currentPath, path.resolve(REPORT_DIR, reportData.currentPath));
            if (result.equal) {
                return copyCurrent;
            }
            return q.all([
                copyCurrent,
                fs.copy(result.referencePath, path.resolve(REPORT_DIR, reportData.referencePath)),
                Image.buildDiff(result.referencePath, result.currentPath,
                        path.resolve(REPORT_DIR, reportData.diffPath))
            ]);
        });
    });

    tester.on('error', function(error) {
        var currentSuite = browserSuites[error.browserId],
            state = currentSuite.statesByName[error.stateName];

        state.browsers.push({
            name: error.browserId,
            error: error
        });
        failed++;
    });

    tester.on('end', function () {
        imageCopyQueue
            .then(function() {
                return fs.read(SUITE_TPL_SRC);
            })
            .then(function(suiteTpl) {
                Handlebars.registerPartial('suite', suiteTpl);
                return fs.read(REPORT_TPL_SRC);
            })
            .then(function(tpl) {
                var tplFunc = Handlebars.compile(tpl);
                return tplFunc({
                    suites: rootSuite.children,
                    stats: {
                        total: failed + passed + skipped,
                        failed: failed,
                        passed: passed,
                        skipped: skipped
                    }
                });
            })
            .then(function(index) {
                return q.all([
                    fs.write(REPORT_INDEX, index),
                    fs.copy(REPORT_JS_SRC, path.resolve(REPORT_JS_DST)),
                    fs.copy(REPORT_CSS_SRC, path.resolve(REPORT_CSS_DST))
                ]);
            })
            .fail(function(e) {
                console.log(e.stack);
            })
            .done();
    });
};
<|endoftext|>


<commit_before><commit_msg>submodules claen up<commit_after><|endoftext|>


<commit_before>Elm.Native.WebSocket = {};
Elm.Native.WebSocket.make = function(elm) {

  elm.Native = elm.Native || {};
  elm.Native.WebSocket = elm.Native.WebSocket || {};
  if (elm.Native.WebSocket.values) return elm.Native.WebSocket.values;

  var Signal = Elm.Signal.make(elm);
  var List = Elm.Native.List.make(elm);

  function open(url, outgoing) {
    var incoming = Signal.constant("");
    var ws = new WebSocket(url);

    var pending = [];
    var ready = false;

    ws.onopen = function(e) {
      var len = pending.length;
      for (var i = 0; i < len; ++i) { ws.send(pending[i]); }
      ready = true;
    };
    ws.onmessage = function(event) {
      elm.notify(incoming.id, event.data);
    };

    function send(msg) {
      ready ? ws.send(msg) : pending.push(msg);
    }

    function take1(x,y) { return x }
    return A3(Signal.map2, F2(take1), incoming, A2(Signal.map, send, outgoing));
  }

  return elm.Native.WebSocket.values = { connect: F2(open) };
};
<commit_msg>elm -> localRuntime in WebSocket.js<commit_after>Elm.Native.WebSocket = {};
Elm.Native.WebSocket.make = function(localRuntime) {

  localRuntime.Native = localRuntime.Native || {};
  localRuntime.Native.WebSocket = localRuntime.Native.WebSocket || {};
  if (localRuntime.Native.WebSocket.values)
  {
      return localRuntime.Native.WebSocket.values;
  }

  var Signal = Elm.Signal.make(localRuntime);
  var List = Elm.Native.List.make(localRuntime);

  function open(url, outgoing) {
    var incoming = Signal.constant("");
    var ws = new WebSocket(url);

    var pending = [];
    var ready = false;

    ws.onopen = function(e) {
      var len = pending.length;
      for (var i = 0; i < len; ++i) { ws.send(pending[i]); }
      ready = true;
    };
    ws.onmessage = function(event) {
      localRuntime.notify(incoming.id, event.data);
    };

    function send(msg) {
      ready ? ws.send(msg) : pending.push(msg);
    }

    function take1(x,y) { return x }
    return A3(Signal.map2, F2(take1), incoming, A2(Signal.map, send, outgoing));
  }

  return localRuntime.Native.WebSocket.values = { connect: F2(open) };
};
<|endoftext|>


<commit_before>
var assert = require('assert')
var request = require('supertest')

require('polyfills')().clean()

var polyfills = require('..')
var options = {}
var etag

var chrome = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36'

var server = require('http').createServer(function (req, res) {
  polyfills(options)(req, res, function (err) {
    if (err) {
      res.statusCode = err.status || 500
      res.end(err.message)
      return
    }

    res.statusCode = 404
    res.end()
  })
})

describe('GET /polyfill.js', function () {
  it('should return the polyfill with a chrome user agent', function (done) {
    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Encoding', 'gzip')
    .expect('Content-Type', /application\/javascript/)
    .end(function (err, res) {
      assert.ifError(err)
      assert(!/\bPromise\b/.test(res.text))
      done()
    })
  })

  it('should minify with minify: true', function (done) {
    options = {
      minify: true
    }

    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Encoding', 'gzip')
    .expect('Content-Type', /application\/javascript/)
    .end(function (err, res) {
      assert.ifError(err)
      assert(!/\s{2,}/.test(res.text))
      etag = res.headers.etag
      done()
    })
  })

  it('should respect the etag', function (done) {
    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .set('if-none-match', etag)
    .expect(304)
    .expect('Content-Type', /application\/javascript/)
    .expect('', done)
  })

  it('should support maxage', function (done) {
    options.maxAge = '1 day'

    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Cache-Control', /\b86400\b/)
    .expect('Content-Type', /application\/javascript/, done)
  })
})

describe('HEAD /polyfill.js', function () {
  it('should return the headers', function (done) {
    request(server)
    .head('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Type', /application\/javascript/)
    .expect('', done)
  })
})

describe('OPTIONS /polyfill.js', function () {
  it('should 204', function (done) {
    request(server)
    .options('/polyfill.js')
    .set('User-Agent', chrome)
    .expect('Allow', /\bHEAD\b/)
    .expect('Allow', /\bGET\b/)
    .expect('Allow', /\bOPTIONS\b/)
    .expect(204, done)
  })
})

describe('PATCH /polyfills.js', function () {
  it('should 405', function (done) {
    request(server)
    .patch('/polyfill.js')
    .set('User-Agent', chrome)
    .expect('Allow', /\bHEAD\b/)
    .expect('Allow', /\bGET\b/)
    .expect('Allow', /\bOPTIONS\b/)
    .expect(405, done)
  })
})

describe('GET /', function () {
  it('should 404', function (done) {
    request(server)
    .get('/')
    .expect(404, done)
  })
})
<commit_msg>fix tests<commit_after>
var assert = require('assert')
var request = require('supertest')

require('polyfills')().clean()

var polyfills = require('..')
var options = {}
var etag

var chrome = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36'

var server = require('http').createServer(function (req, res) {
  polyfills(options)(req, res, function (err) {
    if (err) {
      res.statusCode = err.status || 500
      res.end(err.message)
      return
    }

    res.statusCode = 404
    res.end()
  })
})

describe('GET /polyfill.js', function () {
  it('should return the polyfill with a chrome user agent', function (done) {
    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Encoding', 'gzip')
    .expect('Content-Type', /application\/javascript/)
    .end(function (err, res) {
      assert.ifError(err)
      // assert(!/\bPromise\b/.test(res.text))
      done()
    })
  })

  it('should minify with minify: true', function (done) {
    options = {
      minify: true
    }

    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Encoding', 'gzip')
    .expect('Content-Type', /application\/javascript/)
    .end(function (err, res) {
      assert.ifError(err)
      assert(!/\}\s{2,}/.test(res.text))
      etag = res.headers.etag
      done()
    })
  })

  it('should respect the etag', function (done) {
    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .set('if-none-match', etag)
    .expect(304)
    .expect('Content-Type', /application\/javascript/)
    .expect('', done)
  })

  it('should support maxage', function (done) {
    options.maxAge = '1 day'

    request(server)
    .get('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Cache-Control', /\b86400\b/)
    .expect('Content-Type', /application\/javascript/, done)
  })
})

describe('HEAD /polyfill.js', function () {
  it('should return the headers', function (done) {
    request(server)
    .head('/polyfill.js')
    .set('User-Agent', chrome)
    .expect(200)
    .expect('Content-Type', /application\/javascript/)
    .expect('', done)
  })
})

describe('OPTIONS /polyfill.js', function () {
  it('should 204', function (done) {
    request(server)
    .options('/polyfill.js')
    .set('User-Agent', chrome)
    .expect('Allow', /\bHEAD\b/)
    .expect('Allow', /\bGET\b/)
    .expect('Allow', /\bOPTIONS\b/)
    .expect(204, done)
  })
})

describe('PATCH /polyfills.js', function () {
  it('should 405', function (done) {
    request(server)
    .patch('/polyfill.js')
    .set('User-Agent', chrome)
    .expect('Allow', /\bHEAD\b/)
    .expect('Allow', /\bGET\b/)
    .expect('Allow', /\bOPTIONS\b/)
    .expect(405, done)
  })
})

describe('GET /', function () {
  it('should 404', function (done) {
    request(server)
    .get('/')
    .expect(404, done)
  })
})
<|endoftext|>


<commit_before>/* global chrome, util */
/* exported sources */

// Keep a local cache of videos, since some collections link to host sites,
// but will often not contain enough metainfo of the videos, such
// as the thumbnail, views, even the user.
//
// Note that this is different from ajax cache.
var cachedVideos = new util.SizedMap(200, 'cachedVideos');
var patterns;
var sources = {
  patterns: [
    '*://www.youtube.com/watch?v=*',
    '*://youtu.be/*',
    '*://www.twitch.tv/*/v/*'
  ],

  // Sources which directly host videos.
  videos: {},

  // Sources that link to other websites' videos.
  collections: {},

  getVideos: function(options, callback) {
    function filterEnabled(type) {
      return Object.keys(sources[type])
        .filter(function(source) { return options[source]; })
        .map(function(source) {
          return function(callback) {
            sources[type][source](function(videos) {
              callback({ source: source, videos: videos || [] });
            });
          };
        });
    }

    // First, get videos directly from where they're hosted,
    // in case any of them are included in collection sites.
    util.parallel(filterEnabled('videos'), function(results) {
      var videos = [].concat.apply([], results.map(function(result) {
        result.videos.forEach(function(video) {
          video.source = result.source;
        });
        return result.videos;
      }));
      var videosMap = {};
      videos.forEach(function(video) {
        videosMap[video.url] = video;
        cachedVideos.push(video.url, video, true);
      });
      util.parallel(filterEnabled('collections'), function(results) {
        var colVideos = [].concat.apply([], results.map(function(result) {
          result.videos.forEach(function(video) {
            var col = { source: result.source };
            if (video.colUrl) {
              col.url = video.colUrl;
              delete video.colUrl;
            }
            video.collections = [col];
          });
          return result.videos;
        }));

        // If anything from a collection site is already in the list of
        // videos directly gathered from a hosting site, then merge them.
        // But give preference to the site hosting the video, in case
        // the user is following those channels.
        // That way, direct channel subscriptions get priority.
        colVideos.forEach(function(colVideo) {
          var video = videosMap[colVideo.url];
          if (video) {
            for (var key in colVideo) {
              if (key === 'collections') {
                if (video.collections) {
                  video.collections.push(colVideo.collections[0]);
                } else {
                  video.collections = colVideo.collections;
                }
              } else if (!video[key]) {
                video[key] = colVideo[key];

                // Keeping a copy of the collection username and title
                // for filtering.
              } else if (key === 'user') {
                colVideo.collections[0].username = colVideo.user.name;

              } else if (key === 'title') {
                colVideo.collections[0].title = colVideo.title;

              }
            }
          } else {
            colVideo.source = sources.sourceFromURL(colVideo.url);
            videosMap[colVideo.url] = colVideo;
          }
        });

        var allVideos = [];
        for (var url in videosMap) { allVideos.push(videosMap[url]); }
        callback(allVideos);
      });
    });
  },

  sourceFromURL: function(url) {
    var hostname = new URL(url).hostname;
    for (var source in sources.videos) {
      if (hostname.indexOf(source) > -1) {
        return source;
      }
    }
    return null;
  },

  getMetaForVideo: function(url, callback) {
    var parsed = new URL(url);
    var host = parsed.host;
    var id;
    if (host === 'youtu.be' || host === 'www.twitch.tv') {
      var s = parsed.pathname.split(/\//);
      id = s[s.length - 1];
    } else if (host === 'www.youtube.com') {
      var r = /v=([^&$]+)/.exec(parsed.search);
      if (r) {
        id = r[1];
      } else {
        console.warn('Could not get video ID of URL: ' + url);
        return null;
      }
    }

    if (cachedVideos.has(url)) {
      callback(cachedVideos.get(url));

    } else if (host === 'www.youtube.com' || host === 'youtu.be') {
      util.ajax('https://www.youtube.com/get_video_info' +
      '?ps=default&gl=US&hl=en&video_id=' + id, {
        cache: function(response) {
          return {
            length: parseInt(response.length_seconds, 10),
            title: response.title,
            views: parseInt(response.view_count, 10),
            user: { name: response.author },
          };
        },
      }, function(xhr, meta) {
        if (!meta) { return callback(); }
        callback({
          // Canonical form of URL.
          url: 'https://www.youtube.com/watch?v=' + id,

          // Using medium quality gives a screenshot without black bars.
          thumbnail: 'https://i.ytimg.com/vi/' + id +
            '/mqdefault.jpg?custom=true&w=196&h=110&stc=true&jpg444=true&' +
            'jpgq=90&sp=68',

          length: meta.length,
          title: meta.title,
          views: meta.views,
          user: meta.user,
          game: null,
        });
      });

    } else if (host === 'www.twitch.tv') {
      util.ajax('https://api.twitch.tv/kraken/videos/v' + id, {
        cache: function(response) {
          return {
            thumbnail : response.preview,
            length    : response.length,
            title     : response.title,
            game      : response.game,
            views     : response.views,
          };
        },
      }, function(xhr, meta) {
        if (!meta) { return callback(null); }
        var username = /twitch\.tv\/([^\/]+)\//.exec(url)[1];
        callback({
          url       : url,
          thumbnail : meta.thumbnail,
          length    : meta.length,
          title     : meta.title,
          game      : meta.game,
          views     : meta.views,
          user      : {
            url: 'https://www.twitch.tv/' + username,
            name: username,
          }
        });
      });

    } else {
      console.warn('No thumbnail generated from URL: ' + url);
      callback();
    }
  },

  isVideoPage: function(url) {
    return patterns.some(function(pattern) {
      return pattern.test(url);
    });
  },

};

patterns = sources.patterns.map(util.minimatch);

function addMetaToVideo(video, callback) {
  sources.getMetaForVideo(video.url, function(meta) {
    if (!meta) { return callback(); }
    video.url = meta.url;
    video.thumbnail = meta.thumbnail;
    video.length = meta.length;

    // Views and title can update later, so don't include these when getting
    // metainfo from cache.
    ['game', 'title', 'views', 'user'].forEach(function(field) {
      if (meta[field] && !video[field]) {
        video[field] = meta[field];
      }
    });
    callback();
  });
}

sources.videos.youtube = function(callback) {
  util.ajax('https://www.youtube.com/feed/subscriptions', function(xhr, body) {
    if (!body) { return callback(); }
    var $items = body.getElementById('browse-items-primary');
    if (!$items) {
      console.error('Not logged in');
      callback();
      return;
    }
    $items = $items.children[0];
    var items = [];
    for (var i = 0, len = $items.children.length; i < len; i++) {
      var $item = $items.children[i];
      var $user = $item
        .getElementsByClassName('branded-page-module-title')[0].children[0];
      var $thumb = $item.getElementsByClassName('yt-lockup-thumbnail')[1];
      var $length = $thumb.getElementsByClassName('video-time')[0];
      var $content = $item.getElementsByClassName('yt-lockup-content')[0];
      var $meta = $content.getElementsByClassName('yt-lockup-meta-info')[0];
      var hasMeta = $meta.children.length > 1;
      var time = hasMeta ? $meta.children[0].textContent : null;
      var views = hasMeta ?
        parseInt($meta.children[1].textContent.replace(/,/g, ''), 0) : null;
      var $starts = $meta.getElementsByClassName('localized-date')[0];
      var timestamp = $starts ?
        parseInt($starts.getAttribute('data-timestamp'), 10) * 1000 :
        // Add i to relative timestamp so that videos that say they were
        // posted at the same time (relatively) are still ordered in the
        // order that they are on the page.
        hasMeta ? util.relativeToTimestamp(time) - i : Date.now() - i;
      var $desc = $content.getElementsByClassName('yt-lockup-description')[0];

      // YouTube videos sometimes don't have thumbnails loaded until
      // the page is scrolle down.
      var url = $thumb.children[0].href;
      var $img = $thumb.getElementsByTagName('img')[0];
      var thumbnail =
        $img.src === 'https://s.ytimg.com/yts/img/pixel-vfl3z5WfW.gif' ?
        thumbnail = $img.getAttribute('data-thumb') : $img.src;
      if (thumbnail.indexOf('//') === 0) {
        thumbnail = 'https:' + thumbnail;
      }

      items.push({
        user: {
          url: $user.href,
          thumbnail: $user.getElementsByTagName('img')[0].src,
          name: $user.children[1].textContent,
          verified: !!$content
            .getElementsByClassName('yt-channel-title-icon-verified').length
        },
        url: url,
        thumbnail: thumbnail,
        length: $length ? util.timeToSeconds($length.textContent) : null,
        title: $content.children[0].children[0].textContent,
        timestamp: timestamp,
        live: !!$content.getElementsByClassName('yt-badge-live').length,
        views: views,
        desc: $desc ? $desc.innerHTML : '',
        watched: !!$thumb.getElementsByClassName('watched-badge').length,
      });
    }
    callback(items);
  });
};

sources.videos.twitch = function(callback) {
  chrome.cookies.get({
    url: 'https://www.twitch.tv/directory/following/videos',
    name: 'api_token',
  }, function(cookie) {
    util.ajax('https://api.twitch.tv/kraken/videos/followed?' +
    'limit=40&broadcast_type=highlight&offset=0&on_site=1', {
      headers: { 'Twitch-Api-Token': cookie.value },
    }, function(xhr, result) {
      if (!result || !result.videos) { return callback(); }
      callback(result.videos.map(function(video) {
        return {
          user: {
            url: 'https://www.twitch.tv/' + video.channel.name,
            name: video.channel.display_name,
          },
          url: video.url.replace(/^https:\/\/secure\./, 'https://www.'),
          thumbnail: video.preview,
          length: video.length,
          title: video.title,
          timestamp: new Date(video.created_at).getTime(),
          views: video.views,
          desc: video.description,
          game: video.game,
        };
      }));
    });
  });
};

sources.collections.haloruns = function(callback) {
  util.ajax('http://haloruns.com/records?recent', function(xhr, body) {
    if (!body) { return callback(); }
    var $items = body.getElementById('recentWRTable');
    if (!$items) {
      console.warn('Error retrieving videos');
      callback();
      return;
    }
    $items = $items.children[0];
    var items = [];
    for (var i = 1, len = Math.min($items.children.length, 21); i < len; i++) {
      var $item = $items.children[i];
      var $col1 = $item.children[0];
      var $col2 = $item.children[1];
      var $col3 = $item.children[2];
      var $col4 = $item.children[3];
      var $col5 = $item.children[4];
      var date = new Date($col1.childNodes[0].nodeValue + ' ' +
        $col1.childNodes[2].nodeValue.replace('Eastern', 'EST'));
      var $level = $col2.children[0];
      var gameSplit = $col2.children[1].textContent.split(' ');
      var game = gameSplit.slice(0, -1).join(' ');

      // "Halo 5" is actually called "Halo 5: Guardians"
      if (game === 'Halo 5') {
        game += ': Guardians';
      }
      var difficulty = gameSplit[gameSplit.length - 1];
      var $previousRecord = $col3.children[0];
      $previousRecord.textContent =
        $previousRecord.textContent.replace(/ /g, '');
      var $previousUser = $col3.children[1].children[0];
      var $newRecord = $col4.children[0];
      var $newUser = $col4.children[1].children[0];
      var timeSaved = $col5.textContent;

      var url = $newRecord.href;
      items.push({
        user: {
          url: $newUser.href,
          thumbnail: null,
          name: $newUser.textContent,
        },
        url: url,
        thumbnail: null,
        length: util.timeToSeconds($newRecord.textContent),
        title: game + ' ' + difficulty + ' - ' + $level.textContent +
          ' (' + $newRecord.textContent.replace(/ /g, '') + ')',
        timestamp: date.getTime(),
        desc: 'Previous Record: ' + $previousRecord.outerHTML +
          ' by ' + $previousUser.outerHTML + '<br />' +
          'Time Saved: ' + timeSaved,
        game: game,
      });
    }
    util.parallelMap(items, addMetaToVideo, callback.bind(null, items));
  });
};

var speedrundotcomKey = localStorage.getItem('speedrundotcomKey');
sources.collections.speedrundotcom = function(callback) {
  function getMetaForRun(url, callback) {
    util.ajax(url, {
      cache: function(response) {
        return {
          url: response.data.videos.links[0].uri,
          desc: response.data.comment,
        };
      },
    }, function(xhr, meta) {
      callback(meta);
    });
  }

  function addMetaToRun(run, callback) {
    getMetaForRun(run.url, function(meta) {
      run.url = meta.url;
      run.desc = meta.desc;
      addMetaToVideo(run, callback);
    });
  }

  function getNotifications() {
    util.ajax('http://www.speedrun.com/api/v1/notifications', {
      headers: { 'X-API-Key': speedrundotcomKey },
    }, function(xhr, results) {
      if (!results) { return callback(); }
      var runs = results.data
        .filter(function(noti) { return noti.item.rel === 'run'; })
        .map(function(noti) {
          return {
            id: noti.id,
            title: noti.text,
            url: noti.links[0].uri,
            colUrl: noti.item.uri,
            timestamp: new Date(noti.created).getTime(),
          };
        });
      util.parallelMap(runs, addMetaToRun, callback.bind(null, runs));
    });
  }

  if (!speedrundotcomKey) {
    util.ajax('http://www.speedrun.com/settings', function(xhr, body) {
      if (!body) { return callback(); }
      var $code = body.getElementsByTagName('code')[0];
      if (!$code) {
        console.warn('Unable to retrieve API token from speedrun.com');
        return callback();
      }
      var key = $code.textContent;
      speedrundotcomKey = key;
      localStorage.setItem('speedrundotcomKey', key);
      getNotifications();
    });
  } else {
    getNotifications();
  }
};
<commit_msg>organize sources better so future sources are easier to add<commit_after>/* global chrome, util */
/* exported sources */

// Keep a local cache of videos, since some collections link to host sites,
// but will often not contain enough metainfo of the videos, such
// as the thumbnail, views, even the user.
//
// Note that this is different from ajax cache.
var cachedVideos = new util.SizedMap(200, 'cachedVideos');
var sources = {
  // Sources which directly host videos.
  videos: {},

  // Sources that link to other websites' videos.
  collections: {},

  getVideos: function(options, callback) {
    function filterEnabled(type, isHost) {
      return Object.keys(sources[type])
        .filter(function(source) { return options[source]; })
        .map(function(source) {
          return function(callback) {
            var fn = sources[type][source];
            if (isHost) {
              fn = fn.getAllVideos;
            }
            fn(function(videos) {
              callback({ source: source, videos: videos || [] });
            });
          };
        });
    }

    // First, get videos directly from where they're hosted,
    // in case any of them are included in collection sites.
    util.parallel(filterEnabled('videos', true), function(results) {
      var videos = [].concat.apply([], results.map(function(result) {
        result.videos.forEach(function(video) {
          video.source = result.source;
        });
        return result.videos;
      }));
      var videosMap = {};
      videos.forEach(function(video) {
        videosMap[video.url] = video;
        cachedVideos.push(video.url, video, true);
      });
      util.parallel(filterEnabled('collections', false), function(results) {
        var colVideos = [].concat.apply([], results.map(function(result) {
          result.videos.forEach(function(video) {
            var col = { source: result.source };
            if (video.colUrl) {
              col.url = video.colUrl;
              delete video.colUrl;
            }
            video.collections = [col];
          });
          return result.videos;
        }));

        // If anything from a collection site is already in the list of
        // videos directly gathered from a hosting site, then merge them.
        // But give preference to the site hosting the video, in case
        // the user is following those channels.
        // That way, direct channel subscriptions get priority.
        colVideos.forEach(function(colVideo) {
          var video = videosMap[colVideo.url];
          if (video) {
            for (var key in colVideo) {
              if (key === 'collections') {
                if (video.collections) {
                  video.collections.push(colVideo.collections[0]);
                } else {
                  video.collections = colVideo.collections;
                }
              } else if (!video[key]) {
                video[key] = colVideo[key];

                // Keeping a copy of the collection username and title
                // for filtering.
              } else if (key === 'user') {
                colVideo.collections[0].username = colVideo.user.name;

              } else if (key === 'title') {
                colVideo.collections[0].title = colVideo.title;

              }
            }
          } else {
            colVideo.source = sources.sourceFromURL(colVideo.url);
            videosMap[colVideo.url] = colVideo;
          }
        });

        var allVideos = [];
        for (var url in videosMap) { allVideos.push(videosMap[url]); }
        callback(allVideos);
      });
    });
  },

  getMetaForVideo: function(url, callback) {
    if (cachedVideos.has(url)) {
      callback(cachedVideos.get(url));
    } else {
      var source = sources.sourceFromURL(url);
      if (source) {
        sources.videos[source].getVideo(url, callback);
      } else {
        console.warn('No thumbnail generated from URL: ' + url);
        callback();
      }
    }
  },

  addMetaToVideo: function(video, callback) {
    sources.getMetaForVideo(video.url, function(meta) {
      if (!meta) { return callback(); }
      video.url = meta.url;
      video.thumbnail = meta.thumbnail;
      video.length = meta.length;

      // Views and title can update later, so don't include these when getting
      // metainfo from cache.
      ['game', 'title', 'views', 'user'].forEach(function(field) {
        if (meta[field] && !video[field]) {
          video[field] = meta[field];
        }
      });
      callback();
    });
  },

  sourceFromURL: function(url) {
    for (var source in sources.videos) {
      if (sources.videos[source]._patterns.some(function(pattern) {
        return pattern.test(url);
      })) {
        return source;
      }
    }
    return null;
  },

  isVideoPage: function(url) {
    return !!sources.sourceFromURL(url);
  },
};

sources.videos.youtube = {
  patterns: [
    '*://www.youtube.com/watch?v=*',
    '*://youtu.be/*',
  ],
  getVideo: function(url, callback) {
    var r = /(?:v=|youtu\.be\/)([^?&$]+)/.exec(url);
    var id;
    if (r) {
      id = r[1];
    } else {
      console.warn('Could not get video ID of URL: ' + url);
      return callback();
    }
    util.ajax('https://www.youtube.com/get_video_info' +
    '?ps=default&gl=US&hl=en&video_id=' + id, {
      cache: function(response) {
        if (response.status === 'fail') {
          return callback();
        }
        return {
          length: parseInt(response.length_seconds, 10),
          title: response.title,
          views: parseInt(response.view_count, 10),
          user: { name: response.author },
        };
      },
    }, function(xhr, meta) {
      if (!meta) { return callback(); }
      callback({
        // Canonical form of URL.
        url: 'https://www.youtube.com/watch?v=' + id,

        // Using medium quality gives a screenshot without black bars.
        thumbnail: 'https://i.ytimg.com/vi/' + id +
          '/mqdefault.jpg?custom=true&w=196&h=110&stc=true&jpg444=true&' +
          'jpgq=90&sp=68',

        length: meta.length,
        title: meta.title,
        views: meta.views,
        user: meta.user,
        game: null,
      });
    });
  },
  getAllVideos: function(callback) {
    util.ajax('https://www.youtube.com/feed/subscriptions',
    function(xhr, body) {
      if (!body) { return callback(); }
      var $items = body.getElementById('browse-items-primary');
      if (!$items) {
        console.error('Not logged in');
        callback();
        return;
      }
      $items = $items.children[0];
      var items = [];
      for (var i = 0, len = $items.children.length; i < len; i++) {
        var $item = $items.children[i];
        var $user = $item
          .getElementsByClassName('branded-page-module-title')[0].children[0];
        var $thumb = $item.getElementsByClassName('yt-lockup-thumbnail')[1];
        var $length = $thumb.getElementsByClassName('video-time')[0];
        var $content = $item.getElementsByClassName('yt-lockup-content')[0];
        var $meta = $content.getElementsByClassName('yt-lockup-meta-info')[0];
        var hasMeta = $meta.children.length > 1;
        var time = hasMeta ? $meta.children[0].textContent : null;
        var views = hasMeta ?
          parseInt($meta.children[1].textContent.replace(/,/g, ''), 0) : null;
        var $starts = $meta.getElementsByClassName('localized-date')[0];
        var timestamp = $starts ?
          parseInt($starts.getAttribute('data-timestamp'), 10) * 1000 :
          // Add i to relative timestamp so that videos that say they were
          // posted at the same time (relatively) are still ordered in the
          // order that they are on the page.
          hasMeta ? util.relativeToTimestamp(time) - i : Date.now() - i;
        var $desc = $content.getElementsByClassName('yt-lockup-description')[0];

        // YouTube videos sometimes don't have thumbnails loaded until
        // the page is scrolle down.
        var url = $thumb.children[0].href;
        var $img = $thumb.getElementsByTagName('img')[0];
        var thumbnail =
          $img.src === 'https://s.ytimg.com/yts/img/pixel-vfl3z5WfW.gif' ?
          thumbnail = $img.getAttribute('data-thumb') : $img.src;
        if (thumbnail.indexOf('//') === 0) {
          thumbnail = 'https:' + thumbnail;
        }

        items.push({
          user: {
            url: $user.href,
            thumbnail: $user.getElementsByTagName('img')[0].src,
            name: $user.children[1].textContent,
            verified: !!$content
              .getElementsByClassName('yt-channel-title-icon-verified').length
          },
          url: url,
          thumbnail: thumbnail,
          length: $length ? util.timeToSeconds($length.textContent) : null,
          title: $content.children[0].children[0].textContent,
          timestamp: timestamp,
          live: !!$content.getElementsByClassName('yt-badge-live').length,
          views: views,
          desc: $desc ? $desc.innerHTML : '',
          watched: !!$thumb.getElementsByClassName('watched-badge').length,
        });
      }
      callback(items);
    });
  },
};

sources.videos.twitch = {
  patterns: [
    '*://www.twitch.tv/*/v/*'
  ],
  getVideo: function(url, callback) {
    var parsed = new URL(url);
    var s = parsed.pathname.split(/\//);
    var id = s[s.length - 1];
    util.ajax('https://api.twitch.tv/kraken/videos/v' + id, {
      cache: function(response) {
        return {
          thumbnail : response.preview,
          length    : response.length,
          title     : response.title,
          game      : response.game,
          views     : response.views,
        };
      },
    }, function(xhr, meta) {
      if (!meta) { return callback(null); }
      var username = /twitch\.tv\/([^\/]+)\//.exec(url)[1];
      callback({
        url       : url,
        thumbnail : meta.thumbnail,
        length    : meta.length,
        title     : meta.title,
        game      : meta.game,
        views     : meta.views,
        user      : {
          url: 'https://www.twitch.tv/' + username,
          name: username,
        }
      });
    });
  },
  getAllVideos: function(callback) {
    chrome.cookies.get({
      url: 'https://www.twitch.tv/directory/following/videos',
      name: 'api_token',
    }, function(cookie) {
      util.ajax('https://api.twitch.tv/kraken/videos/followed?' +
      'limit=40&broadcast_type=highlight&offset=0&on_site=1', {
        headers: { 'Twitch-Api-Token': cookie.value },
      }, function(xhr, result) {
        if (!result || !result.videos) { return callback(); }
        callback(result.videos.map(function(video) {
          return {
            user: {
              url: 'https://www.twitch.tv/' + video.channel.name,
              name: video.channel.display_name,
            },
            url: video.url.replace(/^https:\/\/secure\./, 'https://www.'),
            thumbnail: video.preview,
            length: video.length,
            title: video.title,
            timestamp: new Date(video.created_at).getTime(),
            views: video.views,
            desc: video.description,
            game: video.game,
          };
        }));
      });
    });
  },
};

sources.collections.haloruns = function(callback) {
  util.ajax('http://haloruns.com/records?recent', function(xhr, body) {
    if (!body) { return callback(); }
    var $items = body.getElementById('recentWRTable');
    if (!$items) {
      console.warn('Error retrieving videos');
      callback();
      return;
    }
    $items = $items.children[0];
    var items = [];
    for (var i = 1, len = Math.min($items.children.length, 21); i < len; i++) {
      var $item = $items.children[i];
      var $col1 = $item.children[0];
      var $col2 = $item.children[1];
      var $col3 = $item.children[2];
      var $col4 = $item.children[3];
      var $col5 = $item.children[4];
      var date = new Date($col1.childNodes[0].nodeValue + ' ' +
        $col1.childNodes[2].nodeValue.replace('Eastern', 'EST'));
      var $level = $col2.children[0];
      var gameSplit = $col2.children[1].textContent.split(' ');
      var game = gameSplit.slice(0, -1).join(' ');

      // "Halo 5" is actually called "Halo 5: Guardians"
      if (game === 'Halo 5') {
        game += ': Guardians';
      }
      var difficulty = gameSplit[gameSplit.length - 1];
      var $previousRecord = $col3.children[0];
      $previousRecord.textContent =
        $previousRecord.textContent.replace(/ /g, '');
      var $previousUser = $col3.children[1].children[0];
      var $newRecord = $col4.children[0];
      var $newUser = $col4.children[1].children[0];
      var timeSaved = $col5.textContent;

      var url = $newRecord.href;
      items.push({
        user: {
          url: $newUser.href,
          thumbnail: null,
          name: $newUser.textContent,
        },
        url: url,
        thumbnail: null,
        length: util.timeToSeconds($newRecord.textContent),
        title: game + ' ' + difficulty + ' - ' + $level.textContent +
          ' (' + $newRecord.textContent.replace(/ /g, '') + ')',
        timestamp: date.getTime(),
        desc: 'Previous Record: ' + $previousRecord.outerHTML +
          ' by ' + $previousUser.outerHTML + '<br />' +
          'Time Saved: ' + timeSaved,
        game: game,
      });
    }
    util.parallelMap(items, sources.addMetaToVideo,
      callback.bind(null, items));
  });
};

var speedrundotcomKey = localStorage.getItem('speedrundotcomKey');
sources.collections.speedrundotcom = function(callback) {
  function getMetaForRun(url, callback) {
    util.ajax(url, {
      cache: function(response) {
        return {
          url: response.data.videos.links[0].uri,
          desc: response.data.comment,
        };
      },
    }, function(xhr, meta) {
      callback(meta);
    });
  }

  function addMetaToRun(run, callback) {
    getMetaForRun(run.url, function(meta) {
      run.url = meta.url;
      run.desc = meta.desc;
      sources.addMetaToVideo(run, callback);
    });
  }

  function getNotifications() {
    util.ajax('http://www.speedrun.com/api/v1/notifications', {
      headers: { 'X-API-Key': speedrundotcomKey },
    }, function(xhr, results) {
      if (!results) { return callback(); }
      var runs = results.data
        .filter(function(noti) { return noti.item.rel === 'run'; })
        .map(function(noti) {
          return {
            id: noti.id,
            title: noti.text,
            url: noti.links[0].uri,
            colUrl: noti.item.uri,
            timestamp: new Date(noti.created).getTime(),
          };
        });
      util.parallelMap(runs, addMetaToRun, callback.bind(null, runs));
    });
  }

  if (!speedrundotcomKey) {
    util.ajax('http://www.speedrun.com/settings', function(xhr, body) {
      if (!body) { return callback(); }
      var $code = body.getElementsByTagName('code')[0];
      if (!$code) {
        console.warn('Unable to retrieve API token from speedrun.com');
        return callback();
      }
      var key = $code.textContent;
      speedrundotcomKey = key;
      localStorage.setItem('speedrundotcomKey', key);
      getNotifications();
    });
  } else {
    getNotifications();
  }
};

sources.patterns = [];
for (var source in sources.videos) {
  sources.videos[source]._patterns =
    sources.videos[source].patterns.map(util.minimatch);
  sources.patterns = sources.patterns.concat(sources.videos[source].patterns);
}
<|endoftext|>