Dribbble Extender

Shows who follows you on your following list

  1. // ==UserScript==
  2. // @name Dribbble Extender
  3. // @description Shows who follows you on your following list
  4. // @author Kos
  5. // @namespace http://tampermonkey.net/
  6. // @version 0.5
  7. // @license CC BY-SA 2.0
  8. // @homepage https://gf.qytechs.cn/scripts/22003-dribbble-extender
  9. // @include https://dribbble.com/*
  10. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
  11. // @grant GM_addStyle
  12. // ==/UserScript==
  13.  
  14. ;(function() {
  15. 'use strict';
  16. // styles for checkmark
  17. GM_addStyle(
  18. '.us-follows-you-mark {color:#b3e3bd;font-weight:200}\
  19. .us-fetch-ready {color:#2cff5a;font-weight:900}'
  20. );
  21.  
  22. var ACCESS_TOKEN = '9eafaa85aba0e22cdd7b7ddaa23181b59c29989dbd27000c78db21f5609110f4',
  23. followers = [],
  24. // username
  25. username = $('.has-sub .profile-name').parent().attr('href').replace('/', ''),
  26. cacheKey = username,
  27. curPage = 1,
  28. perPage = 100,
  29. repaintDelay = 500,
  30. waitForAllFetch = false,
  31. requestPerIteration = 15,
  32. requestsResponseLeftCount = 0,
  33. mainIntervalStopped = true,
  34. lastIntervalFoundFollowers = null,
  35. justCache = false;
  36.  
  37. // if not recognized logged in user, exit
  38. if (username == '')
  39. {
  40. return;
  41. }
  42. // functions for working with localStorage
  43.  
  44. function lsTest()
  45. {
  46. var test = 'test';
  47. try
  48. {
  49. localStorage.setItem(test, test);
  50. localStorage.removeItem(test);
  51. return true;
  52. }
  53. catch(e)
  54. {
  55. return false;
  56. }
  57. }
  58.  
  59. function getLocalStorageArray(name)
  60. {
  61. var list = localStorage.getItem(name);
  62. if (!list)
  63. {
  64. return [];
  65. }
  66. return JSON.parse(list);
  67. }
  68.  
  69. function saveUpdatedFollowersList(list)
  70. {
  71. localStorage.setItem(cacheKey+'_followers_latest', JSON.stringify(list));
  72. }
  73.  
  74. function addFollowerLocalStorage(id)
  75. {
  76. var list = getLocalStorageArray(cacheKey+'_followers_latest');
  77.  
  78. if (list.indexOf(id) == -1)
  79. {
  80. list.push(id);
  81. }
  82.  
  83. saveUpdatedFollowersList(list);
  84. }
  85.  
  86. function saveFinishedAmountOfFollowers()
  87. {
  88. var list = getLocalStorageArray(cacheKey+'_followers_latest');
  89.  
  90. localStorage.setItem(cacheKey+'_followers', JSON.stringify(list));
  91. saveUpdatedFollowersList([]);
  92.  
  93. // update on page
  94. followers = list;
  95. paintFollowed();
  96. }
  97.  
  98. var localStorageAvailable = lsTest();
  99.  
  100. $(document).ready(function(){
  101.  
  102. if (localStorageAvailable)
  103. {
  104. followers = getLocalStorageArray(cacheKey+'_followers');
  105.  
  106. // if have previously saved followers, increase delay,
  107. // update on page only when all users parsed
  108. if (followers.length)
  109. {
  110. waitForAllFetch = true;
  111. // cache results for 5 minutes
  112. if (localStorageAvailable)
  113. {
  114. var lastFetchTime = localStorage.getItem(cacheKey+'_last_followers_fetch');
  115. if (lastFetchTime && new Date().getTime() - lastFetchTime < 5*60*1000)
  116. {
  117. justCache = true;
  118. waitForAllFetch = false;
  119. }
  120. }
  121. }
  122.  
  123. // clear previously parsed data, to parse all new
  124. saveUpdatedFollowersList([]);
  125. }
  126.  
  127. function finishParse()
  128. {
  129. if (mainIntervalStopped)
  130. {
  131. return;
  132. }
  133.  
  134. clearInterval(mainInterval);
  135. mainIntervalStopped = true;
  136. if (localStorageAvailable)
  137. {
  138. localStorage.setItem(cacheKey+'_last_followers_fetch', new Date().getTime());
  139. }
  140. }
  141. if (!justCache)
  142. {
  143. mainIntervalStopped = false;
  144. var mainInterval = setInterval(function(){
  145. // if there are request we wait to finish, exit
  146. if (requestsResponseLeftCount > 0)
  147. {
  148. return;
  149. }
  150. // if not found any users on last interval
  151. if (lastIntervalFoundFollowers !== null && lastIntervalFoundFollowers === 0)
  152. {
  153. finishParse();
  154. return;
  155. }
  156. requestsResponseLeftCount = requestPerIteration;
  157. lastIntervalFoundFollowers = 0;
  158. for (var i = 0; i < requestPerIteration; i++)
  159. {
  160. $.ajax({
  161. type: 'GET',
  162. url: 'https://api.dribbble.com/v1/users/'+username+'/followers/?page='+(curPage++)+'&per_page='+perPage,
  163. beforeSend: function(jqxhr)
  164. {
  165. jqxhr.setRequestHeader('Authorization', 'Bearer ' + ACCESS_TOKEN);
  166. },
  167. success: function(res)
  168. {
  169. if (res.length === 0)
  170. {
  171. finishParse();
  172. return;
  173. }
  174.  
  175. for (var i = 0; i < res.length; i++)
  176. {
  177. setFollowed(res[i].follower.id);
  178. lastIntervalFoundFollowers++;
  179. }
  180.  
  181. // if not full page, assume it last one
  182. if (res.length < perPage)
  183. {
  184. finishParse();
  185. }
  186. },
  187. complete: function(){
  188. requestsResponseLeftCount--;
  189. // if this is last response of last iteration, save ids for page
  190. // in this 'if' fetch ends
  191. if (requestsResponseLeftCount === 0 && mainIntervalStopped)
  192. {
  193. if (localStorageAvailable)
  194. {
  195. saveFinishedAmountOfFollowers();
  196. }
  197. }
  198. }
  199. });
  200. }
  201. }, 100);
  202. }
  203.  
  204. // paint then set repaint with delay
  205. paintFollowed();
  206. setInterval(paintFollowed, repaintDelay);
  207. });
  208. function setFollowed(id)
  209. {
  210. id = Math.round(id);
  211.  
  212. if (!waitForAllFetch && followers.indexOf(id) == -1)
  213. {
  214. followers.push(id);
  215. }
  216.  
  217. if (localStorageAvailable)
  218. {
  219. addFollowerLocalStorage(id);
  220. }
  221. }
  222.  
  223. function paintFollowed()
  224. {
  225. paintOnFollowingPage();
  226. paintOnShotPage();
  227. paintOnShotsListPage();
  228. paintOnCommentsPage();
  229. paintOnUserPage();
  230. }
  231. function paintOnFollowingPage()
  232. {
  233. var following = $('ol.list-of-scrolling-rows').find('.scrolling-row');
  234. if (following.length == 0)
  235. {
  236. return;
  237. }
  238.  
  239. for (var i = 0; i < following.length; i++)
  240. {
  241. var userId = parseInt(following[i].className.match(/.*?user-row-(\d+).*/)[1]),
  242. userBlock = $(following[i]),
  243. title = userBlock.find('.hover-card-parent');
  244.  
  245. // if not followed, remove mark if has one
  246. if (followers.indexOf(userId) == -1)
  247. {
  248. userBlock.removeClass('us-follows-you');
  249. userBlock.find('.us-follows-you-mark').remove();
  250. continue;
  251. }
  252.  
  253. // if already marked as follower do nothing until all fetched
  254. if (userBlock.hasClass('us-follows-you'))
  255. {
  256. if (mainIntervalStopped)
  257. {
  258. userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
  259. }
  260. continue;
  261. }
  262.  
  263. // set mark of follower
  264. userBlock.addClass('us-follows-you');
  265. title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
  266. }
  267. }
  268. function paintOnShotsListPage()
  269. {
  270. var dribbbles = $('ol.dribbbles > li');
  271. if (dribbbles.length == 0)
  272. {
  273. return;
  274. }
  275.  
  276. for (var i = 0; i < dribbbles.length; i++)
  277. {
  278. var userBlock = $(dribbbles[i]);
  279. if (userBlock.find('.attribution-team').length)
  280. {
  281. continue;
  282. }
  283. var avatar = userBlock.find('.attribution-user img');
  284.  
  285. // avatar is not loaded yet
  286. if (!avatar || !avatar.attr('src'))
  287. {
  288. return;
  289. }
  290. var userId = parseInt(avatar.attr('src').match(/users\/(\d+)/)[1]),
  291. title = userBlock.find('.attribution-user a').first();
  292.  
  293. // if not followed, remove mark if has one
  294. if (followers.indexOf(userId) == -1)
  295. {
  296. userBlock.removeClass('us-follows-you');
  297. userBlock.find('.us-follows-you-mark').remove();
  298. continue;
  299. }
  300.  
  301. // if already marked as follower do nothing until all fetched
  302. if (userBlock.hasClass('us-follows-you'))
  303. {
  304. if (mainIntervalStopped)
  305. {
  306. userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
  307. }
  308. continue;
  309. }
  310.  
  311. // set mark of follower
  312. userBlock.addClass('us-follows-you');
  313. title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
  314. }
  315. }
  316. function paintOnCommentsPage()
  317. {
  318. var dribbbles = $('ol.comments > li');
  319. if (dribbbles.length == 0)
  320. {
  321. return;
  322. }
  323.  
  324. for (var i = 0; i < dribbbles.length; i++)
  325. {
  326. var userBlock = $(dribbbles[i]),
  327. userId = parseInt(userBlock.attr('data-user-id')),
  328. title = userBlock.find('h2');
  329.  
  330. // if not followed, remove mark if has one
  331. if (followers.indexOf(userId) == -1)
  332. {
  333. userBlock.removeClass('us-follows-you');
  334. userBlock.find('.us-follows-you-mark').remove();
  335. continue;
  336. }
  337.  
  338. // if already marked as follower do nothing until all fetched
  339. if (userBlock.hasClass('us-follows-you'))
  340. {
  341. if (mainIntervalStopped)
  342. {
  343. userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
  344. }
  345. continue;
  346. }
  347.  
  348. // set mark of follower
  349. userBlock.addClass('us-follows-you');
  350. title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
  351. }
  352. }
  353. function paintOnShotPage()
  354. {
  355. var userBlock = $('.user');
  356. if (userBlock.length == 0)
  357. {
  358. return;
  359. }
  360.  
  361. var userId = parseInt($('.user > a[rel=contact] img.photo').attr('src').replace(/.*users\/(\d+).*/, '$1')),
  362. title = userBlock.find('.shot-byline-user a');
  363.  
  364. // if not followed, remove mark if has one
  365. if (followers.indexOf(userId) == -1)
  366. {
  367. userBlock.removeClass('us-follows-you');
  368. userBlock.find('.us-follows-you-mark').remove();
  369. return;
  370. }
  371.  
  372. // if already marked as follower do nothing until all fetched
  373. if (userBlock.hasClass('us-follows-you'))
  374. {
  375. if (mainIntervalStopped)
  376. {
  377. userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
  378. }
  379. return;
  380. }
  381.  
  382. // set mark of follower
  383. userBlock.addClass('us-follows-you');
  384. title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
  385. }
  386. function paintOnUserPage()
  387. {
  388. var userBlock = $('.profile-head');
  389. if (userBlock.length == 0)
  390. {
  391. return;
  392. }
  393.  
  394. var avatar = $($('.profile-head img.photo')[0]);
  395. // avatar is not loaded yet
  396. if (!avatar || !avatar.attr('src'))
  397. {
  398. return;
  399. }
  400. var userId = parseInt(avatar.attr('src').replace(/.*users\/(\d+).*/, '$1')),
  401. title = userBlock.find('.profile-name a');
  402.  
  403. // if not followed, remove mark if has one
  404. if (followers.indexOf(userId) == -1)
  405. {
  406. userBlock.removeClass('us-follows-you');
  407. userBlock.find('.us-follows-you-mark').remove();
  408. return;
  409. }
  410.  
  411. // if already marked as follower do nothing until all fetched
  412. if (userBlock.hasClass('us-follows-you'))
  413. {
  414. if (mainIntervalStopped)
  415. {
  416. userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
  417. }
  418. return;
  419. }
  420.  
  421. // set mark of follower
  422. userBlock.addClass('us-follows-you');
  423. title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
  424. }
  425. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址