fetlife_all_members (ASL+role+status filter)

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

Tính đến 08-07-2015. Xem phiên bản mới nhất.

  1. // ==UserScript==
  2. // @name fetlife_all_members (ASL+role+status filter)
  3. // @namespace io.github.bewam
  4. // @description greasemonkey script to filter fetlife members when it's possible. Search by name, gender, role, age, location or status.
  5. // @include http*://fetlife.com/*
  6. // @version 1.9.1.20150708
  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. // NOTE: comment (change first "/**/" to "/*") for debugging.
  13. /**/ console.log = () => {}; /**/
  14.  
  15. var useCurrentPageAsDefault = false;
  16. var onlyWithAvatar = true;
  17. /* care modifing
  18. * TODO : to be removed: https://greasyfork.org/fr/forum/discussion/4199/lock-a-script#latest
  19. */
  20. const FETCH_LATENCY = 500;
  21.  
  22. (function ($) {
  23. // jquery str
  24. const Selector = {
  25. currentPage: 'em.current',
  26. body: 'body',
  27. users: 'div.user_in_list',
  28. pagination: "div.pagination",
  29. nextPage: 'a.next_page',
  30. nextDisabled: 'span.next_page.disabled',
  31. previousPage: 'a.previous_page',
  32. firstUser: '.user_in_list:first',
  33. user: {
  34. imageAvatar: 'img.profile_avatar',
  35. firstSpan: 'div:eq(1) span:first',
  36. profileLink: 'a:first',
  37. shortDesc: 'div span:nth-child(2)',
  38. /** age, gender, role */
  39. location: 'div:eq(1) em',
  40. into: 'div:eq(1) span:eq(2)',
  41. }
  42. };
  43. // script_name avoid name/id conflicts
  44. const _STR = 'fetlife_all_members';
  45. const ARRAY_GENDER = [
  46. 'M',
  47. 'F',
  48. 'CD/TV',
  49. 'MtF',
  50. 'FtM',
  51. 'TG',
  52. 'GF',
  53. 'GQ',
  54. 'IS',
  55. 'B',
  56. 'FEM'
  57. ];
  58. const ARRAY_GENDER_LABEL = [
  59. 'Male',
  60. 'Female',
  61. 'CD/TV',
  62. 'Trans-MtF', 'Trans-FtM',
  63. 'Transgender',
  64. 'Gender Fluid',
  65. 'Genderqueer',
  66. 'Intersex',
  67. 'Butch',
  68. 'Femme'
  69. ];
  70. const ARRAY_ROLE = [
  71. 'Dom',
  72. 'Domme',
  73. 'Switch',
  74. 'sub',
  75. 'Master',
  76. 'Mistress',
  77. 'slave',
  78. 'kajira',
  79. 'kajirus',
  80. 'Top',
  81. 'bottom',
  82. 'Sadist',
  83. 'Masochist',
  84. 'Sadomasochist',
  85. 'Kinkster',
  86. 'Fetishist',
  87. 'Swinger',
  88. 'Hedonist',
  89. 'Exhibitionist',
  90. 'Voyeur',
  91. 'Sensualist',
  92. 'Princess',
  93. 'Slut',
  94. 'Doll',
  95. 'sissy',
  96. 'Rigger',
  97. 'Rope Top',
  98. 'Rope Bottom',
  99. 'Rope Bunny',
  100. 'Spanko',
  101. 'Spanker',
  102. 'Spankee',
  103. 'Furry',
  104. 'Leather Man',
  105. 'Leather Woman',
  106. 'Leather Daddy',
  107. 'Leather Top',
  108. 'Leather bottom',
  109. 'Leather boy',
  110. 'Leather girl',
  111. 'Leather Boi',
  112. 'Bootblack',
  113. 'Primal',
  114. 'Primal Predator',
  115. 'Primal Prey',
  116. 'Bull',
  117. 'cuckold',
  118. 'cuckquean',
  119. 'Ageplayer',
  120. 'Daddy',
  121. 'Mommy',
  122. 'Big',
  123. 'Middle',
  124. 'little',
  125. 'brat',
  126. 'babygirl',
  127. 'babyboy',
  128. 'pet',
  129. 'kitten',
  130. 'pup',
  131. 'pony',
  132. 'Evolving',
  133. 'Exploring',
  134. 'Vanilla',
  135. 'Undecided'
  136. ];
  137. const ARRAY_ROLE_LABEL = ARRAY_ROLE;
  138. const ARRAY_INTO_STATUS = [
  139. 'is into',
  140. 'is curious about'
  141. ];
  142. const ARRAY_INTO_ACTIVITY = [
  143. 'giving',
  144. 'receiving',
  145. 'watching',
  146. 'wearing',
  147. 'watching others wear',
  148. 'everything to do with it'
  149. ];
  150.  
  151. const marker = {
  152. folded: '>',
  153. unfolded: 'v'
  154. };
  155. const isFetishesPage = /^\/fetishes/.test(location.pathname);
  156.  
  157. // NOTE: do not modify unless you know what you're doing.
  158. var overlay = (form) =>
  159. $(Selector.firstUser).parents('.clearfix:first').prepend(form).length;
  160.  
  161. /** TODO @class to manage pages */
  162. // Pagination = {
  163. // _next: '',
  164. // _previous: '',
  165. // _currentNo: '',
  166. // _clientPageNo,
  167. // setNext: setNext,
  168. // getNext: getNext,
  169. // getPrevious :() => this._previous,
  170. // setPrevious : (str) => {this._previous = str;},
  171. // getCurrentNo: () => this._currentNo ,
  172. // setCurrentNo: (n) => {this._currentNo = n;},
  173. // };
  174. // var page = Object.create(Pagination);
  175.  
  176. var lastPageNumber,
  177. _pageCurrentNo,
  178. _clientPageNo,
  179. InputcurrentPageDefaultVal,
  180. _fromPage = -1,
  181. _toPage = -1,
  182. pageMin = 1,
  183. pageMax = 1,
  184. _pageNext = '';
  185.  
  186. /* balance columns */
  187. var alternColumn = 0,
  188. _shownCount = 0;
  189.  
  190. /* store user html block index is shared with mCache */
  191. /* todo */
  192. var members = [],
  193. /** NOTE mCache = ARRAY( { n° index: { [0]'name':'', [1]'age':XX, [2]'gender':WW, [3]'role':'', [4]'location':'', [5]url:'', [6]"hasAvatar":boolean } }) */
  194. mCache = [],
  195. listContainers = [] // columns where lists appear
  196. ;
  197. //TODO Expressions =
  198. var userRegExp = new RegExp('([0-9]{2})(' + ARRAY_GENDER.join('|') +
  199. ')? (' +
  200. ARRAY_ROLE.join('|') + ')?', 'i');
  201. var regInto = new RegExp('^(' + ARRAY_INTO_STATUS.join('|') +
  202. ') ?(.*$)?');
  203.  
  204. /** modified, value, default value */
  205. var filters = {
  206. 'NameContains': [false, '', ''],
  207. 'AgeMin': [false, '', ''],
  208. 'AgeMax': [false, '', ''],
  209. 'Gender': [false, [], []],
  210. 'Role': [false, [], []],
  211. 'LocContains': [false, '', ''],
  212. 'IntoStatus': [false, '', ''],
  213. 'IntoActivity': [false, '', '']
  214. // has_avatar: @see function
  215. };
  216.  
  217. var ajaxLocked = false,
  218. scriptLaunched = false,
  219. stopped = false;
  220.  
  221. /* jshint ignore:start */
  222. GM_addStyle(
  223. '#' + S('Wrapper') + ' { ' +
  224. // 'background-color: rgba(255, 255, 255, 0.4);' +
  225. 'border: solid 2px lightgray;' +
  226. 'color: white !important;/**/ ' +
  227. 'padding:5px;' +
  228. 'min-height:15px !important;' +
  229. 'vertical-align:middle;' +
  230. 'margin-bottom: 10px;' +
  231. '} ' +
  232. '#' + S('Controls') + ' { ' +
  233. 'display: block;/**/ ' +
  234. 'min-height:15px !important;' +
  235. '}' +
  236. '#' + S('Content') + ' { ' +
  237. 'display: none;/**/ ' +
  238. 'margin-top: 5px;' +
  239. '}' +
  240. '#' + S('Buttons') + '{ ' +
  241. 'margin-top: 10px;' +
  242. '}' +
  243. '#' + S('ButtonStop') + '{ ' +
  244. // 'display: none;' +
  245. '}' +
  246. ''
  247. );
  248. /* jshint ignore:end */
  249.  
  250. /*-----------------------------------*/
  251.  
  252. /*----------------html related--------------------------*/
  253. function buildOptions(arr1, arr2) {
  254. var str = '';
  255. $.each(arr1, function (i, v) {
  256. str += '<option value="' + v + '" >' + arr2[i] +
  257. ' </option>';
  258. });
  259. return str;
  260. }
  261.  
  262. function drawBlock() {
  263. var optionsGender = buildOptions(ARRAY_GENDER, ARRAY_GENDER_LABEL);
  264. var optionsRole = buildOptions(ARRAY_ROLE, ARRAY_ROLE_LABEL);
  265. var optionsIntoStatus = buildOptions(ARRAY_INTO_STATUS,
  266. ARRAY_INTO_STATUS);
  267. var optionsIntoActivity = buildOptions(ARRAY_INTO_ACTIVITY,
  268. ARRAY_INTO_ACTIVITY);
  269.  
  270. var BLOCK =
  271. '<div id="' + S('Wrapper') + '"> ' +
  272. ' <div id="' + S('Controls') + '">' +
  273. ' <b>&gt;</b>&nbsp;' +
  274. ' <u>filter members</u>' +
  275. ' </div>' +
  276. ' <div id="' + S('Content') + '"> ' +
  277. ' <div id="' + S('Filters') + '">' +
  278. ' <label for="' + S('NameContains') +
  279. '">name:&nbsp;&nbsp;&nbsp; </label>' +
  280. ' <input id="' + S('NameContains') +
  281. '" type="text" class="filter"></input>' +
  282. ' <br />' +
  283. ' <label for="' + S('LocContains') +
  284. '">location:</label>' +
  285. ' <input id="' + S('LocContains') +
  286. '" type="text" class="filter" ></input>' +
  287. ' <br />' +
  288. ' <u>age</u>&nbsp;' +
  289. ' <label for="' + S('AgeMin') + '">min:</label>' +
  290. ' <input id="' + S('AgeMin') +
  291. '" type="text" class="filter" size="2"></input>' +
  292. '&nbsp; ' +
  293. ' <label for="' + S('AgeMax') + '">max:</label>' +
  294. ' <input id="' + S('AgeMax') +
  295. '" type="text" class="filter" size="2"></input>' +
  296. ' <br />' +
  297. ' <small>' +
  298. ' Use CTRL key to multiple select.' +
  299. ' <br />' +
  300. ' Use MAJ key to do a range select.' +
  301. ' </small>' +
  302. ' <br />' +
  303. ' <label for="' + S('Gender') + '">gender:</label>' +
  304. ' <select id="' + S('Gender') +
  305. '" class="filter" name="gender" multiple="multiple" size="3">' +
  306. ' <option value="" selected="selected">none specified</option>' +
  307. optionsGender +
  308. ' </select>' +
  309. ' <label for="' + S('Role') + '">role:</label>' +
  310. ' <select id="' + S('Role') +
  311. '" class="filter" name="role" multiple="multiple" size="5">' +
  312. ' <option value="" selected="selected">none specified</option>' +
  313. optionsRole +
  314. ' </select>';
  315.  
  316. if(isFetishesPage) {
  317. BLOCK +=
  318. '&nbsp; Into &nbsp;' +
  319. ' <select id="' + S('IntoStatus') +
  320. '" multiple="multiple" size="3">' +
  321. ' <option value="" selected="selected">All</option>' +
  322. optionsIntoStatus +
  323. ' </select>' +
  324. ' <select id="' + S('IntoActivity') +
  325. '" multiple="multiple" size="3">' +
  326. ' <option value="" selected="selected">All</option>' +
  327. optionsIntoActivity +
  328. ' </select>';
  329. }
  330.  
  331. BLOCK +=
  332. ' <br />' +
  333. // ' <input type="hidden" name="' + S('HasAvatar') +
  334. // '" ></input>' +
  335. ' <input type="checkbox" id="' + S('HasAvatar') +
  336. '" name="' + S(
  337. 'HasAvatar') + '" checked="' + (onlyWithAvatar ? 'true' :
  338. 'false') + '"></input>' +
  339. ' <label for="' + S('HasAvatar') +
  340. '">only with avatar</label>' +
  341. ' <br />' +
  342. ' <label for="' + S('FromPage') +
  343. '">From page:&nbsp;</label>' +
  344. ' <input id="' + S('FromPage') +
  345. '" type="text" class="filter" size="3" value="' +
  346. InputcurrentPageDefaultVal + '"></input>' +
  347. ' <input id="' + S('ButtonCurrentPage') +
  348. '" type="button" value="current"></input>' +
  349. ' <label for="' + S('ToPage') +
  350. '"> &nbsp;to page:&nbsp;</label>' +
  351. ' <input id="' + S('ToPage') + '" name="' + S('ToPage') +
  352. '" type="text" class="filter" size="3"></input>' +
  353. ' <br />' +
  354. ' <br />' +
  355. ' </div> ' + // Filters
  356. ' <div id="' + S('Buttons') + '" style="display:inline;">' +
  357. ' <input id="' + S('ButtonGo') +
  358. '" type="button" value="view&nbsp;all"></input>' +
  359. ' <input id="' + S('ButtonStop') +
  360. '" type="button" value="&nbsp;stop&nbsp;" disabled="true"></input>' +
  361. ' </div>' + // Buttons
  362. ' <div id="' + S('Info') + '">' +
  363. ' <br />' +
  364. ' <span id="' + S('ShowCount') + '">' +
  365. ' </span> ' +
  366. ' </div> ' + // Info
  367. ' </div> ' + //Content
  368. '</div>'; // Wrapper
  369. if(overlay(BLOCK) < 1) {
  370. $(Selector.body).prepend(BLOCK);
  371. }
  372. }
  373.  
  374. function disableAllInput() {
  375. // TODO @class Form. Form.disableEntries() => void()
  376. $I('Content').find('*').attr("disabled", 'true');
  377. $I('ButtonStop').removeAttr("disabled");
  378. }
  379.  
  380. function enableAllInput() {
  381. $I('Content').find('*').removeAttr("disabled");
  382. $I('ButtonStop').attr("disabled", 'true');
  383. }
  384.  
  385. function disableInput() {
  386. // TODO @function Form.disableEntries() => void()
  387. $I('Content').find('*').attr("disabled", 'true');
  388. }
  389.  
  390. function enableInput() {
  391. $I('Content').find('*').removeAttr("disabled");
  392. }
  393. /**
  394. * @param str id
  395. * @return object jQuery with the tagged (script name) id
  396. */
  397. function $I(id) {
  398. var str = arguments.length > 1 ? arguments[1] : '';
  399. return $('#' + S(id) + str);
  400. }
  401.  
  402. function S(name) {
  403. return _STR + name;
  404. }
  405.  
  406. function rS(str) {
  407. return str.replace(_STR, '');
  408. }
  409.  
  410. function cleanPage() {
  411. $(Selector.users).remove();
  412. $(Selector.pagination).hide();
  413. _shownCount = 0;
  414. }
  415.  
  416. function toggleMarker($el) {
  417. var cnt = $el.html();
  418. $el.html(cnt == marker.unfolded ? marker.folded : marker.unfolded);
  419. }
  420. /*-----------------------------------*/
  421. /** Would grab 2 containers where to put members in */
  422. function initContainers(pageUsers) {
  423. var parent;
  424. $(pageUsers).each(function () {
  425. parent = $(this).parent().get(0);
  426. console.log("container: " + $(parent).attr("class"));
  427.  
  428. if($.inArray(parent, listContainers) == -1) {
  429. listContainers.push(parent);
  430. }
  431. });
  432. }
  433. /*------------------------------------*/
  434. function updateMemberFilters() {
  435. $I('Content').find('select, input[type=text]').each(function () {
  436.  
  437. var name = rS($(this).attr('id'));
  438. console.log('updating filter: ' + name);
  439. if(!filters[name]) {
  440. return;
  441. }
  442. if(typeof filters[name][2] != 'string') {
  443. var options = $(this).find('option:selected');
  444. filters[name][0] = false;
  445. filters[name][1] = [];
  446. $(options).each(function () {
  447. filters[name][0] = true;
  448. filters[name][1].push($(this).val());
  449. });
  450. if(filters[name][1].length == 1 && filters[name][1]
  451. [0] === '') {
  452. filters[name][0] = false;
  453. }
  454. }
  455. else
  456. if(filters[name][2] != $(this).val()) {
  457. filters[name][0] = true;
  458. filters[name][1] = $(this).val();
  459. }
  460. else {
  461. filters[name][0] = false;
  462. filters[name][1] = filters[name][2];
  463. }
  464. });
  465. console.log(filters);
  466. }
  467.  
  468. function updateAvatarFilter() {
  469. onlyWithAvatar = ($I('HasAvatar', ':checked').length > 0);
  470. }
  471.  
  472. function updatePaginationFilters() {
  473. var reload = false;
  474. var $fromPage = parseInt($I('FromPage').val());
  475. var $toPage = parseInt($I('ToPage').val());
  476.  
  477.  
  478. if(_fromPage != $fromPage) {
  479. _fromPage = $fromPage;
  480. reload = true;
  481. }
  482.  
  483. if(_toPage != $toPage) {
  484. _toPage = $toPage;
  485. reload = true;
  486. }
  487.  
  488. console.log(' _fromPage ' + _fromPage + ' $fromPage ' + $fromPage);
  489. console.log(' _toPage ' + _toPage + ' $toPage ' + $toPage);
  490.  
  491. return reload;
  492. }
  493. /*-----------------------------------*/
  494. var Refining = function (cacheItem) {
  495.  
  496. var c = cacheItem;
  497. var filtered = false;
  498.  
  499. /**
  500. * @return bool false by default, element is not trapped */
  501. this.isFiltered = function () {
  502. // NOTE for debug purpose uncomment the second block and / comment this one.
  503. // TODO how secure is eval() ?
  504. /**
  505. role();
  506. name();
  507. location();
  508. hasAvatar();
  509. gender();
  510. ageMin();
  511. ageMax();
  512. intoStatus();
  513. intoActivity();
  514. /**/
  515.  
  516. /**/
  517. var functions = [
  518. "ageMax",
  519. "ageMin",
  520. "gender",
  521. "hasAvatar",
  522. "intoActivity",
  523. "intoStatus",
  524. "location",
  525. "name",
  526. "role"
  527. ];
  528. for(var fn of functions) {
  529. // jshint ignore : start
  530. eval(fn + '()');
  531. // jshint ignore : end
  532. if(getF()) {
  533. console.log('is filtered: ' + fn.toString());
  534. console.log(c);
  535. // stop filtering here
  536. return true;
  537. }
  538. }
  539. /**/
  540. return filtered;
  541. };
  542. /**
  543. * @arg boolean filtered ?
  544. * @return void(0)
  545. */
  546. function setF(F) {
  547. if(F) {
  548. filtered = true;
  549. }
  550. }
  551.  
  552. function getF() {
  553. return filtered;
  554. }
  555.  
  556. /** NOTE Each filter (except hasAvatar) check if filter was modified
  557. (updateMemberFilters) and look if the changes concern the current
  558. user. Filter must set false if ok.
  559. */
  560.  
  561. function ageMax() {
  562. if(filters.AgeMax[0]) {
  563. setF(c[1] > parseInt(filters.AgeMax[1]));
  564. }
  565. }
  566.  
  567. function ageMin() {
  568. if(filters.AgeMin[0]) {
  569. setF(c[1] < parseInt(filters.AgeMin[1]));
  570. }
  571. }
  572.  
  573. function gender() {
  574. if(filters.Gender[0]) {
  575. setF($.inArray(c[2], filters.Gender[1]) < 0);
  576. }
  577. }
  578.  
  579. function hasAvatar() {
  580. // console.log('avatar ' + onlyWithAvatar.toString());
  581. setF((onlyWithAvatar && !c[6]) ? true : false);
  582. }
  583.  
  584. function intoActivity() {
  585. if(filters.IntoActivity[0]) {
  586. setF($.inArray(c[8], filters.IntoActivity[1]) < 0);
  587. }
  588. }
  589.  
  590. function intoStatus() {
  591. if(filters.IntoStatus[0]) {
  592. setF($.inArray(c[7], filters.IntoStatus[1]) < 0);
  593. }
  594. }
  595.  
  596. function location() {
  597. if(filters.LocContains[0]) {
  598. setF(
  599. c[4].toLowerCase().indexOf(
  600. filters.LocContains[1].toLowerCase()
  601. ) < 0
  602. );
  603. }
  604. }
  605.  
  606. function name() {
  607. if(filters.NameContains[0]) {
  608. setF(
  609. c[0].toLowerCase().indexOf(
  610. filters.NameContains[1].toLowerCase()
  611. ) < 0
  612. );
  613. }
  614. }
  615.  
  616. function role() {
  617. if(filters.Role[0]) {
  618. setF($.inArray(c[3], filters.Role[1]) < 0);
  619. }
  620. }
  621. }; // Filter
  622.  
  623. /** filter each user */
  624. function filterUser(n) {
  625. // console.log('filtering n°' + n);
  626. var o;
  627. var filter = new Refining(mCache[n]);
  628.  
  629. return(filter.isFiltered());
  630.  
  631. }
  632. /*-----------Ajax & pagination -----------------*/
  633. /** @param mixed (str or jQ.), a link "next" in pagination */
  634. function setNext(mix) {
  635. console.log('setNext: ');
  636. console.log(mix);
  637. var next = '';
  638. if(mix.nodeName === 'A') {
  639. next = mix.href;
  640. }
  641. else if(typeof mix == 'string') {
  642. next = mix;
  643. }
  644. else if(
  645. (typeof mix == 'function' ||
  646. typeof mix == 'object') &&
  647. $(mix).is('A')
  648. ) {
  649. next = mix.attr('href');
  650. }
  651. _pageNext = next;
  652. return(next);
  653. }
  654.  
  655. function getNext() {
  656. console.log(_pageNext);
  657. if(_pageNext === '' || _pageNext.match(/^\s*$/)) {
  658. return '';
  659. }
  660. if(_pageNext.indexOf("fetlife.com") < 0) {
  661. return 'https://fetlife.com/' + _pageNext;
  662. }
  663. else {
  664. return _pageNext;
  665. }
  666. }
  667.  
  668. function getCurrentPageNo() {
  669. return parseInt($(Selector.currentPage).text());
  670. }
  671.  
  672. function getLastPageNum() {
  673. var $e = $(Selector.nextPage);
  674. if($e.length > 0) {
  675. return parseInt($e.prev().text());
  676. }
  677. $e = $(Selector.nextDisabled);
  678. if($e.length > 0) {
  679. return parseInt($e.prev().text());
  680. }
  681. return -1;
  682. }
  683.  
  684. function isLastFetchedPage(next) {
  685. var p = next.lastIndexOf('?');
  686. var search;
  687. var S;
  688. if(p > -1) {
  689. // url.search W/o leading ?
  690. search = next.substr(p + 1);
  691. }
  692. else {
  693. // no url.search, stop now
  694. // TODO throw warning
  695. return true;
  696. }
  697. S = search.split('&');
  698. for(var i = 0; i < S.length; i++) {
  699. if(S[i].substr(0, 5) === 'page=') {
  700. return(parseInt(S[i].substr(5)) >= parseInt(_toPage) + 1);
  701. }
  702. }
  703. // unknown case, stop now
  704. // TODO throw warning
  705. return true;
  706. }
  707.  
  708. function addUrlString(url, pageNb) {
  709. var S = location.search.toString();
  710. console.log('location.search: ' + S);
  711. if(S.length <= 0) {
  712. return url + "?page=" + pageNb;
  713. }
  714. if(S.indexOf('page=') > -1) {
  715. return url.replace(/page=\d+/, 'page=' + pageNb);
  716. }
  717. // FIXME: return what ? return (url + '&page=' + pageNb);
  718. }
  719.  
  720. function fetchMembers(startPageNo) {
  721.  
  722. // if (typeof startPageNo === 'undefined')
  723. // seekingEnded();
  724.  
  725. var next;
  726.  
  727. if(startPageNo) {
  728. console.log('start fetching from Page: ' + startPageNo);
  729. if(parseInt(startPageNo) > 0) {
  730. setNext(addUrlString(location.href, startPageNo));
  731. }
  732. }
  733. else {
  734. startPageNo = false;
  735. }
  736.  
  737. next = getNext();
  738. console.log("trying to fetch: " + next);
  739.  
  740. if( 
  741. next === '' ||
  742. next.match(/^\s*$/) ||
  743. next === void(0)
  744. ) {
  745. seekingEnded();
  746. return;
  747. }
  748.  
  749. if(!ajaxLocked) {
  750. ajaxLocked = true;
  751. $.ajax({
  752. url: next,
  753. dataType: 'html',
  754. useCache: false,
  755. success: onMembersPage,
  756. error: function (event) {
  757. seekingEnded();
  758. console.error(event);
  759. }
  760. });
  761. }
  762. showCount();
  763. }
  764.  
  765. function onMembersPage(data) {
  766. var aNext = $(data).find(Selector.nextPage);
  767.  
  768. setNext(aNext);
  769.  
  770. console.log("next page to fetch: " +
  771. getNext());
  772. var users = $(data).find(Selector.users);
  773.  
  774. $.each(users, (i, user) => {
  775. show(storeUser(user));
  776. });
  777.  
  778. if(!stopped && !isLastFetchedPage(getNext())) {
  779. setTimeout(
  780. function () {
  781. ajaxLocked = false;
  782. fetchMembers(false);
  783. }, (FETCH_LATENCY < 0 ? FETCH_LATENCY : 0)
  784. );
  785. }
  786. else {
  787. seekingEnded();
  788. }
  789. }
  790.  
  791.  
  792. function showInfo(str) {
  793. $I('ShowCount').html(str);
  794. }
  795.  
  796. function showCount() {
  797. showInfo("members: " + _shownCount + " of " + members.length);
  798. }
  799.  
  800. function show(n) {
  801. if(!filterUser(n)) {
  802. $(listContainers[alternColumn]).append(members[n]);
  803. _shownCount++;
  804. alternColumn = (alternColumn == (listContainers.length - 1)) ?
  805. 0 : (
  806. alternColumn + 1);
  807. }
  808. }
  809. // TODO for 2.0
  810. // var Cache = function () {}
  811. //
  812. // var Member = function () {
  813. //
  814. // htmlCache = [];
  815. //
  816. //
  817. // var isfilter = function (n) { Refining.isFiltered()}
  818. // var store = function () {};
  819. //
  820. // this.add = function () {};
  821. // this.show = function () {};
  822. // this.get = function (n) {};
  823. //
  824. // return this;
  825. // };
  826.  
  827. function storeUser(user) {
  828. console.log(user);
  829. var i = (members.push(user) - 1);
  830. // TODO add page num
  831. var matches = []; /* match: [whole, age (not null), gender, role ] */
  832. var firstSpan = $(user).find(Selector.user.firstSpan);
  833. var C, into;
  834. var avatar = $(user).find(Selector.user.imageAvatar);
  835. mCache[i] = ['', 0, '', '', '', '', false, '', ''];
  836. C = mCache[i];
  837. /* name */
  838. C[0] = firstSpan.text();
  839. /** profile url */
  840. C[5] = firstSpan.find(Selector.user.profileLink).attr('href');
  841. /** hasAvatar, need to be false if user has */
  842. C[6] = (avatar.attr('src').indexOf('/images/avatar_missing') < 0) ?
  843. true :
  844. false;
  845. matches = $(user).find(Selector.user.shortDesc).text().match(
  846. userRegExp);
  847. // console.log("match: "+(M[1]||"")+", "+(M[2]||"")+", "+(M[3]||""));
  848. /* age */
  849. C[1] = matches[1];
  850. /* gender */
  851. C[2] = matches[2];
  852. /* role */
  853. C[3] = matches[3];
  854. /* location*/
  855. C[4] = $(user).find(Selector.user.location).text() || '';
  856. /* into */
  857. if(isFetishesPage) {
  858. matches = []; /* match: ["into status", "rest aka into activity" ] */
  859. into = $(user).find(Selector.user.into).text() || '';
  860. console.log('into: '+into);
  861. matches = into.match(regInto);
  862. console.log(matches);
  863. if(matches) {
  864. C[7] = matches[1] || '';
  865. C[8] = matches[2] || '';
  866. }
  867. // console.log("mCache[i][7] = "+C[7]+" && mCache[i][8] = "+C[8])
  868. }
  869. console.log(C);
  870. return i;
  871. }
  872.  
  873. function initCache() {
  874. M = []; // TODO undef ?
  875. members = [];
  876. mCache = [];
  877. }
  878.  
  879. function clearCache() {
  880. members = [];
  881. mCache = [];
  882. }
  883.  
  884. function gC(n) {
  885. return mCache[n];
  886. }
  887.  
  888. function getCache(n) {
  889. return gC(n);
  890. }
  891. /*--------------helpers---------------*/
  892. function isInt(n) {
  893. return(!isNaN(n));
  894. }
  895.  
  896. /*--------------Actions--------------*/
  897. function init() {
  898. var $pageNext = $(Selector.nextPage);
  899. var $previousPage = $(Selector.previousPage);
  900. var $pageUsers = $(Selector.users);
  901. var $fromPage = $I('FromPage');
  902.  
  903. /** next_page link and list of members are on current page ? go on */
  904. if(($pageNext.length > 0 || $previousPage.length > 0) &&
  905. $pageUsers.length > 0
  906. ) {
  907. _clientPageNo = _pageCurrentNo = getCurrentPageNo();
  908. initContainers($pageUsers);
  909.  
  910. console.log('_pageCurrentNo :' + _pageCurrentNo);
  911.  
  912. drawBlock();
  913. $I('FromPage').val(
  914. useCurrentPageAsDefault ?
  915. _pageCurrentNo :
  916. 1
  917. );
  918. pageMax = lastPageNumber = getLastPageNum();
  919. console.log("lastPageNumber: " + lastPageNumber);
  920. $I('ToPage').val(lastPageNumber);
  921. setNext($pageNext);
  922. initListener();
  923. }
  924. }
  925.  
  926. function showFilterAgain() {
  927.  
  928. $I('ButtonGo').val("filter\x20again");
  929. enableAllInput();
  930.  
  931. $I('ButtonGo').bind('click', () => {
  932. if(updatePaginationFilters()) {
  933. launchAgain();
  934. }
  935. else {
  936. filterAgain();
  937. }
  938. $(this).unbind('click');
  939. });
  940. }
  941.  
  942. function initListener() {
  943. $I('Controls').click(function () {
  944. $I('Content').toggle("slow");
  945. toggleMarker($(this).find('b:first'));
  946. });
  947. $I('ButtonCurrentPage').click(function () {
  948. $I('FromPage').val(_pageCurrentNo);
  949. });
  950. $I('ButtonGo').click(function () {
  951. $(this).unbind('click');
  952. launchSearch();
  953.  
  954. });
  955. $I('ButtonStop').click(function () {
  956. // $(this).unbind('click');
  957. stopped = true;
  958. });
  959. }
  960.  
  961. function launchSearch() {
  962. if(!scriptLaunched) {
  963. updatePaginationFilters();
  964. console.log("_fromPage: " + _fromPage);
  965. console.log("_toPage: " + _toPage);
  966. showInfo("loading ...");
  967. updateMemberFilters();
  968. updateAvatarFilter();
  969. cleanPage();
  970. disableAllInput();
  971. fetchMembers(_fromPage);
  972. scriptLaunched = true;
  973. }
  974. }
  975.  
  976. function launchAgain() {
  977. cleanPage();
  978. clearCache();
  979. scriptLaunched = false;
  980. launchSearch();
  981. }
  982.  
  983. function filterAgain() {
  984. alternColumn = 0;
  985. // window.scrollTo(0, 0);
  986. updateMemberFilters();
  987. updateAvatarFilter();
  988. cleanPage();
  989. for(var i = 0; i < members.length; i++) {
  990. show(i);
  991. }
  992. showCount();
  993. }
  994. /** due to recursive function*/
  995. function seekingEnded() {
  996. console.log("total members: " + members.length);
  997. ajaxLocked = false;
  998. stopped = false;
  999. enableAllInput();
  1000. showFilterAgain();
  1001. showCount();
  1002. }
  1003. /* jshint ignore:start */
  1004. var count = 0;
  1005. if(typeof $ == 'function') {
  1006. init();
  1007. }
  1008. else {
  1009. setTimeout(function () {
  1010. if(typeof $ !== 'function') {
  1011. alert('fetlife is modified, script ' +
  1012. GM_info.script.name +
  1013. 'can\'t run please contact the author.');
  1014. }
  1015. }, 3000);
  1016. }
  1017. /*-----------------------------------*/
  1018.  
  1019. })(jQuery);
  1020. jQuery.noConflict(true);
  1021. /* jshint ignore:end */