packages feed

hoogle-4.2: datadir/resources/hoogle.js

var embed = false; // are we running as an embedded search box
var instant = false; // should we search on key presses

var $hoogle; // $("#hoogle") after load


/////////////////////////////////////////////////////////////////////
// SEARCHING

$(function(){
    $hoogle = $("#hoogle");
    embed = !$hoogle.hasClass("HOOGLE_REAL");
    var self = embed ? newEmbed() : newReal();
    var $form = $hoogle.parents("form:first");

    var ajaxUrl = !embed ? "?" : $form.attr("action") + "?";
    var ajaxMode = embed ? 'embed' : 'ajax';
    var ajaxPrefix = $form.find("input[name=prefix]").attr("value");
    var ajaxSuffix = $form.find("input[name=suffix]").attr("value");

    var active = $hoogle.val(); // What is currently being searched for (may not yet be displayed)
    var past = cache(100); // Cache of previous searches
    var watch = watchdog(500, function(){self.showWaiting();}); // Timeout of the "Waiting..." callback

    $hoogle.keyup(function(){
        if (!instant) return;

        var now = $hoogle.val();
        if (now == active) return;
        active = now;

        var old = past.ask(now);
        if (old != undefined){self.showResult(old); return;}

        watch.stop();
        if (embed && now == ""){self.hide(); return;}
        watch.start();

        try {
            $.ajax({
                url: ajaxUrl,
                data: {hoogle:now, mode:ajaxMode, prefix:ajaxPrefix, suffix:ajaxSuffix},
                dataType: 'html',
                complete: function(e){
                    watch.stop();
                    if (e.status == 200)
                    {
                        past.add(now,e.responseText);
                        if ($hoogle.val() == now)
                            self.showResult(e.responseText);
                    }
                    else
                        self.showError(e.status, e.responseText);
                }
            });
        } catch (err) {
            // Probably a permissions error from cross domain scripting...
            watch.stop();
        }
    });
})

function newReal()
{
    $hoogle.focus();
    $hoogle.select();
    var $h1 = $("h1");
    var $body = $("#body");

    return {
        showWaiting: function(){$h1.text("Still working...");},
        showError: function(status,text){$body.html("<h1><b>Error:</b> status " + status + "</h1><p>" + text + "</p>")},
        showResult: function(text){$body.html(text);}
    }
}

function newEmbed()
{
    $hoogle.attr("autocomplete","off");
    // IE note: unless the div in the iframe contain any border it doesn't calculate the correct outerHeight()
    //          therefore we put 3 borders on the iframe, and leave one for the bottom div
    var $iframe = $("<iframe id='hoogle-output' scrolling='no' "+
                    "style='position:absolute;border:1px solid rgb(127,157,185);border-bottom:0px;display:none;' />");
    var $body;
    $iframe.load(function(){
        var $contents = $iframe.contents();
        $contents.find("head").html(
            "<style type='text/css'>" +
            "html {border: 0px;}" +
            "body {font-family: sans-serif; font-size: 13px; background-color: white; padding: 0px; margin: 0px;}" +
            "a, i {display: block; color: black; padding: 1px 3px; text-decoration: none; white-space: nowrap; overflow: hidden; cursor: default;}" +
            "a.sel {background-color: rgb(10,36,106); color: white;}" +
            "div {border-bottom:1px solid rgb(127,157,185);}" +
            "</style>");
        $body = $("<div>").appendTo($contents.find("body"));
    });
    $iframe.insertBefore($hoogle);

    var finishOnBlur = true; // Should a blur hide the box

    function show(x){
        if (x == undefined)
            $iframe.css("display","none");
        else {
            $body.html(x).find("a").attr("target","_parent")
                .mousedown(function(){finishOnBlur = false;})
                .mouseup(function(){finishOnBlur = true;})
                .mouseenter(function(){
                    $body.find(".sel").removeClass("sel");
                    $(this).addClass("sel");
                });

            var pos = $hoogle.position();
            // need to display before using $body.outerHeight() on Firefox
            $iframe.css("display","").css(
                {top:px(pos.top + $hoogle.outerHeight() + unpx($hoogle.css("margin-top")))
                ,left:px(pos.left + unpx($hoogle.css("margin-left")))
                ,width:px($hoogle.outerWidth() - 2 /* iframe border */)
                ,height:$body.outerHeight()
                });
        }
    }

    $hoogle.blur(function(){if (finishOnBlur) show();});

    $hoogle.keydown(function(event){
        switch(event.which)
        {
        case Key.Return:
            var sel = $body.find(".sel:first");
            if (sel.size() == 0) return;
            event.preventDefault();
            document.location.href = sel.attr("href");
            break;

        case Key.Escape:
            $body.find(".sel").removeClass("sel");
            show();
            break;

        case Key.Down: case Key.Up:
            var i = event.which == Key.Down ? 1 : -1;
            var all = $body.find("a");
            var sel = all.filter(".sel");
            var now = all.index(sel);
            if (now == -1)
                all.filter(i == 1 ? ":first" : ":last").addClass("sel");
            else {
                sel.removeClass("sel");
                // IE treats :eq(-1) as :eq(0), so filter specifically
                if (now+i >= 0) all.filter(":eq(" + (now+i) + ")").addClass("sel");
            }
            event.preventDefault();
            break;
        }
    });

    return {
        showWaiting: function(){show("<i>Still working...</i>");},
        showError: function(status,text){show("<i>Error: status " + status + "</i>");},
        showResult : function(text){show(text);},
        hide: function(){show();}
    }
}


/////////////////////////////////////////////////////////////////////
// INSTANT BUTTON

$(function(){
    if (embed)
        instant = true;
    else
    {
        setInstant($.getQueryString('ajax') == "1" || $.cookie("instant") == "1");
        $("#instant").css("display","");
    }
});

function setInstant(x)
{
    instant = x == undefined ? !instant : x ? true : false;
    $("#instantVal").html(instant ? "on" : "off");
    if (instant)
    {
        $.cookie("instant","1",{expires:365});
        $hoogle.keyup();
    }
    else
        $.cookie("instant",null);
}


/////////////////////////////////////////////////////////////////////
// SEARCH PLUGIN

$(function(){
    if (embed) return;
    if (window.external && ("AddSearchProvider" in window.external))
        $("#plugin").css("display","");
});

function searchPlugin()
{
    var l = document.location;
    var url = l.protocol + "//" + l.hostname + l.pathname + $("link[rel=search]").attr("href");
    window.external.AddSearchProvider(url);
}


/////////////////////////////////////////////////////////////////////
// DOCUMENTATION

function docs(i)
{
    var e = document.getElementById("d" + i);
    e.className = (e.className == "shut" ? "open" : "shut");
    return false;
}


/////////////////////////////////////////////////////////////////////
// LIBRARY BITS

// Access the browser query string
// From http://stackoverflow.com/questions/901115/get-querystring-values-with-jquery/3867610#3867610
;(function ($) {
    $.extend({      
        getQueryString: function (name) {           
            function parseParams() {
                var params = {},
                    e,
                    a = /\+/g,  // Regex for replacing addition symbol with a space
                    r = /([^&=]+)=?([^&]*)/g,
                    d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
                    q = window.location.search.substring(1);

                while (e = r.exec(q))
                    params[d(e[1])] = d(e[2]);

                return params;
            }

            if (!this.queryStringParams)
                this.queryStringParams = parseParams(); 

            return this.queryStringParams[name];
        }
    });
})(jQuery);


var Key = {
    Up: 38,
    Down: 40,
    Return: 13,
    Escape: 27
};


function unpx(x){var r = 1 * x.replace("px",""); return isNaN(r) ? 0 : r;}
function px(x){return x + "px";}


function cache(maxElems)
{
    // FIXME: Currently does not evict things
    var contents = {}; // what we have in the cache

    return {
        add: function(key,val)
        {
            contents[key] = val;
        },
        
        ask: function(key)
        {
            return contents[key];
        }
    };
}


function watchdog(time, fun)
{
    var id = undefined;
    function stop(){if (id == undefined) return; window.clearTimeout(id); id = undefined;}
    function start(){stop(); id = window.setTimeout(function(){id = undefined; fun();}, time);}
    return {start:start, stop:stop}
}