<commit_before>var $body = $("#inner-body"),
    Type,
    PhoneView = false,
    currentPathname = "/",
    Title = "纸异兽",
    supportHistory = !!(window.history.pushState && window.history.replaceState);

//alert(!!navigator.userAgent.match(/mobile/i));

$(function () {
    //load article list and cnblog list
    if (!$("#left-middle").html().trim()) {
        $.get("/", null, function (data) {
            var $data = $(data);

            $("#left-middle").replaceWith($data.find("#left-middle"));
            $("#right-middle").replaceWith($data.find("#right-middle"));
        }, "html");
    }
    init();
});

$("#aside").on({
    "mouseenter": function () {
        !PhoneView && $(this).removeClass("aside-close").addClass("aside-open");
        $("#aside .profile").scrollTop(0);
    },
    "mouseleave": function () {
        !PhoneView && $(this).removeClass("aside-open").addClass("aside-close");
    },
    "click": function (e) {
        var $this = $(this);

        if (PhoneView) {
            if (e.target.className == "close") {//click close button
                $this.removeClass("aside-open").addClass("aside-close");
            } else if (!$this.hasClass("aside-open")) {
                $this.removeClass("aside-close").addClass("aside-open");
                $("#aside .profile").scrollTop(0);
            }
        }
    }
});

$("#switch").on({
    "mouseenter": function () {
        !PhoneView && $(this).addClass("hover");
    },
    "mouseleave": function () {
        !PhoneView && $(this).removeClass("hover");
    },
    "click": function () {
        var $this = $(this);
        if (PhoneView) {
            $body.toggleClass("right-middle");
            $("html,body").scrollTop(0);
        } else {
            //toggle hover class, if hover exists, so that this is first click after mouseenter
            //if hover not exist, so prove that this isn't.
            $this.toggleClass("hover");

            //if isn't first click, add animation to this event
            if ($this.hasClass("hover")) {
                setTimeout(function () {
                    $body.toggleClass("right-middle");
                    $this.removeClass("hover");
                }, 500);
            } else {
                $body.toggleClass("right-middle");
            }
        }
    }
});

$(window).resize(function () {
    PhoneView = $("body").width() < 900;
    doChanged();
});

$(".content").scroll(function () {
    if (!PhoneView) return;

    if ($(this).scrollTop() >= 10) {
        $("#aside").addClass("slideUp");
    } else {
        $("#aside").removeClass("slideUp");
    }
    fixWidthOfAside();
});

function ATagClick(e, data) {
    //for normal a link
    var isPopstate = !e,
        action = isPopstate ? data.action : $(this).attr("action"),
        title = Title;

    if (!action || !supportHistory) {
        return;
    }

    if (!data && PhoneView && this.id == "readmore") {
        $("#aside").removeClass("aside-open").addClass("aside-close");
        e.stopPropagation();
    }

    e && e.preventDefault();
    var href = isPopstate ? data.href : this.href;
    currentPathname = href.replace(/^.*?\/\/.*?(?=\/)/,"");

    maskLoding.show();
    switch (Type = action) {
        case "article":
            $("#left-bottom").load(href + " #left-bottom>*", function () {
                $body.removeClass("left-top left-middle").addClass("left-bottom");

                if (PhoneView) {
                    $("html,body").scrollTop(0);
                    playMedia();
                } else {
                    $(this).scrollTop(0);
                }

                doChanged();
                setTitle(title = $("h1.post-title").text());
                !isPopstate && window.history.pushState({type: action, url: href}, title, href);
                maskLoding.hide();
                initDuoshuo();
            });
            break;
        case "tag":
            $("#left-top").load(href + " #left-top>*", function () {
                $body.removeClass("left-bottom left-middle").addClass("left-top");
                PhoneView ? $("html,body").scrollTop(0) : $(this).scrollTop(0);
                doChanged();
                setTitle(title = $("h1.page-title").text());
                !isPopstate && window.history.pushState({type: action, url: href}, title, href);
                maskLoding.hide();
            });
            break;
        case "page":
            $("#left-middle").load(href + " #left-middle>*", function () {
                $body.removeClass("left-top left-bottom");
                PhoneView ? $("html,body").scrollTop(0) : $(this).scrollTop(0);
                doChanged();
                setTitle(title);
                !isPopstate && window.history.pushState({type: action, url: href}, document.title, href);
                maskLoding.hide();
            });
            break;
    }
}

$(".contents,.profile").on("click", "a", ATagClick);

//zoom for picture
(function () {
    var $tag = $("原图"),
        img = document.createElement("img"),
        $img = $(img),
        $oriBox = $("

×

"),
        $close = $oriBox.children("#masker-close"),
    //store original image data for zoom smaller to recovery the location
        originalData = {
            ratio: 1,
            width: 0,
            height: 0,
            top: 0,
            left: 0,
            //for limit mousemove event
            isLock: true
        },
        url;

    $(".contents")
        .on("mouseenter", ".post-content img", function () {
            var $this = $(this);

            url = this.src.replace(/-resize$/, "");
            $this.parent().css("position", "relative");
            originalData.ratio = $this.width() / $this.height();

            $tag.css({
                "top": $this.position().top,
                "right": $this.parent().width() - $this.width() - $this.position().left
            });

            $this.after($tag);
        }).on("mouseleave", ".post-content img", function (e) {
            if (e.relatedTarget == $tag[0]) {
                return;
            }
            $tag.remove();
        });

    $tag[0].onclick = function () {
        var $body = $("body"),
            width,
            height;

        //do it before get width because the scroll bar
        if (PhoneView) {
            $body.css("overflow", "hidden");
        }

        width = $body.width();
        height = $body.height();
        maskLoding.show();
        $oriBox.prepend($img);
        img.src = url;

        if (originalData.ratio > width / height) {
            originalData.width = width;
            originalData.height = width / originalData.ratio;
            originalData.top = (height - originalData.height) / 2;
            originalData.left = 0;
        } else {
            originalData.width = height * originalData.ratio;
            originalData.height = height;
            originalData.top = 0;
            originalData.left = (width - originalData.width) / 2;
        }

        $img.css({
            height: originalData.height,
            width: originalData.width,
            top: originalData.top,
            left: originalData.left
        });

        //for sync showing image and masker
        //so we will get a better transition
        setTimeout(function () {
            $body.append($oriBox);
        }, 300);
    };

    //see "jquery remove() and Event" with bug tag in evernote
    img.onload = function () {
        $("#loading").fadeOut();
    };

    $close[0].onclick = function () {
        if (PhoneView) {
            $("body").css("overflow", "");
        }

        $oriBox.remove();
        maskLoding.hide();
    };

    img.onmousewheel = img.dommousescroll = debounce(mousewheel, 20);
    img.addEventListener("DOMMouseScroll", img.onmousewheel);

    function mousewheel(e) {
        var temp;
        if (!$img[0].complete) return;
        if (e.detail ? e.detail < 0 : e.wheelDelta > 0) {
            $img.css({
                "width": function (index, value) {
                    return parseInt(value) * 1.1;
                }, "height": function (index, value) {
                    return parseInt(value) * 1.1;
                }, "left": function (index, value) {
                    return parseInt(value) - (e.offsetX || e.layerX) * 0.1;
                }, "top": function (index, value) {
                    return parseInt(value) - (e.offsetY || e.layerY) * 0.1;
                }
            });
        } else {
            $img.css({
                "width": function (index, value) {
                    value = parseInt(value);
                    temp = value / 1.1;
                    return value > originalData.width && temp > originalData.width ? temp : (temp = false, originalData.width);
                }, "height": function (index, value) {
                    value = parseInt(value);
                    temp = value / 1.1;
                    return value > originalData.height && temp > originalData.height ? temp : (temp = false, originalData.height);
                }, "left": function (index, value) {
                    return temp === false ? ($oriBox.width() - originalData.width) / 2 : parseInt(value) + (e.offsetX || e.layerX) / 11;
                }, "top": function (index, value) {
                    return temp === false ? ($oriBox.height() - originalData.height) / 2 : parseInt(value) + (e.offsetY || e.layerY) / 11;
                }
            })
        }
    }

    //open the limit of draging
    img.ondragstart = function (e) {
        e.returnValue = false;
        return false;
    };
    img.onmousedown = function (e) {
        if (!img.complete) return;
        originalData.isLock = false;
        originalData.left = e.clientX;
        originalData.top = e.clientY;
    };
    img.onmousemove = debounce(function (e) {
        e.preventDefault();
        if (!img.complete || originalData.isLock) return;
        $img.css({
            left: parseInt($img.css("left")) + e.clientX - originalData.left,
            top: parseInt($img.css("top")) + e.clientY - originalData.top
        });
        originalData.left = e.clientX;
        originalData.top = e.clientY;
    }, 20);
    img.onmouseup = function () {
        originalData.isLock = true;
    };
    img.onmouseout = function (e) {
        originalData.isLock = true;
    }
})();

$("#home-btn").click(function () {
    $body.removeClass("left-bottom left-top right-middle").addClass("left-middle");
    currentPathname = "/";

    setTitle(Title);
    window.history.pushState({type: Type = "page", url: "/"}, Title, "/");
    doChanged();
    stopMedia();
});

$(window).on("popstate", function (e) {
    var state = e.originalEvent.state;

    switch (Type = state.type) {
        case "article":
            $body.removeClass("left-top left-middle").addClass("left-bottom");
            ATagClick(undefined, {action: Type, href: state.url});
            break;
        case "tag":
            $body.removeClass("left-bottom left-middle").addClass("left-top");
            break;
        case "page":
            if (currentPathname.search(/^\/$|\/page\//) != 0) {
                $body.removeClass("left-bottom left-top").addClass("left-middle");
                break;
            }
            ATagClick(undefined, {action: Type, href: state.url});
    }
    doChanged();
    stopMedia();
});

function setTitle(t) {
    $("title").text(t)
}

var masker = (function (noTransition) {
    var $mask = $("#mask-layer");

    return {
        show: function () {
            if (noTransition) {
                $mask.show();
            } else {
                $mask.fadeIn();
            }
        },
        hide: function () {
            if (noTransition) {
                $mask.hide();
            } else {
                $mask.fadeOut();
            }
        }
    }
})();

var maskLoding = (function (noTransition) {
    var $loading = $("#loading");

    return {
        show: function () {
            $loading.fadeIn();
            masker.show(noTransition);
        },
        hide: function () {
            $loading.fadeOut();
            masker.hide(noTransition);
        }
    }
})();

function init() {
    PhoneView = $("body").width() < 900;

    var path = window.location.pathname;

    if (path == "/") {
        Type = "page";
    } else if (path.indexOf("/tag/") == 0) {
        Type = "tag";
    } else if (path.indexOf("/page/") == 0) {
        Type = "page";
    } else {
        Type = "article";
        playMedia();
        initDuoshuo();
    }

    currentPathname = window.location.pathname;
    //remove aside-init when the DOM is ready, so we can show a perfect page even the index.js is not loaded
    $("#aside").removeClass("aside-init");
    doChanged();
    window.history.replaceState({type: Type, url: window.location.href}, document.title, window.location.href);
}

function doChanged() {
    fixWidthOfAside();
}

function stopMedia() {
    $("#left-bottom").find("audio,video").each(function () {
        this.pause();
    })
}

function playMedia() {
    $("#left-bottom").find("audio[autoplay=autoplay]").each(function () {
        setTimeout(function () {
            this.play();
        }, 1000)
    })
}

var fixWidthOfAside = (function () {
    /**
     * 修正浏览器bug,test.html中测试
     * 如果子节点为img，其宽度auto，高度根据父节点高度百分比变化
     * 父节点宽度为auto，在resize之后，父节点不会发生宽度变化，在所有浏览器中表现都不一样
     * 只能用js去纠正
     */
    var isPhoneViewBefore = $("body").width() < 900,
        $aside = $("#aside"),
        $title = $aside.find(".title"),
        $container = $("#container"),
        width;

    return function () {
        //do the change when convert layout
        if (isPhoneViewBefore && !PhoneView) {
            //phone to pc
            $body.css("padding-top", "");
            isPhoneViewBefore = PhoneView;
        } else if (!isPhoneViewBefore && PhoneView) {
            //pc to phone
            $body.css("padding-left", "");
            $title.width("auto");
            isPhoneViewBefore = PhoneView;
        }

        if (PhoneView) {
            if (Type == "article") {
                return $body.css("padding-top", 0)
            }
            ;
            $body.css("padding-top", $aside.hasClass("slideUp") ? 0 : $title.height());
        } else {
            if ($body.hasClass("left-bottom")) {
                $body.css("padding-left", 0);
            } else {
                width = $body.height() * 0.3;
                $title.width(width);
                $body.css("padding-left", width);
            }
        }
    }
})();

function debounce(func, delay) {
    var lasttime = new Date();

    return function () {
        var now = new Date();
        if (now - lasttime > delay) {
            lasttime = now;
            func.apply(this, arguments);
        }

    }
}

//duoshuo
function initDuoshuo() {
    var ds = document.getElementById("ds-thread");
    DUOSHUO.EmbedThread(ds);
}<commit_msg>debug duplicate code<commit_after>var $body = $("#inner-body"),
    Type,
    PhoneView = false,
    currentPathname = "/",
    Title = "纸异兽",
    supportHistory = !!(window.history.pushState && window.history.replaceState);

//alert(!!navigator.userAgent.match(/mobile/i));

$(function () {
    //load article list and cnblog list
    if (!$("#left-middle").html().trim()) {
        $.get("/", null, function (data) {
            var $data = $(data);

            $("#left-middle").replaceWith($data.find("#left-middle"));
            $("#right-middle").replaceWith($data.find("#right-middle"));
        }, "html");
    }
    init();
});

$("#aside").on({
    "mouseenter": function () {
        !PhoneView && $(this).removeClass("aside-close").addClass("aside-open");
        $("#aside .profile").scrollTop(0);
    },
    "mouseleave": function () {
        !PhoneView && $(this).removeClass("aside-open").addClass("aside-close");
    },
    "click": function (e) {
        var $this = $(this);

        if (PhoneView) {
            if (e.target.className == "close") {//click close button
                $this.removeClass("aside-open").addClass("aside-close");
            } else if (!$this.hasClass("aside-open")) {
                $this.removeClass("aside-close").addClass("aside-open");
                $("#aside .profile").scrollTop(0);
            }
        }
    }
});

$("#switch").on({
    "mouseenter": function () {
        !PhoneView && $(this).addClass("hover");
    },
    "mouseleave": function () {
        !PhoneView && $(this).removeClass("hover");
    },
    "click": function () {
        var $this = $(this);
        if (PhoneView) {
            $body.toggleClass("right-middle");
            $("html,body").scrollTop(0);
        } else {
            //toggle hover class, if hover exists, so that this is first click after mouseenter
            //if hover not exist, so prove that this isn't.
            $this.toggleClass("hover");

            //if isn't first click, add animation to this event
            if ($this.hasClass("hover")) {
                setTimeout(function () {
                    $body.toggleClass("right-middle");
                    $this.removeClass("hover");
                }, 500);
            } else {
                $body.toggleClass("right-middle");
            }
        }
    }
});

$(window).resize(function () {
    PhoneView = $("body").width() < 900;
    doChanged();
});

$(".content").scroll(function () {
    if (!PhoneView) return;

    if ($(this).scrollTop() >= 10) {
        $("#aside").addClass("slideUp");
    } else {
        $("#aside").removeClass("slideUp");
    }
    fixWidthOfAside();
});

function ATagClick(e, data) {
    //for normal a link
    var isPopstate = !e,
        action = isPopstate ? data.action : $(this).attr("action"),
        title = Title;

    if (!action || !supportHistory) {
        return;
    }

    if (!data && PhoneView && this.id == "readmore") {
        $("#aside").removeClass("aside-open").addClass("aside-close");
        e.stopPropagation();
    }

    e && e.preventDefault();
    var href = isPopstate ? data.href : this.href;
    currentPathname = href.replace(/^.*?\/\/.*?(?=\/)/,"");

    maskLoding.show();
    switch (Type = action) {
        case "article":
            $("#left-bottom").load(href + " #left-bottom>*", function () {
                $body.removeClass("left-top left-middle").addClass("left-bottom");

                if (PhoneView) {
                    $("html,body").scrollTop(0);
                    playMedia();
                } else {
                    $(this).scrollTop(0);
                }

                doChanged();
                setTitle(title = $("h1.post-title").text());
                !isPopstate && window.history.pushState({type: action, url: href}, title, href);
                maskLoding.hide();
                initDuoshuo();
            });
            break;
        case "tag":
            $("#left-top").load(href + " #left-top>*", function () {
                $body.removeClass("left-bottom left-middle").addClass("left-top");
                PhoneView ? $("html,body").scrollTop(0) : $(this).scrollTop(0);
                doChanged();
                setTitle(title = $("h1.page-title").text());
                !isPopstate && window.history.pushState({type: action, url: href}, title, href);
                maskLoding.hide();
            });
            break;
        case "page":
            $("#left-middle").load(href + " #left-middle>*", function () {
                $body.removeClass("left-top left-bottom");
                PhoneView ? $("html,body").scrollTop(0) : $(this).scrollTop(0);
                doChanged();
                setTitle(title);
                !isPopstate && window.history.pushState({type: action, url: href}, document.title, href);
                maskLoding.hide();
            });
            break;
    }
}

$(".contents,.profile").on("click", "a", ATagClick);

//zoom for picture
(function () {
    var $tag = $("原图"),
        img = document.createElement("img"),
        $img = $(img),
        $oriBox = $("

×

"),
        $close = $oriBox.children("#masker-close"),
    //store original image data for zoom smaller to recovery the location
        originalData = {
            ratio: 1,
            width: 0,
            height: 0,
            top: 0,
            left: 0,
            //for limit mousemove event
            isLock: true
        },
        url;

    $(".contents")
        .on("mouseenter", ".post-content img", function () {
            var $this = $(this);

            url = this.src.replace(/-resize$/, "");
            $this.parent().css("position", "relative");
            originalData.ratio = $this.width() / $this.height();

            $tag.css({
                "top": $this.position().top,
                "right": $this.parent().width() - $this.width() - $this.position().left
            });

            $this.after($tag);
        }).on("mouseleave", ".post-content img", function (e) {
            if (e.relatedTarget == $tag[0]) {
                return;
            }
            $tag.remove();
        });

    $tag[0].onclick = function () {
        var $body = $("body"),
            width,
            height;

        //do it before get width because the scroll bar
        if (PhoneView) {
            $body.css("overflow", "hidden");
        }

        width = $body.width();
        height = $body.height();
        maskLoding.show();
        $oriBox.prepend($img);
        img.src = url;

        if (originalData.ratio > width / height) {
            originalData.width = width;
            originalData.height = width / originalData.ratio;
            originalData.top = (height - originalData.height) / 2;
            originalData.left = 0;
        } else {
            originalData.width = height * originalData.ratio;
            originalData.height = height;
            originalData.top = 0;
            originalData.left = (width - originalData.width) / 2;
        }

        $img.css({
            height: originalData.height,
            width: originalData.width,
            top: originalData.top,
            left: originalData.left
        });

        //for sync showing image and masker
        //so we will get a better transition
        setTimeout(function () {
            $body.append($oriBox);
        }, 300);
    };

    //see "jquery remove() and Event" with bug tag in evernote
    img.onload = function () {
        $("#loading").fadeOut();
    };

    $close[0].onclick = function () {
        if (PhoneView) {
            $("body").css("overflow", "");
        }

        $oriBox.remove();
        maskLoding.hide();
    };

    img.onmousewheel = img.dommousescroll = debounce(mousewheel, 20);
    img.addEventListener("DOMMouseScroll", img.onmousewheel);

    function mousewheel(e) {
        var temp;
        if (!$img[0].complete) return;
        if (e.detail ? e.detail < 0 : e.wheelDelta > 0) {
            $img.css({
                "width": function (index, value) {
                    return parseInt(value) * 1.1;
                }, "height": function (index, value) {
                    return parseInt(value) * 1.1;
                }, "left": function (index, value) {
                    return parseInt(value) - (e.offsetX || e.layerX) * 0.1;
                }, "top": function (index, value) {
                    return parseInt(value) - (e.offsetY || e.layerY) * 0.1;
                }
            });
        } else {
            $img.css({
                "width": function (index, value) {
                    value = parseInt(value);
                    temp = value / 1.1;
                    return value > originalData.width && temp > originalData.width ? temp : (temp = false, originalData.width);
                }, "height": function (index, value) {
                    value = parseInt(value);
                    temp = value / 1.1;
                    return value > originalData.height && temp > originalData.height ? temp : (temp = false, originalData.height);
                }, "left": function (index, value) {
                    return temp === false ? ($oriBox.width() - originalData.width) / 2 : parseInt(value) + (e.offsetX || e.layerX) / 11;
                }, "top": function (index, value) {
                    return temp === false ? ($oriBox.height() - originalData.height) / 2 : parseInt(value) + (e.offsetY || e.layerY) / 11;
                }
            })
        }
    }

    //open the limit of draging
    img.ondragstart = function (e) {
        e.returnValue = false;
        return false;
    };
    img.onmousedown = function (e) {
        if (!img.complete) return;
        originalData.isLock = false;
        originalData.left = e.clientX;
        originalData.top = e.clientY;
    };
    img.onmousemove = debounce(function (e) {
        e.preventDefault();
        if (!img.complete || originalData.isLock) return;
        $img.css({
            left: parseInt($img.css("left")) + e.clientX - originalData.left,
            top: parseInt($img.css("top")) + e.clientY - originalData.top
        });
        originalData.left = e.clientX;
        originalData.top = e.clientY;
    }, 20);
    img.onmouseup = function () {
        originalData.isLock = true;
    };
    img.onmouseout = function (e) {
        originalData.isLock = true;
    }
})();

$("#home-btn").click(function () {
    $body.removeClass("left-bottom left-top right-middle").addClass("left-middle");
    currentPathname = "/";

    setTitle(Title);
    window.history.pushState({type: Type = "page", url: "/"}, Title, "/");
    doChanged();
    stopMedia();
});

$(window).on("popstate", function (e) {
    var state = e.originalEvent.state;

    switch (Type = state.type) {
        case "article":
            ATagClick(undefined, {action: Type, href: state.url});
            break;
        case "tag":
            $body.removeClass("left-bottom left-middle").addClass("left-top");
            break;
        case "page":
            if (currentPathname.search(/^\/$|\/page\//) != 0) {
                $body.removeClass("left-bottom left-top").addClass("left-middle");
                break;
            }
            ATagClick(undefined, {action: Type, href: state.url});
    }
    doChanged();
    stopMedia();
});

function setTitle(t) {
    $("title").text(t)
}

var masker = (function (noTransition) {
    var $mask = $("#mask-layer");

    return {
        show: function () {
            if (noTransition) {
                $mask.show();
            } else {
                $mask.fadeIn();
            }
        },
        hide: function () {
            if (noTransition) {
                $mask.hide();
            } else {
                $mask.fadeOut();
            }
        }
    }
})();

var maskLoding = (function (noTransition) {
    var $loading = $("#loading");

    return {
        show: function () {
            $loading.fadeIn();
            masker.show(noTransition);
        },
        hide: function () {
            $loading.fadeOut();
            masker.hide(noTransition);
        }
    }
})();

function init() {
    PhoneView = $("body").width() < 900;

    var path = window.location.pathname;

    if (path == "/") {
        Type = "page";
    } else if (path.indexOf("/tag/") == 0) {
        Type = "tag";
    } else if (path.indexOf("/page/") == 0) {
        Type = "page";
    } else {
        Type = "article";
        playMedia();
        initDuoshuo();
    }

    currentPathname = window.location.pathname;
    //remove aside-init when the DOM is ready, so we can show a perfect page even the index.js is not loaded
    $("#aside").removeClass("aside-init");
    doChanged();
    window.history.replaceState({type: Type, url: window.location.href}, document.title, window.location.href);
}

function doChanged() {
    fixWidthOfAside();
}

function stopMedia() {
    $("#left-bottom").find("audio,video").each(function () {
        this.pause();
    })
}

function playMedia() {
    $("#left-bottom").find("audio[autoplay=autoplay]").each(function () {
        setTimeout(function () {
            this.play();
        }, 1000)
    })
}

var fixWidthOfAside = (function () {
    /**
     * 修正浏览器bug,test.html中测试
     * 如果子节点为img，其宽度auto，高度根据父节点高度百分比变化
     * 父节点宽度为auto，在resize之后，父节点不会发生宽度变化，在所有浏览器中表现都不一样
     * 只能用js去纠正
     */
    var isPhoneViewBefore = $("body").width() < 900,
        $aside = $("#aside"),
        $title = $aside.find(".title"),
        $container = $("#container"),
        width;

    return function () {
        //do the change when convert layout
        if (isPhoneViewBefore && !PhoneView) {
            //phone to pc
            $body.css("padding-top", "");
            isPhoneViewBefore = PhoneView;
        } else if (!isPhoneViewBefore && PhoneView) {
            //pc to phone
            $body.css("padding-left", "");
            $title.width("auto");
            isPhoneViewBefore = PhoneView;
        }

        if (PhoneView) {
            if (Type == "article") {
                return $body.css("padding-top", 0)
            }
            ;
            $body.css("padding-top", $aside.hasClass("slideUp") ? 0 : $title.height());
        } else {
            if ($body.hasClass("left-bottom")) {
                $body.css("padding-left", 0);
            } else {
                width = $body.height() * 0.3;
                $title.width(width);
                $body.css("padding-left", width);
            }
        }
    }
})();

function debounce(func, delay) {
    var lasttime = new Date();

    return function () {
        var now = new Date();
        if (now - lasttime > delay) {
            lasttime = now;
            func.apply(this, arguments);
        }

    }
}

//duoshuo
function initDuoshuo() {
    var ds = document.getElementById("ds-thread");
    DUOSHUO.EmbedThread(ds);
}<|endoftext|>


<commit_before>#!/usr/bin/env node
// Simple CLI for KaTeX.
// Reads TeX from stdin, outputs HTML to stdout.
// To run this from the repository, you must first build KaTeX by running
// `yarn` and `yarn build`.

/* eslint no-console:0 */

let katex;
try {
    katex = require("./");
} catch (e) {
    console.error(
        "KaTeX could not import, likely because dist/katex.js is missing.");
    console.error("Please run 'yarn' and 'yarn build' before running");
    console.error("cli.js from the KaTeX repository.");
    console.error();
    throw e;
}
const {version} = require("./package.json");
const fs = require("fs");

const program = require("commander")
    .version(version)
    .option("-d, --display-mode",
        "Render math in display mode, which puts the math in display style " +
        "(so \\int and \\sum are large, for example), and centers the math " +
        "on the page on its own line.")
    .option("--leqno",
        "Render display math in leqno style (left-justified tags).")
    .option("--fleqn",
        "Render display math flush left.")
    .option("-t, --no-throw-on-error",
        "Render errors (in the color given by --error-color) instead of " +
        "throwing a ParseError exception when encountering an error.")
    .option("-c, --error-color <color>",
        "A color string given in the format 'rgb' or 'rrggbb' (no #). " +
        "This option determines the color of errors rendered by the -t option.",
        "#cc0000",
        (color) => "#" + color)
    .option("-b, --color-is-text-color",
        "Makes \\color behave like LaTeX's 2-argument \\textcolor, " +
        "instead of LaTeX's one-argument \\color mode change.")
    .option("-S, --strict",
        "Turn on strict / LaTeX faithfulness mode, which throws an error " +
        "if the input uses features that are not supported by LaTeX")
    .option("-s, --max-size <n>",
        "If non-zero, all user-specified sizes, e.g. in " +
        "\\rule{500em}{500em}, will be capped to maxSize ems. " +
        "Otherwise, elements and spaces can be arbitrarily large",
        Infinity, parseInt)
    .option("-e, --max-expand <n>",
        "Limit the number of macro expansions to the specified number, to " +
        "prevent e.g. infinite macro loops.  If set to Infinity, the macro " +
        "expander will try to fully expand as in LaTeX.",
        (n) => (n === "Infinity" ? Infinity : parseInt(n)))
    .option("-m, --macro <def>",
        "Define custom macro of the form '\\foo:expansion' (use multiple -m " +
        "arguments for multiple macros).",
        (def, defs) => {
            defs.push(def);
            return defs;
        }, [])
    .option("-f, --macro-file <path>",
        "Read macro definitions, one per line, from the given file.")
    .option("-i, --input <path>", "Read LaTeX input from the given file.")
    .option("-o, --output <path>", "Write html output to the given file.");

if (require.main !== module) {
    module.exports = program;
    return;
}

const options = program.parse(process.argv);

function readMacros() {
    if (options.macroFile) {
        fs.readFile(options.macroFile, "utf-8", function(err, data) {
            if (err) {throw err;}
            splitMacros(data.toString().split('\n'));
        });
    } else {
        splitMacros([]);
    }
}

function splitMacros(macroStrings) {
    // Override macros from macro file (if any)
    // with macros from command line (if any)
    macroStrings = macroStrings.concat(options.macro);

    const macros = {};

    for (const m of macroStrings) {
        const i = m.search(":");
        if (i !== -1) {
            macros[m.substring(0, i).trim()] = m.substring(i + 1).trim();
        }
    }

    options.macros = macros;
    readInput();
}

function readInput() {
    let input = "";

    if (options.input) {
        fs.readFile(options.input, "utf-8", function(err, data) {
            if (err) {throw err;}
            input = data.toString();
            writeOutput(input);
        });
    } else {
        process.stdin.on("data", function(chunk) {
            input += chunk.toString();
        });

        process.stdin.on("end", function() {
            writeOutput(input);
        });
    }
}

function writeOutput(input) {
    const output = katex.renderToString(input, options) + "\n";

    if (options.output) {
        fs.writeFile(options.output, output, function(err) {
            if (err) {
                return console.log(err);
            }
        });
    } else {
        console.log(output);
    }
}

readMacros();
<commit_msg>feat(cli): --trust flag for trusting cli input (#3339)<commit_after>#!/usr/bin/env node
// Simple CLI for KaTeX.
// Reads TeX from stdin, outputs HTML to stdout.
// To run this from the repository, you must first build KaTeX by running
// `yarn` and `yarn build`.

/* eslint no-console:0 */

let katex;
try {
    katex = require("./");
} catch (e) {
    console.error(
        "KaTeX could not import, likely because dist/katex.js is missing.");
    console.error("Please run 'yarn' and 'yarn build' before running");
    console.error("cli.js from the KaTeX repository.");
    console.error();
    throw e;
}
const {version} = require("./package.json");
const fs = require("fs");

const program = require("commander")
    .version(version)
    .option("-d, --display-mode",
        "Render math in display mode, which puts the math in display style " +
        "(so \\int and \\sum are large, for example), and centers the math " +
        "on the page on its own line.")
    .option("--leqno",
        "Render display math in leqno style (left-justified tags).")
    .option("--fleqn",
        "Render display math flush left.")
    .option("-t, --no-throw-on-error",
        "Render errors (in the color given by --error-color) instead of " +
        "throwing a ParseError exception when encountering an error.")
    .option("-c, --error-color <color>",
        "A color string given in the format 'rgb' or 'rrggbb' (no #). " +
        "This option determines the color of errors rendered by the -t option.",
        "#cc0000",
        (color) => "#" + color)
    .option("-b, --color-is-text-color",
        "Makes \\color behave like LaTeX's 2-argument \\textcolor, " +
        "instead of LaTeX's one-argument \\color mode change.")
    .option("-S, --strict",
        "Turn on strict / LaTeX faithfulness mode, which throws an error " +
        "if the input uses features that are not supported by LaTeX.")
    .option("-s, --max-size <n>",
        "If non-zero, all user-specified sizes, e.g. in " +
        "\\rule{500em}{500em}, will be capped to maxSize ems. " +
        "Otherwise, elements and spaces can be arbitrarily large",
        Infinity, parseInt)
    .option("-e, --max-expand <n>",
        "Limit the number of macro expansions to the specified number, to " +
        "prevent e.g. infinite macro loops.  If set to Infinity, the macro " +
        "expander will try to fully expand as in LaTeX.",
        (n) => (n === "Infinity" ? Infinity : parseInt(n)))
    .option("-m, --macro <def>",
        "Define custom macro of the form '\\foo:expansion' (use multiple -m " +
        "arguments for multiple macros).",
        (def, defs) => {
            defs.push(def);
            return defs;
        }, [])
    .option("-f, --macro-file <path>",
        "Read macro definitions, one per line, from the given file.")
    .option("-T, --trust",
        "Trust the input, enabling all HTML features such as \\url.")
    .option("-i, --input <path>", "Read LaTeX input from the given file.")
    .option("-o, --output <path>", "Write html output to the given file.");

if (require.main !== module) {
    module.exports = program;
    return;
}

const options = program.parse(process.argv);

function readMacros() {
    if (options.macroFile) {
        fs.readFile(options.macroFile, "utf-8", function(err, data) {
            if (err) {throw err;}
            splitMacros(data.toString().split('\n'));
        });
    } else {
        splitMacros([]);
    }
}

function splitMacros(macroStrings) {
    // Override macros from macro file (if any)
    // with macros from command line (if any)
    macroStrings = macroStrings.concat(options.macro);

    const macros = {};

    for (const m of macroStrings) {
        const i = m.search(":");
        if (i !== -1) {
            macros[m.substring(0, i).trim()] = m.substring(i + 1).trim();
        }
    }

    options.macros = macros;
    readInput();
}

function readInput() {
    let input = "";

    if (options.input) {
        fs.readFile(options.input, "utf-8", function(err, data) {
            if (err) {throw err;}
            input = data.toString();
            writeOutput(input);
        });
    } else {
        process.stdin.on("data", function(chunk) {
            input += chunk.toString();
        });

        process.stdin.on("end", function() {
            writeOutput(input);
        });
    }
}

function writeOutput(input) {
    const output = katex.renderToString(input, options) + "\n";

    if (options.output) {
        fs.writeFile(options.output, output, function(err) {
            if (err) {
                return console.log(err);
            }
        });
    } else {
        console.log(output);
    }
}

readMacros();
<|endoftext|>


<commit_before>/**
 * Main JS file for Casper behaviours
 */

/* globals jQuery, document */
(function ($, sr, undefined) {
    "use strict";

    var $document = $(document),

        // debouncing function from John Hann
        // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
        debounce = function (func, threshold, execAsap) {
            var timeout;

            return function debounced () {
                var obj = this, args = arguments;
                function delayed () {
                    if (!execAsap) {
                        func.apply(obj, args);
                    }
                    timeout = null;
                }

                if (timeout) {
                    clearTimeout(timeout);
                } else if (execAsap) {
                    func.apply(obj, args);
                }

                timeout = setTimeout(delayed, threshold || 100);
            };
        };

    $document.ready(function () {

        var $postContent = $(".post-content");
        $postContent.fitVids();

        function updateImageWidth() {
            var $this = $(this),
                contentWidth = $postContent.outerWidth(), // Width of the content
                imageWidth = this.naturalWidth; // Original image resolution

            if (imageWidth >= contentWidth) {
                $this.addClass('full-img');
            } else {
                $this.removeClass('full-img');
            }
        }

        var $img = $("img").on('load', updateImageWidth);
        function casperFullImg() {
            $img.each(updateImageWidth);
        }

        casperFullImg();
        $(window).smartresize(casperFullImg);

        $(".scroll-down").arctic_scroll();

        $("#header").click(function(){
           // navigate to new page
            window.location.href = 'http://www.moldedbits.com';
        });

    });

    // smartresize
    jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

    // Arctic Scroll by Paul Adam Davis
    // https://github.com/PaulAdamDavis/Arctic-Scroll
    $.fn.arctic_scroll = function (options) {

        var defaults = {
            elem: $(this),
            speed: 500
        },

        allOptions = $.extend(defaults, options);

        allOptions.elem.click(function (event) {
            event.preventDefault();
            var $this = $(this),
                $htmlBody = $('html, body'),
                offset = ($this.attr('data-offset')) ? $this.attr('data-offset') : false,
                position = ($this.attr('data-position')) ? $this.attr('data-position') : false,
                toMove;

            if (offset) {
                toMove = parseInt(offset);
                $htmlBody.stop(true, false).animate({scrollTop: ($(this.hash).offset().top + toMove) }, allOptions.speed);
            } else if (position) {
                toMove = parseInt(position);
                $htmlBody.stop(true, false).animate({scrollTop: toMove }, allOptions.speed);
            } else {
                $htmlBody.stop(true, false).animate({scrollTop: ($(this.hash).offset().top) }, allOptions.speed);
            }
        });

    };
})(jQuery, 'smartresize');
<commit_msg>+ website link was non-functional<commit_after>/**
 * Main JS file for Casper behaviours
 */

/* globals jQuery, document */
(function ($, sr, undefined) {
    "use strict";

    var $document = $(document),

        // debouncing function from John Hann
        // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
        debounce = function (func, threshold, execAsap) {
            var timeout;

            return function debounced () {
                var obj = this, args = arguments;
                function delayed () {
                    if (!execAsap) {
                        func.apply(obj, args);
                    }
                    timeout = null;
                }

                if (timeout) {
                    clearTimeout(timeout);
                } else if (execAsap) {
                    func.apply(obj, args);
                }

                timeout = setTimeout(delayed, threshold || 100);
            };
        };

    $document.ready(function () {

        var $postContent = $(".post-content");
        $postContent.fitVids();

        function updateImageWidth() {
            var $this = $(this),
                contentWidth = $postContent.outerWidth(), // Width of the content
                imageWidth = this.naturalWidth; // Original image resolution

            if (imageWidth >= contentWidth) {
                $this.addClass('full-img');
            } else {
                $this.removeClass('full-img');
            }
        }

        var $img = $("img").on('load', updateImageWidth);
        function casperFullImg() {
            $img.each(updateImageWidth);
        }

        casperFullImg();
        $(window).smartresize(casperFullImg);

        $(".scroll-down").arctic_scroll();

        $("#header").click(function(){
           // navigate to new page
            window.location.href = 'http://moldedbits.com';
        });

    });

    // smartresize
    jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

    // Arctic Scroll by Paul Adam Davis
    // https://github.com/PaulAdamDavis/Arctic-Scroll
    $.fn.arctic_scroll = function (options) {

        var defaults = {
            elem: $(this),
            speed: 500
        },

        allOptions = $.extend(defaults, options);

        allOptions.elem.click(function (event) {
            event.preventDefault();
            var $this = $(this),
                $htmlBody = $('html, body'),
                offset = ($this.attr('data-offset')) ? $this.attr('data-offset') : false,
                position = ($this.attr('data-position')) ? $this.attr('data-position') : false,
                toMove;

            if (offset) {
                toMove = parseInt(offset);
                $htmlBody.stop(true, false).animate({scrollTop: ($(this.hash).offset().top + toMove) }, allOptions.speed);
            } else if (position) {
                toMove = parseInt(position);
                $htmlBody.stop(true, false).animate({scrollTop: toMove }, allOptions.speed);
            } else {
                $htmlBody.stop(true, false).animate({scrollTop: ($(this.hash).offset().top) }, allOptions.speed);
            }
        });

    };
})(jQuery, 'smartresize');
<|endoftext|>


<commit_before><commit_msg>chore: keep linter happy<commit_after><|endoftext|>


<commit_before>#!/usr/bin/env node

var path = require('path');
var locales = require('./');
var argv = process.argv;

locales(
  path.join(__dirname, argv[2]),
  path.join(__dirname, argv[3])
);
<commit_msg>path fix<commit_after>#!/usr/bin/env node

var locales = require('./');
var argv = process.argv;
locales(argv[2], argv[3]);
<|endoftext|>


<commit_before>#! /usr/bin/env node

"use strict";

var util = require("util");
var vm = require("vm");
var fs = require("fs");
var chalk = require('chalk');
var spider = require("./lib/spider");

var opts = require("nomnom")
  .option("files", {
    position: 0,
    help: "files to compile",
    list: true
  })
  .option("compile", {
    abbr: "c",
    flag: true,
    help: "compile to JavaScript and save as .js files"
  })
  .option("verbose", {
    abbr: "v",
    flag: true,
    help: "verbose mode"
  })
  .option("version", {
    flag: true,
    help: "display the version number",
    callback: function () {
      return "version " + require("./package.json").version;
    }
  })
  .parse();

var context;

if (!opts.compile) {
  context = vm.createContext(global);
}

function generateSpace(len) {
  var chars = [];
  for (var i = 0; i < len; i++) {
    chars.push(' ');
  }

  return chars.join('');
}

function generateErrorColumnString(errorStartIndex, errorEndIndex) {
  var chars = [];
  var i = 0;

  if (!errorEndIndex) {
    errorEndIndex = errorStartIndex;
  }

  for (; i < errorStartIndex; i++) {
    chars.push(' ');
  }

  for (i = errorStartIndex; i <= errorEndIndex; i++) {
    chars.push('^');
  }

  return chars.join('');
}

var problems = 0;

opts.files.forEach(function (fileName, fileIndex) {
  fs.readFile(fileName, "utf-8", function (error, content) {
    if (error) {
      return console.log(error);
    }

    var errors = [];
    var js = spider.compile(content, opts.verbose, errors);

    if (errors.length > 0) {
      var output = [];

      var maxCol = 0;
      var maxLine = 0;

      output.push(chalk.white(fileName), "\n");

      var lines = content.split("\n");
      var tabCharacter = "__SPIDER_TAB";
      errors.forEach(function (error, errorIndex) {
        var line = error.loc.start.line;
        var column = error.loc.start.column + 1;

        var lineCharCount = line.toString().length;
        var columnCharCount = column.toString().length;

        maxCol = Math.max(maxCol, columnCharCount);
        maxLine = Math.max(maxCol, lineCharCount);

        output.push(tabCharacter);
        output.push(chalk.gray("line", line));
        output.push(tabCharacter, lineCharCount);
        output.push(chalk.gray("col", column));
        output.push(tabCharacter, columnCharCount);

        output.push(chalk.red(error.message), "\n");

        if (error.loc && error.loc.start) {
          var start = error.loc.start;
          var end = error.loc.end;

          if (start.line > 0 && start.line <= lines.length) {
            output.push(tabCharacter, tabCharacter, tabCharacter);

            output.push(chalk.green(lines[start.line - 1].replace(/(\r\n|\n|\r)/gm, ""),
              "\n", tabCharacter, tabCharacter, tabCharacter));
            output.push(chalk.red(generateErrorColumnString(start.column, end ? end.column - 1 : 0)));
          }
        }

        output.push("\n");
        problems++;

        if (problems > 0 &&
            fileIndex === opts.files.length - 1
            && errorIndex === errors.length - 1)  {
          output.push(chalk.red(problems + (problems === 1 ? " problem" : " problems")), "\n");
        }
      });

      var str = output.join("");
      var tabLength = Math.max(maxLine, maxCol);

      for (var i = 1; i <= tabLength; i++) {
        var regex = new RegExp(tabCharacter + i, "g");
        str = str.replace(regex, generateSpace(Math.max(2 + tabLength - i, 2)));
      }

      util.print(str.replace(new RegExp(tabCharacter, "g"), generateSpace(2)));
    } else {
      if (opts.compile) {
        var outFileName = fileName.substring(0,
          fileName.lastIndexOf('.')) + ".js";
        fs.writeFile(outFileName, js, function (error) {
          if (error) {
            return console.log(error);
          }
        });
      } else {
        vm.runInContext(js, context);
      }
    }
  });
});<commit_msg>fix spacing in error highlight<commit_after>#! /usr/bin/env node

"use strict";

var util = require("util");
var vm = require("vm");
var fs = require("fs");
var chalk = require('chalk');
var spider = require("./lib/spider");

var opts = require("nomnom")
  .option("files", {
    position: 0,
    help: "files to compile",
    list: true
  })
  .option("compile", {
    abbr: "c",
    flag: true,
    help: "compile to JavaScript and save as .js files"
  })
  .option("verbose", {
    abbr: "v",
    flag: true,
    help: "verbose mode"
  })
  .option("version", {
    flag: true,
    help: "display the version number",
    callback: function () {
      return "version " + require("./package.json").version;
    }
  })
  .parse();

var context;

if (!opts.compile) {
  context = vm.createContext(global);
}

function generateSpace(len) {
  var chars = [];
  for (var i = 0; i < len; i++) {
    chars.push(' ');
  }

  return chars.join('');
}

function generateErrorColumnString(errorStartIndex, errorEndIndex) {
  var chars = [];
  var i = 0;

  if (!errorEndIndex) {
    errorEndIndex = errorStartIndex;
  }

  for (; i < errorStartIndex; i++) {
    chars.push(' ');
  }

  for (i = errorStartIndex; i <= errorEndIndex; i++) {
    chars.push('^');
  }

  return chars.join('');
}

var problems = 0;

opts.files.forEach(function (fileName, fileIndex) {
  fs.readFile(fileName, "utf-8", function (error, content) {
    if (error) {
      return console.log(error);
    }

    var errors = [];
    var js = spider.compile(content, opts.verbose, errors);

    if (errors.length > 0) {
      var output = [];

      var maxCol = 0;
      var maxLine = 0;

      output.push(chalk.white(fileName), "\n");

      var lines = content.split("\n");
      var tabCharacter = "__SPIDER_TAB";
      errors.forEach(function (error, errorIndex) {
        var line = error.loc.start.line;
        var column = error.loc.start.column + 1;

        var lineCharCount = line.toString().length;
        var columnCharCount = column.toString().length;

        maxCol = Math.max(maxCol, columnCharCount);
        maxLine = Math.max(maxCol, lineCharCount);

        output.push(tabCharacter);
        output.push(chalk.gray("line", line));
        output.push(tabCharacter, lineCharCount);
        output.push(chalk.gray("col", column));
        output.push(tabCharacter, columnCharCount);

        output.push(chalk.red(error.message), "\n");

        if (error.loc && error.loc.start) {
          var start = error.loc.start;
          var end = error.loc.end;

          if (start.line > 0 && start.line <= lines.length) {
            output.push(tabCharacter, tabCharacter, tabCharacter);

            output.push(chalk.green(lines[start.line - 1].replace(/(\r\n|\n|\r)/gm, ""),
              "\n", tabCharacter, tabCharacter));
            output.push(chalk.red(generateErrorColumnString(start.column, end ? end.column - 1 : 0)));
          }
        }

        output.push("\n");
        problems++;

        if (problems > 0 &&
            fileIndex === opts.files.length - 1
            && errorIndex === errors.length - 1)  {
          output.push(chalk.red(problems + (problems === 1 ? " problem" : " problems")), "\n");
        }
      });

      var str = output.join("");
      var tabLength = Math.max(maxLine, maxCol);

      for (var i = 1; i <= tabLength; i++) {
        var regex = new RegExp(tabCharacter + i, "g");
        str = str.replace(regex, generateSpace(Math.max(2 + tabLength - i, 2)));
      }

      util.print(str.replace(new RegExp(tabCharacter, "g"), generateSpace(2)));
    } else {
      if (opts.compile) {
        var outFileName = fileName.substring(0,
          fileName.lastIndexOf('.')) + ".js";
        fs.writeFile(outFileName, js, function (error) {
          if (error) {
            return console.log(error);
          }
        });
      } else {
        vm.runInContext(js, context);
      }
    }
  });
});<|endoftext|>


<commit_before>/* vim: set expandtab sw=4 ts=4: */
/**
 * Analyse and visualise trend data using the Keen.io API.
 *
 * Copyright (C) 2014-2015 Dieter Adriaenssens <ruleant@users.sourceforge.net>
 *
 * This file is part of buildtimetrend/dashboard
 * <https://github.com/buildtimetrend/dashboard/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

// Timeframe button constants
var BUTTON_COUNT_NAME = "count";
var BUTTON_COUNT_DEFAULT = "jobs";
var BUTTONS_COUNT = {
    "builds": {
        "caption": "Builds",
        "keenEventCollection": "build_jobs",
        "keenTargetProperty": "job.build"
    },
    "jobs": {
        "caption": "Build jobs",
        "keenEventCollection": "build_jobs",
        "keenTargetProperty": "job.job"
    }
};

var countButtons = new ButtonClass(
    BUTTON_COUNT_NAME,
    BUTTONS_COUNT,
    BUTTON_COUNT_DEFAULT
);
countButtons.onClick = function() { updateCountCharts(); };

var chartBuildsPerProject, chartBuildsPerProjectPie;

function initCharts() {
    // initialize timeframe buttons
    timeframeButtons.initButtons();
    countButtons.initButtons();

    // get Update Period settings
    var updatePeriod = getUpdatePeriod();

    var keenMaxAge = updatePeriod.keenMaxAge;
    var keenTimeframe = updatePeriod.keenTimeframe;
    var keenInterval = updatePeriod.keenInterval;

    var PROJECT_NAME_PROPERTY = 'buildtime_trend.project_name';

    // get count button target
    var countSettings = countButtons.getCurrentButton();

    // display charts
    $('#charts').show();

    // visualization code goes here
    Keen.ready(function() {
        /* Total unique repos */
        var metricTotalRepos = new ChartClass();

        // create query
        metricTotalRepos.queries.push(new Keen.Query("count_unique", {
            eventCollection: "build_jobs",
            targetProperty: "job.repo",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalRepos);

        // draw chart
        metricTotalRepos.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_unique_repos"))
            .title("Unique repos")
            .width(300)
            .colors([BLUE])
            .attributes({
                chartOptions: {prettyNumber: false}
            })
            .prepare();

        metricTotalRepos.request = client.run(metricTotalRepos.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalRepos.chart.error(err.message);
            } else {
                metricTotalRepos.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalRepos);

        /* Total build jobs */
        var metricTotalBuildJobs = new ChartClass();

        // create query
        metricTotalBuildJobs.queries.push(new Keen.Query("count", {
            eventCollection: "build_jobs",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalBuildJobs);

        // draw chart
        metricTotalBuildJobs.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_build_jobs"))
            .title("Total build jobs")
            .width(300)
            .prepare();

        metricTotalBuildJobs.request = client.run(metricTotalBuildJobs.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalBuildJobs.chart.error(err.message);
            } else {
                metricTotalBuildJobs.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalBuildJobs);

        /* Total sub stages */
        var metricTotalSubStages = new ChartClass();

        // create query
        metricTotalSubStages.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalSubStages);

        // draw chart
        metricTotalSubStages.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_substages"))
            .title("Total substages")
            .width(300)
            .prepare();

        metricTotalSubStages.request = client.run(metricTotalSubStages.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalSubStages.chart.error(err.message);
            } else {
                metricTotalSubStages.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalSubStages);

        /* Total events */
        var metricTotalEvents = new ChartClass();

        // draw chart
        metricTotalEvents.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_events"))
            .title("Total events")
            .width(300)
            .colors([LAVENDER])
            .prepare();

        // combine result of total build jobs and total substages
        metricTotalEvents.request = client.run(
            metricTotalBuildJobs.queries.concat(metricTotalSubStages.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                metricTotalEvents.chart.error(err.message);
            } else {
                var totalEvents = 0;
                $.each(res, function() {
                    totalEvents += this.result;
                });
                metricTotalEvents.chart
                    .parseRawData({result: totalEvents})
                    .render();
            }
        });
        chartsUpdate.push(metricTotalEvents);

        /* Unique repos */
        var chartUniqueRepos = new ChartClass();

        // create query
        chartUniqueRepos.queries.push(new Keen.Query("count_unique", {
            eventCollection: "build_jobs",
            targetProperty: "job.repo",
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartUniqueRepos);
        chartsInterval.push(chartUniqueRepos);

        // draw chart
        chartUniqueRepos.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_unique_repos"))
            .title("Unique project repositories")
            .chartType("areachart")
            .height(400)
            .prepare();

        chartUniqueRepos.request = client.run(chartUniqueRepos.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartUniqueRepos.chart.error(err.message);
            } else {
                chartUniqueRepos.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartUniqueRepos);

        /* Builds per project */
        chartBuildsPerProject = new ChartClass();

        // create query
        chartBuildsPerProject.queries.push(new Keen.Query("count_unique", {
            eventCollection: countSettings.keenEventCollection,
            targetProperty: countSettings.keenTargetProperty,
            groupBy: PROJECT_NAME_PROPERTY,
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartBuildsPerProject);
        chartsInterval.push(chartBuildsPerProject);

        // draw chart
        chartBuildsPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_builds_per_project"))
            .title(countSettings.caption + " per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
           .prepare();

        chartBuildsPerProject.request = client.run(chartBuildsPerProject.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartBuildsPerProject.chart.error(err.message);
            } else {
                chartBuildsPerProject.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartBuildsPerProject);

        /* Builds per project (piechart) */
        chartBuildsPerProjectPie = new ChartClass();

        // create query
        chartBuildsPerProjectPie.queries.push(new Keen.Query("count_unique", {
            eventCollection: countSettings.keenEventCollection,
            targetProperty: countSettings.keenTargetProperty,
            groupBy: PROJECT_NAME_PROPERTY,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartBuildsPerProjectPie);

        // draw chart
        chartBuildsPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_builds_per_project_pie"))
            .title(countSettings.caption + " per project")
            .height(400)
            .prepare();

        chartBuildsPerProjectPie.request = client.run(chartBuildsPerProjectPie.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartBuildsPerProjectPie.chart.error(err.message);
            } else {
                chartBuildsPerProjectPie.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartBuildsPerProjectPie);

        /* Substages per project */
        var chartStagesPerProject = new ChartClass();

        // create query
        chartStagesPerProject.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            groupBy: PROJECT_NAME_PROPERTY,
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartStagesPerProject);
        chartsInterval.push(chartStagesPerProject);

        // draw chart
        chartStagesPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_stages_per_project"))
            .title("Substages per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
           .prepare();

        chartStagesPerProject.request = client.run(chartStagesPerProject.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartStagesPerProject.charterror(err.message);
            } else {
                chartStagesPerProject.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartStagesPerProject);

        /* Substages per project (piechart)*/
        var chartStagesPerProjectPie = new ChartClass();

        // create query
        chartStagesPerProjectPie.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            groupBy: PROJECT_NAME_PROPERTY,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartStagesPerProjectPie);

        // draw chart
        chartStagesPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_stages_per_project_pie"))
            .title("Substages per project")
            .height(400)
            .prepare();

        chartStagesPerProjectPie.request = client.run(chartStagesPerProjectPie.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartStagesPerProjectPie.chart.error(err.message);
            } else {
                chartStagesPerProjectPie.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartStagesPerProjectPie);

        /* Total events per project */
        var chartEventsPerProject = new ChartClass();

        // draw chart
        chartEventsPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_total_events"))
            .title("Total events per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
            .prepare();

        chartEventsPerProject.request = client.run(
            chartStagesPerProject.queries.concat(chartBuildsPerProject.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                chartEventsPerProject.chart.error(err.message);
            } else {
                var result1 = res[0].result;
                var mergedResult = [];
                var i=0;

                while (i < result1.length) {
                    var mergedHash = {};
                    $.each(res, function() {
                        $.each(this["result"][i]["value"], function() {
                            if (mergedHash[this[PROJECT_NAME_PROPERTY]]) {
                                mergedHash[this[PROJECT_NAME_PROPERTY]].result += this.result;
                            } else {
                                mergedHash[this[PROJECT_NAME_PROPERTY]] = this;
                            }
                        });
                    });

                    mergedResult[i]={
                        timeframe: result1[i]["timeframe"],
                        value: removeKeys(mergedHash)
                    }
                    i++;
                }

                chartEventsPerProject.chart
                    .parseRawData({result: mergedResult})
                    .render();
            }
        });
        chartsUpdate.push(chartEventsPerProject);

        /* Total events per project (piechart)*/
        var chartEventsPerProjectPie = new ChartClass();

        // draw chart
        chartEventsPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_total_events_pie"))
            .title("Total events per project")
            .height(400)
            .prepare();

        chartEventsPerProjectPie.request = client.run(
            chartStagesPerProjectPie.queries.concat(chartBuildsPerProjectPie.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                chartEventsPerProjectPie.chart.error(err.message);
            } else {
                /* Merge series (sum results)
                 *
                 * First merge the results of the different series into
                 * a key-value list, using the key name to check if an
                 * object already exists.
                 * Add the value to the list if it doesn't exist, if it does exist,
                 * add its value to the value of the existing value.
                 * Secondly, remove the key name from the list,
                 * by inserting every value into an array.
                 *
                 * Using a key-value list will be much faster (O(n)),
                 * than iterating all existing objects to check if the name is the same (O(n^2))
                 */
                // use named keys to lookup if object already exists
                var mergedHash = {};
                $.each(res, function() {
                    $.each(this.result, function() {
                        if (mergedHash[this[PROJECT_NAME_PROPERTY]]) {
                            mergedHash[this[PROJECT_NAME_PROPERTY]].result += this.result;
                        } else {
                            mergedHash[this[PROJECT_NAME_PROPERTY]] = this;
                        }
                    });
                });

                chartEventsPerProjectPie.chart
                    .parseRawData({result: removeKeys(mergedHash)})
                    .render();
            }
        });
        chartsUpdate.push(chartEventsPerProjectPie);
    });
}

/**
 * Refresh count charts eventCollection and targetProperty selected by countButtons.
 */
function updateCountCharts() {
  // get count button target
  var countSettings = countButtons.getCurrentButton();

  // update queries
  chartBuildsPerProject.queries[0].set({
    eventCollection: countSettings.keenEventCollection,
    targetProperty: countSettings.keenTargetProperty
  });
  chartBuildsPerProjectPie.queries[0].set({
    eventCollection: countSettings.keenEventCollection,
    targetProperty: countSettings.keenTargetProperty
  });

  chartBuildsPerProject.chart.title(countSettings.caption + " per project");
  chartBuildsPerProjectPie.chart.title(countSettings.caption + " per project");

  // refresh all query requests
  chartBuildsPerProject.request.refresh();
  chartBuildsPerProjectPie.request.refresh();
}

// initialize page
$(document).ready(function() {
    updateTitle();
    initLinks();
    initMessage();
    populateProjects();

    if (!isEmpty(keenConfig.projectId) && !isEmpty(keenConfig.readKey)) {
        initCharts();
    }
});
<commit_msg>move duplicate code into mergeSum() function<commit_after>/* vim: set expandtab sw=4 ts=4: */
/**
 * Analyse and visualise trend data using the Keen.io API.
 *
 * Copyright (C) 2014-2015 Dieter Adriaenssens <ruleant@users.sourceforge.net>
 *
 * This file is part of buildtimetrend/dashboard
 * <https://github.com/buildtimetrend/dashboard/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

// Timeframe button constants
var BUTTON_COUNT_NAME = "count";
var BUTTON_COUNT_DEFAULT = "jobs";
var BUTTONS_COUNT = {
    "builds": {
        "caption": "Builds",
        "keenEventCollection": "build_jobs",
        "keenTargetProperty": "job.build"
    },
    "jobs": {
        "caption": "Build jobs",
        "keenEventCollection": "build_jobs",
        "keenTargetProperty": "job.job"
    }
};

var countButtons = new ButtonClass(
    BUTTON_COUNT_NAME,
    BUTTONS_COUNT,
    BUTTON_COUNT_DEFAULT
);
countButtons.onClick = function() { updateCountCharts(); };

var chartBuildsPerProject, chartBuildsPerProjectPie;

/* Merge values into a hashtable, add results if key exists */
function mergeSum(object, mergedHashTable, propertyName) {
    var key = object[propertyName];
    if (mergedHashTable[key]) {
        mergedHashTable[key].result += object.result;
    } else {
        mergedHashTable[key] = object;
    }
}

function initCharts() {
    // initialize timeframe buttons
    timeframeButtons.initButtons();
    countButtons.initButtons();

    // get Update Period settings
    var updatePeriod = getUpdatePeriod();

    var keenMaxAge = updatePeriod.keenMaxAge;
    var keenTimeframe = updatePeriod.keenTimeframe;
    var keenInterval = updatePeriod.keenInterval;

    var PROJECT_NAME_PROPERTY = 'buildtime_trend.project_name';

    // get count button target
    var countSettings = countButtons.getCurrentButton();

    // display charts
    $('#charts').show();

    // visualization code goes here
    Keen.ready(function() {
        /* Total unique repos */
        var metricTotalRepos = new ChartClass();

        // create query
        metricTotalRepos.queries.push(new Keen.Query("count_unique", {
            eventCollection: "build_jobs",
            targetProperty: "job.repo",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalRepos);

        // draw chart
        metricTotalRepos.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_unique_repos"))
            .title("Unique repos")
            .width(300)
            .colors([BLUE])
            .attributes({
                chartOptions: {prettyNumber: false}
            })
            .prepare();

        metricTotalRepos.request = client.run(metricTotalRepos.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalRepos.chart.error(err.message);
            } else {
                metricTotalRepos.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalRepos);

        /* Total build jobs */
        var metricTotalBuildJobs = new ChartClass();

        // create query
        metricTotalBuildJobs.queries.push(new Keen.Query("count", {
            eventCollection: "build_jobs",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalBuildJobs);

        // draw chart
        metricTotalBuildJobs.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_build_jobs"))
            .title("Total build jobs")
            .width(300)
            .prepare();

        metricTotalBuildJobs.request = client.run(metricTotalBuildJobs.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalBuildJobs.chart.error(err.message);
            } else {
                metricTotalBuildJobs.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalBuildJobs);

        /* Total sub stages */
        var metricTotalSubStages = new ChartClass();

        // create query
        metricTotalSubStages.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            timezone: TIMEZONE_SECS,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge
        }));
        chartsTimeframe.push(metricTotalSubStages);

        // draw chart
        metricTotalSubStages.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_substages"))
            .title("Total substages")
            .width(300)
            .prepare();

        metricTotalSubStages.request = client.run(metricTotalSubStages.queries, function(err, res){
            if (err) {
                // Display the API error
                metricTotalSubStages.chart.error(err.message);
            } else {
                metricTotalSubStages.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(metricTotalSubStages);

        /* Total events */
        var metricTotalEvents = new ChartClass();

        // draw chart
        metricTotalEvents.chart = new Keen.Dataviz()
            .el(document.getElementById("metric_total_events"))
            .title("Total events")
            .width(300)
            .colors([LAVENDER])
            .prepare();

        // combine result of total build jobs and total substages
        metricTotalEvents.request = client.run(
            metricTotalBuildJobs.queries.concat(metricTotalSubStages.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                metricTotalEvents.chart.error(err.message);
            } else {
                var totalEvents = 0;
                $.each(res, function() {
                    totalEvents += this.result;
                });
                metricTotalEvents.chart
                    .parseRawData({result: totalEvents})
                    .render();
            }
        });
        chartsUpdate.push(metricTotalEvents);

        /* Unique repos */
        var chartUniqueRepos = new ChartClass();

        // create query
        chartUniqueRepos.queries.push(new Keen.Query("count_unique", {
            eventCollection: "build_jobs",
            targetProperty: "job.repo",
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartUniqueRepos);
        chartsInterval.push(chartUniqueRepos);

        // draw chart
        chartUniqueRepos.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_unique_repos"))
            .title("Unique project repositories")
            .chartType("areachart")
            .height(400)
            .prepare();

        chartUniqueRepos.request = client.run(chartUniqueRepos.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartUniqueRepos.chart.error(err.message);
            } else {
                chartUniqueRepos.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartUniqueRepos);

        /* Builds per project */
        chartBuildsPerProject = new ChartClass();

        // create query
        chartBuildsPerProject.queries.push(new Keen.Query("count_unique", {
            eventCollection: countSettings.keenEventCollection,
            targetProperty: countSettings.keenTargetProperty,
            groupBy: PROJECT_NAME_PROPERTY,
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartBuildsPerProject);
        chartsInterval.push(chartBuildsPerProject);

        // draw chart
        chartBuildsPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_builds_per_project"))
            .title(countSettings.caption + " per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
           .prepare();

        chartBuildsPerProject.request = client.run(chartBuildsPerProject.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartBuildsPerProject.chart.error(err.message);
            } else {
                chartBuildsPerProject.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartBuildsPerProject);

        /* Builds per project (piechart) */
        chartBuildsPerProjectPie = new ChartClass();

        // create query
        chartBuildsPerProjectPie.queries.push(new Keen.Query("count_unique", {
            eventCollection: countSettings.keenEventCollection,
            targetProperty: countSettings.keenTargetProperty,
            groupBy: PROJECT_NAME_PROPERTY,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartBuildsPerProjectPie);

        // draw chart
        chartBuildsPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_builds_per_project_pie"))
            .title(countSettings.caption + " per project")
            .height(400)
            .prepare();

        chartBuildsPerProjectPie.request = client.run(chartBuildsPerProjectPie.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartBuildsPerProjectPie.chart.error(err.message);
            } else {
                chartBuildsPerProjectPie.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartBuildsPerProjectPie);

        /* Substages per project */
        var chartStagesPerProject = new ChartClass();

        // create query
        chartStagesPerProject.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            groupBy: PROJECT_NAME_PROPERTY,
            interval: keenInterval,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartStagesPerProject);
        chartsInterval.push(chartStagesPerProject);

        // draw chart
        chartStagesPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_stages_per_project"))
            .title("Substages per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
           .prepare();

        chartStagesPerProject.request = client.run(chartStagesPerProject.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartStagesPerProject.charterror(err.message);
            } else {
                chartStagesPerProject.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartStagesPerProject);

        /* Substages per project (piechart)*/
        var chartStagesPerProjectPie = new ChartClass();

        // create query
        chartStagesPerProjectPie.queries.push(new Keen.Query("count", {
            eventCollection: "build_substages",
            groupBy: PROJECT_NAME_PROPERTY,
            timeframe: keenTimeframe,
            maxAge: keenMaxAge,
            timezone: TIMEZONE_SECS
        }));
        chartsTimeframe.push(chartStagesPerProjectPie);

        // draw chart
        chartStagesPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_stages_per_project_pie"))
            .title("Substages per project")
            .height(400)
            .prepare();

        chartStagesPerProjectPie.request = client.run(chartStagesPerProjectPie.queries, function(err, res) {
            if (err) {
                // Display the API error
                chartStagesPerProjectPie.chart.error(err.message);
            } else {
                chartStagesPerProjectPie.chart
                    .parseRequest(this)
                    .render();
            }
        });
        chartsUpdate.push(chartStagesPerProjectPie);

        /* Total events per project */
        var chartEventsPerProject = new ChartClass();

        // draw chart
        chartEventsPerProject.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_total_events"))
            .title("Total events per project")
            .chartType("columnchart")
            .height(400)
            .attributes({
                chartOptions: {
                    isStacked: true
                }
            })
            .prepare();

        chartEventsPerProject.request = client.run(
            chartStagesPerProject.queries.concat(chartBuildsPerProject.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                chartEventsPerProject.chart.error(err.message);
            } else {
                var result1 = res[0].result;
                var mergedResult = [];
                var i=0;

                while (i < result1.length) {
                    var mergedHash = {};
                    $.each(res, function() {
                        $.each(this["result"][i]["value"], function() {
                            mergeSum(this, mergedHash, PROJECT_NAME_PROPERTY);
                        });
                    });

                    mergedResult[i]={
                        timeframe: result1[i]["timeframe"],
                        value: removeKeys(mergedHash)
                    }
                    i++;
                }

                chartEventsPerProject.chart
                    .parseRawData({result: mergedResult})
                    .render();
            }
        });
        chartsUpdate.push(chartEventsPerProject);

        /* Total events per project (piechart)*/
        var chartEventsPerProjectPie = new ChartClass();

        // draw chart
        chartEventsPerProjectPie.chart = new Keen.Dataviz()
            .el(document.getElementById("chart_total_events_pie"))
            .title("Total events per project")
            .height(400)
            .prepare();

        chartEventsPerProjectPie.request = client.run(
            chartStagesPerProjectPie.queries.concat(chartBuildsPerProjectPie.queries),
            function(err, res) {
            if (err) {
                // Display the API error
                chartEventsPerProjectPie.chart.error(err.message);
            } else {
                /* Merge series (sum results)
                 *
                 * First merge the results of the different series into
                 * a key-value list, using the key name to check if an
                 * object already exists.
                 * Add the value to the list if it doesn't exist, if it does exist,
                 * add its value to the value of the existing value.
                 * Secondly, remove the key name from the list,
                 * by inserting every value into an array.
                 *
                 * Using a key-value list will be much faster (O(n)),
                 * than iterating all existing objects to check if the name is the same (O(n^2))
                 */
                // use named keys to lookup if object already exists
                var mergedHash = {};
                $.each(res, function() {
                    $.each(this.result, function() {
                        mergeSum(this, mergedHash, PROJECT_NAME_PROPERTY);
                    });
                });

                chartEventsPerProjectPie.chart
                    .parseRawData({result: removeKeys(mergedHash)})
                    .render();
            }
        });
        chartsUpdate.push(chartEventsPerProjectPie);
    });
}

/**
 * Refresh count charts eventCollection and targetProperty selected by countButtons.
 */
function updateCountCharts() {
  // get count button target
  var countSettings = countButtons.getCurrentButton();

  // update queries
  chartBuildsPerProject.queries[0].set({
    eventCollection: countSettings.keenEventCollection,
    targetProperty: countSettings.keenTargetProperty
  });
  chartBuildsPerProjectPie.queries[0].set({
    eventCollection: countSettings.keenEventCollection,
    targetProperty: countSettings.keenTargetProperty
  });

  chartBuildsPerProject.chart.title(countSettings.caption + " per project");
  chartBuildsPerProjectPie.chart.title(countSettings.caption + " per project");

  // refresh all query requests
  chartBuildsPerProject.request.refresh();
  chartBuildsPerProjectPie.request.refresh();
}

// initialize page
$(document).ready(function() {
    updateTitle();
    initLinks();
    initMessage();
    populateProjects();

    if (!isEmpty(keenConfig.projectId) && !isEmpty(keenConfig.readKey)) {
        initCharts();
    }
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var install = require('./');

var fs = require('fs');

var usage = function () {
  console.error('Usage:  fish-install DIRECTORY');
  return 1;
};

process.exitCode = (function main (argv) {
  if (argv.length != 1) {
    return usage();
  }

  var dir = argv[0];
  try {
    if (!fs.statSync(dir).isDirectory()) {
      return usage();
    }
  }
  catch (e) {
    if (e.code == 'ENOENT') {
      return usage();
    }
    throw(e);
  }

  install(dir, function (err) {
    if (err) throw err;
  });

  return 0;
}(process.argv.slice(2)));
<commit_msg>Add --help and --version<commit_after>#!/usr/bin/env node
'use strict';

var install = require('./');

var fs = require('fs');

var usage = function () {
  console.error('Usage:  fish-install DIRECTORY');
  console.error('        fish-install [--help | --version]');
  return 1;
};

var version = function () {
  var pkg = require('./package.json');
  console.error(pkg.name + ' v' + pkg.version);
  return 1;
}

var main = function (dir) {
  try {
    if (!fs.statSync(dir).isDirectory()) {
      return usage();
    }
  }
  catch (e) {
    if (e.code == 'ENOENT') {
      return usage();
    }
    throw(e);
  }

  install(dir, function (err) {
    if (err) throw err;
  });

  return 0;
};

process.exitCode = (function (argv) {
  if (argv.length != 1) {
    return usage();
  }

  switch (argv[0]) {
    case '--help':
      return usage();

    case '--version':
      return version();

    default:
      return main(argv[0]);
  }
}(process.argv.slice(2)));
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';
const meow = require('meow');
const logSymbols = require('log-symbols');
const chalk = require('chalk');
const npmName = require('npm-name');

const cli = meow(`
	Usage
	  $ npm-name <name>

	Examples
	  $ npm-name chalk
	  ${logSymbols.error} ${chalk.bold('chalk')} is unavailable
	  $ npm-name unicorn-cake
	  ${logSymbols.success} ${chalk.bold('unicorn-cake')} is available

	Exits with code 0 when the name is available or 2 when taken
`);

const input = cli.input[0];

if (!input) {
	console.error('Package name required');
	process.exit(1);
}

npmName(input).then(available => {
	const name = chalk.bold(input);
	console.log(available ? `${logSymbols.success} ${name} is available` : `${logSymbols.error} ${name} is unavailable`);
	process.exit(available ? 0 : 2);
});
<commit_msg>support searching for package name that are numbers<commit_after>#!/usr/bin/env node
'use strict';
const meow = require('meow');
const logSymbols = require('log-symbols');
const chalk = require('chalk');
const npmName = require('npm-name');

const cli = meow(`
	Usage
	  $ npm-name <name>

	Examples
	  $ npm-name chalk
	  ${logSymbols.error} ${chalk.bold('chalk')} is unavailable
	  $ npm-name unicorn-cake
	  ${logSymbols.success} ${chalk.bold('unicorn-cake')} is available

	Exits with code 0 when the name is available or 2 when taken
`, {
	string: ['_']
});

const input = cli.input[0];

if (!input) {
	console.error('Package name required');
	process.exit(1);
}

npmName(input).then(available => {
	const name = chalk.bold(input);
	console.log(available ? `${logSymbols.success} ${name} is available` : `${logSymbols.error} ${name} is unavailable`);
	process.exit(available ? 0 : 2);
});
<|endoftext|>


<commit_before>var backgroundColors = [
  '#0AE', '#A0E', '#EA0', '#6C6', '#CC6', '#C66'
];
var gameboardColors = [
  '#0CE', '#C0F', '#FC0', '#0E0', '#EE0', '#E00'
];
var shipColors = [
  '#CCC', '#888', '#68A',
  '#9CC', '#488', '#68A',
  '#C9C', '#848', '#68A',
  '#CC9', '#884', '#68A',
  '#AAC', '#668', '#68A',
  '#CAA', '#866', '#68A'
];

function ready(fun) {
  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
    fun();
  }
  else {
    document.addEventListener('DOMContentLoaded', fun);
  }
}

function showForm() {
  document.getElementById('spinner').className = 'hide';
  document.getElementById('form').className = '';
}

function showGameboard() {
  document.getElementById('form-table').className = 'hide';
  document.getElementById('errors').innerHTML = '';
  document.getElementById('gameboard').className = '';
  document.getElementById('signout').className = '';
  document.getElementById('menu').className = '';
  document.getElementById('username').value = '';
  document.getElementById('password').value = '';
}

function handleErrors(message) {
  document.getElementById('errors').innerHTML = message;
}

function clearTiles(board, ship) {
  var bounds = ship.getBounds();
  var increment = ship.rad == 0 || ship.rad == PI ? 1 : 12;
  var shipHead = (bounds.l - 40) / 40 + 12 * (bounds.t - 80) / 40;
  var shipTail = shipHead + increment * ship.size;

  for (var i = shipHead; i < shipTail; i += increment) {
    if (board[i]) {
      board[i] = 0;
    }
  }
}

// Returns true only if the ship is overlaying all empty tiles
function updateBoard(board, ship) {
  var bounds = ship.getBounds();
  var increment = ship.rad == 0 || ship.rad == PI ? 1 : 12;
  var shipHead = (bounds.l - 40) / 40 + 12 * (bounds.t - 80) / 40;
  var shipTail = shipHead + increment * ship.size;

  var tiles = [];
  for (var i = shipHead; i < shipTail; i += increment) {
    // If tile is occupied by a ship
    if (board[i]) {
      return false;
    }
    tiles.push(i);
  }
  ship.tiles = tiles;

  for (var i = 0; i < ship.size; i++) {
    board[tiles[i]] = 1;
  }
  return true;
}

function setMessage(message, content, flash) {
  message.content = content;
  message.length = content.length;
  message.cursorDelay = 0;
  message.flash = flash;

  if (flash) {
    message.delay = 90;
    message.cursor = Math.min(33, content.length);
  }
  else {
    message.delay = 0;
    message.cursor = 0;
  }
}
<commit_msg>Update ship color options<commit_after>var backgroundColors = [
  '#0AE', '#A0E', '#EA0', '#6C6', '#CC6', '#C66'
];
var gameboardColors = [
  '#0CE', '#C0F', '#FC0', '#0E0', '#EE0', '#E00'
];
var shipColors = [
  '#CCC', '#888', '#68A',
  '#C9C', '#848', '#A2A',
  '#9CC', '#488', '#2AA',
  '#9CA', '#486', '#2A4',
  '#AAC', '#668', '#33A',
  '#CAA', '#866', '#A33'
];

function ready(fun) {
  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
    fun();
  }
  else {
    document.addEventListener('DOMContentLoaded', fun);
  }
}

function showForm() {
  document.getElementById('spinner').className = 'hide';
  document.getElementById('form').className = '';
}

function showGameboard() {
  document.getElementById('form-table').className = 'hide';
  document.getElementById('errors').innerHTML = '';
  document.getElementById('gameboard').className = '';
  document.getElementById('signout').className = '';
  document.getElementById('menu').className = '';
  document.getElementById('username').value = '';
  document.getElementById('password').value = '';
}

function handleErrors(message) {
  document.getElementById('errors').innerHTML = message;
}

function clearTiles(board, ship) {
  var bounds = ship.getBounds();
  var increment = ship.rad == 0 || ship.rad == PI ? 1 : 12;
  var shipHead = (bounds.l - 40) / 40 + 12 * (bounds.t - 80) / 40;
  var shipTail = shipHead + increment * ship.size;

  for (var i = shipHead; i < shipTail; i += increment) {
    if (board[i]) {
      board[i] = 0;
    }
  }
}

// Returns true only if the ship is overlaying all empty tiles
function updateBoard(board, ship) {
  var bounds = ship.getBounds();
  var increment = ship.rad == 0 || ship.rad == PI ? 1 : 12;
  var shipHead = (bounds.l - 40) / 40 + 12 * (bounds.t - 80) / 40;
  var shipTail = shipHead + increment * ship.size;

  var tiles = [];
  for (var i = shipHead; i < shipTail; i += increment) {
    // If tile is occupied by a ship
    if (board[i]) {
      return false;
    }
    tiles.push(i);
  }
  ship.tiles = tiles;

  for (var i = 0; i < ship.size; i++) {
    board[tiles[i]] = 1;
  }
  return true;
}

function setMessage(message, content, flash) {
  message.content = content;
  message.length = content.length;
  message.cursorDelay = 0;
  message.flash = flash;

  if (flash) {
    message.delay = 90;
    message.cursor = Math.min(33, content.length);
  }
  else {
    message.delay = 0;
    message.cursor = 0;
  }
}
<|endoftext|>


<commit_before><commit_msg>Put .remove before handle.<commit_after><|endoftext|>