- // ==UserScript==
- // @name iconfont一键复制SVG
- // @namespace http://tampermonkey.net/
- // @version 1.2.0
- // @description 适用于 iconfont 快速复制 SVG 代码。支持图标库文件复制,矢量插画库文件复制。默认复制图标为20px,方便调整。
- // @author 2690874578@qq.com
- // @match https://www.iconfont.cn/*
- // @icon http://img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg
- // @require https://cdn.jsdelivr.net/npm/sweetalert2@11.7.3/dist/sweetalert2.all.min.js
- // @grant GM_registerMenuCommand
- // @grant GM_setValue
- // @grant GM_getValue
- // @run-at document-idle
- // @license GPL-3.0-only
- // ==/UserScript==
-
-
- (async function () {
- "use strict";
-
- // 脚本级全局常量
- // minify js and css
- // https://www.minifier.org/
- // minify html
- // https://www.atatus.com/tools/html-minify
- const SCRIPT_SETTINGS_POPUP = `
- <dialog id="script-popup"><form method="dialog"><h1 class="set-tit1">插件设置</h1><fieldset><legend class="set-tit2">复制大小</legend><input name="size" placeholder="" type="number" max="5000" min="4" class="set-size" value="20"></fieldset><fieldset class="set-radio"><legend class="set-tit2">复制格式</legend><input id="radio-svg" type="radio" name="format" value="svg" class="svg-ra" checked="checked"><label class="s-txt" for="radio-svg">SVG</label><input id="radio-png" type="radio" name="format" value="png" class="svg-ra"><label class="s-txt" for="radio-png">PNG</label></fieldset><fieldset class="btn-outdiv"><button id="dialog-cancel" class="btb-cancel" onclick="document.querySelector('#radio-cancel').click()">取消</button><button id="dialog-confirm" class="btn-ok" onclick="document.querySelector('#radio-confirm').click()">确定</button><input id="radio-cancel" type="radio" name="action" value="cancel" class="hidden"> <input id="radio-confirm" type="radio" name="action" value="confirm" class="hidden"></fieldset></form><style>
- dialog#script-popup{*{all:revert;margin:0;padding:0;border-top-width:0;padding-right:0;padding-left:0;border-left-width:0;border-bottom-width:0;padding-bottom:0;margin:0;padding-top:0;border-right-width:0}.hidden{display:none}ul{list-style:none}img{border:none}a{text-decoration:none}.fl{float:left}.fr{float:right}.clear{clear:both;overflow:hidden;height:0}.center{width:1300px;margin:0 auto}.lv{color:#379e3e!important}.hong{color:#f00!important}a:hover img{filter:alpha(opacity=80);opacity:.8;-webkit-transition:all 0.2s linear;-moz-transition:all 0.2s linear;-ms-transition:all 0.2s linear;transition:all 0.2s linear}&{position:fixed;width:340px;padding:40px 30px;background-color:#fff;border:none;border-radius:4px;box-sizing:border-box;box-shadow:rgb(0 0 0 / .2) 0 12px 28px 0,rgb(0 0 0 / .1) 0 2px 4px 0,rgb(255 255 255 / .05) 0 0 0 1px inset}legend{font-weight:700}.set-tit1{max-width:100%;text-align:center;text-transform:none;word-wrap:break-word;color:#333;font-size:30px;font-style:normal;font-weight:700;margin-bottom:32px}.set-tit2{font-size:18px;color:#333;margin-bottom:8px}.set-size{border:2px solid #e8ebf3;box-sizing:border-box;padding:0 16px;width:100%;margin-bottom:32px;height:46px;border-radius:8px;font-size:14px}.set-size:focus{border:2px solid #4569ff;outline:none;box-shadow:inset 0 1px 1px rgb(0 0 0 / .06),0 0 0 3px rgb(62 157 255 / 30%)}.svg-ra{width:16px;height:16px;vertical-align:middle;margin:0 0 4px 0;padding:0}.set-radio label{margin-left:.5em;margin-right:32px}.s-txt{color:#666;text-align:center;font-size:14px;font-style:normal;font-weight:400;line-height:27px}.btn-outdiv{display:flex;align-items:flex-start;gap:8px;align-self:stretch;margin-top:32px}.btb-cancel{display:flex;width:73px;height:39px;padding:10px;justify-content:center;align-items:center;gap:10px;background-color:#fff;border:1px solid #dcdee2;outline:0;color:#999;transition:all 0.3s ease;border-radius:8px}.btb-cancel:hover{background-color:#e2e8f0;transition:all 0.3s ease}input[type='radio']:checked{border-color:red}.btn-ok{display:flex;height:39px;padding:10px;justify-content:center;align-items:center;gap:10px;flex:1 0 0%;background-color:#4569ff;transition:all 0.3s ease;color:#fff;border:0;outline:0;border-radius:8px}.btn-ok:hover{background:linear-gradient(0deg,rgb(0 0 0 / .1) 0%,rgb(0 0 0 / .1) 100%),#3f5dfa;transition:all 0.3s ease}}
- </style></dialog>
- `;
- const SMALL_DELAY = 200,
- MEDIUM_DELAY = 500,
- LARGE_DELAY = 1000,
- XML = new XMLSerializer(),
- // 支持的图像导出格式
- OUT_FMTS = ["svg", "png"],
- // 设置表: [设置的名称, 默认值]
- MENU = {
- SVG_SIZE: ["svg_size", 20],
- OUT_FMT: ["out_fmt", "svg"],
- };
-
- /**
- * 脚本配置初始化 -------------------------------------------------------------------------
- */
-
- const CFG = (() => {
- /**
- * 判断 size 是否为 1 - 1000 内的整数
- * @param {string | number} size
- * @returns {boolean}
- */
- function _is_size_valid(size) {
- const _size = parseFloat(size);
- if (_size === NaN) return false;
- if (_size - parseInt(size) !== 0) return false;
- if (_size < 4 || _size > 5000) return false;
- return true;
- }
-
- /**
- * 生成 x 合规函数生成器
- * @param {(x: string) => T} parse
- * @param {(x: string) => boolean} is_valid
- * @param {() => T} getter
- * @returns {(alter_on_invalid: (string) => void) => (size: string) => number}
- */
- function _gen_validator(parse, is_valid, getter) {
- return (alter_on_invalid) => (x) => {
- const _x = parse(x);
- if (!is_valid(x)) {
- console.warn(`无效的插件设置项:`, x);
- alter_on_invalid(x);
- return getter();
- }
- return _x;
- };
- }
-
- /**
- * 从对话框关闭事件提取表单数据为字典
- * @param {CloseEvent} event
- * @returns {Map<string, string>}
- */
- function _extract_form_data(event) {
- const form = event.target.querySelector("form");
- const data = new FormData(form);
- return new Map(data.entries());
- }
-
- function _is_format_valid(format) {
- return OUT_FMTS.includes(format);
- }
-
- /**
- * 生成提示函数
- * @param {string} text 应该有 {x} 占位符
- * @param {string} title
- * @returns {(x: string) => void}
- */
- function _alert_invalid_x(text, title) {
- return (x) => alert_error(text.replace("{x}", x), title);
- }
-
- /**
- * 解包表单数据,合法化,对非法的弹窗提示
- * @param {Map<string, string>} data
- * @returns {{ size: number, format: string }}
- */
- function _unpack(data) {
- const alert_size = _alert_invalid_x(
- "尺寸 {x} 不是有效数字!",
- "无效尺寸!"
- );
- const alert_format = _alert_invalid_x(
- "图像格式 {x} 不受支持!",
- "无效格式!"
- );
- const get_stored_x = (x) => () => GM_getValue(...x);
- const validate_size = _gen_validator(
- parseInt,
- _is_size_valid,
- get_stored_x(MENU.SVG_SIZE)
- );
- const pass = (x) => x;
- const validate_format = _gen_validator(
- pass,
- _is_format_valid,
- get_stored_x(MENU.OUT_FMT)
- );
-
- const size = validate_size(alert_size)(data.get("size"));
- const format = validate_format(alert_format)(data.get("format"));
- return { size, format };
- }
-
- /**
- * 弹出脚本配置弹窗以获取配置
- * @param {(data: { size: number, format: string }, ...tasks: (data: { size: number, format: string }) => Promise<void>) => Promise<void>} on_success
- * @param {...(data: { size: number, format: string }) => Promise<void>} tasks
- * @returns {Promise<{ valid: boolean, size: number, format: string }>}
- */
- async function ask_for_config(on_success, ...tasks) {
- const event = await show_settings();
- const data = _extract_form_data(event);
- console.info(`从对话框中提取的表单数据:`, data);
-
- if (data.get("action") === "cancel") {
- show_msg("取消设置", "warning");
- return { valid: false };
- }
-
- const { size, format } = _unpack(data);
- on_success({ size, format }, ...tasks);
- return {
- valid: true,
- size,
- format,
- };
- }
-
- // 在 GM_registerMenuCommand 函数中注册(不可用)
- /**
- * 显式插件设置成功
- * @param {{ size: number, format: string }} _
- * @returns {Promise<void>}
- */
- async function _show_config_ok(_) {
- show_msg("插件设置成功~");
- console.info("插件设置成功弹窗已经触发");
- }
-
- // 在 GM_registerMenuCommand 函数中注册(不可用)
- /**
- * 设置复制图标的提示文本
- * @param {{ size: number, format: string }} config
- * @returns {Promise<void>}
- */
- async function _set_icon_title(config) {
- const { format } = config;
- const _format = format.toUpperCase();
- const icons = $("span.svg-copy");
-
- for (const span of icons) {
- span.title = "复制" + _format;
- }
- console.info(`全部图标 title 已经更新完成 -> ${_format}`);
- }
-
- /**
- * 当插件设置成功时批量执行任务
- * @param {{ size: number, format: string }} config
- * @param {...(data: { size: number, format: string }) => Promise<void>} tasks
- */
- async function on_config_success(config, ...tasks) {
- console.info("插件设置成功,正在批量执行后置任务,使用最新配置:", config);
- for (const task of tasks) {
- task(config);
- }
- }
-
- /**
- * 获取存储的值(失败时使用默认值)
- * @returns {{ size: number, format: string }}
- */
- function get_stored_config() {
- return {
- size: GM_getValue(...MENU.SVG_SIZE),
- format: GM_getValue(...MENU.OUT_FMT),
- };
- }
-
- /**
- * 为弹窗对话框插入存储的配置
- * @param {HTMLDialogElement} popup
- * @param {{ size: number, format: string }} config
- */
- function insert_config(popup, config) {
- const $ = (s) => popup.querySelector(s);
- $('input[name="size"]').value = config.size;
- $(`[id="radio-${config.format}"]`).click();
- }
-
- /**
- * 弹出脚本设置弹窗,返回弹窗关闭事件
- * @returns {Promise<CloseEvent>}
- */
- function show_settings() {
- const popup = $("#script-popup")[0];
- const config = get_stored_config();
- insert_config(popup, config);
- popup.showModal();
-
- return new Promise((resolve, _) => {
- popup.addEventListener("close", resolve);
- });
- }
-
- GM_registerMenuCommand("插件设置", async () => {
- const config = await ask_for_config(
- on_config_success,
- _show_config_ok,
- _set_icon_title,
- );
- if (!config.valid) return;
-
- GM_setValue(MENU.SVG_SIZE[0], config.size);
- GM_setValue(MENU.OUT_FMT[0], config.format);
- });
-
- return {
- /**
- * @returns {number}
- */
- get SVG_SIZE() {
- return GM_getValue(...MENU.SVG_SIZE);
- },
-
- /**
- * @returns {string}
- */
- get OUT_FMT() {
- return GM_getValue(...MENU.OUT_FMT);
- },
- };
- })();
-
- /**
- * 公用函数 ----------------------------------------------------------------------------
- */
-
- function fire(...args) {
- const Swal = window["Swal"];
- if (!(Swal instanceof Function)) {
- // debugger;
- const msg = "弹窗库 SweetAlert2 未加载!";
- alert(msg + "\n你将无法看到正常弹窗,但功能仍会执行!");
- console.warn("弹窗消息:", ...args);
- return Promise.reject(new Error(msg));
- }
- return Swal.fire(...args);
- }
-
- /**
- * 弹出小型提示框,2秒后消失
- * @param {string} text 提示文本
- * @param {"success" | "warning" | "error" | "info" | "question"} status 状态
- * @returns {Promise}
- */
- function show_msg(text = "复制成功,可以粘贴咯~", status = "success") {
- return fire({
- text,
- toast: true,
- timer: 2000,
- showConfirmButton: false,
- icon: status,
- position: "top",
- customClass: {
- popup: "copy-popup",
- htmlContainer: "copy-container",
- icon: "copy-icon",
- },
- });
- }
-
- /**
- * 显示警告弹窗
- * @param {string} text
- * @param {string} title
- */
- function alert_error(text, title = null) {
- fire({
- icon: "error",
- title,
- text,
- });
- }
-
- /**
- * 异步的等待 delay_ms 毫秒
- * @param {number} delay_ms
- * @returns {Promise<void>}
- */
- function sleep(delay_ms) {
- return new Promise((resolve) => setTimeout(resolve, delay_ms));
- }
-
- /**
- * 将 svg 元素序列化为大小为 20x20 的 svg 代码
- * @param {SVGElement} svg
- * @returns {string}
- */
- function svgToStr(svg) {
- // 设置大小
- svg.setAttribute("width", `${CFG.SVG_SIZE}`);
- svg.setAttribute("height", `${CFG.SVG_SIZE}`);
-
- // 序列化
- return XML.serializeToString(svg);
- }
-
- /**
- * 元素选择器
- * @param {string} selector 选择器
- * @returns {HTMLElement[]} 元素
- */
- function $(selector) {
- const self = this?.querySelectorAll ? this : document;
- return [...self.querySelectorAll(selector)];
- }
-
- /**
- * 安全元素选择器,直到元素存在时才返回元素列表,最多等待5秒
- * @param {string} selector 选择器
- * @returns {Promise<Array<HTMLElement>} 元素列表
- */
- async function $$(selector) {
- const self = this?.querySelectorAll ? this : document;
-
- for (let i = 0; i < 10; i++) {
- let elems = [...self.querySelectorAll(selector)];
- if (elems.length > 0) {
- return elems;
- }
- await sleep(200);
- }
-
- const not_found_error = new Error(
- `"${selector}" not found in 2s`
- );
- console.error(not_found_error);
- return [];
- }
-
- /**
- * 域名主函数 ----------------------------------------------------------------------
- */
-
- /**
- * iconfont 主函数
- */
- async function iconfont() {
- console.log("进入 iconfont");
- init_task();
-
- // 域名级全局常量
- const PATHS = ["search", "illustrations", "collections"];
- const STYLE_TEXT = `
- .force-hide{visibility:hidden!important}.block-icon-list li:hover div.icon-cover{display:grid;grid-template-columns:auto auto}.block-icon-list li .icon-cover span.cover-item-line{height:auto;line-height:50px}.svg-copy.disabled{color:#6d6d6d!important}.icon-fuzhidaima:before{font-size:24px}.copy-icon{border:none!important;margin:0 1.25em!important;margin:0 0 0 10px!important}.copy-container{margin:8px 16px!important;padding:0!important;font-size:14px!important}.copy-popup{top:60px;padding:4px 10px!important;height:44px!important;font-size:12px!important;width:fit-content!important;align-content:center;box-shadow:rgb(0 0 0 / .2) 0 12px 28px 0,rgb(0 0 0 / .1) 0 2px 4px 0,rgb(255 255 255 / .05) 0 0 0 1px inset!important}
- `;
-
- /**
- * 阻塞直到图标区存在
- */
- while (true) {
- if ($(".block-icon-list > li")[0]) break;
- await sleep(SMALL_DELAY);
- }
- console.log("图标区出现了,开始执行任务");
-
- function addStyle() {
- const id = "iconfont-svg-copy-style";
- if ($(`#${id}`)[0]) return;
-
- const style = document.createElement("style");
- style.id = id;
- style.innerHTML = STYLE_TEXT;
- document.head.append(style);
- }
-
- /**
- * 初始化 writeText 钩子
- * @returns {Function}
- */
- function initHookOnWriteText() {
- const writeText = navigator.clipboard.writeText;
- const boundedWriteText = (text) =>
- writeText.call(navigator.clipboard, text);
-
- /**
- *
- * @param {(value) => any} resolve
- * @returns {(text: string) => string}
- */
- function wrapHookedWriteText(resolve) {
- /**
- * @param {string} text
- * @returns {string}
- */
- return async function (text) {
- console.log("进入 hooked 的 writeText 函数");
-
- // 无论成功与否,writeText 都要改回去
- Object.defineProperty(navigator.clipboard, "writeText", {
- value: writeText,
- writable: false,
- enumerable: true,
- configurable: true,
- });
-
- // 没有取得 svg 字符串,解决为空字符串
- if (!`${text}`.includes("<svg")) {
- resolve("");
- return;
- }
-
- // 成功取得 svg 字符串,解决为SVG代码
- try {
- // await boundedWriteText(text);
- resolve(text);
- } catch (e) {
- return e;
- }
- };
- }
-
- function hookWriteText() {
- return new Promise((resolve) => {
- // 劫持 writeText 函数,直到一次调用后失效
- Object.defineProperty(navigator.clipboard, "writeText", {
- value: wrapHookedWriteText(resolve),
- writable: false,
- enumerable: true,
- configurable: true,
- });
- });
- }
- return hookWriteText;
- }
-
- /**
- * 返回期约,直到 writeText 被调用且复制内容包含 "<svg" 时才解决为 svg_str。
- * 如果调用 writeText 的内容不包含 "<svg",则解决为 ""。
- */
- const hookWriteText = initHookOnWriteText();
-
- /**
- * 在弹窗中获取 svg
- * @param {HTMLElement} card
- */
- async function copyInPopup(card) {
- // 禁用复制按钮
- const icon = $.call(card, ".svg-copy")[0];
- icon.classList.add("disabled");
- icon.removeEventListener("click", on_copy_icon_clicked, true);
-
- // 触发弹窗
- const download = $.call(card, "[title='下载'], [title='Download']")[0];
- download.click();
-
- // 等待弹窗加载完毕
- while (true) {
- if ($("[id*='dlg_'], [id*='mask_dlg_']").length) {
- break;
- }
- await sleep(SMALL_DELAY);
- }
-
- // 隐藏弹窗
- const dialogs = await $$("[id*='dlg_']");
- dialogs.forEach((elem) => elem.classList.add("force-hide"));
-
- let popup;
- for (let elem of dialogs) {
- if (elem.id.startsWith("dlg_")) {
- popup = elem;
- break;
- }
- }
- if (!popup) throw new Error("#dlg_ not found");
-
- // 取得复制SVG按钮
- const copy_btn = (await $$("#btn-copy-svg"))[0];
- if (!copy_btn) {
- alert_error("此插画无法复制!可能是版权受限!", "复制失败");
- return;
- }
-
- let svg_str = "";
- let i = 1;
- do {
- const copy_task = hookWriteText();
- copy_btn.click();
- await sleep(SMALL_DELAY);
- svg_str = await copy_task;
- console.info(`try copy svg in popup: ${i}`);
- i += 1;
- // debugger;
- } while (svg_str === "" && i < 10);
-
- if (svg_str === "") {
- alert_error("复制失败!可能是网络异常!请稍后再试!", "复制失败");
- return;
- }
-
- // debugger;
- await copy_svg_to_aim_fmt(svg_str);
-
- // 关闭弹窗
- $(".mp-e2e-dialog-close")[0].click();
- // 重新启用复制按钮
- icon.classList.remove("disabled");
- icon.addEventListener("click", on_copy_icon_clicked, true);
- }
-
- /**
- * 复制 blobs 为一个剪贴板对象
- * @param {Blob[]} blobs
- */
- async function copy_blobs(blobs) {
- console.log("blob to be written:", blobs);
- const bundle = {};
- blobs.forEach((blob) => {
- bundle[blob.type] = blob;
- });
- const item = new ClipboardItem(bundle);
- // 复制到剪贴板
- try {
- await navigator.clipboard.write([item]);
- // 提示复制成功
- show_msg();
- }
- catch (err) {
- console.error(err);
- show_msg("复制到剪贴板失败!", "error");
- }
- }
-
- /**
- * svg str 转 png blob
- * @param {string} svg
- * @returns {Promise<Blob[]>}
- */
- async function svg_to_png(svg) {
- // prepare an image for format convertion
- const img = new Image();
- const svg_blob = new Blob([svg], { type: "image/svg+xml" });
- const svg_url = URL.createObjectURL(svg_blob);
- img.src = svg_url;
-
- // draw white background
- const canvas = document.createElement("canvas");
- const size = CFG.SVG_SIZE;
- const sizes = [size, size];
- [canvas.width, canvas.height] = sizes;
- const ctx = canvas.getContext("2d");
- ctx.fillStyle = "transparent";
- ctx.fillRect(0, 0, ...sizes);
-
- // draw image
- await new Promise((resolve) => {
- img.onload = resolve;
- });
- ctx.drawImage(img, 0, 0);
- // release svg resource
- URL.revokeObjectURL(svg_url);
-
- // convert to png
- return new Promise((resolve) =>
- canvas.toBlob(resolve)
- );
- }
-
- /**
- * 复制 svg 代码为目标格式到剪贴板
- * @param {string} svg_str
- */
- async function copy_svg_to_aim_fmt(svg_str) {
- const fmt_to_blobs = new Map([
- ["svg", async () => [
- new Blob([svg_str], { type: "text/plain" }),
- new Blob([svg_str], { type: "image/svg+xml" }),
- ]],
- ["png", async () => [await svg_to_png(svg_str)]],
- ]);
-
- const blob_for_exception = new Blob([`未知输出格式: ${CFG.OUT_FMT}`], { type: "text/plain" });
- const gen_ex_blobs = async () => [blob_for_exception];
- const gen_blobs = fmt_to_blobs.get(CFG.OUT_FMT) ?? gen_ex_blobs;
- const blobs = await gen_blobs();
-
- await copy_blobs(blobs);
- }
-
- /**
- * 当点击复制图标时复制 svg 到剪贴板
- * @param {PointerEvent} event
- */
- function on_copy_icon_clicked(event) {
- // 取得svg
- const card = event.target.closest("li");
- const svg = $.call(card, "svg")[0];
-
- // 如果是在 iframe 中的,那就要通过模拟点击下载的方式来获取
- if (!svg) {
- copyInPopup(card);
- return;
- }
-
- // 序列化
- const svg_str = svgToStr(svg);
- copy_svg_to_aim_fmt(svg_str);
- }
-
- function addCopyIcon() {
- // 获取卡片
- const cards = $(".block-icon-list > li");
- if (!cards[0]) throw new Error("无法选中图标块");
-
- // 制作按钮元素模板
- const template = document.createElement("span");
- template.title = "复制" + CFG.OUT_FMT.toUpperCase();
- template.classList.add(
- "cover-item",
- "iconfont",
- "cover-item-line",
- "icon-fuzhidaima",
- "svg-copy"
- );
-
- cards.forEach((card) => {
- // 添加复制图标
- const icon_copy = template.cloneNode();
- // 增加复制功能
- icon_copy.addEventListener("click", on_copy_icon_clicked, true);
- card.querySelector(".icon-cover").append(icon_copy);
- });
- }
-
- function add_script_settings_popup(popup_html) {
- // 弹窗已存在,退出
- if ($("#script-popup")[0]) return;
- // 弹窗不存在,创建并插入
- document.body.insertAdjacentHTML("beforeend", popup_html);
- }
-
- async function mainTask() {
- console.log("mainTask entered");
-
- const first_path = location.pathname.split("/")[1];
- console.log("当前一级路径:" + first_path);
-
- // 无关路径
- if (!first_path || !PATHS.includes(first_path)) return;
-
- // 等待直到图标块出现
- while (true) {
- if ($(".block-icon-list > li")[0]) break;
- await sleep(SMALL_DELAY);
- }
-
- // 如果已经存在按钮,退出主函数
- if ($(".icon-cover span.svg-copy")[0]) return;
- console.log("正在建造 [复制SVG] 图标...");
-
- addStyle();
- addCopyIcon();
-
- console.log("[复制SVG] 图标 建造完成");
- }
-
- function delayedTask() {
- setTimeout(mainTask, 0);
- }
-
- function getIconsBox(block = true) {
- const s = ".block-icon-list";
- if (block) return $(`${s} li`)[0].closest(s);
- return $$(`${s} li`).then((elems) => elems[0].closest(s));
- }
-
- function monitorIconsChanging() {
- const observer = new MutationObserver(delayedTask);
- observer.observe(getIconsBox(), { childList: true });
- }
-
- const onMainChanged = (function () {
- let icons_box = getIconsBox();
-
- async function inner() {
- const new_box = await getIconsBox(false);
- if (icons_box === new_box) return;
-
- icons_box = new_box;
- mainTask();
- monitorIconsChanging();
- }
-
- function delayed() {
- setTimeout(inner, LARGE_DELAY);
- }
-
- return delayed;
- })();
-
- async function monitorMainChanging() {
- const elem = (await $$("#magix_vf_main"))[0],
- observer = new MutationObserver(onMainChanged);
- observer.observe(elem, { attributes: true });
- }
-
- function init_task() {
- console.info("执行初始化任务");
- add_script_settings_popup(SCRIPT_SETTINGS_POPUP);
- }
-
- function main() {
- console.log("进入 iconfont.main");
- mainTask();
- monitorMainChanging();
- monitorIconsChanging();
- addEventListener("popstate", mainTask, true);
- }
-
- main();
- }
-
- function execute_route(route, host) {
- const action = route[host];
- if (!action) {
- console.log(`未知域名,不能处理:${host}`);
- return;
- }
- action();
- }
-
- /**
- * 路由,主函数入口
- */
- (() => {
- console.log("进入 route");
- const host = location.hostname;
- const route = {
- "www.iconfont.cn": iconfont,
- };
- execute_route(route, host);
- })();
-
- /**
- * [更新日志]
- *
- * 更新日期:2023/4/23
- * 更新版本:1.1.2
- * - 美化SVG尺寸设置弹窗
- *
- * 更新日期:2023/7/24
- * 更新版本:1.1.3
- * - 新增复制为PNG选项
- *
- * 更新日期:2024/6/15
- * 更新版本:1.1.4
- * - 重写配置弹窗,合并尺寸和图像类型到一个弹窗中
- *
- * 更新日期:2024/6/15
- * 更新版本:1.1.5
- * - 修复非图标页面不能打开设置弹窗的 BUG
- *
- * 更新日期:2024/6/16
- * 更新版本:1.1.6
- * - 修复复制图标 title 不随复制格式变化而变化的 BUG
- *
- * 更新日期:2024/7/11
- * 更新版本:1.1.7
- * - 修复复制部分插画失败的问题
- * - 当无法复制时弹窗提示(copyInPopup情形下)
- *
- * 更新日期:2024/7/11
- * 更新版本:1.1.8
- * - 调整 sweetalert2 依赖
- *
- * 更新日期:2024/7/11
- * 更新版本:1.1.9
- * - 优化部分代码
- */
- })();