fluoride.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 retweetVisibility = document.querySelector('input[id="retweet_visibility-'+id+'"]:checked').value;
  88. var body = "csrf_token=" + encodeURIComponent(csrfToken) + "&retweet_visibility=" + encodeURIComponent(retweetVisibility);
  89. var contentType = "application/x-www-form-urlencoded";
  90. http("POST", "/fluoride/" + action + "/" + id,
  91. body, contentType, function(res, type) {
  92. if (antiDopamineMode)
  93. return;
  94. var data = JSON.parse(res);
  95. var count = data.data;
  96. if (count === 0)
  97. count = "";
  98. var counts = document.
  99. querySelectorAll(".status-"+id+" .status-retweet-count");
  100. for (var i = 0; i < counts.length; i++) {
  101. if (count > 0) {
  102. counts[i].innerHTML = "(" + count + ")";
  103. } else {
  104. counts[i].innerHTML = "";
  105. }
  106. }
  107. }, function(err) {
  108. for (var i = 0; i < forms.length; i++) {
  109. updateActionForm(id, forms[i], action);
  110. }
  111. });
  112. }
  113. }
  114. function isInView(el) {
  115. var ract = el.getBoundingClientRect();
  116. if (ract.top > 0 && ract.bottom < window.innerHeight)
  117. return true;
  118. return false;
  119. }
  120. function handleReplyToLink(a) {
  121. if (!a)
  122. return;
  123. var id = a.getAttribute("href");
  124. if (!id || id[0] != "#")
  125. return;
  126. a.onmouseenter = function(event) {
  127. var id = event.target.getAttribute("href");
  128. var status = document.querySelector(id);
  129. if (!status)
  130. return;
  131. if (isInView(status)) {
  132. status.classList.add("highlight");
  133. } else {
  134. var copy = status.cloneNode(true);
  135. copy.id = "reply-to-popup";
  136. var ract = event.target.getBoundingClientRect();
  137. copy.style["max-width"] = (window.innerWidth - ract.left - 32) + "px";
  138. if (ract.top > window.innerHeight / 2) {
  139. copy.style.bottom = (window.innerHeight -
  140. window.scrollY - ract.top) + "px";
  141. }
  142. event.target.parentElement.appendChild(copy);
  143. }
  144. }
  145. a.onmouseleave = function(event) {
  146. var popup = document.getElementById("reply-to-popup");
  147. if (popup) {
  148. event.target.parentElement.removeChild(popup);
  149. } else {
  150. var id = event.target.getAttribute("href");
  151. document.querySelector(id)
  152. .classList.remove("highlight");
  153. }
  154. }
  155. }
  156. function handleReplyLink(a) {
  157. a.onmouseenter = function(event) {
  158. var id = event.target.getAttribute("href");
  159. var status = document.querySelector(id);
  160. if (!status)
  161. return;
  162. if (isInView(status)) {
  163. status.classList.add("highlight");
  164. } else {
  165. var copy = status.cloneNode(true);
  166. copy.id = "reply-popup";
  167. var ract = event.target.getBoundingClientRect();
  168. copy.style["max-width"] = (window.innerWidth - 98) + "px";
  169. if (ract.left > window.innerWidth / 2) {
  170. copy.style.right = (window.innerWidth -
  171. ract.right - 12) + "px";
  172. }
  173. event.target.parentElement.appendChild(copy);
  174. }
  175. }
  176. a.onmouseleave = function(event) {
  177. var popup = document.getElementById("reply-popup");
  178. if (popup) {
  179. event.target.parentElement.removeChild(popup);
  180. } else {
  181. var id = event.target.getAttribute("href");
  182. document.querySelector(id).classList.remove("highlight");
  183. }
  184. }
  185. }
  186. function handleStatusLink(a) {
  187. if (a.classList.contains("mention"))
  188. a.removeAttribute("target");
  189. else
  190. a.target = "_blank";
  191. }
  192. function setPos(el, cx, cy, mw, mh) {
  193. var h = el.clientHeight;
  194. var w = el.clientWidth;
  195. var left, top;
  196. if (cx < mw/2) {
  197. if (w + cx + 20 < mw) {
  198. left = cx + 20;
  199. } else {
  200. left = (mw - w);
  201. }
  202. } else {
  203. if (cx - w - 20 > 0) {
  204. left = cx - w - 20;
  205. } else {
  206. left = 0;
  207. }
  208. }
  209. top = (cy - (h/2));
  210. if (top < 0) {
  211. top = 0;
  212. } else if (top + h > mh) {
  213. top = (mh - h);
  214. }
  215. el.style.left = left + "px";
  216. el.style.top = top + "px";
  217. }
  218. var imgPrev = null;
  219. var imgX = 0;
  220. var imgY = 0;
  221. function handleImgPreview(a) {
  222. a.onmouseenter = function(e) {
  223. var mw = document.documentElement.clientWidth;
  224. var mh = document.documentElement.clientHeight - 24;
  225. imgX = e.clientX;
  226. imgY = e.clientY;
  227. var img = document.createElement("img");
  228. img.id = "img-preview";
  229. img.src = e.target.getAttribute("href");
  230. img.style["max-width"] = mw + "px";
  231. img.style["max-height"] = mh + "px";
  232. imgPrev = img;
  233. img.onload = function(e2) {
  234. setPos(imgPrev, imgX, imgY, mw, mh);
  235. }
  236. document.body.appendChild(img);
  237. }
  238. a.onmouseleave = function(e) {
  239. var img = document.getElementById("img-preview");
  240. if (img)
  241. document.body.removeChild(img);
  242. imgPrev = null;
  243. }
  244. a.onmousemove = function(e) {
  245. if (!imgPrev)
  246. return;
  247. var mw = document.documentElement.clientWidth;
  248. var mh = document.documentElement.clientHeight - 24;
  249. imgX = e.clientX;
  250. imgY = e.clientY;
  251. setPos(imgPrev, imgX, imgY, mw, mh);
  252. }
  253. }
  254. function onPaste(e) {
  255. if (!e.clipboardData.files)
  256. return;
  257. var fp = document.querySelector("#post-file-picker")
  258. var dt = new DataTransfer();
  259. for (var i = 0; i < fp.files.length; i++) {
  260. dt.items.add(fp.files[i]);
  261. }
  262. for (var i = 0; i < e.clipboardData.files.length; i++) {
  263. dt.items.add(e.clipboardData.files[i]);
  264. }
  265. fp.files = dt.files;
  266. }
  267. function onKeydown(e) {
  268. if (e.key == 'Enter' && e.ctrlKey) {
  269. document.querySelector(".post-form").submit();
  270. }
  271. }
  272. document.addEventListener("DOMContentLoaded", function() {
  273. checkCSRFToken();
  274. checkAntiDopamineMode();
  275. var statuses = document.querySelectorAll(".status-container");
  276. for (var i = 0; i < statuses.length; i++) {
  277. var s = statuses[i];
  278. var id = s.dataset.id;
  279. var likeForm = s.querySelector(".status-like");
  280. handleLikeForm(id, likeForm);
  281. var retweetForm = s.querySelector(".status-retweet");
  282. handleRetweetForm(id, retweetForm);
  283. var replyToLink = s.querySelector(".status-reply-to-link");
  284. handleReplyToLink(replyToLink);
  285. var replyLinks = s.querySelectorAll(".status-reply-link");
  286. for (var j = 0; j < replyLinks.length; j++) {
  287. handleReplyLink(replyLinks[j]);
  288. }
  289. var links = s.querySelectorAll(".status-content a");
  290. for (var j = 0; j < links.length; j++) {
  291. handleStatusLink(links[j]);
  292. }
  293. }
  294. var links = document.querySelectorAll(".user-profile-decription a, .user-fields a");
  295. for (var j = 0; j < links.length; j++) {
  296. links[j].target = "_blank";
  297. }
  298. var links = document.querySelectorAll(".status-media-container .img-link, .user-profile-img-container .img-link");
  299. for (var j = 0; j < links.length; j++) {
  300. handleImgPreview(links[j]);
  301. }
  302. var pf = document.querySelector(".post-form")
  303. if (pf) {
  304. pf.addEventListener("paste", onPaste);
  305. pf.addEventListener("keydown", onKeydown);
  306. }
  307. });
  308. // @license-end