fetlife_all_members (ASL+role+status search)

greasemonkey script to find fetlife members when it's possible. Search by name, gender, role, age, location or status.

Fra 12.02.2015. Se den seneste versjonen.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name			fetlife_all_members (ASL+role+status search)
// @namespace		bewam.free.fr
// @description		greasemonkey script to find fetlife members when it's possible. Search by name, gender, role, age, location or status.
// @include			http*://fetlife.com/*
// @version			1.8.5.20150212
// @grant			GM_addStyle
// @run-at			document-end
// @require			http://code.jquery.com/jquery-2.1.1.min.js
// ==/UserScript==
/*-----------------------------------*/	
const DEBUG = false;
function debug() {
	if (DEBUG && console) {
		console.log.apply(console, arguments);
	}
}
/*-----------------------------------*/
(function ($){

const STRING_USERS = 'div.user_in_list',
	STRING_VAF = 'viewAllForm',
	ARRAY_GENDER = ['M','F','CD/TV','MtF','FtM','TG','GF','GQ','IS','B','FEM'],
	ARRAY_GENDER_LABEL = ['Male','Female','CD/TV','Trans-MtF','Trans-FtM','Transgender','Gender Fluid','Genderqueer','Intersex','Butch','Femme'],
	ARRAY_ROLE = ['Dom','Domme','Switch','sub','Master','Mistress','slave','pet','kajira','kajirus','Top','Bottom','Sadist','Masochist','Sadomasochist','Ageplayer','Daddy','babygirl','babyboy','Mommy','brat','Primal','Fetishist','Kinkster','Hedonist','Vanilla','Unsure'],
	ARRAY_ROLE_LABEL = ARRAY_ROLE,
	ARRAY_INTO_STATUS = ['is into', 'is curious about'],
	ARRAY_INTO_ACTIVITY = ['giving', 'receiving', 'watching', 'wearing', 'watching others wear', 'everything to do with it']
;

var placeAfter = '#header_v2';

var next = '';
/* balance columns */
var altPos = 0,
	currentCount = 0
;
var members = [], // user html storage
	/** NOTE mCache = ARRAY( { i: { [0]'name':'', [1]'age':XX, [2]'gender':WW, [3]'role':'', [4]'location':'', [5]url:'', [6]"hasAvatar":boolean } }) */
	mCache = [],
	listContainers = [] // columns where lists appear
;
var userRegExp = new RegExp('([0-9]{2})('+ARRAY_GENDER.join('|')+')? ('+ARRAY_ROLE.join('|')+')?','i');

/** modified, value, default value*/
var filters = {
	'NameContains': [false,'',''],
	'AgeMin': [false,'',''],
	'AgeMax': [false,'',''],
	'Gender': [false, [], []],
	'Role': [false, [], []],
	'LocContains': [false,'',''],
	'IntoStatus': [false,'',''],
	'IntoActivity': [false,'','']
// has_avatar: @see function
}
var ajaxLocked = false,
	scriptLaunched = false,
	 is_fetishes_page = /^\/fetishes/.test(unsafeWindow.location.pathname)
;
/*-----------------------------------*/
function init()
{
	var nextPage = $('a.next_page'),
		previousPage = $('a.previous_page'),
		pageUsers = $(STRING_USERS),
		Ctrls = "#"+STRING_VAF+"Controls"
	;
	
	/** a next_page link and a list of members are on current page ? go on */
	if( ( nextPage.length > 0 || previousPage.length > 0) && pageUsers.length > 0 )
	{
		setContainers(pageUsers)
		debug("listContainers: "+listContainers);

		drawBlock();

		$(Ctrls).click(function()
		{
			$('#'+STRING_VAF+'Content').toggle("slow")
			console.log(this)
		});
		
		setNext(nextPage);
		$('#'+STRING_VAF+'ButtonGo').click( function()
		{
			$(this).attr("disabled", 'true');
			launchSearch();
		});
	}
};
function launchSearch()
{
	if( ! scriptLaunched )
	{
		$('#'+STRING_VAF+'ShowCount').html("loading ...")
		updateFilters();
		debug(filters);
		// TODO delete it when "?page=1"
//		lookPage();
		cleanPage();
		lookAhead(true);
		scriptLaunched = true;
	}
}
GM_addStyle(
	'#'+STRING_VAF+'Container { \
		background-color: rgba(255, 255, 255, 0.4);\
		color: white !important;/**/ \
		padding:5px;\
		min-height:15px !important;\
		vertical-align:middle;\
	} \
	#'+STRING_VAF+'Controls { \
		display: block;/**/ \
		min-height:15px !important;\
	}\
	#'+STRING_VAF+'Content { \
		display: none;/**/ \
	}\
	#'+STRING_VAF+'ButtonStop{ \
		display: none;\
	}\
	'
);

function drawBlock(){

		var options_gender = '',
			options_role = '',
			options_into_activity = '',
			options_into_status = ''
		;
		$.each(ARRAY_GENDER, function(i,v){options_gender+='<option value="'+v+'" >'+ARRAY_GENDER_LABEL[i]+'</option>'});
		$.each(ARRAY_ROLE, function(i,v){options_role+='<option value="'+v+'" >'+ARRAY_ROLE_LABEL[i]+'</option>'});
		$.each(ARRAY_INTO_ACTIVITY, function(i,v){options_into_activity+='<option value="'+v+'" >'+ARRAY_INTO_ACTIVITY[i]+'</option>'});
		$.each(ARRAY_INTO_STATUS, function(i,v){options_into_status+='<option value="'+v+'" >'+ARRAY_INTO_STATUS[i]+'</option>'});
		
		var BLOCK = '<div id="'+STRING_VAF+'Container"> \
				<div id="'+STRING_VAF+'Controls">\
					<b>&gt;</b> \
					<u>view all members</u>\
				</div>\
				<div id="'+STRING_VAF+'Content"> \
					<u>filters</u> <br />\
					&nbsp;name: &nbsp;\
						<input id="'+STRING_VAF+'NameContains" type="text" class="filter"></input>\
					&nbsp;location: &nbsp;\
						<input id="'+STRING_VAF+'LocContains" type="text" class="filter" ></input>\
					\
					age: &nbsp;\
						min: <input id="'+STRING_VAF+'AgeMin" type="text" class="filter" size="2"></input>\
						&nbsp; \
						max: <input id="'+STRING_VAF+'AgeMax" type="text" class="filter" size="2"></input>\
					<br />\
					&nbsp; gender &nbsp;\
						<select id="'+STRING_VAF+'Gender" class="filter" name="gender" multiple="multiple" size="3">\
							<option value="" selected="selected">none specified</option>\
							'+options_gender+
						'</select>\
					\
					&nbsp; Role &nbsp;\
						<select id="'+STRING_VAF+'Role" class="filter" name="role" multiple="multiple" size="5">\
							<option value="" selected="selected">none specified</option>\
							'+options_role+
						'</select>'
		;
		if(is_fetishes_page)
		{
			BLOCK += '\
						&nbsp; Into &nbsp;\
						<select id="'+STRING_VAF+'IntoStatus"  multiple="multiple" size="3">\
							<option value="" selected="selected">All</option>\
							'+options_into_status+
						'</select>\
						<select id="'+STRING_VAF+'IntoActivity" multiple="multiple" size="3">\
							<option value="" selected="selected">All</option>\
							'+options_into_activity+
						'</select>\
					'
			;
		}
				BLOCK +=	'<br />\
 					<input type="hidden" name="'+STRING_VAF+'HasAvatar" ></input>\
					<input type="checkbox" id="'+STRING_VAF+'HasAvatar" name="'+STRING_VAF+'HasAvatar" checked="false"></input>\
					<label for="'+STRING_VAF+'HasAvatar">only with avatar</label>\
					<br />\
					<span id="'+STRING_VAF+'Buttons" style="display:inline;">\
					<input id="'+STRING_VAF+'ButtonGo" type="button" value="view&nbsp;all"></input>\
					<input id="'+STRING_VAF+'ButtonStop" type="button" value="&nbsp;stop&nbsp;"></input>\
					</span> <br />\
				<span id="'+STRING_VAF+'ShowCount">\
				</span> \
			</div> \
			</div>';
		
			
			if($(placeAfter).length > 0)
				$(placeAfter).after(BLOCK)
			else
				$('body').prepend(BLOCK);
			
			
}
function updateFilters()
{
	$('#'+STRING_VAF+'Container').find('select, input[type=text]').each(function(){

		var name = $(this).attr('id').replace(STRING_VAF,'')
		debug(name)
		if( typeof filters[name][2] != 'string')
		{
			var options = $(this).find('option:selected');
			
			filters[name][0] = false;
			filters[name][1] = [];
			$(options).each( function()
			{
					filters[name][0] = true;
					filters[name][1].push($(this).val());
			});
			if(filters[name][1].length == 1 && filters[name][1][0] == "" )
				filters[name][0] = false;
		}
		else
			if(filters[name][2] != $(this).val())
			{
				filters[name][0] = true;
				filters[name][1] = $(this).val();
			}
			else
			{
				filters[name][0] = false;
				filters[name][1] = filters[name][2];
			}
	});
};
/** due to recursive function*/
function seekingEnded()
{
	debug("total members: "+ members.length);
	showReFilter();
	showCount();
}
/*-----------------------------------*/
function setContainers(pageUsers){
	var parent;
	$(pageUsers).each(function(){
		parent = $(this).parent().get(0);
// 		debug("parent: "+$(parent).attr("class"))

		if($.inArray(parent, listContainers) == -1)
			listContainers.push(parent);
	});
}
function setNext(anchor)
{
	
	next = ( anchor.length > 0 )? 'https://fetlife.com' + anchor.attr('href'):'';
}
/*-----------------------------------*/
function filter(n)
{
	return (
		filter_role(n)
		&& filter_name(n)
		&& filter_location(n)
		&& filter_has_avatar(n)
		&& filter_gender(n)
		&& filter_role(n)
		&& filter_age_min(n)
		&& filter_age_max(n)
		&& filter_intoStatus(n)
		&& filter_intoActivity(n)
	);
}
function filter_has_avatar(n){
	debug(' '+n+' '+getCache(n)[6])
	debug("val() "+$('#'+STRING_VAF+'HasAvatar:checked'))

	if( $('#'+STRING_VAF+'HasAvatar:checked').length > 0 )
	{
		if( ! mCache[n][6]){
			debug('don\'t show')
			return false;
		}
		else
			debug('show')
	}
	else
		debug($('#'+STRING_VAF+'HasAvatar:checked').length > 0)

	return true;
}
function filter_name(n)
{
	if(filters['NameContains'][0])
		if( mCache[n][0].toLowerCase().indexOf(filters['NameContains'][1].toLowerCase()) < 0 )
			return false;
	return true;
}
/** TODO age */
function filter_age_min(n)
{
	if(filters['AgeMin'][0])
		if( mCache[n][1] <= parseInt(filters['AgeMin'][1]) )
			return false;
	return true;
}
function filter_age_max(n)
{
	if(filters['AgeMax'][0])
		if( mCache[n][1] >= parseInt(filters['AgeMax'][1]) )
			return false;
	return true;
}
function filter_role(n)
{
	if(filters['Role'][0])
		if($.inArray(mCache[n][3], filters['Role'][1]) < 0)
			return false;
	return true;
}
function filter_gender(n)
{
	if(filters['Gender'][0])
		if($.inArray(mCache[n][2], filters['Gender'][1]) < 0)
			return false;
	return true;
}
function filter_location(n)
{
	if(filters['LocContains'][0])
		if( mCache[n][4].toLowerCase().indexOf(filters['LocContains'][1].toLowerCase()) < 0 )
			return false;
	return true;
}
function filter_intoStatus(n)
{
	
	if(filters['IntoStatus'][0])
		if($.inArray(mCache[n][7], filters['IntoStatus'][1]) < 0)
			return false;
	return true;
}
function filter_intoActivity(n)
{
	if(filters['IntoActivity'][0])
		if($.inArray(mCache[n][8], filters['IntoActivity'][1]) < 0)
			return false;
	return true;
}
/*-----------------------------------*/
function lookPage()
{
//	var currentUsers = $(STRING_USERS);
//	var index = 0
//	currentUsers.each(function()
//	{
//		index = storeUser(this);
//		$(this).remove();
//		show(index);
//	});
}
function cleanPage(){
	$(STRING_USERS).remove();
	$("div.pagination").remove();
}
function lookAhead (firstPage)
{
	if( firstPage )
		next = location.href + ( location.search.toString().length > 0 ? "&page=1" : "?page=1" );
		
	if( next != '' || ! next.match(/\s*/) )
	{
		debug("next: "+next)
		if(! ajaxLocked){
			ajaxLocked = true;
			$.ajax({
				url: next,
				dataType: 'html',
				useCache: false,
				success: function(data)
				{
					setNext($(data).find('a.next_page'));
					$(data).find(STRING_USERS).each(function(){show(storeUser(this))});
					ajaxLocked = false;
 					lookAhead(false);
				}
			});
		}
		showCount()
	}
	else
	{
		/*seeking END (current and other pages), now all users are stored in members array*/
		seekingEnded()
	}
}
/*-----------------------------------*/

function showCount(){
	$('#'+STRING_VAF+'ShowCount').html("members: "+currentCount+" of "+members.length)
}
function show(n)
{
	if(filter(n))
	{
		$(listContainers[altPos]).append(members[n]);
		currentCount++;
		altPos = ( altPos == (listContainers.length -1) )? 0 : (altPos+1);
	}
}
/*-----------------------------------*/
function storeUser(user){
    var i = (members.push(user) - 1);
	var M = []; /* match: [whole, age (not null), gender, role ] */
	var firstSpan = $(user).find('div:eq(1) span:first');
	var C, into, regInto;
	var avatar = $(user).find('img.profile_avatar');
	
	mCache[i] = ['', 0, '', '', '', '', false, '', ''];
	C = mCache[i];
	/* name */
	C[0] = firstSpan.text();
	/** profile url */
	C[5] = firstSpan.find('a:first').attr('href');

	/** hasAvatar, need to be false if user has */
	C[6] = ( avatar.attr('src').indexOf('/images/avatar_missing') < 0 );

	M = $(user).find('div span:nth-child(2)').text().match(userRegExp);
// 	debug("match: "+(M[1]||"")+", "+(M[2]||"")+", "+(M[3]||""));
	/* age */
	C[1] = M[1];
	/* gender */
	C[2] = M[2];
	/* role */
	C[3] = M[3];
	
	/* location*/
	C[4] = $(user).find('div:eq(1) em').text()||'';
	
	/* into */
	if(is_fetishes_page)
	{
		M = []; /* match: ["into status", "rest aka into activity" ] */
		into = $(user).find('div:eq(1) span:eq(2)').text()||'';
		regInto = new RegExp('^('+ARRAY_INTO_STATUS.join('|')+') ?(.*$)?');
		
		M = into.match(regInto);
		if(M)
		{
			C[7] = M[1]||'';
			C[8] = M[2]||'';
		}
// 		debug("mCache[i][7] = "+C[7]+"  &&  mCache[i][8] = "+C[8])
	}
	debug(C);
	return i;
}
function getCache(n){
	return gC(n);
}
function gC(n){
	return mCache[n];
}
/*-----------------------------------*/
function showReFilter()
{
	$('#'+STRING_VAF+'Buttons').append(
		'<input id="'+STRING_VAF+'ButtonReFilter" type="button" value="filter&nbsp;again"></input>'
	)
	$('#'+STRING_VAF+'ButtonGo').remove();
	$('#'+STRING_VAF+'ButtonReFilter').click(function()
	{
		altPos = 0, currentCount = 0;
		window.scrollTo(0,0);
		updateFilters();
		debug(filters);
		$(STRING_USERS).remove();
		for(var i = 0; i < members.length; i++){
			show(i)
		}
		showCount();
	});
}
var count=0;

if(typeof $ == 'function')
	init();
else
{ setTimeout( 5000, function(){ if(typeof $ != 'function') alert('fetlife is modified, script '+ GM_info.script.name +'can\'t run please contact the author OR visit http://'+GM_info.script.namespace+'.')});}
/*-----------------------------------*/
})(jQuery)

jQuery.noConflict(true);