Website icon getter

Get the icon of current tab

  1. /* eslint-disable no-multi-spaces */
  2.  
  3. // ==UserScript==
  4. // @name Website icon getter
  5. // @name:zh-CN 网站图标获取
  6. // @name:en Website icon getter
  7. // @namespace Website-icon-getter
  8. // @version 0.1.4
  9. // @description Get the icon of current tab
  10. // @description:zh-CN 获取当前网站的图标
  11. // @description:en Get the icon of current tab
  12. // @author PY-DNG
  13. // @license GPL-3.0-or-later
  14. // @match http*://*/*
  15. // @match file:///*
  16. // @require https://update.greatest.deepsurf.us/scripts/456034/1303041/Basic%20Functions%20%28For%20userscripts%29.js
  17. // @icon none
  18. // @grant GM_registerMenuCommand
  19. // @grant GM_setClipboard
  20. // @grant GM_xmlhttpRequest
  21. // @noframes
  22. // ==/UserScript==
  23.  
  24. /* global LogLevel DoLog Err $ $All $CrE $AEL $$CrE addStyle detectDom destroyEvent copyProp copyProps parseArgs escJsStr replaceText getUrlArgv dl_browser dl_GM AsyncManager */
  25.  
  26. (function __MAIN__() {
  27. 'use strict';
  28.  
  29. const CONST = {
  30. Text_AllLang: {
  31. DEFAULT: 'en',
  32. 'en': {
  33. CopyIconUrl: 'Copy icon url of current tab',
  34. OpenIconInNewTab: 'Open icon in new tab',
  35. CopyIconBase64: 'Copy icon url of current tab in Base64 format',
  36. DownloadIcon: 'Download icon of current tab',
  37. Download_FileName: 'Icon - {Host}.ico',
  38. },
  39. 'zh': {
  40. CopyIconUrl: '复制当前标签页图标地址',
  41. OpenIconInNewTab: '在新标签页查看图标',
  42. CopyIconBase64: '复制Base64格式的当前标签页图标地址',
  43. DownloadIcon: '下载当前标签页图标',
  44. Download_FileName: 'Icon - {Host}.ico',
  45. }
  46. }
  47. };
  48. const i18n = navigator.language.split('-')[0] || CONST.Text_AllLang.DEFAULT;
  49. CONST.Text = CONST.Text_AllLang[i18n];
  50.  
  51. GM_registerMenuCommand(CONST.Text.CopyIconUrl, copyIconUrl);
  52. GM_registerMenuCommand(CONST.Text.OpenIconInNewTab, openIcon);
  53. GM_registerMenuCommand(CONST.Text.CopyIconBase64, copyIconBase64);
  54. GM_registerMenuCommand(CONST.Text.DownloadIcon, downloadIcon);
  55.  
  56. function downloadIcon() {
  57. dl_browser(getIconUrl(), replaceText(CONST.Text.Download_FileName, {'{Host}': getHost()}));
  58. }
  59.  
  60. async function copyIconBase64() {
  61. const url = await getImageUrl(getIconUrl());
  62. GM_setClipboard(url, 'text');
  63. }
  64.  
  65. function copyIconUrl() {
  66. GM_setClipboard(getIconUrl(), 'text');
  67. }
  68.  
  69. function openIcon() {
  70. window.open(getIconUrl());
  71. }
  72.  
  73. function getIconUrl() {
  74. const head = document.head;
  75. const link = $(head, 'link[rel~="icon"]');
  76. return link ? link.href : getHost() + 'favicon.ico';
  77. }
  78.  
  79. // get host part from a url(includes '^https://', '/$')
  80. function getHost(url=location.href) {
  81. const match = location.href.match(/https?:\/\/[^\/]+\//);
  82. return match ? match[0] : match;
  83. }
  84.  
  85. // Get a base64-formatted url of an image
  86. async function getImageUrl(src) {
  87. const blob = await get(src, 'blob');
  88. return toDataURL(blob);
  89. }
  90.  
  91. async function toDataURL(blob) {
  92. return new Promise((resolve, reject) => {
  93. try {
  94. const reader = new FileReader();
  95. reader.onload = e => resolve(reader.result);
  96. reader.onerror = reject;
  97. reader.readAsDataURL(blob);
  98. } catch(err) {
  99. reject(err);
  100. }
  101. });
  102. }
  103.  
  104. function get(url, responseType='text') {
  105. return new Promise((resolve, reject) => {
  106. try {
  107. GM_xmlhttpRequest({
  108. method: 'GET',
  109. url, responseType,
  110. onload: resp => resolve(resp.response),
  111. onerror: reject,
  112. onabort: reject,
  113. ontimeout: reject
  114. });
  115. } catch(err) {
  116. reject(err);
  117. }
  118. });
  119. }
  120.  
  121. function dl_browser(url, name) {
  122. const a = $CrE('a');
  123. a.href = url;
  124. a.download = name;
  125. a.click();
  126. }
  127.  
  128. // Replace model text with no mismatching of replacing replaced text
  129. // e.g. replaceText('aaaabbbbccccdddd', {'a': 'b', 'b': 'c', 'c': 'd', 'd': 'e'}) === 'bbbbccccddddeeee'
  130. // replaceText('abcdAABBAA', {'BB': 'AA', 'AAAAAA': 'This is a trap!'}) === 'abcdAAAAAA'
  131. // replaceText('abcd{AAAA}BB}', {'{AAAA}': '{BB', '{BBBB}': 'This is a trap!'}) === 'abcd{BBBB}'
  132. // replaceText('abcd', {}) === 'abcd'
  133. /* Note:
  134. replaceText will replace in sort of replacer's iterating sort
  135. e.g. currently replaceText('abcdAABBAA', {'BBAA': 'TEXT', 'AABB': 'TEXT'}) === 'abcdAATEXT'
  136. but remember: (As MDN Web Doc said,) Although the keys of an ordinary Object are ordered now, this was
  137. not always the case, and the order is complex. As a result, it's best not to rely on property order.
  138. So, don't expect replaceText will treat replacer key-values in any specific sort. Use replaceText to
  139. replace irrelevance replacer keys only.
  140. */
  141. function replaceText(text, replacer) {
  142. if (Object.entries(replacer).length === 0) {return text;}
  143. const [models, targets] = Object.entries(replacer);
  144. const len = models.length;
  145. let text_arr = [{text: text, replacable: true}];
  146. for (const [model, target] of Object.entries(replacer)) {
  147. text_arr = replace(text_arr, model, target);
  148. }
  149. return text_arr.map((text_obj) => (text_obj.text)).join('');
  150.  
  151. function replace(text_arr, model, target) {
  152. const result_arr = [];
  153. for (const text_obj of text_arr) {
  154. if (text_obj.replacable) {
  155. const splited = text_obj.text.split(model);
  156. for (const part of splited) {
  157. result_arr.push({text: part, replacable: true});
  158. result_arr.push({text: target, replacable: false});
  159. }
  160. result_arr.pop();
  161. } else {
  162. result_arr.push(text_obj);
  163. }
  164. }
  165. return result_arr;
  166. }
  167. }
  168. })();