GreasyFork script icon

On a script info page it shows its icon from the script meta block

As of 2016-01-18. See the latest version.

  1. // ==UserScript==
  2. // @name GreasyFork script icon
  3. // @namespace wOxxOm.scripts
  4. // @description On a script info page it shows its icon from the script meta block
  5. // @icon https://icons.iconarchive.com/icons/custom-icon-design/mono-general-1/64/information-icon.png
  6. // @version 1.1.2
  7. // @author wOxxOm
  8. // @include /^https://greasyfork\.org/(.*?/)?scripts/\d+.*$/
  9. // @run-at document-start
  10. // @connect-src *
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // ==/UserScript==
  15.  
  16. var scriptID = location.href.match(/scripts\/(\d+)/)[1];
  17. var iconsrc = GM_getValue(scriptID);
  18.  
  19. if (iconsrc && iconsrc.match(/^data:image|https:/))
  20. addIcon();
  21. else {
  22. GM_xmlhttpRequest({
  23. method: 'GET',
  24. url: location.href.replace(/(scripts\/\d+[^/]+)(\/.*)?$/,'$1/code/1.user.js'),
  25. timeout: 10000,
  26. onload: function (r) {
  27. var m = r.responseText.match(/\n\s*\/\/\s+@icon\s+(https?)(:\/\/.*?)[\s\r\n]/);
  28. if (!m)
  29. return;
  30.  
  31. if (m[1] == 'https')
  32. return addIcon(m[1]+m[2]);
  33.  
  34. // download http icon and store it in script db if it's small
  35. GM_xmlhttpRequest({
  36. method: 'GET',
  37. url: m[1]+m[2],
  38. timeout: 10000,
  39. headers: {'Accept':'image/png,image/*;q=0.8,*/*;q=0.5'},
  40. overrideMimeType: 'text\/plain; charset=x-user-defined',
  41. onload: function(ri) {
  42. var rb = ri.response, rbl = rb.length;
  43. if (rbl > 100000) {
  44. console.log('Script icon exceeds 100k, ignoring');
  45. return;
  46. }
  47.  
  48. var ext = ri.finalUrl.substr(ri.finalUrl.lastIndexOf('.')+1).toLowerCase();
  49. var mime = ['png','bmp','gif'].indexOf(ext) >= 0 ? ext : ext.match(/'^jpe?g?/) ? 'jpeg' : ext=='ico' ? 'x-icon' : null;
  50. if (!mime)
  51. return;
  52.  
  53. var rb8 = new Uint8Array(rbl)
  54. for (var i=0; i<rbl; i++)
  55. rb8[i] = rb.charCodeAt(i);
  56. var rbs = String.fromCharCode.apply(null, rb8);
  57.  
  58. addIcon('data:image/' + mime + ';base64,' + btoa(rbs));
  59. }
  60. });
  61. }
  62. });
  63. }
  64.  
  65. function addIcon(url) {
  66. if (url)
  67. iconsrc = url;
  68.  
  69. var h2 = document.querySelector('#script-info header h2');
  70. h2 ? __add(h2) : __wait();
  71.  
  72. function __add(h2) {
  73. if (!h2)
  74. if (!(h2 = document.querySelector('#script-info header h2')))
  75. return;
  76.  
  77. h2.insertAdjacentHTML('afterbegin','<div style="\
  78. position: absolute;\
  79. width: 80px;\
  80. margin-left: calc(-80px - 1ex);\
  81. display: inline-block;\
  82. text-align: right"></div>')
  83. var img = h2.firstChild.appendChild(document.createElement('img'));
  84. img.style.maxWidth = img.style.maxHeight = '64px';
  85. img.style.width = img.style.height = 'auto';
  86. img.src = iconsrc;
  87.  
  88. GM_setValue(scriptID, iconsrc);
  89. }
  90.  
  91. function __wait() {
  92. var ob = new MutationObserver(function(mutations){
  93. for (var i=0, ml=mutations.length, m; (i<ml) && (m=mutations[i]); i++) {
  94. if (m.target.localName == 'h2') {
  95. __add();
  96. ob.disconnect();
  97. return;
  98. }
  99. }
  100. });
  101. ob.observe(document, {subtree:true, childList:true});
  102. }
  103. }