<commit_before>var sq = getUrlParameter('sq');
var noSH;
try {
    /*** get rid of ?sq= parameter ASAP ***/
    if (sq && sq !== undefined) {
        var re = new RegExp("(\\?|&)sq=" + escapeRegExp(encodeURIComponent(sq)));
        var newurl = location.href.replace(re, '');
        if (~newurl.indexOf('&') && !~newurl.indexOf('?')) {
            newurl = newurl.replace('&', '?');
        }
        window.history.replaceState({}, '', newurl);
    }

} catch (err) {
    console.log("Couldn't replaceState! Oh well, not a big deal.");
    console.err(err);
}
/****** DOM STUFF *********/

// adjusts altitude of content and navbar, in case header size changes.
function nav_altitude() {

    var h = $('#menudocumentation').height();
    if ($("#menudocumentation").css("position") != "fixed") {
        $('#resizable').css("top", 0);
    } else {
        $('#resizable').css("top", h);
    }
    $('.content').css("padding-top", h + 40);

    if (!$('#resizable').is(":visible")) {
        $('.content').css('padding-left', 15);
    } else {
        $('.content').css('padding-left', parseInt($('#resizable').css('width')) + 100);
    }

}

function updateSearchHighlight() {
    var root = $('.contents');
    if (noSH) {
        root.removeHighlight();
    } else {
        sq.replace(/[^\w\s]/g, '').split(" ").forEach(function(term) {
            root.highlight(term);
        });

        var el = $("span.search-highlight");
        if (el.length > 0) {
            var top = el.first().offset().top - (window.innerHeight / 2);
            window.scrollTo(0, top);

        }

    }
}
$(document).ready(function() {
    noSH = $.cookie("no-sh");
    $("#searchbox").val(sq);
    // Contains = case insensitive 'contains'
    $.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
        return function(elem) {
            return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
        };
    });

    /*** highlight previous search query on page ****/
    if (sq !== undefined) {
        var $div = $('
Search term highlight - ' + (noSH ? '[off]' : '[on]') + '
').appendTo('body');
        $div.find('#toggle_highlight').on('click', function() {
            noSH = !noSH;
            updateSearchHighlight();
            $(this).text((noSH ? '[off]' : '[on]'));
            if (noSH)
                $.cookie("no-sh", 1);
            else
                $.removeCookie("no-sh");
        });
        updateSearchHighlight();

    }

    $("#searchbox").keyup(function(e) {
        if (e.which >= 37 && e.which <= 40) // ignore if it's an arrow key
            return;
        getdata();
    });
    $("#searchbox").click(function() {
        getdata();
    });
    var $content = $('.content');
    var is_mobile = screen.width <= 719;

    if (!is_mobile) {
        $("#left").perfectScrollbar();
    } else {
        $("#left").detach().appendTo('#top');
        $("#resizable").remove();
    }

    nav_altitude();
    $(window).resize(nav_altitude);
    // closing and opening of hamburger
    $("#menu-trigger").click(function() {
        $("#top")[this.opened ? "removeClass" : "addClass"]("menu-open");
        $(this)[this.opened ? "removeClass" : "addClass"]("trigger-open");
        this.opened = !this.opened;
    });
    $(".content").click(function() {
        $("#top").removeClass("menu-open");
        $("#menu-trigger").removeClass("trigger-open");
        $("#menu-trigger")[0].opened = false;
    });
    $("#top a").click(function() {
        $("#top").removeClass("menu-open");
        $("#menu-trigger").removeClass("trigger-open").get(0).opened = false;

    });

    var cookieName = 'navwidth';
    var width = parseInt($.cookie(cookieName)) || 300;

    var preventNextClick = false; // so resize doesn't trigger click

    function adjust_content_padding(w) {
        $content.css('padding-left', w + 100 + 'px');
    }

    $('#resizable').css('width', width + 'px');
    adjust_content_padding(width);

    $('#resizable').resizable({

        handles: 'e',
        maxWidth: 500,
        minWidth: 5,
        resize: function(event, ui) {

            adjust_content_padding(ui.size.width);
            $.cookie(cookieName, ui.size.width);
            width = ui.size.width;

        },
        stop: function(event, ui) {
            if ($(".ui-resizable-e").is(":hover"))
                preventNextClick = true;
        }
    });
    $(".ui-resizable-e").click(function() {
        if (preventNextClick) {
            preventNextClick = false;
            return;
        }
        var $p = $(this).parent();
        if ($p.width() < width) {
            $p.width(width);
        } else if ($p.width() == 5) {
            width = 300;
            $p.width(300);
        } else {
            $p.width(5);
        }
        adjust_content_padding($p.width());
    })
});

/******* SEARCHING *******/

// perform a search.
// ctx: context of current page, e.g. "Build Apps"
function getdata() {
    ctx = $('html').data('context');
    var domain = "";
    var invoke_url = "https://x6khds7mia.execute-api.us-west-2.amazonaws.com/prod";
    if (ctx == "Legato Documentation")
        ctx = "";
    var keyword = $('#searchbox').val();
    var ac = $("#searchbox").autocomplete({
        delay: 10,
        autoFocus: true,
        source: function(request, response) {
            $.ajax({
                url: invoke_url,
                dataType: "json",
                data: new function() {
                    this.q = request.term;
                    this.size = 25;
                    if (ctx != "")
                        this.fq = "context:'" + ctx + "'"; //only include "context:" filter if ctx isnt empty
                    this.sort = "_score desc";
                    this.return = "category,title,context,id";
                },
                change: function(e, ui) {
                    console.log(e.target.value);
                },

                success: function(data) {
                    var hits = data.hits.hit;
                    var results = []
                    if (hits.length == 0) {
                        results.push({
                            value: "javascript:void(0);",
                            label: "No results found :("
                        });
                    } else
                        for (i = 0; i < hits.length; i++) {
                            var result = new Object();

                            result.value = addParameter(hits[i].id, 'sq', request.term, false);
                            result.cat = hits[i].fields.category;
                            if (result.cat === undefined)
                                result.cat = "Root"

                            result.label = "" + result.cat + "
" + hits[i].fields.title + "";

                            result.title = hits[i].fields.title;
                            results.push(result);
                        }
                    response(results);
                }
            });

        },

        minLength: 1,
        focus: function(event, ui) {
            event.preventDefault(); // so the textbox's value doesn't get replaced.
        },
        select: function(event, ui) {
            event.preventDefault();
            if (ui.item) {
                window.location = ui.item.value;
            }
        },
        open: function() {
            $(this).removeClass("ui-corner-all").addClass("ui-corner-top");

        },
        close: function() {
            $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
        }
    });

    ac.data("ui-autocomplete")._renderItem = function(ul, item) {
        var anchor = $("").attr("href", item.value).html(item.label);
        var li = $("
");
        anchor.appendTo(li)
        return li.appendTo(ul);
    };

    ac.data("ui-autocomplete")._renderMenu = function(ul, items) {
        var that = this;
        ul.attr('id', "search_result");
        ul.attr('class', "ui-autocomplete ui-front ui-menu ui-widget ui-widget-content");
        //ul.attr('data-resultcount',items.length + " results.");
        $.each(items, function(index, item) {
            that._renderItemData(ul, item);
        });
    };
    $("#searchbox").autocomplete("search", keyword);

}
/***** NAVIGATION TREE ******/

function setupTree(treeURL) {
    if (treeURL != "") {

        $(document).ready(function() {
            String.prototype.endsWith = function(suffix) {
                return this.indexOf(suffix, this.length - suffix.length) !== -1;
            };
            $.getJSON(
                treeURL,
                function(data) {
                    $tree = $('#tree1');
                    $tree.tree({
                        data: data.children ? data.children : data,
                        saveState: false,
                        useContextMenu: false,
                        slide: false,
                        closedIcon: "+",
                        openedIcon: "-",
                        keyboardSupport: false,

                        onCreateLi: function(node, $li) {
                            var a = $li.find('span')[0];
                            a.outerHTML = '' + a.outerHTML + '';
                        },

                    });

                    $tree.tree('selectNode', null); //unselect nodes

                    $("#left").perfectScrollbar('update');

                    $tree.bind(
                        'tree.close',
                        function(e) {
                            $("#left").perfectScrollbar('update');
                        }
                    );
                    $tree.bind(
                        'tree.open',
                        function(e) {
                            $("#left").perfectScrollbar('update');
                        }
                    );

                    // we want to get
                    var path = window.location.pathname; // path = /docs/filename.html
                    var page = path.split("/").pop(); // page = filename.html
                    var anchor = window.location.hash.substr(1); // anchor = section
                    if (anchor)
                        page += "#" + anchor; // page = filename.html#section

                    // find node of current page and select it
                    $tree.tree('getTree').iterate(
                        function(node, level) {
                            if (node.href === page) {
                                $tree.tree('openNode', node);
                                $tree.tree('selectNode', node);
                                return false;
                            }
                            return true;
                        }
                    );
                    $('#tree1').tree('setOption', 'slide', true); // now that only the user will be opening nodes, we can turn the animation back on.
                    // needed when clicking an anchor, and thus staying on the same page
                    $tree.bind(
                        'tree.click',
                        function(event) {
                            $tree.tree('setOption', 'selectable', true);
                            $tree.tree('selectNode', event.node);
                            $tree.tree('setOption', 'selectable', false);
                        }
                    );

                    $('#tree1').tree('setOption', 'selectable', false);

                }
            );

        });
    }
}

/*** utils ***/

// http://stackoverflow.com/a/6954277/765210
function addParameter(url, parameterName, parameterValue, atStart /*Add param before others*/ ) {
    replaceDuplicates = true;
    if (url.indexOf('#') > 0) {
        var cl = url.indexOf('#');
        urlhash = url.substring(url.indexOf('#'), url.length);
    } else {
        urlhash = '';
        cl = url.length;
    }
    sourceUrl = url.substring(0, cl);

    var urlParts = sourceUrl.split("?");
    var newQueryString = "";

    if (urlParts.length > 1) {
        var parameters = urlParts[1].split("&");
        for (var i = 0;
            (i < parameters.length); i++) {
            var parameterParts = parameters[i].split("=");
            if (!(replaceDuplicates && parameterParts[0] == parameterName)) {
                if (newQueryString == "")
                    newQueryString = "?";
                else
                    newQueryString += "&";
                newQueryString += parameterParts[0] + "=" + (parameterParts[1] ? parameterParts[1] : '');
            }
        }
    }
    if (newQueryString == "")
        newQueryString = "?";

    if (atStart) {
        newQueryString = '?' + parameterName + "=" + parameterValue + (newQueryString.length > 1 ? '&' + newQueryString.substring(1) : '');
    } else {
        if (newQueryString !== "" && newQueryString != '?')
            newQueryString += "&";
        newQueryString += parameterName + "=" + (parameterValue ? parameterValue : '');
    }
    return urlParts[0] + newQueryString + urlhash;
};
// http://stackoverflow.com/a/21903119/765210
function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
        sURLVariables = sPageURL.split('&'),
        sParameterName,
        i;

    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split('=');

        if (sParameterName[0] === sParam) {
            return sParameterName[1] === undefined ? true : sParameterName[1];
        }
    }
}

function escapeRegExp(str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

/*

highlight v5

Highlights arbitrary terms.

<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>

MIT license.

Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>

*/

jQuery.fn.highlight = function(pat) {
    function innerHighlight(node, pat) {
        var skip = 0;
        if (node.nodeType == 3) {
            var pos = node.data.toUpperCase().indexOf(pat);
            pos -= (node.data.substr(0, pos).toUpperCase().length - node.data.substr(0, pos).length);
            if (pos >= 0) {
                var spannode = document.createElement('span');
                spannode.className = 'search-highlight';
                var middlebit = node.splitText(pos);
                var endbit = middlebit.splitText(pat.length);
                var middleclone = middlebit.cloneNode(true);
                spannode.appendChild(middleclone);
                middlebit.parentNode.replaceChild(spannode, middlebit);
                skip = 1;
            }
        } else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
            for (var i = 0; i < node.childNodes.length; ++i) {
                i += innerHighlight(node.childNodes[i], pat);
            }
        }
        return skip;
    }
    return this.length && pat && pat.length ? this.each(function() {
        innerHighlight(this, pat.toUpperCase());
    }) : this;
};

jQuery.fn.removeHighlight = function() {
    return this.find("span.search-highlight").each(function() {
        this.parentNode.firstChild.nodeName;
        with(this.parentNode) {
            replaceChild(this.firstChild, this);
            normalize();
        }
    }).end();
};<commit_msg>add google analytics<commit_after>var sq = getUrlParameter('sq');
var noSH;
try {
    /*** get rid of ?sq= parameter ASAP ***/
    if (sq && sq !== undefined) {
        var re = new RegExp("(\\?|&)sq=" + escapeRegExp(encodeURIComponent(sq)));
        var newurl = location.href.replace(re, '');
        if (~newurl.indexOf('&') && !~newurl.indexOf('?')) {
            newurl = newurl.replace('&', '?');
        }
        window.history.replaceState({}, '', newurl);
    }

} catch (err) {
    console.log("Couldn't replaceState! Oh well, not a big deal.");
    console.err(err);
}

 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-78024926-1', 'auto');
  ga('send', 'pageview');

/****** DOM STUFF *********/

// adjusts altitude of content and navbar, in case header size changes.
function nav_altitude() {

    var h = $('#menudocumentation').height();
    if ($("#menudocumentation").css("position") != "fixed") {
        $('#resizable').css("top", 0);
    } else {
        $('#resizable').css("top", h);
    }
    $('.content').css("padding-top", h + 40);

    if (!$('#resizable').is(":visible")) {
        $('.content').css('padding-left', 15);
    } else {
        $('.content').css('padding-left', parseInt($('#resizable').css('width')) + 100);
    }

}

function updateSearchHighlight() {
    var root = $('.contents');
    if (noSH) {
        root.removeHighlight();
    } else {
        sq.replace(/[^\w\s]/g, '').split(" ").forEach(function(term) {
            root.highlight(term);
        });

        var el = $("span.search-highlight");
        if (el.length > 0) {
            var top = el.first().offset().top - (window.innerHeight / 2);
            window.scrollTo(0, top);

        }

    }
}
$(document).ready(function() {
    noSH = $.cookie("no-sh");
    $("#searchbox").val(sq);
    // Contains = case insensitive 'contains'
    $.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
        return function(elem) {
            return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
        };
    });

    /*** highlight previous search query on page ****/
    if (sq !== undefined) {
        var $div = $('
Search term highlight - ' + (noSH ? '[off]' : '[on]') + '
').appendTo('body');
        $div.find('#toggle_highlight').on('click', function() {
            noSH = !noSH;
            updateSearchHighlight();
            $(this).text((noSH ? '[off]' : '[on]'));
            if (noSH)
                $.cookie("no-sh", 1);
            else
                $.removeCookie("no-sh");
        });
        updateSearchHighlight();

    }

    $("#searchbox").keyup(function(e) {
        if (e.which >= 37 && e.which <= 40) // ignore if it's an arrow key
            return;
        getdata();
    });
    $("#searchbox").click(function() {
        getdata();
    });
    var $content = $('.content');
    var is_mobile = screen.width <= 719;

    if (!is_mobile) {
        $("#left").perfectScrollbar();
    } else {
        $("#left").detach().appendTo('#top');
        $("#resizable").remove();
    }

    nav_altitude();
    $(window).resize(nav_altitude);
    // closing and opening of hamburger
    $("#menu-trigger").click(function() {
        $("#top")[this.opened ? "removeClass" : "addClass"]("menu-open");
        $(this)[this.opened ? "removeClass" : "addClass"]("trigger-open");
        this.opened = !this.opened;
    });
    $(".content").click(function() {
        $("#top").removeClass("menu-open");
        $("#menu-trigger").removeClass("trigger-open");
        $("#menu-trigger")[0].opened = false;
    });
    $("#top a").click(function() {
        $("#top").removeClass("menu-open");
        $("#menu-trigger").removeClass("trigger-open").get(0).opened = false;

    });

    var cookieName = 'navwidth';
    var width = parseInt($.cookie(cookieName)) || 300;

    var preventNextClick = false; // so resize doesn't trigger click

    function adjust_content_padding(w) {
        $content.css('padding-left', w + 100 + 'px');
    }

    $('#resizable').css('width', width + 'px');
    adjust_content_padding(width);

    $('#resizable').resizable({

        handles: 'e',
        maxWidth: 500,
        minWidth: 5,
        resize: function(event, ui) {

            adjust_content_padding(ui.size.width);
            $.cookie(cookieName, ui.size.width);
            width = ui.size.width;

        },
        stop: function(event, ui) {
            if ($(".ui-resizable-e").is(":hover"))
                preventNextClick = true;
        }
    });
    $(".ui-resizable-e").click(function() {
        if (preventNextClick) {
            preventNextClick = false;
            return;
        }
        var $p = $(this).parent();
        if ($p.width() < width) {
            $p.width(width);
        } else if ($p.width() == 5) {
            width = 300;
            $p.width(300);
        } else {
            $p.width(5);
        }
        adjust_content_padding($p.width());
    })
});

/******* SEARCHING *******/

// perform a search.
// ctx: context of current page, e.g. "Build Apps"
function getdata() {
    ctx = $('html').data('context');
    var domain = "";
    var invoke_url = "https://x6khds7mia.execute-api.us-west-2.amazonaws.com/prod";
    if (ctx == "Legato Documentation")
        ctx = "";
    var keyword = $('#searchbox').val();
    var ac = $("#searchbox").autocomplete({
        delay: 10,
        autoFocus: true,
        source: function(request, response) {
            $.ajax({
                url: invoke_url,
                dataType: "json",
                data: new function() {
                    this.q = request.term;
                    this.size = 25;
                    if (ctx != "")
                        this.fq = "context:'" + ctx + "'"; //only include "context:" filter if ctx isnt empty
                    this.sort = "_score desc";
                    this.return = "category,title,context,id";
                },
                change: function(e, ui) {
                    console.log(e.target.value);
                },

                success: function(data) {
                    var hits = data.hits.hit;
                    var results = []
                    if (hits.length == 0) {
                        results.push({
                            value: "javascript:void(0);",
                            label: "No results found :("
                        });
                    } else
                        for (i = 0; i < hits.length; i++) {
                            var result = new Object();

                            result.value = addParameter(hits[i].id, 'sq', request.term, false);
                            result.cat = hits[i].fields.category;
                            if (result.cat === undefined)
                                result.cat = "Root"

                            result.label = "" + result.cat + "
" + hits[i].fields.title + "";

                            result.title = hits[i].fields.title;
                            results.push(result);
                        }
                    response(results);
                }
            });

        },

        minLength: 1,
        focus: function(event, ui) {
            event.preventDefault(); // so the textbox's value doesn't get replaced.
        },
        select: function(event, ui) {
            event.preventDefault();
            if (ui.item) {
                window.location = ui.item.value;
            }
        },
        open: function() {
            $(this).removeClass("ui-corner-all").addClass("ui-corner-top");

        },
        close: function() {
            $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
        }
    });

    ac.data("ui-autocomplete")._renderItem = function(ul, item) {
        var anchor = $("").attr("href", item.value).html(item.label);
        var li = $("
");
        anchor.appendTo(li)
        return li.appendTo(ul);
    };

    ac.data("ui-autocomplete")._renderMenu = function(ul, items) {
        var that = this;
        ul.attr('id', "search_result");
        ul.attr('class', "ui-autocomplete ui-front ui-menu ui-widget ui-widget-content");
        //ul.attr('data-resultcount',items.length + " results.");
        $.each(items, function(index, item) {
            that._renderItemData(ul, item);
        });
    };
    $("#searchbox").autocomplete("search", keyword);

}
/***** NAVIGATION TREE ******/

function setupTree(treeURL) {
    if (treeURL != "") {

        $(document).ready(function() {
            String.prototype.endsWith = function(suffix) {
                return this.indexOf(suffix, this.length - suffix.length) !== -1;
            };
            $.getJSON(
                treeURL,
                function(data) {
                    $tree = $('#tree1');
                    $tree.tree({
                        data: data.children ? data.children : data,
                        saveState: false,
                        useContextMenu: false,
                        slide: false,
                        closedIcon: "+",
                        openedIcon: "-",
                        keyboardSupport: false,

                        onCreateLi: function(node, $li) {
                            var a = $li.find('span')[0];
                            a.outerHTML = '' + a.outerHTML + '';
                        },

                    });

                    $tree.tree('selectNode', null); //unselect nodes

                    $("#left").perfectScrollbar('update');

                    $tree.bind(
                        'tree.close',
                        function(e) {
                            $("#left").perfectScrollbar('update');
                        }
                    );
                    $tree.bind(
                        'tree.open',
                        function(e) {
                            $("#left").perfectScrollbar('update');
                        }
                    );

                    // we want to get
                    var path = window.location.pathname; // path = /docs/filename.html
                    var page = path.split("/").pop(); // page = filename.html
                    var anchor = window.location.hash.substr(1); // anchor = section
                    if (anchor)
                        page += "#" + anchor; // page = filename.html#section

                    // find node of current page and select it
                    $tree.tree('getTree').iterate(
                        function(node, level) {
                            if (node.href === page) {
                                $tree.tree('openNode', node);
                                $tree.tree('selectNode', node);
                                return false;
                            }
                            return true;
                        }
                    );
                    $('#tree1').tree('setOption', 'slide', true); // now that only the user will be opening nodes, we can turn the animation back on.
                    // needed when clicking an anchor, and thus staying on the same page
                    $tree.bind(
                        'tree.click',
                        function(event) {
                            $tree.tree('setOption', 'selectable', true);
                            $tree.tree('selectNode', event.node);
                            $tree.tree('setOption', 'selectable', false);
                        }
                    );

                    $('#tree1').tree('setOption', 'selectable', false);

                }
            );

        });
    }
}

/*** utils ***/

// http://stackoverflow.com/a/6954277/765210
function addParameter(url, parameterName, parameterValue, atStart /*Add param before others*/ ) {
    replaceDuplicates = true;
    if (url.indexOf('#') > 0) {
        var cl = url.indexOf('#');
        urlhash = url.substring(url.indexOf('#'), url.length);
    } else {
        urlhash = '';
        cl = url.length;
    }
    sourceUrl = url.substring(0, cl);

    var urlParts = sourceUrl.split("?");
    var newQueryString = "";

    if (urlParts.length > 1) {
        var parameters = urlParts[1].split("&");
        for (var i = 0;
            (i < parameters.length); i++) {
            var parameterParts = parameters[i].split("=");
            if (!(replaceDuplicates && parameterParts[0] == parameterName)) {
                if (newQueryString == "")
                    newQueryString = "?";
                else
                    newQueryString += "&";
                newQueryString += parameterParts[0] + "=" + (parameterParts[1] ? parameterParts[1] : '');
            }
        }
    }
    if (newQueryString == "")
        newQueryString = "?";

    if (atStart) {
        newQueryString = '?' + parameterName + "=" + parameterValue + (newQueryString.length > 1 ? '&' + newQueryString.substring(1) : '');
    } else {
        if (newQueryString !== "" && newQueryString != '?')
            newQueryString += "&";
        newQueryString += parameterName + "=" + (parameterValue ? parameterValue : '');
    }
    return urlParts[0] + newQueryString + urlhash;
};
// http://stackoverflow.com/a/21903119/765210
function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
        sURLVariables = sPageURL.split('&'),
        sParameterName,
        i;

    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split('=');

        if (sParameterName[0] === sParam) {
            return sParameterName[1] === undefined ? true : sParameterName[1];
        }
    }
}

function escapeRegExp(str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

/*

highlight v5

Highlights arbitrary terms.

<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>

MIT license.

Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>

*/

jQuery.fn.highlight = function(pat) {
    function innerHighlight(node, pat) {
        var skip = 0;
        if (node.nodeType == 3) {
            var pos = node.data.toUpperCase().indexOf(pat);
            pos -= (node.data.substr(0, pos).toUpperCase().length - node.data.substr(0, pos).length);
            if (pos >= 0) {
                var spannode = document.createElement('span');
                spannode.className = 'search-highlight';
                var middlebit = node.splitText(pos);
                var endbit = middlebit.splitText(pat.length);
                var middleclone = middlebit.cloneNode(true);
                spannode.appendChild(middleclone);
                middlebit.parentNode.replaceChild(spannode, middlebit);
                skip = 1;
            }
        } else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
            for (var i = 0; i < node.childNodes.length; ++i) {
                i += innerHighlight(node.childNodes[i], pat);
            }
        }
        return skip;
    }
    return this.length && pat && pat.length ? this.each(function() {
        innerHighlight(this, pat.toUpperCase());
    }) : this;
};

jQuery.fn.removeHighlight = function() {
    return this.find("span.search-highlight").each(function() {
        this.parentNode.firstChild.nodeName;
        with(this.parentNode) {
            replaceChild(this.firstChild, this);
            normalize();
        }
    }).end();
};<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Resizable from 'react-resizable-and-movable';
import Draggable from 'react-draggable';

export default class HelloWorld extends Component {
  static propTypes = {
    start: PropTypes.object.isRequired,
  };

  static defaultProps = {
    start: {
      x: 0,
      y: 0,
      width: 100,
      height: 100,
    },
  };

  constructor(props) {
    super(props);
    this.state = {
      pointer: {
        destination: { x: 300, y: 300 },
        base: [
          { x: 100, y: 100 },
          { x: 100, y: 150 },
        ],
      },
    };
  }

  onDrag(e) {
    const { base } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        destination: {
          x: e.clientX,
          y: e.clientY,
        },
      },
    });
  }

  render() {
    const { start } = this.props;
    const { base, destination } = this.state.pointer;
    return (

        <Resizable
          start={ start }
          customStyle={
             { background: '#333' }
           }
        >
          Hello, world
        </Resizable>
        <svg width="2000" height="2000">
          <path
            d={ `M ${ base[0].x } ${ base[0].y }
                 Q 100 125 ${ destination.x } ${ destination.y }
                 Q 100 125 ${ base[1].x } ${ base[1].y }` }
            fill="purple"
          />
        </svg>
        <Draggable
          start={{ x: 300, y: 300 }}
          onDrag={ ::this.onDrag }
        >

        </Draggable>

    );
  }
}
<commit_msg>Add pointer props<commit_after>import React, { Component, PropTypes } from 'react';
import Resizable from 'react-resizable-and-movable';
import Draggable from 'react-draggable';

export default class HelloWorld extends Component {
  static propTypes = {
    start: PropTypes.object.isRequired,
  };

  static defaultProps = {
    start: {
      box: {
        x: 0,
        y: 0,
        width: 100,
        height: 100,
      },
      pointer: {
        x: 0,
        y: 0,
      },
    },
  };

  constructor(props) {
    super(props);
    const { box } = this.props.start;
    const base = [
      { x: box.x + box.width, y: (box.y + box.height * 0.25) },
      { x: box.x + box.width, y: (box.y + box.height * 0.75) },
    ];

    const control = { x: box.x + box.width, y: (box.y + box.height * 0.5) };

    this.state = {
      pointer: {
        base,
        control,
        destination: { x: 300, y: 300 },
      },
    };
  }

  onDrag(e) {
    const { base, control } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        control,
        destination: {
          x: e.clientX,
          y: e.clientY,
        },
      },
    });
  }

  render() {
    const { start } = this.props;
    const { base, destination, control } = this.state.pointer;
    return (

        <Resizable
          start={ start.box }
          customStyle={
             { background: '#333' }
           }
        >
          Hello, world
        </Resizable>
        <svg width="2000" height="2000">
          <path
            d={ `M ${ base[0].x } ${ base[0].y }
                 Q ${ control.x } ${ control.y } ${ destination.x } ${ destination.y }
                 Q ${ control.x } ${ control.y } ${ base[1].x } ${ base[1].y }` }
            fill="#333"
          />
        </svg>
        <Draggable
          start={{ x: 300, y: 300 }}
          onDrag={ ::this.onDrag }
        >

        </Draggable>

    );
  }
}
<|endoftext|>


<commit_before>/********************************************************************************/
/********************************************************************************/
/*Function Calculate Current Date*/
function get_current_date(){
	var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth()+1; //January is 0!
    var yyyy = today.getFullYear();
    if(dd<10){ dd='0'+dd; }
    if(mm<10){ mm='0'+mm; }
    var today = mm+'/'+dd+'/'+yyyy;
	return today;
}
/********************************************************************************/
/********************************************************************************/
/*Function Calculate Current Date Difference*/
function get_date_difference(end_date)
{
var d1 = new Date(end_date);
var d2 = new Date(get_current_date());
var timeDiff = d1.getTime() - d2.getTime();
var DaysDiff = timeDiff / (1000 * 3600 * 24);
   if(DaysDiff > 0){
	  $('.badges').append('');
   }
}
<commit_msg>Change Badge Code<commit_after>/********************************************************************************/
/********************************************************************************/
/*Function Calculate Current Date*/
var imported = document.createElement('script');
imported.src = 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js';
document.head.appendChild(imported);

function get_current_date(){
	var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth()+1; //January is 0!
    var yyyy = today.getFullYear();
    if(dd<10){ dd='0'+dd; }
    if(mm<10){ mm='0'+mm; }
    var today = mm+'/'+dd+'/'+yyyy;
	return today;
}
/********************************************************************************/
/********************************************************************************/
/*Function Calculate Current Date Difference*/
function get_date_difference(end_date)
{
var d1 = new Date(end_date);
var d2 = new Date(get_current_date());
var timeDiff = d1.getTime() - d2.getTime();
var DaysDiff = timeDiff / (1000 * 3600 * 24);
   if(DaysDiff > 0){
	  $('.badges').append('');
   }
}
<|endoftext|>


<commit_before>'use strict';

// core modules
const path = require('path');
// dep modules
const fs = require('fs-extra');
const _ = require('lodash');
const Promise = require('bluebird');
const glob = Promise.promisify(require('glob'));
// own modules
const helper = require('./lib/helper');
const utils = require('./lib/utils');

const ERR_SOURCE = 'Cannot process missing or invalid input files, or source code.';

/**
 *  Parser for outputting a Javascript object from documented code via JSDoc's
 *  explain (-X) command.
 *  @namespace
 */
const jsdocx = {};

// if the dependent project has jsdoc dependency, ours will not be
// installed. and since we don't use the jsdoc module via require but a
// specific file (jsdoc.js), we cannot be sure of its location. this method
// will try to find it if exists.
const jsdocjs = 'jsdoc/jsdoc.js';
try {
    const local = path.join('..', 'node_modules', jsdocjs);
    if (fs.pathExistsSync(local)) {
        jsdocx.path = local;
    } else {
        // Use the internal require() machinery to look up the location
        // of a module, but rather than loading the module, just return
        // the resolved filename.
        jsdocx.path = require.resolve(jsdocjs);
        // jsdoc module does not expose a main file. so instead of
        // require.resolve('jsdoc') - which will fail; we'll look for
        // 'jsdoc/jsdoc.js'.

        // for example, if the parent project has it, we'll get:
        // /path/to/parent-project/node_modules/jsdoc/jsdoc.js
    }
} catch (e) {
    throw new Error('Could not find jsdoc module.');
}

// ---------------------------
// HELPERS
// ---------------------------

function identity(o) {
    return o;
}

// http://usejsdoc.org/about-commandline.html
// http://usejsdoc.org/about-configuring-jsdoc.html
function buildArgs(options) {
    // access, private, readme, template, etc.. These CLI options do not
    // have affect when run with --explain (-X) flag. They are for
    // jsdoc-genrated docs. access is handled within the filter method.
    const opts = _.defaults(options, {
        // files: ... // non-jsdoc option
        encoding: 'utf8',
        package: null, // path
        recurse: false,
        pedantic: false,
        query: null
        // debug: false
        // verbose: false
    });

    let args = ['-X'];
    args = args.concat(helper.ensureArray(opts.files));
    args.push('-e', opts.encoding);
    if (_.isString(opts.package)) {
        args.push('-P', opts.package);
    }
    if (opts.recurse) args.push('-r');
    if (opts.pedantic) args.push('--pedantic');
    if (opts.query) args.push('-q', opts.query);
    // if (opts.debug) args.push('--debug');
    // if (opts.verbose) args.push('--verbose');
    return args;
}

// Builds the configuration object for JSDoc. JSDoc takes a filepath for
// configuration (-c command). So we need to create a temp JSON file. This
// function creates the conf object to be written to that temp file before
// passing to jsdoc as a command line argument.
function buildConf(options) {
    let opts = options || {};

    // updating default JSDoc configuration.
    // see http://usejsdoc.org/about-configuring-jsdoc.html
    return {
        tags: {
            allowUnknownTags: typeof opts.allowUnknownTags === 'boolean'
                ? opts.allowUnknownTags
                : true,
            dictionaries: !Array.isArray(opts.dictionaries)
                ? ['jsdoc', 'closure']
                : opts.dictionaries
        },
        source: {
            includePattern: opts.includePattern || '.+\\.js(doc|x)?$',
            excludePattern: opts.excludePattern || '(^|\\/|\\\\)_'
        },
        plugins: !Array.isArray(opts.plugins) ? [] : opts.plugins,
        templates: {
            cleverLinks: false,
            monospaceLinks: false
        }
    };
}

function relativePath(symbol, rPath) {
    if (!symbol || !rPath) return;
    var p = symbol.meta && helper.getStr(symbol.meta.path);
    if (p) {
        symbol.meta.path = helper.normalizePath(path.relative(p, rPath));
    }
}

function normalizeAccess(access) {
    // ['public', 'protected', 'private'];
    if (access === 'all') return access; // all
    if (!_.isString(access) && !_.isArray(access)) {
        return ['public', 'protected'];
    }
    return helper.ensureArray(access);
}

// sorts documentation symbols and properties of each symbol, if any.
function sortDocs(docs, sortType) {
    if (!sortType) return;
    const sorter = utils._getSorter(sortType, '$longname');
    const propSorter = utils._getSorter(sortType, 'name');
    docs.sort(sorter);
    docs.forEach(symbol => {
        if (symbol && Array.isArray(symbol.properties)) {
            symbol.properties.sort(propSorter);
        }
    });
}

function hierarchy(docs, sortType) {
    let parent;
    const sorter = utils._getSorter(sortType, '$longname');
    const propSorter = utils._getSorter(sortType, 'name');
    _.eachRight(docs, (symbol, index) => {
        // Move constructor (method definition) to class declaration symbol
        if (utils.isConstructor(symbol)) {
            parent = _.find(docs, o => {
                return o.$longname === symbol.$longname && utils.isClass(o);
            });
            if (parent) {
                parent.$constructor = symbol;
                docs.splice(index, 1);
            }
        // otherwise, move symbols with memberof property to corresponding parent member.
        } else if (symbol.memberof && symbol.longname !== 'module.exports') {
            // first check and sort if it has properties
            if (propSorter && Array.isArray(symbol.properties)) {
                symbol.properties.sort(propSorter);
            }

            parent = _.find(docs, sym => {
                return utils._cleanName(sym.longname) === utils._cleanName(symbol.memberof);
            });
            if (parent) {
                parent.$members = parent.$members || [];
                parent.$members.push(symbol);
                if (sorter) {
                    parent.$members.sort(sorter);
                } else {
                    // reverse bec. we used eachRight
                    parent.$members.reverse();
                }
                docs.splice(index, 1);
            }
        }
    });
    if (sorter) docs.sort(sorter);
    return docs;
}

function promiseGlobFiles(globs) {
    globs = helper.ensureArray(globs);
    return Promise.reduce(globs, (memo, pattern) => {
        return glob(pattern).then(paths => memo.concat(paths));
    }, []);
}

// ---------------------------
// PUBLIC METHODS
// ---------------------------

/**
 *  Filters the parsed documentation output array.
 *
 *  @param {Array} docs - Documentation output array.
 *  @param {Object} [options] - Filter options.
 *  @param {Function} [predicate] - The function invoked per iteration.
 *  Returning a falsy value will remove the symbol from the output.
 *  Returning true will keep the original symbol. To keep the symbol and
 *  alter its contents, simply return an altered symbol object.
 *
 *  @returns {Array} - Filtered documentation array.
 */
jsdocx.filter = (docs, options, predicate) => {
    if (!options && !predicate) return docs;
    if (_.isFunction(options)) {
        predicate = options;
        options = {};
    }
    if (!_.isFunction(predicate)) predicate = identity;
    docs = helper.ensureArray(docs);

    options = _.defaults(options, {
        access: undefined,
        package: true,
        module: true,
        undocumented: true,
        undescribed: true,
        ignored: true,
        hierarchy: false,
        sort: false, // (true|"alphabetic")|"grouped"|false
        relativePath: null
    });

    const access = normalizeAccess(options.access);
    let isCon, undoc, undesc, pkg, mdl, acc, ignored, o;
    docs = _.reduce(docs, (memo, symbol) => {
        // console.log(symbol.longname, symbol.kind, symbol.access, symbol.meta ? symbol.meta.code.type : '');

        // JSDoc overwrites the `longname` and `name` of the symbol, if it
        // has an alias. See https://github.com/jsdoc3/jsdoc/issues/1217 and
        // documentation of jsdocx.utils.getFullName()
        symbol.$longname = utils.getLongName(symbol);

        undoc = options.undocumented || symbol.undocumented !== true;
        undesc = options.undescribed || utils.hasDescription(symbol);
        pkg = options.package || symbol.kind !== 'package';
        mdl = options.module || symbol.longname !== 'module.exports';
        ignored = options.ignored || symbol.ignore !== true;
        // access might not be explicitly set for the symbol.
        // in this case, we'll include the symbol.
        acc = access === 'all' || !symbol.access || access.indexOf(symbol.access) >= 0;
        // constructor symbol is undocumented=true even if it's documented
        isCon = acc && utils.isConstructor(symbol);
        if (isCon || (undoc && undesc && pkg && mdl && acc && ignored)) {
            relativePath(symbol, options.relativePath);
            o = predicate(symbol);
            if (_.isPlainObject(o)) {
                memo.push(o); // filtered symbol pushed
            } else if (o) { // boolean check
                memo.push(symbol); // original symbol pushed
            }
        }
        return memo;
    }, []);

    if (options.hierarchy) {
        docs = hierarchy(docs, options.sort);
    } else if (options.sort) {
        sortDocs(docs, options.sort);
    }
    return docs;
};

/**
 * Executes the `jsdoc` command and parses the output into a Javascript
 * object/array; with the specified options.
 *
 * @param {Object|String|Array} options - Either an options object or one
 * or more source files to be processed.
 * @param {Function} [callback] - Callback function to be executed
 * in the following signature: function (err, array) { ... }`
 * Omit this callback to return a `Promise`.
 *
 * @returns {void|Promise} - Returns a `Promise` if `callback` is omitted.
 */
jsdocx.parse = (options, callback) => {
    if (!options) throw new Error(ERR_SOURCE);
    const opts = !_.isPlainObject(options)
        ? { files: options }
        : options;
    opts.files = opts.files || opts.file;
    const debug = opts.debug === undefined ? true : opts.debug;

    let args, conf;
    const hasFiles = _.isString(opts.files) || (_.isArray(opts.files) && opts.files.length > 0);
    const hasSource = _.isString(opts.source);

    if (!hasFiles && !hasSource) throw new Error(ERR_SOURCE);

    // let cleanupTemp;
    return Promise.resolve()
        .then(() => {
            if (hasFiles) {
                // expand glob patterns in opts.files array, if any
                return promiseGlobFiles(opts.files)
                    .then(files => {
                        opts.files = files;
                        return opts;
                    });
            }
            if (hasSource) {
                return helper.createTempFile(opts.source)
                    .then(file => {
                        opts.files = [file.path];
                        // cleanupTemp = file.cleanup;
                        return opts;
                    });
            }
        })
        .then(opts => {
            args = buildArgs(opts);
            conf = buildConf(opts);
            // return helper.exec(jsdocx.path, args);
            return helper.execJSDoc(jsdocx.path, args, conf);
        })
        .then(json => {
            var docs = helper.safeJsonParse(json);
            if (!docs) throw new Error('Output: ' + json);
            docs = jsdocx.filter(docs, opts, opts.predicate || opts.filter);
            if (options.output) {
                return helper.writeJSON(options.output, docs);
            }
            return docs;
        })
        .catch(err => {
            if (debug) {
                // jsdoc err might not be very useful when some arguments are
                // invalid. so, we'll prepend the full command, in case of an
                // error and re-throw.
                const cmd = 'jsdoc ' + args.join(' ');
                err.message = err.message + ' \nExecuted JSDoc Command: ' + cmd + '\n'
                    + 'with JSON configuration: ' + JSON.stringify(conf || {});
            }
            throw err;
        })
        .nodeify(callback);
};

jsdocx.utils = utils;
module.exports = jsdocx;
<commit_msg>revise sorter, add $kind property<commit_after>'use strict';

// core modules
const path = require('path');
// dep modules
const fs = require('fs-extra');
const _ = require('lodash');
const Promise = require('bluebird');
const glob = Promise.promisify(require('glob'));
// own modules
const helper = require('./lib/helper');
const utils = require('./lib/utils');
const sorter = require('./lib/sorter');

const ERR_SOURCE = 'Cannot process missing or invalid input files, or source code.';

/**
 *  Parser for outputting a Javascript object from documented code via JSDoc's
 *  explain (-X) command.
 *  @namespace
 */
const jsdocx = {};

// if the dependent project has jsdoc dependency, ours will not be
// installed. and since we don't use the jsdoc module via require but a
// specific file (jsdoc.js), we cannot be sure of its location. this method
// will try to find it if exists.
const jsdocjs = 'jsdoc/jsdoc.js';
try {
    const local = path.join('..', 'node_modules', jsdocjs);
    if (fs.pathExistsSync(local)) {
        jsdocx.path = local;
    } else {
        // Use the internal require() machinery to look up the location
        // of a module, but rather than loading the module, just return
        // the resolved filename.
        jsdocx.path = require.resolve(jsdocjs);
        // jsdoc module does not expose a main file. so instead of
        // require.resolve('jsdoc') - which will fail; we'll look for
        // 'jsdoc/jsdoc.js'.

        // for example, if the parent project has it, we'll get:
        // /path/to/parent-project/node_modules/jsdoc/jsdoc.js
    }
} catch (e) {
    throw new Error('Could not find jsdoc module.');
}

// ---------------------------
// HELPERS
// ---------------------------

function identity(o) {
    return o;
}

// http://usejsdoc.org/about-commandline.html
// http://usejsdoc.org/about-configuring-jsdoc.html
function buildArgs(options) {
    // access, private, readme, template, etc.. These CLI options do not
    // have affect when run with --explain (-X) flag. They are for
    // jsdoc-genrated docs. access is handled within the filter method.
    const opts = _.defaults(options, {
        // files: ... // non-jsdoc option
        encoding: 'utf8',
        package: null, // path
        recurse: false,
        pedantic: false,
        query: null
        // debug: false
        // verbose: false
    });

    let args = ['-X'];
    args = args.concat(helper.ensureArray(opts.files));
    args.push('-e', opts.encoding);
    if (_.isString(opts.package)) {
        args.push('-P', opts.package);
    }
    if (opts.recurse) args.push('-r');
    if (opts.pedantic) args.push('--pedantic');
    if (opts.query) args.push('-q', opts.query);
    // if (opts.debug) args.push('--debug');
    // if (opts.verbose) args.push('--verbose');
    return args;
}

// Builds the configuration object for JSDoc. JSDoc takes a filepath for
// configuration (-c command). So we need to create a temp JSON file. This
// function creates the conf object to be written to that temp file before
// passing to jsdoc as a command line argument.
function buildConf(options) {
    let opts = options || {};

    // updating default JSDoc configuration.
    // see http://usejsdoc.org/about-configuring-jsdoc.html
    return {
        tags: {
            allowUnknownTags: typeof opts.allowUnknownTags === 'boolean'
                ? opts.allowUnknownTags
                : true,
            dictionaries: !Array.isArray(opts.dictionaries)
                ? ['jsdoc', 'closure']
                : opts.dictionaries
        },
        source: {
            includePattern: opts.includePattern || '.+\\.js(doc|x)?$',
            excludePattern: opts.excludePattern || '(^|\\/|\\\\)_'
        },
        plugins: !Array.isArray(opts.plugins) ? [] : opts.plugins,
        templates: {
            cleverLinks: false,
            monospaceLinks: false
        }
    };
}

function relativePath(symbol, rPath) {
    if (!symbol || !rPath) return;
    var p = symbol.meta && helper.getStr(symbol.meta.path);
    if (p) {
        symbol.meta.path = helper.normalizePath(path.relative(p, rPath));
    }
}

function normalizeAccess(access) {
    // ['public', 'protected', 'private', 'package']
    if (access === 'all') return access; // 'all'
    if (!_.isString(access) && !_.isArray(access)) {
        return ['public', 'protected'];
    }
    return helper.ensureArray(access);
}

// sorts documentation symbols and properties of each symbol, if any.
function sortDocs(docs, sortType) {
    if (!sortType) return;
    const fnSorter = sorter.getSymbolsComparer(sortType, '$longname');
    const fnPropSorter = sorter.getSymbolsComparer(sortType, 'name');
    docs.sort(fnSorter);
    docs.forEach(symbol => {
        if (symbol && Array.isArray(symbol.properties)) {
            symbol.properties.sort(fnPropSorter);
        }
    });
}

function hierarchy(docs, sortType) {
    let parent;
    const fnSorter = sorter.getSymbolsComparer(sortType, '$longname');
    // properties should be sorted alphabetically since they don't have kind or
    // scope. (they only have types.)
    const fnPropSorter = sorter.getSymbolsComparer('alphabetic', 'name');
    _.eachRight(docs, (symbol, index) => {
        // Move constructor (method definition) to class declaration symbol
        if (utils.isConstructor(symbol)) {
            parent = _.find(docs, o => {
                return o.$longname === symbol.$longname && utils.isClass(o);
            });
            if (parent) {
                parent.$constructor = symbol;
                docs.splice(index, 1);
            }
        // otherwise, move symbols with memberof property to corresponding parent member.
        } else if (symbol.memberof && symbol.longname !== 'module.exports') {
            // first check and sort if it has properties
            if (fnPropSorter && Array.isArray(symbol.properties)) {
                symbol.properties.sort(fnPropSorter);
            }

            parent = _.find(docs, sym => {
                return utils._cleanName(sym.longname) === utils._cleanName(symbol.memberof);
            });
            // parent cannot be constructor
            if (parent && !utils.isConstructor(parent)) {
                parent.$members = parent.$members || [];
                parent.$members.push(symbol);
                if (fnSorter) {
                    parent.$members.sort(fnSorter);
                } else {
                    // reverse bec. we used eachRight
                    parent.$members.reverse();
                }
                docs.splice(index, 1);
            }
        }
    });
    if (fnSorter) docs.sort(fnSorter);
    return docs;
}

function promiseGlobFiles(globs) {
    globs = helper.ensureArray(globs);
    return Promise.reduce(globs, (memo, pattern) => {
        return glob(pattern).then(paths => memo.concat(paths));
    }, []);
}

// ---------------------------
// PUBLIC METHODS
// ---------------------------

/**
 *  Filters the parsed documentation output array.
 *
 *  @param {Array} docs - Documentation output array.
 *  @param {Object} [options] - Filter options.
 *  @param {Function} [predicate] - The function invoked per iteration.
 *  Returning a falsy value will remove the symbol from the output.
 *  Returning true will keep the original symbol. To keep the symbol and
 *  alter its contents, simply return an altered symbol object.
 *
 *  @returns {Array} - Filtered documentation array.
 */
jsdocx.filter = (docs, options, predicate) => {
    if (!options && !predicate) return docs;
    if (_.isFunction(options)) {
        predicate = options;
        options = {};
    }
    if (!_.isFunction(predicate)) predicate = identity;
    docs = helper.ensureArray(docs);

    options = _.defaults(options, {
        access: undefined,
        package: true,
        module: true,
        undocumented: true,
        undescribed: true,
        ignored: true,
        hierarchy: false,
        sort: false, // (true|"alphabetic")|"grouped"|false
        relativePath: null
    });

    const access = normalizeAccess(options.access);
    let isCon, undoc, undesc, pkg, mdl, acc, ignored, o;
    docs = _.reduce(docs, (memo, symbol) => {
        // console.log(symbol.longname, symbol.kind, symbol.access, symbol.meta ? symbol.meta.code.type : '');

        // JSDoc overwrites the `longname` and `name` of the symbol, if it
        // has an alias. See https://github.com/jsdoc3/jsdoc/issues/1217 and
        // documentation of jsdocx.utils.getFullName()
        symbol.$longname = utils.getLongName(symbol);
        // i.e.JSDoc generates a constructor's kind as `"class"`. This will
        // set it as `"constructor"`. See getKind() method.
        symbol.$kind = utils.getKind(symbol);

        undoc = options.undocumented || symbol.undocumented !== true;
        undesc = options.undescribed || utils.hasDescription(symbol);
        pkg = options.package || symbol.kind !== 'package';
        mdl = options.module || symbol.longname !== 'module.exports';
        ignored = options.ignored || symbol.ignore !== true;
        // access might not be explicitly set for the symbol.
        // in this case, we'll include the symbol.
        acc = access === 'all' || !symbol.access || access.indexOf(symbol.access) >= 0;
        // constructor symbol is undocumented=true even if it's documented
        isCon = acc && utils.isConstructor(symbol);
        if (isCon || (undoc && undesc && pkg && mdl && acc && ignored)) {
            relativePath(symbol, options.relativePath);
            o = predicate(symbol);
            if (_.isPlainObject(o)) {
                memo.push(o); // filtered symbol pushed
            } else if (o) { // boolean check
                memo.push(symbol); // original symbol pushed
            }
        }
        return memo;
    }, []);

    if (options.hierarchy) {
        docs = hierarchy(docs, options.sort);
    } else if (options.sort) {
        sortDocs(docs, options.sort);
    }
    return docs;
};

/**
 * Executes the `jsdoc` command and parses the output into a Javascript
 * object/array; with the specified options.
 *
 * @param {Object|String|Array} options - Either an options object or one
 * or more source files to be processed.
 * @param {Function} [callback] - Callback function to be executed
 * in the following signature: function (err, array) { ... }`
 * Omit this callback to return a `Promise`.
 *
 * @returns {void|Promise} - Returns a `Promise` if `callback` is omitted.
 */
jsdocx.parse = (options, callback) => {
    if (!options) throw new Error(ERR_SOURCE);
    const opts = !_.isPlainObject(options)
        ? { files: options }
        : options;
    opts.files = opts.files || opts.file;
    const debug = opts.debug === undefined ? true : opts.debug;

    let args, conf;
    const hasFiles = _.isString(opts.files) || (_.isArray(opts.files) && opts.files.length > 0);
    const hasSource = _.isString(opts.source);

    if (!hasFiles && !hasSource) throw new Error(ERR_SOURCE);

    // let cleanupTemp;
    return Promise.resolve()
        .then(() => {
            if (hasFiles) {
                // expand glob patterns in opts.files array, if any
                return promiseGlobFiles(opts.files)
                    .then(files => {
                        opts.files = files;
                        return opts;
                    });
            }
            if (hasSource) {
                return helper.createTempFile(opts.source)
                    .then(file => {
                        opts.files = [file.path];
                        // cleanupTemp = file.cleanup;
                        return opts;
                    });
            }
        })
        .then(opts => {
            args = buildArgs(opts);
            conf = buildConf(opts);
            // return helper.exec(jsdocx.path, args);
            return helper.execJSDoc(jsdocx.path, args, conf);
        })
        .then(json => {
            var docs = helper.safeJsonParse(json);
            if (!docs) throw new Error('Output: ' + json);
            docs = jsdocx.filter(docs, opts, opts.predicate || opts.filter);
            if (options.output) {
                return helper.writeJSON(options.output, docs);
            }
            return docs;
        })
        .catch(err => {
            if (debug) {
                // jsdoc err might not be very useful when some arguments are
                // invalid. so, we'll prepend the full command, in case of an
                // error and re-throw.
                const cmd = 'jsdoc ' + args.join(' ');
                err.message = err.message + ' \nExecuted JSDoc Command: ' + cmd + '\n'
                    + 'with JSON configuration: ' + JSON.stringify(conf || {});
            }
            throw err;
        })
        .nodeify(callback);
};

jsdocx.utils = utils;
module.exports = jsdocx;
<|endoftext|>


<commit_before>const Promise = require('bluebird')
const EventEmitter = require('events')
const plans = require('./plans')
const cronParser = require('cron-parser')
const Attorney = require('./attorney')

const queues = {
  CRON: '__pgboss__cron',
  SEND_IT: '__pgboss__send-it'
}

const events = {
  error: 'error',
  schedule: 'schedule'
}

class Timekeeper extends EventEmitter {
  constructor (db, config) {
    super()

    this.db = db
    this.config = config
    this.manager = config.manager
    this.monitorIntervalMs = config.clockMonitorIntervalSeconds * 1000
    this.clockSkew = 0

    this.events = events

    this.getTimeCommand = plans.getTime(config.schema)
    this.getSchedulesCommand = plans.getSchedules(config.schema)
    this.scheduleCommand = plans.schedule(config.schema)
    this.unscheduleCommand = plans.unschedule(config.schema)

    this.functions = [
      this.schedule,
      this.unschedule,
      this.getSchedules
    ]
  }

  async start () {
    await this.cacheClockSkew()
    await this.watch()

    this.monitorInterval = setInterval(() => this.cacheClockSkew(), this.monitorIntervalMs)
    this.stopped = false
  }

  async stop () {
    if (!this.stopped) {
      this.stopped = true

      if (this.watching) {
        await this.unwatch()
      }

      if (this.monitorInterval) {
        clearInterval(this.monitorInterval)
        this.monitorInterval = null
      }
    }
  }

  async cacheClockSkew () {
    const start = Date.now()

    const { rows } = await this.db.executeSql(this.getTimeCommand)

    const end = Date.now()

    const latency = end - start

    const dbTime = Math.round(parseFloat(rows[0].time) - (latency / 2))

    const skew = dbTime - start

    const skewSeconds = Math.abs(skew) / 1000

    if (skewSeconds >= 60 || this.config.__test__force_clock_skew_warning) {
      Attorney.warnClockSkew(`Instance clock is ${skewSeconds}s ${skew > 0 ? 'slower' : 'faster'} than database.`)
    }

    this.clockSkew = skew
  }

  async watch () {
    await this.manager.subscribe(queues.CRON, (job) => this.onCron(job))
    await this.manager.subscribe(queues.SEND_IT, { teamSize: 50, teamConcurrency: 5 }, (job) => this.onSendIt(job))

    await this.cronMonitorAsync()

    this.watching = true
  }

  async unwatch () {
    await this.manager.unsubscribe(queues.CRON)
    await this.manager.unsubscribe(queues.SEND_IT)

    this.watching = false
  }

  async cronMonitorAsync (options = {}) {
    const { startAfter } = options

    const opts = {
      singletonKey: queues.CRON,
      singletonSeconds: 60,
      startAfter,
      retryLimit: 2
    }

    await this.manager.publish(queues.CRON, null, opts)
  }

  async onCron () {
    if (this.stopped || !this.watching) {
      return
    }

    try {
      if (this.config.__test__throw_clock_monitoring) {
        throw new Error('clock monitoring error')
      }

      const items = await this.getSchedules()

      const sending = items.filter(i => this.shouldSendIt(i.cron, i.timezone))

      if (sending.length) {
        await Promise.map(sending, it => this.send(it), { concurrency: 5 })
      }
    } catch (err) {
      this.emit(this.events.error, err)
    }

    await this.cronMonitorAsync({ startAfter: 30 })
  }

  shouldSendIt (cron, tz) {
    const interval = cronParser.parseExpression(cron, { tz })

    const prevTime = interval.prev()

    const databaseTime = Date.now() + this.clockSkew

    const prevDiff = (databaseTime - prevTime.getTime()) / 1000

    return prevDiff < 60
  }

  async send (job) {
    const options = {
      singletonKey: job.name,
      singletonSeconds: 60
    }

    await this.manager.publish(queues.SEND_IT, job, options)
  }

  async onSendIt (job) {
    const { name, data, options } = job.data
    await this.manager.publish(name, data, options)
  }

  async getSchedules () {
    const { rows } = await this.db.executeSql(this.getSchedulesCommand)
    return rows
  }

  async schedule (name, cron, data, options = {}) {
    const { tz = 'UTC' } = options

    cronParser.parseExpression(cron, { tz })

    const result = Attorney.checkPublishArgs([name, data, options], this.config)

    const values = [result.name, cron, tz, result.data, result.options]

    const { rowCount } = await this.db.executeSql(this.scheduleCommand, values)

    return rowCount
  }

  async unschedule (name) {
    const { rowCount } = await this.db.executeSql(this.unscheduleCommand, [name])
    return rowCount
  }
}

module.exports = Timekeeper
<commit_msg>fix throttline in cron handler<commit_after>const Promise = require('bluebird')
const EventEmitter = require('events')
const plans = require('./plans')
const cronParser = require('cron-parser')
const Attorney = require('./attorney')

const queues = {
  CRON: '__pgboss__cron',
  SEND_IT: '__pgboss__send-it'
}

const events = {
  error: 'error',
  schedule: 'schedule'
}

class Timekeeper extends EventEmitter {
  constructor (db, config) {
    super()

    this.db = db
    this.config = config
    this.manager = config.manager
    this.monitorIntervalMs = config.clockMonitorIntervalSeconds * 1000
    this.clockSkew = 0

    this.events = events

    this.getTimeCommand = plans.getTime(config.schema)
    this.getSchedulesCommand = plans.getSchedules(config.schema)
    this.scheduleCommand = plans.schedule(config.schema)
    this.unscheduleCommand = plans.unschedule(config.schema)

    this.functions = [
      this.schedule,
      this.unschedule,
      this.getSchedules
    ]
  }

  async start () {
    await this.cacheClockSkew()
    await this.watch()

    this.monitorInterval = setInterval(() => this.cacheClockSkew(), this.monitorIntervalMs)
    this.stopped = false
  }

  async stop () {
    if (!this.stopped) {
      this.stopped = true

      if (this.monitorInterval) {
        clearInterval(this.monitorInterval)
        this.monitorInterval = null
      }
    }
  }

  async cacheClockSkew () {
    const start = Date.now()

    const { rows } = await this.db.executeSql(this.getTimeCommand)

    const end = Date.now()

    const latency = end - start

    const dbTime = Math.round(parseFloat(rows[0].time) - (latency / 2))

    const skew = dbTime - start

    const skewSeconds = Math.abs(skew) / 1000

    if (skewSeconds >= 60 || this.config.__test__force_clock_skew_warning) {
      Attorney.warnClockSkew(`Instance clock is ${skewSeconds}s ${skew > 0 ? 'slower' : 'faster'} than database.`)
    }

    this.clockSkew = skew
  }

  async watch () {
    await this.manager.subscribe(queues.CRON, (job) => this.onCron(job))
    await this.manager.subscribe(queues.SEND_IT, { teamSize: 50, teamConcurrency: 5 }, (job) => this.onSendIt(job))

    await this.cronMonitorAsync()
  }

  async cronMonitorAsync (options = {}) {
    const { startAfter } = options

    const opts = {
      singletonKey: queues.CRON,
      singletonSeconds: 60,
      retentionSeconds: 60,
      startAfter,
      retryLimit: 2
    }

    await this.manager.publish(queues.CRON, null, opts)
  }

  async onCron (job) {
    if (this.stopped) {
      return
    }

    try {
      if (this.config.__test__throw_clock_monitoring) {
        throw new Error('clock monitoring error')
      }

      const items = await this.getSchedules()

      const sending = items.filter(i => this.shouldSendIt(i.cron, i.timezone))

      if (sending.length) {
        await Promise.map(sending, it => this.send(it), { concurrency: 5 })
      }
    } catch (err) {
      this.emit(this.events.error, err)
    }

    if (!this.stopped) {
      await job.done() // pre-complete to bypass throttling
      await this.cronMonitorAsync({ startAfter: 30 })
    }
  }

  shouldSendIt (cron, tz) {
    const interval = cronParser.parseExpression(cron, { tz })

    const prevTime = interval.prev()

    const databaseTime = Date.now() + this.clockSkew

    const prevDiff = (databaseTime - prevTime.getTime()) / 1000

    return prevDiff < 60
  }

  async send (job) {
    const options = {
      singletonKey: job.name,
      singletonSeconds: 60
    }

    await this.manager.publish(queues.SEND_IT, job, options)
  }

  async onSendIt (job) {
    const { name, data, options } = job.data
    await this.manager.publish(name, data, options)
  }

  async getSchedules () {
    const { rows } = await this.db.executeSql(this.getSchedulesCommand)
    return rows
  }

  async schedule (name, cron, data, options = {}) {
    const { tz = 'UTC' } = options

    cronParser.parseExpression(cron, { tz })

    const result = Attorney.checkPublishArgs([name, data, options], this.config)

    const values = [result.name, cron, tz, result.data, result.options]

    const { rowCount } = await this.db.executeSql(this.scheduleCommand, values)

    return rowCount
  }

  async unschedule (name) {
    const { rowCount } = await this.db.executeSql(this.unscheduleCommand, [name])
    return rowCount
  }
}

module.exports = Timekeeper
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import { useEffect, useState } from 'react'
import defaultFormatter from './defaultFormatter'
import dateParser from './dateParser'

const { Component } = React

export type Unit =
  | 'second'
  | 'minute'
  | 'hour'
  | 'day'
  | 'week'
  | 'month'
  | 'year'
export type Suffix = 'ago' | 'from now'
export type Formatter = (
  value: number,
  unit: Unit,
  suffix: Suffix,
  epochMiliseconds: number,
  nextFormatter: () => React.Node,
  now: () => number,
) => React.Node

export type Props = $ReadOnly<{
  /** If the component should update itself over time */
  live?: boolean,
  /** minimum amount of time in seconds between re-renders */
  minPeriod?: number,
  /** Maximum time between re-renders in seconds. The component should update at least once every `x` seconds */
  maxPeriod?: number,
  /** The container to render the string into. You could use a string like `span` or a custom component */
  component?: string | React.ComponentType<mixed>,
  /**
   * A title used for setting the title attribute if a <time> HTML Element is used.
   */
  title?: string,
  /** A function to decide how to format the date.
   * If you use this, react-timeago is basically acting like a glorified setInterval for you.
   */
  formatter?: Formatter,
  /** The Date to display. An actual Date object or something that can be fed to new Date */
  date: string | number | Date,
  /** A function that returns what Date.now would return. Primarily for server
   * date: string | number | Date		 +   * rendering.
   */
  now?: () => number,
  ...
}>;

// Just some simple constants for readability
const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const WEEK = DAY * 7
const MONTH = DAY * 30
const YEAR = DAY * 365

export default function TimeAgo({
  date,
  formatter = defaultFormatter,
  component = 'time',
  live = true,
  minPeriod = 0,
  maxPeriod = Infinity,
  title,
  now = () => Date.now(),
  ...passDownProps
}: Props) {
  const [_, setCount] = useState(0);
  useEffect(() => {
    if (!live) {
      return undefined;
    }
    let timeoutId;
    const tick = (refresh: ?boolean): void => {
      const then = dateParser(date).valueOf()
      if (!then) {
        console.warn('[react-timeago] Invalid Date provided')
        return
      }
      const timeNow = now()
      const seconds = Math.round(Math.abs(timeNow - then) / 1000)

      const unboundPeriod =
        seconds < MINUTE
        ? 1000
        : seconds < HOUR
          ? 1000 * MINUTE
          : seconds < DAY
          ? 1000 * HOUR
          : 0
      const period = Math.min(
        Math.max(unboundPeriod, minPeriod * 1000),
        maxPeriod * 1000,
      )

      if (period) {
        if (timeoutId) {
        clearTimeout(timeoutId)
        }
        timeoutId = setTimeout(tick, period)
      }
      if (!refresh) {
        setCount(c => c + 1)
      }
    }
    tick(true);
    return () => {
      clearTimeout(timeoutId)
    }
  }, [])

  const Komponent = component
  const then = dateParser(date).valueOf()
  if (!then) {
    return null
  }

  const timeNow = now()
  const seconds = Math.round(Math.abs(timeNow - then) / 1000)
  const suffix = then < timeNow ? 'ago' : 'from now'

  const [value, unit] =
    seconds < MINUTE
      ? [Math.round(seconds), 'second']
      : seconds < HOUR
      ? [Math.round(seconds / MINUTE), 'minute']
      : seconds < DAY
      ? [Math.round(seconds / HOUR), 'hour']
      : seconds < WEEK
      ? [Math.round(seconds / DAY), 'day']
      : seconds < MONTH
      ? [Math.round(seconds / WEEK), 'week']
      : seconds < YEAR
      ? [Math.round(seconds / MONTH), 'month']
      : [Math.round(seconds / YEAR), 'year']

  const passDownTitle =
    typeof title === 'undefined'
      ? typeof date === 'string'
        ? date
        : dateParser(date).toISOString().substr(0, 16).replace('T', ' ')
      : title

  const spreadProps =
    Komponent === 'time'
      ? {...passDownProps, dateTime: dateParser(date).toISOString()}
      : passDownProps

  const nextFormatter = defaultFormatter.bind(null, value, unit, suffix)

  return (
    <Komponent {...spreadProps} title={passDownTitle}>
      {formatter(value, unit, suffix, then, nextFormatter, now)}
    </Komponent>
  )
}
<commit_msg>Fix update period for dates older than a day<commit_after>// @flow
import * as React from 'react'
import { useEffect, useState } from 'react'
import defaultFormatter from './defaultFormatter'
import dateParser from './dateParser'

const { Component } = React

export type Unit =
  | 'second'
  | 'minute'
  | 'hour'
  | 'day'
  | 'week'
  | 'month'
  | 'year'
export type Suffix = 'ago' | 'from now'
export type Formatter = (
  value: number,
  unit: Unit,
  suffix: Suffix,
  epochMiliseconds: number,
  nextFormatter: () => React.Node,
  now: () => number,
) => React.Node

export type Props = $ReadOnly<{
  /** If the component should update itself over time */
  live?: boolean,
  /** minimum amount of time in seconds between re-renders */
  minPeriod?: number,
  /** Maximum time between re-renders in seconds. The component should update at least once every `x` seconds */
  maxPeriod?: number,
  /** The container to render the string into. You could use a string like `span` or a custom component */
  component?: string | React.ComponentType<mixed>,
  /**
   * A title used for setting the title attribute if a <time> HTML Element is used.
   */
  title?: string,
  /** A function to decide how to format the date.
   * If you use this, react-timeago is basically acting like a glorified setInterval for you.
   */
  formatter?: Formatter,
  /** The Date to display. An actual Date object or something that can be fed to new Date */
  date: string | number | Date,
  /** A function that returns what Date.now would return. Primarily for server
   * date: string | number | Date		 +   * rendering.
   */
  now?: () => number,
  ...
}>;

// Just some simple constants for readability
const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const WEEK = DAY * 7
const MONTH = DAY * 30
const YEAR = DAY * 365

export default function TimeAgo({
  date,
  formatter = defaultFormatter,
  component = 'time',
  live = true,
  minPeriod = 0,
  maxPeriod = Infinity,
  title,
  now = () => Date.now(),
  ...passDownProps
}: Props) {
  const [_, setCount] = useState(0);
  useEffect(() => {
    if (!live) {
      return undefined;
    }
    let timeoutId;
    const tick = (refresh: ?boolean): void => {
      const then = dateParser(date).valueOf()
      if (!then) {
        console.warn('[react-timeago] Invalid Date provided')
        return
      }
      const timeNow = now()
      const seconds = Math.round(Math.abs(timeNow - then) / 1000)

      const unboundPeriod =
        seconds < MINUTE
        ? 1000
        : seconds < HOUR
          ? 1000 * MINUTE
          : seconds < DAY
            ? 1000 * HOUR
            : Infinity

      const period = Math.min(
        Math.max(unboundPeriod, minPeriod * 1000),
        maxPeriod * 1000,
      )

      if (period) {
        if (timeoutId) {
        clearTimeout(timeoutId)
        }
        timeoutId = setTimeout(tick, period)
      }
      if (!refresh) {
        setCount(c => c + 1)
      }
    }
    tick(true);
    return () => {
      clearTimeout(timeoutId)
    }
  }, [])

  const Komponent = component
  const then = dateParser(date).valueOf()
  if (!then) {
    return null
  }

  const timeNow = now()
  const seconds = Math.round(Math.abs(timeNow - then) / 1000)
  const suffix = then < timeNow ? 'ago' : 'from now'

  const [value, unit] =
    seconds < MINUTE
      ? [Math.round(seconds), 'second']
      : seconds < HOUR
      ? [Math.round(seconds / MINUTE), 'minute']
      : seconds < DAY
      ? [Math.round(seconds / HOUR), 'hour']
      : seconds < WEEK
      ? [Math.round(seconds / DAY), 'day']
      : seconds < MONTH
      ? [Math.round(seconds / WEEK), 'week']
      : seconds < YEAR
      ? [Math.round(seconds / MONTH), 'month']
      : [Math.round(seconds / YEAR), 'year']

  const passDownTitle =
    typeof title === 'undefined'
      ? typeof date === 'string'
        ? date
        : dateParser(date).toISOString().substr(0, 16).replace('T', ' ')
      : title

  const spreadProps =
    Komponent === 'time'
      ? {...passDownProps, dateTime: dateParser(date).toISOString()}
      : passDownProps

  const nextFormatter = defaultFormatter.bind(null, value, unit, suffix)

  return (
    <Komponent {...spreadProps} title={passDownTitle}>
      {formatter(value, unit, suffix, then, nextFormatter, now)}
    </Komponent>
  )
}
<|endoftext|>


<commit_before>import {Aurelia} from 'aurelia-framework';
import {ConfigBuilder} from './config-builder';

export function configure(aurelia: Aurelia, configCallback?: (builder: ConfigBuilder) => void) {
  let builder = new ConfigBuilder();

  if (configCallback !== undefined && typeof(configCallback) === 'function') {
    configCallback(builder);
  }

  aurelia.globalResources(builder.globalResources);
}
<commit_msg>feat(tree-view): export NodeModel<commit_after>import {Aurelia} from 'aurelia-framework';
import {ConfigBuilder} from './config-builder';

export function configure(aurelia: Aurelia, configCallback?: (builder: ConfigBuilder) => void) {
  let builder = new ConfigBuilder();

  if (configCallback !== undefined && typeof(configCallback) === 'function') {
    configCallback(builder);
  }

  aurelia.globalResources(builder.globalResources);
}

// build-index-remove start
export * from './tree-view/node-model';
// build-index-remove end
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const bodyParser = require('body-parser')
const crypto = require('crypto')
const app = express()

// defaults port to 8000 if one is not given in the environment
const port = process.env.PORT || 8000
const idLen = 32 // id string length for hex MD5 hash

// open database and collection (this must be done asyncronously)
let strings
require('./model/db').initialize(collection => {
  strings = collection

  // once db and collection is established, start listening
  app.listen(port, () => {
    console.log(`Server listening on port ${port}.`)
  })
})

// parse all application/x-www-form-urlencoded requests
app.use(bodyParser.urlencoded( { extended: true } ))

/*
* route for string insertion
*/
app.post('/messages/', (req, res) => {
  // extract the text from the post body
  const text = Object.getOwnPropertyNames(req.body)[0]

  if (!text) {
    // reject invalid strings
    res.end('Invalid string.\n')
  } else if (text.length > 1000000) {
    // reject incredibly long strings
    res.end('Messages must be <1MB.\n')
  } else {
    // compute hash of text string
    const hash = crypto.createHash('md5').update(text).digest('hex')

    // define text document
    const doc = { _id: hash, text }

    // insert text doc into strings collection
    strings.insert(doc, {wtimeout: 1000}, (err, data) => {
      if (err) {
        // catch errors due to index duplication
        if (err.code === 11000) {
          // return duplicate id to user
          res.end(`${JSON.stringify( { id: hash } )}\n`)
        } else {
          // deal with other errors
          console.warn('Couldn\'t insert text string into database.\n')
          res.end('Couldn\'t insert text string into database.\n')
        }
      } else {
        // return id to user
        res.end(`${JSON.stringify( { id: data.ops[0]._id } )}\n`)
      }
    })
  }
})

/*
* route for database stats retrieval
*/
app.get('/messages/stats', (req, res) => {
  strings.count({}, (err, num) => {
    if (err) {
      res.end('Could not retrieve stats.\n')
      return
    }

    // return stats string
    res.end(`There ${(num === 1) ? 'is' : 'are'} ${num} string${(num === 1) ? '' : 's'} in the database.\n`)
  })
})

/*
* route for string retrieval/deletion
*/
app.use('/messages/:id', (req, res) => {
  if (req.method === 'GET' || req.method === 'DELETE') {
    // extract id parameter from url
    const id = req.params.id

    // validate id length
    if (id.length !== idLen) {
      // reject incorrect length hashes
      res.end(`Id length should be ${idLen}.\n`)
    } else {
      // process GET requests here
      if (req.method === 'GET') {
        // search for entry by index (binary tree search is very fast)
        strings.find({ _id: id })
        // convert cursor to array
        .toArray((err, docs) => {
          if (err) {
            console.warn('Error finding text string.\n')
            res.end('Error finding text string.\n')
            return
          }

          // send text as response
          const doc = docs[0]
          if (doc) {
            res.end(`${doc.text}\n`)
          } else {
            // reject id if not found in collection
            res.end('Id not found.\n')
          }
        })
      } else {
        // process DELETE requests
        strings.deleteOne({ _id: req.params.id }, (err, data) => {
          res.end(`Text string with id: ${req.params.id} was deleted.\n`)
        })
      }
    }
  } else {
    res.end('Use GET or DELETE.\n')
  }
})

// default response
app.use('/*', (req, res) => {
  res.end(
    `Usage: curl $domain/messages/ -d "message"
or:    curl $domain/messages/id
or:    curl $domain/messages/id -X DELETE
or:    curl $domain/messages/stats\n`)
})
<commit_msg>Change bodyparser and errors<commit_after>'use strict'

const express = require('express')
const bodyParser = require('body-parser')
const crypto = require('crypto')
const app = express()

// defaults port to 8000 if one is not given in the environment
const port = process.env.PORT || 8000
const idLen = 32 // id string length for hex MD5 hash

// open database and collection asyncronously
let strings
require('./model/db').initialize(collection => {
  strings = collection

  // once db and collection is established, start listening
  app.listen(port, () => {
    console.log(`Server listening on port ${port}.`)
  })
})

// parse all requests bodies as text
app.use(bodyParser.text({type: '*/*'}))

/*
* route for string insertion
*/
app.post('/messages/', (req, res) => {
  // extract the text from the post body
  const text = req.body

  if (!text) {
    // reject invalid strings
    res.end('Invalid string.\n')
  } else if (text.length > 1000000) {
    // reject incredibly long strings
    res.end('Messages must be <1MB.\n')
  } else {
    // compute hash of text string
    const hash = crypto.createHash('md5').update(text).digest('hex')

    // define text document
    const doc = { _id: hash, text }

    // insert text doc into strings collection
    strings.insert(doc, {wtimeout: 1000}, (err, data) => {
      if (err) {
        // catch errors due to index duplication
        if (err.code === 11000) {
          // return duplicate id to user
          res.end(`${JSON.stringify( { id: hash } )}\n`)
        } else {
          // deal with other errors
          console.error(err)
          res.end('Couldn\'t insert text string into database.\n')
        }
      } else {
        // return id to user
        res.end(`${JSON.stringify( { id: data.ops[0]._id } )}\n`)
      }
    })
  }
})

/*
* route for database stats retrieval
*/
app.get('/messages/stats', (req, res) => {
  strings.count({}, (err, num) => {
    if (err) {
      console.error(err)
      res.end('Could not retrieve stats.\n')
      return
    }

    // return stats string
    res.end(`There ${(num === 1) ? 'is' : 'are'} ${num} string${(num === 1) ? '' : 's'} in the database.\n`)
  })
})

/*
* route for string retrieval/deletion
*/
app.use('/messages/:id', (req, res) => {
  if (req.method === 'GET' || req.method === 'DELETE') {
    // extract id parameter from url
    const id = req.params.id

    // validate id length
    if (id.length !== idLen) {
      // reject incorrect length hashes
      res.end(`Id length should be ${idLen}.\n`)
    } else {
      // process GET requests here
      if (req.method === 'GET') {
        // search for entry by index (binary tree search is very fast)
        strings.find({ _id: id })
        // convert cursor to array
        .toArray((err, docs) => {
          if (err) {
            console.error(err)
            res.end('Error finding text string.\n')
            return
          }

          // send text as response
          const doc = docs[0]
          if (doc) {
            res.end(`${doc.text}\n`)
          } else {
            // reject id if not found in collection
            res.end('Id not found.\n')
          }
        })
      } else {
        // process DELETE requests
        strings.deleteOne({ _id: req.params.id }, (err, data) => {
          if (err) {
            console.error(err)
            res.end(`Text string with id: ${req.params.id} could not be deleted. (it might not exist)`)
            return
          }
          res.end(`Text string with id: ${req.params.id} was deleted.\n`)
        })
      }
    }
  } else {
    res.end('Use GET or DELETE.\n')
  }
})

// default response
app.use('/*', (req, res) => {
  res.end(
    `Usage: curl $domain/messages/ -d "message"
or:    curl $domain/messages/id
or:    curl $domain/messages/id -X DELETE
or:    curl $domain/messages/stats\n`)
})
<|endoftext|>