<commit_before>var sh = { };

/* Pushes the given page onto the page stack. Invokes callback afterwards.
 */
sh.push = function (which, callback, immediate)
{
    var page = sh.item(which);
    if (! page.length)
    {
        return;
    }

    var prevPage = $(".sh-page.sh-visible").last();
    if (prevPage.length)
    {
        console.log("Prev page: " + prevPage);
        if (prevPage[0] === page[0])
        {
            prevPage = [];
        }
        else
        {
            prevPage.prop("rememberedScrollTop",  $(document).scrollTop());
        }
    }

    page.addClass("sh-page-transitioning");

    if (! immediate)
    {
        // position page to the right
        var width = $(window).width();
        page.css("left", width + "px")
            .css("right", -width + "px");
        page.find("> header").css("left", width + "px")
                             .css("right", -width + "px");
    }

    // adjust header label width
    var header = page.find("> header");
    if (header.length) {
        var spanLeftWidth = 16;
        var spanRightWidth = 16;
        var spans = header.find("> span:first-child, > span:last-child");
        for (var i = 0; i < spans.length; ++i) {
            var item = spans[i];
            if (i === 0) {
                spanLeftWidth += $(item).width();
            }
            else {
                spanRightWidth += $(item).width();
            }
        }
        header.css("padding-left", spanLeftWidth + "px")
              .css("padding-right", spanRightWidth + "px");
    }

    page.addClass("sh-visible");
    $(".sh-page.sh-visible").each(function (i)
    {
        $(this).css("zIndex", i);
    });

    /*
    // provide synchronized scrolling for a better experience on Fire TV Silk Browser
    $("html").height(p.prop("scrollHeight"));
    p.css("overflow", "hidden");
    $(document).off("scroll").on("scroll").scroll(function ()
    {
        p.scrollTop($(document).scrollTop());
    });
    */

    if (! immediate)
    {
        // slide into view
        page.animate({
            left: 0,
            right: 0
        }, 350, function ()
        {
            $(document).scrollTop(0);
            if (prevPage.length)
            {
                prevPage.css("display", "none");
            }
            page.removeClass("sh-page-transitioning");
            if (callback)
            {
                callback();
            }
        });

        page.find("> header").animate({
            left: 0,
            right: 0
        }, 350);
    }
    else
    {
        $(document).scrollTop(0);
        if (prevPage.length)
        {
            prevPage.css("display", "none");
        }
        page.removeClass("sh-page-transitioning");
        if (callback)
        {
            callback();
        }
    }
}

/* Pops the topmost page off the page stack. Invokes callback afterwards.
 */
sh.pop = function (callback, reverse, immediate)
{
    var pages = $(".sh-page.sh-visible");

    if (pages.length > 1)
    {
        var page = $(pages[pages.length - 1]);
        var prevPage = $(pages[pages.length - 2]);

        prevPage.css("display", "block");
        $(document).scrollTop(prevPage.prop("rememberedScrollTop") || 0);

        if (! immediate)
        {
            // slide out
            page.addClass("sh-page-transitioning");
            var width = $(window).width();
            page.animate({
                left: (reverse ? -width : width) + "px",
                right: (reverse ? width : -width) + "px"
            }, 350, function ()
            {
                page.removeClass("sh-visible");
                page.removeClass("sh-page-transitioning");
                page.css("left", "0").css("right", "0");
                page.trigger("sh-closed");

                if (callback)
                {
                    callback();
                }
            });

            page.find("> header").animate({
                left: (reverse ? -width : width) + "px",
                right: (reverse ? width : -width) + "px"
            }, 350, function ()
            {
                page.find("> header").css("left", "0").css("right", "0");
            });
        }
        else
        {
            page.removeClass("sh-visible");
            page.removeClass("sh-page-transitioning");
            page.css("left", "0").css("right", "0");
            page.find("> header").css("left", "0").css("right", "0");
            page.trigger("sh-closed");

            if (callback)
            {
                callback();
            }
        }
    }
}

/* Sets up swipe-back touch gesture for the given page.
 * Invokes the given callback on swiping back.
 */
sh.onSwipeBack = function (which, callback)
{
    var page = sh.item(which);

    if (! page.length)
    {
        return;
    }

    page.on("touchstart", function (ev)
    {
        var backIndicator = $(
            tag("div")
            .style("position", "fixed")
            .style("top", "0")
            .style("bottom", "0")
            .style("left", "8px")
            .style("font-size", "10vh")
            .content(
                tag("span").class("sh-fw-icon sh-icon-back")
                .style("line-height", "100vh")
            )
            .html()
        );

        this.swipeContext = {
            beginX: ev.originalEvent.touches[0].screenX,
            beginY: ev.originalEvent.touches[0].screenY,
            status: 0,
            scrollTop: 0,
            backIndicator: backIndicator
        };
    });
    page.on("touchmove", function (ev)
    {
        var dx = ev.originalEvent.touches[0].screenX - this.swipeContext.beginX;
        var dy = ev.originalEvent.touches[0].screenY - this.swipeContext.beginY;
        var pos = dx - 16;

        var fullWidth = $(this).width();
        var swipeThreshold = fullWidth * 0.20;

        switch (this.swipeContext.status)
        {
        case 0: // initiated
            if (pos > 0)
            {
                var angle = Math.atan(dy / dx);
                if (Math.abs(angle) > Math.PI / 4)
                {
                    this.swipeContext.status = 3;
                }
                else
                {
                    var scrollTop = $(document).scrollTop();
                    console.log("scrollTop: " + scrollTop);
                    page.addClass("sh-page-transitioning");
                    page.find("> section").css("margin-top", (-scrollTop) + "px");
                    this.swipeContext.scrollTop = scrollTop;
                    this.swipeContext.status = 1;
                }
            }
            break;

        case 1: // swiping
            page.css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");
            page.find("> header").css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                                 .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");

            if (dx > swipeThreshold)
            {
                $("body").append(this.swipeContext.backIndicator);
                this.swipeContext.status = 2;
            }
            break;

        case 2: // activated
            page.css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");
            page.find("> header").css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                                 .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");

            if (dx < swipeThreshold)
            {
                this.swipeContext.backIndicator.remove();
                this.swipeContext.status = 1;
            }
            break;

        case 3: // aborted
            break;
        }
    });
    page.on("touchend", function (ev)
    {
        function resetPage()
        {
            page.removeClass("sh-page-transitioning");
            page.find("> section").css("margin-top", "0");
            if (swipeContext.scrollTop > 0)
            {
                $(document).scrollTop(swipeContext.scrollTop);
            }
            page.css("left", "0").css("right", "0")
        }

        function resetHeader()
        {
            page.find("> header").css("left", "0").css("right", "0")
        }

        function finish()
        {
            if (swipeContext.status === 2)
            {
                callback();
            }

            var left = page.css("left");
            setTimeout(function ()
            {
                if (page.css("left") === left)
                {
                    resetPage();
                    resetHeader();
                }
            }, 100);
        }

        var swipeContext = this.swipeContext;
        var fullWidth = $(this).width();
        this.swipeContext.backIndicator.remove();

        if (swipeContext.status === 1)
        {
            page.animate({
                left: "0",
                right: "0"
            }, 300, resetPage);
            page.find("> header").animate({
                left: "0",
                right: "0"
            }, 300, resetHeader);
        }
        else if (swipeContext.status === 2)
        {
            page.animate({
                left: fullWidth + "px",
                right: -fullWidth + "px"
            }, 300, finish);
            page.find("> header").animate({
                left: fullWidth + "px",
                right: -fullWidth + "px"
            }, 300);
        }
    });
}

/* Pops the topmost page off the page stack. Invokes callback afterwards.
 * The page slides in a way that signalizes acceptance.
 */
sh.accept = function (callback)
{
    sh.pop(callback, true);
}

/* Pushes the given page onto the page stack as a dialog with buttons
 * and a callback for each button.
 */
sh.dialog = function (which, callbacks)
{
    var dlg = sh.item(which);
    if (dlg.length)
    {
        $(".sh-page").css("z-index", 0);
        $(dlg).css("z-index", 1000);
        var buttons = dlg.find("> header > span > a");
        for (var i = 0; i < buttons.length; ++i)
        {
            var f = function (i, callback)
            {
                return function ()
                {
                    if (i === 0)
                    {
                        sh.pop(callback);
                    }
                    else
                    {
                        sh.accept(callback);
                    }
                };
            } (i, callbacks[i] || function () { });
            $(buttons[i]).off("click").on("click", f);
        }
    }
    sh.push(dlg);
}

/* Opens the given menu attached to a parent element.
 * Invokes callback afterwards.
 */
sh.menu = function (parent, which, callback)
{
    var menu = sh.item(which);
    var content = menu.find(">:first-child");
    var p = $(parent);

    if (menu.length && p.length && content.length)
    {
        var w = Number(p.css("width").replace("px", ""));
        var h = Number(p.css("height").replace("px", ""));

        var l = p.offset().left + (w - content.width()) / 2;

        if (l < 0)
        {
            l = 0;
        }
        else if (l + content.width() >= $(window).width())
        {
            l = $(window).width() - content.width() - 5;
        }

        /*
        if (l + content.width() > $(window).width())
        {
            l = l + w - content.width() - 1;
        }
        */

        content.css("left", l + "px");
        content.css("top", p.offset().top - $(document).scrollTop() + h + "px");

        menu.css("zIndex", $(".sh-page").length);
        menu.addClass("sh-visible");
    }
}

/* Closes the currently opened menu. Invokes callback afterwards.
 */
sh.menu_close = function (callback)
{
    var menu = $(".sh-menu.sh-visible").last();
    if (menu.length)
    {
        menu.removeClass("sh-visible");
        menu.trigger("sh-closed");
        if (callback)
        {
            callback();
        }
    }
}

/* Toggles the given submenu.
 */
sh.toggle_submenu = function (item)
{
    $(item).parent().find(".sh-submenu").each(function (i)
    {
        if (this !== item)
        {
            $(this).removeClass("sh-submenu-visible");
        }
    });
    $(item).toggleClass("sh-submenu-visible");
}

sh.popup = function (which, callback)
{
    var p = sh.item(which);
    if (p.length)
    {
        p.addClass("sh-visible");
        p.css("zIndex", $(".sh-page.sh-visible").length + $(".sh-popup.sh-visible").length);

        if (callback)
        {
            callback();
        }
    }
}

sh.popup_close = function (which, callback)
{
    var popup = sh.item(which);
    if (popup.length)
    {
        popup.removeClass("sh-visible");
        popup.trigger("sh-closed");
        if (callback)
        {
            callback();
        }
    }
}

sh.set_footer = function (whichPage, size)
{
    var page = sh.item(whichPage);
    if (page.length)
    {
        if (size === 0)
        {
            page.find("> footer").css("visibility", "hidden");
            page.css("padding-bottom", "0");
        }
        else
        {
            page.find("> footer").css("visibility", "visible")
                                 .height(size);
            page.css("padding-bottom", size + "px");
        }
    }
}

sh.item = function (which)
{
    switch ($.type(which))
    {
    case "string":
        return $("#" + which);
    default:
        return $(which);
    }
}

sh.zSort = function (data)
{
    data.sort(function (a, b)
    {
        var aZ = Number($(a).css("zIndex")) || 0;
        var bZ = Number($(b).css("zIndex")) || 0;
        return aZ - bZ;
    });
    return data;
}

sh.topmost = function (filter)
{
    var items = sh.zSort($(filter));
    if (items.length)
    {
        return items[items.length - 1];
    }
    else
    {
        return undefined;
    }
}

sh.isFullscreen = function ()
{
    var state = document.fullScreen ||
                document.mozFullScreen ||
                document.webkitIsFullScreen;

    return (state === true);
}

sh.requestFullscreen = function (target)
{
    var e = $(target).get(0);
    if (e.requestFullscreen)
    {
        e.requestFullscreen();
    }
    else if (e.msRequestFullscreen)
    {
        e.msRequestFullscreen();
    }
    else if (e.mozRequestFullScreen)
    {
        e.mozRequestFullScreen();
    }
    else if (e.webkitRequestFullscreen)
    {
        e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
}

sh.exitFullscreen = function ()
{
    if (document.exitFullscreen)
    {
        document.exitFullscreen();
    }
    else if (document.webkitExitFullscreen)
    {
        document.webkitExitFullscreen();
    }
    else if (document.mozCancelFullScreen)
    {
        document.mozCancelFullScreen();
    }
    else if (document.msExitFullscreen)
    {
        document.msExitFullscreen();
    }
}

/*
$(function ()
{
    sh.push("main-page");
});
*/
<commit_msg>improved page handling<commit_after>var sh = { };

sh.pageFreeze = function(which, where)
{
    var page = sh.item(which);

    var scrollTop = where;
    page.prop("rememberedScrollTop",  scrollTop);
    page.addClass("sh-page-transitioning");
    page.find("> section").css("margin-top", (-scrollTop) + "px");
}

sh.pageUnfreeze = function(which)
{
    var page = sh.item(which);

    page.removeClass("sh-page-transitioning");
    page.find("> section").css("margin-top", "0");
    $(document).scrollTop(page.prop("rememberedScrollTop") || 0);
}

/* Pushes the given page onto the page stack. Invokes callback afterwards.
 */
sh.push = function (which, callback, immediate)
{
    var page = sh.item(which);
    if (! page.length)
    {
        return;
    }

    sh.pageFreeze(page, 0);

    var prevPage = $(".sh-page.sh-visible").last();
    if (prevPage.length)
    {
        console.log("Prev page: " + prevPage);
        if (prevPage[0] === page[0])
        {
            prevPage = [];
            page.prop("rememberedScrollTop",  0);
        }
        else
        {
            sh.pageFreeze(prevPage, $(document).scrollTop());
        }
    }

    if (! immediate)
    {
        // position page to the right
        var width = $(window).width();
        page.css("left", width + "px")
            .css("right", -width + "px");
        page.find("> header").css("left", width + "px")
                             .css("right", -width + "px");
    }

    // adjust header label width
    var header = page.find("> header");
    if (header.length) {
        var spanLeftWidth = 16;
        var spanRightWidth = 16;
        var spans = header.find("> span:first-child, > span:last-child");
        for (var i = 0; i < spans.length; ++i) {
            var item = spans[i];
            if (i === 0) {
                spanLeftWidth += $(item).width();
            }
            else {
                spanRightWidth += $(item).width();
            }
        }
        header.css("padding-left", spanLeftWidth + "px")
              .css("padding-right", spanRightWidth + "px");
    }

    page.addClass("sh-visible");
    $(".sh-page.sh-visible").each(function (i)
    {
        $(this).css("zIndex", i);
    });

    /*
    // provide synchronized scrolling for a better experience on Fire TV Silk Browser
    $("html").height(p.prop("scrollHeight"));
    p.css("overflow", "hidden");
    $(document).off("scroll").on("scroll").scroll(function ()
    {
        p.scrollTop($(document).scrollTop());
    });
    */

    if (! immediate)
    {
        // slide into view
        page.animate({
            left: 0,
            right: 0
        }, 350, function ()
        {
            if (prevPage.length)
            {
                prevPage.css("display", "none");
            }
            sh.pageUnfreeze(page);
            if (callback)
            {
                callback();
            }
        });

        page.find("> header").animate({
            left: 0,
            right: 0
        }, 350);
    }
    else
    {
        if (prevPage.length)
        {
            prevPage.css("display", "none");
        }
        sh.pageUnfreeze(page);
        if (callback)
        {
            callback();
        }
    }
}

/* Pops the topmost page off the page stack. Invokes callback afterwards.
 */
sh.pop = function (callback, reverse, immediate)
{
    var pages = $(".sh-page.sh-visible");

    if (pages.length > 1)
    {
        var page = $(pages[pages.length - 1]);
        var prevPage = $(pages[pages.length - 2]);

        prevPage.css("display", "block");

        if (! immediate)
        {
            // slide out
            sh.pageFreeze(page, $(document).scrollTop());

            var width = $(window).width();
            page.animate({
                left: (reverse ? -width : width) + "px",
                right: (reverse ? width : -width) + "px"
            }, 350, function ()
            {
                page.removeClass("sh-visible");
                sh.pageUnfreeze(page);
                sh.pageUnfreeze(prevPage);

                page.css("left", "0").css("right", "0");
                page.trigger("sh-closed");

                if (callback)
                {
                    callback();
                }
            });

            page.find("> header").animate({
                left: (reverse ? -width : width) + "px",
                right: (reverse ? width : -width) + "px"
            }, 350, function ()
            {
                page.find("> header").css("left", "0").css("right", "0");
            });
        }
        else
        {
            page.removeClass("sh-visible");
            sh.pageUnfreeze(page);
            sh.pageUnfreeze(prevPage);

            page.css("left", "0").css("right", "0");
            page.find("> header").css("left", "0").css("right", "0");
            page.trigger("sh-closed");

            if (callback)
            {
                callback();
            }
        }
    }
}

/* Sets up swipe-back touch gesture for the given page.
 * Invokes the given callback on swiping back.
 */
sh.onSwipeBack = function (which, callback)
{
    var page = sh.item(which);

    if (! page.length)
    {
        return;
    }

    page.on("touchstart", function (ev)
    {
        var backIndicator = $(
            tag("div")
            .style("position", "fixed")
            .style("top", "0")
            .style("bottom", "0")
            .style("left", "8px")
            .style("font-size", "10vh")
            .content(
                tag("span").class("sh-fw-icon sh-icon-back")
                .style("line-height", "100vh")
            )
            .html()
        );

        this.swipeContext = {
            beginX: ev.originalEvent.touches[0].screenX,
            beginY: ev.originalEvent.touches[0].screenY,
            status: 0,
            scrollTop: 0,
            backIndicator: backIndicator
        };
    });
    page.on("touchmove", function (ev)
    {
        var dx = ev.originalEvent.touches[0].screenX - this.swipeContext.beginX;
        var dy = ev.originalEvent.touches[0].screenY - this.swipeContext.beginY;
        var pos = dx - 16;

        var fullWidth = $(this).width();
        var swipeThreshold = fullWidth * 0.20;

        switch (this.swipeContext.status)
        {
        case 0: // initiated
            if (pos > 0)
            {
                var angle = Math.atan(dy / dx);
                if (Math.abs(angle) > Math.PI / 4)
                {
                    this.swipeContext.status = 3;
                }
                else
                {
                    var scrollTop = $(document).scrollTop();
                    sh.pageFreeze(page, scrollTop);

                    var pages = $(".sh-page.sh-visible");
                    if (pages.length > 1)
                    {
                        var prevPage = $(pages[pages.length - 2]);
                        prevPage.css("display", "block");
                    }

                    this.swipeContext.scrollTop = scrollTop;
                    this.swipeContext.status = 1;
                }
            }
            break;

        case 1: // swiping
            page.css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");
            page.find("> header").css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                                 .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");

            if (dx > swipeThreshold)
            {
                $("body").append(this.swipeContext.backIndicator);
                this.swipeContext.status = 2;
            }
            break;

        case 2: // activated
            page.css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");
            page.find("> header").css("left", Math.max(0, Math.min(fullWidth, pos)) + "px")
                                 .css("right", -Math.max(0, Math.min(fullWidth, pos)) + "px");

            if (dx < swipeThreshold)
            {
                this.swipeContext.backIndicator.remove();
                this.swipeContext.status = 1;
            }
            break;

        case 3: // aborted
            break;
        }
    });
    page.on("touchend", function (ev)
    {
        function resetPage()
        {
            sh.pageUnfreeze(page);
            page.css("left", "0").css("right", "0")
        }

        function resetHeader()
        {
            page.find("> header").css("left", "0").css("right", "0")
        }

        function finish()
        {
            if (swipeContext.status === 2)
            {
                callback();
            }

            var left = page.css("left");
            setTimeout(function ()
            {
                if (page.css("left") === left)
                {
                    resetPage();
                    resetHeader();
                }
            }, 100);
        }

        var swipeContext = this.swipeContext;
        var fullWidth = $(this).width();
        this.swipeContext.backIndicator.remove();

        if (swipeContext.status === 1)
        {
            page.animate({
                left: "0",
                right: "0"
            }, 300, resetPage);
            page.find("> header").animate({
                left: "0",
                right: "0"
            }, 300, resetHeader);
        }
        else if (swipeContext.status === 2)
        {
            page.animate({
                left: fullWidth + "px",
                right: -fullWidth + "px"
            }, 300, finish);
            page.find("> header").animate({
                left: fullWidth + "px",
                right: -fullWidth + "px"
            }, 300);
        }
    });
}

/* Pops the topmost page off the page stack. Invokes callback afterwards.
 * The page slides in a way that signalizes acceptance.
 */
sh.accept = function (callback)
{
    sh.pop(callback, true);
}

/* Pushes the given page onto the page stack as a dialog with buttons
 * and a callback for each button.
 */
sh.dialog = function (which, callbacks)
{
    var dlg = sh.item(which);
    if (dlg.length)
    {
        $(".sh-page").css("z-index", 0);
        $(dlg).css("z-index", 1000);
        var buttons = dlg.find("> header > span > a");
        for (var i = 0; i < buttons.length; ++i)
        {
            var f = function (i, callback)
            {
                return function ()
                {
                    if (i === 0)
                    {
                        sh.pop(callback);
                    }
                    else
                    {
                        sh.accept(callback);
                    }
                };
            } (i, callbacks[i] || function () { });
            $(buttons[i]).off("click").on("click", f);
        }
    }
    sh.push(dlg);
}

/* Opens the given menu attached to a parent element.
 * Invokes callback afterwards.
 */
sh.menu = function (parent, which, callback)
{
    var menu = sh.item(which);
    var content = menu.find(">:first-child");
    var p = $(parent);

    if (menu.length && p.length && content.length)
    {
        var w = Number(p.css("width").replace("px", ""));
        var h = Number(p.css("height").replace("px", ""));

        var l = p.offset().left + (w - content.width()) / 2;

        if (l < 0)
        {
            l = 0;
        }
        else if (l + content.width() >= $(window).width())
        {
            l = $(window).width() - content.width() - 5;
        }

        /*
        if (l + content.width() > $(window).width())
        {
            l = l + w - content.width() - 1;
        }
        */

        content.css("left", l + "px");
        content.css("top", p.offset().top - $(document).scrollTop() + h + "px");

        menu.css("zIndex", $(".sh-page").length);
        menu.addClass("sh-visible");
    }
}

/* Closes the currently opened menu. Invokes callback afterwards.
 */
sh.menu_close = function (callback)
{
    var menu = $(".sh-menu.sh-visible").last();
    if (menu.length)
    {
        menu.removeClass("sh-visible");
        menu.trigger("sh-closed");
        if (callback)
        {
            callback();
        }
    }
}

/* Toggles the given submenu.
 */
sh.toggle_submenu = function (item)
{
    $(item).parent().find(".sh-submenu").each(function (i)
    {
        if (this !== item)
        {
            $(this).removeClass("sh-submenu-visible");
        }
    });
    $(item).toggleClass("sh-submenu-visible");
}

sh.popup = function (which, callback)
{
    var p = sh.item(which);
    if (p.length)
    {
        p.addClass("sh-visible");
        p.css("zIndex", $(".sh-page.sh-visible").length + $(".sh-popup.sh-visible").length);

        if (callback)
        {
            callback();
        }
    }
}

sh.popup_close = function (which, callback)
{
    var popup = sh.item(which);
    if (popup.length)
    {
        popup.removeClass("sh-visible");
        popup.trigger("sh-closed");
        if (callback)
        {
            callback();
        }
    }
}

sh.set_footer = function (whichPage, size)
{
    var page = sh.item(whichPage);
    if (page.length)
    {
        if (size === 0)
        {
            page.find("> footer").css("visibility", "hidden");
            page.css("padding-bottom", "0");
        }
        else
        {
            page.find("> footer").css("visibility", "visible")
                                 .height(size);
            page.css("padding-bottom", size + "px");
        }
    }
}

sh.item = function (which)
{
    switch ($.type(which))
    {
    case "string":
        return $("#" + which);
    default:
        return $(which);
    }
}

sh.zSort = function (data)
{
    data.sort(function (a, b)
    {
        var aZ = Number($(a).css("zIndex")) || 0;
        var bZ = Number($(b).css("zIndex")) || 0;
        return aZ - bZ;
    });
    return data;
}

sh.topmost = function (filter)
{
    var items = sh.zSort($(filter));
    if (items.length)
    {
        return items[items.length - 1];
    }
    else
    {
        return undefined;
    }
}

sh.isFullscreen = function ()
{
    var state = document.fullScreen ||
                document.mozFullScreen ||
                document.webkitIsFullScreen;

    return (state === true);
}

sh.requestFullscreen = function (target)
{
    var e = $(target).get(0);
    if (e.requestFullscreen)
    {
        e.requestFullscreen();
    }
    else if (e.msRequestFullscreen)
    {
        e.msRequestFullscreen();
    }
    else if (e.mozRequestFullScreen)
    {
        e.mozRequestFullScreen();
    }
    else if (e.webkitRequestFullscreen)
    {
        e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
}

sh.exitFullscreen = function ()
{
    if (document.exitFullscreen)
    {
        document.exitFullscreen();
    }
    else if (document.webkitExitFullscreen)
    {
        document.webkitExitFullscreen();
    }
    else if (document.mozCancelFullScreen)
    {
        document.mozCancelFullScreen();
    }
    else if (document.msExitFullscreen)
    {
        document.msExitFullscreen();
    }
}

/*
$(function ()
{
    sh.push("main-page");
});
*/
<|endoftext|>


<commit_before>// User type
function User(userid) {
    this.id = userid;
    this.plans = [];
}

/* method: User::getPlans()
 * loads data assigned to user and calls User::displayPlans()
 */
User.prototype.getPlans = function(callback) {
    var self = this;
    var url = app.plansServer + '/plans/' + this.id;
    var request = new XMLHttpRequest({
        mozAnon: true,
        mozSystem: true});
    request.onload = function() {
        console.log('DEBUG: plans loaded from server');
        self.initiatePlans(this.responseText);
        localStorage.setItem('plans', this.responseText);
        self.displayPlans();
        if (callback) {
            callback();
        }
    };
    request.onerror = function(error) {
        console.log('DEBUG: Failed to get plans from ``' + url + '``', error);
    };
    request.open("get", url, true);
    request.send();
};

/* method: User::reloadPlans()
 * removes all existing plans UI and calls User::getPlans()
 */
User.prototype.reloadPlans = function() {
    // remove UI from plans
    for (var i = 0; i < this.plans.length; i++) {
        this.plans[i].removeUI();
    }
    // clean this.plans
    this.plans = [];
    // clean phone's storage
    localStorage.setItem('plans', '[]');
    // load plans from server
    this.getPlans(app.toggleSettings);
};

/* method: User::initiatePlans()
 * parses plans from JSON representation and initiates a new Plan for
 * each of them
 */
User.prototype.initiatePlans = function(plansString) {
    var plans = JSON.parse(plansString);
    for (var i = 0; i < plans.length; i++) {
        this.plans.push(new Plan(plans[i]));
    }
    return true;
}

/* method: User::loadPlans()
 * loads plans from either localStorage or using User::getPlans
 */
User.prototype.loadPlans = function() {
    var plans = localStorage.getItem('plans');
    if (plans === null) {
        return this.getPlans();
    }
    console.log('DEBUG: plans loaded from device');
    this.initiatePlans(plans);
    this.displayPlans();
};

/* method: User::displayPlans()
 * loads local names of days and calls Plan::createUI for each plan
 */
User.prototype.displayPlans = function() {
    var self = this;
    navigator.globalization.getDateNames(function(dayOfWeek){
        var deck = document.getElementById('plan-group');
        var tabbar = document.getElementById('plan-group-menu');
        for (var i = 0; i < self.plans.length; i++) {
            self.plans[i].createUI(deck, tabbar, dayOfWeek);
        }
    }, function() {}, {type: 'narrow', item: 'days'});
};

// Plan type
function Plan(plan) {
    this.schedule = plan.week;
    this.title = plan.title;
    this.id = plan.id;
    this.active = plan.active;
    this.tab = null;
    this.card = null;
    this.table = null;
};

/* method Plan::selectTab()
 * switches the active tab of <brick-tabbar>
 * uses polling as brick might not be fully loaded yet
 */
Plan.prototype.selectTab = function(deck) {
    var self = this;
    function selectActiveTab() {
        if (!self.tab.targetElement) {
            return window.setTimeout(selectActiveTab, 100);
        }
        deck.showCard(self.tab.targetElement);
    }
    selectActiveTab();
};

/* method Plan::createUI()
 * creates <brick-card> and <brick-tab> elements, then renders plan into
 * the <table> element
 */
Plan.prototype.createUI = function(deck, tabbar, dayOfWeek) {
    // create card
    this.card = document.createElement('brick-card');
    this.card.setAttribute('id', this.id);
    deck.appendChild(this.card);

    //create tab
    this.tab = document.createElement('brick-tabbar-tab');
    this.tab.setAttribute('target', this.id);
    this.tab.appendChild(document.createTextNode(this.title));
    tabbar.appendChild(this.tab);

    // link card and tab DOM Elements
    this.card.tabElement = this.tab;
    this.card.addEventListener('show', function() {
        this.tabElement.select();
    });

    // create plan table
    this.table = document.createElement('table');

    // hide not used days
    var numberOfDays = this.schedule.length;
    var cleanPlan = [];
    for (var j = 0; j < numberOfDays; j++) {
        if (this.schedule[j].length > 0) {
            cleanPlan.push(this.schedule[j]);
        }
    }

    // rotate the table
    var daysInHours = [];
    for (j = 0; j < cleanPlan.length; j++) {
        for (var k = 0; k < cleanPlan[j].length; k++) {
            if (!daysInHours[k]) {
                daysInHours[k] = [];
            }
            daysInHours[k][j] = cleanPlan[j][k];
        }
    }

    // create plan's DOM Elements
    for (var j = 0; j < daysInHours.length; j++) {
        var tr = this.table.insertRow(-1);
        var td = tr.insertCell(-1);
        td.appendChild(document.createTextNode(j + 1));
        for (var k = 0; k < cleanPlan.length; k++) {
            var td = tr.insertCell(-1);
            if (daysInHours[j][k]) {
                td.appendChild(document.createTextNode(daysInHours[j][k]));
            }
        }
    }

    // create plan's header
    var thead = this.table.createTHead();
    var tr = thead.insertRow();
    var th_empty = document.createElement('th');
    tr.appendChild(th_empty);
    var weekDayNumber;
    for (var j = 0; j < numberOfDays; j++) {
        var weekDayNumber = (j + 1) % 7;
        if (this.schedule[j].length > 0) {
            var th = document.createElement('th');
            th.appendChild(document.createTextNode(dayOfWeek.value[weekDayNumber]));
            tr.appendChild(th);
        }
    }
    this.card.appendChild(this.table);

    // select current plan if active
    if (this.active) {
        this.selectTab(deck);
    }
};

Plan.prototype.removeUI = function() {
    this.card.remove();
    this.tab.remove();
};

var app = {
    plans: [],
    planGroup: null,
    initialize: function() {
        this.bindEvents();
    },

    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },

onDeviceReady: function() {
    app.plansServer = localStorage.getItem('plansServer');
    app.userID = localStorage.getItem('userID');
    app.activateFingerSwipe();
    app.assignButtons();

    console.log(app.userID);
    if (app.plansServer && app.userID) {
        app.user = new User(app.userID);
        app.user.loadPlans();
    } else {
        app.toggleSettings();
    }
},

    activateFingerSwipe: function() {
        // Switching from one tab to another is done automatically
        // We just need to link it backwards - change menu if slides
        // changed without touching the menu
        app.planGroupMenu = document.getElementById('plan-group-menu');

        // Implementing one finger swipe to change deck card
        app.planGroup = document.getElementById('plan-group');

        var startX = null;
        var slideThreshold = 100;

        function touchStart(sX) {
            startX = sX;
        }

        function touchEnd(endX) {
            var deltaX;
            if (startX) {
                deltaX = endX - startX;
                if (Math.abs(deltaX) > slideThreshold) {
                    startX = null;
                    if (deltaX > 0) {
                        app.previousPlan();
                    } else {
                        app.nextPlan();
                    }
                }
            }
        }

        app.planGroup.addEventListener('touchstart', function(evt) {
            var touches = evt.changedTouches;
            if (touches.length === 1) {
                // runs only for one finger touch
                touchStart(touches[0].pageX);
            }
        });

        app.planGroup.addEventListener('touchmove', function(evt) {
            // switched off scrolling on webkit
            evt.preventDefault();
            touchEnd(evt.changedTouches[0].pageX);
        });
    },

    toggleSettings: function() {
        app.flipbox.toggle();
    },

    assignButtons: function(){
        app.flipbox = document.querySelector('brick-flipbox');
        var settingsButton = document.getElementById('settings-button');
        var settingsOffButton = document.getElementById('settings-off-button');
        settingsButton.addEventListener('click', app.toggleSettings);
        settingsOffButton.addEventListener('click', app.toggleSettings);

        var reloadButton = document.getElementById('reload-button');
        reloadButton.addEventListener('touchend', function() {
            app.user.reloadPlans();
        }, false);

        var serverInput = document.getElementById('input-server');
        if (app.plansServer) {
            serverInput.value = app.plansServer;
        }
        serverInput.addEventListener('blur', function() {
            app.plansServer = serverInput.value || null;
            localStorage.setItem('plansServer', app.plansServer);
        });

        var userInput = document.getElementById('input-user');
        if (app.userID) {
            userInput.value = app.userID;
        }
        userInput.addEventListener('blur', function() {
            app.userID = userInput.value || null;
            if (app.userID) {
                if (app.user) {
                    app.user.id = app.userID;
                } else {
                    app.user = new User(app.userID);
                }
                localStorage.setItem('userID', app.userID);
            }
        });

        document.getElementById('form-settings').addEventListener('submit', function(e) {
            e.preventDefault();
        }, false)
    },

    previousPlan: function() {
        app.planGroup.previousCard();
    },

    nextPlan: function() {
        app.planGroup.nextCard();
    }
}

window.onload = function() {
  app.initialize();
}

<commit_msg>no need to return a thing<commit_after>// User type
function User(userid) {
    this.id = userid;
    this.plans = [];
}

/* method: User::getPlans()
 * loads data assigned to user and calls User::displayPlans()
 */
User.prototype.getPlans = function(callback) {
    var self = this;
    var url = app.plansServer + '/plans/' + this.id;
    var request = new XMLHttpRequest({
        mozAnon: true,
        mozSystem: true});
    request.onload = function() {
        console.log('DEBUG: plans loaded from server');
        self.initiatePlans(this.responseText);
        localStorage.setItem('plans', this.responseText);
        self.displayPlans();
        if (callback) {
            callback();
        }
    };
    request.onerror = function(error) {
        console.log('DEBUG: Failed to get plans from ``' + url + '``', error);
    };
    request.open("get", url, true);
    request.send();
};

/* method: User::reloadPlans()
 * removes all existing plans UI and calls User::getPlans()
 */
User.prototype.reloadPlans = function() {
    // remove UI from plans
    for (var i = 0; i < this.plans.length; i++) {
        this.plans[i].removeUI();
    }
    // clean this.plans
    this.plans = [];
    // clean phone's storage
    localStorage.setItem('plans', '[]');
    // load plans from server
    this.getPlans(app.toggleSettings);
};

/* method: User::initiatePlans()
 * parses plans from JSON representation and initiates a new Plan for
 * each of them
 */
User.prototype.initiatePlans = function(plansString) {
    var plans = JSON.parse(plansString);
    for (var i = 0; i < plans.length; i++) {
        this.plans.push(new Plan(plans[i]));
    }
}

/* method: User::loadPlans()
 * loads plans from either localStorage or using User::getPlans
 */
User.prototype.loadPlans = function() {
    var plans = localStorage.getItem('plans');
    if (plans === null) {
        return this.getPlans();
    }
    console.log('DEBUG: plans loaded from device');
    this.initiatePlans(plans);
    this.displayPlans();
};

/* method: User::displayPlans()
 * loads local names of days and calls Plan::createUI for each plan
 */
User.prototype.displayPlans = function() {
    var self = this;
    navigator.globalization.getDateNames(function(dayOfWeek){
        var deck = document.getElementById('plan-group');
        var tabbar = document.getElementById('plan-group-menu');
        for (var i = 0; i < self.plans.length; i++) {
            self.plans[i].createUI(deck, tabbar, dayOfWeek);
        }
    }, function() {}, {type: 'narrow', item: 'days'});
};

// Plan type
function Plan(plan) {
    this.schedule = plan.week;
    this.title = plan.title;
    this.id = plan.id;
    this.active = plan.active;
    this.tab = null;
    this.card = null;
    this.table = null;
};

/* method Plan::selectTab()
 * switches the active tab of <brick-tabbar>
 * uses polling as brick might not be fully loaded yet
 */
Plan.prototype.selectTab = function(deck) {
    var self = this;
    function selectActiveTab() {
        if (!self.tab.targetElement) {
            return window.setTimeout(selectActiveTab, 100);
        }
        deck.showCard(self.tab.targetElement);
    }
    selectActiveTab();
};

/* method Plan::createUI()
 * creates <brick-card> and <brick-tab> elements, then renders plan into
 * the <table> element
 */
Plan.prototype.createUI = function(deck, tabbar, dayOfWeek) {
    // create card
    this.card = document.createElement('brick-card');
    this.card.setAttribute('id', this.id);
    deck.appendChild(this.card);

    //create tab
    this.tab = document.createElement('brick-tabbar-tab');
    this.tab.setAttribute('target', this.id);
    this.tab.appendChild(document.createTextNode(this.title));
    tabbar.appendChild(this.tab);

    // link card and tab DOM Elements
    this.card.tabElement = this.tab;
    this.card.addEventListener('show', function() {
        this.tabElement.select();
    });

    // create plan table
    this.table = document.createElement('table');

    // hide not used days
    var numberOfDays = this.schedule.length;
    var cleanPlan = [];
    for (var j = 0; j < numberOfDays; j++) {
        if (this.schedule[j].length > 0) {
            cleanPlan.push(this.schedule[j]);
        }
    }

    // rotate the table
    var daysInHours = [];
    for (j = 0; j < cleanPlan.length; j++) {
        for (var k = 0; k < cleanPlan[j].length; k++) {
            if (!daysInHours[k]) {
                daysInHours[k] = [];
            }
            daysInHours[k][j] = cleanPlan[j][k];
        }
    }

    // create plan's DOM Elements
    for (var j = 0; j < daysInHours.length; j++) {
        var tr = this.table.insertRow(-1);
        var td = tr.insertCell(-1);
        td.appendChild(document.createTextNode(j + 1));
        for (var k = 0; k < cleanPlan.length; k++) {
            var td = tr.insertCell(-1);
            if (daysInHours[j][k]) {
                td.appendChild(document.createTextNode(daysInHours[j][k]));
            }
        }
    }

    // create plan's header
    var thead = this.table.createTHead();
    var tr = thead.insertRow();
    var th_empty = document.createElement('th');
    tr.appendChild(th_empty);
    var weekDayNumber;
    for (var j = 0; j < numberOfDays; j++) {
        var weekDayNumber = (j + 1) % 7;
        if (this.schedule[j].length > 0) {
            var th = document.createElement('th');
            th.appendChild(document.createTextNode(dayOfWeek.value[weekDayNumber]));
            tr.appendChild(th);
        }
    }
    this.card.appendChild(this.table);

    // select current plan if active
    if (this.active) {
        this.selectTab(deck);
    }
};

Plan.prototype.removeUI = function() {
    this.card.remove();
    this.tab.remove();
};

var app = {
    plans: [],
    planGroup: null,
    initialize: function() {
        this.bindEvents();
    },

    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },

onDeviceReady: function() {
    app.plansServer = localStorage.getItem('plansServer');
    app.userID = localStorage.getItem('userID');
    app.activateFingerSwipe();
    app.assignButtons();

    console.log(app.userID);
    if (app.plansServer && app.userID) {
        app.user = new User(app.userID);
        app.user.loadPlans();
    } else {
        app.toggleSettings();
    }
},

    activateFingerSwipe: function() {
        // Switching from one tab to another is done automatically
        // We just need to link it backwards - change menu if slides
        // changed without touching the menu
        app.planGroupMenu = document.getElementById('plan-group-menu');

        // Implementing one finger swipe to change deck card
        app.planGroup = document.getElementById('plan-group');

        var startX = null;
        var slideThreshold = 100;

        function touchStart(sX) {
            startX = sX;
        }

        function touchEnd(endX) {
            var deltaX;
            if (startX) {
                deltaX = endX - startX;
                if (Math.abs(deltaX) > slideThreshold) {
                    startX = null;
                    if (deltaX > 0) {
                        app.previousPlan();
                    } else {
                        app.nextPlan();
                    }
                }
            }
        }

        app.planGroup.addEventListener('touchstart', function(evt) {
            var touches = evt.changedTouches;
            if (touches.length === 1) {
                // runs only for one finger touch
                touchStart(touches[0].pageX);
            }
        });

        app.planGroup.addEventListener('touchmove', function(evt) {
            // switched off scrolling on webkit
            evt.preventDefault();
            touchEnd(evt.changedTouches[0].pageX);
        });
    },

    toggleSettings: function() {
        app.flipbox.toggle();
    },

    assignButtons: function(){
        app.flipbox = document.querySelector('brick-flipbox');
        var settingsButton = document.getElementById('settings-button');
        var settingsOffButton = document.getElementById('settings-off-button');
        settingsButton.addEventListener('click', app.toggleSettings);
        settingsOffButton.addEventListener('click', app.toggleSettings);

        var reloadButton = document.getElementById('reload-button');
        reloadButton.addEventListener('touchend', function() {
            app.user.reloadPlans();
        }, false);

        var serverInput = document.getElementById('input-server');
        if (app.plansServer) {
            serverInput.value = app.plansServer;
        }
        serverInput.addEventListener('blur', function() {
            app.plansServer = serverInput.value || null;
            localStorage.setItem('plansServer', app.plansServer);
        });

        var userInput = document.getElementById('input-user');
        if (app.userID) {
            userInput.value = app.userID;
        }
        userInput.addEventListener('blur', function() {
            app.userID = userInput.value || null;
            if (app.userID) {
                if (app.user) {
                    app.user.id = app.userID;
                } else {
                    app.user = new User(app.userID);
                }
                localStorage.setItem('userID', app.userID);
            }
        });

        document.getElementById('form-settings').addEventListener('submit', function(e) {
            e.preventDefault();
        }, false)
    },

    previousPlan: function() {
        app.planGroup.previousCard();
    },

    nextPlan: function() {
        app.planGroup.nextCard();
    }
}

window.onload = function() {
  app.initialize();
}

<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-2016 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint browser: true, jquery: true */
/* globals define: false */

(function (f, define) {
    'use strict';
    define([
        './window.assert',
        './window.logger',
        './vendor/kendo/kendo.binder',
        './vendor/kendo/kendo.grid',
        './vendor/kendo/kendo.combobox'
    ], f);
})(function () {

    'use strict';

    (function ($, undefined) {

        var kendo = window.kendo;
        var ui = kendo.ui;
        var Widget = ui.Widget;
        var assert = window.assert;
        var logger = new window.Logger('kidoju.widgets.styleeditor');
        var STRING = 'string';
        var UNDEFINED = 'undefined';
        var CHANGE = 'change';
        var KEYPRESS = 'keypress';
        // var NS = '.kendoStyleEditor';
        var WIDGET_CLASS = 'k-grid k-widget kj-styleeditor';
        var COLON = ':';
        var SEMICOLON = ';';

        /*********************************************************************************
         * Helpers
         *********************************************************************************/

        /**
         * Normalize value
         * Removes spaces around colons and semi-colons and end with semi-colon
         * @param value
         */
        function normalizeValue(value) {
            assert.type(STRING, value, kendo.format(assert.messages.type.default), 'value', STRING);
            return value.replace(/[\s]*(\:|\;)[\s]*/g, '$1') + ((value.length && value.charAt(value.length - 1) === SEMICOLON) ? '' : SEMICOLON);
        }

        /*********************************************************************************
         * Widget
         *********************************************************************************/

        /**
         * @class StyleEditor Widget (kendoStyleEditor)
         */
        var StyleEditor = Widget.extend({

            /**
             * Initializes the widget
             * @param element
             * @param options
             */
            init: function (element, options) {
                var that = this;
                options = options || {};
                Widget.fn.init.call(that, element, options);
                logger.debug('widget initialized');
                // if ($.isFunction($.fn.kendoGrid)) {
                that._setDataSource();
                that.value(that.options.value);
                that._layout();
                that._setDestroyHandler();
                that._setKeyPressHandler();
                // }
                // TODO a simple textarea would do when running kendo-core without grid
            },

            /**
             * StyleEditor options
             * @property options
             */
            options: {
                name: 'StyleEditor',
                height: 400,
                value: '',
                messages: {
                    columns: {
                        name: 'Name',
                        value: 'Value'
                    },
                    toolbar: {
                        create: 'New Style',
                        destroy: 'Delete'
                    },
                    validation: {
                        name: 'Name is required',
                        value: 'Value is required'
                    }
                }
            },

            /**
             * Widget events
             * @property events
             */
            events: [
                CHANGE
            ],

            /* This function has too many statements. */
            /* jshint -W071 */

            /* This function's cyclomatic complexity is too high. */
            /* jshint -W074 */

            /**
             * Gets or sets the style value
             * @param value
             * @returns {string}
             */
            value: function (value) {

                /*
                // Sort function on style names
                // Sorting is not user-friendly as positions change unexpectedly in the grid
                function sort(a, b) {
                    if (a.name > b.name) {
                        return 1;
                    }
                    if (a.name < b.name) {
                        return -1;
                    }
                    // a must be equal to b
                    return 0;
                }
                */

                var that = this;
                var i;
                var data;
                if ($.type(value) === STRING) {
                    var _value = that.value();
                    value = normalizeValue(value);
                    if (value !== _value) {
                        // Break the various style names/values and fill the data source
                        var styles = value.split(SEMICOLON);
                        data = [];
                        for (i = 0; i < styles.length; i++) {
                            var style = styles[i].split(COLON);
                            if ($.isArray(style) && style.length === 2) {
                                data.push({ name: style[0].trim(), value: style[1].trim() });
                            }
                        }
                        that._dataSource.data(data); // (data.sort(sort));
                        // that.trigger(CHANGE);
                    }
                } else if ($.type(value) === UNDEFINED) {
                    // Convert the data source into an HTML style attribute
                    value = '';
                    data = that._dataSource.data();
                    for (i = 0; i < data.length; i++) {
                        var name = data[i].name;
                        var val = data[i].value;
                        if ($.type(name) === STRING && $.type(val) === STRING) {
                            name = name.trim();
                            val = val.trim();
                            if (name.length && val.length) {
                                value += name + COLON + val + SEMICOLON;
                            }
                        }
                    }
                    return value;
                } else {
                    throw new TypeError('value is expected to be a string if not undefined');
                }
            },

            /* jshint +W074 */
            /* jshint +W071 */

            /**
             * Sets the data source
             * @private
             */
            _setDataSource: function () {
                var that = this;
                // This dataSource is private to the widget
                that._dataSource = new kendo.data.DataSource({
                    autoSync: true,
                    change: function (e) {
                        // triggers the change event for MVVM
                        // that.trigger(CHANGE, { value: that.value() }); // otherwise that.value is executed twice (also by MVVM)
                        that.trigger(CHANGE);
                    },
                    data: [],
                    schema: {
                        model: {
                            id: 'name',
                            fields: {
                                name: {
                                    type: 'string',
                                    validation: {
                                        required: true
                                    }
                                },
                                value: {
                                    type: 'string',
                                    validation: {
                                        required: true
                                    }
                                }
                            }
                        }
                    }
                });
            },

            /**
             * Builds the widget layout
             * @private
             */
            _layout: function () {

                // This function is taken from http://demos.kendoui.com/web/grid/editing-custom.html
                // See also http://www.telerik.com/forums/kendo-ui-grid-s-combobox-editor-template-validation
                function cssDropDownEditor(container, options) {
                    // We cannot set the combobox name for validation before initializing the kendo ui widget
                    // See http://www.telerik.com/forums/comboxbox-in-grid-with-validation
                    // $('<input name="style_name" data-bind="value: ' + options.field + '" required data-required-msg="' + that.options.messages.validation.name + '">')
                    var combobox = $('<input data-bind="value: ' + options.field + '" required data-required-msg="' + that.options.messages.validation.name + '">')
                        .appendTo(container)
                        .kendoComboBox({
                            autoBind: false,
                            change: function (e) {
                                // The change event handler assigns a default value depending on the style name
                                if (e /*instanceof $.Event*/ && e.sender instanceof kendo.ui.ComboBox) {
                                    var dataItem = e.sender.dataItem();
                                    // var grid = container.closest('.k-grid').data('kendoGrid');
                                    var grid = that.element.data('kendoGrid');
                                    var uid = container.parent().attr(kendo.attr('uid'));
                                    if (grid instanceof kendo.ui.Grid && $.type(uid) === 'string' && $.type(dataItem) !== UNDEFINED) {
                                        var style = grid.dataSource.getByUid(uid);
                                        style.set('value', dataItem.get('value'));
                                    }
                                }
                            },
                            // dataSource: viewModel.css,
                            dataTextField: 'name',
                            dataValueField: 'name'
                        })
                        .data('kendoComboBox');
                    // The workaround for validation to work is to set the name after initializing the kendo ui widget
                    // TODO http://www.telerik.com/forums/how-to-enforce-validation-in-grid-sample
                    combobox.element.attr('name', 'name');
                    $('').appendTo(container);
                }

                var that = this;
                that.wrapper = that.element;
                that.element.addClass(WIDGET_CLASS);
                that.grid = that.element
                    .kendoGrid({
                    columns: [
                        { field: 'name', title: that.options.messages.columns.name, editor: cssDropDownEditor, template: '#=name#' },
                        { field: 'value', title: that.options.messages.columns.value }
                    ],
                    /*
                     dataBound: function (e) {
                     if (e.sender instanceof kendo.ui.Grid) {
                     var selected = e.sender.select();
                     if (selected instanceof $ && selected.length === 0) {
                     e.sender.select('tr:eq(1)');
                     e.sender.editCell('tr:eq(1) td:eq(0)');
                     }
                     }
                     },
                     */
                    dataSource: that._dataSource,
                    edit: function (e) {
                        if (e /*instanceof $.Event*/ && e.sender instanceof kendo.ui.Grid && e.container instanceof $) {
                            // Select the edited row
                            this.select(e.container.parent());
                            // Find the combobox and update dataSource
                            var comboBox = e.container.find('input:not(.k-input)').data('kendoComboBox');
                            if (comboBox instanceof kendo.ui.ComboBox) {
                                var styles = e.sender.dataSource.data();
                                var css = [];
                                var all = [
                                    // This is where we define all style names displayed in the combo box and their respective default values
                                    { name: 'background-color', value: '#FFFFFF' },
                                    { name: 'border-color', value: '#000000' },
                                    { name: 'border-radius', value: '5px' },
                                    { name: 'border-style', value: 'solid' },
                                    { name: 'border-width', value: '1px' },
                                    { name: 'color', value: '#000000' },
                                    { name: 'font-family', value: 'Times New Roman' },
                                    { name: 'font-size', value: '20px' },
                                    { name: 'font-style', value: 'italic' },
                                    { name: 'font-weight', value: 'bold' },
                                    { name: 'padding', value: '10px' },
                                    { name: 'margin', value: '10px' },
                                    { name: 'text-align', value: 'center' },
                                    { name: 'text-decoration', value: 'underline' },
                                    { name: 'vertical-align', value: 'middle' }
                                ];
                                for (var i = 0; i < all.length; i++) {
                                    var found = false;
                                    for (var j = 0; j < styles.length; j++) {
                                        /* Blocks are nested too deeply. */
                                        /* jshint -W073 */
                                        if (all[i].name === styles[j].name && all[i].name !== comboBox.value()) {
                                            found = true;
                                            break;
                                        }
                                        /* jshint +W073 */
                                    }
                                    if (!found) {
                                        css.push(all[i]);
                                    }
                                }
                                comboBox.setDataSource(css);
                            }
                        }
                    },
                    editable: true,
                    height: that.options.height,
                    resizable: true,
                    scrollable: true,
                    selectable: 'row',
                    sortable: true,
                    toolbar: [
                        { name: 'create', text: that.options.messages.toolbar.create },
                        { name: 'destroy', text: that.options.messages.toolbar.destroy }
                    ]
                })
                    .data('kendoGrid');

            },

            /**
             * Refreshed the grid
             */
            refresh: function () {
                var that = this;
                if  (that.grid instanceof kendo.ui.Grid) {
                    that.grid.refresh();
                }
            },

            /**
             * Add a click event handler for the destroy button
             * @private
             */
            _setDestroyHandler: function () {
                var element = this.element;
                element.find('.k-grid-toolbar>.k-grid-delete').click(function (e) {
                    var grid = element.data('kendoGrid');
                    if (grid instanceof kendo.ui.Grid) {
                        var selected = grid.select();
                        if (selected instanceof $ && selected.length) {
                            // allthough shorter, the following displays an alert to confirm deletion
                            // grid.removeRow(selected);
                            var uid = selected.attr(kendo.attr('uid'));
                            var style = grid.dataSource.getByUid(uid);
                            grid.dataSource.remove(style);
                        }
                    }
                });
            },

            /* This function's cyclomatic complexity is too high. */
            /* jshint -W074 */

            /**
             * Set a keypress event handler to prevent some unhealthy characters to be used for style names and values
             * @private
             */
            _setKeyPressHandler: function () {
                var element = this.element;
                element.find('table').on(KEYPRESS, function (e) {
                    if (e /*instanceof $.Event*/ && e.target instanceof window.HTMLElement) {
                        var input = $(e.target);
                        if (input.hasClass('k-input') && input.parent().hasClass('k-dropdown-wrap')) {
                            // the drop down with a list of style names has the focus
                            // allowed characters are a-z (96-123) and minus/hiphen/dash (45)
                            if (!(e.which === 45 || (e.which > 96 && e.which < 123))) {
                                e.preventDefault();
                            }
                        } else if (input.hasClass('k-textbox') && input.parent().hasClass('k-edit-cell')) {
                            // the textbox for style value has the focus
                            // do not allow < (60), > (62), ; (59) and " (34)
                            if (e.which === 34 || e.which === 59 || e.which === 60 || e.which === 62) {
                                e.preventDefault();
                            }
                        }
                    }
                });
            },

            /* jshint +W074 */

            /**
             * Clears the widget
             * @method _clear
             * @private
             */
            _clear: function () {
                var that = this;
                // unbind kendo
                // kendo.unbind($(that.element));
                // unbind all other events
                that.element.find('*').off();
                that.element.off();
                // remove descendants
                that.element.empty();
                that.element.removeClass(WIDGET_CLASS);
            },

            /**
             * Destroys the widget including all DOM modifications
             * @method destroy
             */
            destroy: function () {
                var that = this;
                Widget.fn.destroy.call(that);
                that._clear();
                // if ($.isFunction(that._refreshHandler)) {
                //    that.options.tools.unbind(CHANGE, that._refreshHandler);
                // }
                kendo.destroy(that.element);
            }

        });

        kendo.ui.plugin(StyleEditor);

    }(window.jQuery));

    return window.kendo;

}, typeof define === 'function' && define.amd ? define : function (_, f) { 'use strict'; f(); });
<commit_msg>Fixed jscs error<commit_after>/**
 * Copyright (c) 2013-2016 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint browser: true, jquery: true */
/* globals define: false */

(function (f, define) {
    'use strict';
    define([
        './window.assert',
        './window.logger',
        './vendor/kendo/kendo.binder',
        './vendor/kendo/kendo.grid',
        './vendor/kendo/kendo.combobox'
    ], f);
})(function () {

    'use strict';

    (function ($, undefined) {

        var kendo = window.kendo;
        var ui = kendo.ui;
        var Widget = ui.Widget;
        var assert = window.assert;
        var logger = new window.Logger('kidoju.widgets.styleeditor');
        var STRING = 'string';
        var UNDEFINED = 'undefined';
        var CHANGE = 'change';
        var KEYPRESS = 'keypress';
        // var NS = '.kendoStyleEditor';
        var WIDGET_CLASS = 'k-grid k-widget kj-styleeditor';
        var COLON = ':';
        var SEMICOLON = ';';

        /*********************************************************************************
         * Helpers
         *********************************************************************************/

        /**
         * Normalize value
         * Removes spaces around colons and semi-colons and end with semi-colon
         * @param value
         */
        function normalizeValue(value) {
            assert.type(STRING, value, kendo.format(assert.messages.type.default), 'value', STRING);
            return value.replace(/[\s]*(\:|\;)[\s]*/g, '$1') + ((value.length && value.charAt(value.length - 1) === SEMICOLON) ? '' : SEMICOLON);
        }

        /*********************************************************************************
         * Widget
         *********************************************************************************/

        /**
         * @class StyleEditor Widget (kendoStyleEditor)
         */
        var StyleEditor = Widget.extend({

            /**
             * Initializes the widget
             * @param element
             * @param options
             */
            init: function (element, options) {
                var that = this;
                options = options || {};
                Widget.fn.init.call(that, element, options);
                logger.debug('widget initialized');
                // if ($.isFunction($.fn.kendoGrid)) {
                that._setDataSource();
                that.value(that.options.value);
                that._layout();
                that._setDestroyHandler();
                that._setKeyPressHandler();
                // }
                // TODO a simple textarea would do when running kendo-core without grid
            },

            /**
             * StyleEditor options
             * @property options
             */
            options: {
                name: 'StyleEditor',
                height: 400,
                value: '',
                messages: {
                    columns: {
                        name: 'Name',
                        value: 'Value'
                    },
                    toolbar: {
                        create: 'New Style',
                        destroy: 'Delete'
                    },
                    validation: {
                        name: 'Name is required',
                        value: 'Value is required'
                    }
                }
            },

            /**
             * Widget events
             * @property events
             */
            events: [
                CHANGE
            ],

            /* This function has too many statements. */
            /* jshint -W071 */

            /* This function's cyclomatic complexity is too high. */
            /* jshint -W074 */

            /**
             * Gets or sets the style value
             * @param value
             * @returns {string}
             */
            value: function (value) {

                /*
                // Sort function on style names
                // Sorting is not user-friendly as positions change unexpectedly in the grid
                function sort(a, b) {
                    if (a.name > b.name) {
                        return 1;
                    }
                    if (a.name < b.name) {
                        return -1;
                    }
                    // a must be equal to b
                    return 0;
                }
                */

                var that = this;
                var i;
                var data;
                if ($.type(value) === STRING) {
                    var _value = that.value();
                    value = normalizeValue(value);
                    if (value !== _value) {
                        // Break the various style names/values and fill the data source
                        var styles = value.split(SEMICOLON);
                        data = [];
                        for (i = 0; i < styles.length; i++) {
                            var style = styles[i].split(COLON);
                            if ($.isArray(style) && style.length === 2) {
                                data.push({ name: style[0].trim(), value: style[1].trim() });
                            }
                        }
                        that._dataSource.data(data); // (data.sort(sort));
                        // that.trigger(CHANGE);
                    }
                } else if ($.type(value) === UNDEFINED) {
                    // Convert the data source into an HTML style attribute
                    value = '';
                    data = that._dataSource.data();
                    for (i = 0; i < data.length; i++) {
                        var name = data[i].name;
                        var val = data[i].value;
                        if ($.type(name) === STRING && $.type(val) === STRING) {
                            name = name.trim();
                            val = val.trim();
                            if (name.length && val.length) {
                                value += name + COLON + val + SEMICOLON;
                            }
                        }
                    }
                    return value;
                } else {
                    throw new TypeError('value is expected to be a string if not undefined');
                }
            },

            /* jshint +W074 */
            /* jshint +W071 */

            /**
             * Sets the data source
             * @private
             */
            _setDataSource: function () {
                var that = this;
                // This dataSource is private to the widget
                that._dataSource = new kendo.data.DataSource({
                    autoSync: true,
                    change: function (e) {
                        // triggers the change event for MVVM
                        // that.trigger(CHANGE, { value: that.value() }); // otherwise that.value is executed twice (also by MVVM)
                        that.trigger(CHANGE);
                    },
                    data: [],
                    schema: {
                        model: {
                            id: 'name',
                            fields: {
                                name: {
                                    type: 'string',
                                    validation: {
                                        required: true
                                    }
                                },
                                value: {
                                    type: 'string',
                                    validation: {
                                        required: true
                                    }
                                }
                            }
                        }
                    }
                });
            },

            /**
             * Builds the widget layout
             * @private
             */
            _layout: function () {

                // This function is taken from http://demos.kendoui.com/web/grid/editing-custom.html
                // See also http://www.telerik.com/forums/kendo-ui-grid-s-combobox-editor-template-validation
                function cssDropDownEditor(container, options) {
                    // We cannot set the combobox name for validation before initializing the kendo ui widget
                    // See http://www.telerik.com/forums/comboxbox-in-grid-with-validation
                    // $('<input name="style_name" data-bind="value: ' + options.field + '" required data-required-msg="' + that.options.messages.validation.name + '">')
                    var combobox = $('<input data-bind="value: ' + options.field + '" required data-required-msg="' + that.options.messages.validation.name + '">')
                        .appendTo(container)
                        .kendoComboBox({
                            autoBind: false,
                            change: function (e) {
                                // The change event handler assigns a default value depending on the style name
                                if (e /*instanceof $.Event*/ && e.sender instanceof kendo.ui.ComboBox) {
                                    var dataItem = e.sender.dataItem();
                                    // var grid = container.closest('.k-grid').data('kendoGrid');
                                    var grid = that.element.data('kendoGrid');
                                    var uid = container.parent().attr(kendo.attr('uid'));
                                    if (grid instanceof kendo.ui.Grid && $.type(uid) === 'string' && $.type(dataItem) !== UNDEFINED) {
                                        var style = grid.dataSource.getByUid(uid);
                                        style.set('value', dataItem.get('value'));
                                    }
                                }
                            },
                            // dataSource: viewModel.css,
                            dataTextField: 'name',
                            dataValueField: 'name'
                        })
                        .data('kendoComboBox');
                    // The workaround for validation to work is to set the name after initializing the kendo ui widget
                    // TODO http://www.telerik.com/forums/how-to-enforce-validation-in-grid-sample
                    combobox.element.attr('name', 'name');
                    $('').appendTo(container);
                }

                var that = this;
                that.wrapper = that.element;
                that.element.addClass(WIDGET_CLASS);
                that.grid = that.element
                    .kendoGrid({
                    columns: [
                        { field: 'name', title: that.options.messages.columns.name, editor: cssDropDownEditor, template: '#=name#' },
                        { field: 'value', title: that.options.messages.columns.value }
                    ],
                    /*
                     dataBound: function (e) {
                     if (e.sender instanceof kendo.ui.Grid) {
                     var selected = e.sender.select();
                     if (selected instanceof $ && selected.length === 0) {
                     e.sender.select('tr:eq(1)');
                     e.sender.editCell('tr:eq(1) td:eq(0)');
                     }
                     }
                     },
                     */
                    dataSource: that._dataSource,
                    edit: function (e) {
                        if (e /*instanceof $.Event*/ && e.sender instanceof kendo.ui.Grid && e.container instanceof $) {
                            // Select the edited row
                            this.select(e.container.parent());
                            // Find the combobox and update dataSource
                            var comboBox = e.container.find('input:not(.k-input)').data('kendoComboBox');
                            if (comboBox instanceof kendo.ui.ComboBox) {
                                var styles = e.sender.dataSource.data();
                                var css = [];
                                var all = [
                                    // This is where we define all style names displayed in the combo box and their respective default values
                                    { name: 'background-color', value: '#FFFFFF' },
                                    { name: 'border-color', value: '#000000' },
                                    { name: 'border-radius', value: '5px' },
                                    { name: 'border-style', value: 'solid' },
                                    { name: 'border-width', value: '1px' },
                                    { name: 'color', value: '#000000' },
                                    { name: 'font-family', value: 'Times New Roman' },
                                    { name: 'font-size', value: '20px' },
                                    { name: 'font-style', value: 'italic' },
                                    { name: 'font-weight', value: 'bold' },
                                    { name: 'padding', value: '10px' },
                                    { name: 'margin', value: '10px' },
                                    { name: 'text-align', value: 'center' },
                                    { name: 'text-decoration', value: 'underline' },
                                    { name: 'vertical-align', value: 'middle' }
                                ];
                                for (var i = 0; i < all.length; i++) {
                                    var found = false;
                                    for (var j = 0; j < styles.length; j++) {
                                        /* Blocks are nested too deeply. */
                                        /* jshint -W073 */
                                        if (all[i].name === styles[j].name && all[i].name !== comboBox.value()) {
                                            found = true;
                                            break;
                                        }
                                        /* jshint +W073 */
                                    }
                                    if (!found) {
                                        css.push(all[i]);
                                    }
                                }
                                comboBox.setDataSource(css);
                            }
                        }
                    },
                    editable: true,
                    height: that.options.height,
                    resizable: true,
                    scrollable: true,
                    selectable: 'row',
                    sortable: true,
                    toolbar: [
                        { name: 'create', text: that.options.messages.toolbar.create },
                        { name: 'destroy', text: that.options.messages.toolbar.destroy }
                    ]
                })
                    .data('kendoGrid');

            },

            /**
             * Refreshed the grid
             */
            refresh: function () {
                var that = this;
                if (that.grid instanceof kendo.ui.Grid) {
                    that.grid.refresh();
                }
            },

            /**
             * Add a click event handler for the destroy button
             * @private
             */
            _setDestroyHandler: function () {
                var element = this.element;
                element.find('.k-grid-toolbar>.k-grid-delete').click(function (e) {
                    var grid = element.data('kendoGrid');
                    if (grid instanceof kendo.ui.Grid) {
                        var selected = grid.select();
                        if (selected instanceof $ && selected.length) {
                            // allthough shorter, the following displays an alert to confirm deletion
                            // grid.removeRow(selected);
                            var uid = selected.attr(kendo.attr('uid'));
                            var style = grid.dataSource.getByUid(uid);
                            grid.dataSource.remove(style);
                        }
                    }
                });
            },

            /* This function's cyclomatic complexity is too high. */
            /* jshint -W074 */

            /**
             * Set a keypress event handler to prevent some unhealthy characters to be used for style names and values
             * @private
             */
            _setKeyPressHandler: function () {
                var element = this.element;
                element.find('table').on(KEYPRESS, function (e) {
                    if (e /*instanceof $.Event*/ && e.target instanceof window.HTMLElement) {
                        var input = $(e.target);
                        if (input.hasClass('k-input') && input.parent().hasClass('k-dropdown-wrap')) {
                            // the drop down with a list of style names has the focus
                            // allowed characters are a-z (96-123) and minus/hiphen/dash (45)
                            if (!(e.which === 45 || (e.which > 96 && e.which < 123))) {
                                e.preventDefault();
                            }
                        } else if (input.hasClass('k-textbox') && input.parent().hasClass('k-edit-cell')) {
                            // the textbox for style value has the focus
                            // do not allow < (60), > (62), ; (59) and " (34)
                            if (e.which === 34 || e.which === 59 || e.which === 60 || e.which === 62) {
                                e.preventDefault();
                            }
                        }
                    }
                });
            },

            /* jshint +W074 */

            /**
             * Clears the widget
             * @method _clear
             * @private
             */
            _clear: function () {
                var that = this;
                // unbind kendo
                // kendo.unbind($(that.element));
                // unbind all other events
                that.element.find('*').off();
                that.element.off();
                // remove descendants
                that.element.empty();
                that.element.removeClass(WIDGET_CLASS);
            },

            /**
             * Destroys the widget including all DOM modifications
             * @method destroy
             */
            destroy: function () {
                var that = this;
                Widget.fn.destroy.call(that);
                that._clear();
                // if ($.isFunction(that._refreshHandler)) {
                //    that.options.tools.unbind(CHANGE, that._refreshHandler);
                // }
                kendo.destroy(that.element);
            }

        });

        kendo.ui.plugin(StyleEditor);

    }(window.jQuery));

    return window.kendo;

}, typeof define === 'function' && define.amd ? define : function (_, f) { 'use strict'; f(); });
<|endoftext|>


<commit_before>const Nuxt = require('nuxt')
const bodyParser = require('body-parser')
const session = require('express-session')
const app = require('express')()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000
const sessionSecret = process.env.SECRET || 'ncdunioubaiub9287&@!'

app.set('port', port)

app.use(bodyParser.json())

app.use(session({
	secret: sessionSecret,
	resave: false,
	saveUninitialized: false,
	cookie: { maxAge: 60000 }
}))

app.post('/api/login', function (req, res) {
	if (req.body.data.email === 'demo@gmail.com' && req.body.data.password === 'demo') {
		req.session.authUser = { username: 'demo' }
		return res.json({ username: 'demo' })
	}
	res.status(401).json({ error: 'Bad credentials' })
})

app.post('/api/logout', function (req, res) {
	delete req.session.authUser
	res.json({ ok: true })
})

var config = require('./nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')

const nuxt = new Nuxt(config)

if (config.dev) {
	nuxt.build()
	.catch( function (error) {
		console.log(error) // eslint-disable-line no-console
		process.exit(1)
	})
}

app.use(nuxt.render)
app.listen(port, host)
console.log('Server is listening on ' + host + ':' + port) // eslint-disable-line no-console
<commit_msg>passport js implemented with passport-local strategy<commit_after>const Nuxt = require('nuxt')
const bodyParser = require('body-parser')
const session = require('express-session')
const express = require('express')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const appData = require('./data.json')

const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000
const sessionSecret = process.env.SECRET || 'ncdunioubaiub9287&@!'

const userData = appData.users

function getUser (email) {
  const user = userData.find(function (user) {
    return user.email === email
  })
  return Object.assign({}, user)
}

const server = express()

server.use(bodyParser.json())
server.use(session({
  secret: sessionSecret,
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 60000 }
}))
server.use(passport.initialize())
server.use(passport.session())

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password'
  },
  function (email, password, done) {
    const user = getUser(email)

    if (!user || user.password !== password) {
      return done(null, false)
    }

    delete user.password
    return done(null, user)
  }
))

passport.serializeUser(function (user, done) {
  return done(null, user.email)
})

passport.deserializeUser(function (email, done) {
  const user = getUser(email)
  delete user.password
  return done(null, user)
})

const authRoutes = express.Router()

authRoutes.post('/login', function(req, res, next) {
  passport.authenticate('local', function (err, user, info) {
    if (err) { return next(err) }
    if (!user) { return res.status(401).json({ error: 'Bad credentials' }) }
    req.logIn(user, function(err) {
      if (err) { return next(err) }
      return res.json({ user: user.email })
    })
  })(req, res, next)
})

authRoutes.post('/logout', function (req, res) {
  req.logout()
  res.json({ user: req.user })
})

server.use('/auth', authRoutes)

const config = require('./nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')

const nuxt = new Nuxt(config)

if (config.dev) {
  nuxt.build()
  .catch( function (error) {
    console.log(error) // eslint-disable-line no-console
    process.exit(1)
  })
}

server.use(nuxt.render)
server.listen(port, host)
console.log('Server is listening on ' + host + ':' + port) // eslint-disable-line no-console
<|endoftext|>


<commit_before>var autorate, rating, downloadToggle, downloadType;
var $search = $("input[type=text]");
var playing = false;
var focused = false;
var keycodes = {
  49: 1,
  50: 2,
  51: 3,
  52: 4,
  53: 5
};
var songTypes = {
  "Dirty":         /^Dirty$/,
  "Clean":         /^Clean$/,
  "Intro - Dirty": /^Intro - Dirty$/,
  "Intro - Clean": /^Intro - Clean$/,
  "Main":          /^Main$/,
  "Inst":          /^Inst$/,
  "Acap":          /^Acap/
}

function rate(rating) {
    $('option[value="' + rating + '"]').attr('selected', 'selected').parent().focus();
    $('#ctl00_PageContent_submit').click();
}

function download(songType) {
  var re = new RegExp(songTypes[songType]);
  var $availFormats = $("#ad_sublisting li");
  var typeText = ".float_left";
  var dl = ".reviw_tdonw";
  var found = false;

  $availFormats.each(function(index, li) {
    if ($(this).find(typeText).text().match(re)) {
      found = true
      $(this).find(dl).children()[0].click()
    }
  })

  if (!found) {
    console.log("Format not found")
  }
}

function playPause(e) {
  if (!playing) {
    document.getElementsByClassName("jp-play")[0].click()
    playing = true;
  } else {
    document.getElementsByClassName("jp-pause")[0].click()
    playing = false;
  }
}

document.addEventListener('keydown', function(e) {
  if (!focused) {
    var keycode = keycodes[e.keyCode];
    if (keycode) {
      rate(keycode);
    }
    if (e.keyCode === 68) {
      download("Acap")
    }
    if (e.keyCode === 80) {
      playPause();
    }
  }
}, true);

chrome.storage.local.get(["autorate", "rating", "downloadToggle", "downloadType"], function(settings) {
  autorate = settings.autorate ? true : false;

  rating = settings.rating;

  downloadToggle = settings.downloadToggle ? true : false;

  downloadType = settings.downloadType;
});

chrome.storage.onChanged.addListener(function(changes, local) {
  if (changes.autorate) {
    if (changes.autorate.newValue) {
      autorate = true;
    } else {
      autorate = false;
    }
  }

  if (changes.rating) {
    rating = changes.rating.newValue;
  }

  if (changes.downloadToggle) {
    if (changes.downloadToggle.newValue) {
      downloadToggle = true;
    } else {
      downloadToggle = false;
    }
  }

  if (changes.downloadType) {
    downloadType = changes.downloadType.newValue;
  }
});

$(function() {
  if (autorate) {
    rate(rating);
  }

  $search.focus(function() {
    focused = true;
  });
  $search.blur(function() {
    focused = false;
  });
});
<commit_msg>[feat] Added warning text when song type isn't available for auto DL<commit_after>var autorate, rating, downloadToggle, downloadType;
var $submit = $('#ctl00_PageContent_submit');
var $search = $("input[type=text]");
var $searchBtn = $(".search_btn");
var $warning = $("div.header_border_bottom:first");
var artist = $("#artist_details li:first div.artist_details");
var playing = false;
var focused = false;
var keycodes = {
  49: 1,
  50: 2,
  51: 3,
  52: 4,
  53: 5
};
var songTypes = {
  "Dirty":         /^Dirty$/,
  "Clean":         /^Clean$/,
  "Intro - Dirty": /^Intro - Dirty$/,
  "Intro - Clean": /^Intro - Clean$/,
  "Main":          /^Main$/,
  "Inst":          /^Inst$/,
  "Acap":          /^Acap/
};

function rate(rating) {
    $('option[value="' + rating + '"]').attr('selected', 'selected').parent().focus();
    $submit.click();
}

function download(songType) {
  var re = new RegExp(songTypes[songType]);
  var $availFormats = $("#ad_sublisting li");
  var typeText = ".float_left";
  var dl = ".reviw_tdonw";
  var found = false;

  $availFormats.each(function(index, li) {
    if ($(this).find(typeText).text().match(re)) {
      found = true
      $(this).find(dl).children()[0].click()
    }
  })

  if (!found) {
    if ($("#removeMe").length === 0) {
      var warning = "

" + songType + " isn't an option on this song! Try manually downloading

"
      $warning.after(warning);
    } else {
      $("#removeMe").show();
    }
    setTimeout(function() {
      $("#removeMe").hide();
    }, 3000)
  }
}

function remove() {
  $removeMe.hide();
  console.log("running")
}

function playPause(e) {
  if (!playing) {
    document.getElementsByClassName("jp-play")[0].click()
    playing = true;
  } else {
    document.getElementsByClassName("jp-pause")[0].click()
    playing = false;
  }
}

function searchArtist(artist) {
  $search.val(artist);
  $searchBtn.click();
}

document.addEventListener('keydown', function(e) {
  if (!focused) {
    var keycode = keycodes[e.keyCode];
    if (keycode) {
      rate(keycode);
    }
    if (e.keyCode === 68) {
      download(downloadType);
    }
    if (e.keyCode === 80) {
      playPause();
    }
  }
}, true);

chrome.storage.local.get(["autorate", "rating", "downloadToggle", "downloadType"], function(settings) {
  autorate = settings.autorate ? true : false;

  rating = settings.rating;

  downloadToggle = settings.downloadToggle ? true : false;

  downloadType = settings.downloadType;
});

chrome.storage.onChanged.addListener(function(changes, local) {
  if (changes.autorate) {
    if (changes.autorate.newValue) {
      autorate = true;
    } else {
      autorate = false;
    }
  }

  if (changes.rating) {
    rating = changes.rating.newValue;
  }

  if (changes.downloadToggle) {
    if (changes.downloadToggle.newValue) {
      downloadToggle = true;
    } else {
      downloadToggle = false;
    }
  }

  if (changes.downloadType) {
    downloadType = changes.downloadType.newValue;
  }
});

$(function() {
function log() {
  console.log("working")
}
  if (autorate) {
    rate(rating);
  }

  $search.focus(function() {
    focused = true;
  });
  $search.blur(function() {
    focused = false;
  });

  var artistName = artist.text();
  artist.html("" + artistName + " - search for more songs by artist"  )

  $("#searchArtist").click(function(e) {
    e.preventDefault();
    console.log("artist name", artistName)
    searchArtist(artistName);
  });
});
<|endoftext|>