		/////////////////////////////////////////////
		// Le Friend Selector
		/////////////////////////////////////////////

		// IE's implementation of array has no indexOf method
		get_position = function(arr, item){
				for(var i=0; i<arr.length; i++){
					if(arr[i]==item){
						return i;
					}
				}
				return -1;
		}
		
		var sml = sml || {};
		sml.fs = {};
		
		sml._data_load_methods = sml._data_load_methods || [];
		sml._data_load_methods.push(function(){
				sml.fs.bind_selectors();
				sml.bind_friend_grids();
				sml.reset_sizing();
			});
		
		sml.fs.types = ['fb','email', 'birthdays'];
		
		sml.fs.bind_selectors = function(leftover, include_existing) {
			include_existing = include_existing || false;
			if(sml.loaded != true)
				return sml.get_friends_list();
			var binders = $('.bindableSelector');
			if(include_existing) {
				binders = binders.extend($('.bindedSelector'));
			}
			for( var i=0; i<binders.length; i++ ) {
				var selected_users;
				try {
					selected_users = binders[i].getAttribute('preselectedUsers').split(',');
					if( selected_users.length && selected_users[0].length == 0 )
						selected_users = []
				} catch (e) {
					selected_users = [];
				}

				var params = sml.lib.attributeToObject(binders[i].getAttribute('params'));
				params['selected_users'] = selected_users;
				params['from_flag'] = binders[i].getAttribute('from_flag');
				params['render_label'] = binders[i].getAttribute('fs_render_label') || undefined;
				params['funspace_all'] = binders[i].getAttribute('funspace_all') != "false";
				params['select_all'] = binders[i].getAttribute('select_all') != "false";
				params['clear'] = params['clear'] != "false";
				params['typeahead'] = binders[i].getAttribute('typeahead') != "false";
				params['control_placement'] = binders[i].getAttribute('control_placement') || 'bottom';
				params['clear_hook'] = binders[i].getAttribute('clear_hook');
				var get_friends_fn = binders[i].getAttribute('fs_get_friends')
				if (get_friends_fn) {
					params['get_friends'] = get_friends_fn;
				}
		
				sml.fs.draw_friend_selector(binders[i].getAttribute('id'), params);
			}
		}

		//////////////////////////////////////////////
		// how each checkbox row should be rendered .. allows customization
		//////////////////////////////////////////////
		sml.fs.default_render_label = function(friend, id, checked) {
			var group_type = (friend.type)?id+'_'+friend.type:id+'_fb';
			var birthday_attribute = '';
			if (friend.birthday) {
				var today = new Date();
				var birthday_negative_1 = new Date(today.getFullYear() - 1, friend.birthday.getMonth(), friend.birthday.getDate());
				var birthday_positive_1 = new Date(today.getFullYear() + 1, friend.birthday.getMonth(), friend.birthday.getDate());
				var birthday_neutral = new Date(today.getFullYear(), friend.birthday.getMonth(), friend.birthday.getDate());
				if ((Math.min(Math.abs(birthday_negative_1 - today), Math.abs(birthday_positive_1 - today), Math.abs(birthday_neutral - today)) / (3600 * 24 * 1000)) <= 2) {
					birthday_attribute = 'celebrity="true"';
				}
			}
			var birthday_element = '';
			if (friend.birthday_str) {
				birthday_element = '<span class="birthday" style="display: none">' + friend.birthday_str + '</span> ';
			}
			if(friend.type == 'email'){
				var friend_span = '<span>' + friend.first_name + ' ' + friend.email + '</span>';
			}else{
				var friend_span = '<span>' + friend.first_name + ' ' + friend.last_name + '</span>';
			};
			return '<label class="' + group_type + '" ' + birthday_attribute + '>\
<nobr>\
	<input type="checkbox"\
		is_installed="'+friend.is_app_user+'"\
		value="'+friend.uid+'" name="'+group_type+'_ids[]" id="'+id+'_'+friend.uid+'_'+'ids[]" class="inputcheckbox" ' + checked  + '/>' +
	birthday_element + friend_span + '</nobr></label>';
		}

		
		// draw_friend_selector
		// Arguments:
		// id: div id where friend selector will render
	    // params:
		//     selected_users: list of facebook user ids to pre-select
		//     control_placement: keyword specifying where selection
		//         controls should appear.  Either 'top' or 'bottom',
		//         default is 'bottom'
		//     select_all: see selection_control_html
		//     funspace_all: see selection_control_html
		//     clear: see selection_control_html
		//     clear_hook: a string with js code that will be additionally run 
		//                 when the user clicks Clear link
		//	   from_flag: the flag to use in logging link clicks.
		sml.fs.draw_friend_selector = function(id, params) {
			params = params || {};
			//initialize defaults and required vars
			var friends_to_lower = params['friends_to_lower'] || [];
			var friend_list = [];
			if (params['uids']) {
				for (var i = 0; i < params['uids'].length; i++) {
					friend_list.push(sml.facebook_people_dict[params['uids'][i]]);
				}
			} else {
				orig_list = params['get_friends'] || ((params.tabs)?sml.all_friend_list:sml.facebook_friend_list);
				friend_list = [];
				for (var i=0; i < orig_list.length; i++) {
					if (orig_list[i].uid != sml.viewer_uid) {
						friend_list.push(orig_list[i]);
					}
				}
			}
			var render_label = params['render_label'] || sml.fs.default_render_label;
			var selected_users = params['selected_users'] || [] ;
			var control_placement = params['control_placement'] || 'bottom';
			var selection_controls = sml.fs.selection_control_html(id, params);
			var height = (document.getElementById(id).getAttribute('fs_height'))?' style="height:'+document.getElementById(id).getAttribute('fs_height')+'px"':'';

			if (params['get_friends']) { 
				friend_list = params['get_friends'](); 
			}

			if (friends_to_lower){
				var new_friend_list = [];
				var lower_friend_list = [];
				for (i = 0; i < friend_list.length; i++) {
					var f = friend_list[i];
					if (get_position(friends_to_lower, parseInt(f.uid)) > -1){
						lower_friend_list.push(f);
					} else {
						new_friend_list.push(f);
					}
				}
				friend_list = new_friend_list.concat(lower_friend_list);
			}

			var selected_users_map = {}
			if(selected_users.length == 0){ selected_users_map[sml.viewer_uid] = false; }
			for (var i=0; i < selected_users.length; i++) {
				selected_users_map[selected_users[i]] = true;
			}		
			
			var html_block = new HTMLBlock(); // init html maker
			for( var i=0; i<friend_list.length; i++ )
			{
				var is_top = friend_list[i].uid in selected_users_map;
				var checked = (is_top && selected_users_map[friend_list[i].uid]) ? 'checked' : '';
				friend_list[i].index = i
				if( is_top ) {
					html_block.prepend(render_label(friend_list[i], id, checked, i));
				}
				else {
					html_block.add(render_label(friend_list[i], id, checked, i));
				}
			} 
			if(params.tabs){
				html_block.prepend('<div id="'+id+'_fs_add_email" class="fs_email_add_more"><a onclick="sml.fs.email_popup(\''+id+'\')"><img src="'+sml.util.make_image_url('/images/superpoke/email.gif')+'" />&nbsp;&nbsp;Add more e-mails!</a></div>');
			}
			html_block.prepend('<div id="'+id+'_fs" class="condensed_multi_friend_body" '+height+'>');
			html_block.add('</div></div>');
			if (control_placement == 'bottom') {
				html_block.add(selection_controls);
			}
			if (params.typeahead) {
				html_block.prepend('<div class="sml_typeahead_div"><input type="text" id="' + id + '_typeahead" class="sml_typeahead_input" value="" placeholder="Start Typing a Friend\'s Name"/></div>');
			}
			html_block.prepend('<div class="condensed_multi_friend_selector">');
			if(params.tabs)
				html_block.prepend('\
<a id="'+id+'_tab_all" class="sml_friend_selector_tab sml_friend_selector_tab_selected" href="#" onclick="sml.fs.filter(\''+id+'\',\'all\')">All</a>\
<a id="'+id+'_tab_fb" class="sml_friend_selector_tab" href="#" onclick="sml.fs.filter(\''+id+'\',\'fb\')">Facebook</a>\
<a id="'+id+'_tab_email" class="sml_friend_selector_tab" href="#" onclick="sml.fs.filter(\''+id+'\',\'email\')">Email</a>\
' + (params.birthdays ? '<a id="'+id+'_tab_birthdays" class="sml_friend_selector_tab" href="#" onclick="sml.fs.filter(\''+id+'\',\'birthdays\')">Birthdays</a>' : '') + '\
<div style="clear:both"></div>');
			if (control_placement == 'top') {
				html_block.prepend('<div class="breakline"></div>');
			   	html_block.prepend(selection_controls);
			}
			document.getElementById(id).innerHTML = html_block.dump();
			sml.lib.removeClass(document.getElementById(id), 'bindableSelector', 'bindedSelector');
			
			if (params.typeahead) {
				$('#' + id + '_typeahead').placeholder(); //put placeholder on typeahead input
				sml.typeahead.draw(
					id + '_typeahead',
					friend_list,
					function(uid) {						
						var item = document.getElementById(id+'_'+uid+'_'+'ids[]');
						item.checked=true;

						// Reset the typeahead to read Type a friend's name
						$('#' + id + '_typeahead').val('');
						$('#' + id + '_typeahead').focus();
						// Scroll to the selected friend
						var friend_selector = $('#' + id + '_fs')[0];
						if(item.offsetTop == 0) {
							item = item.offsetParent;
						}
						friend_selector.scrollTop = item.offsetTop - friend_selector.offsetTop;
					}
				);
			}
		}
		
		sml.fs.email_popup = function(id) {
			sml.email.popup(null,null, function(is_cancel) {
				if(!is_cancel) {
					$('#'+id).attr('filter','email');
					sml.lib.addClass(document.getElementById(id), 'bindableSelector');
					sml.fs.bind_selectors();
				}
			});
		}
		
		sml.fs.filter = function(id, type) {
			type = type || 'all';

			// if no emails give the popup
			if(type=='email' && sml.email_friend_list.length < 1) {
				sml.fs.email_popup(id);
			} else {
				$('#'+id).attr('filter',type);
				for(var x=0; x < sml.fs.types.length; x++) {
					if(type == sml.fs.types[x] || type=='all') {
						$('.'+id+'_'+sml.fs.types[x]).show();
						if(type==sml.fs.types[x]){
							element = document.getElementById(id+'_tab_'+sml.fs.types[x]);
							if (element) sml.lib.addClass(element, 'sml_friend_selector_tab_selected');
						}
					}
					else {
						$('.'+id+'_'+sml.fs.types[x]).hide();
					}
					if(type != sml.fs.types[x]) {
						var element = document.getElementById(id+'_tab_'+sml.fs.types[x]);
						if (element) sml.lib.removeClass(element, 'sml_friend_selector_tab_selected');  
					}
				}
				if(type=='all')
					sml.lib.addClass(document.getElementById(id+'_tab_'+'all'),'sml_friend_selector_tab_selected');  
				else
					sml.lib.removeClass(document.getElementById(id+'_tab_'+'all'),'sml_friend_selector_tab_selected');  
				if(document.getElementById(id + '_typeahead'))
					document.getElementById(id + '_typeahead').focus();
					
				if(type=='email')
					document.getElementById(id+'_fs_add_email').style.display='block';
				else
					document.getElementById(id+'_fs_add_email').style.display='none';
			}
			if (type == 'birthdays') {
				$('[celebrity=true]').show();
				$('.birthday').show();
			} else {
				$('.birthday').hide();
			}
			sml.ajax_log_click('$sml_activity.sml_links._fb_tabs_BASE'+type);	
			return false;
		}
		
		// selection_control_html
		// Arguments:
	    // id: div id of the friend selector these controls apply to
		// params: The following are a true or false value indicating whether
		//     or not to include the specified control, all values
		//     default to true.  Valid controls are:
		//     select_all
		//     funspace_all
		//     clear
		//	   There is also a from_flag used when logging clicks on the above controls.
		sml.fs.selection_control_html = function(id, params) {
			params = params || {};
			var clear_hook = params['clear_hook'] || '';
			var select_all = params['select_all'] === undefined ? false : params['select_all'];
			var funspace_all = params['funspace_all'] === undefined ? true: params['funspace_all'];
			var clear = params['clear'] === undefined ? true : params['clear'];
			var from_flag = params['from_flag'] || 0;
			var appname = params['appname'] || 'FunSpace';

			var selection_controls = '<div class="fs_footer">';
			if (select_all) {
				selection_controls += '<a id="' + id + '_fs_select_all" class="fs_select_all" onclick="sml.ajax_log_click(\'$hash($funwall_links.select_all_clicked)\', \'' + from_flag +
					'\'); sml.fs.select_all(\'' + id + '\'); return false;" href="#">Select All</a>&nbsp;';
			}

			if (funspace_all) {
				selection_controls += '<a id="' + id + '_fs_installed" onclick="sml.ajax_log_click(\'$hash($funwall_links.friends_with_funwall_clicked)\', \'' + from_flag +
					'\'); sml.fs.select_installed_users(\''+ id + '\'); return false;" href="#">w/' + appname + '</a>';
				if (clear) {
					selection_controls += ' | ';
				}
			}

			if (clear) {
			    selection_controls += '<a id="' + id + '_fs_clear" onclick="sml.ajax_log_click(\'$hash($funwall_links.clear_clicked)\', \'' + from_flag +
					'\'); sml.fs.select_all(\'' + id + '\', false); ' + clear_hook + ';return false;" href="#">Clear</a>';
			}

			selection_controls += '</div>';
			
			return selection_controls;
		}
		
		sml.fs.selected_by_group = function(id) {
			var total = 0;
			var result = {};
			for(var x=0; x < sml.fs.types.length; x++) {
				var new_set = new Array();
				var set = document.getElementsByName(id+'_'+sml.fs.types[x]+'_ids[]');
				for (var i=0; i<set.length; i++)
				{
					if( set[i].checked == true){
						new_set.push(set[i].value);
					}
				}
				result[sml.fs.types[x]] = new_set;
				total += new_set.length;
			}
			result['total'] = total;
			return result;
		};

		sml.fs.select_all_hook = function(){};

		sml.fs.select_all = function(id, select_all, type) {
			if (select_all == undefined) {
				select_all = true;
			}
			var items = $("label:visible", $("#" + id));
			if (select_all) {
				$(":checkbox", items).attr("checked", "true");
				sml.fs.select_all_hook();
			} else {
				$(":checkbox", items).removeAttr("checked");
			}
		};
		
		sml.fs.select_installed_users = function(id) {
			var items = $("label:visible", $("#" + id));
			$(":checkbox[is_installed=true]", items).attr("checked", "true");
			$(":checkbox[is_installed!=true]", items).removeAttr("checked");
		};

		//this is in the off case that you need to transfer all your checked people from one selector to another
		sml.fs.copy_selection_state = function(id1, id2) {
			for(var x=0; x < sml.fs.types.length; x++) {
				var set1 = document.getElementsByName(id1 + '_'+sml.fs.types[x]+'_ids[]');
				var set2 = document.getElementsByName(id2 + '_'+sml.fs.types[x]+'_ids[]');
				for (var i = 0; i < set1.length; i++) {
					set2[i].checked = set1[i].checked;
				}
			}
		}
		
		//###########################################
		//## Friend Grid
		//###########################################
		sml.friend_grid = {};
		sml.bind_friend_grids = function() {
			if (sml.loaded != true) {
				return sml.get_friends_list();
			}
			var binders = $('.bindableFriendGrid');
			for (var i = 0; i < binders.length; i++) {
				var binder = binders[i];
				var friends = binder.getAttribute('friend_uids');
				if (friends) {
					var friend_uids = friends.split(',');
					sml.bind_names(friend_uids);
				} else {
					var friend_uids = undefined;
				}
				sml.draw_friend_grid(binder.getAttribute('id'),
					binder.getAttribute('rows'), binder.getAttribute('columns'), 
					binder.getAttribute('image_width'), binder.getAttribute('image_height'),
					binder.getAttribute('uid'), friend_uids);
			}
		}

		sml.shuffle = function(list) {
			if (list.length > 1) {
				index = len = list.length;
				while (--index) {
					var rand_index = Math.floor(Math.random() * len);
					var temp = list[index];
					list[index] = list[rand_index];
					list[rand_index] = temp;
				}
			}
		};
		

		sml.draw_friend_grid = function(id, rows, columns, image_width, image_height, uid, friend_uids) {
			// Create a list of up to rows * columns friends with those that have the app shown first.
			// The two groups (those with the app and those without) are randomly shuffled.
			if (friend_uids) {
				var friend_list = new Array();
				for (var friend_index = 0; friend_index < friend_uids.length; friend_index++) {
					var friend_uid = friend_uids[friend_index];
					var friend = sml.get_person(friend_uid) || {'uid': friend_uid}
					friend_list.push(friend);
				}
			} else {
				var friend_list = sml.facebook_friend_list;
			}
			
			var stati = sml.friends_by_app_status(friend_list,uid);
			var friends_with_app = stati['with'];
			var friends_without_app = stati['without'];
			var friend_count = stati.total_friends;
			
			var total = rows * columns;
			sml.shuffle(friends_with_app);
			friend_short_list = friends_with_app.slice(0, total);
			var extra = total - friends_with_app.length;
			if (extra > 0) {
				sml.shuffle(friends_without_app);
				friend_short_list = friend_short_list.concat(friends_without_app.slice(0, extra));
			}

			// Generate the header.
			var html_block = new HTMLBlock();
			html_block.add(sml.get_name_html(uid, true, "{'firstnameonly': true, 'possessive': true}"));
			var width_value = ((columns*image_width)+((columns-1)*1));
			html_block.add(' Friends (' + friend_count + ')<center><div id="friend_grid_type_ahead" style="margin-top: 5px; width:' + width_value +'px;"><input type=text id="friend_typeahead" placeholder="Search people" style="width:'+columns*image_width+'px;"/></div></center>');

			// Now generate a table containing the photos of those friends.
			var missing_image_url = "$serdes.make_static_url('/images/funwall/unknown.jpg')";
			html_block.add('<div style="margin-top: 5px;"><table cellpadding="0" cellspacing="2">');
			friend_index = 0;
			for (var row = 0; row < rows; row++) {
				html_block.add('<tr>');
				for (var column = 0; column < columns; column++) {
					html_block.add('<td><div style="width: ' + image_width + 'px; height: ' + image_height + 'px;">');
					if (friend_index < friend_short_list.length) {
						var friend = friend_short_list[friend_index++];
						if (friend.pic_square) {
							var image_url = friend.pic_square;
						} else {
							var image_url = missing_image_url;
						}
						html_block.add('<a href="#" onclick="sml.friend_grid_callback(\'' + friend.uid + '\', false); return false;">' + sml.get_image_html(friend.uid, image_width, image_height) + '</a>');
					}
					html_block.add('</div></td>');
				}
				html_block.add('</tr>');
			}
			html_block.add('</table></div>');
			document.getElementById(id).innerHTML = html_block.dump();
			sml.lib.removeClass(document.getElementById(id), 'bindableFriendGrid');

			// Initialise the typeahead control.
			$('#friend_typeahead').placeholder();
			sml.typeahead.draw('friend_typeahead', friend_list, function(uid) { sml.friend_grid_callback(uid, true); });
		}
		
		//###########################################
		//## TypeAhead support.
		//###########################################	
		
		sml.typeahead = {};
		sml.typeahead.item = function(row,a,b,query) {
			var name = row[1]['first_name']+' '+row[1]['last_name'];
			var pic = row[1]['pic_square'] || "$serdes.make_static_url('/images/funwall/unknown.jpg')";
			var query_loc = name.toLowerCase().indexOf(query.toLowerCase());
			var name_query = name.substr(query_loc, query.length);
			name = name.replace(name_query, '<em>'+name_query+'</em>');
			return "<div class='sml_typeahead_item' ><img class='sml_typeahead_img' src='"+pic+"'/><div class='sml_typeahead_name'>"+name+"</div></div>";
		}
		
		sml.typeahead.name = function(row) {
			return row['first_name']+' '+row['last_name'];
		}
		
		sml.typeahead.on_select = function(element_id, ele) {
			sml.typeahead.dict[element_id](ele.extra[0].uid);
		}
	
		sml.typeahead.dict = {};
		sml.typeahead.defaults = {'onItemSelect':sml.typeahead.on_select, 'selectOnly':true, 'leftOffset': 33 ,'delay':40,'maxItemsToShow':'6', 'formatItem':sml.typeahead.item, 'formatName':sml.typeahead.name,'resultsClass':'sml_typeahead_results'};
		sml.typeahead.draw = function(element_id, list, callback) {
			sml.typeahead.dict[element_id] = callback;
			sml.typeahead.defaults['onItemSelect'] = function(ele){sml.typeahead.on_select(element_id, ele);};

			$("#"+element_id).autocompleteArray(list, sml.typeahead.defaults);
		}


