/* eslint-disable no-multi-spaces */ // ==UserScript== // @name Website icon getter // @name:zh-CN 网站图标获取 // @name:en Website icon getter // @namespace Website-icon-getter // @version 0.1 // @description Get the icon of current tab // @description:zh-CN 获取当前网站的图标 // @description:en Get the icon of current tab // @author PY-DNG // @license GPL-v3 // @match http*://*/* // @icon none // @grant GM_registerMenuCommand // @grant GM_setClipboard // @downloadURL none // ==/UserScript== (function __MAIN__() { 'use strict'; const CONST = { Text_AllLang: { DEFAULT: 'en', 'en': { CopyIconUrl: 'Copy icon url of current tab', OpenIconInNewTab: 'Open icon in new tab', CopyIconBase64: 'Copy icon url of current tab in Base64 format', DownloadIcon: 'Download icon of current tab', Download_FileName: 'Icon - {Host}.ico', }, 'zh': { CopyIconUrl: '复制当前标签页图标地址', OpenIconInNewTab: '在新标签页查看图标', CopyIconBase64: '复制Base64格式的当前标签页图标地址', DownloadIcon: '下载当前标签页图标', Download_FileName: 'Icon - {Host}.ico', } } }; const i18n = navigator.language.split('-')[0] || CONST.Text.DEFAULT; CONST.Text = CONST.Text_AllLang[i18n]; GM_registerMenuCommand(CONST.Text.CopyIconUrl, copyIconUrl); GM_registerMenuCommand(CONST.Text.OpenIconInNewTab, openIcon); GM_registerMenuCommand(CONST.Text.CopyIconBase64, copyIconBase64); GM_registerMenuCommand(CONST.Text.DownloadIcon, downloadIcon); function downloadIcon() { dl_browser(getIconUrl(), replaceText(CONST.Text.Download_FileName, {'{Host}': getHost()})); } function copyIconBase64() { getImageUrl(getIconUrl(), 0, 0, function(url) { GM_setClipboard(url, 'text'); }); } function copyIconUrl() { GM_setClipboard(getIconUrl(), 'text'); } function openIcon() { window.open(getIconUrl()); } function getIconUrl() { const head = document.head; const link = $(head, 'link[rel~="icon"]'); return link ? link.href : getHost() + 'favicon.ico'; } // Basic functions // querySelector function $() { switch(arguments.length) { case 2: return arguments[0].querySelector(arguments[1]); break; default: return document.querySelector(arguments[0]); } } // querySelectorAll function $All() { switch(arguments.length) { case 2: return arguments[0].querySelectorAll(arguments[1]); break; default: return document.querySelectorAll(arguments[0]); } } // createElement function $CrE() { switch(arguments.length) { case 2: return arguments[0].createElement(arguments[1]); break; default: return document.createElement(arguments[0]); } } // get host part from a url(includes '^https://', '/$') function getHost(url=location.href) { const match = location.href.match(/https?:\/\/[^\/]+\//); return match ? match[0] : match; } // Get a base64-formatted url of an image // When image load error occurs, callback will be called without any argument function getImageUrl(src, fitx, fity, callback, args=[]) { const image = new Image(); image.setAttribute("crossOrigin",'anonymous'); image.onload = convert; image.onerror = image.onabort = callback; image.src = src; function convert() { const cvs = $CrE('canvas'); const ctx = cvs.getContext('2d'); let width, height; if (fitx && fity) { width = window.innerWidth; height = window.innerHeight; } else if (fitx) { width = window.innerWidth; height = (width / image.width) * image.height; } else if (fity) { height = window.innerHeight; width = (height / image.height) * image.width; } else { width = image.width; height = image.height; } cvs.width = width; cvs.height = height; ctx.drawImage(image, 0, 0, width, height); try { callback.apply(null, [cvs.toDataURL()].concat(args)); } catch (e) { DoLog(LogLevel.Error, ['Error at getImageUrl.convert()', e]); callback(); } } } function dl_browser(url, name) { const a = $CrE('a'); a.href = url; a.download = name; a.click(); } // Replace model text with no mismatching of replacing replaced text // e.g. replaceText('aaaabbbbccccdddd', {'a': 'b', 'b': 'c', 'c': 'd', 'd': 'e'}) === 'bbbbccccddddeeee' // replaceText('abcdAABBAA', {'BB': 'AA', 'AAAAAA': 'This is a trap!'}) === 'abcdAAAAAA' // replaceText('abcd{AAAA}BB}', {'{AAAA}': '{BB', '{BBBB}': 'This is a trap!'}) === 'abcd{BBBB}' // replaceText('abcd', {}) === 'abcd' /* Note: replaceText will replace in sort of replacer's iterating sort e.g. currently replaceText('abcdAABBAA', {'BBAA': 'TEXT', 'AABB': 'TEXT'}) === 'abcdAATEXT' but remember: (As MDN Web Doc said,) Although the keys of an ordinary Object are ordered now, this was not always the case, and the order is complex. As a result, it's best not to rely on property order. So, don't expect replaceText will treat replacer key-values in any specific sort. Use replaceText to replace irrelevance replacer keys only. */ function replaceText(text, replacer) { if (Object.entries(replacer).length === 0) {return text;} const [models, targets] = Object.entries(replacer); const len = models.length; let text_arr = [{text: text, replacable: true}]; for (const [model, target] of Object.entries(replacer)) { text_arr = replace(text_arr, model, target); } return text_arr.map((text_obj) => (text_obj.text)).join(''); function replace(text_arr, model, target) { const result_arr = []; for (const text_obj of text_arr) { if (text_obj.replacable) { const splited = text_obj.text.split(model); for (const part of splited) { result_arr.push({text: part, replacable: true}); result_arr.push({text: target, replacable: false}); } result_arr.pop(); } else { result_arr.push(text_obj); } } return result_arr; } } })();