FollowOver

Rollover for user info and follow button.

  1. // ==UserScript==
  2. // @name FollowOver
  3. // @namespace https://www.ekkooff.com/
  4. // @version 1.3.1
  5. // @description Rollover for user info and follow button.
  6. // @author Kevin Roberts (@echo)
  7. // @match https://gab.ai/*
  8. // @include https://gab.ai/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. var styles, userInfoPopup, loadingPopup;
  16.  
  17. var addToBody = function() {
  18. $('body').append(styles).append(userInfoPopup).append(loadingPopup);
  19. if($('body').css('background-color')!=='rgba(0, 0, 0, 0)'&&$('body').css('background-color')!=='transparent') {
  20. var bgcolor = $('body').css('background-color');
  21. userInfoPopup.css('background-color',bgcolor);
  22. loadingPopup.css('background-color',bgcolor);
  23. $('#followpopup-followback').css('background-color',bgcolor);
  24. }
  25.  
  26. $('#followpopup-follow').click(function() {
  27. var id = $(this).data('id');
  28. var d = {};
  29. if($(this).text()==='Unfollow'||$(this).text()==='Pending') {
  30. d._method = 'delete';
  31. }
  32. var priv = $(this).hasClass('private');
  33. $.ajax({
  34. method: 'POST',
  35. url: '/users/'+id+'/follow',
  36. data: d
  37. }).done(function(data) {
  38. if(data.state==='success') {
  39. if(d._method) {
  40. $('#followpopup-follow').text('Follow').removeClass().addClass('follow');
  41. if($('#followpopup-followback i').hasClass('ion-arrow-swap')) {
  42. $('#followpopup-followback i').removeClass().addClass('ion-arrow-left-c');
  43. }
  44. } else if(!priv) {
  45. $('#followpopup-follow').text('Unfollow').removeClass().addClass('unfollow');
  46. if($('#followpopup-followback i').hasClass('ion-arrow-left-c')) {
  47. $('#followpopup-followback i').removeClass().addClass('ion-arrow-swap');
  48. }
  49. } else {
  50. $('#followpopup-follow').text('Pending').removeClass().addClass('pending');
  51. }
  52.  
  53. if(priv) {
  54. $('#followpopup-follow').addClass('private');
  55. }
  56. }
  57. });
  58. return false;
  59. });
  60.  
  61. };
  62.  
  63. var init = function() {
  64. if(!window.hasOwnProperty('$')) {
  65. setTimeout(init,100);
  66. return;
  67. }
  68. var timeout=null;
  69. var timeout2=null;
  70. var id=null;
  71.  
  72. //addToBody();
  73.  
  74. styles = $('<style type="text/css">\n\
  75. #followpopup {\n\
  76. font-family: freight-sans-pro!important;\n\
  77. background-color:white;\n\
  78. position:absolute;\n\
  79. z-index:10000;\n\
  80. display:none;\n\
  81. height:140px;\n\
  82. width:500px;\n\
  83. padding:4px;\n\
  84. border:1px solid #666;\n\
  85. border-radius:5px;\n\
  86. }\n\
  87. #followpopup-loading {\n\
  88. position: absolute;\n\
  89. z-index: 9999;\n\
  90. }\n\
  91. #followpopup-loading .spinning {\n\
  92. display: inline-block;\n\
  93. animation-name: spin;\n\
  94. animation-duration: 1500ms;\n\
  95. animation-iteration-count: infinite;\n\
  96. animation-timing-function: linear;\n\
  97. }\n\
  98. \n\
  99. #followpopup-head {\n\
  100. text-align: center;\n\
  101. margin-bottom:3px;\n\
  102. }\n\
  103. #followpopup-head span {\n\
  104. padding-right:5px;\n\
  105. }\n\
  106. \n\
  107. #followpopup-bio {\n\
  108. height: 82px;\n\
  109. overflow: hidden;\n\
  110. word-wrap: break-word;\n\
  111. -ms-word-break: break-all;\n\
  112. overflow-wrap: break-word;\n\
  113. word-break: break-word;\n\
  114. text-overflow: ellipsis;\n\
  115. margin-bottom:5px;\n\
  116. }\n\
  117. \n\
  118. #followpopup-info {\n\
  119. border: 1px solid #edeeee;\n\
  120. overflow:hidden;\n\
  121. white-space: nowrap;\n\
  122. text-overflow: ellipsis;\n\
  123. border-radius:4px;\n\
  124. display:block;\n\
  125. width:100%;\n\
  126. height: 28px;\n\
  127. position:relative;\n\
  128. }\n\
  129. \n\
  130. #followpopup-follow {\n\
  131. right:0;\n\
  132. position:absolute;\n\
  133. width:55px;\n\
  134. color: white;\n\
  135. text-decoration: none;\n\
  136. padding: 5px 10px;\n\
  137. display: inline-block;\n\
  138. }\n\
  139. \n\
  140. #followpopup-info span {\n\
  141. border-right: 1px solid #edeeee;\n\
  142. padding: 3px;\n\
  143. display: inline-block;\n\
  144. }\n\
  145. \n\
  146. #followpopup #followpopup-followback {\n\
  147. background-color: white;\n\
  148. right:75px;\n\
  149. position:absolute;\n\
  150. padding-right: 6px;\n\
  151. height: 100%;\n\
  152. }\n\
  153. \n\
  154. #followpopup .follow {\n\
  155. background: #34cf7f;\n\
  156. }\n\
  157. \n\
  158. #followpopup .unfollow {\n\
  159. background: red;\n\
  160. }\n\
  161. \n\
  162. #followpopup .pending {\n\
  163. background: #edeeee;\n\
  164. color: #000;\n\
  165. }\n\
  166. \n\
  167. #followpopup .empty {\n\
  168. background: transparent;\n\
  169. }\n\
  170. #followpopup .ion-checkmark-circled.verified-badge {\n\
  171. background:transparent;\n\
  172. color:#33f!important;\n\
  173. }\n\
  174. \n\
  175. </style>');
  176.  
  177.  
  178. userInfoPopup = $('<div id="followpopup">\
  179. <div id="followpopup-head">\
  180. <strong><span id="followpopup-name"></span></strong>\
  181. <span id="followpopup-username"></span>\
  182. </div>\
  183. <div id="followpopup-bio"></div>\
  184. <div id="followpopup-info">\
  185. ?<span id="followpopup-score"></span>\
  186. <span id="followpopup-posts"></span>\
  187. <span id="followpopup-followers"></span>\
  188. <span id="followpopup-following"></span>\
  189. <span id="followpopup-followback"></span>\
  190. <a href="#" id="followpopup-follow"></a>\
  191. </div>\
  192. </div>');
  193.  
  194. loadingPopup = $('<div id="followpopup-loading"><i class="ion-load-c spinning"> </i></div>');
  195.  
  196. var targets = 'a.post__meta__name__full, a.post__meta__name__username, .notification-list .notification-list__item__message div:first-child span a:first-child, .notification-list__item__users a, a.notification-list__item__mention__name, a.notification-list__item__mention__username, a.inner-post-mention, a.profile-badge__name, a.profile-badge__username';
  197. var nameTarget = 'span.mini-user-list__item__name';
  198. var usernameTarget = 'span.mini-user-list__item__username';
  199. var allTargets = targets+', '+nameTarget+', '+usernameTarget;
  200.  
  201. var populatePopup = function(data,css) {
  202. id = data.id;
  203. if(!$('#followpopup').length) addToBody();
  204. $('#followpopup-name').html(data.name+(data.verified?' <i class="ion-checkmark-circled verified-badge"></i>':''));
  205. $('#followpopup-username').text('@'+data.username);
  206. $('#followpopup-bio').text(data.bio);
  207. $('#followpopup-score').text(data.score);
  208. $('#followpopup-posts').text(data.post_count+' posts');
  209. $('#followpopup-followers').text(data.follower_count+' followers');
  210. $('#followpopup-following').text(data.following_count+' following');
  211. if(data.followed) {
  212. if(data.following) {
  213. $('#followpopup-followback').html('<i class="ion-arrow-swap"></i>');
  214. } else {
  215. $('#followpopup-followback').html('<i class="ion-arrow-left-c"></i>'+(data.is_accessible?'':'?'));
  216. }
  217. } else {
  218. $('#followpopup-followback').html((data.is_accessible?'':'?'));
  219. }
  220. $('#followpopup-follow').data('id',id);
  221. if(authUser.username!=data.username) {
  222. if(data.follow_pending) {
  223. $('#followpopup-follow').text('Pending').removeClass().addClass('pending');
  224. } else if(!data.following) {
  225. $('#followpopup-follow').text('Follow').removeClass().addClass('follow');
  226. } else {
  227. $('#followpopup-follow').text('Unfollow').removeClass().addClass('unfollow');
  228. }
  229. } else {
  230. $('#followpopup-follow').text('').removeClass().addClass('empty');
  231. }
  232. if(data.is_private) {
  233. $('#followpopup-follow').addClass('private');
  234. }
  235. userInfoPopup.css(css).show();
  236. };
  237.  
  238. var getUserInfo = function(name,e) {
  239. clearTimeout(timeout);
  240. var offset = $(e.currentTarget).offset();
  241. var offset2 = $(e.currentTarget.parentElement).offset();
  242. var height = $(window).height();
  243. var width = $(window).width();
  244. var css = {
  245. top:((e.clientY<(height/2))?offset.top+25:offset.top-160)+'px',
  246. left:(((width-515)>offset2.left)?offset2.left:width-515)+'px'
  247. };
  248. var loadingCss = {
  249. top: ((e.clientY<(height/2))?offset.top+25:offset.top-25)+'px',
  250. left: offset.left
  251. };
  252. loadingPopup.css(loadingCss).show();
  253. $.ajax({
  254. method: 'GET',
  255. url: '/users/'+name,
  256. headers: {
  257. 'Authorization': 'Bearer '+window.storage.id_token
  258. }
  259. }).done(function(data) {
  260. loadingPopup.hide();
  261. populatePopup(data,css);
  262. }).error(function(data) {
  263. loadingPopup.hide();
  264. });
  265. };
  266.  
  267. $('body').on('mouseenter',targets ,function(e){
  268. clearTimeout(timeout);
  269. clearTimeout(timeout2);
  270. var name = e.currentTarget.href.substr(e.currentTarget.href.lastIndexOf('/')+1);
  271. timeout2 = setTimeout(function() {
  272. getUserInfo(name,e);
  273. },500);
  274. }).on('mouseenter',nameTarget,function(e) {
  275. clearTimeout(timeout);
  276. clearTimeout(timeout2);
  277. var name = $(e.currentTarget).next().text().substring(1);
  278. timeout2 = setTimeout(function() {
  279. getUserInfo(name,e);
  280. },500);
  281. }).on('mouseenter',usernameTarget,function(e) {
  282. clearTimeout(timeout);
  283. clearTimeout(timeout2);
  284. var name = $(e.currentTarget).text().substring(1);
  285. timeout2 = setTimeout(function() {
  286. getUserInfo(name,e);
  287. },500);
  288. }).on('click',allTargets,function(e){
  289. clearTimeout(timeout);
  290. clearTimeout(timeout2);
  291. userInfoPopup.hide();
  292. }).on('mouseleave',allTargets,function(){
  293. clearTimeout(timeout2);
  294. timeout = setTimeout(function(){
  295. loadingPopup.hide();
  296. userInfoPopup.hide();
  297. }, 650);
  298. });
  299.  
  300. (function(history){
  301. var pushState = history.pushState;
  302. history.pushState = function(state) {
  303. if (typeof history.onpushstate == "function") {
  304. history.onpushstate({state: state});
  305. }
  306. clearTimeout(timeout);
  307. clearTimeout(timeout2);
  308. userInfoPopup.hide();
  309. return pushState.apply(history, arguments);
  310. };
  311. })(window.history);
  312.  
  313. userInfoPopup.mouseenter(function() {
  314. clearTimeout(timeout);
  315. clearTimeout(timeout2);
  316. loadingPopup.hide();
  317. }).mouseleave(function() {
  318. clearTimeout(timeout2);
  319. timeout = setTimeout(function() {
  320. loadingPopup.hide();
  321. userInfoPopup.hide();
  322. }, 650);
  323. });
  324. };
  325.  
  326. window.addEventListener('load',init,false);
  327. })();