fluoride.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0
  2. var reverseActions = {
  3. "like": "unlike",
  4. "unlike": "like",
  5. "retweet": "unretweet",
  6. "unretweet": "retweet"
  7. };
  8. var csrfToken = "";
  9. var antiDopamineMode = false;
  10. function checkCSRFToken() {
  11. var tag = document.querySelector("meta[name='csrf_token']");
  12. if (tag)
  13. csrfToken = tag.getAttribute("content");
  14. }
  15. function checkAntiDopamineMode() {
  16. var tag = document.querySelector("meta[name='antidopamine_mode']");
  17. if (tag)
  18. antiDopamineMode = tag.getAttribute("content") === "true";
  19. }
  20. function http(method, url, body, type, success, error) {
  21. var req = new XMLHttpRequest();
  22. req.onload = function() {
  23. if (this.status === 200 && typeof success === "function") {
  24. success(this.responseText, this.responseType);
  25. } else if (typeof error === "function") {
  26. error(this.responseText);
  27. }
  28. };
  29. req.onerror = function() {
  30. if (typeof error === "function") {
  31. error(this.responseText);
  32. }
  33. };
  34. req.open(method, url);
  35. req.setRequestHeader("Content-Type", type);
  36. req.send(body);
  37. }
  38. function updateActionForm(id, f, action) {
  39. f.querySelector("[type='submit']").value = action;
  40. f.action = "/" + action + "/" + id;
  41. f.dataset.action = action;
  42. }
  43. function handleLikeForm(id, f) {
  44. f.onsubmit = function(event) {
  45. event.preventDefault();
  46. var action = f.dataset.action;
  47. var forms = document.
  48. querySelectorAll(".status-"+id+" .status-like");
  49. for (var i = 0; i < forms.length; i++) {
  50. updateActionForm(id, forms[i], reverseActions[action]);
  51. }
  52. var body = "csrf_token=" + encodeURIComponent(csrfToken);
  53. var contentType = "application/x-www-form-urlencoded";
  54. http("POST", "/fluoride/" + action + "/" + id,
  55. body, contentType, function(res, type) {
  56. if (antiDopamineMode)
  57. return;
  58. var data = JSON.parse(res);
  59. var count = data.data;
  60. if (count === 0)
  61. count = "";
  62. var counts = document.
  63. querySelectorAll(".status-"+id+" .status-like-count");
  64. for (var i = 0; i < counts.length; i++) {
  65. if (count > 0) {
  66. counts[i].innerHTML = "(" + count + ")";
  67. } else {
  68. counts[i].innerHTML = "";
  69. }
  70. }
  71. }, function(err) {
  72. for (var i = 0; i < forms.length; i++) {
  73. updateActionForm(id, forms[i], action);
  74. }
  75. });
  76. }
  77. }
  78. function handleRetweetForm(id, f) {
  79. f.onsubmit = function(event) {
  80. event.preventDefault();
  81. var action = f.dataset.action;
  82. var forms = document.
  83. querySelectorAll(".status-"+id+" .status-retweet");
  84. for (var i = 0; i < forms.length; i++) {
  85. updateActionForm(id, forms[i], reverseActions[action]);
  86. }
  87. var body = "csrf_token=" + encodeURIComponent(csrfToken);
  88. var contentType = "application/x-www-form-urlencoded";
  89. http("POST", "/fluoride/" + action + "/" + id,
  90. body, contentType, function(res, type) {
  91. if (antiDopamineMode)
  92. return;
  93. var data = JSON.parse(res);
  94. var count = data.data;
  95. if (count === 0)
  96. count = "";
  97. var counts = document.
  98. querySelectorAll(".status-"+id+" .status-retweet-count");
  99. for (var i = 0; i < counts.length; i++) {
  100. if (count > 0) {
  101. counts[i].innerHTML = "(" + count + ")";
  102. } else {
  103. counts[i].innerHTML = "";
  104. }
  105. }
  106. }, function(err) {
  107. for (var i = 0; i < forms.length; i++) {
  108. updateActionForm(id, forms[i], action);
  109. }
  110. });
  111. }
  112. }
  113. function isInView(el) {
  114. var ract = el.getBoundingClientRect();
  115. if (ract.top > 0 && ract.bottom < window.innerHeight)
  116. return true;
  117. return false;
  118. }
  119. function handleReplyToLink(a) {
  120. if (!a)
  121. return;
  122. var id = a.getAttribute("href");
  123. if (!id || id[0] != "#")
  124. return;
  125. a.onmouseenter = function(event) {
  126. var id = event.target.getAttribute("href");
  127. var status = document.querySelector(id);
  128. if (!status)
  129. return;
  130. if (isInView(status)) {
  131. status.classList.add("highlight");
  132. } else {
  133. var copy = status.cloneNode(true);
  134. copy.id = "reply-to-popup";
  135. var ract = event.target.getBoundingClientRect();
  136. if (ract.top > window.innerHeight / 2) {
  137. copy.style.bottom = (window.innerHeight -
  138. window.scrollY - ract.top) + "px";
  139. }
  140. event.target.parentElement.appendChild(copy);
  141. }
  142. }
  143. a.onmouseleave = function(event) {
  144. var popup = document.getElementById("reply-to-popup");
  145. if (popup) {
  146. event.target.parentElement.removeChild(popup);
  147. } else {
  148. var id = event.target.getAttribute("href");
  149. document.querySelector(id)
  150. .classList.remove("highlight");
  151. }
  152. }
  153. }
  154. function handleReplyLink(a) {
  155. a.onmouseenter = function(event) {
  156. var id = event.target.getAttribute("href");
  157. var status = document.querySelector(id);
  158. if (!status)
  159. return;
  160. if (isInView(status)) {
  161. status.classList.add("highlight");
  162. } else {
  163. var copy = status.cloneNode(true);
  164. copy.id = "reply-popup";
  165. var ract = event.target.getBoundingClientRect();
  166. if (ract.left > window.innerWidth / 2) {
  167. copy.style.right = (window.innerWidth -
  168. ract.right - 12) + "px";
  169. }
  170. event.target.parentElement.appendChild(copy);
  171. }
  172. }
  173. a.onmouseleave = function(event) {
  174. var popup = document.getElementById("reply-popup");
  175. if (popup) {
  176. event.target.parentElement.removeChild(popup);
  177. } else {
  178. var id = event.target.getAttribute("href");
  179. document.querySelector(id).classList.remove("highlight");
  180. }
  181. }
  182. }
  183. function handleStatusLink(a) {
  184. if (a.classList.contains("mention"))
  185. a.removeAttribute("target");
  186. else
  187. a.target = "_blank";
  188. }
  189. document.addEventListener("DOMContentLoaded", function() {
  190. checkCSRFToken();
  191. checkAntiDopamineMode();
  192. var statuses = document.querySelectorAll(".status-container");
  193. for (var i = 0; i < statuses.length; i++) {
  194. var s = statuses[i];
  195. var id = s.dataset.id;
  196. var likeForm = s.querySelector(".status-like");
  197. handleLikeForm(id, likeForm);
  198. var retweetForm = s.querySelector(".status-retweet");
  199. handleRetweetForm(id, retweetForm);
  200. var replyToLink = s.querySelector(".status-reply-to-link");
  201. handleReplyToLink(replyToLink);
  202. var replyLinks = s.querySelectorAll(".status-reply-link");
  203. for (var j = 0; j < replyLinks.length; j++) {
  204. handleReplyLink(replyLinks[j]);
  205. }
  206. var links = s.querySelectorAll(".status-content a");
  207. for (var j = 0; j < links.length; j++) {
  208. handleStatusLink(links[j]);
  209. }
  210. }
  211. var links = document.querySelectorAll(".user-profile-decription a");
  212. for (var j = 0; j < links.length; j++) {
  213. links[j].target = "_blank";
  214. }
  215. });
  216. // @license-end