Greasy Fork is available in English.

粤语划词翻译

快捷粤语翻译查询

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name         粤语划词翻译
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  快捷粤语翻译查询
// @author       口吃者
// @match        http://*/*
// @include      https://*/*
// @include      file:///*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=shyyp.net
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM_registerMenuCommand
// @require      https://update.greasyfork.org/scripts/498507/1398070/sweetalert2.js
// @license MIT
// ==/UserScript==
const shyypTokenUrl = 'https://shyyp.net/api/gqgq2';
const shyypLongScriptUrl = 'https://shyyp.net/romanizer';//长文注音
const shyypConvertUrl = 'https://shyyp.net/translator';//粤普转换
const shyypSingleWorldUrl = 'https://shyyp.net/w/';
const shyypIconUrl = 'https://img.picui.cn/free/2024/10/25/671b96c09fe69.png'
const shyypIconMosaicUrl = 'https://img.picui.cn/free/2024/10/25/671b96c14d5cc.png'
const shyypIconOnlyHeadUrl = 'https://img.picui.cn/free/2024/10/25/671b96c1c28f3.png'
const yueyuemaoSingleWordUrl = 'https://www.yueyumao.com/detail/';
const yueyuemaoPhraseUrl = 'https://www.yueyumao.com/search.php?q=';
const yueyuemaoLongScriptUrl = 'https://www.yueyumao.com/';
const yueyuemaoIconCat01 = 'https://img.picui.cn/free/2024/10/26/671cb240d0071.png';
const yueyuemaoIconCat02 = 'https://img.picui.cn/free/2024/10/26/671cb240cf59a.png';
const yueyuemaoIconCat03 = 'https://img.picui.cn/free/2024/10/26/671cb240c851a.png';
const moreIconUrl = 'https://img.picui.cn/free/2024/10/26/671c6876ed78d.png';
let textEncry = '';
let selected;// 当前选中文本
let pageX;// 图标显示的 X 坐标
let pageY;// 图标显示的 Y 坐标
const dragFluctuation = 4;// 当拖动多少像素以上时不触发查询
const zIndex = '2147473647'; // 渲染图层
/**鼠标拖动*/
class Drag {
    constructor(element) {
        this.dragging = false;
        this.startDragTime = 0;
        this.stopDragTime = 0;
        this.mouseDownPositionX = 0;
        this.mouseDownPositionY = 0;
        this.elementOriginalLeft = parseInt(element.style.left);
        this.elementOriginalTop = parseInt(element.style.top);
        this.backAndForthLeftMax = 0;
        this.backAndForthTopMax = 0;
        this.element = element;

        // 绑定事件处理函数
        // 事件处理函数由dom元素调用,一般是指向dom元素,强制绑定到Drag类上
        this.startDrag = this.startDrag.bind(this);
        this.dragElement = this.dragElement.bind(this);
        this.stopDrag = this.stopDrag.bind(this);

        // 添加鼠标事件监听器
        this.attachEventListeners();
    }

    attachEventListeners() {
        this.element.addEventListener('mousedown', this.startDrag);
    }

    detachEventListeners() {
        window.removeEventListener('mousemove', this.dragElement);
        window.removeEventListener('mouseup', this.stopDrag);
    }

    startDrag(e) {
        //阻止默认鼠标事件,比如选中文字
        e.preventDefault();
        this.dragging = true;
        this.startDragTime = new Date().getTime();
        this.mouseDownPositionX = e.clientX;
        this.mouseDownPositionY = e.clientY;
        this.elementOriginalLeft = parseInt(this.element.style.left);
        this.elementOriginalTop = parseInt(this.element.style.top);
        this.backAndForthLeftMax = 0;
        this.backAndForthTopMax = 0;

        // 设置全局鼠标事件
        window.addEventListener('mousemove', this.dragElement);
        window.addEventListener('mouseup', this.stopDrag);
        log('startDrag');
    }

    stopDrag(e) {
        e.preventDefault();
        this.dragging = false;
        this.stopDragTime = new Date().getTime();
        this.detachEventListeners();
        log('stopDrag');
    }

    dragElement(e) {
        log('dragging');
        if (!this.dragging) {
            return;
        }
        e.preventDefault();

        // 移动元素
        this.element.style.left = `${this.elementOriginalLeft + (e.clientX - this.mouseDownPositionX)}px`;
        this.element.style.top = `${this.elementOriginalTop + (e.clientY - this.mouseDownPositionY)}px`;

        // 获取最大移动距离
        let left = Math.abs(this.elementOriginalLeft - parseInt(this.element.style.left));
        let top = Math.abs(this.elementOriginalTop - parseInt(this.element.style.top));

        //更新最大移动距离
        if (left > this.backAndForthLeftMax) {
            this.backAndForthLeftMax = left;
        }
        if (top > this.backAndForthTopMax) {
            this.backAndForthTopMax = top;
        }
        log('dragElement');
    }
}
(function() {
    'use strict';
    const icon = document.createElement('tr-icon');// 翻译图标
    icon.id = 'cantonese_translate';
    icon.style.cssText = 'display: none;top: 186px;left: 37px;position: absolute;z-index: 2147473647;cursor:move;';
    const imgShyyp = getImg(shyypIconUrl, 'shyyp', '羊羊粤语-长文注音');
    const imgShyyp01 = getImg(shyypIconMosaicUrl, 'shyyp01', '羊羊粤语-单字查询');
    const imgShyyp02 = getImg(shyypIconOnlyHeadUrl, 'shyyp03', '羊羊粤语-粤普转换');
    const imgYueyuemao = getImg(yueyuemaoIconCat01, 'yueyuemao', '粤语猫-长文注音');
    const imgYueyuemao01 = getImg(yueyuemaoIconCat02, 'yueyuemao01', '粤语猫-单字查询');
    const imgYueyuemao02 = getImg(yueyuemaoIconCat03, 'yueyuemao02', '粤语猫-关联词组');
    const ifMore = getImg(moreIconUrl, 'more', '更多-折叠其后功能)');
    const iconsArray = [
        {id:1, iconEle: imgShyyp, name: imgShyyp.getAttribute('title'), mouseupFuc: longscriptPopup}, 
        {id:2, iconEle: imgShyyp01, name: imgShyyp01.getAttribute('title'), mouseupFuc: singleWorldPopup},
        {id:3, iconEle: imgShyyp02, name: imgShyyp02.getAttribute('title'), mouseupFuc: toMandarionOrCantonese}, 
        {id:7, iconEle: ifMore, name: ifMore.getAttribute('title'), mouseupFuc: showMore},
        {id:4, iconEle: imgYueyuemao, name: imgYueyuemao.getAttribute('title'), mouseupFuc: longscriptQueryYueYueMao}, 
        {id:5, iconEle: imgYueyuemao01, name: imgYueyuemao01.getAttribute('title'), mouseupFuc: singleWordQueryYueYueMao}, 
        {id:6, iconEle: imgYueyuemao02, name: imgYueyuemao02.getAttribute('title'), mouseupFuc: phraseCorrelationYueYueMao}
    ];
    const sortOrder = GM_getValue('sortOrder', iconsArray.map(icon => icon.id));
    const hideConfig = GM_getValue('hideConfig', {});
    const customIcons = getCustomMadeIconArray(sortOrder);
    let isIconImgMore = false;
    customIcons.forEach(iconCustom => {
        const iconEleNew = iconCustom.iconEle;
        iconEleNew.addEventListener('mouseup', iconCustom.mouseupFuc);
        if (isIconImgMore) {
            iconEleNew.setAttribute('is-more', 'true');
        }
        if (iconCustom.id === 7) {
            isIconImgMore = true;
        }
        if (!hideConfig[iconCustom.id]) {
            icon.appendChild(iconEleNew);
        }
    });

    // 绑定图标拖动事件
    const iconDrag = new Drag(icon);
    //区分拖动和点击事件,有足够位移才触发窗口事件
    document.body.appendChild(icon);
    // 鼠标事件:防止选中的文本消失;显示、隐藏翻译图标
    document.addEventListener('mouseup', showIcon);
    // 选中变化事件
    document.addEventListener('selectionchange', showIcon);
    document.addEventListener('touchend', showIcon);
    //粤普转换自动化操作
    window.onload = () =>{
        checkUrlAndExecute(async function auto() {
            selected = await GM.getValue('selectedText', '');
            await new Promise(resolve => setTimeout(resolve, 200));
            var textareaEle = document.querySelector("#stage0");
            textareaEle.value = selected;
        } ,shyypConvertUrl)
        checkUrlAndExecute(async function auto() {
            selected = await GM.getValue('selectedText', '');
            await new Promise(resolve => setTimeout(resolve, 200));
            var textareaEle = document.querySelector("#hanzi_string");
            textareaEle.value = selected;
            await new Promise(resolve => setTimeout(resolve, 200));
            var butttonSubmit = document.querySelector("#form_1 button");
            butttonSubmit.click();
        } ,yueyuemaoLongScriptUrl)
    }
    const listItems = customIcons.map((icon, index) => (
        `
        <li data-index="${index}" draggable="true">
            ${icon.name}
            <input data-id="${icon.id}" type="checkbox" ${hideConfig[icon.id] ? '' : 'checked'}>
        </li>`
    )).join('');
    GM_registerMenuCommand("功能定制化", function() {
        Swal.fire({
            title: '个性化',
            html: `
                <div id="swIconDiv" style="max-height: 300px; overflow: auto;">
                    <ul id="iconList" style="list-style-type:none; margin:0; padding:20px;">
                        ${listItems}
                    </ul>
                </div>
                <p style="font-size: small;color: #A7A7A7">拖动调整顺序,确认后刷新生效</p>
                `,
            showCancelButton: true,
            showDenyButton: true,
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            denyButtonText: `重置`,
            didOpen: () => {
                // 在弹窗完全打开后初始化拖放功能
                const iconList = document.getElementById('iconList');

                iconList.addEventListener('dragstart', dragStartHandler);
                iconList.addEventListener('drop', dropHandler);
                iconList.addEventListener('dragover', dragOverHandler);

                // 在拖动结束时移除样式
                iconList.addEventListener('dragend', dragEndHandler);

                // 设置可拖动属性
                Array.from(iconList.children).forEach(item => {
                    item.draggable = true;
                });
            },
            willClose: () => {
                // 在关闭前清除事件监听器以防止内存泄漏
                const iconList = document.getElementById('iconList');
                iconList.removeEventListener('dragstart', dragStartHandler);
                iconList.removeEventListener('drop', dropHandler);
                iconList.removeEventListener('dragover', dragOverHandler);
                iconList.removeEventListener('dragend', dragEndHandler);
            },
            preConfirm: () => {
                document.querySelectorAll('#iconList input').forEach(hideInput => {
                    if (!hideInput.checked) {
                        hideConfig[parseInt(hideInput.getAttribute('data-id'))] = true;
                    }else {
                        delete hideConfig[parseInt(hideInput.getAttribute('data-id'))];
                    }
                });
                GM_setValue('hideConfig', hideConfig);
                return {
                    updatedIcons: customIcons.slice() // 返回修改后的图标数组副本
                };
            }
        }).then((result) => {
            if (result.isConfirmed) {
                const ids = result.value.updatedIcons.map(({id}) => id);
                GM_setValue('sortOrder', ids);
                console.log(result.value.updatedIcons); // 打印调整后的图标顺序
            } else if (result.isDenied) {
                GM_setValue('sortOrder', iconsArray.map(icon => icon.id));
                GM_setValue('hideConfig', {});
            }
        });
    });


    var cssText = `
        #cantonese_translate img:hover{
            cursor:pointer;
        }
        #cantonese_translate img:hover{
            border:1px solid #1ABB27
        }
        #cantonese_translate img{
            cursor:pointer;
            display:inline-block;
            width:20px;
            height:20px;
            border:1px solid #dfe1e5;
            border-radius:4px;
            background-color:rgba(255,255,255,1);
            padding:2px;
            margin:0;
            margin-right:5px;
            box-sizing:content-box;vertical-align:middle
        }
        #iconList li:hover{
            cursor:pointer;
            border:1px solid #1ABB27;
        }
        #swIconDiv + p:hover{
            background-color: lightgoldenrodyellow;
            font-weight:bold;
        }
    `
    GMaddStyle(cssText);
    /*  获取长文注音路径参数x请求的json*/
    function createMutationJson(srcValue) {
        const queryTemplate = `mutation Submit($src: String!){ submitSrc(src: $src) }`;
        const variables = { src: srcValue };
        const query = `{"query":"${queryTemplate}","variables":${JSON.stringify(variables)}}`;
        return JSON.parse(query);
    }
    async function sendPostRequest(url, data) {
        const body = JSON.stringify(data);
        const headers = new Headers({
            'Content-Type': 'application/json'
        });
        const options = {
            method: 'POST',
            headers,
            body
        };
        try {
            const response = await fetch(url, options);
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json();
        } catch (error) {
            console.error('Failed to fetch:', error);
        }
    }
    function sendPostRequestWithGM(url, data) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                data: JSON.stringify(data),
                headers: {
                    'Content-Type': 'application/json'
                },
                onload: function(response) {
                    resolve(JSON.parse(response.responseText));
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }
    /* 长文获取整页html,目前不需要 */
    async function sendGetRequestHtml(urlBase, param) {
        const url = new URL(urlBase);
        url.searchParams.set('x', param);
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            const htmlContent = await response.text();
            return htmlContent;
        } catch (error) {
            console.error('Failed to fetch:', error);
        }
    }
    function getImg(src01, alt01, title01, options = {}) {
        // 创建一个新的 img 元素
        const img = document.createElement('img');
        // 设置 img 元素的基本属性
        img.src = src01;
        img.alt = alt01;
        img.title = title01;
        // 设置额外的属性
        if (options.width) {
            img.width = options.width;
        }
        if (options.height) {
            img.height = options.height;
        }
        if (options.className) {
            img.className = options.className;
        }
        if (options.style) {
            Object.keys(options.style).forEach(key => {
                img.style[key] = options.style[key];
            });
        }
        // 返回创建的 img 元素
        return img;
    }
    /** 弹出居中窗口 */
    function popupCenter(url, title = '_blank', w, h) {
        // 检查参数有效性
        if (!url || typeof url !== 'string') {
            console.error('Invalid URL provided');
            return null;
        }

        // 设置默认标题和窗口尺寸
        title = title || '_blank';
        w = Math.min(w, screen.availWidth);
        h = Math.min(h, screen.availHeight);

        // 计算居中位置
        let x = (screen.availWidth - w) / 2;
        let y = (screen.availHeight - h) / 2;

        // 确保窗口不会超出屏幕边界
        x = Math.max(x, 0);
        y = Math.max(y, 0);

        // 打开新窗口
        let win;
        try {
            win = window.open(url, title, `width=${w},height=${h},left=${x},top=${y}`);
            if (win) {
                win.focus();
                let closeNewWindow =  window.addEventListener('focus', function() {
                    win.close();
                    window.removeEventListener('focus', closeNewWindow);
                });
            } else {
                throw new Error('Failed to open the window');
            }
        } catch (e) {
            console.error('Error opening the window:', e);
        }

        return win;
    }
    /**显示 icon*/
    function showIcon(e) {
        log('showIcon event:', e);
        let offsetX = -100; // 横坐标翻译图标偏移
        let offsetY = -40; // 纵坐标翻译图标偏移
        // 更新翻译图标 X、Y 坐标
        if (e.pageX && e.pageY) { // 鼠标
            log('mouse pageX/Y');
            pageX = e.pageX;
            pageY = e.pageY;
        }
        if (e.changedTouches) { // 触屏
            if (e.changedTouches.length > 0) { // 多点触控选取第 1 个
                log('touch pageX/Y');
                pageX = e.changedTouches[0].pageX;
                pageY = e.changedTouches[0].pageY;
                // 触屏修改翻译图标偏移(Android、iOS 选中后的动作菜单一般在当前文字顶部,翻译图标则放到底部)
                offsetX = -26; // 单个翻译图标块宽度
                offsetY = 16 * 3; // 一般字体高度的 3 倍,距离系统自带动作菜单、选择光标太近会导致无法点按
            }
        }
        log(`selected:${selected}, pageX:${pageX}, pageY:${pageY}`)
        if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) { // 点击了翻译图标
            e.preventDefault();
            return;
        }
        selected = window.getSelection().toString().trim(); // 当前选中文本
        GM_setValue('selectedText', selected);
        log(`selected:${selected}, icon display:${icon.style.display}`);
        if (selected && icon.style.display != 'block' && pageX && pageY) { // 显示翻译图标
            log('show icon');
            icon.style.top = `${pageY + offsetY}px`;
            icon.style.left = `${pageX + offsetX}px`;
            icon.style.display = 'block';
            // 兼容部分 Content Security Policy
            icon.style.position = 'absolute';
            icon.style.zIndex = zIndex;
        } else if (!selected) { // 隐藏翻译图标
            log('hide icon');
            hideIcon();
        }
    }
    /**隐藏 icon*/
    function hideIcon() {
        icon.style.display = 'none';
        pageX = 0;
        pageY = 0;
        icon.querySelectorAll('img[is-more]').forEach(ele => {
            ele.style.display = 'none';
        });
    }
    /* 长文注音弹出 */
    async function longscriptPopup(){
        try {
            const response = await sendPostRequestWithGM(shyypTokenUrl, createMutationJson(selected));
            textEncry = response.data.submitSrc;
            console.log(textEncry);
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(`${shyypLongScriptUrl}?x=${textEncry}`, '长文注音', 1024, 800);
            }
        } catch (error) {
            console.error('Error:', error);
        }
    }
    /* 单字弹出 */
    async function singleWorldPopup(){
        try {
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(`${shyypSingleWorldUrl}${selected}`, '单字查询', 1024, 800);
            }
        } catch (error) {
            console.error('Error:', error);
        }
    }
    /* 粤普转换弹出 */
    async function toMandarionOrCantonese(){
        try {
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(shyypConvertUrl, '粤普转换', 1024, 800);
            }
        } catch (error) {
            console.error('Error:', error);
        }
    }
    /* 粤粤猫 单字查询 */
    async function singleWordQueryYueYueMao(){
        try {
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(`${yueyuemaoSingleWordUrl}${selected}.html`, '单字查询', 1024, 800);
            }
        } catch(error) {}
    }
    /* 粤粤猫 长文注音 */
    async function longscriptQueryYueYueMao(){
        try {
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(yueyuemaoLongScriptUrl, '长文注音', 1024, 800);
            }
        } catch(error) {}
    }

    /* 粤粤猫 关联词组 */
    async function phraseCorrelationYueYueMao(){
        try {
            await new Promise(resolve => setTimeout(resolve, 100));
            if (iconDrag.backAndForthLeftMax <= dragFluctuation && iconDrag.backAndForthTopMax <= dragFluctuation) {
                popupCenter(`${yueyuemaoPhraseUrl}${selected}`, '长文注音', 1024, 800);
            }
        } catch(error) {}
    }
    /* 新窗口自动化操作 */
    function checkUrlAndExecute(customFunction, targetUrl) {
        // 获取当前页面的完整URL
        const currentUrl = window.location.href;
        
        // 检查当前URL是否与目标URL相等
        if (currentUrl === targetUrl) {
            // 如果URL匹配,则执行自定义函数
            customFunction();
        }
    }
    /* 返回定制化顺序的图标数组 */
    function getCustomMadeIconArray(defaultOrder) {
        // 根据 defaultOrder 排序图标数组
        console.log(defaultOrder)
        //排序逻辑是通过比较 a 和 b 的 id 在 defaultOrder 数组中的索引来决定的,与id大小无关
        const sortedIcons = iconsArray.filter(icon => defaultOrder.includes(icon.id))
                                 .sort((a, b) => defaultOrder.indexOf(a.id) - defaultOrder.indexOf(b.id));
        return sortedIcons;
    }
    /* 更多 */
    function showMore() {
        icon.querySelectorAll('img[is-more]').forEach(ele => {
            if (ele.style.display == 'inline-block') {
                ele.style.display = 'none';
            } else {
                ele.style.display = 'inline-block';
            }
        });
    }
    function arrayMove(arr, fromIndex, toIndex) {
        const element = arr[fromIndex];
        arr.splice(fromIndex, 1);
        arr.splice(toIndex, 0, element);
        return arr;
    }
/*     function setupIconList() {
        const sortOrder = GM_getValue('sortOrder', iconsArray.map(icon => icon.id));
        const hideConfig = GM_getValue('hideConfig', {});

        const icons = getCustomMadeIconArray(sortOrder);

        // 清空现有的图标列表
        document.querySelectorAll('.icon-item').forEach(ele => ele.remove());

        // 重新渲染图标列表
        icons.forEach((icon, index) => {
            const iconItem = document.createElement('div');
            iconItem.className = 'icon-item';

            const upButton = document.createElement('button');
            upButton.textContent = '⬆️';
            upButton.onclick = function() {
                if (index > 0) {
                    const newIcons = arrayMove(icons, index, index - 1);
                    const newSortOrder = newIcons.map(i => i.id);
                    GM_setValue('sortOrder', newSortOrder);
                    setupIconList(); // 重新设置图标列表
                }
            };

            const downButton = document.createElement('button');
            downButton.textContent = '⬇️';
            downButton.onclick = function() {
                if (index < icons.length - 1) {
                    const newIcons = arrayMove(icons, index, index + 1);
                    const newSortOrder = newIcons.map(i => i.id);
                    GM_setValue('sortOrder', newSortOrder);
                    setupIconList(); // 重新设置图标列表
                }
            };

            const toggleVisibilityButton = document.createElement('button');
            toggleVisibilityButton.textContent = hideConfig[icon.id] ? '显示' : '隐藏';
            toggleVisibilityButton.onclick = function() {
                if (toggleVisibilityButton.textContent === '隐藏') {
                    hideConfig[icon.id] = true;
                    toggleVisibilityButton.textContent = '显示';
                } else {
                    delete hideConfig[icon.id];
                    toggleVisibilityButton.textContent = '隐藏';
                }
                GM_setValue('hideConfig', hideConfig);
                setupIconList(); // 重新设置图标列表
            };

            const iconLabel = document.createElement('span');
            iconLabel.className = 'icon-name';
            iconLabel.textContent = icon.name;

            iconItem.appendChild(upButton);
            iconItem.appendChild(downButton);
            iconItem.appendChild(toggleVisibilityButton);
            iconItem.appendChild(iconLabel);

            document.body.appendChild(iconItem);
        });
    } */
    function swapIcons(index1, index2) {
        // 获取图标列表
        if(index1 === index2){
            return;
        }
        if(index1 > index2){
            swapIcons(index2, index1);
            return;
        }
        const iconList = document.getElementById('iconList');
        // 根据data-index获取图标元素
        const icon1 = iconList.querySelector(`[data-index="${index1}"]`);
        const icon2 = iconList.querySelector(`[data-index="${index2}"]`);
        if (!icon1 || !icon2) {
            console.error('Invalid index provided.');
            return;
        }
        // 获取两个图标在父节点中的位置
        const parent = icon1.parentNode;
/*             const index1Pos = Array.from(parent.children).indexOf(icon1);
        const index2Pos = Array.from(parent.children).indexOf(icon2); */
        let tempIcon1Next = icon1.nextSibling;
        // 交换它们的位置
        parent.insertBefore(icon1, icon2.nextSibling);
        parent.insertBefore(icon2, tempIcon1Next);

    }
    // 初始化拖放功能
    const dragStartHandler = function(e) {
        e.dataTransfer.setData('text/plain', e.target.dataset.index);
    };

    const dropHandler = function(e) {
        e.preventDefault(); // 阻止默认行为

        // 获取当前拖动元素的新位置
        const newIndex = e.target.closest('li') ? e.target.closest('li').dataset.index : e.target.children.length - 1;
        const oldIndexStr = e.dataTransfer.getData('text/plain');
        const oldIndex = parseInt(oldIndexStr);

        if (newIndex === oldIndex) return;

        // 更新 DOM 中的列表项
        const itemToMove = this.querySelector(`li[data-index="${oldIndex}"]`);

        // 确保在正确的位置插入或移除元素
        swapIcons(newIndex, oldIndex);

        // 更新 icons 数组中的顺序
        const temp = customIcons[newIndex];
        customIcons[newIndex] = customIcons[oldIndex];
        customIcons[oldIndex] = temp;

        // 更新列表项的 data-index 属性
        for (let i = 0; i < customIcons.length; i++) {
            const listItem = this.querySelector(`#iconList :nth-child(${i+1})`);
            listItem.dataset.index = i.toString();
        }
    };

    const dragOverHandler = function(e) { 
        e.preventDefault(); 
        e.currentTarget.classList.add('drag-over');
    };

    const dragEndHandler = function(e) { 
        e.currentTarget.classList.remove('drag-over');
    };
})();
/**日志输出*/
function log(...args) {
    const debug = false;
    if (!debug) {
        return;
    }
    if (args) {
        for (let i = 0; i < args.length; i++) {
            console.log(args[i]);
        }
    }
}
function GMaddStyle(css){
    var myStyle = document.createElement('style');
    myStyle.textContent = css;
    var doc = document.head || document.documentElement;
    doc.appendChild(myStyle);
}