// JJFB class
// TODO: documentate this stuff

log = function(msg) {
	try { console.log(msg); } catch(err) {}
};

// when the document's ready, hide all the facebook divs.
$().ready(function() {
	JJFB.toggleFacebookDivs(false);
});

JJFB = {
	// put your facebook permissions in this scope hash
	// see http://developers.facebook.com/docs/authentication/permissions
	// "By default, your application can access all public data in a user's profile, 
	// including her name, profile picture, gender, and friend list."
	perms : {
		// User's name, profile picture, list of friends, photos and Friend's birthdays and photos
		default_required : 'friends_birthday, user_photos, friends_photos, user_birthday, email',
		upload_photo     : 'user_photos, publish_stream',
		default_optional : '',
		birthday         : 'user_birthday, friends_birthday',
		birthday_alerts  : 'user_birthday, email, friends_birthday',
		basic            : ''
	},
	
	// keep track of the user's status in case a status change gets fired 2x (i.e. on login)
	current_status : "",
	current_perms : null,
	
	get_login_status_complete : false,
	
	// store the first name & thumbnail URL / embed HTML for the user
	user_info: "",
	user_thumb_url: "",
	user_thumb_secure_url: "",

	// fill with upcoming birthdays for maximum excitement
	upcoming_birthdays : new Array(),

	// and keep track of whether we've loaded all the users (so we don't keep doing that)
	all_users_loaded : false,

	// all friends
	all_friends : new Array(),
	all_friends_loaded : false,

	// cache whether the user has birthday permissions
	birthday_permissions : null,
	
	// flag to differentiate the first call to refreshUserStatus to determine whether we
	// need to refresh the whole page due to the user not being who we expect when you
	// first hit the page or whether the user's status changed via an in-page event,
	// i.e. a login from the client, where we don't necessarily want a full page reload.
	first_refresh : true,
	
	// is the user logged in via facebook?
	facebook_logged_in : function() {
		return FB.getAuthResponse() != null;
	},

	// user logged in via jibjab?
	// TODO: fix the cookie
	jibjab_logged_in : function() {
		return $.cookie('user_logged_in') == 'true';
	},

	// this is fired on every page load and informs us what our facebook login status is
	refreshUserStatus: function(response) {
		// swap some divs around, populate some content depending on the user's state

		log("refreshUserStatus");
		log(response);
		log('current_facebook_user = '+ current_facebook_user);

		if ((!response.authResponse) && (current_facebook_user != null)) {
			// server thinks we're logged in, but we ain't. reload!
			log("status changed!");
            JJFB.delete_cookies();
			location.reload();
		}
		
		// if status hasn't changed, false alarm
		if (response.status == JJFB.current_status) { return; }

		if (!response.authResponse) {
			// if you haven't FB-connected w/ us yet, render the FBML
			// so the facepile shows up.
			FB.XFBML.parse();
		}

		JJFB.current_status = response.status;

		// there should only be a authResponse if you're a FB-connected user.
		if (response.authResponse) {
			if ((current_facebook_user != null) && (response.authResponse.userID != current_facebook_user)) {
				// logged in user is different from what the server thinks
				log("user changed!");
				OmnitureHelper.log_event('login', 'Facebook');
				location.reload();				
			}

			// if we just got here and we're already signed in via facebook elsewhere, reload
			if ((JJFB.first_refresh == true) && (current_facebook_user == null) && (response.authResponse.userID != current_facebook_user)) {
				log("wait a sec, I know you.");
				OmnitureHelper.log_event('login', 'Facebook');
				location.reload();
			}
			
			JJFB.current_perms = response.scope;

			// store the user's thumbnail URL
			JJFB.user_thumb_url = "http://graph.facebook.com/" + response.authResponse.userID + "/picture";
			JJFB.user_thumb_secure_url = "https://graph.facebook.com/" + response.authResponse.userID + "/picture";

			// word. we are logged in via facebook. get our info from facebook.
			FB.api("/me", function(r) { 
				JJFB.user_info = r;
				$('body').trigger('JJFB.userInfoRetrieved');
			});
			
			// show any facebook-dependent divs on the page
			JJFB.toggleFacebookDivs(true);
			// render any XFBML on the page
			FB.XFBML.parse();
		} else {
			log("no authResponse.");
			JJFB.toggleFacebookDivs(false);
		}
	},

	toggleFacebookDivs: function(show) {
		log('toggleFacebookDivs called');
		if (show) {
			$(".need_to_connect").hide();
			$(".fb_connected").show();
		} else {
			$(".fb_connected").hide();
			$(".need_to_connect").show();
		}
	},

	// apiKey needs to be a function in order to be called from Flash
	apiKey: function() { 
		return JJFB._apiKey; 
	},

	init : function(fbConfig, callback) {
		// 1. if logged in to facebook?
		// 2. if logged in to jibjab?
		// 3. 
		JJFB.applicationId = fbConfig.applicationId;
		JJFB._apiKey = fbConfig.apiKey;
		log('JJFB.init('+fbConfig+')');
		window.fbAsyncInit = function() {
			FB.init({
			  appId  : JJFB.applicationId,
			  status : true, // check login status
			  cookie : true,
			  xfbml  : false,  // parse XFBML
			  oauth  : true
			});

			FB.Event.subscribe('auth.statusChange', JJFB.refreshUserStatus);
		
		  if(!(_gaq === undefined)) {
        FB.Event.subscribe('edge.create', function(targetUrl) {
          _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]);
        });

        FB.Event.subscribe('edge.remove', function(targetUrl) {
          _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]);
        });

        FB.Event.subscribe('message.send', function(targetUrl) {
          _gaq.push(['_trackSocial', 'facebook', 'send', targetUrl]);
        });			
      }

			// trigger an event that you can bind to to know when facebook is ready.
			FB.getLoginStatus(function(response) { 
				// extend the facebook methods to include the permissions request when the FB object is available
				extend_fb_methods();
				
				JJFB.refreshUserStatus(response);
				JJFB.first_refresh = false;
				log('jjfb.ready.');
				$('body').trigger('JJFB.ready', response);
				
				JJFB.get_login_status_complete = true;
			});
			
			if (!(callback === undefined)) {
				callback();
			}

			// if for some reason (*AHEM* firefox) getLoginStatus isn't complete, force it again in 2 seconds.
			setTimeout(function() {
				if (!JJFB.get_login_status_complete) {
					FB.getLoginStatus(function(r) { JJFB.get_login_status_complete = true; }, true);
				}
			} , 2000);
		};
		
		(function() {
		  var e = document.createElement('script');
		  // when we are testing this for a local test shell for flash dev.
		  if(document.location.protocol == "file:") {
        protocol = "http:";
		  } else {
        protocol = document.location.protocol;
		  }
      e.src = protocol + '//connect.facebook.net/en_US/all.js';		    
      // e.src = "http://10.10.10.220/~francisco/connect-js/pkg.php";
		  e.async = true;
		  document.getElementById('fb-root').appendChild(e);
		}());

	},

	// ask for additional scope while the user's logged in
	// after_callback only called on success- feel free to split that into success/failure callbacks
	get_additional_perms : function(additional_perms, after_callback) {
		log("get_additional_perms: " + additional_perms);

    with_fb_login = function() {
      FB.login(function(add_perm_response) {
        log('get_additional_perms');
        JJFB.grantedGraphPermissions(function(granted_add_perms) {
            log(granted_add_perms);

            if (JJFB.gotAllPerms(additional_perms, granted_add_perms)) {
                if (!(after_callback === undefined)) {
                    after_callback();
                }
            }
        });
      }, {scope:additional_perms});
    };

    if (JJFB.facebook_logged_in()) {
      log('get_additional_perms JJFB.facebook_logged_in');
      JJFB.grantedGraphPermissions(function(initial_granted_perms) {
        if (JJFB.gotAllPerms(additional_perms, initial_granted_perms)) {
          if (!(after_callback === undefined)) {
            after_callback();
          }
          return;
        } else {
          with_fb_login();
        }
      });
    } else {
      with_fb_login();
    }
	},

	// Wrapping FB.api calls with login dialog plus scope because on
  // firefox trying to perform actions without proper scope doesnt fire
  // the callbacks so we dont know if it succeedded or failed
	// causes an annoying dialog pop up when scope are already acquired
	// NOTE: optional_scope IS NOT USED ANYMORE
	login_with_perms : function(tentative_required_scope, optional_scope, after_callback, login_mode) {
        log('login_with_perms');
        var required_scope = tentative_required_scope;
        FB.login(function(response) {
            log(response);

            if (response.authResponse) {
                log("login_with_perms() callback: REQUIRED PERMISSIONS:");
                log(required_scope);
                log("login_with_perms() callback: RESPONSE PERMISSIONS:");
                JJFB.grantedGraphPermissions(function(granted_perms) {
                    log(granted_perms);
                    JJFB.on_successful_login(response, granted_perms, required_scope, optional_scope, after_callback, login_mode);
	        });
            } else {
                // user is not logged in
                log('no perms');
                // $(".facebook_connected").hide();
                // $(".facebook_not_connected").show();
            }
        }, {scope:required_scope});
        // handle optional scope
	},

	on_successful_login : function(response, granted_perms, required_scope, optional_scope, after_callback, login_mode) {
    OmnitureHelper.log_event('login', 'Facebook');

    // If our required permissions have been met (including no permissions)...
    if (JJFB.gotAllPerms(required_scope, granted_perms)) {
      var x = new Date();
      
      // Post to JJ, see if we're known
      var post_params = { 
        user_timezone: Math.floor(x.getTimezoneOffset() / -60),
        perms: granted_perms.join(","),
        status: response.status,
        access_token: response.authResponse.accessToken,
        access_token_expires_in: response.authResponse.expiresIn
      };
 

      $.post('/user/facebook_login', post_params, function(fb_login_response, _status) {
        log("RESPONSE from facebook_login:");
        log(fb_login_response);

        var shouldShowMlbEmailOptInModal = (SiteConfig.channel_name == 'mlb' && 
          (SiteConfig.controller == 'view' || SiteConfig.controller == 'sendables') &&
          fb_login_response.became_new_facebook_user && login_mode != 'basic');
        
        var shouldPromptBirthdayAlert = (SiteConfig.channel_name != 'mlb' &&
          SiteConfig.channel_name != 'holidays' &&
          !fb_login_response.facebook_user &&
          !fb_login_response.jibjab_user &&
          login_mode != 'basic');

        if (shouldShowMlbEmailOptInModal) {
          ModalHelper.showMlbEmailOptInModal(function(){
              if (!(after_callback === undefined)) {
                  after_callback(response);
              }
          });
        } else if (shouldPromptBirthdayAlert && !fb_login_response.proxied_email) {
            ModalHelper.showBirthdayOptInModal();
        } else {
            // Call the after_callback once we're done posting to jibjab
            if (!(after_callback === undefined)) {
                after_callback(response);
            }
        };
      }, 'json'); // END post '/user/facebook_login'
    } else {
      // our required scope weren't met
      log('no scope');
    }
	},

	logout: function(url) {
		// on logout clear the birthday cookie
		$.cookie('birthday_data', null, { path: '/', expires: 10 });
		
		if(JJFB.facebook_logged_in()) {
            FB.logout(function() {
                JJFB.delete_cookies();

                if (url) {
                    location.href = url;
                }
            });
            return;
		}

		if (url) {
			location.href = url;
		}
	},

    delete_cookies: function() {
        // make extra sure that darn cookie is gone
        subdomains = document.domain.split('.');
        for(i=0; i <= subdomains.length-2; i++) {
            arr = subdomains.slice(i, subdomains.length);
            thisdomain = arr.join('.');
            log('delete_cookies: ' + thisdomain);
            $.cookie("fbsr_" + JJFB.applicationId, null, {domain: thisdomain});
            $.cookie("fbsr_" + JJFB.applicationId, null, {domain: thisdomain, path: '/'});
        }
    },

	// does an AJAX request to the server to get an updated header.
	// call me after user state changes.
	updateHeader: function(template_id, channel_name, category_id, pagename, header) {
	  log("Updating header..")
	  log(arguments)
		if (header === undefined) { header = ''; }
		
		data = {
		  template_id:template_id,
		  jjchannel:channel_name,
		  category_id:category_id,
		  pagename:pagename,
		  logout_from:location.pathname+location.search,
		  header:header
	  }
    
		$.get('/user/user_links', data, function(resp, textStatus, jqXHR) {
			$("#my_nav").replaceWith(resp);
		}, 'html')
	},

			
	gotAllPerms: function(optional_scope, scope) {
		optionals = optional_scope.split(",");

		if (scope === undefined) { return false; }

		if (scope == null) { scope = ""; } // if facebook gives us null back.

		if (typeof scope == "string") {
			scope_array = scope.split(",");
		} else {
			scope_array = scope;
		}

		for(i=0;i<scope_array.length;i++) { scope_array[i] = $.trim(scope_array[i]); }
		
		gotAllPerms = true;
		
		// if we've got any optional scope at all
		if (optionals.length > 0) {
			// make sure they're all satisfied.
			for (i = 0; i < optionals.length; i++) {
				if ($.trim(optionals[i]) == "") {
					// ignore empty requirements... do nothing
				} else if (($.inArray($.trim(optionals[i]), scope_array)) == -1) {
					gotAllPerms = false;
					break;
				}
			}
		}
		
		log("gotAllPerms = " + gotAllPerms);
		return gotAllPerms;
	},

	gotoUrl: function(url) {
		return function(response) {
			if(url) {
				location.href = url;
			}
		};
	},
	
	reloadPage: function(response) {
		// ignore the response, just making this signature match the callback called after login
		location.reload();
	},

	// do work with a callback
	get_friends: function(done_callback, sort_callback) {
		if (!JJFB.facebook_logged_in()) { log("not logged in. no friends."); return; }

		if (JJFB.all_friends_loaded) {
			done_callback(JJFB.all_friends);
			return;
		}

		var result = null;
		
		FB.api("/me/friends", function(response) {
			// got a big ol' payload of friends... hopefully.
			if (response.data && response.data.length > 0) {
				if (sort_callback) {
					result = sort_callback(response.data);
				} else {
					result = response.data;
				}
			}
			JJFB.all_friends = result;
			JJFB.all_friends_loaded = true;

			done_callback(result);
		});
	},

	show_friends_photos: function(limit, placeholder, container, item_callback) {
		if (!placeholder || !container || !placeholder.html()) {
		//if (!placeholder || !container || !placeholder.html() || !container.html()) {
			log("show_friends_photos called with invalid placeholder or container");
			return;
		}

		render_callback = function(result) {
			// don't select the same ones each time
			selected = new Array();
			if (result) {
				for (var i=0; i < result.length && i < limit; i++) {
					while (true) {
						rand = Math.floor(Math.random() * result.length);
						// make sure it doesn't currently exist
						var idx = 0;
						for (; idx < selected.length; idx++) {
							if (selected[idx] == rand) {
								break;
							}
						}
						if (idx >= selected.length) {
							// doesn't exist, add it
							selected.push(rand);
							break;
						}
					}
				}

				// display it
				content = placeholder.html();

				for (var i=0; i < selected.length; i++) {
					selected_idx = selected[i];
					user_id = (result[selected_idx].uid) ? result[selected_idx].uid : result[selected_idx].id;
					thumb_url = "http://graph.facebook.com/" + user_id + "/picture";
					div_content = content.replace(/rel=["']#img["']/,"src='" + thumb_url + "'");
					div_content = div_content.replace('#name', result[selected_idx].name);
					div_content = div_content.replace('#uid', user_id);
					if (item_callback) {
						container.append(item_callback(div_content, i, selected.length));
					} else {
						container.append(div_content);
					}
					//container.append(div_content);
				}
			}
		};

		JJFB.get_friends(render_callback, null);
	},

	// get all the user's friends via an opengraph request
	get_all_friends: function(after_callback, sort_order) {
		if (!JJFB.facebook_logged_in()) { log("not logged in. no friends."); return; }
		
		FB.api("/me/friends?fields=birthday,name", function(response) {
log(response);
			// got a big ol' payload of friends... hopefully.
			if (response.data && response.data.length > 0) {

				if (sort_order === undefined) { sort_order = "name"; }

				users = JJFB.users_sort(response.data, sort_order);
				
				JJFB.upcoming_birthdays = users;
				JJFB.all_users_loaded = true;
			}
		
			if($.isFunction(after_callback)){		
			  after_callback();
			}
		});
	},
	
	show_all_friends: function(placeholder, container, show_all_div) {
		// https://graph.facebook.com/bennett.kolasinski/friends?access_token=
		// hide the 'show all' div
		if (show_all_div) { show_all_div.hide(); }
		
		JJFB.show_throbber(container);
		JJFB.get_all_friends(function() {
			log('got friends. showing now.');
			content = placeholder.html();

			container.html('<div id="' + placeholder[0].id + '" style="display:none;">'+content+'</div>');
			container.animate({height: '500px'}, 1500);
			container.css({'height' : '500px'});

			JJFB.render_users(users, placeholder, container);			
		});
	},
	
	// BIRTHDAY FUNCTIONS
	// should they be split into another file / namespace?

	// ask facebook if we've got friends_birthday permissions.
	has_birthday_permissions : function(callback) {
		if (!JJFB.facebook_logged_in()) { callback(false); return; }

		if (JJFB.birthday_permissions != null) { callback(JJFB.birthday_permissions); return; }
		
		// get facebook permissions 
		$.getJSON('https://graph.facebook.com/me/permissions?access_token='+response.authResponse.accessToken+'&callback=?', function(response){
		  var bool = (response && response.data && response.data[0].friends_birthday)  ? true : false;
		  JJFB.birthday_permissions = bool;	  
		  callback(bool);
		});
	},
	
	// get upcoming birthdays of the user's friends.
	// both 'birthday' and 'birthday_date' are strings so I can't think of a great way to order by them in FQL.
	// so we're going to stick with the old way.  beer for someone who improves on this method:
	// 1. get all your friends (birthday, uid, and name) from FB
	// 2. sort them w/ javascript
	// 3. find the birthday closest to today, return the next 12
	// 4. store the top 12 in a cookie that expires tomorrow.
	// see http://wiki.developers.facebook.com/index.php/Sample_FQL_Queries for some additional ideas
	get_upcoming_birthdays: function(limit, placeholder, container, alt_placeholder, permissions_link) {
		if (!JJFB.facebook_logged_in()) { log("not logged in. no birthdays."); return; }
		
		// ask facebook if we've got friends_birthday permissions.

		var hasFriendsBirthday = function(response) {
			var rows = response.data;
			// log("hasFriendsBirthdayQuery:");
			// log(hasFriendsBirthdayQuery)
			
			if (rows.length == 0) { return; }
			
			if (rows[0].friends_birthday == 1) {
				// we've got the friends_birthday permission
				JJFB.get_and_show_upcoming_birthdays(limit, placeholder, container);
			} else {
				log('get upcoming birthdays, no birthday permission');
				if (alt_placeholder === undefined) {
					alt_placeholder = placeholder;
				}

				if (permissions_link === undefined)
				{
					// a bit hacky yet again. just didn't want to keep tacking arguments onto this function so...
					// if we didn't pass in additional markup to ask for more permissions, assume this is
					// for the share with friends module. otherwise we're in the upcoming birthdays module.
					// TODO: this will likely have to be reconsidered in the future.
					JJFB.get_all_friends(function() { 
						JJFB.render_users(JJFB.upcoming_birthdays, alt_placeholder, container, limit);
	 				}, "name");
				} else {
					// we don't have the birthday scope yet. randomize the friend order:
					JJFB.get_all_friends(function() { 
						JJFB.render_users(JJFB.upcoming_birthdays, alt_placeholder, container, limit+1);
	 				}, "random");
					container.append(permissions_link);
				}
				// TODO: don't hardcode markup in here, foo
				// var upcoming_birthdays_link = "<a href='#' class='upcoming_birthdays_link' onclick='JJFB.get_additional_scope(JJFB.perms.birthday, JJFB.reloadPage); return false;' >See All Your Friends' Birthdays!</a>";
			}
		};

		$.getJSON('https://graph.facebook.com/me/permissions?access_token='+response.authResponse.accessToken+'&callback=?', hasFriendsBirthday);
	},
	
	get_and_show_upcoming_birthdays: function(limit, placeholder, container, item_callback, none_callback) {
		JJFB.load_upcoming_birthdays(function(upcoming) {
			if (upcoming.length == 0 && none_callback != null) {
				none_callback();
	  		} else {
				JJFB.render_users(upcoming, placeholder, container, limit, item_callback);
			}
		});
		//JJFB.render_users(JJFB.upcoming_birthdays, placeholder, container, limit);
	},

	load_upcoming_birthdays: function(callback) {
log('load_upcoming_birthdays');
		d = new Date();
		midnight_today = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
		done = false;
		jQuery.jStore.ready(function() {
			found = jQuery.jStore.get('birthdays');
			found = eval('('+found+')');

			if (typeof(found) == 'string') {
				if (found) {
					if (found.retrieved == midnight_today.getTime() && found.user == FB._userID) {
						log("found birthdays from storage");
						JJFB.upcoming_birthdays = found.birthdays;
						if (callback) {
							callback(JJFB.upcoming_birthdays);
						}
						done = true;
					} else {
						//log("removing old value from storage");
						jQuery.jStore.remove('birthdays');
					}
				} else {
					log("no birthdays in storage");
				}
			}
		});
		if (done) {
			return;
		}

		log("loading birthdays from facebook");

		var hasFriends = function(response) {
			// got the response.
			var rows = response.data;
			var upcoming = new Array();
			var now = new Date();
			var current_year = now.getFullYear();
			var no_birthdays = new Array();

			for (i=0; i<rows.length; i++) {
				// log("row " + rows[i].name);
				// if our 'friend' decided to share his/her birthday with us...
				if (rows[i].birthday) {
					// parse that sucker and make it a date in this year.
					// birthday comes in the "MM/DD/YYYY" or "MM/DD" format
					date_split = rows[i].birthday.split("/");
					if (date_split.length >= 2) { // making sure we've got at least month and day
						rows[i].birthday_date = Date.parse(date_split[0] + "/" + date_split[1] + "/" + current_year);
						upcoming.push(rows[i]);
					} else {
						no_birthdays.push(rows[i]);
					}
				} else {
					no_birthdays.push(rows[i]);
				}
			}

			// sort users by birthday. if FQL treated birthday like a date, grumble grumble...
			upcoming = JJFB.users_sort(upcoming, "birthday_date");

			// linear search through sorted birthdays array. good in january, bad in december.
			// TODO: do we care to be a little smarter about this? just for nerd points?
			var pre = 0;
			
			// do you have any friends with upcoming birthdays?
			if (upcoming.length > 0) {
				while (upcoming[pre] && upcoming[pre].birthday_date < midnight_today) { pre++; }

				// console.log("now = " + now + ", birthday = " + birthdays[pre].birthday_date + ", prev birthday = " + birthdays[pre-1].birthday_date + ", next birthday = " + birthdays[pre+1].birthday_date + ", pre = " + pre);

				// add the past birthdays to the end of the list
				upcoming = upcoming.concat(upcoming.splice(0,pre));

				// AND THEN add your friends with no shared birthdays to the end of that list so they show up
				// in the autofill box
				upcoming = upcoming.concat(no_birthdays);

				// store the next 12 birthdays in a cookie so we don't make this expensive call every page load
				var bdays_to_store = new Array();
				for (i=0; i<12; i++) {
					bday = upcoming[i];
					if (bday) {
						bdays_to_store.push("{uid:'"+bday.uid+"', name:'"+bday.name.replace(/'/g, "\\'")+"', birthday_date: '"+bday.birthday_date+"'}");
					}
				}

				var bday_data = "{retrieved:"+midnight_today.getTime()+",user:" + FB._userID + ",birthdays:["+bdays_to_store.join(',')+']'+'}';

				jQuery.jStore.ready(function() {
					log("putting birthdays in storage");
					jQuery.jStore.store('birthdays', bday_data);
					//jQuery.jStore.store('birthdays', to_store);
				});
			}

			JJFB.upcoming_birthdays = upcoming;
			if (callback) {
				callback(JJFB.upcoming_birthdays);
			}
		};

	        $.getJSON('https://graph.facebook.com/me/friends?fields=birthday,name&access_token='+response.authResponse.accessToken+'&callback=?', hasFriends);
	},
	
	// sort users by key. pass in 'random' to sort randomly.
	users_sort: function(users, key) {
		if (key == "random") {
			users.sort(function(a,b) { return 0.5 - Math.random(); } );
		} else {
	    users.sort(function (thisObject,thatObject) {	
				if (thisObject[key] > thatObject[key]) {
					return 1;
				} else if (thisObject[key] < thatObject[key]) {
					return -1;
				}
				return 0;
	    });
		}
		return users;
	},

	render_users: function(users, placeholder, container, limit, item_callback) {

		if (!users || !placeholder || !container || !placeholder.html()){
			log("render_users called with invalid placeholder or container");
			return;
		}

		log("render_users called, limit=" + limit + ", users.length = " + users.length);
		
		// clear out the container (which may have the throbber)
		// placeholder.html("");
		
		content = placeholder.html();
		var users_to_show = users.length;
		if (!(limit === undefined)) {
			if (users.length > limit) {
				users_to_show = limit;
			}
		}
		for(i=0; i<users_to_show; i++){
			// replace the placeholders in the content
			// if we dont have an image for the user show this. 
			// if(!users[i].pic_square) {
			// 	users[i].pic_square = '/images/facebook_blank.gif';
			// }
			
			// our FQL query returns objects with 'uid' set, but the opengraph query returns objects with 'id' instead of 'uid'
			// accomodate for that here.
			user_id = (users[i].uid) ? users[i].uid : users[i].id;
			user_thumbnail_url = "http://graph.facebook.com/" + user_id + "/picture";
			div_content = content.replace(/rel=["']#img["']/,"src='" + user_thumbnail_url + "'");
			div_content = div_content.replace('#name',users[i].name);			
			div_content = div_content.replace('#uid',user_id);

			if (users[i].birthday_date) {
				dDate = new Date(parseInt(users[i].birthday_date));
				bd = JJFB.parse_date(dDate);
				div_content = div_content.replace('#birthday',bd);
			} else {
				div_content = div_content.replace('#birthday','');
			}

			if (item_callback) {
				div_content = item_callback(div_content, i, users_to_show);
			}

			// add the new element
			container.append(div_content);
		}
	},
	
	show_throbber: function(container) {
		container.html('<center><img src="/images/icons/loading_throbber_blue.gif" /></center>');
	},
  
	parse_date: function(epoch){
		
		var now = new Date();
		now.setTime(Date.parse((now.getMonth()+1)+'/'+now.getDate()+'/'+now.getFullYear()));
		
		var d = new Date(epoch);
		var curr_date = d.getDate();
		var curr_month = d.getMonth();
		var m_names = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");

		if((curr_month == now.getMonth()) && (curr_date == now.getDate())){
			return "Today";
		}
		if((curr_month == now.getMonth()) && (curr_date == now.getDate()+1)){
			return "Tomorrow";
		}		
	
		return m_names[curr_month]+ " " + curr_date;
	},
	
	hide_birthdays: function(jquery_obj){
		jquery_obj.hide('slow');
		document.cookie = 'hide_birthday=1; path=/';		
	},

	show_birthdays: function(jquery_obj){
		if(!/hide_birthday=([^;]+)/.test(document.cookie)){
			// wait a few seconds then show the birthdays s l o w l y 
			window.setTimeout(function(){
				jquery_obj.show('slow');
			}, 1000)		
		}
	},
	
	share: function(title, url, preview_url, skip_tracking, ref_code) {
		//handle if ref value is not passed dont error out
		if(ref_code === undefined){
			ref_code = "jj_fb_self";
		}
	  if(typeof(skip_tracking) == "undefined") {
	    skip_tracking = false;
    }
		if(JJFB.facebook_logged_in() && title && url && preview_url) {
			JJFB.publishToStream(null, title, url, preview_url,null, ref_code);
		} else {
			//url=location.href;
			//title=document.title;

			// add cmpid
			var idx = url.indexOf('?');
			var qs_join = (idx >= 0) ? '&' : '?';
			url += qs_join + 'cmpid=fb_nc';

			// share.php vs sharer.php?!?!? sharer.php takes params, share.php does meta-tags??
			// preview_url not used, should determine this from meta-tags
			fb_win = window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(url)+'&t='+encodeURIComponent(title),'sharer','toolbar=0,status=0,width=626,height=436');
			if(skip_tracking == false) {
  			if(JJFB.facebook_logged_in()) {
  				OmnitureHelper.log_event('share','Facebook')
  			} else {
  				OmnitureHelper.log_event('share','Facebook Not Connected My Wall')
  			}
			}
			fb_type = 'my_wall'
			JJFB.gotoPostShare(fb_type);
		}
		return false;
	},

  // this def needs to be expanded/revisited.. what about caption? description? may just have to pass in a big object to it.. 
	publishToStream: function(friend_id, title, url, preview_url, callback, ref_code){
    var caption = '';
    var cmpid;
		
		//handle if ref value is not passed dont error out
		if(ref_code === undefined)
		{
			//for self and friends wall post use generic code
			if(friend_id)
			{
			  ref_code = "jj_fb_friend";
			}else {
				ref_code = "jj_fb_self";
			}
			
		}
		
		
    if(friend_id) {
        cmpid =  SiteConfig.fb_friend_post_cmpid; 
    } else {
        cmpid =  SiteConfig.fb_post_cmpid; 
    }

		var share_url = FBWallPostConfig.name_url.replace("{*link*}", url)
		var action_link_url = FBWallPostConfig.action_link_url.replace("{*link*}", url)

    // add cmpid to the urls 
    if (cmpid){
       share_url += (url.indexOf('?') > -1)?"&":"?" + "cmpid="+cmpid;
       action_link_url += (action_link_url.indexOf('?') > -1)?"&":"?" + "cmpid="+cmpid;
    }

    var publish = {
      method: 'stream.publish',
      ref: ref_code,
      attachment: {
        name: FBWallPostConfig.name_label.replace("{*title*}", title),
        href: share_url,
        description: FBWallPostConfig.description.replace("{*description*}", FBWallPostConfig.template_description),
        'media': [{ 
          'type': 'image', 
          'src': preview_url, 
          'href': share_url
        }]
      },
      action_links: [
        { text: FBWallPostConfig.action_link_label, href: action_link_url }
      ],
      user_prompt_message: 'Add a message'
    };

    if(friend_id) {
      publish.target_id = friend_id;
    }

    FB.ui(publish, function(response) {
			if(response && response.post_id) {
				if(JJFB.postShareRedirect() == true) {
					if(friend_id) {
						fb_type = 'friends_wall'
					} else {
						fb_type = 'my_wall'
					}
					JJFB.gotoPostShare(fb_type);
					// post share does logging
				} else {
					if(friend_id) {
						OmnitureHelper.log_event('share','Facebook Friends Wall');
					} else {
						OmnitureHelper.log_event('share','Facebook My Wall');
					}					
				}
	      if(!(typeof(callback) != "undefined")) {
	        callback();
	      }
			}
    });
  },

	gotoPostShare: function(fb_type) {
	  if(JJFB.postShareRedirect() == false) {
	    return;
	  }
		location.href = "/view/post_share/" + $.cookie('content_view_make_key') + "?destination=facebook&fb_type=" + fb_type;
	},

  // returns true if we're on one of the channels that redirects after sharing
  postShareRedirect: function() {
    if ((typeof(SiteConfig) != "undefined") && SiteConfig && SiteConfig.redirect_to_post_share) 
      return SiteConfig.redirect_to_post_share;
    else 
      return false;
  },

    uploadPhoto: function(make_id) {
        JJFB.login_with_perms(JJFB.perms.upload_photo, JJFB.perms.default_optional, function(response) {
            data = {
                make: make_id,
                access_token: response.authResponse.accessToken,
                access_token_expires_in: response.authResponse.expiresIn
            }

            handler = function(json) {
                $('#throbber').hide();
                $('#modal_preview_wrap').show();
                document.getElementById('upload_photo_link').href = json.link;
                OmnitureHelper.log_event('share','Facebook Album')
            }
            ModalHelper.showModal('upload_modal');  

            $.post('/share/facebook_upload', data, handler, 'json');
        }, 'basic');
	},

	updateDirtyFacebookFields: function(field) {
		data = 'field=' + field 
		handler = function(json) { }
		$.post('/user/update_facebook', data, handler, 'json');
	},
	
    grantedGraphPermissions: function(callback) {
        if(!FB.getAuthResponse()) {
            callback([]);
            return;
        }

        FB.api('/me/permissions', function(response) {
            if (response.data && response.data.length == 1) {
                var keys = [];
                $.each(response.data[0], function(k) { keys.push(k) });
                callback(keys);
            } else {
                callback([]);
            }
        });
	},
	
	grantedPermissions: function(callback) {
		if(!FB.getAuthResponse()) {
			callback([]);
			return;
		}
	
		user_id = FB._userID;
		// the entire list as of 07/19/2010 (we should keep this up-to-date)
		// from: http://developers.facebook.com/docs/authentication/permissions
		scope = 'publish_stream,create_event,rsvp_event,sms,offline_access,manage_pages,email,read_insights,read_stream,read_mailbox,ads_management,xmpp_login,user_about_me,friends_about_me,user_activities,friends_activities,user_birthday,friends_birthday,user_education_history,friends_education_history,user_events,friends_events,user_groups,friends_groups,user_hometown,friends_hometown,user_interests,friends_interests,user_likes,friends_likes,user_location,friends_location,user_notes,friends_notes,user_online_presence,friends_online_presence,user_photo_video_tags,friends_photo_video_tags,user_photos,friends_photos,user_relationships,friends_relationships,user_religion_politics,friends_religion_politics,user_status,friends_status,user_videos,friends_videos,user_website,friends_website,user_work_history,friends_work_history,read_friendlists,read_requests';
		var granted = [];
		
		$.getJSON('https://graph.facebook.com/me/permissions?access_token='+response.authResponse.accessToken+'&callback=?', function(response){
		  var bool;
		  if(response && response.data){
		    for(key in response.data[0]){
		      if(scope.search(key) != -1){
			granted.push(key);
		      }
		    }
		  }

		  callback(granted);
		});

	},
	
	isProxyEmail: function(email) {
		re = /^.+@proxymail\.facebook\.com$/
		if (email.match(re) == null) {
			return false;
		} else {
			return true;
		}
	},
	
	// DEPRECATED
	getSession: function(cb) {
    var authResponse = FB.getAuthResponse();
    // MISSING: base_domain, secret, session_key, sig
		cb({
		  access_token: authResponse.accessToken,
		  expires: authResponse.expiresIn,
		  uid: authResponse.userID
		});
	}
}

extend_fb_methods = function() {
	// FB EXTENSION
	// we're extending the FB.ui methods here to provide a 'permissions.request' modal dialog on the page
	// rather than via a popup since we ask for the permissions in a 2 step process- required then optional.
	// if a popup blocker is on, the second popup would be blocked since the user only initiated the first
	// popup with a click.
	FB.provide("UIServer.Methods",{ 'permissions.request' : { 
		size : {width: 575, height: 300}, 
    url: 'connect/uiserver.php',
	  transform : FB.UIServer.genericTransform }
	});
}


