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.

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

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