Sleazy Fork is available in English.

fetlife_all_members

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

Versión del día 17/06/2014. Echa un vistazo a la versión más reciente.

  1. // ==UserScript==
  2. // @name fetlife_all_members
  3. // @namespace bewam.free.fr
  4. // @description greasemonkey script to find fetlife members when it's possible. Search by name, gender, role, age, location or status.
  5. // @include http*://fetlife.com/*
  6. // @version 1.8.2.20140617
  7. // @grant none
  8. // @run-at document-end
  9. // ==/UserScript==
  10. /*-----------------------------------*/
  11. const DEBUG = false;
  12. function debug() {
  13. if (DEBUG && console) {
  14. console.log.apply(this, arguments);
  15. }
  16. }
  17. /*-----------------------------------*/
  18. (function ($){
  19.  
  20. const STRING_USERS = 'div.user_in_list',
  21. STRING_VAF = 'viewAllForm',
  22. ARRAY_GENDER = ['M','F','CD/TV','MtF','FtM','TG','GF','GQ','IS','B','FEM'],
  23. ARRAY_GENDER_LABEL = ['Male','Female','CD/TV','Trans-MtF','Trans-FtM','Transgender','Gender Fluid','Genderqueer','Intersex','Butch','Femme'],
  24. ARRAY_ROLE = ['Dom','Domme','Switch','sub','Master','Mistress','slave','pet','kajira','kajirus','Top','Bottom','Sadist','Masochist','Sadomasochist','Ageplayer','Daddy','babygirl','brat','Primal','Fetishist','Kinkster','Hedonist','Vanilla','Unsure'],
  25. ARRAY_ROLE_LABEL = ARRAY_ROLE,
  26. ARRAY_INTO_STATUS = ['is into', 'is curious about'],
  27. ARRAY_INTO_ACTIVITY = ['giving', 'receiving', 'watching', 'wearing', 'watching others wear', 'everything to do with it']
  28. ;
  29.  
  30.  
  31.  
  32. var next = '';
  33. /* balance columns */
  34. var altPos = 0,
  35. currentCount = 0
  36. ;
  37. var members = [], // user html storage
  38. /** NOTE mCache = ARRAY( { i: { [0]'name':'', [1]'age':XX, [2]'gender':WW, [3]'role':'', [4]'location':'', [5]url:'', [6]"hasAvatar":boolean } }) */
  39. mCache = [],
  40. listContainers = [] // columns where lists appear
  41. ;
  42. var userRegExp = new RegExp('([0-9]{2})('+ARRAY_GENDER.join('|')+')? ('+ARRAY_ROLE.join('|')+')?','i');
  43.  
  44. /** modified, value, default value*/
  45. var filters = {
  46. 'NameContains': [false,'',''],
  47. 'AgeMin': [false,'',''],
  48. 'AgeMax': [false,'',''],
  49. 'Gender': [false, [], []],
  50. 'Role': [false, [], []],
  51. 'LocContains': [false,'',''],
  52. 'IntoStatus': [false,'',''],
  53. 'IntoActivity': [false,'','']
  54. // has_avatar: @see function
  55. }
  56. var ajaxLocked = false,
  57. scriptLaunched = false,
  58. is_fetishes_page = /^\/fetishes/.test(unsafeWindow.location.pathname)
  59. ;
  60. /*-----------------------------------*/
  61. function init()
  62. {
  63. var nextPage = $('a.next_page');
  64.  
  65. var pageUsers = $(STRING_USERS);
  66. /** a next_page link and a list of members are on current page ? go on */
  67. if( nextPage.length > 0 && pageUsers.length > 0 )
  68. {
  69. setContainers(pageUsers)
  70. debug("listContainers: "+listContainers);
  71.  
  72. drawBlock();
  73.  
  74. setNext(nextPage);
  75. $('#'+STRING_VAF+'ButtonGo').click(function()
  76. {
  77. $(this).attr("disabled",'true');
  78. if(!scriptLaunched)
  79. {
  80. $('#'+STRING_VAF+'ShowCount').html("loading ...")
  81. updateFilters();
  82. debug(filters);
  83. lookPage();
  84. lookAhead();
  85. scriptLaunched=true;
  86. }
  87. });
  88. }
  89. };
  90.  
  91. function drawBlock(){
  92.  
  93. var options_gender = '',
  94. options_role = '',
  95. options_into_activity = '',
  96. options_into_status = ''
  97. ;
  98. $.each(ARRAY_GENDER, function(i,v){options_gender+='<option value="'+v+'" >'+ARRAY_GENDER_LABEL[i]+'</option>'});
  99. $.each(ARRAY_ROLE, function(i,v){options_role+='<option value="'+v+'" >'+ARRAY_ROLE_LABEL[i]+'</option>'});
  100. $.each(ARRAY_INTO_ACTIVITY, function(i,v){options_into_activity+='<option value="'+v+'" >'+ARRAY_INTO_ACTIVITY[i]+'</option>'});
  101. $.each(ARRAY_INTO_STATUS, function(i,v){options_into_status+='<option value="'+v+'" >'+ARRAY_INTO_STATUS[i]+'</option>'});
  102. var BLOCK = '<div id="'+STRING_VAF+'Container" style="position:fixed;top:100px;z-index:1000000;background-color:rgba(255, 255, 255, 0.4);color:black !important;padding:5px;vertical-align:middle;"> \
  103. <center><u>view all members</u></center>\
  104. <u>filters</u> <br />\
  105. name &nbsp;&nbsp;&nbsp;&nbsp;\
  106. <input id="'+STRING_VAF+'NameContains" type="text" class="filter"></input> <br />\
  107. location \
  108. <input id="'+STRING_VAF+'LocContains" type="text" class="filter" ></input> <br />\
  109. \
  110. age <br />\
  111. min: <input id="'+STRING_VAF+'AgeMin" type="text" class="filter" size="2"></input>\
  112. &nbsp;&nbsp;&nbsp;&nbsp; \
  113. max: <input id="'+STRING_VAF+'AgeMax" type="text" class="filter" size="2"></input> <br />\
  114. \
  115. gender &nbsp;&nbsp;\
  116. <select id="'+STRING_VAF+'Gender" class="filter" name="gender" multiple="multiple" size="3">\
  117. <option value="" selected="selected">none specified</option>\
  118. '+options_gender+
  119. '</select> <br />\
  120. \
  121. Role &nbsp;&nbsp;&nbsp;&nbsp;\
  122. <select id="'+STRING_VAF+'Role" class="filter" name="role" multiple="multiple" size="5">\
  123. <option value="" selected="selected">none specified</option>\
  124. '+options_role+
  125. '</select>'
  126. ;
  127. if(is_fetishes_page)
  128. {
  129. BLOCK += '<br />\
  130. Into &nbsp;\
  131. <select id="'+STRING_VAF+'IntoStatus" multiple="multiple" size="3">\
  132. <option value="" selected="selected">All</option>\
  133. '+options_into_status+
  134. '</select>\
  135. <select id="'+STRING_VAF+'IntoActivity" multiple="multiple" size="3">\
  136. <option value="" selected="selected">All</option>\
  137. '+options_into_activity+
  138. '</select>\
  139. '
  140. ;
  141. }
  142. BLOCK += '<br />\
  143. <input type="checkbox" id="'+STRING_VAF+'HasAvatar"><label>\
  144. only with avatar\
  145. </label></input>\
  146. <center>\
  147. <span id="'+STRING_VAF+'Buttons" style="display:inline;">\
  148. <input id="'+STRING_VAF+'ButtonGo" type="button" value="view&nbsp;all"></input>\
  149. </span> <br />\
  150. <span id="'+STRING_VAF+'ShowCount">\
  151. </span> \
  152. </center>\
  153. </div>';
  154. $('body').prepend(BLOCK);
  155. }
  156. function updateFilters()
  157. {
  158. $('#'+STRING_VAF+'Container').find('select, input[type=text]').each(function(){
  159.  
  160. var name = $(this).attr('id').replace(STRING_VAF,'')
  161. debug(name)
  162. if( typeof filters[name][2] != 'string')
  163. {
  164. var options = $(this).find('option:selected');
  165. filters[name][0] = false;
  166. filters[name][1] = [];
  167. $(options).each( function()
  168. {
  169. filters[name][0] = true;
  170. filters[name][1].push($(this).val());
  171. });
  172. if(filters[name][1].length == 1 && filters[name][1][0] == "" )
  173. filters[name][0] = false;
  174. }
  175. else
  176. if(filters[name][2] != $(this).val())
  177. {
  178. filters[name][0] = true;
  179. filters[name][1] = $(this).val();
  180. }
  181. else
  182. {
  183. filters[name][0] = false;
  184. filters[name][1] = filters[name][2];
  185. }
  186. });
  187. };
  188. /** due to recursive function*/
  189. function seekingEnded()
  190. {
  191. debug("total members: "+ members.length);
  192. showReFilter();
  193. showCount();
  194. }
  195. /*-----------------------------------*/
  196. function setContainers(pageUsers){
  197. var parent;
  198. $(pageUsers).each(function(){
  199. parent = $(this).parent().get(0);
  200. // debug("parent: "+$(parent).attr("class"))
  201.  
  202. if($.inArray(parent, listContainers) == -1)
  203. listContainers.push(parent);
  204. });
  205. }
  206. function setNext(anchor)
  207. {
  208. next = ( anchor.length > 0 )? 'https://fetlife.com' + anchor.attr('href'):'';
  209. }
  210. /*-----------------------------------*/
  211. function filter(n)
  212. {
  213. return (
  214. filter_role(n)
  215. && filter_name(n)
  216. && filter_location(n)
  217. && filter_has_avatar(n)
  218. && filter_gender(n)
  219. && filter_role(n)
  220. && filter_age_min(n)
  221. && filter_age_max(n)
  222. && filter_intoStatus(n)
  223. && filter_intoActivity(n)
  224. );
  225. }
  226. function filter_has_avatar(n){
  227. if($('#'+STRING_VAF+'HasAvatar').attr('checked') && ! mCache[n][6] )
  228. return false;
  229. return true;
  230. }
  231. function filter_name(n)
  232. {
  233. if(filters['NameContains'][0])
  234. if( mCache[n][0].toLowerCase().indexOf(filters['NameContains'][1].toLowerCase()) < 0 )
  235. return false;
  236. return true;
  237. }
  238. /** TODO age */
  239. function filter_age_min(n)
  240. {
  241. if(filters['AgeMin'][0])
  242. if( mCache[n][1] <= parseInt(filters['AgeMin'][1]) )
  243. return false;
  244. return true;
  245. }
  246. function filter_age_max(n)
  247. {
  248. if(filters['AgeMax'][0])
  249. if( mCache[n][1] >= parseInt(filters['AgeMax'][1]) )
  250. return false;
  251. return true;
  252. }
  253. function filter_role(n)
  254. {
  255. if(filters['Role'][0])
  256. if($.inArray(mCache[n][3], filters['Role'][1]) < 0)
  257. return false;
  258. return true;
  259. }
  260. function filter_gender(n)
  261. {
  262. if(filters['Gender'][0])
  263. if($.inArray(mCache[n][2], filters['Gender'][1]) < 0)
  264. return false;
  265. return true;
  266. }
  267. function filter_location(n)
  268. {
  269. if(filters['LocContains'][0])
  270. if( mCache[n][4].toLowerCase().indexOf(filters['LocContains'][1].toLowerCase()) < 0 )
  271. return false;
  272. return true;
  273. }
  274. function filter_intoStatus(n)
  275. {
  276. if(filters['IntoStatus'][0])
  277. if($.inArray(mCache[n][7], filters['IntoStatus'][1]) < 0)
  278. return false;
  279. return true;
  280. }
  281. function filter_intoActivity(n)
  282. {
  283. if(filters['IntoActivity'][0])
  284. if($.inArray(mCache[n][8], filters['IntoActivity'][1]) < 0)
  285. return false;
  286. return true;
  287. }
  288. /*-----------------------------------*/
  289. function lookPage()
  290. {
  291. var currentUsers = $(STRING_USERS);
  292. var index = 0
  293. currentUsers.each(function()
  294. {
  295. index = storeUser(this);
  296. $(this).remove();
  297. show(index);
  298. });
  299. }
  300. function lookAhead ()
  301. {
  302. if( next != '' || ! next.match(/\s*/) )
  303. {
  304. debug("next: "+next)
  305. if(! ajaxLocked){
  306. ajaxLocked = true;
  307. $.ajax({
  308. url: next,
  309. dataType: 'html',
  310. useCache: false,
  311. success: function(data)
  312. {
  313. setNext($(data).find('a.next_page'));
  314. $(data).find(STRING_USERS).each(function(){show(storeUser(this))});
  315. ajaxLocked = false;
  316. lookAhead();
  317. }
  318. });
  319. }
  320. showCount()
  321. }
  322. else
  323. {
  324. /*seeking END (current and other pages), now all users are stored in members array*/
  325. seekingEnded()
  326. }
  327. }
  328. /*-----------------------------------*/
  329.  
  330. function showCount(){
  331. $('#'+STRING_VAF+'ShowCount').html("members: "+currentCount+" of "+members.length)
  332. }
  333. function show(n)
  334. {
  335. if(filter(n))
  336. {
  337. $(listContainers[altPos]).append(members[n]);
  338. currentCount++;
  339. altPos = ( altPos == (listContainers.length -1) )? 0 : (altPos+1);
  340. }
  341. }
  342. /*-----------------------------------*/
  343. function storeUser(user){
  344. var i = (members.push(user) - 1);
  345. var M = []; /* match: [whole, age (not null), gender, role ] */
  346. var firstSpan = $(user).find('div:eq(1) span:first');
  347. var into, regInto;
  348.  
  349. mCache[i] = ['',0,'','','','',false,'', ''];
  350.  
  351.  
  352. /* name */
  353. mCache[i][0] = firstSpan.text();
  354. /** profile url */
  355. mCache[i][5] = firstSpan.find('a:first').attr('href');
  356.  
  357. /** hasAvatar */
  358. mCache[i][6] = ( $(user).find('div:first a:first img').attr('src').indexOf('/images/avatar_missing') == -1 );
  359.  
  360. M = $(user).find('div span:nth-child(2)').text().match(userRegExp);
  361. // debug("match: "+(M[1]||"")+", "+(M[2]||"")+", "+(M[3]||""));
  362. /* age */
  363. mCache[i][1] = M[1];
  364. /* gender */
  365. mCache[i][2] = M[2];
  366. /* role */
  367. mCache[i][3] = M[3];
  368. /* location*/
  369. mCache[i][4] = $(user).find('div:eq(1) em').text()||'';
  370. /* into */
  371. if(is_fetishes_page)
  372. {
  373. M = []; /* match: ["into status", "rest aka into activity" ] */
  374. into = $(user).find('div:eq(1) span:eq(2)').text()||'';
  375. regInto = new RegExp('^('+ARRAY_INTO_STATUS.join('|')+') ?(.*$)?');
  376. M = into.match(regInto);
  377. if(M)
  378. {
  379. mCache[i][7] = M[1]||'';
  380. mCache[i][8] = M[2]||'';
  381. }
  382. debug("mCache[i][7] = "+mCache[i][7]+" && mCache[i][8] = "+mCache[i][8])
  383. }
  384. return i;
  385. }
  386. /*-----------------------------------*/
  387. function showReFilter()
  388. {
  389. $('#'+STRING_VAF+'Buttons').append(
  390. '<input id="'+STRING_VAF+'ButtonReFilter" type="button" value="filter&nbsp;again"></input>'
  391. )
  392. $('#'+STRING_VAF+'ButtonGo').remove();
  393. $('#'+STRING_VAF+'ButtonReFilter').click(function()
  394. {
  395. altPos = 0, currentCount = 0;
  396. window.scrollTo(0,0);
  397. updateFilters();
  398. debug(filters);
  399. $(STRING_USERS).remove();
  400. for(var i = 0; i < members.length; i++){
  401. show(i)
  402. }
  403. showCount();
  404. });
  405. }
  406. var count=0;
  407.  
  408. if(typeof $ == 'function')
  409. init();
  410. else
  411. { 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+'.')});}
  412. /*-----------------------------------*/
  413. })(unsafeWindow.jQuery)
  414.  
  415.