/*jslint browser: true */ /*global jQuery: true */

/**
 * jQuery Cookie plugin
 *
 * Copyright (c) 2010 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

// TODO JsDoc

/**
 * Create a cookie with the given key and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String key The key of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given key.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String key The key of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function (key, value, options) {

    // key and value given, set cookie...
    if (arguments.length > 1 && (value === null || typeof value !== "object")) {
        options = jQuery.extend({}, options);

        if (value === null) {
            options.expires = -1;
        }

        if (typeof options.expires === 'number') {
            var days = options.expires, t = options.expires = new Date();
            t.setDate(t.getDate() + days);
        }

        return (document.cookie = [
            encodeURIComponent(key), '=',
            options.raw ? String(value) : encodeURIComponent(String(value)),
            options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
            options.path ? '; path=' + options.path : '',
            options.domain ? '; domain=' + options.domain : '',
            options.secure ? '; secure' : ''
        ].join(''));
    }

    // key and possibly options given, get cookie...
    options = value || {};
    var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
};
(function($, global) {

var queue = {},
    pmidre = /\d+/,
    trimre = /(^\s+|\s+$)/g,
    trim = function(str) { return str.replace(trimre, str) },
    proto = "prototype",
    entrezajax = {},
    identity = function() { },
    pmidExtractor = function(n) { return ($(n).text().match(pmidre) || [ null ])[0] },
    defaultFormat = "<span class='authors'>%AuthorList</span>. <a href='http://www.ncbi.nlm.nih.gov/pubmed/%Id'><em class='title'>%Title</em></a> <span class='source'>%Source</span>. <span class='issue'>%SO</span>. PubMed PMID: <span class='pmid'>%Id</span>.",
    tokenizer = /(%\w*|%\{\w+\}|[^%]*)/g,
    methods = [ "esearch", "elink", "espell", "einfo", "esummary", "efetch", "esearch+esummary", "esearch+efetch", "esearch+elink", "elink+esummary", "elink+efetch" ],
    copy = function(from, to) {
        to = to || {};
        for (var p in from) {
            if (!from.hasOwnProperty || from.hasOwnProperty(p))
                to[p] = from[p];
        }
        return to;
    };


function EntrezAjax(opts) {
    var baseUrl = opts.baseUrl || "http://entrezajax.appspot.com/",
        apiKey = opts.apiKey,
        db = opts.db || "pubmed",
        makeRequest = function(method, params, success, error) {
            params = copy(params);
            error = error || identity;

            return $.getJSON(baseUrl + method + "?callback=?",
                copy({
                    db: db,
                    apikey: apiKey
                }, params),
                function(data) {
                    if (!data.entrezajax || data.entrezajax.error) {
                        error(data);
                    } else {
                        success(data.result);
                    }
                }
            ).error(function() {
                error({});
            });
        },
        entrez = {};

    $.each(methods, function() {
        entrez[this] = (function(method) {
                return function(params, success, error) {
                    return makeRequest(method, params, success, error);
                };
            })(this);
    });

    return entrez;
}


var pubmed = EntrezAjax({ apiKey: "6b19beeaedade171ecc320ddd87f7ae6", db: "pubmed" }),
    pubMedSummary = function(doc, format) {
        var tokens = format.match(tokenizer),
            formatted = $.map(tokens, function(token) {
                if (token[0] == "%") {
                    var prop = token.substring(1),
                        val = doc[prop];
                    if (val != undefined)
                        token = val.join ? val.join(", ") : val;
                }
                return token;
            }).join("");

        return $("<span class='pretty-pmid' />").html(formatted);
    },
    waitAndCall = function(cb, wait, max) {
        var waitTimeoutId = null,
            startedAt,
            wrapped = function() {
                var now = (new Date()).getTime(),
                    cbthis = this,
                    cbargs = arguments,
                    tid = setTimeout(function() {
                        if (tid == waitTimeoutId)
                            waitTimeoutId = null;
                        cb.apply(cbthis, cbargs);
                    }, wait);

                if (waitTimeoutId !== null && now - startedAt < max) {
                    clearTimeoutId(waitTimeoutId);
                } else {
                    startedAt = now;
                }

                waitTimeoutId = tid;
            };
        wrapped.unwrap = function() { return cb };
        return wrapped;
    },
    makeAllRequests = waitAndCall(function(format) {
        var ids = $.map(queue, function(nodes, id) { return id }),
            queued = queue;

        pubmed.esummary({ id: ids.join(",") }, function(docs) {
            for (var i = docs.length; i--;)
                $.each(queued[docs[i].Id] || [], function() {
                    $(this.node)
                        .removeClass("pmid pretty-please")
                        .empty()
                        .append(pubMedSummary(docs[i], this.format));
                });
        });

        queue = {};
    }, 500, 2000);



/**
 * Turns a set of PubMed IDs (PMIDs) into a prettier version. This can be
 * called with no arguments, or it can be passed an option argument. This
 * option argument can have 2 properties format and pmid.
 *
 * The pmid option let's you specify an "extractor" function for a PubMed
 * ID. For each context node, the PubMed ID is extracted using this function
 * by calling it with the node as a single argument.
 *
 * The format option let's you specify the format of the output. This is done
 * using a string (with intermixed HTML) with various "keywords", prepended
 * with a '%', as placeholders for a document's attributes. Allowed keywords
 * simply map to the values returned by Entrez's esummary web service.
 *
 * The default format string is:
 *
 * "<span class='authors'>%AuthorList</span>.
 * <em class='title'>%Title</em>
 * <span class='source'>%Source</span>.
 * <span class='issue'>%SO</span>.
 * PubMed PMID: <span class='pmid'>%Id</span>."
 *
 * @param opts An optional options argument.
 */
$.fn.prettyPubMedId = function(opts) {
    opts = copy(opts, { format: defaultFormat, pmid: pmidExtractor });
    this.each(function() {
        var pmid = opts.pmid(this);
        if (pmid)
            (queue[pmid] = queue[pmid] || []).push({ node: this, format: opts.format });
    });
    if (this.length)
        makeAllRequests(opts.format);
    return this;
};


$(document).ready(function() {
    $(".pmid.pretty-please").prettyPubMedId();
});

})(jQuery, window);

(function($) {

/**
 * For anchors with local targets, we scroll "smoothly" to the target. This will
 * also allow CSS selectors to be used, in-place of simple targets (eg. 
 * &lt;a href="h1:eq(0)"&gt;Go somewhere&lt;/a&gt).
 */
$.fn.smoothScroll = function() {
	this.each(function() {
		var source = $(this);
		source.click(function() {
			var target = $(source.attr("href"));
			if (target.length == 0)
				return;
			
			try {
				$.scrollTo(target, { duration: 500 });
			} catch (e) {
				if (typeof console != "undefined" && console.log)
					console.log(e);
				return;		// Let click fall through if scrollTo fails.
			}
			
			return false;
		});
	});
};


$(document).ready(function() {
	
	// Links within a page with .smooth-scroll class will have a nicer motion
	$("a.smooth-scroll[href^='#']").smoothScroll();
	
	// Forms with class autosubmit will be automatically submitted when any
	// input element emits a change event.
	$("form.autosubmit").each(function() {
		var form = $(this);
		form.find("input[type='submit']").hide();
		form.find(":input").change(function() {
			form.submit();
		});
	});
	
	
	$(".showHoverableOnHover").live("mouseover", function() {
      $(this).find(".hoverable").slideDown(function() {
        var hoverable = $(this);
        $("body").one("click", function() { hoverable.fadeOut(); });
      });
    });
    $(".showHoverableOnHover .hoverable").live("click", function(e) {
    	e.stopPropagation();
    });
    

    $(".pmid.pretty-on-hover").prettyPubMedId({
    	format: "<span class='pop-up'><span class='authors'>%AuthorList</span>. <em class='title'><a class='noLinkControl' href='http://www.ncbi.nlm.nih.gov/pubmed/%Id'>%Title</a></em> (%Id)<span class='bubble below wide'><span class='authors'>%AuthorList</span>. <a href='http://www.ncbi.nlm.nih.gov/pubmed/%Id'><em class='title'><strong>%Title</strong></em></a> <span class='source'>%Source</span>. <span class='issue'>%SO</span>. PubMed PMID: <span class='pmid'>%Id</span>.</span></span>"
    });
    /*
    $(".pmid.pretty-on-hover").prettyPubMedId({
    	format: "<span class='showHoverableOnHover'><em class='title'><a href='http://www.ncbi.nlm.nih.gov/pubmed/%Id'>%Title</a></em> (%Id)<div class='hoverable'><span class='authors'>%AuthorList</span>. <a href='http://www.ncbi.nlm.nih.gov/pubmed/%Id'><em class='title'><strong>%Title</strong></em></a> <span class='source'>%Source</span>. <span class='issue'>%SO</span>. PubMed PMID: <span class='pmid'>%Id</span>.</div></span>"
    });
    */
});

/**
 * Returns a decorated version of the function f that will warn FireBug users
 * that the function has been deprecated. If the function is named (ie. has
 * been declared as "function NAME(...", then the name can be deduced 
 * automatically for the warnings, otherwise a human readable name has to be
 * given as well, via the fName parameter.
 * 
 * @param f A function to deprecated
 * @param fName A human-readable name for the function (Optional).
 * @return A function that decorates f
 */
function deprecated(f, fName) {
	if (typeof console == "undefined")
		return f;
	
	// Determine the function's name
	fName = fName || "*unknown function*";
	var m = /^function (\w*)\(/.exec(f.toString());
	if (m && m.length > 1 && m[1])
		fName = m[1];
	
	// Each call to the function returned will log a warning
	return function() {
		if (typeof console != "undefined" && console.log && console.trace) {
			console.group("Deprecated function: " + fName);
			console.trace();
			console.groupEnd();
		}
		return f.apply(this, arguments);
	}
}


// deprecated should be global. 
window.deprecated = deprecated;

})(jQuery);


