Sleazy Fork is available in English.

Improvements to FetLife's UI

Button to add an event to Google Calendar, visited profile links shown in red, mutual kinks shown brighter, homepage feed enlarged, show pagination at the top of people lists, profile pages show date of last activity plus emoji if user's inactive

  1. // ==UserScript==
  2. // @name Improvements to FetLife's UI
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Button to add an event to Google Calendar, visited profile links shown in red, mutual kinks shown brighter, homepage feed enlarged, show pagination at the top of people lists, profile pages show date of last activity plus emoji if user's inactive
  6. // @author SomeFunGuy
  7. // @license MIT
  8. // @match https://fetlife.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=fetlife.com
  10. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
  11. // @grant none
  12. // ==/UserScript==
  13. /* global $ */
  14.  
  15. (function() {
  16. 'use strict';
  17. console.log("Improve Fetlife: Start");
  18.  
  19. var s = document.createElement("style");
  20. s.innerHTML="/* Added by Tampermonkey script: Improvements to FetLife's UI */ ";
  21.  
  22. //visited profile links shown in red
  23. s.innerHTML+="#main-content .flex.flex-wrap a:visited {color: rgb(125,0,0) }";
  24.  
  25. //mutual kinks shown brighter
  26. s.innerHTML+=".bg-gray-600 { background-color: #777; }";
  27.  
  28. //homepage feed enlargement
  29. s.innerHTML+="#stories-list div { font-size: 18px; } #stories-list a.dib img { width: 150px !important; height: 150px !important; }";
  30.  
  31. document.head.appendChild(s);
  32.  
  33. //Improve group members e.g. page https://fetlife.com/groups/17165/members
  34. //or attendee list e.g. https://fetlife.com/events/1064773/rsvps
  35. if (pathMatch(/\/groups\/\d+\/members/) || pathMatch(/\/events\/\d+\/rsvps/) || pathMatch(/\/p\/.*\/kinksters/)) {
  36. copyPrevNextPageLinksToTop();
  37. } else if (pathMatch(/\/events\/\d+\/?/)) {
  38. setTimeout(addButtonCalendar, 750);
  39. } else if (pathMatch(/\/users\/\d+\/?/)) {
  40. addIdleLine();
  41. }
  42.  
  43. console.log("Improve Fetlife: Complete");
  44. })();
  45.  
  46. function pathMatch(regexp) {
  47. var result = window.location.pathname.match(regexp);
  48. //console.log(window.location.pathname, regexp, result);
  49. return result;
  50. }
  51.  
  52. function addIdleLine() {
  53. console.log("idle?");
  54.  
  55. const lastActive = $(".dn.db-s.mb4.visibility-block div[data-story-timestamp]");
  56. if(!lastActive || lastActive.length <= 0) {
  57. console.log("No activity found");
  58. setTimeout(addIdleLine, 250);
  59. return;
  60. }
  61.  
  62. const lastActiveWrapper = lastActive.first().find(".f6.gray-500.nowrap time");
  63. if(!lastActiveWrapper || lastActiveWrapper.length <= 0) {
  64. console.log("No activity found");
  65. setTimeout(addIdleLine, 250);
  66. return;
  67. }
  68.  
  69. const prettyDate = lastActiveWrapper.last().text();
  70. const actualDate = lastActiveWrapper.last().attr("datetime");
  71. console.log(prettyDate);
  72.  
  73. const wrapper = $("body header main .flex.flex-column.items-start-ns");
  74. var newRow = '<div class="pt3-ns w-100 mw100-s mw30">'
  75. +'<div class="flex flex-row items-start f5 pv0-ns pv1">'
  76. +'<div class="flex-auto flex flex-row-ns flex-column">'
  77. +'<div class="flex-none w140-ns gray-400 f5">Last Active</div>'
  78. +'<div class="flex-auto mv0 gray-150 flex-auto"><div>'
  79. + prettyDate;
  80.  
  81. const isOld = ((new Date() - new Date(actualDate)) / 1000 / 60 / 60 / 24 >= 60);
  82. if(isOld) { newRow += ' 💤'; }
  83.  
  84. newRow += '</div></div>'
  85. +'</div></div><!----></div>';
  86. console.log(newRow);
  87.  
  88. $("body header main .flex.flex-column.items-start-ns").prepend(newRow);
  89. }
  90.  
  91. function copyPrevNextPageLinksToTop() {
  92. $("header.justify-start, header.relative, header.items-end").after( $("footer div.pagination").clone().addClass("tc").css("margin-bottom", "10px") ); //tc = text center
  93. }
  94.  
  95. function addButtonCalendar() {
  96. var destination = $(".w275-l > div:first-child");
  97.  
  98. var sidebar = $(".w275-l > div:first-child > div > div");
  99. if (sidebar.length == 0) {
  100. destination.after("<div style='color:red; border:3px solid gray'>Cannot find sidebar</div>");
  101. return;
  102. }
  103.  
  104. //dates
  105. var dates;
  106. var i=0;
  107. do {
  108. dates = $(sidebar[i]).find("p span");
  109. console.log("try i="+i);
  110. i += 1;
  111. } while (i < sidebar.length && dates.length == 0);
  112.  
  113. var start = null;
  114. var end = null;
  115. if (dates.length > 2) {
  116. //start and end are on different days
  117. dates = dates.map(function(i,node) { return node.innerHTML; });
  118. start = dates[2] +" "+ dates[3];
  119. end = dates[5] +" "+ dates[6];
  120. } else if (dates.length==1 || dates.length==2) {
  121. //start and end are on the same day
  122. var parts = dates[dates.length-1].innerHTML.split("<br>");
  123. var times = parts[1].split(" - ");
  124. start = parts[0] +" "+ times[0];
  125. end = parts[0] +" "+ times[1];
  126. } else {
  127. destination.after("<div style='color:red; border:3px solid gray'>Date field has too few elements: " + dates.length +"</div>");
  128. return;
  129. }
  130.  
  131. //description += start +" to " + end; //debugging
  132. start = new Date(start).toISOString().replace(/-|:|\.\d\d\d/g,"");
  133. end = new Date(end).toISOString().replace(/-|:|\.\d\d\d/g,"");
  134.  
  135. //location
  136. var location = $(sidebar[1]).find("p").html();
  137. //replace parts of HTML with needed info, and then clear out the map link and closing span tag
  138. location = location.replace(/\s*<br>/,": ").replace(/<br>/,", ").replace(/<span [^>]*>/,"").replace(/(, United States )?<a.*/,"");
  139.  
  140. var url = "http://www.google.com/calendar/render?action=TEMPLATE"
  141. + "&text=" + encodeURIComponent($("header h1").text())
  142. + "&dates=" + encodeURIComponent(start) + "/" + encodeURIComponent(end)
  143. + "&location=" + encodeURIComponent(location)
  144. + "&trp=false&sprop=&sprop=name:";
  145.  
  146. //description can be long, so add it last
  147. var description = "URL: "+ window.location +"<br><br>" + $(".story__copy").html();
  148. url += "&details=" + encodeURIComponent(description);
  149.  
  150. //add button!
  151. var btn = "<a target='_blank' data-color='lined' "
  152. + "class='relative no-underline items-center br1 us-none ba b-gray-750 hover-b-gray-600 b-animate bg-transparent hover-bg-transparent gray-300 hover-gray-300 fill-gray-300 fw4 tc justify-center inline-flex lh-copy f7 pv1 ph2' "
  153. + "href=\""+ url +"\">Add to Google Calendar</a>";
  154. destination.after(btn);
  155. }