/**
 * A simple querystring parser.
 * Example usage: var q = $.parseQuery(); q.foo returns  "bar" if query contains "?foo=bar"; multiple values are added to an array. 
 * Values are unescaped by default and plus signs replaced with spaces, or an alternate processing function can be passed in the params object .
 * http://actingthemaggot.com/jquery
 *
 * Copyright (c) 2008 Michael Manning (http://actingthemaggot.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 **/
$.parseQuery = function(qs,options) {
	var q = (typeof qs === 'string'?qs:window.location.search), o = {'f':function(v){return unescape(v).replace(/\+/g,' ');}}, options = (typeof qs === 'object' && typeof options === 'undefined')?qs:options, o = jQuery.extend({}, o, options), params = {};
	jQuery.each(q.match(/^\??(.*)$/)[1].split('&'),function(i,p){
		p = p.split('=');
		p[1] = o.f(p[1]);
		params[p[0]] = params[p[0]]?((params[p[0]] instanceof Array)?(params[p[0]].push(p[1]),params[p[0]]):[params[p[0]],p[1]]):p[1];
	});
	return params;
};

$.urlParam = function(key) {
	var results = new RegExp('[\\?&]' + key + '=([^&#]*)').exec(window.location.href);
	if (results)
		return results[1] || '';
	return '';
};

$.fn.extend({
	sum: function(fn) {
		var total = 0;
		$(this).each(function() {
			total += fn.apply($(this));
		});
		return total;
	},
	reduce: function(fn, start) {
		var $this = $(this);
		var current = start;
		for (var i = 0; i < $this.length; i++) {
			current = fn(current, $this.get(i));
		}
		return current;
	},
	showOver: function(target, offsetX, offsetY, zIndex) {
		var $this = $(this), pos = target.position(), offsetX = (offsetX? offsetX: 0), offsetY = (offsetY? offsetY: 0), 
			zIndex = (zIndex? zIndex: 10000);
		return $this.css({
			position: 'absolute',
			left: pos.left + target.outerWidth()/2 - $this.outerWidth()/2 + offsetX,
			top: pos.top + target.outerHeight()/2 - $this.outerHeight()/2 + offsetY
		}).appendTo(target.offsetParent()).show();
	}
});

// namespace pattern. http://yuiblog.com/blog/2007/06/12/module-pattern/
var shredunion = (function() {
	var local = {
		listbar: {
			updateDisplayForSelection: function($clicked) {
				if ($clicked.hasClass('selected')) return;
				$('#listbar_controls_1 .switch').removeClass('selected');
				$clicked.addClass('selected');

				// change the previous selected item and previous adjacent items
				$('#listbar_controls_1 .before_selected').removeClass('before_selected');
				$('#listbar_controls_1 .after_selected').removeClass('after_selected');

				// change the new selected item and new adjacent items
				var choice = $clicked.find('.tooltip_text').text();
				$('#listbar_controls_1 .title_text').text(choice);
				$clicked.prev('.switch').addClass('before_selected');
				$clicked.next('.switch').addClass('after_selected');				
			}
		},
		FB: {}
	};
	
	return {
		classes: {
			Popover: function(content, options, onSetup) {
				if (!options) options = {};
				var width = options.width || 300;
				var body = $('<div class="popover"/>').css({width: width, maxWidth: 300, maxHeight: 200, overflow: 'hidden', 
					background: 'url(/static/img/popover_bg.jpg) -'+((300-width)/2)+'px no-repeat'
				}).corner('10px').append(content);
				var container = $('<div/>').css({width: width, zIndex: 20}).append(body)
					.append($('<img src="/static/img/popover_point.png" height="16" width="24"/>')
						.css('margin-left', width/2 - 12));

				var clickedInside = function(event, $el) {
					var offset = $el.offset();
					var x = event.pageX - offset.left;
					var y = event.pageY - offset.top;
					return x > 0 && x < $el.width() && y > 0 && y < $el.height();
				};

				var shown = this.shown = false;

				var hide = this.hide = function() {
					$('body').unbind('click');
					container.fadeOut('fast', function() {
						shown = false;
					});
				};

				this.show = function(parent) {
					if (!parent) alert('ERROR: you need to specify an element above which the popover should appear.');
					if (shown) return this;
					shown = true;
					var $parent = $(parent);
					var pos = $parent.offset();

					if (container.parent().get() != $('body').get(0)) {
						container.appendTo('body').hide();
						if (onSetup) onSetup();
					}
					container.css({position: 'absolute', left: pos.left + $parent.outerWidth()/2 - width/2, 
						top: pos.top - container.outerHeight() + 2})
						.fadeIn('normal', function() {
							$('body').click(function(event) {
								if (!container.is(':hidden') && !clickedInside(event, container)) {
									hide();
								}
							});			
						});
					return this;
				};
			}			
		},
		util: {
			target: function(e) {
				var target;
				if (!e) var e=window.event;
				if (e.target) target=e.target;
			  	if (e.srcElement) target=e.srcElement;
				if (target.nodeType==3) target = target.parentNode; // defeat Safari bug
				return target;
			},
			setHashPath: function(path) {
				// the hash is the part of the url after the "#", which is used to represent ajax navigation
				// state in order to create a permalink, & to preserve state when the user navigates away from the
				// page and then returns.
				// using the hash is a bit of a hack; it's necessary to prevent the browser from reloading the page
				// when the location is changed.
				// first remove existing hash if present.
				var loc = new String(window.location);
				var hashIndex = loc.indexOf('#');
				if (hashIndex != -1)
					loc = loc.substring(0, hashIndex);
				window.location = loc + "#" + path;
			},
			hashPathParams: function() {
				// parse the current hash path and return a dictionary of query parameters. 
				// n.b.: requires jquery.parsequery.js (included above)
				var loc = new String(window.location);
				var hashIndex = loc.indexOf('#');
				if (hashIndex == -1) return null;
				var query = loc.substring(hashIndex+1, loc.length);
				if (query.length == 0) return null;
				return $.parseQuery(query);
			}
		},
		forms: {
			addOptions: function(select, optionList, clearExisting, addEmpty) {
				// take a list of tuples (value, text) and add them as options to a select,
				// optionally clearing the existing options first
				select = $(select);
				if (clearExisting)
					select.attr('options').length = 0;
				if (addEmpty)
					select.attr('options')[select.attr('options').length] = new Option("---------", "");
				for (var i=0; i<optionList.length; i++)
					select.attr('options')[select.attr('options').length] = new Option(optionList[i]["fields"]["name"], optionList[i]["pk"]);
			}
		},
		ui: {
			shrinkToFit: function($element, height, width) {
				if ($element.text().length > 0) {
					while ($element.outerHeight() > height || (width && $element.outerWidth() > width)) {
						var size = parseInt($element.css('font-size'));
						$element.css('font-size', size - 1 + "px");
					}
				}
			},
			equalizeDivHeights: function(divs, shrink) {
				var currentHighest = 0;
				for (var i=0; i<divs.length; i++) {
					if (shrink)
						divs[i].css('min-height', '');
					currentHighest = Math.max(currentHighest, divs[i].height());					
				}
				
				for (var i=0; i<divs.length; i++)
					divs[i].css('min-height', currentHighest + "px");
			},
			asyncLoad: function(event) {
				// event handler, to be assigned on click to links with class "async" so that they replace
				// themselves with their linked content.
				event.preventDefault();
				$link = $(this);
				$.post($link.attr('href'), null, function(resp) { // post to prevent caching
					$resp = $(resp);
					$link.after($resp).remove();
					// prevent tooltips from getting stuck if they're open and the hover target is removed 
					$('#tooltip').remove();
				});
			},
			showTip: function(event) {
				event.preventDefault();
				var $this = $(this);
				var coords = $this.offset();
				$("#tooltip").remove();
				var tip = $("<div/>").attr('id','tooltip').html($this.find('.tooltip_text').html()).appendTo($('body'));
				tip.css({
					position: 'absolute',
					top: coords.top + $this.outerHeight() + 3,
					zIndex: 2000,
					display: 'none'
				});
				// it's necessary to do the above first or the tip width will be incorrect.
				tip.css({left: coords.left + $this.outerWidth()/2 - tip.outerWidth()/2 + "px"});
				tip.fadeIn('fast');
			},
			hideTip: function(event) {
				event.preventDefault();
				$('#tooltip').fadeOut('fast', function() {$(this).remove();});
			},
			showLoadingDots: function(target, width, height, offsetX, offsetY, zIndex) {
				return $('<img src="/static/img/loading_horiz.gif" height="'+height+'" width="'+width+'"/>')
					.hide().appendTo('body').showOver(target, offsetX, offsetY, zIndex);
			}
		},
		FB: {
			askToConnect: function(parent, onSuccess, onFailure) {
				if (shredunion.auth.isAuthenticated) {
					if (onSuccess) onSuccess();
					return;
				}
				shredunion.trackEvent('login/signup', 'opened FB popover');
				if (!local.FB.promptNode)
					local.FB.promptNode = $('#fbPrompt').remove();
				// if parent is a DOM element, show prompt in popover; otherwise show as modal
				if (parent) {
					var popover = new shredunion.classes.Popover(local.FB.promptNode, null, function() {
						$('#no_fb').click(function() {
							popover.hide();
							shredunion.trackEvent('login/signup', 'canceled FB popover');
							if (onFailure) onFailure();
						});
						$('#yes_fb').click(function() {
							popover.hide();
							shredunion.trackEvent('login/signup', 'accepted FB popover');
							shredunion.FB.connect(onSuccess);
						});
					}).show(parent);
				} else {
					local.FB.promptNode.modal({
						opacity: 70,
						close: false,
						onOpen: function(dialog) {
							dialog.overlay.fadeIn('fast', function() {
								dialog.container.show();
								$('#fbPrompt').corner('10px');
								dialog.data.show();
							});
						},
						onShow: function() {
							$('#no_fb').click(function() {
								if (onFailure) onFailure();
							});
							$('#yes_fb').click(function() {
								shredunion.FB.connect(onSuccess);
							});							
						}
					});
				}
			},
			connect: function(onSuccess, onFailure) {
				shredunion.trackEvent('login/signup', 'started FB connect');
				// prompt user to connect with facebook
				FB.Connect.requireSession(function() {
					var sequencer = new FB.BatchSequencer();
					var hasOfflinePerm = FB.Facebook.apiClient.users_hasAppPermission('offline_access', sequencer);
					var hasEmailPerm = FB.Facebook.apiClient.users_hasAppPermission('email', sequencer);

					// prompt for any permissions that have not yet been granted; then authenticate
					sequencer.execute(function() {
						var lookupRider = function() {
							FB.Facebook.get_sessionWaitable().waitUntilReady(function(session) {
								$.post(shredunion.paths.facebook_connect, session, function(resp) {
									if (resp.rider_name) {
										shredunion.trackEvent('login/signup', 'success at step 1');
										if (onSuccess) onSuccess(resp);							
									} else {							
										// user not logged in and FB creds unrecognized; prompt again
										$.get(shredunion.paths.facebook_connect_2, null, function(resp) {
											var setupConnectDialog = function() {
												var rNo = $('#connect_form_existing_user_0'), rYes = $('#connect_form_existing_user_1'),
													fields = $('#fields_slider'), container = $('#fields_container');

												var finishSlide = function(finalX) {
													var disable = finalX == 0? 'div:last input': 'div:first input';
													var enable = finalX == 0? 'div:first input': 'div:last input';
													fields.find(disable).attr('disabled', true);
													fields.find(enable).removeAttr('disabled')[0].focus();										
												};
												var slideForm = function(animated) {
													var finalX = rNo[0].checked? 0: -container.width();
													if (fields.css('left') != finalX) {
														if (animated) {
															fields.animate({left: finalX}, 'normal', function() {
																finishSlide(finalX);
															});
														} else {
															fields.css('left', finalX);
															finishSlide(finalX);
														}
													}
												};
												rNo.add(rYes).click(function() {slideForm(true);});
												slideForm(false);

												var form = $('#FBdialog form');
												$('#FBdialog form :submit').click(function() {
													$.post(form.attr('action'), form.serialize(), function(resp) {
														if (resp.status == 'error') {
															shredunion.trackEvent('login/signup', 'form error at step 2');
															$('#FBdialog').replaceWith(resp.html);
															$('#FBdialog').corner('10px');
															setupConnectDialog();
														} else {
															shredunion.trackEvent('login/signup', 'success at step 2');
															if (onSuccess) onSuccess(resp);							
														}
													}, 'json');
													return false;
												});
											};
											$.modal.close(); // in case login dialog is open
											$(resp).modal({
												opacity: 70,
												position: [30,],
												overlayClose: true,
												onOpen: function(dialog) {
													dialog.overlay.fadeIn('fast', function() {
														dialog.container.show();
														$('#FBdialog').corner('10px');
														dialog.data.show();
													});
												},
												onClose: function() {
													shredunion.trackEvent('login/signup', 'canceled at step 2');
													$.modal.close();
													if (onFailure) onFailure('user canceled SU account dialog');
												},
												onShow: setupConnectDialog
											});
										})
									}						
								}, 'json');
							});
						};

						if (hasOfflinePerm.result == 0 || hasEmailPerm.result == 0) {
							// if one perm is already granted, will only ask for the other
							FB.Connect.showPermissionDialog('offline_access,email', lookupRider);
						} else {
							lookupRider();
						}
					});

				}, 
				function() {
					shredunion.trackEvent('login/signup', 'canceled FB connect');					
					if (onFailure) onFailure('user canceled FB connect dialog');
				}, true);
				return false;
			}
		},
		listbar: {
			onSwitchClick: function(event) {
				var $clicked = $(this);
				local.listbar.updateDisplayForSelection($clicked);
				// optional callback to be overriden by other scripts
				if (shredunion.listbar.onSwitchClickExtra)
					shredunion.listbar.onSwitchClickExtra.apply(this);
				return false;
			},
			onSwitchClickExtra: undefined,
			applyChoice: function(choice) {
				var $clicked = $($('#listbar_controls_1 .switch .tooltip_text:contains('+choice+')').parent());
				local.listbar.updateDisplayForSelection($clicked);
			}
		},
		auth: {
			isAuthenticated: false,
			setupForms: function(stayOnPage) {
				// this is a separate function because it needs to be re-run every time a new login form
				// is loaded into the page. can't use delegation because the submit event doesn't propagate
				$('#login_form').submit(function() {
					var $this = $(this);
					$this.css('opacity', 0.3);
					$.post($this.attr('action'), $this.serialize(), function(resp) {
						if (resp.redirect) {
							shredunion.trackEvent('login/signup', 'login form success');
							if (stayOnPage) {
								location.reload(true);
							} else {
								var oldLocation = new String(location.pathname);
								location.pathname = resp.redirect;
								if (oldLocation == resp.redirect)
									location.reload(true);
							}
						} else {
							shredunion.trackEvent('login/signup', 'login form failure');
							$('#login_form').replaceWith(resp.html);
							shredunion.auth.setupForms(stayOnPage);
						}
					}, 'json');
					$this.find('input').attr('disabled', true);
					return false;
				});

				$('#signup_form').submit(function() {
					var $this = $(this);
					$this.css('opacity', 0.3);
					$.post($this.attr('action'), $this.serialize(), function(resp) {
						if (resp == 'ok') {
							shredunion.trackEvent('login/signup', 'signup form success');
							var oldLocation = new String(location.pathname);
							location.pathname = resp.redirect;
							if (oldLocation + location.hash == resp.redirect)
								location.reload(true); // force reload if necessary 
						} else {
							shredunion.trackEvent('login/signup', 'signup form failure');
							$('#signup_form').replaceWith(resp);
							shredunion.auth.setupForms(stayOnPage);
						}
					});
					$this.find('input').attr('disabled', true);
					return false;
				});
			},
			openDialog: function(event, cannotClose, extraMessage, stayOnPage) {
				shredunion.trackEvent('login/signup', 'opened dialog');				
				var next = $(this).attr('href'),
					url = shredunion.paths.login_signup + '?invite_token=' + $.urlParam('invite_token');
				$.get(url, (next? {next: next} : null), function(resp) {
					$(resp).modal({
						close: !cannotClose,
						closeHTML: '',
						opacity: 70,
						position: [30,],
						overlayClose: true,
						onShow: function() {
							shredunion.auth.setupForms(stayOnPage);
						},
						onOpen: function(dialog) {
							dialog.overlay.fadeIn('fast', function() {
								dialog.container.show();
								$('#login_signup').corner('10px');
								if (extraMessage) {
									$('#login_signup div:first').prepend(
										$('<h2/>').text(extraMessage).css('margin-bottom', 10));
								}
								dialog.data.show();
							});
						},
						onClose: function() {
							shredunion.trackEvent('login/signup', 'closed dialog');
							$.modal.close();
						}
					});
				});
				return false;
			}
		},
		track: undefined, // defined in default.html
		trackEvent: undefined // defined in default.html
	};
})();

$(function() {
	// set up the close button for the system messages bar
	$("#close_messages").click(function() {
		$('#messages').hide("slow");
	});
	// and close it after 1 min automatically
	setTimeout(function() {$('#messages').hide("slow");}, 60000);

	var searchInput = $('#search input');
	if (searchInput.length > 0) {
		searchInput.autocomplete(shredunion.paths.autocomplete_search, {
			minChars: 3, width: 250, scrollHeight: 400, max: 100,
			formatResult: function(data, value) {
				return $('<div/>').html(value).find('a.name').text();
			}
		}).result(function(event, data, value) {
			var val = $('<div/>').html(value);
			if (val.find('.not_result').length > 0) {
				searchInput.val('');	
			} else
				window.location = val.find('a.name').attr('href');
		});
	}

	
	// set up fixed rollover for current page
	var foundSelectedTab = false;
	$('#nav li[id!=search], #user_nav li').each(function() {
		if (foundSelectedTab) return;
		var path = new String(window.location);
		path = path.substring(path.indexOf('/', 7));
		var href = $(this).find('a:first').attr('href');
		if (href.length == 1 && path.length != 1) return;
		if (path.search(href) > -1) {
			var $this = $(this);
			var rider = href.search('rider') > -1;
			$this.addClass('selected').css(
				'background', 
				'url(/static/img/' + (rider? 'user' : 'main') + 'bar_rollover.jpg) no-repeat -' + 
				($this.offset().left - $this.parent().offset().left) + 'px ' + (rider? '-1' : '0') + 'px');
			foundSelectedTab = true;
		}
	})
	
	// set up menubar rollovers
	$('#nav > li[id!=search][class!=selected]').hover(
		function() {
			var $this = $(this);
			$this.css('background', 'url(/static/img/mainbar_rollover.jpg) no-repeat -' + ($this.offset().left - $this.parent().offset().left) + "px 0px");
		},
		function() {$(this).css('background','none');}
	);
	$('#user_nav > li[class!=selected]').hover(
		function() {
			var $this = $(this);
			$this.css('background', 'url(/static/img/userbar_rollover.jpg) no-repeat -' + ($this.offset().left - $this.parent().offset().left) + "px -1px");
		},
		function() {$(this).css('background','none');}
	);
	
	// set up self-replacing links
	$('a.async').live('click', shredunion.ui.asyncLoad);
	
	// set up tooltips
	$('.has_tooltip').live('mouseover', shredunion.ui.showTip).live('mouseout', shredunion.ui.hideTip);

	// display IE6 warning
	if ($.browser.msie && parseFloat($.browser.version) < 7)
		$("#browser_warning").css('display','block');
		
	// make ads open in new window and track them
	$('.ads:not(.no_retarget) a').each(function() {
		this.target = '_blank';
	}).click(function() {
		shredunion.trackEvent('Ads', 'click', this.href);
	});
	
	// facebook
	if (window.FB) {
		FB.init("ea12ebdda3e5ed48f0b843f316bf3653", "/static/fb/xd_receiver.html");
		FB.ensureInit(function() {
			// debug
			var writeStatus = function() {
				FB.Connect.get_status().waitUntilReady(function(status) {
					switch (status) {
						case FB.ConnectState.connected:
							$('#FB_status').text('connected');
							break;
						case FB.ConnectState.appNotAuthorized:
							$('#FB_status').text('unauthorized');
							break;
						case FB.ConnectState.userNotLoggedIn:
							$('#FB_status').text('not logged in');
							break;
					}
				});
			};
			//writeStatus();
			$('#FBtestLogin').click(function() {
				shredunion.FB.connect(function() {
					writeStatus();
					if (window.console) console.log('callback works');
				});
			});
		
			$('.FBConnectButton').live('click', function() {
				shredunion.trackEvent('login/signup', 'clicked FB button');
				shredunion.FB.connect(function(resp) {
					if (resp.redirect)
						window.location.href = resp.redirect;
					else
						window.location.reload();
				});
				return false;
			})
		});
	} else {
		if (window.console) console.log('FB libs not loaded');
	}
	
	// paging
	$('a.pageturner').live('click', function() {
		var link = this;
		$.get(link.href, null, function(resp) {
			$(link).parents('.pageholder').empty().append(resp);
		});
		return false;
	});
	
	// listbar
	$('#listbar_controls_1 .switch').click(shredunion.listbar.onSwitchClick);
	
	// when not logged in
	if ($('#signup_splash').length > 0) {
		$('#signup_splash .cycle').cycle();
		$('#nav_login_signup a, a.login').click(shredunion.auth.openDialog);

		if ($.urlParam('invite_token')) shredunion.auth.openDialog();
	} else {
		shredunion.auth.isAuthenticated = true;
	}
	
	// lightboxes
	$('a.lightbox').live('click', function() {
		shredunion.trackEvent('Pictures', 'view', $(this).attr('href'));
		tb_show(this, function(el) {
			var pic_id = $(el).find('.picId').text();
			$.post(shredunion.paths.view_picture, {picture_id: pic_id}, function(resp) {
				$('#TB_caption').prepend(resp);
			});
		});
		this.blur();
		return false;
	});
});
