// 'jq.idPrefix.js'
// 'jquery-tooltip/jquery.tooltip.js'
// 'hunch.ac.js'
// 'facebox/facebox.js'
// 'hunch.confirm.js'
// 'jquery.hunch-utils.js'
// 'jquery.loader.js'
// 'fileuploader.js'
// 'jquery.fileupload.js'
// 'jquery.flag.js'
// 'jquery.placeholder.js'
// 'persistentplaceholder.js'
// 'jquery.toggle.js'
// 'jquery.remaining.js'
// 'hunch.hoverhelper.js'
// 'hunch.stars.js'
// 'hunch.imagescale.js'
// 'cookies.js'
// 'elastic/jquery.elastic.js'
// 'hunch.hovercard.js'
// 'onload.js'

/* Copyright (C) 2011, Hunch.com */

(function($) {
	var getId = function(id, divider){ return (id ? id.split(divider || '-')[0] : ''); },
	    getIdSuffix = function(id, divider){ return (id ? id.split(divider || '-').pop() : ''); };

	$.idPrefix = function(object, divider) { return getId(object.id, divider); };
	$.idSuffix = function(object, divider) { return getIdSuffix(object.id, divider); };

	$.fn.extend({
		idPrefix: function(divider) {
			return getId(this.attr('id'), divider);
		},
		idSuffix: function(divider) {
			return getIdSuffix(this.attr('id'), divider);
		}
	});
})(jQuery);

/*
 * jQuery Tooltip plugin 1.3
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
 * http://docs.jquery.com/Plugins/Tooltip
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
 *
 * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

;(function($) {

		// the tooltip element
	var helper = {},
		// the current tooltipped element
		current,
		// the title of the current element, used for restoring
		title,
		// timeout id for delayed tooltips
		tID,
		// IE 5.5 or 6
		IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
		// flag for mouse tracking
		track = false;

	$.tooltip = {
		blocked: false,
		defaults: {
			delay: 200,
			fade: false,
			showURL: true,
			extraClass: "text-tooltip",
			top: 15,
			left: 15,
			id: "tooltip"
		},
		block: function() {
			$.tooltip.blocked = !$.tooltip.blocked;
		},
		/* HACK - to create a mouseover event from another event - useful when adding the tooltip on-the-fly */
		mouseoverEvent: function(evt) {
			var event=$.Event('mouseover');event.pageX=evt.pageX;event.pageY=evt.pageY;event.FAKE_MOUSEOVER=true;
			return event;
		}
	};

	$.fn.extend({
		tooltip: function(settings) {
			settings = $.extend({}, $.tooltip.defaults, settings);
			createHelper(settings);
			return this.each(function() {
					$.data(this, "tooltip", settings);
					this.tOpacity = helper.parent.css("opacity");
					// copy tooltip into its own expando and remove the title
					this.tooltipText = this.title;
					$(this).removeAttr("title");
					// also remove alt attribute to prevent default tooltip in IE
					this.alt = "";
				})
				.bind('mouseover', save)
				.bind('mouseout', hide)
				.bind('click', hide);
		},
        darkTooltip: function(settings) {
            return this.tooltip($.extend(
                {
                    delay: 0,
                    showURL: false,
                    id: 'black-tooltip',
                    top: -36,
                    left: 0,
                    useTarget: true,
                    bodyHandler: function() { return $.htmlEscape(this.tooltipText).split(' ').join('&nbsp;'); }
                },
                settings
            ));
        },
		tooltipDisable: function(id) {
			$(document.body).unbind('mousemove.tooltip');
			$(id || '#'+$.tooltip.defaults.id).hide();
			return $(this).unbind();
		},
		fixPNG: IE ? function() {
			return this.each(function () {
				var image = $(this).css('backgroundImage');
				if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
					image = RegExp.$1;
					$(this).css({
						'backgroundImage': 'none',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
					}).each(function () {
						var position = $(this).css('position');
						if (position != 'absolute' && position != 'relative')
							$(this).css('position', 'relative');
					});
				}
			});
		} : function() { return this; },
		unfixPNG: IE ? function() {
			return this.each(function () {
				$(this).css({'filter': '', backgroundImage: ''});
			});
		} : function() { return this; },
		hideWhenEmpty: function() {
			return this.each(function() {
				$(this)[ $(this).html() ? "show" : "hide" ]();
			});
		},
		url: function() {
			return this.attr('href') || this.attr('src');
		}
	});

	function createHelper(settings) {
		// there can be only one tooltip helper
		if( helper.parent )
			return;
		// create the helper, h3 for title, div for url
		helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
			// add to document
			.appendTo(document.body)
			// hide it at first
			.hide();

		// apply bgiframe if available
		if ( $.fn.bgiframe )
			helper.parent.bgiframe();

		// save references to title and url elements
		helper.title = $('h3', helper.parent);
		helper.body = $('div.body', helper.parent);
		helper.url = $('div.url', helper.parent);
	}

	function settings(element) {
		return $.data(element, "tooltip");
	}

	// main event handler to start showing tooltips
	function handle(event) {
		// show helper, either with timeout or on instant
		if( settings(this).delay )
			tID = setTimeout(show, settings(this).delay);
		else
			show();

		// if selected, update the helper position when the mouse moves
		track = !!settings(this).track;
		$(document.body).bind('mousemove.tooltip', update);

		// update at least once
		update(event);
	}

	// save elements title before the tooltip is displayed
	function save() {
		// if this is the current source, or it has no title (occurs with click event), stop
		if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
			return;

		// save current
		current = this;
		title = this.tooltipText;

		if ( settings(this).bodyHandler ) {
			helper.title.hide();
			var bodyContent = settings(this).bodyHandler.call(this);
			if (bodyContent.nodeType || bodyContent.jquery) {
				helper.body.empty().append(bodyContent)
			} else {
				helper.body.html( bodyContent );
			}
			helper.body.show();
		} else if ( settings(this).showBody ) {
			var parts = title.split(settings(this).showBody);
			helper.title.html(parts.shift()).show();
			helper.body.empty();
			for(var i = 0, part; (part = parts[i]); i++) {
				if(i > 0)
					helper.body.append("<br/>");
				helper.body.append(part);
			}
			helper.body.hideWhenEmpty();
		} else {
			helper.title.html(title).show();
			helper.body.hide();
		}

		// if element has href or src, add and show it, otherwise hide it
		if( settings(this).showURL && $(this).url() )
			helper.url.html( $(this).url().replace('http://', '') ).show();
		else
			helper.url.hide();

		// add an optional class for this tip
		helper.parent.addClass(settings(this).extraClass);

		// fix PNG background for IE
		if (settings(this).fixPNG )
			helper.parent.fixPNG();

		handle.apply(this, arguments);
	}

	// delete timeout and show helper
	function show() {
		tID = null;
		if ((!IE || !$.fn.bgiframe) && settings(current).fade) {
			if (helper.parent.is(":animated"))
				helper.parent.stop().show().fadeTo(settings(current).fade, current.tOpacity);
			else
				helper.parent.is(':visible') ? helper.parent.fadeTo(settings(current).fade, current.tOpacity) : helper.parent.fadeIn(settings(current).fade);
		} else {
			helper.parent.show();
		}
		update();
	}

	/**
	 * callback for mousemove
	 * updates the helper position
	 * removes itself when no current element
	 */
	function update(event)	{
		if($.tooltip.blocked)
			return;

		if (event && event.target.tagName == "OPTION") {
			return;
		}

		// stop updating when tracking is disabled and the tooltip is visible
		if ( !track && helper.parent.is(":visible")) {
			$(document.body).unbind('mousemove.tooltip', update);
		}

		// if no current element is available, remove this listener
		if( current == null ) {
			$(document.body).unbind('mousemove.tooltip', update);
			return;
		}

        //HACK - support positioning relative to a target element - 3/18/11
        var $target, $target_offset, useTarget = false;
        if (event && event.target && (useTarget = settings(current).useTarget)) {
            $target = $(event.target);
            $target_offset = $target.offset();
        }

        //HACK - allow multiple ids - 3/18/11
        if (helper.parent[0].id != settings(current).id) helper.parent[0].id = settings(current).id;

		// remove position helper classes
		helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");

		var left = helper.parent[0].offsetLeft;
		var top = helper.parent[0].offsetTop;
		if (event) {
			// position the helper 15 pixel to bottom right, starting from mouse position
			left = (useTarget ? $target_offset.left : event.pageX) + settings(current).left;
			top = (useTarget ? $target_offset.top : event.pageY) + settings(current).top;
			var right='auto';
			if (settings(current).positionLeft) {
				right = $(window).width() - left;
				left = 'auto';
			}
			helper.parent.css({
				left: left,
				right: right,
				top: top
			});
		}

		var v = viewport(),
			h = helper.parent[0];
		// check horizontal position
		if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
            if (useTarget)
                left = (v.cx < $target_offset.left + $target.outerWidth() ?
                        v.cx - 3:
                        $target_offset.left + $target.outerWidth() - 3
                       ) - h.offsetWidth;
            else
                left -= h.offsetWidth + 20 + settings(current).left;

			helper.parent.css({left: left + 'px'}).addClass("viewport-right");
		}
		// check vertical position
		if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
			top -= h.offsetHeight + 20 + settings(current).top;
			helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
		}
	}

	function viewport() {
		return {
			x: $(window).scrollLeft(),
			y: $(window).scrollTop(),
			cx: $(window).width(),
			cy: $(window).height()
		};
	}

	// hide helper and restore added classes and the title
	function hide(event) {
		if($.tooltip.blocked)
			return;
		// clear timeout if possible
		if(tID)
			clearTimeout(tID);
		// no more current element
		current = null;

		var tsettings = settings(this);
		function complete() {
			helper.parent.removeClass( tsettings.extraClass ).hide().css("opacity", "");
		}
		if ((!IE || !$.fn.bgiframe) && tsettings.fade) {
			if (helper.parent.is(':animated'))
				helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
			else
				helper.parent.stop().fadeOut(tsettings.fade, complete);
		} else
			complete();

		if( settings(this).fixPNG )
			helper.parent.unfixPNG();
	}

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * Hunch autocomplete
 */
;(function($) {
    var TYPING_TIMEOUT = '_acTypingTimeout',
        XHR = '_acXhr',
        IGNORE_KEYPRESS = '_acIgnoreKeypress',
        AC_ON = '_acOn',
        ORIGINAL_OBJECT = '_acOriginalObject',
        SAW_MOVEMENT = '_acSawMouseMove',
        prior_updown_keydown = 0;

    $.hunchac = {
        defaults: {
            timeout: 200,
            url: '/ws/ws_search/',
            queryKey: 'query',
            render: function(list, container, input) {
                var ul = $('<ul/>');
                $.each(list, function() {
                    var node = $('<li class="ac-element">'+this.name+'</li>');
                    $.data(node[0], ORIGINAL_OBJECT, this);
                    ul.append(node);
                });
                container.html(ul);
            },
            dropDownOnly: true,         // Makes it so you can only select dropdown items
            jsonListAttr: 'docs',       // The attribute to access from the returned json
            xhrFunc: null,              // An optional xhr function to use instead of $.ajax, takes a
                                        //   `query` argument and must do all the rest of the work itself
            containerClass: 'yui-skin-sam',
            wrapClass: 'ac-wrap yui-skin-sam',
            innerClass: 'yui-ac-content', //TODO - the css/markup is a mess for this...

            // autostart: false
            data: {},
            ajaxType: 'get'
        },
        curInput: null,
        TYPING_TIMEOUT: TYPING_TIMEOUT,
        XHR: XHR,
        IGNORE_KEYPRESS: IGNORE_KEYPRESS,
        AC_ON: AC_ON,
        ORIGINAL_OBJECT: ORIGINAL_OBJECT
    };

    $.fn.extend({
        hunchac: function(settings) {
            settings = $.extend({}, $.hunchac.defaults, settings);
            var callback = function(json, status, $input) {
                $('body').trigger('off.hunchac');
                var container = $(settings.container),
                    docs = (settings.jsonListAttr ? json[settings.jsonListAttr] : json),
                    size = settings.render(docs, container, $input, status);
                container.show();
                if (settings.loadingClass) $input.removeClass('loading');
                $('body').hunchacMode(container, $input, size || docs.length, settings);
            };

            // make a container if necessary...
            if (!settings.container) {
                settings.container = $(
                    '<div>',
                    {
                        'class': settings.wrapClass,
                        css: settings.containerCSS,
                        html: '<div class="ac-container' + (settings.containerClass ? ' '+settings.containerClass : '') + '"></div>'
                    }
                ).insertAfter(this).find('div.ac-container');
            } else if (settings.containerCSS) {
                container.css(settings.containerCSS);
            }

            return this.each(function() {

                var last_key;

                function keypress(e) {
                    $.hunchac.curInput = this;
                    last_key = e.which;
                    var xhr = $.data(this, XHR),
                        isAcOn = $.data(document.body, AC_ON);

                    window.clearTimeout($.data(this, TYPING_TIMEOUT));

                    if (xhr) {
                        try { xhr.abort(); } catch(e) {}
                        $.data(this, XHR, null);
                        if (settings.loadingClass) $(this).removeClass('loading');
                    }

                    if ($.data(document.body, IGNORE_KEYPRESS))
                        return $.data(document.body, IGNORE_KEYPRESS, false);
                    else
                        if (e.keyCode == 9)
                            return true; // ignore tabs
                    else if (isAcOn && !e.charCode && (e.keyCode == 38 || e.keyCode == 40)) {
                        if (e.timeStamp && e.timeStamp - prior_updown_keydown > 100) {
                            /* little hack for ff and opera and any other browser that passes repeated keypress events when holding down the up or down arrow */
                            $(this).trigger('keydown.hunchac', [e.keyCode]);
                        }
                        return false;
                    }
                    else if (isAcOn && e.charCode < 32 && e.keyCode != 8 && e.keyCode != 46) {
                        if (e.keyCode == 37 || e.keyCode == 39)
                            return true; // allow left/right arrows
                        if (e.keyCode == 13 && !settings.dropDownOnly)
                            return true; // allow non-dropDownOnly to submit
                        return false;
                    }
                    else {
                        if (isAcOn && e.keyCode == 8 && ($.browser.safari || $.browser.msie)) {
                            // Safari hack - doesn't do keypress for backspace - but do this in case if it starts sending it...
                            return true;
                        }
                        window.clearTimeout($.data(this, TYPING_TIMEOUT));
                        $.data(this, TYPING_TIMEOUT, window.setTimeout(function() {
                            $(e.target).trigger('hunchac');
                        }, settings.timeout));
                    }
                    return true;
                };

                $(this)
                    .attr('autocomplete', 'off')
                    .keypress(keypress)
                    .keyup(function(e) {
                        if (last_key != e.keyCode && e.keyCode >= 48 && e.keyCode <= 57) {
                            // Special case for writing in Chinese characters...
                            // (where a number is selected at the end) will use just this band-aid hack for now...
                            keypress.call(this, e);
                        }
                    })
                    .bind('hunchac', function() {
                        var self = this,
                            query = $(this).val(),
                            request = {
                                url: settings.url,
                                data: $.extend(
                                    settings.data || {},
                                    settings.getData ? settings.getData() : null
                                ),
                                type: settings.ajaxType,
                                dataType: (settings.jsonp ? 'jsonp' : 'json'),
                                cache: false,
                                success: function(json, status) {
                                    if (!json) return null; // seems that Safari will call this func with null when canceled
                                    $.data(self, XHR, null);
                                    return callback(json, status, $(self));
                                }
                            };
                        request.data[settings.queryKey] = query;

                        if (query) {
                            if (settings.loadingClass) $(this).addClass('loading');
                            $.data(this, XHR, (settings.xhrFunc ? settings.xhrFunc(query, request.success) : $.ajax(request)));
                        } else {
                            $(this).hunchacCancel(); // stop the dropdown from showing when nothing...
                        }
                    })
                    .click(function() {
                        if ($.hunchac.curInput !== null && this != $.hunchac.curInput)
                            $('body').trigger('cancel.hunchac');
                        return false; // prevent click from reaching window
                    });

                if (settings.autostart && $(this).valNoPlaceholder())
                    $(this).trigger('hunchac');

                if (settings.activate)
                    $(this).bind('activateacInput.hunchac', settings.activate);
            });
        },
        hunchacCancel: function() {
            // this function tries as hard as it can to cancel the auto-complete...
            return this.each(function() {
                var xhr = $.data(this, XHR);
                window.clearTimeout($.data(this, TYPING_TIMEOUT));
                if (xhr) {
                    try { xhr.abort(); } catch(e) {}
                }
                $.data(this, XHR, null);
                $('body')
                    .trigger('off.hunchac')
                    .trigger('cancel.hunchac');
                $(this).removeClass('loading');
            });
        },
        hunchacMode: function(container, input, size, settings) {
            var selected = (settings.dropDownOnly ? 0 : -1),  // selected index
                self = this,
                active;

            $.data(document.body, AC_ON, true);

            $('body').one('cancel.hunchac', function() {
                $('body').trigger('off.hunchac', [true]);
            });

            $('body').bind('activateac.hunchac', function() {
                if (active) {
                    input.trigger('activateacInput.hunchac', [active, $.data(active, ORIGINAL_OBJECT)]);
                    $('body').unbind('activateac.hunchac');
                }
            });

            $('body').one('off.hunchac', function(e) {
                container.children().hide(); // fixes opera 9.64 no-hide bug
                $.data(document.body, AC_ON, false);
                $.data(document.body, SAW_MOVEMENT, false);
                input.unbind('keydown.hunchac');
                $('body').add(window).unbind('click.hunchac').unbind('cancel.hunchac').unbind('activateac.hunchac');
            });

            // Handles close autocomplete
            $('body').add(window).bind('click.hunchac', function() {
                $('body').trigger('cancel.hunchac');
            });

            var select = function() {
                var $active = $('li.ac-element', container).removeClass('active').removeClass('yui-ac-highlight');
                if (selected == -1) {
                    active = null;
                } else {
                    $active = $active.slice(selected, selected+1).addClass('active').addClass('yui-ac-highlight');
                    if ($active.size()) active = $active[0];
                }
            };

            select();

            var getElt = function(e) {
                var elt = $(e.target).closest('li.ac-element');
                return elt.size() ? elt[0] : null;
            };

            // Track this since FF generates mouseover without motion, so if mouse happened
            // to already be over a result we think the user selected it when instead it just
            // happened to overlap.
            container.mousemove(function(e) {
                $.data(document.body, SAW_MOVEMENT, true);
            });

            container.mouseover(function(e) {
                // if container but not children or hasn't moved yet
                if (e.target == container[0] || !$.data(document.body, SAW_MOVEMENT))
                    return;
                // check if we have an element selected and if so, select it
                var elt = getElt(e);
                if (elt) {
                    selected = $('li.ac-element', container).index(elt);
                    select();
                }
            }).bind('click.hunchac', function(e) {
                var elt = getElt(e);
                if (elt) {
                    $('body').trigger('activateac.hunchac');
                    $.data(document.body, IGNORE_KEYPRESS, false);
                }
                return false;
            });

            input.bind('keydown.hunchac', function(e, override) {
                var key = override || e.which;

                if (key == 40 || key == 38) {
                    if (key == 40) {
                        selected = selected >= size - 1 ? (settings.dropDownOnly ? 0 : -1) : selected + 1;
                    } else {
                        selected = selected <= 0 ? (!settings.dropDownOnly && selected == 0 ? -1 : size - 1) : selected - 1;
                    }
                    if (!override) prior_updown_keydown = e.timeStamp;
                    select();
                    return false; // Prevent safari from moving cursor to front of input
                }
                else if (key == 27)
                    $('body').trigger('cancel.hunchac');
                else if (key == 13) {
                    if (!settings.dropDownOnly && selected == -1) return true;
                    $('body').trigger('activateac.hunchac');
                }
                else {
                    if (key == 8 && ($.browser.safari || $.browser.msie)) {
                        // Safari hack - doesn't do keypress for backspace - but do this in case if it starts sending it...
                        $.data(this, TYPING_TIMEOUT, window.setTimeout(function() {
                            $(e.target).trigger('hunchac');
                        }, settings.timeout));
                    }
                    return true;
                }
                $.data(document.body, IGNORE_KEYPRESS, true);
            });
        }
    });
})(jQuery);

/*
 * Facebox (for jQuery)
 * version: 1.2 (05/05/2008)
 * @requires jQuery v1.2 or later
 *
 * Examples at http://famspam.com/facebox/
 *
 * Licensed under the MIT:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
 *
 * Usage:
 *
 *  jQuery(document).ready(function() {
 *    jQuery('a[rel*=facebox]').facebox()
 *  })
 *
 *  <a href="#terms" rel="facebox">Terms</a>
 *    Loads the #terms div in the box
 *
 *  <a href="terms.html" rel="facebox">Terms</a>
 *    Loads the terms.html page in the box
 *
 *  <a href="terms.png" rel="facebox">Terms</a>
 *    Loads the terms.png image in the box
 *
 *
 *  You can also use it programmatically:
 *
 *    jQuery.facebox('some html')
 *
 *  The above will open a facebox with "some html" as the content.
 *
 *    jQuery.facebox(function($) {
 *      $.get('blah.html', function(data) { $.facebox(data) })
 *    })
 *
 *  The above will show a loading screen before the passed function is called,
 *  allowing for a better ajaxy experience.
 *
 *  The facebox function can also display an ajax page or image:
 *
 *    jQuery.facebox({ ajax: 'remote.html' })
 *    jQuery.facebox({ image: 'dude.jpg' })
 *
 *  Want to close the facebox?  Trigger the 'close.facebox' document event:
 *
 *    jQuery(document).trigger('close.facebox')
 *
 *  Facebox also has a bunch of other hooks:
 *
 *    loading.facebox
 *    beforeReveal.facebox
 *    reveal.facebox (aliased as 'afterReveal.facebox')
 *    init.facebox
 *
 *  Simply bind a function to any of these hooks:
 *
 *   $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
 *
 */
(function($) {
  $.facebox = function(data, klass, force) {

	// Figure out the push
  	push = !force && isPush();
    $.facebox.settings.staticOverlay = (!data.clearable && !~(' '+klass+' ').indexOf(' clearable '))
          ? true : false;
    $.facebox.loading(push);

    if (data.ajax) fillFaceboxFromAjax(data.ajax);
	else if (data.post) fillFaceboxFromAjaxPost(data.post, data.data);
    else if (data.image) fillFaceboxFromImage(data.image, null, push);
    else if (data.div) fillFaceboxFromHref(data.div, null, push);
    else if ($.isFunction(data)) data.call($);
    else $.facebox.reveal(data, klass, push);
  };

  $.clearableFacebox = function(data, klass, force) {
      return $.facebox(data, (klass ? klass + ' ' : '') + 'clearable', force);
  };

  /*
   * Public, $.facebox methods
   */

  /* added staticOverlay as a hack to make it so the overlay can't be closed by default (except for images) */

  $.extend($.facebox, {
    settings: {
      opacity      : .4,
      overlay      : true,
      staticOverlay : false,
      loadingImage : '/media/img/loading-32px.gif',
      closeImage   : '/media/js/facebox/closelabel.gif',
      imageTypes   : [ 'png', 'jpg', 'jpeg', 'gif' ],/*
	  faceboxHtml  : '\
	<div id="facebox" style="display:none;">\
      <div class="popup">\
        <table><tbody><tr><td class="body">\
                <div class="content"></div>\
        </td></tr></tbody></table>\
      </div>\
    </div>',*/
	  pushHtml     : '<div class="popup-frame"></div>',
      faceboxHtml  : '\
    <div id="facebox" style="display:none;"> \
      <div class="popup"> \
        <table> \
          <tbody> \
            <tr> \
              <td class="b tl"/><td class="b"/><td class="b tr"/> \
            </tr> \
            <tr> \
              <td class="b"/> \
              <td class="body"> \
                <div class="content"> \
                </div> \
              </td> \
              <td class="b"/> \
            </tr> \
            <tr> \
              <td class="b bl"/><td class="b"/><td class="b br"/> \
            </tr> \
          </tbody> \
        </table> \
      </div> \
    </div>'
    },

    loading: function(push) {
      init();
      if ($('#facebox .body>.loading').length == 1) return true;
	  else if ($('#facebox .popup-frame>.loading').length == 1) return true;
      showOverlay();

	  var loading_image = '<div class="loading"><img src="'+$.facebox.settings.loadingImage+'"/></div>', content;
	  if (push) {
	  	if (isSlide()) {
			$('#facebox .content #slide-frame-inner').append($($.facebox.settings.pushHtml));
			getLastFrame().append(loading_image);
			slideRight();
			doCrumbs();
		} else {
		  	$('#facebox .content').append($($.facebox.settings.pushHtml));
			getLastFrame().append(loading_image);
		}
	  } else {
	  	$('#facebox .content').empty();
      	$('#facebox .body').children().hide().end().append(loading_image);

        // Initially position to handle our centered #content div and page scroll
        var $content = $('#content'), $fb = $('#facebox');
        $fb.css({
	        top: getPageScroll()[1] + (getPageHeight() / 10),
	        left: $content.offset().left + ~~(($content.width() - ($fb.width() || 410)) / 2)
	    }).show();
	  }

      $(document).bind('keydown.facebox', function(e) {
	  	if ($.facebox.settings.noEscape) return true; /* hack to allow disabling of escape keys */
        if (e.keyCode == 27) $.facebox.close();
        return true;
      });
      $(document).trigger('loading.facebox');
    },

    reveal: function(data, klass, push) {
      $(document).trigger('beforeReveal.facebox');
	  if (push) {
		$('#facebox .loading').remove();
	  	getLastFrame().append(data).fadeIn('normal');
		doCrumbs();
	  } else {
	  	if (klass) $('#facebox .content').addClass(klass);
        $('#facebox .content').append(data);
        $('#facebox .loading').remove();
        $('#facebox .body').children().fadeIn('normal');
	  }
      $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2));
      $(document).trigger('reveal.facebox').trigger('afterReveal.facebox');
    },

    reAlign: function(sideOnly) {
        var data = {
            left: $(window).width() / 2 - ($('#facebox table').width() / 2)
        };
        if (!sideOnly) data.top = getPageScroll()[1] + (getPageHeight() / 10);
        $('#facebox').css(data);
    },

	html: function(data, klass) { // avoid the stupid fadeIn - assumes facebox exists and no .loading
		$('#facebox .content').html(data);
	},

    close: function() {
	  $(document).trigger('close.facebox');
      return false;
    },

	recursive_close: recursive_close,

	isPush: isPush
  });

  /*
   * Public, $.fn methods
   */

  $.fn.facebox = function(settings) {
    init(settings);
	var push = isPush();

    function clickHandler() {
      $.facebox.loading(push);

      // support for rel="facebox.inline_popup" syntax, to add a class
      // also supports deprecated "facebox[.inline_popup]" syntax
      var klass = this.rel.match(/facebox\[?\.(\w+)\]?/);
      if (klass) klass = klass[1];

      fillFaceboxFromHref(this.href, klass, push);
      return false;
    }

    return this.click(clickHandler);
  };

  /*
   * Private methods
   */

  function getFrames() {
  	return $('#facebox .popup-frame');
  }

  function getLastFrame() {
  	return $('#facebox .content .popup-frame:last');
  }

  function isPush() {
  	return $('#facebox .popup-frame').size() > 0;
  }

  function isSlide() {
  	return true; // return $('#slide-frame');
  }

  //TODO - keep track of state to control sliding?
  function slideLeft(callback) {
  	$('#slide-frame-inner').animate({left: '+=630px'}, 'normal', callback);
  }

  function slideRight(callback) {
  	$('#slide-frame-inner').animate({left: '-=630px'}, 'normal', callback);
  }

  function doCrumbs() {
  	var crumbs = $('#slide-crumbs');
	if (crumbs.length) {
		var titles = [], frames = getFrames();
		if (frames.length <= 1) crumbs.html('&nbsp;');
		else {
			frames.each(function(i) {
				var $t = $(this), title = $t.data('title');
				if (!title) {
					title = $t.find('h1:first');
					if (title.length) title = title.text();
					else {
						title = $t.find('h2:first');
						if (title.length) title = title.text();
						else {
							title = $t.find('h3:first');
							if (title.length) title = title.text();
							else title = null;
						}
					}
					if (title) {
						if (title.lastIndexOf(':') == title.length - 1) title = title.slice(0, title.length-1);
						if (title.length > 30) title = title.slice(0, 30) + '…';
						$t.data('title', title);
					}
				}
				titles.push(title || '…');
			});
			crumbs.html(
				$.map(titles, function(x, i) {
					if (i < titles.length - 1) {
						return '<a href="#" onclick="$.facebox.recursive_close(' + (titles.length-1-i) + ');return false">'+x+'</a>';
					} else {
						return '<strong>'+x+'</strong>';
					}
				}).join(' &gt; ')
			);
			//crumbs.text(titles.join(' > '));
		}
	}
  }

  function recursive_close(i) {
  	var _helper = function() {
		if (i-- > 0) $(document).trigger('close.facebox', _helper);
		return false;
	};
	return _helper();
  }

  // called one time to setup facebox on this page
  function init(settings) {
    if ($.facebox.settings.inited) return true;
    else $.facebox.settings.inited = true;

    $(document).trigger('init.facebox');
    makeCompatible();

    var imageTypes = $.facebox.settings.imageTypes.join('|');
    $.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$', 'i');

    if (settings) $.extend($.facebox.settings, settings);
	$('body').append($.facebox.settings.faceboxHtml);

/* NO MORE PRELOADING!
    var preload = [ new Image(), new Image() ]
    preload[0].src = $.facebox.settings.closeImage
    preload[1].src = $.facebox.settings.loadingImage

    $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
      preload.push(new Image())
      preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1')
    })
*/
    $('#facebox .close, #facebox .cancel').live('click', $.facebox.close);
    //$('#facebox .close').click($.facebox.close)
    $('#facebox .close_image').attr('src', $.facebox.settings.closeImage);
  }

  // getPageScroll() by quirksmode.com
  function getPageScroll() {
    var xScroll, yScroll;
    if (self.pageYOffset) {
      yScroll = self.pageYOffset;
      xScroll = self.pageXOffset;
    } else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
      yScroll = document.documentElement.scrollTop;
      xScroll = document.documentElement.scrollLeft;
    } else if (document.body) {// all other Explorers
      yScroll = document.body.scrollTop;
      xScroll = document.body.scrollLeft;
    }
    return new Array(xScroll,yScroll);
  }

  // Adapted from getPageSize() by quirksmode.com
  function getPageHeight() {
    var windowHeight;
    if (self.innerHeight) {	// all except Explorer
      windowHeight = self.innerHeight;
    } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
      windowHeight = document.documentElement.clientHeight;
    } else if (document.body) { // other Explorers
      windowHeight = document.body.clientHeight;
    }
    return windowHeight;
  }

  // Backwards compatibility
  function makeCompatible() {
    var $s = $.facebox.settings;

    $s.loadingImage = $s.loading_image || $s.loadingImage;
    $s.closeImage = $s.close_image || $s.closeImage;
    $s.imageTypes = $s.image_types || $s.imageTypes;
    $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml;
  }

  // Figures out what you want to display and displays it
  // formats are:
  //     div: #id
  //   image: blah.extension
  //    ajax: anything else
  function fillFaceboxFromHref(href, klass, push) {
    // div
    if (href.match(/#/)) {
      var url    = window.location.href.split('#')[0];
      var target = href.replace(url,'');
      $.facebox.reveal($(target).clone().show(), klass, push);

    // image
    } else if (href.match($.facebox.settings.imageTypesRegexp)) {
      fillFaceboxFromImage(href, klass, push);
    // ajax
    } else {
      fillFaceboxFromAjax(href, klass);
    }
  }

  function fillFaceboxFromImage(href, klass, push) {
    $.facebox.settings.staticOverlay = false;
    var image = new Image();
    image.onload = function() {
      $.facebox.reveal((push?'<h1 class="br">View Image:</h1>':'') + '<div class="image"><img src="' + image.src + '" /></div><div class="footer"><a class="cancel" href="#">' + (push ? 'Go back' : 'Close') + '</a></div>', klass, push);
    };
    image.src = href;
  }

  function fillFaceboxFromAjaxPost(href, args, klass) {
  	$.post(href, args, function(data) { $.facebox.reveal(data, klass); });
  }

  function fillFaceboxFromAjax(href, klass) {
    $.get(href, function(data) { $.facebox.reveal(data, klass); });
  }

  function skipOverlay() {
    return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null;
  }

  function showOverlay() {
    if (skipOverlay()) return;

    if ($('#facebox_overlay').length == 0)
      $("body").append('<div id="facebox_overlay" class="facebox_hide"></div>');

    if (!$('#facebox_overlay').hasClass("facebox_overlayBG")) {
      $('#facebox_overlay').hide().addClass("facebox_overlayBG")
        .css('opacity', $.facebox.settings.opacity)
        .click(function() { if (!$.facebox.settings.staticOverlay) { $(document).trigger('close.facebox'); } })
        .fadeIn(200);
    }
    return false;
  }

  function hideOverlay() {
    if (skipOverlay()) return;

    $('#facebox_overlay').fadeOut(200, function(){
      $("#facebox_overlay").removeClass("facebox_overlayBG");
      $("#facebox_overlay").addClass("facebox_hide");
      $("#facebox_overlay").remove();
    });

    return false;
  }

  /*
   * Bindings
   */

  var closing = false;

  $(document).bind('close.facebox', function(o, force, callback) {
  	if (typeof(callback) == 'undefined' && $.isFunction(force)) { callback = force, force = false; } // allow optional args
	if (closing && !callback && !force) return; // if force or callback ignore closing - to handle force or recursive close
	closing = true;
  	if (!force && getFrames().size() > 1) {
		var remFrame = function() { getLastFrame().remove(); doCrumbs(); if (callback) { callback(); } closing = false; };
		if (isSlide()) {
			slideLeft(remFrame);
		} else {
			remFrame();
		}
	} else {
		$(document).unbind('keydown.facebox');
	    $('#facebox').fadeOut(function() {
	      $('#facebox .content').removeClass().addClass('content').empty();
	      hideOverlay();
	      $('#facebox .loading').remove();
		  if (callback) callback();
		  closing = false;
	    });
	}
  });

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * Hunch dialog - relies on facebox.js
 */
;(function($, undefined) {
    var getFn = function(fn, animate, settings) {
        return function(evt) {
            if (animate) $(this).parents('div.buttons:first').addClass('loading');
            return fn.call(this, evt, settings);
        };
    };

    $.confirm = function(settings) {
        settings = $.extend({}, $.confirm.settings, settings);
        var text = (settings.text_fn ? (settings.text_fn.call(settings.self || this)) : settings.text),
            title = (settings.title_fn ? (settings.title_fn.call(settings.self || this)) : settings.title),
            html = (settings.html_fn ? (settings.html_fn.call(settings.self || this)) : settings.html),
            left = getFn(settings.left, settings.animate, settings),
            right = getFn(settings.right, settings.animate, settings),
            $dialog = $('<div>'),
            buttonsClass = settings.center_buttons ? 'clr text-center' : 'buttons',
            $buttons;

        if (settings.can_confirm && !settings.can_confirm()) return;

        if (title) {
            $dialog.append(
                $('<div>', {'class': 'header'})
                    .append($('<p>', {'class': 'strong', text: title}))
            );
        }

        if (text || html) {
            $dialog.append($('<div>', {text: text, html: html}));
        }

        $buttons = $('<div>', {
            'class': buttonsClass + ' br-top footer',
            html: '<span class="indicator">loading...</span>'
        });

        var clicked = false;

        if (settings.right) {
            $buttons.append(
                $('<button />')
                    .addClass(settings.right_class || '')
                    .append($('<span />').text(settings.right_text))
                    .click(function(evt) {
                        if (clicked) return false;
                        clicked = true;
                        return right.call(settings.self || this, evt);
                    })
            );
        }

        if (settings.center_buttons) $buttons.prepend('&nbsp;&nbsp;');

        $buttons[settings.center_buttons ? 'prepend' : 'append'](
            $('<button />')
                .addClass(settings.left_class || '')
                .append($('<span />').text(settings.left_text))
                .click(function(evt) {
                    if (clicked) return false;
                    clicked = true;
                    return left.call(settings.self || this, evt);
                })
        );

        if (settings.animate) {
            $buttons.append($('<span class="indicator">loading...</span>'));
        }

        $dialog.append($buttons);

        if (settings.can_close) {
            $dialog.append('<a href="#" class="close' + (title ? ' has-header' : '') + '">close</a>');
        }

        $.facebox($dialog);
    };
    $.confirm.settings = {
        text: 'Are you sure you want to continue?',
        sub_text: '',
        left_text: 'Yes',
        right_text: 'No',
        can_close: true
        //can_confirm: function(){},
        //left: function(){},
        //right: function(){}
        //text_fn
    };
    $.extend($.confirm, {
        close: function() {
            $(document).trigger('close.facebox');
            return false;
        },
        loading: function() {
            $('#facebox').find('div.footer').addClass('loading');
        }
    });

    $.fleck = function(url, rel_type, rel_id) {
        $.facebox({
            post: url,
            data: {rel_type: rel_type, rel_id: rel_id}
        });
        return false;
    };

    $.fn.extend({
        confirm: function(delegateSelector, settings) {
            // this is already $(this)
            if (settings === undefined) {
                settings = delegateSelector;
                delegateSelector = undefined;
            }
            function _confirm() {
                settings.self = this;
                $.confirm(settings);
                return false;
            }
            return delegateSelector ? this.delegate(delegateSelector, 'click', _confirm) :
                settings.live ? this.live('click', _confirm) :
                this.click(_confirm);
        },
        fleck: function(url, rel_type, rel_id, live) {
            var fleckClick = function() {
                return $.fleck(url, rel_type, $.isFunction(rel_id) ? rel_id.call(this) : rel_id);
            };
            if (live === true) {
                return $(this).live('click', fleckClick);
            } else {
                return $.each(this, function() { $(this).click(fleckClick); });
            }
        }
    });
})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * Hunch utils
 */
;(function($) {

    $.getQueryParams = function(url) {
        url = url || document.location.href;
        url = url.split('#')[0].split('?')[1] || '';
        if (url.substring(0, 1) == '?')
            url = url.substring(1, url.length);

        function decode(x) {
            return decodeURIComponent(x.split('+').join(' '));
        }

        var params = {}, pair, key, value;
        if (url) {
            url = url.split('#')[0].split('&');
            for (var i=0, len=url.length; i<len; i++) {
                pair = url[i].split('=');
                key = decode(pair[0]);
                value = pair[1] ? decode(pair[1]) : '';
                params[key] = value;
	        }
        }
        return params;
    };

    $.cleanParam = function(x, doQuestionMark) {
        // wrapper around $.param that converts things like "foo=" to just "foo",
        // e.g., {foo: "", bar: 2} -> foo&bar=2 instead of foo=&bar=2
        var _rClean = /(^|&)([^=]*)=(&|$)/g; // do re twice to catch consecutive occurences
        x = $.param(x).replace(_rClean, '$1$2$3').replace(_rClean, '$1$2$3');
        return (doQuestionMark && x ? '?' : '') + x;
    };


	$.addError = {
		defaults: {
			msg: 'This field is required',
			error_class: 'errorlist',
			where: 'before'
		},
		clearAll: function(error_class) {
			$('.'+(error_class ? error_class : 'errorlist')).remove();
		},
		addAll: function(errors, settings) {
			$.each(errors, function(k, v) {
				$('#id_'+k).addError($.extend({}, settings, {msg: v}));
			});
		}
	};

	$._hunchError = {
		defaults: {
			msg: 'Hunch encountered an error.<br/>Please Refresh the page to retry.',
			timeoutMsg: 'Hunch encountered a timeout error.<br/>Please Refresh the page to retry.'
		}
	};

	$.fn.extend({
		// Adds form errors
		addError: function(settings) {
			settings = $.extend({}, $.addError.defaults, settings);

			return this.each(function() {
				var $t = $(this), err, i = 0;
				$t.parent().children('.'+settings.error_class).remove();
				err = $('<ul/>').addClass(settings.error_class);
				if (!$.isArray(settings.msg)) settings.msg = [settings.msg];
				for (; i<settings.msg.length; i++) { err.append($('<li/>').html(settings.msg[i])); }
				if ($t.hasClass('error-in-parent') || settings.where_parent) $t.parent()[settings.where_parent || 'prepend'](err); // hack to allow custom placement relative to parent
				else $t[settings.where](err);
			});
		},
		// Removes form errors
		remError: function(settings) {
			settings = $.extend({}, $.addError.defaults, settings);
			return this.each(function() {
				$(this).parent().children('.'+settings.error_class).remove();
			});
		},
		// Displays javascript error message on the page
		hunchError: function(settings, force) {
			if (settings === true) {
				settings = null;
				force = true;
			}
			if (!force) return false; /* temporarily disabled */
            if (settings && settings.textStatus == 'abort') return false;
			var timeoutMsg = (settings.msg == null && settings.textStatus == 'timeout');
			settings = $.extend({}, $._hunchError.defaults, settings);
			if (timeoutMsg) settings.msg = $._hunchError.defaults.timeoutMsg;
			var t = $(this),
				top = settings.scrollTop || t.offset().top,
				error = $('<div class="hunch-error"/>').html(settings.msg);
    		t.prepend(error);
    		window.scrollTo(0, top);
			if (settings.fade)
				window.setTimeout(function() { error.fadeOut(); error = null; }, settings.fade);
			else
				error = null;
		}
	});

	$.hunchError = function(settings, force) {
		if (settings === true) {
			settings = null;
			force = true;
		}
		settings = $.extend({scrollTop: 0}, settings);
		$('body').hunchError(settings, force);
	};

    $.ajaxError = function(data, textStatus) {
        // ignore when ajax gets killed from a page reload...
        if (data && data.status === 0 && textStatus === '') return;
        $.hunchError({textStatus: textStatus}, true);
    };


	//
	// Sharing JS
	//

	// Callback takes a data argument that is of the form {url: <url>}
	var shortenUrlCache = {};
	$.shortenUrl = function(url, callback, noshorten) {
		if (noshorten) {
			setTimeout(function() { callback(url); }, 1);
		} else if (shortenUrlCache[url]) {
			// for consistency - make this asynchronous
			setTimeout(function() { callback(shortenUrlCache[url]); }, 1);
		} else {
			$.ajax({
				url: '/ws/shorten-url/',
				data: {url: url},
				type: 'post',
				dataType: 'json',
				error: function(data, textStatus) {
                    $.hunchError({textStatus: textStatus}, true);
                    callback(url, true);
                },
				success: function(data) { callback(shortenUrlCache[url] = data.url); }
			});
		}
		return false;
	};

    var loader = 'http://hunch.com/media/img/loading-32px.gif';

    $.clickSharePreload = function() { (new Image).src = loader; };

	var loadingWindow = function(width, height) {
		var w = window.open('', '_blank', (height && width ? 'height='+height+',width='+width : ''));
		w.document.write('<html><head><title>loading...</title></head><body><img src="'+loader+'" /></body></html>');
		w.document.close();
		return w;
	};

	$.clickFacebook = function(url, title, noshorten) {
		url = url || window.location.href;
		title = typeof(title) == 'undefined' ? document.title : title;
		var w = loadingWindow(625, 350);
		$.shortenUrl(url, function(url) {
			w.location = 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(url) + (title ? '&t=' + encodeURIComponent(title) : '');
		}, noshorten);
		return false;
	};

	$.clickTwitter = function(status, url, post_status, noshorten) {
		url = url || window.location.href;
		post_status = post_status || '';
		var w = loadingWindow(550, 370);
		$.shortenUrl(url, function(url) {
			status = status + (status ? ' ' : '') + url + (post_status ? ' ' : '') + post_status;
			w.location = 'http://twitter.com/share?text=' + encodeURIComponent(status) + '&url=';
		}, noshorten);
		return false;
	};

	$.clickEmail = function(subject, body, url, post_body, noshorten) {
		url = url || window.location.href;
		post_body = post_body || '';
		$.shortenUrl(url, function(url) {
			body = body + (body ? ' ' : '') + url + (post_body ? ' ' : '') + post_body;
			window.location = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body).replace('%0A', '%0D%0A'); // extra support for newlines
		}, noshorten);
		return false;
	};

	/*
	$.asyncScript = function(url) {
		// Is effectively async for pretty much everything but Opera and FF < 3.6
		var s = document.createElement('script');
		s.type = 'text/javascript';
		s.async = true;
		s.src = url;
		var s2 = document.getElementsByTagName('script')[0];
		s2.parentNode.insertBefore(s, s2);
	};*/

    /*! From mustache.js - See http://mustache.github.com/ for more info. */
    $.htmlEscape = function(s) {
        s = String(s === null ? "" : s);
        return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
            switch(s) {
              case "&": return "&amp;";
              case "\\": return "\\\\";
              case '"': return '&quot;';
              case "'": return '&#39;';
              case "<": return "&lt;";
              case ">": return "&gt;";
            default: return s;
            }
        });
    };

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * Hunch jquery loader
 */
;(function($) {
    $._hunchloader = {
        defaults: {},
        ajax: {
            type: 'post',
            timeout: 10000,
            dataType: 'json'
        }
    };

    $.postLoader = function(url, data, extra) {
        return doLoader(null, url, data, extra, 'post');
    };

    $.getLoader = function(url, extra) {
        return doLoader(null, url, null, extra, 'get');
    };

    $.fn.extend({
        postLoader: function(url, data, extra) {
            return doLoader(this, url, data, extra, 'post');
        },
        getLoader: function(url, extra) {
            return doLoader(this, url, null, extra, 'get');
        }
    });

    var doLoader = function(object, url, data, extra, type) {
        extra = $.extend({}, $._hunchloader.defaults, extra);
        var options = $.extend({}, $._hunchloader.ajax, extra.ajax);
        if (extra.cache === false) {
            options.cache = false;
        } else if (!extra.cache && type.toLowerCase() == 'get') {
            options.cache = false; /* don't cache GET's by default */
        }
        if (extra.timeout) {
            options.timeout = extra.timeout;
        }

        if (extra.thisLoader) {
            extra.thisLoader.addClass('loading');
        }

        var obj; // the dom element - prevent reloads, while loading
        if (object && !extra.allowConcurrent) {
            obj = object.get(0);
            if (obj) {
                if (obj.loading) {
                    return false;
                }
                obj.loading = true;
            }
            object.addClass('loading');
        }

        if (url) { options.url = url; }
        if (data) { options.data = data; }
        if (type) { options.type = type; }

        options.error = function(data, textStatus) {
            if (extra.error) { extra.error(data, textStatus); }

            $.hunchError();
        };

        options.success = function(data, textStatus) {

            // Call passed in success fn if given
            if (extra.success) {
                var ret = extra.success(data, textStatus);
                if (ret === false) {
                    return;
                }
            }

            if (data.error) {
                return $.options.error();
            } else {

                if (extra.thisLoader) {
                    extra.thisLoader.removeClass('loading');
                }

                // Add html if returned
                if (object) {
                    object.removeClass('loading');
                    if ((data.html = $.trim(data.html))) {
                        if (extra.replaceWith) {
                            var new_object = $(data.html);
                            object.replaceWith(new_object);
                            new_object.addClass('loading').removeClass('loading'); /* safari bug doesn't fix the loading indicator sometimes... */
                        } else {
                            object.html(data.html);
                        }
                    }
                }

                // Do selectors if returned
                if (data.replace) {
                    $.each(data.replace, function() {
                        if (this.html !== null) {
                            $(this.slct).html(this.html);
                        }
                    });
                }
            }
        };

        options.complete = function(data, textStatus) {
            if (extra.complete) { extra.complete(data, textStatus); }
            if (obj) { obj.loading = false; }
        };

        $.ajax(options);

        return false;
    };
})(jQuery);

/* Copyright (C) 2011, Hunch.com */


(function($) {

    $.ajaxFileUpload = function(cfg) {
        cfg = $.extend(
            {error: $.noop},
            $.ajaxSettings,
            cfg,
            {dataType: 'json'}
        );

        // make the form and iframe elements
        var id = new Date().getTime(),
            $form = create$Form(id, cfg.fileElementId),
            $iframe = create$Iframe(id, cfg.secureuri);

        // prepare and submit the form
        try {
            $form
                .attr('action', cfg.url)
                .attr('method', 'POST')
                .attr('target', $iframe.attr('id'))
                .submit();
        } catch (x) {
            cfg.error(null, x);
        }

        // add load event to the iframe
        $iframe.load(function() {
            try {
                var iframe = $iframe.get(0);
                var doc = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument.document;
                var responseText = doc.body ? doc.body.innerHTML : null;
                var status = 'success';
                var data = parseData(responseText);
                if (cfg.success) {
                    cfg.success(data, 'success');
                }
            } catch (x) {
                if (cfg.error) {
                    cfg.error(null, x);
                }
                return;
            }
        });

        //TODO - timeout?

        // return an XHR-like object
        return {
            abort: function() {
                $iframe.remove();
            }
        };
    };


    function parseData(data) {
        if (data.substr(1,33) == '<h1>Request Entity Too Large</h1>') {
            // handle the result of uploading a file that is too large
		    data = {'valid':false, 'error':'Uploaded file is too large, please select a smaller file.'};
		} else {
            // deal with html that ends up in the responseText...
            data = data.replace(/^<pre[^>]*>/gi, '').replace(/<\/pre>$/gi, '');
            data = $.parseJSON(data);
		}
        return data;
    }

    function create$Iframe(id, URI) {
		//create frame
        var io;
        var frameId = 'jUploadFrame' + id;

        if (window.ActiveXObject) {
            io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
            if (typeof URI == 'boolean'){
                io.src = 'javascript:false';
            } else if (typeof URI == 'string'){
                io.src = URI;
            }
        }
        else {
            io = document.createElement('iframe');
            io.id = frameId;
            io.name = frameId;
        }
        io.style.position = 'absolute';
        io.style.top = '-1000px';
        io.style.left = '-1000px';

        document.body.appendChild(io);
        return $(io);
    }

    function create$Form(id, fileElementId) {
        //create form
		var formId = 'jUploadForm' + id;
		var fileId = 'jUploadFile' + id;
		var $form = $('<form>', {
            action: '',
            method: 'POST',
            name: formId,
            id: formId,
            enctype: 'multipart/form-data',
            encoding: 'multipart/form-data'
        });

		var $oldElement = $('#' + fileElementId);
		var $newElement = $oldElement.clone();
		$oldElement.attr('id', fileId);
		$oldElement.before($newElement);
		$oldElement.appendTo($form);

		//set attributes
		return $form
            .css('position', 'absolute')
		    .css('top', '-1200px')
		    .css('left', '-1200px')
		    .appendTo('body');
    }

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * jQuery/Hunch Ajax File Upload plugin
 * Relies on: jquery 1.2+, ajaxFileUpload
 */
 ;(function($) {

    $.fileupload = {
        defaults: {
            auto_id: 'id_',
            url: '/teach/ajax-image-upload/',
            bg: null
        },
        uploads_count: 0,
        getUploadsCount: function() { return $.fileupload.uploads_count; },
        incUploadsCount: function() { $.fileupload.uploads_count += 1; },
        decUploadsCount: function() { $.fileupload.uploads_count -= 1; },
        isUploading: function() { return $.fileupload.getUploadsCount() > 0; }
    };

    $.fn.extend({
        fileupload: function(settings) {
            settings = $.extend({}, $.fileupload.defaults, settings);

            return this.each(function(i,o) {
                var t = $(o);
                if (t.attr('type') == 'file') {

                        // name of this element
                    var name = t.attr('name').split('-'),
                        // prefix for this element
                        pref,
                        // the id of this element
                        id;

                    if (name.length == 1) {
                        pref = '', name = name[0];
                    } else if (name.length == 2) {
                        pref = name[0]+'-', name = name[1];
                    } else {
                        // only accepts attr names of "<prefix>-<name_with_underscores>"
                        return;
                    }

                    id = settings.auto_id+pref+name;

                    t.change(function() { return doFileUpload(settings.url, pref, name, id, settings.container, settings.bg, settings.callback, settings.error_selector); })
                     .bind('showFileInput', function() { showFileInput(id); })
                     .bind('hideFileInput', function() { hideFileInput(id); })
                     .bind('presetFileInput', function(event, custom_text) { hideFileInput(id, custom_text); } )
                     .bind('determineFileInput', function() { determineFileInput(id); });

                    t.trigger('determineFileInput');
                }
            });
        }
    });

    function doFileUpload(url, pref, name, id, container, bg, callback, error_selector) {
        // let us display error messages wherever we want, using error_selector
        var $error;
        if (error_selector) { $error = $(error_selector); }
        else { $error_li = $('<li>'); $error_ul = $('<ul class="errorlist">'); $error_ul.append($error_li); $('#'+id).before($error_ul); $error = $error_li; }
        $.fileupload.incUploadsCount();
        $('#'+id+'+.ajax-loader', container).remove();
        $('#'+id, container).after($('<img class="ajax-loader" style="padding-left:8px;"/>').attr('src', '/media/img/ajax-loader-form'+({'white': '-w'}[bg] || '')+'.gif'));
        remFileError($error);

        var full_url = url;
        if (pref) { full_url += '?prefix=' + pref.substr(0, pref.length-1); }

        $.ajaxFileUpload({
            url: full_url,
            secureuri: false,
            fileElementId: id,
            dataType: 'json',
            success: function(data, status) {
                $('#'+id+'+.ajax-loader', container).remove();
                if (typeof(data.error) != 'undefined') {
                    if (data.error != '' || !data.valid) {
                        //alert("Data error is: " + data.error);
                            addFileError(data.error, $error);
                    } else {
                        //alert("image_url=" + data.image_url + ", image_id=" + data.image_id + ", image_image_id=" + data.image_image_id + ", image_name=" + data.image_name);
                        $('#'+id+'_url', container).attr("value", data.image_url);
                        $('#'+id+'_id', container).attr("value", data.image_id);
                        $('#'+id+'_image_id', container).attr("value", data.image_image_id);
                        $('#'+id+'_name', container).attr("value", data.image_name);
                        hideFileInput(id);
                        if (callback) callback(data, status);
                    }
                }

                $('#'+id).change(function() { return doFileUpload(url, pref, name, id, container, bg, callback, error_selector); });

                $.fileupload.decUploadsCount();
            },
            error: function(data, status, e) {
                $('#'+id+'+.ajax-loader', container).remove();
                if (window.console && window.console.log) {
                    window.console.log('File upload javascript error');
                    window.console.log(status);
                    window.console.log(e);
                }
                //alert('File upload javascript error: ' + status + e);
                $('#'+id).change(function() { return doFileUpload(url, pref, name, id, container, bg, callback, error_selector); });
                $('body').append(data.repsonseText);

                $.fileupload.decUploadsCount();
            }
        });

        return false;
    }

    /* makes a value - previewable - escapes and line breaks */
    function cleanVal(x) {
        if (x) { return x.replace(/</i, "&lt;").replace(/>/i, "&gt;").replace(/\\n/i, "<br />"); }
        else { return x; }
    }

    function determineFileInput(id) {
        if ($('#'+id+'_name').val()) {
            hideFileInput(id);
        }
    }

    function hideFileInput(id, custom_text) {
        $('#'+id).hide();
        var other_id = 'uploaded-text-'+id;
        if (!$('#'+other_id).size()) { $('#'+id).after($('<span/>').attr('id', other_id)); }
        $('#'+other_id).html('')
                       .append('using <em>' + (custom_text ? cleanVal(custom_text) : cleanVal($('#'+id+'_name').val())) + '</em> ')
                       .append($('<a href="javascript:void(0)"/>').html('change').click(function() { showFileInput(id) }))
                       .show();
    }

    function showFileInput(id) {
        $('#uploaded-text-'+id).hide();
        $('#'+id).show();
    }

    function remFileError($error) {
        $error.empty();
    }

    function addFileError(error, $error) {
        remFileError($error);
        $error.html(error);
   }
})(jQuery);

/* Copyright (C) 2011, Hunch.com */

 /*
  * jQuery/Hunch Flag plugin
  *
  * Make sure whatever you call this on has data-type="..." and data-object_id="..."
  */
;(function($, undefined) {
    $.flag = {
        defaults: {
            url: '/ws/flag/',
            max_msg: 400
        },
        open: false
    };

    $.fn.flag = function(delegateSelector, cfg) {
        if (typeof(delegateSelector) != 'string' && cfg) {
            cfg = delegateSelector;
            delegateSelector = null;
        }
        cfg = $.extend({}, $.flag.defaults, cfg);

        function flagClick(e) {
            e.preventDefault();
            var $this = $(this),
                type = $this.data('type'),
                object_id = $this.data('object_id');

            if (type && object_id) {
                var data = {
                    type: type,
                    object_id: object_id
                };
                $.flag.open = true;
                $.facebox(function() {

                    function flagSuccess(data, textStatus) {
                        if (data.error) {
                            $.ajaxError();
                        } else {
                            data.html && $.facebox(data.html);
                            if (data.success) {
                                window.setTimeout(function() {
                                    $(document).trigger('close.facebox');
                                }, 4000);
                            } else {
                                var $fb = $('#facebox');
                                $fb.find('textarea').remainingMulti(null, cfg.max_msg);
                                $fb.find('form').submit(function(e) {
                                    e.preventDefault();

                                    if (this.active) return;
                                    this.active = true;

                                    $.ajax({
                                        url: cfg.url,
                                        data: $(this).addClass('loading').serialize(),
                                        type: 'post',
                                        dataType: 'json',
                                        error: $.ajaxError,
                                        success: flagSuccess
                                    });
                                });
                            }
                        }
                    }

                    $.ajax({
                        url: cfg.url,
                        data: data,
                        dataType: 'json',
                        error: $.ajaxError,
                        success: flagSuccess
                    });
                });
            }
        }

        return delegateSelector ? this.delegate(delegateSelector, 'click', flagClick)
            : cfg.live ? this.live('click', flagClick)
            : this.click(flagClick);
    };

    $(document).bind('clear.flag', function() {
        if ($.flag.open) {
            $.flag.open = false;
            $(document).trigger('close.facebox');
        }
    });

    $(document).bind('close.facebox', function() {
        $.flag.open = false;
    });

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * jQuery/Hunch Placeholder plugin
 *
 * NOTE: consider using persistant placeholders instead...
 * (as seen on http://hunch.com/media/test/elements.html)
 */
;(function($) {
	$.placeholder = {
		defaults: {
			ignore: (navigator.userAgent.toLowerCase().indexOf('safari') >= 0)
		}
	};

	$.fn.extend({
		placeholder: function(settings) {
			var $win = $(window);
			settings = $.extend({}, $.placeholder.defaults, settings);
			if (settings.ignore) return this;

			return this.each(function() {
				var t = $(this),
					reset = function() { if (t.val() == t.attr('placeholder')) placeholdersClear(t); return true; };

				if (t.attr('placeholder') != null) {
					if (t.val() == '' || t.val() == t.attr('placeholder')) { placeholdersSet(t); }
					if (!settings.ignoreSubmits) {
						t.parents('form').submit(reset);
					}
					t.blur(function() { if (t.val() == '') placeholdersSet(t); })
					 .focus(function() { if (t.val() == t.attr('placeholder')) placeholdersClear(t); })
					 .keydown(function() { if (/(^|\s)placeholder(\s|$)/.test(this.className)) placeholdersClear($(this)); });

					t.bind('clearPlaceholder', function() {
						if (t.val() == t.attr('placeholder')) { return placeholdersClear($(this)); }
						else { return $(this); }
					});
					t.bind('setPlaceholder', function() {
						if (t.val() == '') { return placeholdersSet($(this)); }
						else { return $(this); }
					});
					t.bind('resetPlaceholder', function() {
						if (t.hasClass('placeholder') || t.val() == '') { return placeholdersSet($(this)); }
						else { return $(this); }
					});
					$win.unload(reset); // supposed to prevent the firefox caching?
				}
			});
		},
		valNoPlaceholder: function() {
			var t  = $(this);
			if (t.val() == t.attr('placeholder')) return '';
			else return t.val();
		}
	});

	function placeholdersSet(t) {
		return t.addClass('placeholder').val(t.attr('placeholder')); //.blur(); //NO BLUR - messes up whatever else is focused in FF3
	}

	function placeholdersClear(t) {
		return t.val('').removeClass('placeholder');
	}
 })(jQuery);

/* Copyright (C) 2011, Hunch.com */

/*
 * Persistent Placeholder - treats a label like a placeholder and
 * makes it persist even when you focus on an input. Huzzah!
 */
(function($) {

    var parentSelector = '.input-wrapper',
        inputSelectors = [parentSelector+'>input.text', parentSelector+'>textarea'],
        len = inputSelectors.length,
        i;

    function update(force) {
        var $input = $(this),
            $parent = $input.parent(parentSelector);
        return $parent[force === true || $input.val() ? 'addClass' : 'removeClass']('filled');
    }

    function focus() {
        update.call(this).addClass('focus');
    }

    function blur() {
        update.call(this).removeClass('focus');
    }

    function keydown(evt) {
        var c = evt.keyCode;
        ((47 < c && c < 91) || (95 < c && c < 112) || (185 < c && c < 223)) && update.call(this, true);
    }

    $.fn.prepareInput = function() {
        return this.each(update);
    };

    for (i=0; i<len; i++) {
         $(inputSelectors[i]).live('focus', focus).live('blur', blur).live('keyup', update).live('click', update).live('keydown', keydown).live('update.placeholder', update);
    }

    $(function() {
        for (i=0; i<len; i++) {
            $(inputSelectors[i]).prepareInput();
        }
    });

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

 /*
 * jQuery/Hunch Toggle plugin
 *
 * In addtion to the defaults below - you must specify a url, text,
 * and (data object or getData function) for each state (setTrue & setFalse)
 *
 * Optionally set data at the base level instead and both setTrue and setFalse will use that.
 *
 * Requires:
 *  jquery.hunch-utils.js
 *
 */
;(function($) {

	$.hunchtoggle = {
		defaults: {
			live: false,
			setClass: 'is-set',
			setTrue: {
				url: '',
				text: ''
			},
			setFalse: {
				url: '',
				text: ''
			},
			animate: false,
			is_button: false
		}
	};

    $.savetoggle = {
        setClass: 'saved',
        setTrue: {
            url: '/feed/ws/rate/',
            html: '<em>saved for later</em><em class="unsave">remove save</em>',
            clickedClass: 'clicked-save'
        },
        setFalse: {
            url: '/feed/ws/rate/',
            html: 'save for later',
            clickedClass: 'clicked-saved'
        },
        getData: function() {
            return {
                type: 'save',
                result_id: $(this).data('result_id')
            };
        }
    };

	$.fn.extend({
		hunchtoggle: function(settings) {

			settings = $.extend({}, $.hunchtoggle.defaults, settings);

            if (settings.setTrueCallback) {
                settings.setTrue.callback = settings.setTrueCallback;
                delete settings.setTrueCallback;
            }

            if (settings.setFalseCallback) {
                settings.setFalse.callback = settings.setFalseCallback;
                delete settings.setFalseCallback;
            }

			function setText(obj, text, html) {
				if (settings.is_button) {
                    html ?
                        obj.children('span').html(html) :
					    obj.children('span').text(text);
				} else {
					html ? obj.html(html) : obj.text(text);
				}
				return obj;
			};

			var toggle = function(evt, noSend) {
				if (!this.clicked) {
					this.clicked = true;
					var t = this, $t = $(this), url, text, data;

					var was_set = $t.hasClass(settings.setClass);
					var state = (was_set ? settings.setFalse : settings.setTrue);

					url = state.url;
					text = state.text;

					if (settings.data) { //TODO - these should probably be ordered in the reverse of what they are now?
						data = settings.data;
					} else if (settings.getData) {
						data = settings.getData(t, !was_set);
					} else if (state.data) {
						data = state.data;
					} else if (state.getData) {
						data = state.getData(t);
					} else {
						var e = 'Did not specify data or getData on the toggle call for this elt!';
						throw new Error(e, e);
					}

					var had_action = $t.hasClass('action');
					var had_action_favorite =  $t.hasClass('action-favorite');

					if (settings.animate) {
						if (!had_action) $t.addClass('action');
						if (!had_action_favorite) $t.addClass('action-favorite');
						setText($t.addClass('action-loading'), 'loading...');
					}

                    if (state.clickedClass) $t.addClass(state.clickedClass);

					var request = {
						url:url, data:data, type:'post', dataType:'json', timeout:10000,
						success: function(data, textStatus) {
                            if (state.clickedClass) $t.removeClass(state.clickedClass);

							if (data.status == 'success') {
								if (was_set) {
									setText($t.removeClass(settings.setClass), state.text, state.html);
								} else {
									setText($t.addClass(settings.setClass), state.text, state.html);
								}
							} else if (data.status == 'limit') {
								if (settings.animate) setText($t, settings.setFalse.text, settings.setFalse.html);
								$.hunchError({msg: 'You have reached the limit for this action.<br />Sorry for the inconvenience.', fade: 4000}, true);
							} else {
								$.hunchError(true);
							}
                            var cbs = [state.callback, settings.callback];
                            for (var i=0, len=cbs.length; i<len; i++) {
                                cbs[i] && cbs[i].call(t, data.status == 'success', !was_set);
                            }
						},
						error: function(data, textStatus) {
							$.hunchError();
						},
						complete: function(data, textStatus) {
							t.clicked = false;
							if (settings.animate) {
								if (!had_action) $t.removeClass('action');
								if (!had_action_favorite) $t.removeClass('action-favorite');
								$t.removeClass('action-loading');
							}
						}
					};

                    // for case when we don't send the data
                    if (noSend) {
                        var fakeData = {status: 'success'};
                        request.success(fakeData);
                        request.complete(fakeData);
                        this.clicked = false;
                        return false;
                    }

					// Allow a confirm to be called on either set true or set false state
					if (settings.confirmTrue && !was_set) {
						settings.confirmTrue.toggleRequest = request;
						settings.confirmTrue.self = this;
						$.confirm(settings.confirmTrue);
					} else if (settings.confirmFalse && was_set) {
						settings.confirmFalse.toggleRequest = request;
						settings.confirmFalse.self = this;
						$.confirm(settings.confirmFalse);
					} else {
						$.ajax(request);
					}
				}
				return false;
			};

			if (settings.live) {
                this.live('click', toggle);
                this.live('hunchtoggle', toggle);
            } else {
                this.click(toggle);
                this.bind('hunchtoggle', toggle);
            }
		},
        followtoggle: function(settings) {
            var defaultSettings = {
                setClass: 'following',
                setTrue: {
                    url: '/people/ws/follow/',
                    html: '<s></s><em>Following</em><em class="unfollow">Unfollow</em>',
                    clickedClass: 'clicked-follow'
                },
                setFalse: {
                    url: '/people/ws/unfollow/',
                    html: 'Follow',
                    clickedClass: 'clicked-following'
                },
                getData: function(el) {
                    var $el = $(el);
                    //$el.blur(); //HACK - since element gets stuck with active state let's remove it!
                    return {user_id:  $el.data('user_id')};
                }
            };
            settings = $.extend(defaultSettings, settings);

            if (settings["followClickTrackPage"]) {
                function trackFollowClick() {
                    mpq.push(["track", "follow_click", {"page": settings["followClickTrackPage"]}]);
                }
                if (settings["setTrueCallback"]) {
                    var oldSetTrueCallback = settings["setTrueCallback"];
                    settings["setTrueCallback"] = function(a,b,c) {
                        trackFollowClick();
                        oldSetTrueCallback.call(this,a,b,c);
                    };
                } else {
                    settings["setTrueCallback"] = trackFollowClick;
                }
            }
            return this.hunchtoggle(settings);
        },
        savetoggle: function(settings) {
            //TODO - this function isn't technically being used yet, test if before blindly using it...
            settings = $.extend({}, $.savetoggle, settings);
            return this.hunchtoggle(settings);
        }
	});
})(jQuery);

/* Copyright (C) 2011, Hunch.com */

;(function($) {
    $.remainingCount = function(text) {
        return text ? (text+'').replace(/\r\n/g, '\n').length : 0; //TODO - convert to string in a better way
    };

	$.fn.extend({
        /*TODO: stop using remaining - and shift over to using remainingMulti - it's way better */
		remaining: function($count_container, max, remaining_text, too_many_text) {
            return this.remainingMulti($count_container, max, remaining_text, too_many_text);
		},
        remainingMulti: function(get_count_container, max, remaining_text, too_many_text) {
            return this.each(function() {
                var $this = $(this), $count_container;

                if (!get_count_container) {
                    var $parent = $this.closest('.input-wrapper');
                    $count_container = $('<p>', {'class': 'remaining'}).insertAfter($parent.length ? $parent : $this);
                } else {
                    $count_container = $.isFunction(get_count_container) ?
                        get_count_container.call(this, this) :
                        get_count_container;
                }

                function doIt() {
                    var len = $(this).remainingCount(),
                        text = len > max ?
                        '<span class="warn-text"><strong>'+(len-max)+'</strong> '+(too_many_text || 'characters too many')+'</span>' :
                        '<strong>'+(max-len)+'</strong> '+(remaining_text || 'characters remaining');

                    $count_container.html(text);
                }

                $this.change(doIt).keyup(doIt);
                doIt.call(this);
            });
        },
        remainingCount: function() {
            // this is $(this)
            return $.remainingCount(this.valNoPlaceholder());
        }
	});
})(jQuery);

/* Copyright (C) 2011, Hunch.com */

(function($) {
    $.hoverHelper = {
        defaults: {
            className: 'hover-helper',
            htmlWrapper: '#container',
            content: null,
            lineHeight: 18
        },
        starDefaults: {
            content: '<p><strong>Are we right?</strong><br />If not, hover and click the stars to correct us.</p>',
            leftExtra: -34,
            onClose: function() { $.post('/ws/saw-stars-help/'); }
        }
    };
    $.fn.extend({
        hoverHelper: function(cfg) {
            cfg = $.extend($.hoverHelper.defaults, cfg);

            var $htmlWrapper = $(cfg.htmlWrapper);

            return $(this).each(function() {
                var $this = $(this),
                    $parent = $('<div />', {'class': cfg.className}).appendTo(cfg.htmlWrapper);

                function closeIt(prevent) {
                    return function(evt) {
                        if (prevent) evt.preventDefault();
                        if ($parent) {
                            $parent.remove();
                            $parent = null;
                            $this = null;
                            if (cfg.onClose) cfg.onClose();
                        }
                    };
                }

                $this.find('a').click(closeIt(1)); // for stars

                $parent
                    .append($('<div />', {'class': 'content'}).html(cfg.content))
                    .append($('<a />', {'class': 'close', href: '#', text: 'X'}).click(closeIt(0)))
                    .append('<b class="arrow"></b>');

                function position() {
                    if ($parent === null || $this === null) return;

                    // handle visibility
                    if ($this.filter(':visible').length) {
                        $parent.show();
                    } else {
                        $parent.hide();
                        return;
                    }

                    // handle position
                    var offset = $this.offset(),
                        wrapperOffset = $htmlWrapper.offset(),
                        height = $parent.outerHeight();

                    offset.top = offset.top - wrapperOffset.top;
                    offset.left = offset.left - wrapperOffset.left;

                    $parent
                        .css({
                            top: (offset.top - height - cfg.lineHeight + 2) + 'px',
                            left: (offset.left + (cfg.leftExtra || 0)) + 'px'
                        });
                };

                // position the hover!
                (function go() {
                    if ($parent && $this) {
                        position();
                        window.setTimeout(go, 800);
                    }
                })();

                // bind a position event to the hoverHelper and the hoverable object
                $parent.bind('position.hoverHelper', position);
                $this.bind('position.hoverHelper', position);

            });
        },
        starHelper: function(cfg) {
            return $(this).hoverHelper($.extend({}, $.hoverHelper.starDefaults, cfg));
        }
    });
})(jQuery);

/*! Copyright (C) 2011, Hunch.com */

(function($) {

    //
    // Rating Stars
    //
    var _starToText = {
        '0': 'Not interested&nbsp;',
        '5': 'I <b>love</b> this&nbsp;',
        '4': 'I <b>like</b> this&nbsp;',
        '3': 'It’s <b>okay</b>&nbsp;',
        '2': 'I <b>dislike</b> this&nbsp;',
        '1': 'I <b>hate</b> this&nbsp;'
    };
    function starToText(num_stars) { return _starToText[''+num_stars] || ''; }

    function starFeedback(num_stars) {
        return function(evt, no_ajax, no_trigger) {
            evt.preventDefault();
            var $t = $(this),
                $parent = $t.closest('.stars'),
                $cur = $parent.find('.si-cur'),
                old_cur_class = $cur.attr('class'),
                old_style = $cur.attr('style'),
                result_id = $parent.data('id'),
                $stars_text = $parent.siblings('.stars-text'),
                has_rated = $cur.hasClass('si-u'), // if the user has rated the item already
                old_stars_html,
                new_stars_html;

            $cur.attr('style', '').attr('class', 'si-cur si-u si'+num_stars);

            if (num_stars == 0) $cur.parent().removeClass('si-percent-wrap'); // special case for .si-percent-wrap

            if ($stars_text.length) {
                old_stars_html = $stars_text.html();
                new_stars_html = starToText(num_stars);
                $stars_text.data('orig_text', new_stars_html);
                $stars_text.html(new_stars_html);
            }

            function errorCase(data, textStatus) {
                $cur.attr('class', old_cur_class).attr('style', old_style);
                if ($stars_text.length)
                    $stars_text.data('orig_text', old_stars_html).html(old_stars_html);
                $.hunchError({textStatus: textStatus}, true);
                if (num_stars == 0) $cur.parent().addClass('si-percent-wrap'); // special case for .si-percent-wrap
            }

            if (no_ajax !== true && !$parent.data('no_ajax')) {
                $.ajax({
                    url: '/ws/ws_train_item/',
                    type: 'post',
                    dataType: 'json',
                    data: {
                        result_id: result_id,
                        num_stars: num_stars,
                        source: 'refiner'
                    },
                    success: function(data, textStatus) {
                        if (!data.ok) {
                            errorCase(data, textStatus);
                        } else {
                            $(document).trigger('starsuccess.hunch', [$parent, result_id, num_stars, has_rated]);
                        }
                    },
                    error: errorCase
                });
            } else if (!no_trigger) {
                $(document).trigger('starsuccess.hunch', [$parent, result_id, num_stars, has_rated]);
            }

	        !no_trigger && window.mpq && window.mpq.push(['track', 'rate_item', {'rating': num_stars}]);

        };
    }

    function starHover(num_stars) {
        return function(evt) {
            var $stars_text = $(this).closest('.stars').siblings('.stars-text'),
                orig_text = $stars_text.data('orig_text');
            if (!orig_text) $stars_text.data('orig_text', (orig_text = $stars_text.html()));
            if (evt.type == 'mouseover') {
                $stars_text.html(starToText(num_stars));
            } else {
                $stars_text.html(orig_text);
            }
        };
    }

    // window.hnStarHover = function(elt) { // this doesn't really work properly yet...
    //     if (elt.isHoverReady) return;
    //     elt.isHoverReady = true;
    //     var $par = $(elt).find('.si-rate');
    //     for (var i=1; i<6; i++)
    //         $par.children('a.si'+i).hover(starHover(i));
    // };

    //TODO - if the live mouseover / mouseout is too slow, consider adding stuff to the actual html elements...
    //NOTE - ipad and iphone are jerks for some reason with multiple star clicks, so lets us touchstart instead...
    var i = 0,
        clickEvent = /(iPad|iPhone)/i.test(navigator.userAgent) ? 'touchstart' : 'click';

    for (; i<6; i++) {
        $('a.si'+i)
            .live(clickEvent + ' starrate.hunch', starFeedback(i))
            .live('mouseover mouseout', starHover(i));
    }


    // for (var i=1; i<6; i++)
    //     $('a.si'+i).click(starFeedback(i)).hover(starHover(i), starHover(i));

})(jQuery);

/* Copyright (C) 2011, Hunch.com */

(function($) {

$.fn.imageScaleLoader = function(b_width, b_height, cfg) {

    // convert arguments to support imageScaleLoader(cfg) and imageScaleLoader(b_width, b_height, cfg)
    if ($.isPlainObject(b_width) && !b_height && !cfg) {
        cfg = b_width;
        b_width = null;
    }

    cfg = $.extend(
        {
            width: b_width || 120,
            height: b_height || 100,
            placeholder_url: '/media/img/t.png',
            attr: 'src' //TODO - test to see if 'src' can actually be used reliably...
        },
        cfg
    );

    return this.each(function() {
        var image = new Image(),
            $this = $(this), //TODO - set width and height attrs too?
            src = $this.attr(cfg.attr);

        if (src && !$.data(this, '_imageScaleLoader')) {
            $.data(this, '_imageScaleLoader', true);

            if ($this.attr('src') != cfg.placeholder_url)
                $this.attr('src', cfg.placeholder_url);

            image.onload = function () {
                var h = this.height,
                    w = this.width,
                    wscale, hscale, scale;

                if (parseInt(w) > cfg.width || parseInt(h) > cfg.height) {
			        wscale = w / cfg.width;
                    hscale = h / cfg.height;
			        scale = (wscale < hscale ? hscale : wscale);
			        w = w/scale;
                    h = h/scale;
		        }

                w = parseInt(w);
                h = parseInt(h);

                $this.attr('src', this.src)
                    .attr('width', w)
                    .attr('height', h)
                    .css({width: w+'px', height: h+'px'});

                if (cfg.onload) cfg.onload.call($this.get(0), w, h);
            };

            image.src = src;
        }
    });
};

})(jQuery);

// http://www.quirksmode.org/js/cookies.html cookies
$.cookies = {
    create: function (name, value, seconds, path) {
        var expires, date;
        path = path || '/';
        if (seconds) {
            date = new Date();
            date.setTime(date.getTime() + (seconds*1000));
            expires = '; expires=' + date.toGMTString();
        }
        else {
            expires = '';
        }
        document.cookie = name + '=' + value + expires + '; path=' + path;
    },
    read: function(name) {
        var nameEQ = name + '=',
            ca = document.cookie.split(';'),
            c;
        for (var i=0, len=ca.length; i<len; i++) {
            c = ca[i];
            while (c.charAt(0)==' ') {
                c = c.substring(1, c.length);
            }
            if (c.indexOf(nameEQ) === 0) {
                return c.substring(nameEQ.length, c.length);
            }
        }
        return null;
    },
    erase: function(name, path) {
        this.create(name, '', -1, path);
    }
};
(function(a){a.fn.extend({elastic:function(c){var b=["paddingTop","paddingRight","paddingBottom","paddingLeft","fontSize","lineHeight","fontFamily","width","fontWeight"];return this.each(function(){if(this.type!="textarea"){return false}var h=a(this),d=a("<div />").css({position:"absolute",display:"none","word-wrap":"break-word"}),j=parseInt(h.css("line-height"),10)||parseInt(h.css("font-size"),"10"),l=parseInt(h.css("height"),10)||j*3,k=parseInt(h.css("max-height"),10)||Number.MAX_VALUE,e=0,g=0;if(k<0){k=Number.MAX_VALUE}d.appendTo(h.parent());var g=b.length;while(g--){d.css(b[g].toString(),h.css(b[g].toString()))}function m(i,n){curratedHeight=Math.floor(parseInt(i,10));if(h.height()!=curratedHeight){h.css({height:curratedHeight+"px",overflow:n})}}function f(){var o=h.val().replace(/&/g,"&amp;").replace(/  /g,"&nbsp;").replace(/<|>/g,"&gt;").replace(/\n/g,"<br />");var i=d.html().replace(/<br>/ig,"<br />");if(o+"&nbsp;"!=i){d.html(o+"&nbsp;");if(Math.abs(d.height()+j-h.height())>3){var n=d.height()+j;if(n>=k){m(k,"auto")}else{if(n<=l){m(l,"hidden")}else{m(n,"hidden")}}}}}h.css({overflow:"hidden"});h.bind("keyup change cut paste",function(){f()});if(!c){h.bind("blur",function(){if(d.height()<k){if(d.height()>l){h.height(d.height())}else{h.height(l)}}})}h.live("input paste",function(i){setTimeout(f,250)});f()})}})})(jQuery);
/* Copyright (C) 2011, Hunch.com */


/* hunch hovercard */

$.hovercard = {


    //
    // VARIABLES
    //

    card: null,
    cardID: 'hunch-hovercard',
    cardClass: 'hunch-hovercard',
    cardInner: null,

    showTimeout: null,
    hideTimeout: null,

    emptyCfg: {},
    currentCfg: this.emptyCfg,
    currentElt: null,

    defaults: {
        ajaxCache: true, //NOTE - this only caches the url, not the data (maybe fix this in the future)

        gravity: 'top',

        // these offsetFoo values depend on which gravity is chosen
        offsetY: 11,

        showDelay: 120,
        hideDelay: 120,

        borderColor: '#ddd',
        borderWidth: '4px',
        borderRadius: '2px',

        arrow: {
            size: 9
        },

        arrowInner: {
            size: 9,
            color: '#fff',
            offset: 6
        },

        loadingCardWidth: 100,

        loader: {
            src: 'http://hunch.com/media/img/loading-333-fff-16px.gif',
            width: 16,
            height: 16
        }
    },


    //
    // METHODS
    //

    setup: function() {
        // for setting up global params like cardID, cardClass
    },

    //
    // Prepares the hovercard for use on this page (only runs once)
    //
    init: function(cfg) {
        if (this._did_init) return;
        this._did_init = true;

        var self = this;

        this.card = $('<div />', {
            'class': this.cardClass,
            'id': this.cardID
        })
            .appendTo('body')
            .html('<div class="hc-bubble"><div class="hc-content"></div></div><b class="arrow"></b><b class="arrow arrow-inner"></b>')
            .hover(function() {
                self._cardHoverIn();
            }, function() {
                self._cardHoverOut();
            });
        this.cardInner = this.card.find('.hc-content').first();
        this.cardBubble = this.card.find('.hc-bubble').first();
    },

    //
    // Shows the card next to the given element with the given content or a loader (after a timeout)
    //
    showCard: function(elt, cfg, content) {

        this._clearHideTimeout();
        if (!this._did_init) this.init(cfg);

        if (this.currentElt === elt) {
            return;
        }

        this._clearShowTimeout();

        var self = this;
        this.showTimeout = window.setTimeout(function() {

            self.clearCard();
            self.currentElt = elt;
            self.currentCfg = cfg;

            if (cfg.url && !content && cfg.loader) {
                content = $('<img/>', cfg.loader);
            }
            self.cardInner.html(content);
            if (cfg.loadingCardWidth) self.card.css('width', cfg.loadingCardWidth);

            if (cfg.borderColor) self.cardBubble.css('borderColor', cfg.borderColor);
            if (cfg.borderWidth) self.cardBubble.css('borderWidth', cfg.borderWidth);
            if (cfg.borderRadius) self.cardBubble.css('borderRadius', cfg.borderRadius);

            self.card.show();
            self._positionCard();

            if (cfg.content) {
                var content = $.isFunction(cfg.content) ? cfg.content(elt) : cfg.content;
                self.updateCard(content, elt, null, cfg);
            } else if (cfg.url) {
                 //TODO - support getting url from rel attribute or any other attribute too?
                var url = $.isFunction(cfg.url) ? cfg.url(elt) : cfg.url,
                    data = $.isFunction(cfg.data) ? cfg.data(elt) : cfg.data,
                    cache_url = data ? url + (url.split('?').length > 1 ? '&' : '?') + $.param(data) : url,
                    cached_data;

                // only do request if we got a url
                if (!url) {
                    self.card.hide();
                } else {
                    if (cfg.ajaxCache && (cached_data = self._cache.get(cache_url, cfg.namespace))) {
                        self.updateCard(cached_data, elt, cache_url, cfg);
                        if (cfg.postAjaxProcess) cfg.postAjaxProcess.call(self.card, self, cfg, cache_url);
                        //(hover-css-anim-fix) - this is polled now - self._positionCard();
                    } else {
                        var request = $.extend({
                            dataType: 'html',
                            type: 'get'
                        }, cfg.ajaxSettings, {
                            url: url,
                            success: function(data) {
                                if (cfg.ajaxProcess) data = cfg.ajaxProcess(data, elt, url, cfg);
                                if (data !== null) self.updateCard(data, elt, cache_url, cfg);
                                if (cfg.postAjaxProcess) cfg.postAjaxProcess.call(self.card, self, cfg, cache_url);
                            },
                            error: function(data, textStatus) {
                                try { console.log('Hovercard AJAX error:', data, textStatus); } catch (x) {}
                                if (cfg.ajaxError) cfg.ajaxError.call(this, data, textStatus);
                            }
                        });
                        if (data || cfg.data) request.data = data || cfg.data;

                        $.ajax(request);
                    }
                }
            }

            //(hover-css-anim-fix) - check to see if we need to update position
            this.positionInterval = window.setInterval(function() {
                self._positionCard();
            }, 100);

            /* fire mixpanel tracking if we have it */
            if (cfg.mp_track)
                mpq.push(["track", cfg.mp_track]);

        }, cfg.showDelay);
    },

    //
    // Updates the HTML and position of the hovercard
    //
    updateCard: function(ajax_data, elt, cache_url, cfg, cache_update) {
        this.updateCache(ajax_data, cache_url, cfg);
        if (this.currentElt === elt) {
            var html = ajax_data;
            if (this.responseIsJson(cfg) && typeof(ajax_data) == 'object') {
                html = ajax_data.html;
                if (ajax_data.width) this.card.css('width', ajax_data.width + 'px');
            }
            this.cardInner.html(html);
            this._positionCard();
        }
    },

    //
    // Returns whether the ajax response will be JSON
    //
    responseIsJson: function(cfg) {
        return (cfg.ajaxSettings.dataType == 'json' || cfg.ajaxSettings.dataType == 'jsonp');
    },

    //
    // Updates the cached HTML for the given cache_url
    //
    updateCache: function(html, cache_url, cfg) {
        if (cfg.ajaxCache && cache_url) this._cache.add(html, cache_url, cfg.namespace);
    },

    //
    // Adds 'px' to all values in obj that are numbers, o/w sets them to "auto"
    //
    _addPx: function(obj) {
        for (var attr in obj) {
            if (typeof(obj[attr]) == 'number') obj[attr] += 'px';
            else obj[attr] = 'auto';
        }
        return obj;
    },

    //
    // Positions the card relative to currentElt
    //
    _positionCard: function() {
        if (!this.currentElt || this.currentCfg == this.emtpyCfg) return;

        var cfg = this.currentCfg,
            $curElt = $(this.currentElt),
            offset = $curElt.offset(),
            height = $curElt.outerHeight(),
            width = $curElt.outerWidth(),
            $window = $(window),
            $document = $(document),
            window_width = $window.width(),
            window_height = window.innerHeight ? window.innerHeight : $window.height(),
            scroll_top = $window.scrollTop(),
            scroll_left = $window.scrollLeft(),
            card_height = this.card.outerHeight(),
            card_width = this.card.outerWidth(),
            top, right, left, dir, pos;

        if (cfg.gravity == 'top') {

            dir = 'down';
            pos = 'left';

            top = offset.top - card_height - cfg.offsetY;
            if (top < scroll_top) {
                dir = 'up';
                top = offset.top + height + cfg.offsetY;
            }

            left = offset.left;

            // need to take scroll_left into account when determining if it will be visible or not
            // but when we actually position it, we ignore scroll_left, because left and right
            // inside the body tag are relative to the viewport!
            if (left + card_width > scroll_left + window_width) {
                pos = 'right';
                left = null;
                right = window_width - (offset.left + width);
            }

        } else {
            throw new Error('invalid gravity ' + cfg.gravity);
        }

        this._arrow(this.card.children('b.arrow').first(), this.currentCfg, this.currentCfg.arrow, dir, pos);
        this._arrow(this.card.children('b.arrow-inner').first(), this.currentCfg, this.currentCfg.arrowInner, dir, pos);

        this.card.css(this._addPx({
            top: top,
            right: right,
            left: left
        }));
    },

    //
    // Positions the arrow for the card
    //
    _arrow: function(elt, cfg, arrowCfg, dir, pos) {
        //TODO -- maybe this would have been easier if I just hardcoded it?
        var size = arrowCfg.size,
            offset = size - (arrowCfg.offset || 0),
            a = size + 'px', b = 'transparent',
            borderWidth = [a, a, a, a],
            borderColor = [b, b, b, b],
            width = {
                up: 0,
                left: 1,
                down: 2,
                right: 3
            }[dir],
            color = (width + 2) % 4, // the important color part is always opposite the important width
            top, right, bottom, left;

        borderWidth[width] = 0;
        borderColor[color] = arrowCfg.color || cfg.borderColor;

        switch (dir) {
          case 'up': top = -offset; break;
          case 'right': right = -offset; break;
          case 'left': left = -offset; break;
          case 'down': bottom = -offset; break;
        }

        switch (pos) {
            case 'top': top = size; break;
            case 'right': right = size; break;
            case 'bottom': bottom = size; break;
            case 'left': left = size; break;
        }

        var css = $.extend({
            borderWidth: borderWidth.join(' '),
            borderColor: borderColor.join(' ')
        }, this._addPx({
            top: top,
            right: right,
            bottom: bottom,
            left: left
        }));

        elt.css(css);

        //TODO - add ie6 support?
    },

    //
    // Hides the card (after a timeout)
    //
    hideCard: function(elt, cfg) {
        if (!this._did_init) this.init(cfg);

        this._clearShowTimeout();

        var self = this;
        this.hideTimeout = window.setTimeout(function() { self.clearCard(); }, this.currentCfg.hideDelay);
    },

    //
    // Timeout functions for showing/clearing the card
    //
    clearCard: function() {
        this.card.hide();
        window.clearInterval(this.positionInterval);
        this.positionInterval = null;
        this.currentElt = null;
    },
    _cardHoverIn: function(evt) {
        this._clearHideTimeout();
    },
    _cardHoverOut: function(evt) {
        this.hideCard(evt);
    },
    _clearShowTimeout: function() {
        if (this.showTimeout) {
            try { window.clearTimeout(this.showTimeout); } catch (x) {}
            this.showTimeout = null;
        }
    },
    _clearHideTimeout: function() {
        if (this.hideTimeout) {
            try { window.clearTimeout(this.hideTimeout); } catch (x) {}
            this.hideTimeout = null;
        }
    },

    //
    // The cache of url requests to content
    //
    _cache: {
        _asKey: function(url, namespace) {
            return (namespace ? namespace + '-' : '') + url;
        },
        _data: {},
        add: function(content, url, namespace) {
            var key = this._asKey(url, namespace);
            this._data[key] = content;
        },
        get: function(url, namespace) {
            return this._data[this._asKey(url, namespace)];
        }
    }
};

$.fn.extend({
    hovercard: function(cfg) {
        var hovercard = $.hovercard;
        cfg = $.extend({}, hovercard.defaults, cfg);

        // preload loader image
        if (!cfg.didSetup && cfg.loader && cfg.loader.src) {
            cfg.didSetup = true;
            (new Image).src = cfg.loader.src;
        }

        return $(this).each(function() {
            function hoverIn(evt) { hovercard.showCard(this, cfg); }
            function hoverOut(evt) { hovercard.hideCard(this, cfg); }
            $(this).hover(hoverIn, hoverOut);
        });
    },
    personhover: function(cfg) {
        return this.hovercard($.extend(
            {
                url: '/feed/ws/hovercard/',
                data: function(elt) {
                    return {
                        user_id: $(elt).data('user_id')
                    };
                },
                showDelay: 200,
                mp_track: "show_personhover",
                ajaxSettings: {
                    dataType: 'json'
                },
                loadingCardWidth: 375,
                postAjaxProcess: function(hovercard, cfg, cache_url) {
                    $(this).find('button.follow').followtoggle({
                        followClickTrackPage: "personhover",
                        callback: function(success, newState) {
                            hovercard.updateCache(hovercard.cardInner.html(), cache_url, cfg);
                        }
                    });
                    $(this).find('img[title]').darkTooltip();
                }
            },
            cfg
        ));
    },
    similarityhover: function(cfg) {
        return this.hovercard($.extend(
            {
                url: '/people/ws/similaritycard/',
                data: function(elt) {
                    var d = {
                        user_id: $(elt).data('user_id'),
                        category: $(elt).data('category')
                    };
                    if ($(elt).data("login_next"))
                        d["login_next"] = $(elt).data("login_next");
                    return d;
                },
                showDelay: 150,
                mp_track: "show_similarityhover",
                ajaxSettings: {
                    dataType: 'json'
                },
                loadingCardWidth: 208,
                postAjaxProcess: function(hovercard, cfg, cache_url) {
                    $(this).find('img[title]').darkTooltip();
                }
            },
            cfg
        ));
    }
});

/* Copyright (C) 2011, Hunch.com */

(function(){
    var menuOver = function() {
        var self = this;
        clearTimeout(self.out_id);
        self.over_id = setTimeout(function() {
            $(self).addClass('hover').siblings('.hover').removeClass('hover');
            self = null;
        }, 20);
    };

    var menuOut = function() {
        var self = this;
        clearTimeout(self.over_id);
        self.out_id = setTimeout(function() {
            $(self).removeClass('hover');
            self = null;
        }, 400);
    };

    function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for(var i=0;i < ca.length;i++) {
            var c = ca[i];
            while (c.charAt(0)==' ') c = c.substring(1,c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
        }
        return null;
    }

    $.tts = function(slct, fade) { return $(slct).darkTooltip(); };

    $(function() {
        $('ul#nav>li').hover(menuOver, menuOut);
        $('.dropdown>.elt').live('mouseover', menuOver);
        $('.dropdown>.elt').live('mouseout', menuOut);
        $('form#site-search').submit(function() {
            var input = $('input#search-box'), val = input.val();
            return (val != '' && val != input.attr('placeholder'));
        });

        $('input#search-box').placeholder({ignoreSubmits: 1}).hunchac({
            container: '#site-search div#ac-container',
            loadingClass: false,
            dropDownOnly: false,
            render: function(list, container, input) {
                var ul = $('<ul/>'),
                matched = false,
                lists = [
                    ['Users', $.grep(list, function(x) {
                        if (x.type == 'u') {
                            var user_type = '';
                            if (x.is_you) {
                                user_type = ' is-you';
                            } else if (x.is_following){
                                user_type = ' is-following';
                            } else if (x.is_official){
                                user_type = ' is-official';
                            }
                            var img = '<div class="img' + user_type + '">' + (x.image_url ? '<img src="' + x.image_url + '.c36_c36">' : '&nbsp;') + '</div>';
                            x.name = (img + '<p class="strong">' + x.name + '</p>');
                            if (x.fullname) x.name += '<p class="xsmall">' + x.fullname + '</p>';
                            if (x.metadata) { for (var i=0;i<x.metadata.length;i++) {  x.name += '<p class="dim xsmall">' + x.metadata[i] + '</p>'; } }
                            return true;
                        }
                        return false;
                    }),
                     'ac-um-'],
                    ['Topics', $.grep(list, function(x) {
                        if (x.type == 't' || x.type == 'st') {
                            x.name = '<p class="strong no-image">' + x.name + '</p>';
                            if (x.parent_names) x.name += '<p class="dim xsmall no-image">' + x.parent_names + '</p>';
                            return true;
                        }
                        return false;
                    }),
                     'ac-tm-'],
                    ['Items', $.grep(list, function(x) {
                        if (x.type == 'i') {
                            var img = '<div class="img">' + (x.image_url ? '<img src="' + x.image_url + '.c36_c36">' : '&nbsp;') + '</div>';
                            var rec = ('<div class="right" style="margin: 0 0 9px 9px;">' +
                                       '<button type="button">recommend</button>' +
                                       '</div>');
                            x.name = (img + rec + '<p class="strong">' + x.name + '</p>');
                            if (x.metadata) { for (var i=0;i<x.metadata.length;i++) {  x.name += '<p class="xsmall">' + x.metadata[i] + '</p>'; } }
                            if (x.topic_names) x.name += '<p class="dim xsmall">' + x.topic_names + '</p>';
                            return true;
                        }
                        return false;
                    }),
                     'ac-rm-']
                ];
                for (var i=0; i<lists.length; i++) {
                    if (lists[i][1].length) {
                        ul.append('<li class="ac-header'+(matched?'':' ac-first-header')+'">'+lists[i][0]+'</li>');
                        matched = true;
                        $.each(lists[i][1], function(j, x) {
                            var $node = $('<li id="'+lists[i][2]+(j+1)+'" class="ac-element">'+x.name+'</li>');
                            $.data($node[0], $.hunchac.ORIGINAL_OBJECT, x);
                            ul.append($node);
                            // special set cookie when click on rec button...
                            if (x.type == 'i') {
                                $node.find('button').click(function() {
                                    document.cookie= 'dorec=1;path=' + x.url + ';';
                                });
                            }
                        });
                    }
                }
                if (!matched)
                    ul.append('<li class="ac-none">No matches</li>');
                container.html('<div class="yui-ac-content" />');
                var wrap = $('.yui-ac-content', container);
                wrap.append(ul);
            },
            activate: function(e, active, data) {
                if (data.type == 'dym') { // 'Did you mean...' case
                    var self = this;
                    setTimeout(function() {
                        $(self).val(data.name.substring(21)).trigger('hunchac');
                        self = null;
                    }, 5);
                }
                else {
                    location.href = data.url;
                }
            },
            autostart: 1
        });
        $.tts('.tt');
        $('body .notify-msg a.close').click(function() {
            $('body .notify-msg').hide();
            $('.is-notify-msg').removeClass('is-notify-msg');
            document.cookie='notify=0;path=/;';
            return false;
        });
        if (/\((iPhone|iPod|Android|webOS);/.test(navigator.userAgent) || readCookie('hunch.mobile') == '0') {
            $('#mobile-switch', this).click(function(evt) {
                evt.preventDefault();
                document.cookie = "hunch.mobile=1; path=/";
                window.location.href = '/m/';
                return false;
            });
            $('#mobile-footer').removeClass('hide');
        }
        //if (/\((iPhone|iPad|iPod);/.test(navigator.userAgent)) { $('body').addClass('touch'); }

        var sh = $('#stars-helper');
        if (sh.length) sh.parent().starHelper();

        // async loading magic!
        $('.async-load').each(function() {
            var $this = $(this),
            url = $this.data('url');
            url && $this.getLoader(url, {replaceWith: true});
        });

        // special handler for users who have no followers going to browse by default
        // note: this is a little ugly, since it adds the cookie for any logged-in user
        // regardless of whether or not the user needs it... so clean this up serverside!
        if ($('#nav').find('a.user').length) {
            $('#nav').find('li>a[href="/"]').click(function() {
                document.cookie = 'fs=1; path=/;';
            });
        }
    });
})();

