// 'jq.idPrefix.js'
// 'jquery-tooltip/jquery.tooltip.js'
// 'hunch.ac.js'
// 'hunch.fadetocolor.js'
// 'facebox/facebox.js'
// 'hunch.confirm.js'
// 'jquery.hunch-utils.js'
// 'jquery.loader.js'
// 'file_upload/ajaxfileupload.js'
// 'jquery.fileupload.js'
// 'jquery.flag.js'
// 'jquery.minipagination.js'
// 'hunch.showmore.js'
// 'jquery.placeholder.js'
// 'jquery.toggle.js'
// 'jquery.remaining.js'
// 'onload.js'

;(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);
		},
		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;
		}

		// 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 = event.pageX + settings(current).left;
			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) {
			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);

/*
 * 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/',
			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
			// autostart: false
			data: {}
		},
		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) {
			var settings = $.extend({}, $.hunchac.defaults, settings),
				callback = function(json, input) {
					$('body').trigger('off.hunchac');
					var container = $(settings.container),
						docs = (settings.jsonListAttr ? json[settings.jsonListAttr] : json),
						size = settings.render(docs, container, input);
					container.show();
					if (settings.loadingClass) input.removeClass('loading');
					$('body').hunchacMode(container, input, size || docs.length, settings);
				};
			if (!settings.container) throw new Exception('Must specify a `container` in the settings!');

			return this.each(function() {
				
				var last_key, keypress = function(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));
						}
				}
				
				$(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,
						request = {
							url: settings.url,
							data: $.extend(settings.data || {}, {query: $(this).val()}),
							type: 'get',
							dataType: 'json',
							cache: false,
							success: function(json) {
								$.data(self, XHR, null);
								return callback(json, $(self));
							}
						}
					if (settings.loadingClass) $(this).addClass('loading');
					if (request.data.query)	$.data(this, XHR, (settings.xhrFunc ? settings.xhrFunc(request.data.query, request.success) : $.ajax(request)));
				}).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);
			});
		},
		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');
			});

			$('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);

/*
 * Function to fade from current color to another
 */
jQuery.fn.fadeToColor = function(color, time, callback) {
	var getRGB = function(bg) {
		var i;
		bg = bg.toLowerCase();
		if (/^#[0-9a-f]{3}$/.test(bg)) {
			bg = [bg.substr(1, 1), bg.substr(2, 1), bg.substr(3, 1)];
			for (i=0; i<3; i++) bg[i] = parseInt(bg[i]+bg[i], 16);
			return bg;
		} else if (/^#[0-9a-f]{6}$/.test(bg)) {
			return [parseInt(bg.substr(1, 2), 16), parseInt(bg.substr(3, 2), 16), parseInt(bg.substr(5, 2), 16)];
		} else {
			bg = bg.match(/\d+/g);
			if (bg && bg.length == 3) {
				for (i=0; i<3; i++) bg[i] = parseInt(bg[i]);
				return bg;
			}
		}
		return null;
	}
	var changeRatio = function(n, num_ns) {
		return 1 - (n/num_ns - 1) * (n/num_ns - 1);
	}
	
	if (!this || !this.length) return;
	
	var self = this,
		endColor = getRGB(color) || [255, 255, 255],
		rgb = getRGB($(this).css('background-color')),
		rgbIntervals = [],
		numIntervals = parseInt((time || 500) / 80),
		i;
	
	if (!rgb) return;
	
	for (i = 0; i < 3; i++) {
		rgb[i] = parseInt(rgb[i]);
		rgbIntervals.push((endColor[i]-rgb[i])/numIntervals);
	}
	
	var count = 0;
	var intervalId = window.setInterval(function() {
		count += 1;
		if (count == numIntervals) {
			$(self).css('background-color', 'rgb('+endColor.join(',')+')');
			window.clearInterval(intervalId);
			if (callback) callback.call(self);
		} else {
			var newRGB = [],
				i, val;
			for (i=0; i < 3; i++) {
				val = rgb[i]+parseInt(rgbIntervals[i]*count*changeRatio(count, numIntervals));
				if (val < 0) val = 0;
				if (val > 255) val = 255;
				newRGB.push(val);
			}
			newRGB = 'rgb('+newRGB.join(',')+')';
			$(self).css('background-color', newRGB);
		}
	}, 80);
	return this;
};
/*jQuery.fn.fadeFromColor = function(color, time, callback) {
	var orig_color = $(this).css('background-color');
	$(this).css('background-color', color).fadeToColor(orig_color, time, callback);
};*/
/*
 * 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 = true;
    $.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)
  }

  /*
   * 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="tl"/><td class="b"/><td class="tr"/> \
            </tr> \
            <tr> \
              <td class="b"/> \
              <td class="body"> \
                <div class="content"> \
                </div> \
		<!-- \
                <div class="footer"> \
                  <a href="#" class="close"></a> \
                </div> \
		--> \
              </td> \
              <td class="b"/> \
            </tr> \
            <tr> \
              <td class="bl"/><td class="b"/><td class="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);
        $('#facebox').css({
	        top:	getPageScroll()[1] + (getPageHeight() / 10),
	        left:	$('#content').offset().left + 295 // Have it handle our centered #content div
	    }).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')
    },

	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);

/*
 * Hunch dialog - relies on facebox.js
 */
;(function($) {
	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) {
		var settings = $.extend({}, $.confirm.settings, settings),
			text = (settings.text_fn ? (settings.self ? settings.text_fn.call(settings.self) : settings.text_fn()) : settings.text),
			left = getFn(settings.left, settings.animate, settings),
			right = getFn(settings.right, settings.animate, settings),
			dialog, buttons;
		if (settings.can_confirm && !settings.can_confirm()) return;
		dialog = $('<div />').append($('<h2 />').text(text));
		buttons = $('<div class="buttons" />').css('padding-top', '9px');
		if (settings.sub_text) dialog.append($('<p />').text(settings.sub_text));
		if (settings.right) {
			buttons.append($('<button />')
								.addClass(settings.right_class || '')
								.append($('<span />').text(settings.right_text))
								.click(right));
		}
		buttons.append($('<button />')
							.addClass(settings.left_class || '')
							.append($('<span />').text(settings.left_text))
							.click(left));
		if (settings.animate) {
			buttons.append($('<span class="indicator">loading...</span>'));
		}
		dialog.append(buttons);
		$.facebox(dialog);
	}
	$.confirm.settings = {
		text: 'Are you sure you want to continue?',
		sub_text: '',
		left_text: 'Yes',
		right_text: 'No'
		//can_confirm: function(){},
		//left: function(){},
		//right: function(){}
		//text_fn
	}
	$.confirm.close = function() {
		$(document).trigger('close.facebox');
		return false;
	}

	$.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(settings) {
			$(this).click(function() {
				settings.self = this;
				$.confirm(settings);
				return false;
			});
		},
		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);

/*
 * Hunch utils
 */
;(function($) {
	$.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 */
			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);
	};


	//
	// 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) },
				success: function(data) { callback(shortenUrlCache[url] = data.url); }
			});
		}
		return false;
	}

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

	// Uses of encodeURI instead of encodeURIComponent are to allow stuff like &quot;Cats&quot; to not get escaped-again
	$.clickFacebook = function(url, title, noshorten) {
		url = url || window.location.href;
		title = typeof(title) == 'undefined' ? document.title : title;
		var w = loadingWindow(350,625);
		$.shortenUrl(url, function(url) {
			w.location = 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(url) + (title ? '&t=' + encodeURI(title) : '');
		}, noshorten);
		return false;
	}

	$.clickTwitter = function(status, url, post_status, noshorten) {
		url = url || window.location.href;
		post_status = post_status || '';
		var w = loadingWindow();
		$.shortenUrl(url, function(url) {
			status = status + (status ? ' ' : '') + url + (post_status ? ' ' : '') + post_status;
			w.location = 'http://twitter.com/home?status=' + encodeURI(status);
		}, 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=' + encodeURI(subject) + '&body=' + encodeURI(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);
	};*/
})(jQuery);

/*
 * 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) {
						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);


jQuery.extend({
	

    createUploadIframe: function(id, uri)
	{
			//create frame
            var frameId = 'jUploadFrame' + id;
            
            if(window.ActiveXObject) {
                var 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 {
                var 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
    },
    createUploadForm: function(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"></form>');	
		var oldElement = $('#' + fileElementId);
		var newElement = $(oldElement).clone();
		$(oldElement).attr('id', fileId);
		$(oldElement).before(newElement);
		$(oldElement).appendTo(form);
		//set attributes
		$(form).css('position', 'absolute');
		$(form).css('top', '-1200px');
		$(form).css('left', '-1200px');
		$(form).appendTo('body');
		return form;
    },

    ajaxFileUpload: function(s) {
        // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout		
        s = jQuery.extend({}, jQuery.ajaxSettings, s);
        var id = new Date().getTime()        
		var form = jQuery.createUploadForm(id, s.fileElementId);
		var io = jQuery.createUploadIframe(id, s.secureuri);
		var frameId = 'jUploadFrame' + id;
		var formId = 'jUploadForm' + id;		
        // Watch for a new set of requests
        if ( s.global && ! jQuery.active++ )
		{
			jQuery.event.trigger( "ajaxStart" );
		}
        var requestDone = false;
        // Create the request object
        var xml = {}   
        if ( s.global )
            jQuery.event.trigger("ajaxSend", [xml, s]);
        // Wait for a response to come back
        var uploadCallback = function(isTimeout)
		{			
			var io = document.getElementById(frameId);
            try 
			{				
				if(io.contentWindow)
				{
					 xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
                	 xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
					 
				}else if(io.contentDocument)
				{
					 xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;
                	xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;
				}						
            }catch(e)
			{
				jQuery.handleError(s, xml, null, e);
			}
            if ( xml || isTimeout == "timeout") 
			{				
                requestDone = true;
                var status;
                try {
                    status = isTimeout != "timeout" ? "success" : "error";
                    // Make sure that the request was successful or notmodified
                    if ( status != "error" )
					{
                        // process the data (runs the xml through httpData regardless of callback)
                        var data = jQuery.uploadHttpData( xml, s.dataType );    
                        // If a local callback was specified, fire it and pass it the data
                        if ( s.success )
                            s.success( data, status );
    
                        // Fire the global callback
                        if( s.global )
                            jQuery.event.trigger( "ajaxSuccess", [xml, s] );
                    } else
                        jQuery.handleError(s, xml, status);
                } catch(e) 
				{
                    status = "error";
                    jQuery.handleError(s, xml, status, e);
                }

                // The request was completed
                if( s.global )
                    jQuery.event.trigger( "ajaxComplete", [xml, s] );

                // Handle the global AJAX counter
                if ( s.global && ! --jQuery.active )
                    jQuery.event.trigger( "ajaxStop" );

                // Process result
                if ( s.complete )
                    s.complete(xml, status);

                jQuery(io).unbind()

                setTimeout(function()
									{	try 
										{
											$(io).remove();
											$(form).remove();	
											
										} catch(e) 
										{
											jQuery.handleError(s, xml, null, e);
										}									

									}, 100)

                xml = null

            }
        }
        // Timeout checker
        if ( s.timeout > 0 ) 
		{
            setTimeout(function(){
                // Check to see if the request is still happening
                if( !requestDone ) uploadCallback( "timeout" );
            }, s.timeout);
        }
        try 
		{
           // var io = $('#' + frameId);
			var form = $('#' + formId);
			$(form).attr('action', s.url);
			$(form).attr('method', 'POST');
			$(form).attr('target', frameId);
            if(form.encoding)
			{
                form.encoding = 'multipart/form-data';				
            }
            else
			{				
                form.enctype = 'multipart/form-data';
            }			
            $(form).submit();

        } catch(e) 
		{			
            jQuery.handleError(s, xml, null, e);
        }
        if(window.attachEvent){
            document.getElementById(frameId).attachEvent('onload', uploadCallback);
        }
        else{
            document.getElementById(frameId).addEventListener('load', uploadCallback, false);
        } 		
        return {abort: function () {}};	

    },

    uploadHttpData: function( r, type ) {
        var data = !type;
        data = type == "xml" || data ? r.responseXML : r.responseText;
        // If the type is "script", eval it in global context
        if ( type == "script" )
            jQuery.globalEval( data );
        // Get the JavaScript object, if JSON is used.
        if ( type == "json" ) {
		    //alert(data)
		    //alert("'" + data.substr(1,33) + "'")
		    if (data.substr(1,33) == '<h1>Request Entity Too Large</h1>') { /* HACK to 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 {
		        var preRe = new RegExp('^<pre[^>]*>', 'gi');
		        var preEndRe = new RegExp('</pre>$', 'gi');
		        //alert(data.replace(preRe, '').replace(preEndRe, ''))
	            eval( "data = " + data.replace(preRe, '').replace(preEndRe, '') );
		        //alert("DID JSON!");
		    }
		}
		
        // evaluate scripts within html
        if ( type == "html" )
            jQuery("<div>").html(data).evalScripts();
			//alert($('param', data).each(function(){alert($(this).attr('value'));}));
        return data;
    }
})

/*
 * 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; }
	};
	/*
	var $up = {
		_iframe: function(url, frame_id) {
			console.log('iframe!');
			var iframe = $('<iframe id="'+frame_id+'" name="'+frame_id+'" src="'+url+'"/>').css({position: 'absolute', top:'-1000px', left: '-1000px'});
			$('body').append(iframe);
			return iframe;
		},
		_form: function(old_input, form_id, file_id) {
			console.log('form!');
			var form = $('<form action="" method="post" id="'+form_id+'" name="'+form_id+'" enctype="multipart/form-data"></form>').css({position: 'absolute', top: '-1200px', left: '-1200px'}),
				new_input = old_input.clone();
			//old_input.attr('id', file_id).before(new_input).appendTo(form);
			form.append($('<input name="image" value="1" />'));
			form.appendTo('body');
			return form;
		},
		_iframeDoc: function(iframe) {
			iframe = iframe.get(0);
			return iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument.document;
		},
		_load: function(frame_id, msg) {
			return function() {
				var frame = $('#'+frame_id),
					doc = $up._iframeDoc(frame);
				if (msg) alert(msg);
				console.log(msg || 'LOADED:', doc.body ? doc.body.innerHTML : null);
				return false;
			}
		},
		ajaxFileUpload: function(cfg) {
			// cfg params
			//  url: file upload url
			//  input: the input for which we're doing the upload
			var id = (new Date).getTime(),
				frame_id = id+'-frame', form_id = id+'-form', file_id = id+'-file',
				form = $up._form(cfg.input, form_id, file_id),
				iframe = $up._iframe(cfg.url, frame_id);
			console.log('in ajaxFileUpload!');
			iframe.load($up._load(frame_id));
			$($up._iframeDoc(iframe)).ready($up._load(frame_id, 'READY'));
			console.log('form submit!');
			form.attr('target', frame_id).attr('action', cfg.url).submit();
			// ignored - form.encoding || enctype 'multipart/form-data'
		}
	}
	*/
	$.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); })
					 .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) {
		$.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(id)

		var full_url = url;
		if (pref) { full_url += '?prefix=' + pref.substr(0, pref.length-1); }
		/*
		var request = {
			url: full_url,
			input: $('#'+id)
		}
		$up.ajaxFileUpload(request);
		return;
		*/

		$.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, id);
					} 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); });

				$.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); });
				$('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(id) {
		$('#'+id).parent().children('.errorlist').remove();
	}

	function addFileError(error, id) {
		remFileError(id);
		$('#'+id).before($('<ul class="errorlist"/>').append($('<li/>').html(error)));
	}
})(jQuery);

 /*
 * jQuery/Hunch Flag plugin
 *
 * In addition to the default below - you must specify a url and data (or do settingsContentFunc)
 *
 */
;(function($) {

	$.hunchflag = {
		defaults: {
			live: false, // determines if we use live-delegation or not
			flags: [['Offensive', 'offensive'], ['Poor quality <span>Ex: bad photo, not helpful</span>', 'quality']],
			data: {},
			checked: '', // if set will auto-check any radios whose value matches 'checked'
			cssFlagWidth: '',
			closeable: true,
			title: '',
			comments: true,
			commentsTitle: 'Comments',
			textareaCols: 20,
			textareaRows: 3,
			align: 'right', // {left|right}
			extraLink: '',
			customContentFunc: null,
			wrapper: 'body>#container'
		},
		clear: clearHunchFlags,
		timeoutId: null,
		setTimeout: function(expr, timeout) {
			$.hunchflag.timeoutId = setTimeout(expr, timeout);
		}
	}

	$.fn.extend({
		hunchflagClearer: function(extraFunc, live) {
			this.each(function(i, o) {
				if (!o._clearsHunchFlag) {
					o._clearsHunchFlag = 1;
					$(o).bind('clearHunchFlag', function() { clearHunchFlags(); });
				}
			});
			var clear_func = function() {
				clearHunchFlags();
				clearTimeout($.hunchflag.timeoutId);
				if (extraFunc) {
					extraFunc(this);
				}
			}
			if (live) $(this).live('click', clear_func);
			else $(this).click(clear_func);

		},
		hunchflag: function(settings) {

			// Set this for clearing any current flags
			this.hunchflagClearer();
			settings = $.extend({}, $.hunchflag.defaults, settings);

			var flag_func = function() {
				var data = settings.data;
				if (settings.dataFunc) $.extend(data, settings.dataFunc(this));
				var offset = getHunchFlagOffset(this, settings.wrapper);

				var f = $('<div id="hunch-flag"/>');
				if (settings.cssFlagWidth) { f.css('width', settings.cssFlagWidth); }
				if (settings.closeable) { f.append($('<div class="js-close"/>').text('X').click(function() { f.slideUp('fast'); })); }

				if (settings.title) {
					f.append($('<p style="padding-bottom:9px;"/>').text(settings.title));
				}

				if (settings.customContentFunc) {
					settings.customContentFunc(f, this);
				} else {
					if (!settings.flags || !settings.flags.length) {
						f.append('<input type="radio" style="display:none;" name="flag" value="quality" checked="checked" />')
					} else {
						$.each(settings.flags, function(i, x) {
							f.append($('<label/>').append($('<input type="radio" name="flag" />').attr('value', x[1])).append(' ' + x[0]));
						});
					}

					if (settings.checked) {
						$('input[type=radio][value='+settings.checked+']', f).attr('checked', 'checked');
					}

					if (settings.comments) {
						f.append($('<p class="comment"/>').html(settings.commentsTitle));
						var limitor = function() {
							var t = $(this);
							if (t.val().length > 500) {
								t.val(t.val().substr(0, 500));
								this.scrollTop = this.scrollHeight;
							}
						}
						f.append($('<textarea cols="'+settings.textareaCols+'" rows="'+settings.textareaRows+'" wrap="soft"/>').change(limitor).keyup(limitor));
						f.append('<p class="right-text">500 character max.</p>');
					}

					var ul = $('<ul class="feedback clearfix"/>').append($('<li/>').append($('<input type="button"/>').attr('name', 'f_submit').attr('value', 'Submit').click(function() {

						// Submit function
						var radio = $('#hunch-flag input[type=radio][name=flag]:checked');
						var reason = 'other';
						if (radio.size()) { reason = radio.val(); }
						var comment = $('#hunch-flag textarea').val();
						if (comment) {
							var remBrs = new RegExp('\n', 'g');
							comment = comment.substr(0,500).replace(remBrs, ' ');
						} else {
							comment = '';
						}

						$('#hunch-flag *:input').attr('disabled', 'disabled');
						$('#hunch-flag ul').append($('<li/>').append($('<img/>').attr('src', '/media/img/ajax-loader-form-w.gif')));

						data.reason = reason;
						data.message = comment;

						$.ajax({
							type : 'POST',
							url : settings.url,
							data : data,
							success : function(data, textStatus) {
								if (data.substr(0,8) == '<hunch/>') {
									$('#hunch-flag').css('width', $('#hunch-flag').width()).html('Thank you!').css('padding-top', '15px').css('padding-bottom', '15px').css('text-align', 'center');
									setTimeout("$('#hunch-flag').fadeOut('normal', function() { $(this).remove(); });", 1000);
								} else { $.hunchError(); }
							},
							timeout : 5000,
							error : function() { $.hunchError(); },
							dataType : 'html',
							cache : false,
							async : true
						});
					})));

					if (settings.extraLink) {
						ul.append($('<li/>').append(settings.extraLink));
						if (settings.extraLinkFunc) { settings.extraLinkFunc(f, this); }
					}

					f.append(ul);
				}

				// align and append
				if (settings.align == 'left') { f.css('left', offset.left); }
				else { f.css('right', offset.right); }
				$(settings.wrapper).append(f.css('top', offset.top));

				if (settings.checked) {
					$('input[type=radio][value='+settings.checked+']', f).attr('checked', 'checked');
				}

				return false;
			}

			if (settings.live) this.live('click', flag_func);
			else this.click(flag_func);
		}
	});

	// Removes any other open flags
	function clearHunchFlags() {
		$('#hunch-flag').remove();
	}

	// Figures out placement of the flag box
	function getHunchFlagOffset(o, wrapper_selector) {
		var t = $(o);
		var p = $(wrapper_selector);
		var offp = p.offset();
		var offt = t.offset();

		var left = offt.left - offp.left;

		return {
			left: left,
			right: p.width() - left - t.outerWidth(),
			top: offt.top + t.outerHeight()
		}
	}
})(jQuery);

/*
 * jQuery/Hunch mini-pagination plugin
 */
;(function($) {
	$.extend({
		minipagination: new function() {
			
			function buildCache(t) {
				var cache = [];
				
				$(t.config.elts, t).each(function(i, o) {
					cache.push(o);
				});
				
				return cache;
			}
			
			this.clearPage = function(t) {
				if ($.browser.msie) {
					function empty() {
						while ( this.firstChild ) this.removeChild( this.firstChild );
					}
					empty.apply( t.get(0) );
				} else {
					t.get(0).innerHTML = "";
				}
			}
			
			function goToNextPage(t) {
				var c = t.config;
				c.page++;
				if (c.page >= (c.totalPages-1)) { c.page = (c.totalPages-1); }
				goToPage(t);
			}
			
			function goToPrevPage(t) {
				var c = t.config;
				c.page--;
				if (c.page <= 0) { c.page = 0; }
				goToPage(t);
			}
			
			function goToPage(t) {
				var c = t.config;
				if (c.page < 0 || c.page > (c.totalPages-1)) { c.page = 0; }
				
				// update page #
				$(c.cssPage, c.container).text(c.page+1);
				var counts = $(c.cssCounts, c.container);
				if (counts.size()) {
					var first = (c.page * c.size) + 1;
					var last = (c.page + 1) * c.size;
					last = (last > c.totalSize ? c.totalSize : last);
					counts.text(first+'–'+last);
				}
				
				// update classes
				if (c.page == 0) {
					$(c.cssPrev, c.container).addClass(c.cssDisPrevClass);
				} else {
					$(c.cssPrev, c.container).removeClass(c.cssDisPrevClass);
				}
				
				if (c.page == c.totalPages - 1) {
					$(c.cssNext, c.container).addClass(c.cssDisNextClass);
				} else {
					$(c.cssNext, c.container).removeClass(c.cssDisNextClass);
				}
								
				renderPage(t,t.cache);
			}
			
			function renderPage(t,elts) {
				
				var c = t.config;
				var l = elts.length;
				var s = (c.page * c.size);
				var e = (s + c.size);
				if (e > elts.length ) {
					e = elts.length;
				}
				
				$.minipagination.clearPage(t);
				
				var tHTML = t.get(0);
				
				// some browsers need a tbody for tables - TODO: special case this for msie only?
				if (c.elts == 'tr') {
					var g = $('<tbody/>');
					t.append(g);
					tHTML = g.get(0);
				}
				
				for (var i=s; i<e; i++) {
					// t.append(elts[i]);
					tHTML.appendChild(elts[i]);
				}
				
				// $(table).trigger("applyWidgets");
				
				/// updatePageDisplay(c);
			}
			
			this.defaults = {
				elts: 'li',
				size: 10,
				page: 0,
				totalSize: 0,
				totalPages: 0,
				container: null,
				fixedHeight: false,
				height: 0,
				cssPage: '.page_num',
				cssPageTotal: '.page_total',
				cssCounts: '.page-counts',
				cssNext: '.next',
				cssPrev: '.prev',
				cssDisNextClass: 'next-dis',
				cssDisPrevClass: 'prev-dis',
				cssFirst: '.first',
				cssLast: '.last'
			};
			
			this.construct = function(settings) {
				
				return this.each(function() {
					
					var config = $.extend({}, $.minipagination.defaults, settings),
						t = $(this);
/*					
					// Create the container if asked to do so
					if (config.container == 'create') {
						config.container = $('<div class="mini-paginator clr">\
<a class="prev" href="#" onclick="return false">prev</a>\
<b>Page <span class="page_num">1</span></b> of <span class="page_total"></span>\
<a class="next" href="#" onclick="return false">next</a>\
</div>');
						$(this).after(config.container);
					}*/
					
					var pager = $(config.container);
					
					config.totalSize = $(config.elts, t).size();
					config.totalPages = Math.ceil(config.totalSize / config.size);
					
					$(config.cssPageTotal, pager).text(config.totalPages);
					t.config = config;
					
					t.pager = pager;
					t.cache = buildCache(t);
					goToPage(t);
					
					// handle fixedHeight
					if (config.fixedHeight) {
						if (!config.height) {
							config.height = t.height();
						}
						t.css('height', config.height);
					}
					
					// handle 1 page
					if (1 == config.totalPages || 0 == config.totalPages) {
						pager.css('height', pager.height()).html('<!-- -->');
					}
					
					//config.size = parseInt($(".pagesize",pager).val());
					
					//$(config.cssFirst,pager).click(function() {
					//	moveToFirstPage(table);
					//	return false;
					//});
					$(config.cssNext,pager).click(function() {
						goToNextPage(t);
						return false;
					});
					$(config.cssPrev,pager).click(function() {
						goToPrevPage(t);
						return false;
					});
					//$(config.cssLast,pager).click(function() {
					//	moveToLastPage(table);
					//	return false;
					//});
					//$(config.cssPageSize,pager).change(function() {
					//	setPageSize(table,parseInt($(this).val()));
					//	return false;
					//});
				});
			};
		}
	});
	
	$.fn.extend({ minipagination: $.minipagination.construct });
})(jQuery);

(function($){
	$.showmore = {
		defaults: {
			elts: '>li',
			page_size: 10,
			text: 'Show more',
			all_text: '',
			last_css: ''
		}
	}

	$.fn.showmore = function(cfg) {
		return $.each(this, function() {
			cfg = $.extend({}, $.showmore.defaults, cfg);

			var page = 1, total, pages, moreText;

			total = $(cfg.elts, this).each(function(i) {
				$(this).addClass('page-' + parseInt(i/cfg.page_size + 1));
			}).length;
			pages = parseInt((total + cfg.page_size - 1) / cfg.page_size);

			if (pages > 1) {

				moreText = function(page) {
					var start = page * cfg.page_size,
						end = start + cfg.page_size;
					if (end > total) end = total;
					return cfg.text + ' (' + start + '–' + end + ' of ' + total + ')…';
				}
				$(cfg.elts + ':not(.page-1)', this).hide();

				var self = this, more, all;

				more = $('<a class="space" href="#"></a>').text(moreText(page)).click(function() {
					var this_page = page + 1;
					$(cfg.elts + '.page-' + this_page, self).show();
					if (cfg.last_css) $(cfg.elts + '.page-' + (this_page-1) + ':last', self).css(cfg.last_css);
					if (this_page >= pages) {
						$(this).hide();
						all.hide();
						all = more = self = null;
					}
					else { $(this).text(moreText(this_page)); }
					page = this_page;
					return false;
				});

				if (cfg.all_text) {
					all = $('<a class="right space" href="#"></a>').text(cfg.all_text).click(function() {
						$(cfg.elts, self).show();
						$(this).hide();
						more.hide();
						if (cfg.last_css) {
							$(cfg.elts, self).each(function(i) {
								if (!((i+1) % cfg.page_size)) $(this).css(cfg.last_css);
							});
						}
						all = more = self = null;
						return false;
					});
					$(this).after(all);
				}

				$(this).after(more);
			}
		});
	}
})(jQuery);

/*
 * jQuery/Hunch Placeholder plugin
 */
 ;(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);

 /*
 * 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.flag.js (if you have clearFlag enabled - default)
 *  jquery.hunch-utils.js
 *
 */
;(function($) {

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

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

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

			if (settings.clearFlag) {
				this.hunchflagClearer();
			}

			var setText = function(obj, text) {
				if (settings.is_button) {
					obj.children('span').text(text);
				} else {
					obj.text(text);
				}
				return obj;
			}

			var toggle = function() {
				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) {
						data = settings.data;
					} else if (settings.getData) {
						data = settings.getData(t);
					} 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...');
					}

					var request = {
						url:url, data:data, type:'post', dataType:'json', timeout:10000,
						success: function(data, textStatus) {
							if (data.status == 'success') {
								if (was_set) {
									setText($t.removeClass(settings.setClass), state.text);
								} else {
									setText($t.addClass(settings.setClass), state.text);
								}
							} else if (data.status == 'limit') {
								if (settings.animate) setText($t, settings.setFalse.text);
								$.hunchError({msg: 'You have reached the limit for this action.<br />Sorry for the inconvenience.', fade: 4000}, true);
							} else {
								$.hunchError();
							}
						},
						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');
							}
						}
					}

					// 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);
			else this.click(toggle);
		}
	});
})(jQuery);

;(function($) {
	$.fn.extend({
		remaining: function($count_container, max, remaining_text, too_many_text) {
			var $t = $(this);
			var do_it = function() {
				var len = $t.val().replace(/\r\n/g, '\n').length;
				if (len > max) $count_container.html('<span class="warn-text"><strong>'+(len-max)+'</strong> '+(too_many_text || 'characters too many')+'</span>');
				else $count_container.html('<strong>'+(max-len)+'</strong> '+(remaining_text || 'characters remaining'));
			}
			this.change(do_it).keyup(do_it);
			do_it();
		}
	})
})(jQuery);

(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);
    };

	$.tts = function(slct, fade) { return $(slct).tooltip({ track: true, delay: 0, showURL: false, showBody: ' -- ', fade: (typeof(fade) == 'undefined' ? 250 : fade)}); };

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

		$('input#search-box').placeholder({ignoreSubmits: 1}).hunchac({
			container: '#ac-container',
			loadingClass: false,
			dropDownOnly: false,
			render: function(list, container, input) {
				var ul = $('<ul/>'),
					lists = [
						['Topic matches', $.grep(list, function(x) { return x.type == 't' || x.type == 'st'; }), 'ac-tm-'],
						['Result matches', $.grep(list, function(x) {
								if (x.type == 'i') {
									x.name = x.name + ' &nbsp;<span class="dim xsmall">(' + x.topic_name + ')</span>';
									return true;
								}
								return false;
							}),
						 'ac-rm-']
					];
				for (var i=0; i<lists.length; i++) {
					ul.append('<li class="ac-header'+(i==0?' ac-first-header':'')+'">'+lists[i][0]+'</li>');
					if (lists[i][1].length)
						$.each(lists[i][1], function(j) {
							var node = $('<li id="'+lists[i][2]+(j+1)+'" class="ac-element">&nbsp;&nbsp;'+(this.workshop ? '<b class="in-workshop"></b>' : '')+this.name+'</li>');
							$.data(node[0], $.hunchac.ORIGINAL_OBJECT, this);
							ul.append(node);
						});
					else
						ul.append('<li class="ac-none">&nbsp;&nbsp;No matches</li>');
				}
				container.html('<div class="yui-ac-content" /><div class="yui-ac-shadow" />');
				var wrap = $('.yui-ac-content', container);
				wrap.append(ul);
				$('.yui-ac-shadow', container).height(wrap.height()+2).width(wrap.width()+2);
			},
			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|iPad|iPod);/.test(navigator.userAgent)) { $('body').addClass('touch'); }
    });
})();
