Jaml.automaticScope = false;

var YourSpark = {
	API_KEY: "790c4ff32acc046a93c9899a14b9b55d",
  
	current_user_id: 0,
	markers: [],
	ignoreMarker: null,
	tierFilter: 2,
	filterOptions: {},
	signupRequiredFields: ['first_name', 'last_name', 'user_name', 'email', 'password'],
	bookmarkedSpotURLs: [],
	bookmarkedTripURLs: [],
	
	onready: function() {
		$.ajaxSetup({beforeSend : function(xhr) {
			xhr.setRequestHeader('Accept', 'application/json');
			xhr.setRequestHeader('X-YourSpark-API-Key', YourSpark.API_KEY);
			xhr.setRequestHeader('X-YourSpark-API-Version', '0');
		}});
		this.fb_init(function() {
			FB.Connect.get_status().waitUntilReady(function(state){
				if(FB.ConnectState.connected==state) {
					//alert("connected");
				} else {
					//alert("not logged in");
				}
			});
	  	//FB.Facebook.get_sessionState().waitUntilReady(function(session) {
	    //	var is_now_logged_into_facebook = session ? true : false;
			//	if(is_now_logged_into_facebook == already_logged_into_facebook) {
			//		alert("logged in");
			//	} else {
	    //		alert("got fb");
	      	//YourSpark.refresh();
			//	}
	    //});
	  });
	},

	fb_init: function(callback) {
		if(window.is_initialized) {
	    callback();
	  } else {
	    FB_RequireFeatures(["XFBML"], function() {
	      FB.Facebook.init(window.facebook_api_key, "/xd_receiver.html");
	      window.is_initialized = true;
	      callback();
	    });
	  }
	},
	
	fbPermissions: function(callback) {
		FB_RequireFeatures(["XFBML"], function() {
			FB.Connect.get_status().waitUntilReady(function(state) {
				if(FB.ConnectState.connected==state) {
					FB.Connect.showPermissionDialog('publish_stream,offline_access', function(perms) {
						FB.Facebook.apiClient.users_getInfo(FB.Connect.get_loggedInUser(), "first_name, last_name, username, contact_email, pic, pic_square", function(info) {
							YourSpark.fbUserInfo = info[0];
							if(callback) callback();
						});
					});
				}
			});
		});
	},
	
  //
	//publishFeedStory: function(bundle_id, template_data) {
	//  this.init(function() {
	//    FB.Connect.showFeedDialog(bundle_id, template_data);
	//  });
	//},

	
	
	checkDupe: function(url, q, obj, username) {
		if(username) { $('#passport_url').html('http://YourSpark.com/'+q) };
		$.ajax({
		  type: "POST",
		  url: url,
		  data: { q:q },
		  dataType: 'json',
		  success: function(transport) {
				if(transport.in_use) {
					obj.removeClass("good");
					obj.addClass("bad");
					obj.html("It's taken :(");
				} else {
					obj.removeClass("bad");
					obj.addClass("good");
					obj.html("It's good!");
				}
			}
		});
	},
	
	popup: function(url) {
		$.ajax({
		  type: "GET",
		  url: url,
		  dataType: 'html',
		  success: function(html) { $('#popup').html(html) }
		});
	},

	unload: function() {
		GUnload();
	},

	refresh: function() {
	  window.location = '/';
	},
	
	showAndroidOverlay: function() {
		$('#overlay-wrapper').html(tmpl('download_android_app', {}));
		YourSpark.setOverlayPosition();
	},
	
	showPalmOverlay: function() {
		$('#overlay-wrapper').html(tmpl('download_palm_app', {}));
		YourSpark.setOverlayPosition();
	},
	
	
	
	
	escapeHTML: function(val) {
		return val.replace(/</g,"&lt;").replace(/>/g,"&gt;");
	},

	fetchUsers: function() {
		var q = $('input[name="q"]').val();
		if (!q) return false;
		$('#find-friends-wrapper').html(tmpl('loading_friends_tmpl', {network:'search'}));
		$.getJSON('/friends/find', {q:q}, function(users) {
			$('#find-friends-wrapper').html(tmpl('potential_friends_tmpl', {friends:users, network:'search', q:q}));
			$('a.btn').click(function() { YourSpark.requestFriendship($(this).attr('url'), this) });
			$('h3').html('We found <b>'+ users.length + '</b> people');
			$('input[name="q"]').addClass('on');
		});
	},
	
	fetchPotentialFriends: function(network, params) {
		$('#subnav li').removeClass('selected');
		$('#subnav li[name="'+network+'"]').addClass('selected');
		$('#subnav li[name="'+network+'"]').append($('.spinner'));
		$('#subnav li[name="'+network+'"] .spinner').show();
		$('#fb_inviter').hide();
		if(network == 'search') {
			$('#find-friends-wrapper').html(tmpl('search_friends_tmpl', {q:''}));
			$('#subnav li[name="'+params+'"]').addClass('selected');
			$('.spinner').hide();
			return;
		} else if(network=='facebook') {
			var loading_template = 'loading_friends_tmpl';
			var template = 'facebook_friends_tmpl';
			var url = '/potential_facebook_friends?format=json&show_pending=true';
			var network = 'Facebook';
		}	else if(network=='twitter') {
			var loading_template = 'loading_friends_tmpl';
			var template = 'twitter_friends_tmpl';
			var url = '/potential_twitter_friends?format=json&show_pending=true';
			var network = 'Twitter';
		} else if(network.search('redirect') > -1) {
			$('#find-friends-wrapper').html(tmpl('redirect_'+params+'_friends_tmpl', {}));
			$('#subnav li[name="'+params+'"]').addClass('selected');
			$('.spinner').hide();
			return;
		} else if(network=='gmail') {
			var loading_template = 'loading_contacts_tmpl';
			var template = 'import_friends_tmpl';
			var url = '/friends/import?format=json&'+params;
			var network = "Gmail"
		} else if(network=='yahoo') {
			var loading_template = 'loading_contacts_tmpl';
			var template = 'import_friends_tmpl';
			var url = '/friends/import?format=json&'+params;
			var network = "Yahoo"
		} else if(network=='windows') {
			var loading_template = 'loading_contacts_tmpl';
			var template = 'import_friends_tmpl';
			var url = '/friends/import?format=json&'+params;
			var network = "Live"
		} else if(network=='email') {
			$('#find-friends-wrapper').html(tmpl('email_friends_tmpl', {}));
			$('#subnav li[name="'+params+'"]').addClass('selected');
			$('.spinner').hide();
			return;
		}
		$('#find-friends-wrapper').html(tmpl(loading_template, {network:network}));
		if(network=='gmail' || network=='yahoo' || network=='windows') $.ajaxSetup({ error: function() { YourSpark.flashDialog('fail', 'Oops!', 'There was a problem fetching your contacts.'); } });
		$.getJSON(url, function(data, status) {
			$('#find-friends-wrapper').html(tmpl(template, {friends:data.potential_friends, contacts:(data.contacts || []), network:network, q:''}));
			if(network=="Facebook") $('#fb_inviter').show(); else $('#fb_inviter').hide();
			if(data.potential_friends.length > 0) { $('.inviter .title').show(); $('.inviter p').show(); } else { $('.inviter .title').hide(); $('.inviter p').hide(); }
			$('a.btn').click(function() { YourSpark.requestFriendship($(this).attr('url'), this) });
			$('.spinner').hide();
			$('input[name="q"]').focus();
		});
	},
	
	flashDialog: function(status, title, body) {
		var flash = '<div class="flash-wrapper" onclick="$(this).hide();"><div class="flash '+status+'"><p class="title">'+title+'</p><p>'+body+'</p><a href="#" class="close"></a></div></div>';
		setTimeout(function() { $('.flash-wrapper').hide(); }, 10000);
		$('#content').append(flash);
		window.scrollTo(0, 0);
	},
	
	requestFriendship: function(url, obj) {
		if(obj) {
			var o = $(obj);
			if(o.parent().hasClass('disabled')) return false;
			o.parent().addClass('disabled');
			o.html('Request Sent');
		}
		$.post(url, function(data, status) {
			//alert("it worked!");
			// remove the li (where did it come from?)
			// add a new li to the waiting area
		});
	},

	acceptFriendship: function(url) {
		$.post(url, function(data, status) {
			// alert("it worked!");
			// remove the li (where did it come from?)
			// add a new li to the friends area
		});
		YourSpark.changeFriendshipCount();
	},

	rejectFriendship: function(url) {
		$.post(url, function(data, status) {
			//alert("it worked!");
		});
		YourSpark.changeFriendshipCount();
	},
	
	changeFriendshipCount: function() {
		var requestCnt = parseInt($('#pending_friend_count').html());
		if(requestCnt-1 > 0) {
			$('#pending_friend_count').html(requestCnt-1);
		} else {
			$('#pending_friend_requests').hide();
		}
	},
	
	loadFriends: function(url) {
		$.getJSON(url, function(data){
          	$.each(data['friends_waiting'], function(i, user) {
            	$("<li><a href=\"" + user.url + "\">" + h(user.name) + "</a> - <a href=\"#\" onclick=\"YourSpark.rejectFriendship('" + user.reject_url + "'); return false;\">Forget it</a></li>").appendTo("#friends_waiting");
          	});
          	$.each(data['friends_needing_approval'], function(i, user) {
            	$("<li><a href=\"" + user.url + "\">" + h(user.name) + "</a> - <a href=\"#\" onclick=\"YourSpark.acceptFriendship('" + user.accept_url + "'); return false;\">Accept</a> or <a href=\"#\" onclick=\"YourSpark.rejectFriendship('" + user.reject_url + "'); return false;\">Ignore</a></li>").appendTo("#friend_requests");
          	});
          	$.each(data['friends'], function(i, user) {
            	$("<li><a href=\"" + user.url + "\">" + h(user.name) + "</a> - <a href=\"#\" onclick=\"YourSpark.rejectFriendship('" + user.reject_url + "'); return false;\">Remove</a></li>").appendTo("#friends");
          	});
	    });
	},

	loadTwitterFriends: function(url) {
		$.getJSON(url, function(data){
          	$.each(data['twitter_friends'], function(i, user) {
            	$("<li><a href=\"" + user.url + "\">" + h(user.name) + "</a> - <a href=\"#\" onclick=\"YourSpark.requestFriendship('" + user.request_url + "'); return false;\">Add</a></li>").appendTo("#twitter_friends");
          	});
	    });
	},
	
	getSearchParams: function(options) {
		if(options.l && options.l != 'City, State or Zip Code') { this.filterOptions["l"] = options.l; Paginator.currentPage = 1; this.filterOptions["sw"] = ''; this.filterOptions["ne"] = ''; }
		if(options.l == 'City, State or Zip Code') { this.filterOptions["l"] = ''; this.filterOptions["sw"] = ''; }
		if(options.q && options.q != 'Coffee ') this.filterOptions["q"] = options.q;
		if(options.q == 'Coffee ') this.filterOptions["q"] = '';
		if(options.c) { this.filterOptions["category_id"] = options.c; }
		if(options.c == " ") { this.filterOptions["category_id"] = ""; }
		if(options.featured) { this.filterOptions["featured"] = options.featured; }
		if(options.context) { this.filterOptions = {}; this.filterOptions["context"] = options.context; }
		if(options.sw) { this.filterOptions["sw"] = options.sw; this.filterOptions["l"] = ''; }
		if(options.ne) this.filterOptions["ne"] = options.ne;
		if(options.lat) { this.filterOptions["lat"] = options.lat; this.filterOptions["l"] = ''; }
		if(options.lng) this.filterOptions["lng"] = options.lng;
		this.filterOptions["order"] = options.order || $('#spots_sort').val() || '';
		this.filterOptions["limit"] = 20;
		
		var combinedOptions = {};
		$.each(options, function(k,v) {
			combinedOptions[k] = v;
		});
		$.each(this.filterOptions, function(k,v) {
			combinedOptions[k] = v;
		});
		
		//window.location = "#"+$.param(combinedOptions);
		this.setSearchFilters();
		return combinedOptions;
	},
	
	setSearchFilters: function() {
		var opts = $('#spots_sort > option');
		for(i=0; i < opts.length; i++) {
			if(opts[i].value == this.filterOptions["order"]) {
				$('#spots_sort').attr('selectedIndex', i);
			}
		}

		var opts = $('#spots_category > option');
		for(i=0; i < opts.length; i++) {
			if(opts[i].value == this.filterOptions["category_id"]) {
				$('#spots_category').attr('selectedIndex', i);
			}
		}
		
		if(this.filterOptions["featured"] == 1) $('#featured_filter').attr('checked', true);
	},
	
	getAnchorOptions: function() {
	  var qs = window.location.toString();
		var anchorPos = qs.search('#');
		if(anchorPos == -1) return false;
		qs = qs.substring(anchorPos + 1);
		qs = qs.replace(/#/, '');
		if(qs.length > 0) {
			var options = {};
		  var vars = qs.split("&");
			for (var i=0; i < vars.length; i++) {
		  	var pair = vars[i].split("=");
		  	options[pair[0].toString()] = pair[1].toString()
		  }
			return options;
		} else {
			return false;
		}
	},
	
	getQueryString: function(v) {
	  var qs = window.location.toString();
		qs = qs.substring(qs.search('#') + 1);
	  var vars = qs.split("&");
	  for (var i=0; i < vars.length; i++) {
	  	var pair = vars[i].split("=");
	  	if (pair[0] == v) {
	    	return pair[1];
	    }
	  }
	},
	
	
	
	displayCategories: function(id) {
		$.getJSON('/categories/'+id, function(data) {
			$('#categories').empty();
			$('#spot_pic').attr("src", data.image_url);
	    $.each(data.categories, function(i, cat) {
				$('#categories').append('<option value="'+cat.id+'">'+cat.name+'</option>');
		    if(cat.categories) {
			    $.each(cat.categories, function(i, cat2) {
						$('#categories').append('<option value="'+cat2.id+'">&nbsp;&nbsp;'+cat2.name+'</option>');
				    if(cat2.categories) {
							$.each(cat2.categories, function(i, cat3) {
								$('#categories').append('<option value="'+cat3.id+'">&nbsp;&nbsp;'+cat3.name+'</option>');
							});
						}
					});
				}
			});
		});
	},
	
	
	
	
	signupInit: function() {
		YourSpark.fbPermissions(function() {
			if(YourSpark.fbUserInfo.pic) {
				$('#fb_image_url').val(YourSpark.fbUserInfo.pic);
			}
			if(YourSpark.fbUserInfo.pic_square) {
				$('#profile_pic').attr("src", YourSpark.fbUserInfo.pic_square);
			}
			if(YourSpark.fbUserInfo.first_name) {
				$('#first_name').val(YourSpark.fbUserInfo.first_name);
				YourSpark.signupBlur($('#first_name'));
			}
			if(YourSpark.fbUserInfo.last_name) {
				$('#last_name').val(YourSpark.fbUserInfo.last_name);
				YourSpark.signupBlur($('#last_name'));
			}
			if(YourSpark.fbUserInfo.username) {
				$('#user_name').val(YourSpark.fbUserInfo.username);
				YourSpark.signupKeyUp('#user_name', 0, '/users/check_username');
			}
			if(YourSpark.fbUserInfo.contact_email) {
				$('#email').val(YourSpark.fbUserInfo.contact_email);
				YourSpark.signupBlur($('#email'), '/users/check_email');
			}
			$('#password').focus();
		});
	},
	
	signupFocus: function(el) {
		$(el).parent().parent().addClass('selected');
		if($(el).nextAll('.help:visible').size() > 0) return;
		$(el).nextAll('.info').show();
	},
	
	signupBlur: function(el, url) {
		$(el).parent().parent().removeClass('selected');
		if($(el).val()) {
			if($(el).attr('id') == "user_name") return;
			if($(el).attr('id') == "email") {
				if(!$(el).val().match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.@-]+\.[A-Za-z]{2,6}$/)) {
					YourSpark.signupHelpShow(el, 'fail', 'error');
					return;
				}
				$.ajax({
				  type: "POST",
				  url: url,
				  data: { q:$(el).val() },
				  dataType: 'json',
				  success: function(transport) {
						$(el).nextAll('.spinner').hide();
						if(transport.in_use) {
							YourSpark.signupHelpShow(el, 'error', 'error');
						} else {
							YourSpark.signupHelpShow(el, 'pass', 'success');
						}
					}
				});
				return;
			}
			YourSpark.signupHelpShow(el, 'pass', 'success');
		} else {
			YourSpark.signupHelpHide(el, 'info', '');
		}
	},
	
	signupKeyUp: function(el, event, url) {
		if(this.signupValidateKey(event)) {
			$('#passport_url').html($(el).val());
			if($(el).val()) {
				$(el).parent().parent().attr('class', '');
				$(el).nextAll().hide();
				$(el).nextAll('.spinner').show();
				setTimeout(function() {
					$.ajax({
					  type: "POST",
					  url: url,
					  data: { q:$(el).val() },
					  dataType: 'json',
					  success: function(transport) {
							$(el).nextAll('.spinner').hide();
							if(transport.in_use) {
								YourSpark.signupHelpShow(el, 'error', 'error');
							} else {
								YourSpark.signupHelpShow(el, 'pass', 'success');
							}
						}
					});
				}, 1000);
			} else {
				$(el).parent().parent().attr('class', '');
				$(el).nextAll().hide();
			}
			return;
		}
		if($(el).val($(el).val().replace(/\W+/, "")));
	},
	
	signupHelpShow: function(el, className, parentClass) {
		$(el).nextAll().hide();
		$(el).parent().parent().attr('class', parentClass);
		$(el).nextAll('.'+className).show();
	},
	
	signupHelpHide: function(el, className, parentClass) {
		$(el).nextAll().hide();
		$(el).parent().parent().attr('class', parentClass);
		$(el).nextAll('.'+className).hide();
	},
	
	signupValidate: function() {
		$('.fail').hide();
		$('input').each(function(i, el) {
			if($.inArray($(el).attr('id'), YourSpark.signupRequiredFields) > -1 && !$(el).val()) {
				$(el).nextAll('.help').hide();
				YourSpark.signupHelpShow(el, 'fail', 'error');
			}
		})
		return !($('.fail:visible').size() > 0);
	},
	
	signupValidateKey: function(event) {
		if(!event) return true;
		var code = event.keyCode;
		if(code >= 48 && code <= 57 && !event.altKey && !event.ctrlKey && !event.shiftKey) return true; //0-9 (without modifier keys)
		if(code >= 65 && code <= 90 && !event.altKey && !event.ctrlKey) return true; //a-z (without crtl & alt)
		if(code == 0 || code == 8 || code == 46) return true; //underscore, backspace, or delete
		return false;
	},

	setOverlayPosition: function() {
	  var scroll_top = document.documentElement.scrollTop || document.body.scrollTop;
	  var scroll_height = document.documentElement.scrollHeight;
	  var client_height = window.innerHeight || document.documentElement.clientHeight;
	  $('.overlay-screen').css('height', scroll_height+"px");
	  $('.overlay-content').css('top', scroll_top+(client_height*0.09)+"px");
	},
	
	
	

	fetchUser: function(url) {
		$.getJSON(url, function(user) {
			$('#header-wrapper').html(tmpl('user_header_tmpl', user));
			$('#user-subnav-wrapper').html(tmpl('user_subnav_tmpl', user));
			if($('#stamps-wrapper').html() != null) {
				$.getJSON(user.stamps_url, {limit:10}, function(stamps) {
					$('#stamps-wrapper').html(tmpl('user_stamps_tmpl', {user:user, stamps:stamps}));
				});
			}
			$.getJSON(user.friends_url, {limit:28, order:'rand'}, function(data) {
				$('#user-friends-wrapper').html(tmpl('user_friends_tmpl', data));
				$('#friends_count').html(user.friends_count);
			});
			$.getJSON(user.top_spots_url, function(spots) {
				$('#top-spots-wrapper').html(tmpl('top_spots_tmpl', {user:user, spots:spots}));
			});
			if($('#user-events-wrapper').html() != null) {
				$.getJSON(user.events_url, function(events) {
					$('#user-events-wrapper').html(tmpl('user_events_tmpl', {user:user, events:events.events}));
					$('.feed:last').addClass('last');
				});
			}
			if($('#user-photos-wrapper').html() != null) {
				$.getJSON(user.photos_url, function(data) {
					$('#user-photos-wrapper').html(tmpl('user_photos_tmpl', {photos:data.activity}));
				});
			}
		});
	},
	
	activityStream: function() {	  
	  function unescapeUnicode(str) {
	    return str.replace(/\\\\u([0-9a-f]{4})/g, function(str, num) {
	      return String.fromCharCode(Number("0x" + num));
      });
	  }
	  
	  $.ajax({
	    url: '/stream',
	    data: { limit: '100' },
	    dataType: 'json',
	    success: function(checkins) {
	      // Fix idiotic escaping by ActiveSupport::JSON.
        var str = $.toJSON(checkins);        
        checkins = $.evalJSON(unescapeUnicode(str));
        
  	    // Split into two columns.
  	    var filteredCheckins = [], templateName;
  	    for (var i = 0, len = checkins.length; i < len; i++) {
  	      templateName = 'activity_feed_item_' + checkins[i].type;
  	      if (!(templateName in Jaml.templates)) continue;
  	      filteredCheckins.push(checkins[i]);
  	    }
  	    
  	    var left = [], right = [];  	    
  	    for (var i = 0, len = filteredCheckins.length; i < len; i++) {
  	      (i % 2 === 0 ? left : right).push(filteredCheckins[i]);
  	    }
  	    
  	    var $wrapper = $('#activity-feed-wrapper');
  	    
  	    // Store the checkins for the hell of it.
  	    $wrapper.data('checkins', checkins);
  	    
  	    // Hide the contents while we mess with stuff.
  	    $wrapper.css('visibility', 'hidden');

  	    // Inject the HTML.
  	    var content = Jaml.render('activity_feed_template', 
  	     { left: left, right: right });  	     
        $wrapper.html(content);
  	    
  	    YourSpark._activityStreamWrapper = $wrapper;
  	    
  	    $wrapper.css('visibility', 'visible');

  	    YourSpark._animationInterval = window.setInterval(
  	     function() { YourSpark._animateStreamColumn() }, 3000);
  	  }
	  });
	},
	
	// Alternate animation between left and right columns.
	_animateStreamColumn: function() {
	  var $wrapper = YourSpark._activityStreamWrapper;
	  if (!YourSpark._lastAnimatedColumn) {
	    YourSpark._lastAnimatedColumn = 'right';
	  }
	  
	  var columnToAnimate = YourSpark._lastAnimatedColumn === 'left' ?
	   'right' : 'left';
	   
	  var $column = $wrapper.find('div.' + columnToAnimate);
	  
	  // Animate by the height of one item.
	  var itemHeight = $column.find('div:first').outerHeight();
	  
	  // Figure out offset.
	  var oldValue = window.parseInt($column.css('bottom'), 10);
	  var newValue = oldValue - itemHeight;
	  
	  // If offset plus height of wrapper is greater than height of column,
	  // stop, or else we'll scroll off into nothing.
	  var wrapperHeight = $wrapper.height(),
	   columnHeight = $column.height();
	  if (Math.abs(oldValue) + wrapperHeight >= columnHeight) {
	    window.clearInterval(YourSpark._animationInterval);
	    return;
	  }

    // Animate the column.
    $column.animate({ bottom: newValue + 'px' }, 600);
    
    YourSpark._lastAnimatedColumn = columnToAnimate;
	},
	
	updateActivityStream: function() {
		YourSpark.activityStreamEndPos = YourSpark.activityStreamEndPos - 2;
		YourSpark.activityStreamStartPos = YourSpark.activityStreamEndPos - 13;
		if(YourSpark.activityStreamStartPos < 0) {
			YourSpark.activityStream();
			return;
		}
		$('#activity-feed-wrapper').html(tmpl('activity_feed_tmpl', {checkins:YourSpark.activityStreamCheckins.slice(YourSpark.activityStreamStartPos, YourSpark.activityStreamEndPos)}));
		$('#activity .item:gt(2)').show();
		$('#activity .item:lt(2)').fadeIn();
		setTimeout(function(){ YourSpark.updateActivityStream() }, 3000);
	},
	
	foundersToList: function(founders) {
		return jQuery.map(founders, function(f) {
			return "<a href=\"" + f.url + "\">" + h(f.name) + "</a>";
		}).join(", ");
	},

	fetchItem: function(url) {
		$.getJSON(url, function(item) {
			$.getJSON(item.events_url, function(data) {
				$('#item-events-wrapper').html(tmpl('item_events_tmpl', data));
			});
		});
	},
	

	timeAgoInWords: function(fromTime, toTime) {
		toTime = YourSpark.parseISO8601(toTime);
		var fromSeconds = fromTime.getTime();
		var toSeconds = toTime.getTime();
		var distanceInSeconds = Math.round(Math.abs(fromSeconds - toSeconds) / 1000)
		var distanceInMinutes = Math.round(distanceInSeconds / 60)
		if (distanceInMinutes <= 1)
			return 'a minute ago'
		if (distanceInMinutes < 45)
		  return distanceInMinutes + ' minutes ago'
		if (distanceInMinutes < 90)
		  return "an hour ago"
		if (distanceInMinutes < 1440)
		  return "" + (Math.round(distanceInMinutes / 60)) + ' hours ago'
		if (distanceInMinutes < 2880)
		  return "yesterday"
		if (distanceInMinutes < 43200)
		  return (Math.round(distanceInMinutes / 1440)) + ' days ago'
		if (distanceInMinutes < 86400)
		  return "a month ago"
		if (distanceInMinutes < 525600)
		  return (Math.round(distanceInMinutes / 43200)) + ' months ago'
		if (distanceInMinutes < 1051200)
		  return "a year ago"
		return "over " + (Math.round(distanceInMinutes / 525600)) + ' years ago'
	},

	parseISO8601: function(date) {
		if(typeof(date) == 'string') {  // parse iso8601 format
			var parts = date.split('T'),
			dateParts = parts[0].split('-'),
			timeParts = parts[1].split('+'),
			timeSubParts = timeParts[0].split(':'),
			timeSecParts = timeSubParts[2].split('.'),
			timeHours = Number(timeSubParts[0]),
			_date = new Date;

			_date.setUTCFullYear(Number(dateParts[0]));
			_date.setUTCMonth(Number(dateParts[1])-1);
			_date.setUTCDate(Number(dateParts[2]));
			_date.setUTCHours(Number(timeHours));
			_date.setUTCMinutes(Number(timeSubParts[1]));
			_date.setUTCSeconds(Number(timeSecParts[0]));
			if (timeSecParts[1]) _date.setUTCMilliseconds(Number(timeSecParts[1]));

			return _date;
		} else {
			return date;
		}
	}

}

var Paginator = {
	
	currentPage: 1,
	perPage: 20,
	params: {},
	
	paginate: function(url, options) {
		setTimeout(function() {
			$('.spinner').show();
			Paginator.params = options;
			options["page_count"] = '1';
			$.getJSON(url, options, function(data) {
				var pages = data.page_count;
				var html = '';
		    if(parseInt(pages) > 1) {
					html += '<div class="paging" class="clearfix"><img src="images/spinner.gif" width="25" height="25" class="spinner" />';
					if(Paginator.currentPage > 1) {
						html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+(Paginator.currentPage-1)+'); return false;" class="prev">Prev</a>';
					} else {
						html += '<a href="#" onclick="return false;" class="prev inactive">Prev</a>';
					}
					if(Paginator.currentPage > 4) html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', 1); return false;">1</a><a href="#" onclick="return false;" class="dots">...</a>';
					if(Paginator.currentPage > 4) {
						if(pages == Paginator.currentPage) html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+(Paginator.currentPage-2)+'); return false;">'+(Paginator.currentPage-2)+'</a>';
						html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+(Paginator.currentPage-1)+'); return false;">'+(Paginator.currentPage-1)+'</a>';
						html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+Paginator.currentPage+'); return false;" class="selected">'+Paginator.currentPage+'</a>';
						if(pages >= (Paginator.currentPage+1)) html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+(Paginator.currentPage+1)+'); return false;">'+(Paginator.currentPage+1)+'</a>';
					} else {
						for(i=0; i < 4; i++) {
							var pnum = i+1;
							var selected = (Paginator.currentPage == pnum) ? ' class="selected"' : '';
							if(pages >= pnum) html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+pnum+'); return false;"'+selected+'>'+pnum+'</a>';
						}
					}
					if(pages > 4 && pages >= (Paginator.currentPage+1)) html += '<a href="#" onclick="return false;" class="dots">...</a>';
					if(Paginator.currentPage < pages) {
						html += '<a href="#" onclick="Paginator.loadPage(\''+url+'\', '+(Paginator.currentPage+1)+'); return false;" class="next">Next</a>';
					} else {
						html += '<a href="#" onclick="return false;" class="next inactive">Next</a>';
					}
					html += '</div>';
				} else {
					html += '<div class="paging" class="clearfix"><img src="images/spinner.gif" width="25" height="25" class="spinner" /></div>';
				}

				$('#pagination_links_bottom').html(html);
				$('.paging').show();
				$('.spinner').hide();
				window.scrollTo(0, 0);
			});
		}, 500);
	},
	
	loadPage: function(url, n) {
		$('#map-placeholder').show();
		var options = Paginator.params;
		options["offset"] = (n-1)*Paginator.perPage;
		this.currentPage = n;
		this.paginate(url, options);
		options["page_count"] = '';
		$.getJSON(url, options, Paginator.handler);
	}

}

window.onunload = YourSpark.unload;

// TODO: merge these into YourSpark
function checkLogin(input, action) {
	if (action == "emailFocus") {
		if (input.value.indexOf("@") == 1) {
		} else {
			input.value = '';
			input.selectAll();
		}
	}
	if (action == "emailBlur") {
		if (input.value == '' || input.value.indexOf("@") == -1) {
			input.value = "Email Address";
		}
	}
	if (action == "passwordFocus") {
		if (input.value == 'password') {
			input.value = "";
		}
	}
	if (action == "passwordBlur") {
		if (input.value == '') {
			input.value = "password";
		}
	}
}


function twitterCallback2(twitters) {
	var statusHTML = [];
	for (var i=0; i<twitters.length; i++) {
		if(!twitters[i].in_reply_to_user_id) {
			var username = twitters[i].user.screen_name;
			var status = twitters[i].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
				return '<a href="'+url+'">'+url+'</a>';
			}).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
				return reply.charAt(0)+'<a href="http://twitter.com/'+reply.substring(1)+'">'+reply.substring(1)+'</a>';
			});
			statusHTML.push('<li><span>'+status+'</span> <a style="font-size:85%" href="http://twitter.com/'+username+'/statuses/'+twitters[i].id+'">'+relative_time(twitters[i].created_at)+'</a></li>');
		}
	}
	document.getElementById('twitter_update_list').innerHTML = statusHTML.join('');
}

function relative_time(time_value) {
	var values = time_value.split(" ");
	time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
	var parsed_date = Date.parse(time_value);
	var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
	var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
	delta = delta + (relative_to.getTimezoneOffset() * 60);

	if (delta < 60) {
	  return 'less than a minute ago';
	} else if(delta < 120) {
	  return 'about a minute ago';
	} else if(delta < (60*60)) {
	  return (parseInt(delta / 60)).toString() + ' minutes ago';
	} else if(delta < (120*60)) {
	  return 'about an hour ago';
	} else if(delta < (24*60*60)) {
	  return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
	} else if(delta < (48*60*60)) {
	  return '1 day ago';
	} else {
	  return (parseInt(delta / 86400)).toString() + ' days ago';
	}
}

jQuery.fn.extend({
	check: function() {
  	return this.each(function() { this.checked = true; });
	},
	uncheck: function() {
  	return this.each(function() { this.checked = false; });
	},
	toggleSelected: function() {
  	return this.each(function() { this.checked ? this.checked = false : this.checked = true; });
	}
});

function h(str) {
	if(!str) return;
	return str.replace(/\&/gi, '&amp;').replace(/\"/gi, "\&quot;").replace(/\>/gi, "&gt;").replace(/\</gi, "&lt;");
}


Jaml.registerHelper('h', function(str) {
	if (!str) return;
	return str.replace(/\&/gi, '&amp;').replace(/\"/gi, "\&quot;").replace(/\>/gi, "&gt;").replace(/\</gi, "&lt;");
});

Jaml.register('activity_feed_template', function(data) {
  with(this) {
    div({ cls: 'left' },
      Jaml.render('activity_feed_item', data.left)
    );
    div({ cls: 'right' },
      Jaml.render('activity_feed_item', data.right)
    );
  }
});

Jaml.register('activity_feed_item', function(item, i) {
  this.div({ cls: 'item' },
    Jaml.render('activity_feed_item_' + item.type, item)
  );
});

Jaml.register('activity_feed_item_visit', function(data, i) {
  with(this) {
    a({ href: data.user.url },
      img({ src: data.user.image_url, width: '50', height: '50', cls: 'avatar' })
    );
    p(
      a({ href: data.user.url }, data.user.name),
      " checked in at ",
      a({ href: data.spot.url }, data.spot.name)
    );
    img({ src: data.spot.image_url, width: '50', height: '50', cls: 'icon' });
  }
});

Jaml.register('activity_feed_item_photo', function(data, i) {
  with(this) {
    a({ href: data.user.url },
      img({ src: data.user.image_url, width: '50', height: '50',
       cls: 'avatar' })
    );
    p(
      a({ href: data.user.url }, data.user.name),
      " took a photo at ",
      a({ href: data.spot.url }, data.spot.name)      
    );
    a({ href: data.checkin.url },
      img({ src: data.photo.urls.square_50, width: '50', height: '50',
       cls: 'icon' })
    );
  }
});


Jaml.register('activity_feed_item_pick_up', function(data, i) {
  with(this) {
    a({ href: data.user.url },
      img({ src: data.user.image_url, width: '50', height: '50', cls: 'avatar' })
    );
    p(
      a({ href: data.user.url }, data.user.name),
      " picked up ",
      data.item.determiner,
      " ",
      a({ href: data.item.url }, data.item.name),
      " at ",
      a({ href: data.spot.url }, data.spot.name)
    );
    img({ src: data.item.image_url, width: '50', height: '50', cls: 'icon' });
  }
});

Jaml.register('flash_dialog_template', function(data) {
  with(this) {
    div({ cls: 'flash ' + status },
      p({ cls: 'title' }, title),
      p({ cls: 'body'  }, body ),
      a({ href: '#', cls: 'close' })
    )
  }
});



Jaml.register('friends_waiting_partial', function(data) {
  with(this) {
    li(
      a({ href: data.url }, h(data.name)),
      ' - ',
      a({ href: '#', cls: 'reject' }, "Forget it")
    );
  }
});

Jaml.register('friends_needing_approval_partial', function(data) {
  with(this) {
    li(
      a({ href: data.url }, h(data.name)),
      ' - ',
      a({ href: '#', cls: 'accept' }, "Accept"),
      " or ",
      a({ href: '#', cls: 'reject' }, "Ignore")
    );
  }
});

Jaml.register('friends_partial', function(data) {
  with(this) {
    li(
      a({ href: data.url }, h(data.name)),
      ' - ',
      a({ href: '#', cls: 'reject' }, "Remove")
    );
  }
});

	
	
Jaml.register('twitter_friends_partial', function(data) {
  with(this) {
    li(
      a({ href: data.url }, h(data.name)),
      ' - ',
      a({ href: '#', cls: 'request' }, "Add")
    );
  }
});

// Class.
// Takes the same arguments as `JAML.render`: a template name and an 
// object of data with which to populate the template.
// 
// Binds the "close" button automatically.
//
YourSpark.Overlay = function(template, data) {
  this.html = Jaml.render(template, data);  
  this.element = $( $(this.html) );
  this.element.hide().appendTo(document.body);
  
  var that = this;
  this.element.delegate('.overlay-close', 'click', function() {
    that.hide();
  });
};

$.extend(YourSpark.Overlay.prototype, {
  _adjust: function() {
    var dde = document.documentElement,
     db = document.body;
     
    var scrollTop = dde.scrollTop || db.scrollTop;
    var scrollHeight = dde.scrollHeight;
    var clientHeight = window.innerHeight || dde.clientHeight;
    
    var top = scrollTop + (clientHeight * 0.09);
    
    this.element.find('.overlay-screen').css('height', scrollHeight);
    this.element.find('.overlay-content').css('top', top);
  },
  
  // Shows the overlay.
  show: function() {
    this._adjust();
    this.element.show();
  },
  
  // Hides the overlay.
  hide: function() {
    this.element.hide();
  },
  
  // Removes the overlay from the page. If you want to hide an overlay
  // with the possibility of showing it again, use `hide` instead.
  destroy: function() {
    this.hide();
    this.element.undelegate();
    this.element.remove();
  },
  
  // Proxy for `$.fn.delegate` on the overlay element.
  delegate: function(selector, type, handler) {
    return this.element.delegate(selector, type, handler);
  },
  
  // Proxy for `$.fn.undelegate` on the overlay element.
  undelegate: function(selector, type, handler) {
    return this.element.undelegate(selector, type, handler);
  }
});


YourSpark.Friendship = {
  // The URLs to POST to for various friend-related actions.
  // These are assigned in the view so we can make use of the
  // URL-related view helpers.
  URLS: {},  
  
  // Called on domready.
  setup: function() {
    this.$pending = $('#pending_friend_requests');
    this.$current = $('#friendships');
    
    function _wrapper(__handlerName) {
      return function(event) {
        event.preventDefault();
        YourSpark.Friendship[__handlerName](event, this);
        return false;
      }
    }
    
    this.$pending.delegate('a.approve', 'click', _wrapper('accept'));
    this.$pending.delegate('a.decline', 'click', _wrapper('reject'));

    // Handlers for showing/hiding tooltips.
    this.$current.delegate('a.tooltip-trigger', 'hover', function(event) {
      var tooltip = $(this).prev();
      if (event.type === 'mouseenter' || event.type === 'mouseover') tooltip.show();
      else tooltip.hide();
    });
    
    this.$current.delegate('.notifications', 'click',
     _wrapper('toggleNotification'));
    this.$current.delegate('.remove', 'click', 
     _wrapper('confirmRemoval'));
     
    this._assignClasses(this.$current);
    this._assignClasses(this.$pending);
  },
  
  // Returns how many friends the current user has.
  getCount: function() {
    return Number(this.$current.attr('data-count'));
  },
  
  // Sets number of current friends and updates views accordingly. Pass -1
  // to decrement current value.
  setCount: function(value) {
    if (value === -1) {
      // Decrement.
      value = this.getCount() - 1;
    }    
    if (value < 0) value = 0;

    this.$current.attr('data-count', value);

    var headingHTML;
    if (value > 0) {
      headingHTML = "<b>" + value + "</b> " +
       (value === 1 ? "friend" : "friends");
    } else {
      headingHTML = "Looks like you haven't added any friends.";
      this.$current.slideUp();
    }
    
    this.$current.prev('h2').html(headingHTML);
  },
  
  request: function(url, obj) {
    if (obj) {
      obj = $(obj);
      if (obj.parent().hasClass('disabled')) return false;
      obj.parent().addClass('disabled');
      obj.html("Request Sent");
    }
    
    $.post(url);
  },
  
  // Accepts the given friend request.
  accept: function(event, element) {
    var url = $(element).attr('href');
    $.post(url);

    this.setRequestCount(-1);
    this._dismissRequest(element);
  },
  
  // Rejects the given friend request.
  reject: function(event, element) {
    var url = $(element).attr('href');
    
    $.post(url);
    
    this.setRequestCount(-1);  
    this._dismissRequest(element);
  },
  
  // Removes the friend with the given ID.
  remove: function(id) {
    $.post(this.URLS.REJECT_FRIENDSHIP, { user_id: id });
    var row = $('#friend_' + id), that = this;
    row.slideUp(function() {
      row.remove();
      that._assignClasses(that.$current);
    });
    this.setCount(-1);
  },
  
  // Shows the "Sure you want to remove this friend?" overlay.
  confirmRemoval: function(event, element) {
    element = $(element);
    
    var name = element.attr('data-friend_name'),
     id = element.attr('data-friend_id');
     
    var overlay = new YourSpark.Overlay(
     'overlay_remove_friend', { name: name, id: id});
     
    overlay.delegate('.action-remove', 'click', function() {
      var id = Number($(this).attr('data-friend_id'));
      YourSpark.Friendship.remove(id);
      overlay.destroy();
      return false;
    });
    
    overlay.delegate('.action-cancel', 'click', function() {
      overlay.destroy();
      return false;
    });
     
    overlay.show();    
  },
  
  // Switches notifications for a given friend from "on"
  // to "off" (or vice-versa).
  toggleNotification: function(event, element) {
    element = $(element);
    var on = element.hasClass('on');
    var id = Number(element.attr('data-friend_id'));
    
    var push = element.closest('.push'),
     tooltip = push.find('.tooltip b');
     
    tooltip.removeClass(on ? "on" : "off").addClass(
     on ? "off" : "on").html(on ? "Off" : "On");
    element.toggleClass('on', !on);

    var params = { user_id: id, on: (on ? '0' : '1') };
    // console.log("post to " + YourSpark.Friendship.URLS.SET_NOTIFICATIONS
    //  + " with params:", params);     
    $.post(this.URLS.SET_NOTIFICATIONS, params);
  },
  
  // Returns number of pending friendship requests.
  getRequestCount: function() {
    return Number(this.$pending.attr('data-count'));
  },

  // Sets number of pending friendship requests and updates views
  // accordingly. Pass -1 to decrement current value.
  setRequestCount: function(value) {
    if (value === -1) {
      // Decrement.
      value = this.getRequestCount() - 1;
    }    
    if (value < 0) value = 0;

    this.$pending.attr('data-count', value);
    var $badge = $('#friend_requests_badge');
    
    if (value === 0) {
      // hide everything
      $badge.hide();
      this.$pending.slideUp();
      return;
    }

    var headingHTML = "<b>" + value + "</b> friend " + (value === 1 ? "request" : "requests");    
    $('#pending_friend_count').html(headingHTML);
    $badge.html(value);    
  },
  
  _dismissRequest: function(element) {
    var row = $(element).closest('div.friend-request');
    var that = this;
    row.slideUp(function() {
      row.remove();
      that._assignClasses(that.$pending);
    });
  },
  
  _assignClasses: function($element) {
    $element.find('div.feed').removeClass('last');
    $element.find('div.feed:last').addClass('last');
  }
};

Jaml.register('overlay_remove_friend', function(data) {
  with(this) {
    div({ cls: 'overlay', id: 'overlay_remove_friend', 
     style: 'display:none' },
      div({ cls: 'overlay-content' },
        div({ cls: 'overlay-inner' },
          div({ id: 'add-profile-photo' },
            h1(
              span("Remove ", data.name, "?")
            ),
            a({ href: '#', cls: 'overlay-close', title: 'Close' }, ""),
            div({ cls: 'overlay-group' },
              p("Are you sure you want to remove ",
                data.name,
                " from your friends list?"
              )
            ),
            div({ cls: 'overlay-action' },
              a({ href: '#', cls: 'button action-remove',
               'data-friend_id': data.id }, "Remove"),
              a({ href: '#', cls: 'button action-cancel',
               style: 'background: #b0cb79; text-shadow: #455626 1px 1px 1px; margin-right: 10px' },
                "Cancel"
              )
            )
          )
        )
      ),
      div({ cls: 'overlay-screen' })
    )
  }
});
