// ==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.20150223
// @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);
}
}
/*-----------------------------------*/
var useCurrentPageAsDefault = false;
(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 = '';
var lastPageNumber,
currentPageNumber,
InputcurrentPageDefaultVal
;
/* 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 )
{
currentPageNumber = parseInt($('em.current').text());
setContainers(pageUsers)
debug("listContainers: "+listContainers);
debug($('em.current').text()+' currentPageNumber '+currentPageNumber );
drawBlock();
$('#'+STRING_VAF+'FromPage').val(useCurrentPageAsDefault ? currentPageNumber:1);
lastPageNumber = parseInt($(nextPage).prev().text());
$('#'+STRING_VAF+'ToPage').val(lastPageNumber);
debug("lastPageNumber: " + lastPageNumber );
$(Ctrls).click(function()
{
$('#'+STRING_VAF+'Content').toggle("slow")
console.log(this)
});
$('#'+STRING_VAF+'ButtonCurrentPage').click( function()
{
$('#'+STRING_VAF+'FromPage').val(currentPageNumber);
});
setNext(nextPage);
$('#'+STRING_VAF+'ButtonGo').click( function()
{
$(this).attr("disabled", 'true');
launchSearch();
});
}
};
function launchSearch()
{
var FromPage = parseInt($('#'+STRING_VAF+'FromPage').val());
debug('from page: '+FromPage);
if( ! scriptLaunched )
{
$('#'+STRING_VAF+'ShowCount').html("loading ...")
updateFilters();
debug(filters);
// TODO delete it when "?page=1"
// lookPage();
$('#'+STRING_VAF+'ButtonCurrentPage').attr("disabled", 'true');
$('#'+STRING_VAF+'FromPage').attr("disabled", 'true');
cleanPage();
lookAhead(FromPage);
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>></b> \
<u>view all members</u>\
</div>\
<div id="'+STRING_VAF+'Content"> \
<label for="'+STRING_VAF+'NameContains">name:</label>\
<input id="'+STRING_VAF+'NameContains" type="text" class="filter"></input>\
<br />\
<label for="'+STRING_VAF+'LocContains">location:</label>\
<input id="'+STRING_VAF+'LocContains" type="text" class="filter" ></input>\
\
<br />\
<u>age</u> \
<label for="'+STRING_VAF+'AgeMin">min:</label>\
<input id="'+STRING_VAF+'AgeMin" type="text" class="filter" size="2"></input>\
\
<label for="'+STRING_VAF+'AgeMax">max:</label>\
<input id="'+STRING_VAF+'AgeMax" type="text" class="filter" size="2"></input>\
<br />\
<label for="'+STRING_VAF+'Gender">gender:</label>\
<select id="'+STRING_VAF+'Gender" class="filter" name="gender" multiple="multiple" size="3">\
<option value="" selected="selected">none specified</option>\
'+options_gender+
'</select>\
\
<label for="'+STRING_VAF+'Role">role:</label>\
<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 +=
' Into \
<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 />\
<label for="'+STRING_VAF+'FromPage">From page:</label>\
<input id="'+STRING_VAF+'FromPage" type="text" class="filter" size="3" value="'+InputcurrentPageDefaultVal+'"></input>\
<input id="'+STRING_VAF+'ButtonCurrentPage" type="button" value="current"></input>'+
// '\
// <label for="'+STRING_VAF+'ToPage">to page:</label>\
// <input id="'+STRING_VAF+'ToPage" type="text" class="filter" size="3"></input>\
// '+
'<br />\
<span id="'+STRING_VAF+'Buttons" style="display:inline;">\
<input id="'+STRING_VAF+'ButtonGo" type="button" value="view all"></input>\
<input id="'+STRING_VAF+'ButtonStop" type="button" value=" stop "></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(! filters[name])
return;
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)
{
var myPage = 1;
if( firstPage )
{
if( isInt(parseInt(firstPage)) )
myPage = firstPage;
next = location.href + ( location.search.toString().length > 0 ? "&page="+myPage : "?page=" +myPage);
}
debug("next: "+next);
if( next != '' || ! next.match(/\s*/) /* || currentPageNumber != lastPageNumber */ )
{
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];
}
/*--------------helpers---------------*/
function isInt(n){
return( ! isNaN(n) );
}
/*-----------------------------------*/
function showReFilter()
{
$('#'+STRING_VAF+'Buttons').append(
'<input id="'+STRING_VAF+'ButtonReFilter" type="button" value="filter 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);