<commit_before>"use strict";

var popupGlobal = {
    feeds: [],
    savedFeeds: [],
    backgroundPage: chrome.extension.getBackgroundPage(),
    isSidebar: false
};

$(document).ready(async function () {
    $("#feed, #feed-saved").css("font-size", popupGlobal.backgroundPage.appGlobal.options.popupFontSize / 100 + "em");
    $("#website").text(chrome.i18n.getMessage("FeedlyWebsite"));
    $("#mark-all-read>span").text(chrome.i18n.getMessage("MarkAllAsRead"));
    $("#mark-read-engagement>span").text(chrome.i18n.getMessage("MarkAsReadEngagement"));
    $("#update-feeds>span").text(chrome.i18n.getMessage("UpdateFeeds"));
    $("#open-all-news>span").text(chrome.i18n.getMessage("OpenAllFeeds"));
    $("#open-unsaved-all-news>span").text(chrome.i18n.getMessage("OpenAllSavedFeeds"));

    if (popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds) {
        $("#popup-content").addClass("tabs");
    }

    popupGlobal.isSidebar = browser && browser.sidebarAction && await browser.sidebarAction.isOpen({});
    setPopupWidth(false);

    executeAsync(renderFeeds);
});

$("#login").click(function () {
    popupGlobal.backgroundPage.getAccessToken(function() {
	setTimeout(renderFeeds, 500);
    });
});

//using "mousedown" instead of "click" event to process middle button click.
$("#feed, #feed-saved").on("mousedown", "a", function (event) {
    var link = $(this);
    if (event.which === 1 || event.which === 2) {
        var isActiveTab = !(event.ctrlKey || event.which === 2) && !popupGlobal.backgroundPage.appGlobal.options.openFeedsInBackground;
        var isFeed = link.hasClass("title") && $("#feed").is(":visible");
        var url = link.data("link");

        if (isFeed && popupGlobal.backgroundPage.appGlobal.feedTabId && popupGlobal.backgroundPage.appGlobal.options.openFeedsInSameTab) {
            chrome.tabs.update(popupGlobal.backgroundPage.appGlobal.feedTabId, {url: url}, function(tab) {
                onOpenCallback(isFeed, tab);
            });
        } else {
            chrome.tabs.create({url: url, active: isActiveTab }, function(tab) {
                onOpenCallback(isFeed, tab);
            });
        }
    }

    function onOpenCallback(isFeed, tab) {
        if (isFeed) {
            popupGlobal.backgroundPage.appGlobal.feedTabId = tab.id;

            if (popupGlobal.backgroundPage.appGlobal.options.markReadOnClick) {
                markAsRead([link.closest(".item").data("id")]);
            }
        }
    }
});

$("#popup-content").on("click", "#mark-all-read", markAllAsRead);

$("#popup-content").on("click", "#mark-read-engagement", markAsReadEngagement);

$("#popup-content").on("click", "#open-all-news", function () {
    $("#feed").find("a.title[data-link]").filter(":visible").each(function (key, value) {
        var news = $(value);
        chrome.tabs.create({url: news.data("link"), active: false }, function () {});
    });
    if (popupGlobal.backgroundPage.appGlobal.options.markReadOnClick) {
        markAllAsRead();
    }
});

$("#popup-content").on("click", "#open-unsaved-all-news", function () {
   $("#feed-saved").find("a.title[data-link]").filter(":visible").each(function (key, value) {
           var news = $(value);
           chrome.tabs.create({url: news.data("link"), active: false }, function () {});
       });
        markAllAsUnsaved();
});

$("#feed").on("click", ".mark-read", function (event) {
    var feed = $(this).closest(".item");
    markAsRead([feed.data("id")]);
});

$("#feedly").on("click", "#btn-feeds-saved", function () {
    $(this).addClass("active-tab");
    $("#btn-feeds").removeClass("active-tab");
    renderSavedFeeds();
});

$("#feedly").on("click", "#btn-feeds", function () {
    $(this).addClass("active-tab");
    $("#btn-feeds-saved").removeClass("active-tab");
    renderFeeds();
});

$("#popup-content").on("click", ".show-content", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var contentContainer = feed.find(".content");
    var feedId = feed.data("id");

    if (!contentContainer.html()) {
        var feeds = $("#feed").is(":visible") ? popupGlobal.feeds : popupGlobal.savedFeeds;

        var template = $("#feed-content").html();
        Mustache.parse(template);
        for (let feed of feeds) {
            if (feed.id === feedId) {

                // @if BROWSER='firefox'
                // We should sanitize the content of feeds because of AMO review.
                feed.title = DOMPurify.sanitize(feed.title);
                feed.content = DOMPurify.sanitize(feed.content);
                // @endif

                contentContainer.html(Mustache.render(template, feed));
		if (popupGlobal.isSidebar)
		    contentContainer.css({width: "95%", marginLeft: 5});

                //For open new tab without closing popup
                contentContainer.find("a").each(function (key, value) {
                    var link = $(value);
                    link.data("link", link.attr("href"));
                    link.attr("href", "javascript:void(0)");
                });
            }
        }
    }
    contentContainer.slideToggle("fast", function () {
        $this.toggleClass("glyphicon-chevron-down");
        $this.toggleClass("glyphicon-chevron-up");

        var expanded = $(".content").is(":visible");
        setPopupWidth(expanded);
    });
});

/* Manually feeds update */
$("#feedly").on("click", "#update-feeds", function () {
    if ($("#feed").is(":visible")) {
        renderFeeds(true);
    } else {
        renderSavedFeeds(true);
    }
});

/* Save or unsave feed */
$("#popup-content").on("click", ".save-feed", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var feedId = feed.data("id");
    var saveItem = !$this.data("saved");
    popupGlobal.backgroundPage.toggleSavedFeed([feedId], saveItem);
    $this.data("saved", saveItem);
    $this.toggleClass("saved");
});

$("#popup-content").on("click", "#website", function(){
    popupGlobal.backgroundPage.openFeedlyTab();

    // Close the popup since the user wants to see Feedly website anyway
    window.close();
});

$("#popup-content").on("click", ".categories > span", function (){
    $(".categories").find("span").removeClass("active");
    var button = $(this).addClass("active");
    var categoryId = button.data("id");
    if (categoryId) {
        $(".item").hide();
        $(".item[data-categories~='" + categoryId + "']").show();
    } else {
        $(".item").show();
    }
});

$("#feedly").on("click", "#feedly-logo", function (event) {
    if (event.ctrlKey) {
        popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds = !popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds;
        location.reload();
    }
});

function executeAsync(func) {
    const timeout = popupGlobal.backgroundPage.appGlobal.environment.os === "mac" ? 500 : 0;
    setTimeout(function () {
        func();
    }, timeout);
}

function renderFeeds(forceUpdate) {
    showLoader();
    popupGlobal.backgroundPage.getFeeds(popupGlobal.backgroundPage.appGlobal.options.forceUpdateFeeds || forceUpdate, function (feeds, isLoggedIn) {
        popupGlobal.feeds = feeds;
        if (isLoggedIn === false) {
            showLogin();
        } else {
            if (feeds.length === 0) {
                showEmptyContent();
            } else {
                var container = $("#feed").show().empty();

                if (popupGlobal.backgroundPage.appGlobal.options.showCategories) {
                    renderCategories(container, feeds);
                }

                var feedsTemplate = $("#feedTemplate").html();
                Mustache.parse(feedsTemplate);

                container.append(Mustache.render(feedsTemplate, {feeds: feeds}));
                renderTimeAgo(container);

                showFeeds();

                if (popupGlobal.backgroundPage.appGlobal.options.expandFeeds) {
                    container.find(".show-content").click();
                }
            }
        }
    });
}

function renderSavedFeeds(forceUpdate) {
    showLoader();
    popupGlobal.backgroundPage.getSavedFeeds(popupGlobal.backgroundPage.appGlobal.options.forceUpdateFeeds || forceUpdate, function (feeds, isLoggedIn) {
        popupGlobal.savedFeeds = feeds;
        if (isLoggedIn === false) {
            showLogin();
        } else {
            if (feeds.length === 0) {
                showEmptyContent();
            } else {
                var container = $("#feed-saved").empty();

                if (popupGlobal.backgroundPage.appGlobal.options.showCategories) {
                    renderCategories(container, feeds);
                }

                var feedTemplate = $("#feedTemplate").html();
                Mustache.parse(feedTemplate);

                container.append(Mustache.render(feedTemplate, {feeds: feeds}));
                renderTimeAgo(container);

                showSavedFeeds();

                if (popupGlobal.backgroundPage.appGlobal.options.expandFeeds) {
                    container.find(".show-content").click();
                }
            }
        }
    });
}

function markAsRead(feedIds) {
    var feedItems = $();
    for (var i = 0; i < feedIds.length; i++) {
        feedItems = feedItems.add(".item[data-id='" + feedIds[i] + "']");
    }

    feedItems.fadeOut("fast", function(){
        $(this).remove();
    });

    feedItems.attr("data-is-read", "true");

    //Show loader if all feeds were read
    if ($("#feed").find(".item[data-is-read!='true']").length === 0) {
        showLoader();
    }
    popupGlobal.backgroundPage.markAsRead(feedIds, function () {
        if ($("#feed").find(".item[data-is-read!='true']").length === 0) {
            renderFeeds();
        }
    });
}

function markAsUnSaved(feedIds) {
    var feedItems = $();
    for (var i = 0; i < feedIds.length; i++) {
        feedItems = feedItems.add(".item[data-id='" + feedIds[i] + "']");
    }

    popupGlobal.backgroundPage.toggleSavedFeed(feedIds, false);

    feedItems.data("saved", false);
    feedItems.find(".saved").removeClass("saved");
}

function markAllAsRead() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsRead(feedIds);
}

function markAsReadEngagement() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        var engagement = +$(value).find(".engagement").text();
        if(engagement < popupGlobal.backgroundPage.appGlobal.options.engagementFilterLimit) {
            feedIds.push($(value).data("id"));
        }
    });
    markAsRead(feedIds);
}

function markAllAsUnsaved() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsUnSaved(feedIds);
}

function renderCategories(container, feeds){
    $(".categories").remove();
    var categories = getUniqueCategories(feeds);
    var template = $("#categories-template").html();
    Mustache.parse(template);
    container.append(Mustache.render(template, {categories: categories}));
}

function renderTimeAgo(container) {
    let timeagoInstance = timeago();
    let timeagoNodes = document.querySelectorAll(".timeago");
    timeagoInstance.render(timeagoNodes, window.navigator.language);
}

function getUniqueCategories(feeds){
    var categories = [];
    var addedIds = [];
    feeds.forEach(function(feed){
        feed.categories.forEach(function (category) {
            if (addedIds.indexOf(category.id) === -1) {
                categories.push(category);
                addedIds.push(category.id);
            }
        });
    });
    return categories;
}

function showLoader() {
    $("body").children("div").hide();
    $("#loading").show();
}

function showLogin() {
    $("body").children("div").hide();
    $("#login-btn").text(chrome.i18n.getMessage("Login"));
    $("#login").show();
}

function showEmptyContent() {
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed-empty").text(chrome.i18n.getMessage("NoUnreadArticles")).show();
    $("#feedly").show().find("#popup-actions").hide();
}

function showFeeds() {
    if (popupGlobal.backgroundPage.appGlobal.options.resetCounterOnClick) {
        popupGlobal.backgroundPage.resetCounter();
    }
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed").show();
    $("#feedly").show().find("#popup-actions").show().children().show();
    $(".mark-read").attr("title", chrome.i18n.getMessage("MarkAsRead"));
    $(".show-content").attr("title", chrome.i18n.getMessage("More"));
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-unsaved, #mark-read-engagement").hide();

    if (popupGlobal.backgroundPage.appGlobal.options.showEngagementFilter) {
        $("#mark-read-engagement").show();
    }
}

function showSavedFeeds() {
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed-saved").show().find(".mark-read").hide();
    $("#feed-saved").find(".show-content").attr("title", chrome.i18n.getMessage("More"));
    $("#feedly").show().find("#popup-actions").show().children().hide();
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-unsaved").show();
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-refresh").show();
}

function setPopupWidth(expanded) {
    if (! popupGlobal.isSidebar) {
	var width = expanded
            ? popupGlobal.backgroundPage.appGlobal.options.expandedPopupWidth
            : popupGlobal.backgroundPage.appGlobal.options.popupWidth;

	$("#feed, #feed-saved").width(width);
    }
}
<commit_msg>fix the font size, was too small in sidebar<commit_after>"use strict";

var popupGlobal = {
    feeds: [],
    savedFeeds: [],
    backgroundPage: chrome.extension.getBackgroundPage(),
    isSidebar: false
};

$(document).ready(async function () {
    $("#feed, #feed-saved").css("font-size", popupGlobal.backgroundPage.appGlobal.options.popupFontSize / 100 + "em");
    $("#website").text(chrome.i18n.getMessage("FeedlyWebsite"));
    $("#mark-all-read>span").text(chrome.i18n.getMessage("MarkAllAsRead"));
    $("#mark-read-engagement>span").text(chrome.i18n.getMessage("MarkAsReadEngagement"));
    $("#update-feeds>span").text(chrome.i18n.getMessage("UpdateFeeds"));
    $("#open-all-news>span").text(chrome.i18n.getMessage("OpenAllFeeds"));
    $("#open-unsaved-all-news>span").text(chrome.i18n.getMessage("OpenAllSavedFeeds"));

    if (popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds) {
        $("#popup-content").addClass("tabs");
    }

    popupGlobal.isSidebar = browser && browser.sidebarAction && await browser.sidebarAction.isOpen({});
    if (popupGlobal.isSidebar) {
	$(document.body).css("font-size", "12pt");
    }
    setPopupWidth(false);

    executeAsync(renderFeeds);
});

$("#login").click(function () {
    popupGlobal.backgroundPage.getAccessToken(function() {
	setTimeout(renderFeeds, 500);
    });
});

//using "mousedown" instead of "click" event to process middle button click.
$("#feed, #feed-saved").on("mousedown", "a", function (event) {
    var link = $(this);
    if (event.which === 1 || event.which === 2) {
        var isActiveTab = !(event.ctrlKey || event.which === 2) && !popupGlobal.backgroundPage.appGlobal.options.openFeedsInBackground;
        var isFeed = link.hasClass("title") && $("#feed").is(":visible");
        var url = link.data("link");

        if (isFeed && popupGlobal.backgroundPage.appGlobal.feedTabId && popupGlobal.backgroundPage.appGlobal.options.openFeedsInSameTab) {
            chrome.tabs.update(popupGlobal.backgroundPage.appGlobal.feedTabId, {url: url}, function(tab) {
                onOpenCallback(isFeed, tab);
            });
        } else {
            chrome.tabs.create({url: url, active: isActiveTab }, function(tab) {
                onOpenCallback(isFeed, tab);
            });
        }
    }

    function onOpenCallback(isFeed, tab) {
        if (isFeed) {
            popupGlobal.backgroundPage.appGlobal.feedTabId = tab.id;

            if (popupGlobal.backgroundPage.appGlobal.options.markReadOnClick) {
                markAsRead([link.closest(".item").data("id")]);
            }
        }
    }
});

$("#popup-content").on("click", "#mark-all-read", markAllAsRead);

$("#popup-content").on("click", "#mark-read-engagement", markAsReadEngagement);

$("#popup-content").on("click", "#open-all-news", function () {
    $("#feed").find("a.title[data-link]").filter(":visible").each(function (key, value) {
        var news = $(value);
        chrome.tabs.create({url: news.data("link"), active: false }, function () {});
    });
    if (popupGlobal.backgroundPage.appGlobal.options.markReadOnClick) {
        markAllAsRead();
    }
});

$("#popup-content").on("click", "#open-unsaved-all-news", function () {
   $("#feed-saved").find("a.title[data-link]").filter(":visible").each(function (key, value) {
           var news = $(value);
           chrome.tabs.create({url: news.data("link"), active: false }, function () {});
       });
        markAllAsUnsaved();
});

$("#feed").on("click", ".mark-read", function (event) {
    var feed = $(this).closest(".item");
    markAsRead([feed.data("id")]);
});

$("#feedly").on("click", "#btn-feeds-saved", function () {
    $(this).addClass("active-tab");
    $("#btn-feeds").removeClass("active-tab");
    renderSavedFeeds();
});

$("#feedly").on("click", "#btn-feeds", function () {
    $(this).addClass("active-tab");
    $("#btn-feeds-saved").removeClass("active-tab");
    renderFeeds();
});

$("#popup-content").on("click", ".show-content", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var contentContainer = feed.find(".content");
    var feedId = feed.data("id");

    if (!contentContainer.html()) {
        var feeds = $("#feed").is(":visible") ? popupGlobal.feeds : popupGlobal.savedFeeds;

        var template = $("#feed-content").html();
        Mustache.parse(template);
        for (let feed of feeds) {
            if (feed.id === feedId) {

                // @if BROWSER='firefox'
                // We should sanitize the content of feeds because of AMO review.
                feed.title = DOMPurify.sanitize(feed.title);
                feed.content = DOMPurify.sanitize(feed.content);
                // @endif

                contentContainer.html(Mustache.render(template, feed));
		if (popupGlobal.isSidebar)
		    contentContainer.css({width: "95%", marginLeft: 5});

                //For open new tab without closing popup
                contentContainer.find("a").each(function (key, value) {
                    var link = $(value);
                    link.data("link", link.attr("href"));
                    link.attr("href", "javascript:void(0)");
                });
            }
        }
    }
    contentContainer.slideToggle("fast", function () {
        $this.toggleClass("glyphicon-chevron-down");
        $this.toggleClass("glyphicon-chevron-up");

        var expanded = $(".content").is(":visible");
        setPopupWidth(expanded);
    });
});

/* Manually feeds update */
$("#feedly").on("click", "#update-feeds", function () {
    if ($("#feed").is(":visible")) {
        renderFeeds(true);
    } else {
        renderSavedFeeds(true);
    }
});

/* Save or unsave feed */
$("#popup-content").on("click", ".save-feed", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var feedId = feed.data("id");
    var saveItem = !$this.data("saved");
    popupGlobal.backgroundPage.toggleSavedFeed([feedId], saveItem);
    $this.data("saved", saveItem);
    $this.toggleClass("saved");
});

$("#popup-content").on("click", "#website", function(){
    popupGlobal.backgroundPage.openFeedlyTab();

    // Close the popup since the user wants to see Feedly website anyway
    window.close();
});

$("#popup-content").on("click", ".categories > span", function (){
    $(".categories").find("span").removeClass("active");
    var button = $(this).addClass("active");
    var categoryId = button.data("id");
    if (categoryId) {
        $(".item").hide();
        $(".item[data-categories~='" + categoryId + "']").show();
    } else {
        $(".item").show();
    }
});

$("#feedly").on("click", "#feedly-logo", function (event) {
    if (event.ctrlKey) {
        popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds = !popupGlobal.backgroundPage.appGlobal.options.abilitySaveFeeds;
        location.reload();
    }
});

function executeAsync(func) {
    const timeout = popupGlobal.backgroundPage.appGlobal.environment.os === "mac" ? 500 : 0;
    setTimeout(function () {
        func();
    }, timeout);
}

function renderFeeds(forceUpdate) {
    showLoader();
    popupGlobal.backgroundPage.getFeeds(popupGlobal.backgroundPage.appGlobal.options.forceUpdateFeeds || forceUpdate, function (feeds, isLoggedIn) {
        popupGlobal.feeds = feeds;
        if (isLoggedIn === false) {
            showLogin();
        } else {
            if (feeds.length === 0) {
                showEmptyContent();
            } else {
                var container = $("#feed").show().empty();

                if (popupGlobal.backgroundPage.appGlobal.options.showCategories) {
                    renderCategories(container, feeds);
                }

                var feedsTemplate = $("#feedTemplate").html();
                Mustache.parse(feedsTemplate);

                container.append(Mustache.render(feedsTemplate, {feeds: feeds}));
                renderTimeAgo(container);

                showFeeds();

                if (popupGlobal.backgroundPage.appGlobal.options.expandFeeds) {
                    container.find(".show-content").click();
                }
            }
        }
    });
}

function renderSavedFeeds(forceUpdate) {
    showLoader();
    popupGlobal.backgroundPage.getSavedFeeds(popupGlobal.backgroundPage.appGlobal.options.forceUpdateFeeds || forceUpdate, function (feeds, isLoggedIn) {
        popupGlobal.savedFeeds = feeds;
        if (isLoggedIn === false) {
            showLogin();
        } else {
            if (feeds.length === 0) {
                showEmptyContent();
            } else {
                var container = $("#feed-saved").empty();

                if (popupGlobal.backgroundPage.appGlobal.options.showCategories) {
                    renderCategories(container, feeds);
                }

                var feedTemplate = $("#feedTemplate").html();
                Mustache.parse(feedTemplate);

                container.append(Mustache.render(feedTemplate, {feeds: feeds}));
                renderTimeAgo(container);

                showSavedFeeds();

                if (popupGlobal.backgroundPage.appGlobal.options.expandFeeds) {
                    container.find(".show-content").click();
                }
            }
        }
    });
}

function markAsRead(feedIds) {
    var feedItems = $();
    for (var i = 0; i < feedIds.length; i++) {
        feedItems = feedItems.add(".item[data-id='" + feedIds[i] + "']");
    }

    feedItems.fadeOut("fast", function(){
        $(this).remove();
    });

    feedItems.attr("data-is-read", "true");

    //Show loader if all feeds were read
    if ($("#feed").find(".item[data-is-read!='true']").length === 0) {
        showLoader();
    }
    popupGlobal.backgroundPage.markAsRead(feedIds, function () {
        if ($("#feed").find(".item[data-is-read!='true']").length === 0) {
            renderFeeds();
        }
    });
}

function markAsUnSaved(feedIds) {
    var feedItems = $();
    for (var i = 0; i < feedIds.length; i++) {
        feedItems = feedItems.add(".item[data-id='" + feedIds[i] + "']");
    }

    popupGlobal.backgroundPage.toggleSavedFeed(feedIds, false);

    feedItems.data("saved", false);
    feedItems.find(".saved").removeClass("saved");
}

function markAllAsRead() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsRead(feedIds);
}

function markAsReadEngagement() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        var engagement = +$(value).find(".engagement").text();
        if(engagement < popupGlobal.backgroundPage.appGlobal.options.engagementFilterLimit) {
            feedIds.push($(value).data("id"));
        }
    });
    markAsRead(feedIds);
}

function markAllAsUnsaved() {
    var feedIds = [];
    $(".item:visible").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsUnSaved(feedIds);
}

function renderCategories(container, feeds){
    $(".categories").remove();
    var categories = getUniqueCategories(feeds);
    var template = $("#categories-template").html();
    Mustache.parse(template);
    container.append(Mustache.render(template, {categories: categories}));
}

function renderTimeAgo(container) {
    let timeagoInstance = timeago();
    let timeagoNodes = document.querySelectorAll(".timeago");
    timeagoInstance.render(timeagoNodes, window.navigator.language);
}

function getUniqueCategories(feeds){
    var categories = [];
    var addedIds = [];
    feeds.forEach(function(feed){
        feed.categories.forEach(function (category) {
            if (addedIds.indexOf(category.id) === -1) {
                categories.push(category);
                addedIds.push(category.id);
            }
        });
    });
    return categories;
}

function showLoader() {
    $("body").children("div").hide();
    $("#loading").show();
}

function showLogin() {
    $("body").children("div").hide();
    $("#login-btn").text(chrome.i18n.getMessage("Login"));
    $("#login").show();
}

function showEmptyContent() {
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed-empty").text(chrome.i18n.getMessage("NoUnreadArticles")).show();
    $("#feedly").show().find("#popup-actions").hide();
}

function showFeeds() {
    if (popupGlobal.backgroundPage.appGlobal.options.resetCounterOnClick) {
        popupGlobal.backgroundPage.resetCounter();
    }
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed").show();
    $("#feedly").show().find("#popup-actions").show().children().show();
    $(".mark-read").attr("title", chrome.i18n.getMessage("MarkAsRead"));
    $(".show-content").attr("title", chrome.i18n.getMessage("More"));
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-unsaved, #mark-read-engagement").hide();

    if (popupGlobal.backgroundPage.appGlobal.options.showEngagementFilter) {
        $("#mark-read-engagement").show();
    }
}

function showSavedFeeds() {
    $("body").children("div").hide();
    $("#popup-content").show().children("div").hide().filter("#feed-saved").show().find(".mark-read").hide();
    $("#feed-saved").find(".show-content").attr("title", chrome.i18n.getMessage("More"));
    $("#feedly").show().find("#popup-actions").show().children().hide();
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-unsaved").show();
    $("#feedly").show().find("#popup-actions").show().children().filter(".icon-refresh").show();
}

function setPopupWidth(expanded) {
    if (! popupGlobal.isSidebar) {
	var width = expanded
            ? popupGlobal.backgroundPage.appGlobal.options.expandedPopupWidth
            : popupGlobal.backgroundPage.appGlobal.options.popupWidth;

	$("#feed, #feed-saved").width(width);
    }
}
<|endoftext|>


<commit_before>import Vector3 from './vector3.js';
import { clamp, approximatelyEquals } from './math.js';

// Copied from THREE.JS
/**
 * @author bhouston / http://exocortex.com
 */

class Line3 {
  constructor (start, end) {

    this.start = (start !== undefined) ? start : new Vector3();
    this.end = (end !== undefined) ? end : new Vector3();

  }

  set (start, end) {

    this.start.copy(start);
    this.end.copy(end);

    return this;

  }

  copy (line) {

    this.start.copy(line.start);
    this.end.copy(line.end);

    return this;

  }

  center (optionalTarget) {

    const result = optionalTarget || new Vector3();

    return result.addVectors(this.start, this.end).multiplyScalar(0.5);

  }

  delta (optionalTarget) {

    const result = optionalTarget || new Vector3();

    return result.subVectors(this.end, this.start);

  }

  distanceSq () {

    return this.start.distanceToSquared(this.end);

  }

  distance () {

    return this.start.distanceTo(this.end);

  }

  at (t, optionalTarget) {

    const result = optionalTarget || new Vector3();

    return this.delta(result).multiplyScalar(t).add(this.start);

  }

  closestPointToPointParameter (point, clampToLine) {

    const startP = new Vector3();
    const startEnd = new Vector3();

    startP.subVectors(point, this.start);
    startEnd.subVectors(this.end, this.start);

    const startEnd2 = startEnd.dot(startEnd);
    const startEnd_startP = startEnd.dot(startP);

    let t = startEnd_startP / startEnd2;

    if (clampToLine) {
      t = clamp(t, 0, 1);
    }

    return t;

  }

  closestPointToPoint (point, clampToLine, optionalTarget) {

    const t = this.closestPointToPointParameter(point, clampToLine);

    const result = optionalTarget || new Vector3();

    return this.delta(result).multiplyScalar(t).add(this.start);

  }

  applyMatrix4 (matrix) {

    this.start.applyMatrix4(matrix);
    this.end.applyMatrix4(matrix);

    return this;

  }

  equals (line) {

    return line.start.equals(this.start) && line.end.equals(this.end);

  }

  clone () {

    return new Line3().copy(this);

  }

  intersectLine (line) {
    // http://stackoverflow.com/questions/2316490/the-algorithm-to-find-the-point-of-intersection-of-two-3d-line-segment/10288710#10288710
    // Consider two lines r1 and r2, represented by the following parametric equations:A + vt and B + us, respectively.
    // Where A is a point of r1 and v a vector parallel to line.
    // And B is a point of r2 and u a vector parallel to line.
    // 'this' represents r2 and 'line' represents r1
    const da = this.end.clone().sub(this.start); //u
    const db = line.end.clone().sub(line.start); //v
    const dc = line.start.clone().sub(this.start); // AB

    const daCrossDb = da.clone().cross(db);
    const dcCrossDb = dc.clone().cross(db);

    // Lines are not coplanar, stop here
    // Coplanar only if the vectors AB, u, v are linearly dependent, i.e AB . (u × v) = 0
    const coplanarResult = dc.dot(daCrossDb);
    if (!approximatelyEquals(coplanarResult, 0)) {
      return;
    }

    const s = dcCrossDb.dot(daCrossDb) / daCrossDb.lengthSq();

    // Make sure we have an intersection
    if (s > 1.0 || isNaN(s)) {
      return;
    }

    const intersection = this.start.clone().add(da.clone().multiplyScalar(s));
    const distanceTest = intersection.clone().sub(line.start).lengthSq() + intersection.clone().sub(line.end).lengthSq();

    if (distanceTest <= line.distanceSq()) {
      return intersection;
    }

    return;
  }
}

export default Line3;
<commit_msg>fix: Normalize coplanar dot product check in intersectLine (#49)<commit_after>import Vector3 from './vector3.js';
import { clamp, approximatelyEquals } from './math.js';

// Copied from THREE.JS
/**
 * @author bhouston / http://exocortex.com
 */

class Line3 {
  constructor (start, end) {

    this.start = (start !== undefined) ? start : new Vector3();
    this.end = (end !== undefined) ? end : new Vector3();

  }

  set (start, end) {

    this.start.copy(start);
    this.end.copy(end);

    return this;

  }

  copy (line) {

    this.start.copy(line.start);
    this.end.copy(line.end);

    return this;

  }

  center (optionalTarget) {

    const result = optionalTarget || new Vector3();

    return result.addVectors(this.start, this.end).multiplyScalar(0.5);

  }

  delta (optionalTarget) {

    const result = optionalTarget || new Vector3();

    return result.subVectors(this.end, this.start);

  }

  distanceSq () {

    return this.start.distanceToSquared(this.end);

  }

  distance () {

    return this.start.distanceTo(this.end);

  }

  at (t, optionalTarget) {

    const result = optionalTarget || new Vector3();

    return this.delta(result).multiplyScalar(t).add(this.start);

  }

  closestPointToPointParameter (point, clampToLine) {

    const startP = new Vector3();
    const startEnd = new Vector3();

    startP.subVectors(point, this.start);
    startEnd.subVectors(this.end, this.start);

    const startEnd2 = startEnd.dot(startEnd);
    const startEnd_startP = startEnd.dot(startP);

    let t = startEnd_startP / startEnd2;

    if (clampToLine) {
      t = clamp(t, 0, 1);
    }

    return t;

  }

  closestPointToPoint (point, clampToLine, optionalTarget) {

    const t = this.closestPointToPointParameter(point, clampToLine);

    const result = optionalTarget || new Vector3();

    return this.delta(result).multiplyScalar(t).add(this.start);

  }

  applyMatrix4 (matrix) {

    this.start.applyMatrix4(matrix);
    this.end.applyMatrix4(matrix);

    return this;

  }

  equals (line) {

    return line.start.equals(this.start) && line.end.equals(this.end);

  }

  clone () {

    return new Line3().copy(this);

  }

  intersectLine (line) {
    // http://stackoverflow.com/questions/2316490/the-algorithm-to-find-the-point-of-intersection-of-two-3d-line-segment/10288710#10288710
    // Consider two lines r1 and r2, represented by the following parametric equations:A + vt and B + us, respectively.
    // Where A is a point of r1 and v a vector parallel to line.
    // And B is a point of r2 and u a vector parallel to line.
    // 'this' represents r2 and 'line' represents r1
    const da = this.end.clone().sub(this.start); //u
    const db = line.end.clone().sub(line.start); //v
    const dc = line.start.clone().sub(this.start); // AB

    const daCrossDb = da.clone().cross(db);
    const dcCrossDb = dc.clone().cross(db);

    // Lines are not coplanar, stop here
    // Coplanar only if the vectors AB, u, v are linearly dependent, i.e AB . (u × v) = 0
    const coplanarResult = dc.dot(daCrossDb);
    const normalizedCoplanarResult =
      coplanarResult / (dc.lengthSq() * daCrossDb.lengthSq());
    if (!approximatelyEquals(normalizedCoplanarResult, 0)) {
      return;
    }

    const s = dcCrossDb.dot(daCrossDb) / daCrossDb.lengthSq();

    // Make sure we have an intersection
    if (s > 1.0 || isNaN(s)) {
      return;
    }

    const intersection = this.start.clone().add(da.clone().multiplyScalar(s));
    const distanceTest = intersection.clone().sub(line.start).lengthSq() + intersection.clone().sub(line.end).lengthSq();

    if (distanceTest <= line.distanceSq()) {
      return intersection;
    }

    return;
  }
}

export default Line3;
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const fetch = require('node-fetch');
const ini = require('ini').parse;
const decompress = require('decompress');

function decompressXZ() {
    // taken from https://github.com/kevva/decompress-tarxz undex the MIT license
    // we don't add it as a dependency because it depends on too old a version of lzma-native
    // which doesn't have node-pre-gyp binaries for recent Node versions

    const decompressTar = require('decompress-tar');
    const fileType = require('file-type');
    const isStream = require('is-stream');
    const lzmaNative = require('lzma-native');

    return function(input) {
        if (!Buffer.isBuffer(input) && !isStream(input)) {
            return Promise.reject(new TypeError(`Expected a Buffer or Stream, got ${typeof input}`));
        }

        if (Buffer.isBuffer(input) && (!fileType(input) || fileType(input).ext !== 'xz')) {
        	return Promise.resolve([]);
        }

        const decompressor = lzmaNative.createDecompressor();
        const result = decompressTar()(decompressor);

        if (Buffer.isBuffer(input)) {
            decompressor.end(input);
        } else {
            input.pipe(decompressor);
        }

        return result;
    }
}

function printProgress(input, totalBytes) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = 'Downloading Realm binaries [:bar] (:ratek)';
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(url, destination) {
    return fetch(url).then((response) => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }
        const totalBytes = parseInt(response.headers.get('Content-Length'));
        if (fs.existsSync(destination) && fs.statSync(destination).size === totalBytes) {
            return;
        }

        if (process.stdout.isTTY) {
            printProgress(response.body, totalBytes);
        } else {
            console.log(`Downloading ${url}`);
        }
        return new Promise((resolve) => {
            const file = fs.createWriteStream(destination);
            response.body.pipe(file).once('finish', () => file.close(resolve));
        });
    });
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
console.log(options);

let serverFolder, archive, extractedFolder;

const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));
if (!options.sync) {
    serverFolder = `core/v${dependencies.REALM_CORE_VERSION}/`;
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
    case 'mac':
        serverFolder += `macos/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.xz`;
        break;
    case 'ios':
        flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
        serverFolder += `ios/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.xz`;
        break;
    case '..\\win': // handle gyp idiocy
        options.platform = 'win';
    case 'win':
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        serverFolder += `windows/${arch}/nouwp/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    case 'linux':
        serverFolder = 'core';
        archive = `realm-core-${dependencies.REALM_CORE_VERSION}.tgz`;
        extractedFolder = `realm-core-${dependencies.REALM_CORE_VERSION}`;
        break;
    }
} else {
    serverFolder = 'sync';
    switch (options.platform) {
    case 'mac':
        archive = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
        extractedFolder = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
        break;
    case 'ios':
        archive = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.xz`;
        extractedFolder = `core`;
        break;
    }
}

if (!archive) {
    process.exit();
}

const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
const vendorDir = path.resolve(__dirname, '../vendor');
const downloadedArchive = path.resolve(os.tmpdir(), archive);

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

if (!fs.existsSync(realmDir)) {
    const targetFolder = extractedFolder ? vendorDir : realmDir;

    const decompressOptions = /tar\.xz$/.test(archive) ? { plugins: [ decompressXZ() ] } : undefined;

    let pipeline = download(url, downloadedArchive);
    pipeline = pipeline.then(() => decompress(downloadedArchive, targetFolder, decompressOptions));

    if (extractedFolder) {
        pipeline = pipeline.then(() => fs.renameSync(path.resolve(vendorDir, extractedFolder), realmDir));
    }

    pipeline.catch(error => {
        console.log('Downloading Realm binaries failed with', error);
        process.exit(-1);
    });
}<commit_msg>Fix downloading the prebuilt core for ios<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const fetch = require('node-fetch');
const ini = require('ini').parse;
const decompress = require('decompress');

function decompressXZ() {
    // taken from https://github.com/kevva/decompress-tarxz undex the MIT license
    // we don't add it as a dependency because it depends on too old a version of lzma-native
    // which doesn't have node-pre-gyp binaries for recent Node versions

    const decompressTar = require('decompress-tar');
    const fileType = require('file-type');
    const isStream = require('is-stream');
    const lzmaNative = require('lzma-native');

    return function(input) {
        if (!Buffer.isBuffer(input) && !isStream(input)) {
            return Promise.reject(new TypeError(`Expected a Buffer or Stream, got ${typeof input}`));
        }

        if (Buffer.isBuffer(input) && (!fileType(input) || fileType(input).ext !== 'xz')) {
        	return Promise.resolve([]);
        }

        const decompressor = lzmaNative.createDecompressor();
        const result = decompressTar()(decompressor);

        if (Buffer.isBuffer(input)) {
            decompressor.end(input);
        } else {
            input.pipe(decompressor);
        }

        return result;
    }
}

function printProgress(input, totalBytes) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = 'Downloading Realm binaries [:bar] (:ratek)';
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(url, destination) {
    return fetch(url).then((response) => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }
        const totalBytes = parseInt(response.headers.get('Content-Length'));
        if (fs.existsSync(destination) && fs.statSync(destination).size === totalBytes) {
            return;
        }

        if (process.stdout.isTTY) {
            printProgress(response.body, totalBytes);
        } else {
            console.log(`Downloading ${url}`);
        }
        return new Promise((resolve) => {
            const file = fs.createWriteStream(destination);
            response.body.pipe(file).once('finish', () => file.close(resolve));
        });
    });
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
console.log(options);

let serverFolder, archive, extractedFolder, forceXZ = false;

const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));
if (!options.sync) {
    serverFolder = `core/v${dependencies.REALM_CORE_VERSION}/`;
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
    case 'mac':
        serverFolder += `macos/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.xz`;
        break;
    case 'ios':
        flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
        serverFolder += `ios/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.xz`;
        break;
    case '..\\win': // handle gyp idiocy
        options.platform = 'win';
    case 'win':
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        serverFolder += `windows/${arch}/nouwp/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    case 'linux':
        serverFolder = 'core';
        archive = `realm-core-${dependencies.REALM_CORE_VERSION}.tgz`;
        extractedFolder = `realm-core-${dependencies.REALM_CORE_VERSION}`;
        break;
    }
} else {
    serverFolder = 'sync';
    switch (options.platform) {
    case 'mac':
        archive = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
        extractedFolder = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
        break;
    case 'ios':
        archive = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
        extractedFolder = `core`;
        forceXZ = true;
        break;
    }
}

if (!archive) {
    process.exit();
}

const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
const vendorDir = path.resolve(__dirname, '../vendor');
const downloadedArchive = path.resolve(os.tmpdir(), archive);

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

if (!fs.existsSync(realmDir)) {
    const targetFolder = extractedFolder ? vendorDir : realmDir;

    const decompressOptions = forceXZ || /tar\.xz$/.test(archive) ? { plugins: [ decompressXZ() ] } : undefined;

    let pipeline = download(url, downloadedArchive);
    pipeline = pipeline.then(() => decompress(downloadedArchive, targetFolder, decompressOptions));

    if (extractedFolder) {
        pipeline = pipeline.then(() => fs.renameSync(path.resolve(vendorDir, extractedFolder), realmDir));
    }

    pipeline.catch(error => {
        console.log('Downloading Realm binaries failed with', error);
        process.exit(-1);
    });
}
<|endoftext|>


<commit_before>/**
 * Created by halcwb on 05/06/15.
 */

"use strict";

(function () {
    var INDEX = "index.html";

    var http = require("http");
    var fs = require("fs");

    var server;
    var started = false;

    exports.start = function (port, custom404, callback) {
        var err404, url;

        // Port has to be defined
        if (!port) throw 'No port defined';

        // Setup custom 404 page
        if (custom404) {
            fs.readFile("./" + custom404, { encoding: 'utf8' }, function (err, data) {
                if (err) {
                    throw err;
                } else {
                    err404 = data;
                }
            });
        }

        // Create a server instance
        server = http.createServer();

        // Setup server request response
        server.on("request", function (request, response) {
            url = request.url || INDEX;
            if (url === "/") url = INDEX;

            serveFile(response, url, err404);
        });

        // Start the server
        server.listen(port, callback);
        started = true;
    };

    exports.stop = function (callback) {
        if (server && started) {
            started = false;
            callback = callback || function () {};
            server.close(callback);
        } else {
            throw "Server was not started or already closed";
        }
    };

    var serveFile = function (response, file, notFound) {
        fs.readFile("./" + file, function (err, data) {
            if (err) {
                response.statusCode = 404;
                response.end(notFound || err.toString());
            } else {
                response.end(data);
            }
        });
    };

})();
<commit_msg>finally got rid of read file race condition?<commit_after>/**
 * Created by halcwb on 05/06/15.
 */

"use strict";

(function () {
    var INDEX = "index.html";

    var http = require("http");
    var fs = require("fs");

    var server;
    var started = false;
    var notFoundFile;

    exports.start = function (port, custom404, callback) {
        var url;
        notFoundFile = custom404;
        // Port has to be defined
        if (!port) throw 'No port defined';

        // Create a server instance
        server = http.createServer();

        // Setup server request response
        server.on("request", function (request, response) {
            url = request.url || INDEX;
            if (url === "/") url = INDEX;

            serveFile(response, url);
        });

        // Start the server
        server.listen(port, callback);
        started = true;
    };

    exports.stop = function (callback) {
        if (server && started) {
            started = false;
            callback = callback || function () {};
            server.close(callback);
        } else {
            throw "Server was not started or already closed";
        }
    };

    var serveFile = function (response, file, notFound) {
        fs.readFile("./" + file, { encoding: 'utf8' }, function (err, html) {
            if (err) {
                response.statusCode = 404;

                if (notFoundFile) {
                    fs.readFile("./" + notFoundFile, { encoding: 'utf8' }, function (err, notFound) {
                        if (err) {
                            throw err;
                        } else {
                            response.end(notFound);
                        }
                    });
                } else {
                    response.end(err.toString());
                }
            } else {
                response.end(html);
            }
        });
    };

})();
<|endoftext|>


<commit_before>/**
 * Created by Joao Carvalho on 31-03-2015.
 */
module.exports = function (grunt) {
    var semver = require('semver'),
        child_process = require('child_process'),
        format = require('util').format,
        readlineSync = require('readline-sync');

    require('load-grunt-tasks')(grunt);

    //need this to get version... dont work in grunt-sed with <%= pkg.version %>!!! did not understand why!
    var pk = grunt.file.readJSON('package.json'),
        gitVersion;

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        buildDir: 'dist',
        banner: [
            '/*!',
            ' * <%= pkg.name %> v%%VERSION%%',
            ' * <%= pkg.homepage %>',
            ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>; Licensed MIT',
            ' */\n\n'
        ].join('\n'),
        clean: {
            dist: {
                files: [
                    {
                        dot: true,
                        src: [
                            '<%= buildDir %>/*'
                        ]
                    }
                ]
            }
        },
        sed: {
            version: {
                pattern: '%%VERSION%%',
                replacement: function() {
                    return grunt.option('tag') || pk.version;
                },
                recursive: true,
                path: '<%= buildDir %>'
            }
        },
        ngAnnotate: {
            dist: {
                files: [
                    {
                        expand: true,
                        src: '<%= buildDir %>/<%= pkg.name %>.js'
                    }
                ]
            }
        },
        concat: {
            options: {
                separator: ';'
            },
            dist: {
                src: ['src/**/*.js'],
                dest: '<%= buildDir %>/<%= pkg.name %>.js'
            }
        },
        jshint: {
            options: {
                jshintrc: true,
                multistr: true,
                '-W030': true
            },
            build: ['Grunfile.js', 'src/**/*.js', 'tests/**/*_spec.js']
        },
        uglify: {
            options: {
                banner: '<%= banner %>',
                footer: '\n'
            },
            build: {
                options: {
                    mangle: true,
                    beautify: false,
                    compress: true,
                    indent_level: 0
                },
                files: {
                    '<%= buildDir %>/<%= pkg.name %>.min.js': ['<%= buildDir %>/<%= pkg.name %>.js']
                }
            }
        },
        shell: {
            options: {
                stdout: true,
                stderr: true
            },

            /**
             * Get remote repos on git and set grunt.option('remote');
             * If more than one user is able to choose which
             */
            remote: {
                command: function (ok) {

                    //return (ok && 'gruntlistremotes=$(git remote);printf "$gruntlistremotes"') || '$(exit 1)';
                    return ok === 'true'?
                            'gruntlistremotes=$(git remote);printf "$gruntlistremotes"'
                            :
                            '$(echo "Task shell:remote not running" >&2 ; exit 1)';
                },
                options: {
                    async: false,
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function (error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        else {
                            var remotes = stdout.split('\n');
                            if (remotes.length===1) {
                                grunt.option('remote', remotes[0]);
                                cb();
                                return;
                            }
                            remotes.forEach(function(item,idx,alist) {
                                alist[idx] = [ '[', idx + 1, ']-', item].join('');
                            });

                            var resp = 0;
                            grunt.log.writeln(format('%s ', '\n\nThere are more than 1 remote associate with this repo, please choose the one to push into.\n\n' + remotes));
                            while (isNaN(resp) || resp===0 || resp>remotes.length) {
                                resp = readlineSync.question('\nYour choice?');
                                if (resp==='') { cb(false); return; }
                                resp = parseInt(resp);
                            }
                            grunt.option('remote',stdout.split('\n')[resp-1]);//using original array
                            //grunt.log.write(grunt.option('remote'));
                            cb();
                        }

                    }
                }
            },
            /**
             * Git Tag with value in grunt.option("tag")
             * (called from task release)
             */
            tag: {
                command: function (ok) {
                    return ok === 'true'?
                        'git tag -a v<%= grunt.option("tag") %> -m \'Version <%= grunt.option("tag") %>\''
                        :
                        '$(echo "Task shell:tag not running" >&2 ; exit 1)';
                    //return ok? 'xxxgit tag -a v<%= grunt.option("tag") %> -m \'Version <%= grunt.option("tag") %>\'' : '$(exit 1)';
                },
                options: {
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function(error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        cb();
                    }
                }
            },
            /**
             * Git push to remote with tags
             * (called from task push)
             */
            push: {
                command: function (ok) {
                    return ok === 'true'?
                        'git push <%= grunt.option("remote") %> master --tags'
                        :
                        '$(echo "Task shell:push not running" >&2 ; exit 1)';
                },
                options: {
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function(error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        cb();
                    }
                }
            }
        },
        step: {
            options: {
                option: false
            }
        },
        exec: {
            /**
             * Tasks used for releas to get git status, branch, add and commit
             */
            test_git_on_master: '[[ $(git symbolic-ref --short -q HEAD) = master ]]',
            test_git_is_clean: '[[ -z "$(git status --porcelain)" ]]',
            git_add: 'git add .',
            git_commit: {
                cmd: function (m) {
                    return format('git commit -m "%s"', m);
                }
            },

            karma: './node_modules/karma/bin/karma start'
        },
        /**
         * Test runner
         */
        karma: {
            /*mochasingle: {
                configFile: 'karma.mocha.conf.js',
                singleRun: true
            },*/
            jasminesingle: {
                configFile: 'karma.jasmine.conf.js',
                singleRun: true
            },
            /*mocha: {
                configFile: 'karma.mocha.conf.js',
                singleRun: false
            },*/
            jasmine: {
                configFile: 'karma.jasmine.conf.js',
                singleRun: false
            }

        },
        /**
         * e2e tests with protractor
         */
        protractor_webdriver: {
            update: {
                options: {
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager update --standalone'
                }
            },
            continuous: {
                options: {
                    keepAlive : true,
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager start --seleniumPort 4444'
                }
            },
            single: {
                options: {
                    keepAlive : false,
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager start --seleniumPort 4444'
                }
            }
        },
        protractor: {
            options: {
                configFile: "tests/e2e/local/protractor.js",
                noColor: false,
                debug: false,
                args: { }
            },
            single: {
                options: {
                    keepAlive: false
                }
            },
            continuous: {
                options: {
                    keepAlive: true
                }
            }
        },
        /**
         * Local Web server
         */
        connect: {
            options: {
                port: 8888,
                hostname: 'localhost'
            },
            e2etest: {
                /*options: {
                    // set the location of the application files
                    base: ['app']
                }*/
            }
        },
        ngdocs: {
          all: [ 'src/**/*.js' ]
        },
        watch: {
            options: {
                livereload: true
            },
//            karma: {
//                files: ['app/js/**/*.js', 'test/unit/*.js'],
//                tasks: ['karma:continuous:run']
//            },
            protractor: {
                files: ['tests/e2e/*.html', 'src/**/*.js', 'tests/e2e/*_spec.js'],
                tasks: ['protractor:continuous']
            }
        }
    });

    /**
     * Release Tasks
     */
    grunt.registerTask('updmanifests', 'Update manifests.', function () {
        //Update version on manifests files
        var _ = grunt.util._,
            pkg = grunt.file.readJSON('package.json'),
            bower = grunt.file.readJSON('bower.json'),
            version = grunt.option('tag', gitVersion);
        bower = JSON.stringify(_.extend(bower, {
            name: pkg.name,
            version: version
        }), null, 2);
        pkg = JSON.stringify(_.extend(pkg, {
            version: version
        }), null, 2);
        grunt.file.write('package.json', pkg);
        grunt.file.write('bower.json', bower);
    });
    /**
     * Task release: Inspired here: http://kroltech.com/2014/04/use-grunt-to-push-a-git-tag/
     * :patch
     * :minor
     * :major
     */
    grunt.registerTask('release:patch', ['release']);
    grunt.registerTask('release:minor', function () {grunt.option('tagType', 'minor');grunt.task.run(['release']);});
    grunt.registerTask('release:major', function () {grunt.option('tagType', 'major');grunt.task.run(['release']);});
    grunt.registerTask('release', function () {
        if (grunt.option('tagType') !== 'major' &&
            grunt.option('tagType') !== 'minor') {
            grunt.option('tagType', 'patch');
        }
        var resp = readlineSync.question(format('\nRelease [%s] (Y/n)?', grunt.option('tagType')));
        if (resp.toLowerCase() !== 'y') {
            return false;
        }
        var done = this.async();
        child_process.exec('git describe --tags --abbrev=0',
            function (err, stdout, stderr) {
                if (stderr) {
                    grunt.log.error(stderr);
                }
                else {
                    gitVersion = semver.inc(stdout.trim(), grunt.option('tagType'));
                    grunt.option('tag', gitVersion);
                    grunt.task.run([
                        'exec:test_git_on_master',
                        'exec:test_git_is_clean',
                        format('step:Release/Update to version %s?', gitVersion),
                        'clean:dist',
                        'updmanifests',
                        'build',
                        'exec:git_add',
                        format('exec:git_commit:%s', format('Release version %s [ci skip]', gitVersion)),
                        'shell:tag:true',
                        'shell:remote:true',
                        'push'
                    ]);
                }
                done();
            }
        );
    });
    grunt.registerTask('push', function () {
        //push to remote
        this.requires('release');
        if (grunt.option("remote")) {
            grunt.task.run([
                format('step:Push changes to %s?', grunt.option("remote")),
                'shell:push:true'
            ]);
        }
        else {
            return false;
        }
    });

    /**
     * Test tasks
     */
    grunt.registerTask('test', ['jshint', /*'karma:mochasingle',*/ 'karma:jasminesingle']);
    grunt.registerTask('test:e2e', ['jshint', 'connect:e2etest', 'protractor_webdriver:continuous', 'protractor:continuous', 'watch:protractor']);
    grunt.registerTask('test:e2e:single', ['jshint', 'connect:e2etest', 'protractor_webdriver:single', 'protractor:single']);
    //grunt.registerTask('test:mocha', ['jshint', 'karma:mocha']);
    grunt.registerTask('test:jasmine', ['jshint', 'karma:jasmine']);

    /**
     * Build Task
     */
    grunt.registerTask('build', ['jshint', 'concat', 'ngAnnotate', 'uglify', 'sed:version']);
    /**
     * Default
     */
    grunt.registerTask('default', ['build']);
};
<commit_msg>Change Gruntfile.js [ci skip]<commit_after>/**
 * Created by Joao Carvalho on 31-03-2015.
 */
module.exports = function (grunt) {
    var semver = require('semver'),
        child_process = require('child_process'),
        format = require('util').format,
        readlineSync = require('readline-sync');

    require('load-grunt-tasks')(grunt);

    //need this to get version... dont work in grunt-sed with <%= pkg.version %>!!! did not understand why!
    var pk = grunt.file.readJSON('package.json'),
        gitVersion;

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        buildDir: 'dist',
        banner: [
            '/*!',
            ' * <%= pkg.name %> v%%VERSION%%',
            ' * <%= pkg.homepage %>',
            ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>; Licensed MIT',
            ' */\n\n'
        ].join('\n'),
        clean: {
            dist: {
                files: [
                    {
                        dot: true,
                        src: [
                            '<%= buildDir %>/*'
                        ]
                    }
                ]
            }
        },
        sed: {
            version: {
                pattern: '%%VERSION%%',
                replacement: function() {
                    return grunt.option('tag') || pk.version;
                },
                recursive: true,
                path: '<%= buildDir %>'
            }
        },
        ngAnnotate: {
            dist: {
                files: [
                    {
                        expand: true,
                        src: '<%= buildDir %>/<%= pkg.name %>.js'
                    }
                ]
            }
        },
        concat: {
            options: {
                separator: ';'
            },
            dist: {
                src: ['src/**/*.js'],
                dest: '<%= buildDir %>/<%= pkg.name %>.js'
            }
        },
        jshint: {
            options: {
                jshintrc: true,
                multistr: true,
                '-W030': true
            },
            build: ['Grunfile.js', 'src/**/*.js', 'tests/**/*_spec.js']
        },
        uglify: {
            options: {
                banner: '<%= banner %>',
                footer: '\n'
            },
            build: {
                options: {
                    mangle: true,
                    beautify: false,
                    compress: true,
                    indent_level: 0
                },
                files: {
                    '<%= buildDir %>/<%= pkg.name %>.min.js': ['<%= buildDir %>/<%= pkg.name %>.js']
                }
            }
        },
        shell: {
            options: {
                stdout: true,
                stderr: true
            },

            /**
             * Get remote repos on git and set grunt.option('remote');
             * If more than one user is able to choose which
             */
            remote: {
                command: function (ok) {

                    //return (ok && 'gruntlistremotes=$(git remote);printf "$gruntlistremotes"') || '$(exit 1)';
                    return ok === 'true'?
                            'gruntlistremotes=$(git remote);printf "$gruntlistremotes"'
                            :
                            '$(echo "Task shell:remote not running" >&2 ; exit 1)';
                },
                options: {
                    async: false,
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function (error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        else {
                            var remotes = stdout.split('\n');
                            if (remotes.length===1) {
                                grunt.option('remote', remotes[0]);
                                cb();
                                return;
                            }
                            remotes.forEach(function(item,idx,alist) {
                                alist[idx] = [ '[', idx + 1, ']-', item].join('');
                            });

                            var resp = 0;
                            grunt.log.writeln(format('%s ', '\n\nThere are more than 1 remote associate with this repo, please choose the one to push into.\n\n' + remotes));
                            while (isNaN(resp) || resp===0 || resp>remotes.length) {
                                resp = readlineSync.question('\nYour choice?');
                                if (resp==='') { cb(false); return; }
                                resp = parseInt(resp);
                            }
                            grunt.option('remote',stdout.split('\n')[resp-1]);//using original array
                            //grunt.log.write(grunt.option('remote'));
                            cb();
                        }

                    }
                }
            },
            /**
             * Git Tag with value in grunt.option("tag")
             * (called from task release)
             */
            tag: {
                command: function (ok) {
                    return ok === 'true'?
                        'git tag -a v<%= grunt.option("tag") %> -m \'Version <%= grunt.option("tag") %>\''
                        :
                        '$(echo "Task shell:tag not running" >&2 ; exit 1)';
                    //return ok? 'xxxgit tag -a v<%= grunt.option("tag") %> -m \'Version <%= grunt.option("tag") %>\'' : '$(exit 1)';
                },
                options: {
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function(error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        cb();
                    }
                }
            },
            /**
             * Git push to remote with tags
             * (called from task push)
             */
            push: {
                command: function (ok) {
                    return ok === 'true'?
                        'git push <%= grunt.option("remote") %> master --tags'
                        :
                        '$(echo "Task shell:push not running" >&2 ; exit 1)';
                },
                options: {
                    stdout: false,
                    stderr: true,
                    stdin: false,
                    callback: function(error, stdout, stderr, cb) {
                        if (error !== 0) {
                            grunt.log.write('Error:',error,'\n');
                            cb(false);
                        }
                        cb();
                    }
                }
            }
        },
        step: {
            options: {
                option: false
            }
        },
        exec: {
            /**
             * Tasks used for releas to get git status, branch, add and commit
             */
            test_git_on_master: '[[ $(git symbolic-ref --short -q HEAD) = master ]]',
            test_git_is_clean: '[[ -z "$(git status --porcelain)" ]]',
            git_add: 'git add .',
            git_commit: {
                cmd: function (m) {
                    return format('git commit -m "%s"', m);
                }
            },

            karma: './node_modules/karma/bin/karma start'
        },
        /**
         * Test runner
         */
        karma: {
            /*mochasingle: {
                configFile: 'karma.mocha.conf.js',
                singleRun: true
            },*/
            jasminesingle: {
                configFile: 'karma.jasmine.conf.js',
                singleRun: true
            },
            /*mocha: {
                configFile: 'karma.mocha.conf.js',
                singleRun: false
            },*/
            jasmine: {
                configFile: 'karma.jasmine.conf.js',
                singleRun: false
            }

        },
        /**
         * e2e tests with protractor
         */
        protractor_webdriver: {
            update: {
                options: {
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager update --standalone'
                }
            },
            continuous: {
                options: {
                    keepAlive : true,
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager start --seleniumPort 4444'
                }
            },
            single: {
                options: {
                    keepAlive : false,
                    path: './node_modules/.bin/',
                    command: 'webdriver-manager start --seleniumPort 4444'
                }
            }
        },
        protractor: {
            options: {
                configFile: "tests/e2e/local/protractor.js",
                noColor: false,
                debug: false,
                args: { }
            },
            single: {
                options: {
                    keepAlive: false
                }
            },
            continuous: {
                options: {
                    keepAlive: true
                }
            }
        },
        /**
         * Local Web server
         */
        connect: {
            options: {
                port: 8888,
                hostname: 'localhost'
            },
            e2etest: {
                /*options: {
                    // set the location of the application files
                    base: ['app']
                }*/
            }
        },
        ngdocs: {
          all: [ 'src/**/*.js' ]
        },
        watch: {
            options: {
                livereload: true
            },
//            karma: {
//                files: ['app/js/**/*.js', 'test/unit/*.js'],
//                tasks: ['karma:continuous:run']
//            },
            protractor: {
                files: ['tests/e2e/*.html', 'src/**/*.js', 'tests/e2e/*_spec.js'],
                tasks: ['protractor:continuous']
            }
        }
    });

    /**
     * Release Tasks
     */
    grunt.registerTask('updmanifests', 'Update manifests.', function () {
        //Update version on manifests files
        var _ = grunt.util._,
            pkg = grunt.file.readJSON('package.json'),
            bower = grunt.file.readJSON('bower.json'),
            version = grunt.option('tag', gitVersion);
        bower = JSON.stringify(_.extend(bower, {
            name: pkg.name,
            version: version
        }), null, 2);
        pkg = JSON.stringify(_.extend(pkg, {
            version: version
        }), null, 2);
        grunt.file.write('package.json', pkg);
        grunt.file.write('bower.json', bower);
    });
    /**
     * Task release: Inspired here: http://kroltech.com/2014/04/use-grunt-to-push-a-git-tag/
     * :patch
     * :minor
     * :major
     */
    grunt.registerTask('release:patch', ['release']);
    grunt.registerTask('release:minor', function () {grunt.option('tagType', 'minor');grunt.task.run(['release']);});
    grunt.registerTask('release:major', function () {grunt.option('tagType', 'major');grunt.task.run(['release']);});
    grunt.registerTask('release', function () {
        if (grunt.option('tagType') !== 'major' &&
            grunt.option('tagType') !== 'minor') {
            grunt.option('tagType', 'patch');
        }
        var resp = readlineSync.question(format('\nRelease [%s] (Y/n)?', grunt.option('tagType')));
        if (resp.toLowerCase() !== 'y') {
            return false;
        }
        var done = this.async();
        child_process.exec('git describe --tags --abbrev=0',
            function (err, stdout, stderr) {
                if (stderr) {
                    grunt.log.error(stderr);
                }
                else {
                    gitVersion = semver.inc(stdout.trim(), grunt.option('tagType'));
                    grunt.option('tag', gitVersion);
                    grunt.task.run([
                        'exec:test_git_on_master',
                        'exec:test_git_is_clean',
                        format('step:Release/Update to version %s?', gitVersion),
                        'clean:dist',
                        'updmanifests',
                        'build',
                        'exec:git_add',
                        format('exec:git_commit:%s', format('Release version %s [ci skip]', gitVersion)),
                        'shell:tag:true',
                        'shell:remote:true',
                        'push'
                    ]);
                }
                done();
            }
        );
    });
    grunt.registerTask('push', function () {
        //push to remote
        this.requires('release');
        if (grunt.option("remote")) {
            grunt.task.run([
                format('step:Push changes to %s?', grunt.option("remote")),
                'shell:push:true'
            ]);
        }
        else {
            return false;
        }
    });

    /**
     * Test tasks
     */
    grunt.registerTask('test', ['jshint', /*'karma:mochasingle',*/ 'karma:jasminesingle']);
    grunt.registerTask('test:e2e', ['jshint', 'connect:e2etest', 'protractor_webdriver:continuous', 'protractor:continuous', 'watch:protractor']);
    grunt.registerTask('test:e2e:single', ['jshint', 'connect:e2etest', 'protractor_webdriver:single', 'protractor:single']);
    //grunt.registerTask('test:mocha', ['jshint', 'karma:mocha']);
    grunt.registerTask('test:jasmine', ['jshint', 'karma:jasmine']);
    /**
     * Build Task
     */
    grunt.registerTask('build', ['jshint', 'concat', 'ngAnnotate', 'uglify', 'sed:version']);
    /**
     * Default
     */
    grunt.registerTask('default', ['build']);
};
<|endoftext|>


<commit_before>/**
 * XadillaX <i@2333.moe> created at 2017-12-03 14:46:14 with ❤
 *
 * Copyright (c) 2017 xcoder.in, all rights reserved.
 */
"use strict";

const assert = require("assert");

const cheerio = require("cheerio");
const spidex = require("spidex");

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

const days = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];

describe("constellation", function() {
    for(let i = 0; i < 12; i++) {
        for(let j = 0; j < days[i]; j++) {
            it(`${i + 1}.${j + 1} should OK`, function(done) {
                spidex.post("http://www.xzw.com/cquery/post.php", {
                    data: {
                        "date[0]": 2004,
                        "date[1]": i + 1,
                        "date[2]": j + 1
                    },
                    header: {
                        Host: "www.xzw.com"
                    },
                    timeout: 10000
                }, function(html, status, header) {
                    if(status !== 302) {
                        throw new Error("not 302");
                    }

                    const url = `http://www.xzw.com${header.location}`;
                    spidex.get(url, {
                        timeout: 10000
                    }, function(html) {
                        const $ = cheerio.load(html);
                        const standard = $(".sContent h2").text();

                        assert.equal(constellation(i + 1, j + 1, "zh-cn"), standard);
                        done();
                    });
                }).on("error", function(e) {
                    assert.ifError(e);
                });
            });
        }
    }
});
<commit_msg>test: update timeout<commit_after>/**
 * XadillaX <i@2333.moe> created at 2017-12-03 14:46:14 with ❤
 *
 * Copyright (c) 2017 xcoder.in, all rights reserved.
 */
"use strict";

const assert = require("assert");

const cheerio = require("cheerio");
const spidex = require("spidex");

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

const days = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];

describe("constellation", function() {
    for(let i = 0; i < 12; i++) {
        for(let j = 0; j < days[i]; j++) {
            it(`${i + 1}.${j + 1} should OK`, function(done) {
                this.timeout(20000);

                spidex.post("http://www.xzw.com/cquery/post.php", {
                    data: {
                        "date[0]": 2004,
                        "date[1]": i + 1,
                        "date[2]": j + 1
                    },
                    header: {
                        Host: "www.xzw.com"
                    },
                    timeout: 10000
                }, function(html, status, header) {
                    if(status !== 302) {
                        throw new Error("not 302");
                    }

                    const url = `http://www.xzw.com${header.location}`;
                    spidex.get(url, {
                        timeout: 10000
                    }, function(html) {
                        const $ = cheerio.load(html);
                        const standard = $(".sContent h2").text();

                        assert.equal(constellation(i + 1, j + 1, "zh-cn"), standard);
                        done();
                    });
                }).on("error", function(e) {
                    assert.ifError(e);
                });
            });
        }
    }
});
<|endoftext|>