Xbox CLoud Gaming优化整合

脚本免费!谨防上当受骗!整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764,3群826510890,4群82737876反馈】

// ==UserScript==
// @name                 Xbox CLoud Gaming优化整合
// @name:zh-CN           Xbox CLoud Gaming优化整合
// @namespace            http://tampermonkey.net/xbox/nft
// @version              3.10.3.3
// @author               奈非天
// @license              MIT
// @match                https://www.xbox.com/*/*play*
// @run-at               document-start
// @grant                none
// @require              https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.4.1/jquery.min.js
// @description:zh-cn    脚本免费!谨防上当受骗!整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764,3群826510890,4群82737876反馈】
// @description          脚本免费!谨防上当受骗!整合和修改现有脚本,优化项详见脚本说明。【若你有好的想法或者BUG可以进xbox云游戏QQ交流1群531602832,2群313340764,3群826510890,4群82737876反馈】

// ==/UserScript==
(function () {
    'use strict';
    // Your code here...

    //★★★★★★★★★★★★★★★★★★★★Reference Project License Agreement Begin 参考项目许可协议开始★★★★★★★★★★★★★★★★★★★★//

    /*  better-xcloud MIT License

        Copyright (c) 2023 redphx

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    */

    //★★★★★★★★★★★★★★★★★★★★Reference Project License Agreement End 参考项目许可协议结束★★★★★★★★★★★★★★★★★★★★//


    let nftxboxversion = 'v3.10.3.3';

    let naifeitian = {
        isType(obj) {
            return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
        },
        getValue(key) {
            try {
                return JSON.parse(localStorage.getItem(key));
            } catch (e) {
                return localStorage.getItem(key);
            }
        },

        setValue(key, value) {
            if (this.isType(value) === 'object' || this.isType(value) === 'array' || this.isType(value) === 'boolean') {
                return localStorage.setItem(key, JSON.stringify(value));
            }
            return localStorage.setItem(key, value);
        },
        isValidIP(ip) {
            let reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
            return reg.test(ip);
        },
        isNumber(val) {
            return !isNaN(parseFloat(val)) && isFinite(val);
        },
        toElement(key, onChange) {
            const CE = createElement;
            const setting = key;
            const currentValue = key['default'] == undefined ? key : key['default'];

            let $control;
            if (setting['options'] != undefined) {

                $control = CE('select', { id: 'xcloud_setting_' + key['name'] });

                for (let value in setting.options) {
                    const label = setting.options[value];

                    const $option = CE('option', { value: value }, label);
                    $control.appendChild($option);
                }

                $control.value = currentValue;
                $control.addEventListener('change', e => {
                    key['default'] = e.target.value;

                    this.setValue(key['name'], key);
                    onChange && onChange(e);
                });

            } else if (typeof setting.default === 'number') {
                $control = CE('input', { 'type': 'number', 'min': setting.min, 'max': setting.max });

                $control.value = currentValue;
                $control.addEventListener('change', e => {
                    let value = Math.max(setting.min, Math.min(setting.max, parseInt(e.target.value)));
                    e.target.value = value;

                    key['default'] = e.target.value
                    this.setValue(key['name'], key);
                    onChange && onChange(e);
                });
            } else {
                if (key.fps == undefined) {
                    $control = CE('input', { 'type': 'checkbox' });
                    $control.checked = currentValue;

                    $control.addEventListener('change', e => {
                        key['default'] = e.target.checked;
                        NFTconfig[key['name'].slice(0, -2)]['default'] = e.target.checked;
                        this.setValue(key['name'], key);
                        if (key['name'] == 'STATS_SLIDE_OPENGM' && e.target.checked) {
                            if (this.getValue('STATS_SHOW_WHEN_PLAYINGGM')['default']) {
                                $('#xcloud_setting_STATS_SHOW_WHEN_PLAYINGGM').click();
                            }
                        } else if (key['name'] == 'STATS_SHOW_WHEN_PLAYINGGM' && e.target.checked) {
                            if (this.getValue('STATS_SLIDE_OPENGM')['default']) {
                                $('#xcloud_setting_STATS_SLIDE_OPENGM').click();
                            }
                        }
                        onChange && onChange(e);
                    });
                } else {

                    let stats_info_sortedEntries = Object.entries(NFTconfig['stats_info']).sort((a, b) => a[1][1] - b[1][1]);
                    //流统计信息
                    $control = CE('div', { 'class': 'stats-container' });
                    stats_info_sortedEntries.forEach(entry => {

                        //entry[1][0]     是否选中
                        //entry[1][1]     顺序
                        //entry[1][2]     名字
                        let divElement = document.createElement('div');
                        divElement.className = `drag-handle ${entry[1][0] === true ? 'stats-selected' : 'stats-delete'}`;
                        divElement.draggable = "true";
                        divElement.dataset.name = entry[0];
                        divElement.dataset.index = entry[1][1];
                        divElement.textContent = entry[1][2];

                        let dragIndicator = document.createElement('div');
                        dragIndicator.className = "drag-indicator";
                        divElement.appendChild(dragIndicator);

                        $control.appendChild(divElement);


                    });
                    let placeholder = document.createElement('div');
                    placeholder.className = "placeholder drag-handle";
                    $control.appendChild(placeholder);
                }
            }

            $control.id = `xcloud_setting_${key.name}`;
            return $control;
        },
        isSafari() {
            let userAgent = userAgentOriginal.toLowerCase();
            if (userAgent.indexOf('safari') !== -1 && userAgent.indexOf('chrome') === -1) {
                return true;
            } else {
                return false;
            }
        },
        getGM(defaultValue, n) {
            let newval = this.getValue(n) == null ? defaultValue : this.getValue(n);
            if (newval?.options != undefined) {
                newval.options = defaultValue.options;
            }
            naifeitian.setValue(n, newval);
            return newval;
        },
        showSetting() {
            $('#settingsBackgroud').css('display', '');
            $('body').css('overflow', 'hidden');
        },
        hideSetting() {
            $('#settingsBackgroud').css('display', 'none');
            $('body').css('overflow', 'visible');
        },
        patchFunctionBind() {
            const nativeBind = Function.prototype.bind;
            Function.prototype.bind = function () {
                let valid = false;
                if (this.name.length <= 2 && arguments.length === 2 && arguments[0] === null) {
                    if (arguments[1] === 0 || (typeof arguments[1] === 'function')) {
                        valid = true;
                    }
                }

                if (!valid) {
                    return nativeBind.apply(this, arguments);
                }

                if (typeof arguments[1] === 'function') {
                    console.log('还原 Function.prototype.bind()');
                    Function.prototype.bind = nativeBind;
                }

                const orgFunc = this;
                const newFunc = (a, item) => {
                    if (NFTconfig['PATCH_ORDERS'].length === 0) {
                        orgFunc(a, item);
                        return;
                    }

                    naifeitian.patch(item);
                    orgFunc(a, item);
                }

                return nativeBind.apply(newFunc, arguments);
            };
        },
        patch(item) {
            // console.log('patch', '-----');
            let patchName;
            let appliedPatches;

            for (let id in item[1]) {
                if (NFTconfig['PATCH_ORDERS'].length <= 0) {
                    return;
                }

                appliedPatches = [];
                const func = item[1][id];
                let funcStr = func.toString();

                for (let groupIndex = 0; groupIndex < NFTconfig['PATCH_ORDERS'].length; groupIndex++) {
                    const group = NFTconfig['PATCH_ORDERS'][groupIndex];
                    let modified = false;

                    for (let patchIndex = 0; patchIndex < group.length; patchIndex++) {
                        const patchName = group[patchIndex];
                        if (appliedPatches.indexOf(patchName) > -1) {
                            continue;
                        }

                        const patchedFuncStr = naifeitian.handle_remote_patch(patchName, funcStr);
                        if (!patchedFuncStr) {
                            // Only stop if the first patch is failed
                            if (patchIndex === 0) {
                                break;
                            } else {
                                continue;
                            }
                        }

                        modified = true;
                        funcStr = patchedFuncStr;

                        console.log(`应用 "${patchName}" 修补`);
                        appliedPatches.push(patchName);

                        // Remove patch from group
                        group.splice(patchIndex, 1);
                        patchIndex--;
                    }

                    // Apply patched functions
                    if (modified) {
                        item[1][id] = eval(funcStr);
                    }

                    // Remove empty group
                    if (!group.length) {
                        NFTconfig['PATCH_ORDERS'].splice(groupIndex, 1);
                        groupIndex--;
                    }
                }
            }
        },
        handle_remote_patch(name, funcStr) {
            //根据不同的字符串执行不同的方法
            if (name == 'remotePlayConnectMode') {
                const text = 'connectMode:"cloud-connect"';
                if (!funcStr.includes(text)) {
                    return false;
                }

                return funcStr.replace(text, `connectMode:window.BX_REMOTE_PLAY_CONFIG?"xhome-connect":"cloud-connect",remotePlayServerId:(window.BX_REMOTE_PLAY_CONFIG&&window.BX_REMOTE_PLAY_CONFIG.serverId)||''`);

            } else if (name == 'remotePlayDirectConnectUrl') {
                const index = funcStr.indexOf('/direct-connect');
                if (index === -1) {
                    return false;
                }
                return funcStr.replace(funcStr.substring(index - 9, index + 15), 'https://www.xbox.com/play');
            } else if (name == 'remotePlayKeepAlive') {
                if (!funcStr.includes('onServerDisconnectMessage(e){')) {
                    return false;
                }

                funcStr = funcStr.replace('onServerDisconnectMessage(e){', `onServerDisconnectMessage (e) {
                const msg = JSON.parse(e);
                if (msg.reason === 'WarningForBeingIdle') {
                    try {
                        this.sendKeepAlive();
                        return;
                    } catch (ex) {}
                }
            `);
                return funcStr;

            } else if (name == 'EnableStreamGate') {
                const index = funcStr.indexOf(',EnableStreamGate:');
                if (index === -1) {
                    return false;
                }

                // Find the next "},"
                const endIndex = funcStr.indexOf('},', index);

                const newCode = `
EnableStreamGate: false,
PwaPrompt: false,
`;
                funcStr = funcStr.substring(0, endIndex) + ',' + newCode + funcStr.substring(endIndex);
                return funcStr;
            } else if (name == 'remotePlayGuideWorkaround') {
                const text = 'nexusButtonHandler:this.featureGates.EnableClientGuideInStream';
                if (!funcStr.includes(text)) {
                    return false;
                }

                return funcStr.replace(text, `nexusButtonHandler: !window.BX_REMOTE_PLAY_CONFIG && this.featureGates.EnableClientGuideInStream`);

            } else if (name == 'patchStreamHud') {
                const text = 'let{onCollapse';
                if (!funcStr.includes(text)) {
                    return false;
                }

                // 恢复悬浮窗 "..." 按钮
                funcStr = funcStr.replace(text, 'e.guideUI = null;' + text);

                return funcStr;

            } else if (name == "loadingEndingChunks") {
                // Add patches that are only needed when start playing
                const text = 'Symbol("ChatSocketPlugin")';
                if (!funcStr.includes(text)) {
                    return false;
                }

                NFTconfig['PATCH_ORDERS'] = NFTconfig['PATCH_ORDERS'].concat(NFTconfig['PLAYING_PATCH_ORDERS']);

                return funcStr;
            }
        },
        isDivTopOrBottomOutOfBounds(divElement) {
            const $div = $(divElement);
            $div.css("height","")

            // 获取div的边界信息
            const divRect = $div[0].getBoundingClientRect();
            const divTop = divRect.top;
            const divBottom = divRect.bottom;

            const viewportHeight = window.innerHeight || document.documentElement.clientHeight;

            return (
                divBottom > viewportHeight ||
                divTop < 0
            );
        }

    }
    //★★ 1=开   0=关 ★★//
    let default_language_list = { '智能简繁': 'Auto', '简体': 'zh-CN', '繁体': 'zh-TW' }
    let NFTconfig =
    {
        enableRemotePlay: 0,
        PATCH_ORDERS: [],
        PLAYING_PATCH_ORDERS: [],
        no_need_VPN_play: 1,
        regionBlock: {
            blockIp: '美服',
            options: {
                '韩服': '168.126.63.1',
                '美服': '4.2.2.2',
                '日服': '210.131.113.123'
            }
        },
        chooseLanguage: 1,
        IfErrUsedefaultGameLanguage: 'zh-CN',
        high_bitrate: 1,
        disableCheckNetwork: 1,
        IPv6: 0,
        autoFullScreen: 0,
        blockXcloudServer: 0,
        blockXcloudServerList: ['AustraliaEast', 'AustraliaSouthEast', 'BrazilSouth', 'EastUS', 'EastUS2', 'JapanEast', 'KoreaCentral', 'NorthCentralUs', 'SouthCentralUS', 'UKSouth', 'WestEurope', 'WestUS', 'WestUS2'],
        defaultXcloudServer: 'KoreaCentral',
        video_stretch: {
            default: 'none',
            options: {
                none: '无',
                fill: '填充',
                setting: '微调'
            },
            name: 'video_stretchGM'
        },
        rtcCodecPreferences: {
            default: '自动',
            options: [
                '默认',
                '自动'
            ]
        },
        video_stretch_x_y: {
            x: 0,
            y: 0,
            name: 'video_stretch_x_yGM'
        },

        noPopSetting: 0,
        disableTouchControls: 0,
        autoOpenOC: 1,
        autoShowTouch: true,
        STATS_SHOW_WHEN_PLAYING: {
            default: false,
            name: 'STATS_SHOW_WHEN_PLAYINGGM'
        },

        STATS_POSITION: {
            default: 'top-left',
            options: {
                'top-left': '上左',
                'top-center': '上中',
                'top-right': '上右'
            },

            name: 'STATS_POSITIONGM'
        },

        STATS_TRANSPARENT: {
            default: false,
            name: 'STATS_TRANSPARENTGM'
        },

        STATS_OPACITY: {
            default: 80,
            min: 10,
            max: 100,
            name: 'STATS_OPACITYGM'
        },

        STATS_TEXT_SIZE: {
            default: '0.9rem',
            options: {
                '0.9rem': '小',
                '1.0rem': '中',
                '1.1rem': '大'
            },

            name: 'STATS_TEXT_SIZEGM'
        },

        STATS_CONDITIONAL_FORMATTING: {
            default: false,
            name: 'STATS_CONDITIONAL_FORMATTINGGM'
        },
        STATS_SLIDE_OPEN: {
            default: false,
            name: 'STATS_SLIDE_OPENGM'
        },
        VIDEO_CLARITY: {
            default: 0,
            min: 0,
            max: 3,
            name: 'VIDEO_CLARITYGM'
        },

        VIDEO_CONTRAST: {
            default: 100,
            min: 0,
            max: 150,
            name: 'VIDEO_CONTRASTGM'
        },

        VIDEO_SATURATION: {
            default: 100,
            min: 0,
            max: 150,
            name: 'VIDEO_SATURATIONGM'
        },

        VIDEO_BRIGHTNESS: {
            default: 100,
            min: 0,
            max: 150,
            name: 'VIDEO_BRIGHTNESSGM'
        },
        antiKick: 0,
        useCustomfakeIp: 0,
        customfakeIp: '',
        xcloud_game_language: default_language_list['简体'],
        REMOTE_PLAY_RESOLUTION: {
            'default': '1080p',
            'options': {
                '1080p': '1080p',
                '720p': '720p',
            },
            'name': 'REMOTE_PLAY_RESOLUTIONGM'
        },
        REMOTE_SERVER_LIST: ['eau', 'seau', 'brs', 'eus', 'eus2', 'ejp', 'ckr', 'mxc', 'ncus', 'scus', 'uks', 'weu', 'wus', 'wus2'],

        stats_info: {
            fps: [true, 1, "帧率"],
            rtt: [true, 2, "延迟"],
            dt: [true, 3, "解码"],
            br: [true, 4, "码率"],
            pl: [true, 5, "丢包"],
            fl: [true, 6, "丢帧"],
        }
    }

    const integratekeys = Object.keys(NFTconfig);

    integratekeys.forEach(key => {
        NFTconfig[key] = naifeitian.getGM(NFTconfig[key], key + 'GM');
    });

    NFTconfig['PATCH_ORDERS'] = [

        NFTconfig['enableRemotePlay'] == 1 && ['remotePlayKeepAlive'],
        NFTconfig['enableRemotePlay'] == 1 && ['remotePlayDirectConnectUrl'],

    ];
    NFTconfig['PATCH_ORDERS'] = [

        NFTconfig['enableRemotePlay'] == 1 && ['remotePlayConnectMode'],
        NFTconfig['enableRemotePlay'] == 1 && ['remotePlayGuideWorkaround'],

        ['patchStreamHud'],
        ['EnableStreamGate']
    ]


    let regionsMenuItemList = [];
    let languageMenuItemList = [];
    let crturl = "";
    let canShowOC = null;

    let letmeOb = true;
    let checkIpsuc = false;

    let STREAM_WEBRTC;
    const ICON_VIDEO_SETTINGS = '<path d="M16 9.144A6.89 6.89 0 0 0 9.144 16 6.89 6.89 0 0 0 16 22.856 6.89 6.89 0 0 0 22.856 16 6.9 6.9 0 0 0 16 9.144zm0 11.427c-2.507 0-4.571-2.064-4.571-4.571s2.064-4.571 4.571-4.571 4.571 2.064 4.571 4.571-2.064 4.571-4.571 4.571zm15.704-7.541c-.065-.326-.267-.607-.556-.771l-4.26-2.428-.017-4.802c-.001-.335-.15-.652-.405-.868-1.546-1.307-3.325-2.309-5.245-2.953-.306-.103-.641-.073-.923.085L16 3.694l-4.302-2.407c-.282-.158-.618-.189-.924-.086a16.02 16.02 0 0 0-5.239 2.964 1.14 1.14 0 0 0-.403.867L5.109 9.84.848 12.268a1.14 1.14 0 0 0-.555.771 15.22 15.22 0 0 0 0 5.936c.064.326.267.607.555.771l4.261 2.428.017 4.802c.001.335.149.652.403.868 1.546 1.307 3.326 2.309 5.245 2.953.306.103.641.073.923-.085L16 28.306l4.302 2.407a1.13 1.13 0 0 0 .558.143 1.18 1.18 0 0 0 .367-.059c1.917-.648 3.695-1.652 5.239-2.962.255-.216.402-.532.405-.866l.021-4.807 4.261-2.428a1.14 1.14 0 0 0 .555-.771 15.21 15.21 0 0 0-.003-5.931zm-2.143 4.987l-4.082 2.321a1.15 1.15 0 0 0-.429.429l-.258.438a1.13 1.13 0 0 0-.174.601l-.022 4.606a13.71 13.71 0 0 1-3.623 2.043l-4.117-2.295a1.15 1.15 0 0 0-.559-.143h-.546c-.205-.005-.407.045-.586.143l-4.119 2.3a13.74 13.74 0 0 1-3.634-2.033l-.016-4.599a1.14 1.14 0 0 0-.174-.603l-.257-.437c-.102-.182-.249-.333-.429-.437l-4.085-2.328a12.92 12.92 0 0 1 0-4.036l4.074-2.325a1.15 1.15 0 0 0 .429-.429l.258-.438a1.14 1.14 0 0 0 .175-.601l.021-4.606a13.7 13.7 0 0 1 3.625-2.043l4.11 2.295a1.14 1.14 0 0 0 .585.143h.52c.205.005.407-.045.586-.143l4.119-2.3a13.74 13.74 0 0 1 3.634 2.033l.016 4.599a1.14 1.14 0 0 0 .174.603l.257.437c.102.182.249.333.429.438l4.085 2.327a12.88 12.88 0 0 1 .007 4.041h.007z" fill-rule="nonzero"/>';

    //视频调整
    const ICON_HD_VIDEO_SETTINGS = '<g transform="matrix(.142357 0 0 .142357 -2.22021 -2.22164)" fill="none" stroke="#fff" stroke-width="16"><circle cx="128" cy="128" r="40"/><path d="M130.05 206.11h-4L94 224c-12.477-4.197-24.049-10.711-34.11-19.2l-.12-36c-.71-1.12-1.38-2.25-2-3.41L25.9 147.24a99.16 99.16 0 0 1 0-38.46l31.84-18.1c.65-1.15 1.32-2.29 2-3.41l.16-36C69.951 42.757 81.521 36.218 94 32l32 17.89h4L162 32c12.477 4.197 24.049 10.711 34.11 19.2l.12 36c.71 1.12 1.38 2.25 2 3.41l31.85 18.14a99.16 99.16 0 0 1 0 38.46l-31.84 18.1c-.65 1.15-1.32 2.29-2 3.41l-.16 36A104.59 104.59 0 0 1 162 224l-31.95-17.89z"/></g>';

    //流监控
    const ICON_HD_STREAM_STATS = '<g transform="scale(2)" class="ICON_HD_STREAM_STATS_OFF" style="display:block"><path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7 7 0 0 0-2.79.588l.77.771A6 6 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755q-.247.248-.517.486z"></path><path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829"></path><path d="M3.35 5.47q-.27.24-.518.487A13 13 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7 7 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12z"></path></g><g transform="scale(2)" class="ICON_HD_STREAM_STATS_ON" style="display:none"><path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8M1.173 8a13 13 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5s3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5s-3.879-1.168-5.168-2.457A13 13 0 0 1 1.172 8z"/><path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0"/></g>';

    // Quickly create a tree of elements without having to use innerHTML
    function createElement(elmName, props = {}) {
        let $elm;
        const hasNs = 'xmlns' in props;

        if (hasNs) {
            $elm = document.createElementNS(props.xmlns, elmName);
        } else {
            $elm = document.createElement(elmName);
        }

        for (let key in props) {
            if (key === 'xmlns') {
                continue;
            }

            if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) {
                continue;
            }

            if (hasNs) {
                $elm.setAttributeNS(null, key, props[key]);
            } else {
                $elm.setAttribute(key, props[key]);
            }
        }

        for (let i = 2, size = arguments.length; i < size; i++) {
            const arg = arguments[i];
            const argType = typeof arg;

            if (argType === 'string' || argType === 'number') {
                $elm.textContent = arg;
            } else if (arg) {
                $elm.appendChild(arg);
            }
        }

        return $elm;
    }

    function setMachineFullScreen() {
        try {
            let element = document.documentElement;
            if (element.requestFullscreen) {
                element.requestFullscreen();
            } else if (element.mozRequestFullScreen) {
                element.mozRequestFullScreen();
            } else if (element.msRequestFullscreen) {
                element.msRequestFullscreen();
            } else if (element.webkitRequestFullscreen) {
                element.webkitRequestFullScreen();
            }
            screen?.orientation?.lock("landscape");
        } catch (e) {
        }
    }

    function exitMachineFullscreen() {
        try {
            screen?.orientation?.unlock();
            if (document.exitFullScreen) {
                document.exitFullScreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (element.msExitFullscreen) {
                element.msExitFullscreen();
            }
        } catch (e) {
        }
    }
    function exitGame() {

        canShowOC = null;
        setTimeout(RemotePlay.detect, 10);
        document.documentElement.style.overflowY = "";
        StreamStats.hideSettingsUi();
        letmeOb = true;
        StreamStats.stop();
        bindmslogoevent();
        $('.better-xcloud-quick-settings-bar').css("display", "none");
        if (NFTconfig['autoFullScreen'] == 1) {
            exitMachineFullscreen();
        }
        if (NFTconfig['noPopSetting'] == 0) {
            $('#popSetting').css('display', 'block');
        }
    }
    function inGame() {
        if (!IS_REMOTE_PLAYING) {
            let path = window.location.pathname;
            history.pushState({}, null, window.location.pathname.substr(0, window.location.pathname.indexOf("/launch/")));
            history.pushState({}, null, path);
        }
        document.documentElement.style.overflowY = "hidden";

        document.body.style.top = '0px';
        if (NFTconfig['autoFullScreen'] == 1) {
            setMachineFullScreen();
        }
        if (NFTconfig['noPopSetting'] == 0) {
            $('#popSetting').css('display', 'none');
        }
    }
    class StreamBadges {
        static get BADGE_PLAYTIME() { return '游玩时间'; };
        static get BADGE_BATTERY() { return '电量'; };
        static get BADGE_IN() { return '下载'; };
        static get BADGE_OUT() { return '上传'; };

        static get BADGE_SERVER() { return '服务器'; };
        static get BADGE_VIDEO() { return '编解码器'; };
        static get BADGE_AUDIO() { return '音频'; };

        static get BADGE_BREAK() { return 'break'; };

        static ipv6 = false;
        static resolution = null;
        static video = null;
        static audio = null;
        static fps = 0;
        static region = '';

        static startBatteryLevel = 100;
        static startTimestamp = 0;

        static #cachedDoms = {};

        static #interval;
        static get #REFRESH_INTERVAL() { return 3000; };

        static #renderBadge(name, value, color) {
            const CE = createElement;

            if (name === StreamBadges.BADGE_BREAK) {
                return CE('div', { 'style': 'display: block' });
            }

            let $badge;
            if (StreamBadges.#cachedDoms[name]) {
                $badge = StreamBadges.#cachedDoms[name];
                $badge.lastElementChild.textContent = value;
                return $badge;
            }

            $badge = CE('div', { 'class': 'better-xcloud-badge' },
                CE('span', { 'class': 'better-xcloud-badge-name' }, name),
                CE('span', { 'class': 'better-xcloud-badge-value', 'style': `background-color: ${color}` }, value));

            if (name === StreamBadges.BADGE_BATTERY) {
                $badge.classList.add('better-xcloud-badge-battery');
            }

            StreamBadges.#cachedDoms[name] = $badge;
            return $badge;
        }

        static async #updateBadges(forceUpdate) {
            if (!forceUpdate && !document.querySelector('.better-xcloud-badges')) {
                StreamBadges.#stop();
                return;
            }

            // 游玩时间
            let now = +new Date;
            const diffSeconds = Math.ceil((now - StreamBadges.startTimestamp) / 1000);
            const playtime = StreamBadges.#secondsToHm(diffSeconds);

            // 电量
            let batteryLevel = '100%';
            let batteryLevelInt = 100;
            let isCharging = false;
            if (navigator.getBattery) {
                try {
                    const bm = await navigator.getBattery();
                    isCharging = bm.charging;
                    batteryLevelInt = Math.round(bm.level * 100);
                    batteryLevel = `${batteryLevelInt}%`;

                    if (batteryLevelInt != StreamBadges.startBatteryLevel) {
                        const diffLevel = Math.round(batteryLevelInt - StreamBadges.startBatteryLevel);
                        const sign = diffLevel > 0 ? '+' : '';
                        batteryLevel += ` (${sign}${diffLevel}%)`;
                    }
                } catch (e) { }
            }

            const stats = await STREAM_WEBRTC.getStats();
            let totalIn = 0;
            let totalOut = 0;
            stats.forEach(stat => {
                if (stat.type === 'candidate-pair' && stat.state == 'succeeded') {
                    totalIn += stat.bytesReceived;
                    totalOut += stat.bytesSent;
                }
            });

            const badges = {
                [StreamBadges.BADGE_IN]: totalIn ? StreamBadges.#humanFileSize(totalIn) : null,
                [StreamBadges.BADGE_OUT]: totalOut ? StreamBadges.#humanFileSize(totalOut) : null,
                [StreamBadges.BADGE_PLAYTIME]: playtime,
                [StreamBadges.BADGE_BATTERY]: batteryLevel,
            };

            for (let name in badges) {
                const value = badges[name];
                if (value === null) {
                    continue;
                }

                const $elm = StreamBadges.#cachedDoms[name];
                $elm && ($elm.lastElementChild.textContent = value);

                if (name === StreamBadges.BADGE_BATTERY) {
                    // Show charging status
                    $elm.setAttribute('data-charging', isCharging);

                    if (StreamBadges.startBatteryLevel === 100 && batteryLevelInt === 100) {
                        $elm.style.display = 'none';
                    } else {
                        $elm.style = '';
                    }
                }
            }
        }

        static #stop() {
            StreamBadges.#interval && clearInterval(StreamBadges.#interval);
            StreamBadges.#interval = null;
        }

        static #secondsToHm(seconds) {
            const h = Math.floor(seconds / 3600);
            const m = Math.floor(seconds % 3600 / 60) + 1;

            const hDisplay = h > 0 ? `${h}小时` : '';
            const mDisplay = m > 0 ? `${m}分钟` : '';
            return hDisplay + mDisplay;
        }

        // https://stackoverflow.com/a/20732091
        static #humanFileSize(size) {
            let i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
            return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
        }

        static async render() {
            // Video
            let video = '';
            if (StreamBadges.resolution) {
                video = `${StreamBadges.resolution.height}p`;
            }

            if (StreamBadges.video) {
                video && (video += '/');
                video += StreamBadges.video.codec;
                if (StreamBadges.video.profile) {
                    let profile = StreamBadges.video.profile;
                    profile = profile.startsWith('4d') ? '高' : (profile.startsWith('42e') ? '中' : '低');
                    video += ` (${profile})`;
                }
            }

            // 音频
            let audio;
            if (StreamBadges.audio) {
                audio = StreamBadges.audio.codec;
                const bitrate = StreamBadges.audio.bitrate / 1000;
                audio += ` (${bitrate} kHz)`;
            }

            // 电量
            let batteryLevel = '';
            if (navigator.getBattery) {
                batteryLevel = '100%';
            }

            // Server + Region
            let server = StreamBadges.region;
            server += '@' + (StreamBadges.ipv6 ? 'IPv6' : 'IPv4');

            const BADGES = [
                [StreamBadges.BADGE_PLAYTIME, '1m', '#ff004d'],
                [StreamBadges.BADGE_BATTERY, batteryLevel, '#00b543'],
                [StreamBadges.BADGE_IN, StreamBadges.#humanFileSize(0), '#29adff'],
                [StreamBadges.BADGE_OUT, StreamBadges.#humanFileSize(0), '#ff77a8'],
                [StreamBadges.BADGE_BREAK],
                [StreamBadges.BADGE_SERVER, server, '#ff6c24'],
                video ? [StreamBadges.BADGE_VIDEO, video, '#742f29'] : null,
                audio ? [StreamBadges.BADGE_AUDIO, audio, '#5f574f'] : null,
            ];

            const $wrapper = createElement('div', { 'class': 'better-xcloud-badges' });
            BADGES.forEach(item => item && $wrapper.appendChild(StreamBadges.#renderBadge(...item)));

            await StreamBadges.#updateBadges(true);
            StreamBadges.#stop();
            StreamBadges.#interval = setInterval(StreamBadges.#updateBadges, StreamBadges.#REFRESH_INTERVAL);

            return $wrapper;
        }
    }
    class StreamStats {
        static #interval;
        static #updateInterval = 1000;

        static #$container;
        static #$fps;
        static #$rtt;
        static #$dt;
        static #$pl;
        static #$fl;
        static #$br;

        static #$settings;

        static #lastStat;

        static status() {
            return StreamStats.#interval != null;
        }

        static start() {
            clearInterval(StreamStats.#interval);

            StreamStats.#$container.classList.remove('better-xcloud-gone');
            StreamStats.#interval = setInterval(StreamStats.update, StreamStats.#updateInterval);
            $('#xcloud_setting_STATS_BUTTON').text("关闭监控");
            $('.ICON_HD_STREAM_STATS_ON').css("display", 'block');
            $('.ICON_HD_STREAM_STATS_OFF').css("display", 'none');

        }


        static stop() {
            clearInterval(StreamStats.#interval);

            StreamStats.#$container.classList.add('better-xcloud-gone');
            StreamStats.#interval = null;
            StreamStats.#lastStat = null;
            $('#xcloud_setting_STATS_BUTTON').text("启动监控");
            $('.ICON_HD_STREAM_STATS_ON').css("display", 'none');
            $('.ICON_HD_STREAM_STATS_OFF').css("display", 'block');
        }

        static toggle() {
            StreamStats.#isHidden() ? StreamStats.start() : StreamStats.stop();
            screenClicktohide();
            if (naifeitian.isDivTopOrBottomOutOfBounds(".better-xcloud-stats-settings")) {
                $(".better-xcloud-stats-settings").css("height", "90%");
            } else {
                $(".better-xcloud-stats-settings").css("height", "");
            }
        }

        static #isHidden = () => StreamStats.#$container.classList.contains('better-xcloud-gone');

        static update() {
            if (StreamStats.#isHidden() || !STREAM_WEBRTC) {
                StreamStats.stop();
                return;
            }

            try {

                STREAM_WEBRTC.getStats().then(stats => {
                    stats.forEach(stat => {
                        let grade = '';
                        if (stat.type === 'inbound-rtp' && stat.kind === 'video') {
                            // FPS
                            $(".stats_fps span").text(stat.framesPerSecond || 0);

                            // Packets Lost
                            const packetsLost = stat.packetsLost;
                            if (packetsLost != undefined) {
                                const packetsReceived = stat.packetsReceived;
                                const packetsLostPercentage = (packetsLost * 100 / ((packetsLost + packetsReceived) || 1)).toFixed(2);
                                $(".stats_pl span").text(`${packetsLost} (${packetsLostPercentage}%)`);
                            } else {
                                $(".stats_pl span").text(`-1 (-1%)`);
                            }

                            // Frames Dropped
                            const framesDropped = stat.framesDropped;
                            if (framesDropped != undefined) {
                                const framesReceived = stat.framesReceived;
                                const framesDroppedPercentage = (framesDropped * 100 / ((framesDropped + framesReceived) || 1)).toFixed(2);
                                $(".stats_fl span").text(`${framesDropped} (${framesDroppedPercentage}%)`);
                            } else {
                                $(".stats_fl span").text(`-1 (-1%)`);
                            }
                            if (StreamStats.#lastStat) {
                                const lastStat = StreamStats.#lastStat;
                                // Bitrate
                                const timeDiff = stat.timestamp - lastStat.timestamp;
                                const bitrate = 8 * (stat.bytesReceived - lastStat.bytesReceived) / timeDiff / 1000;
                                $(".stats_br span").text(`${bitrate.toFixed(2)} Mbps`);

                                // Decode time
                                const totalDecodeTimeDiff = stat.totalDecodeTime - lastStat.totalDecodeTime;
                                const framesDecodedDiff = stat.framesDecoded - lastStat.framesDecoded;
                                const currentDecodeTime = totalDecodeTimeDiff / framesDecodedDiff * 1000;
                                $(".stats_dt span").text(`${currentDecodeTime.toFixed(2)}ms`);

                                if (NFTconfig['STATS_CONDITIONAL_FORMATTING']['default']) {
                                    grade = (currentDecodeTime > 12) ? 'bad' : (currentDecodeTime > 9) ? 'ok' : (currentDecodeTime > 6) ? 'good' : '';
                                }
                                $(".stats_dt span").attr('data-grade', grade);
                            }

                            StreamStats.#lastStat = stat;
                        } else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
                            // Round Trip Time
                            const roundTripTime = typeof stat.currentRoundTripTime !== 'undefined' ? stat.currentRoundTripTime * 1000 : '???';
                            $(".stats_rtt span").text(`${roundTripTime}ms`);

                            if (NFTconfig['STATS_CONDITIONAL_FORMATTING']['default']) {
                                grade = (roundTripTime > 100) ? 'bad' : (roundTripTime > 75) ? 'ok' : (roundTripTime > 40) ? 'good' : '';
                            }
                            $(".stats_rtt span").attr('data-grade', grade);
                        }
                    });
                });
            } catch (e) { }
        }

        static #refreshStyles() {
            const PREF_POSITION = NFTconfig['STATS_POSITION']['default'];
            const PREF_TRANSPARENT = NFTconfig['STATS_TRANSPARENT']['default'];
            const PREF_OPACITY = NFTconfig['STATS_OPACITY']['default'];
            const PREF_TEXT_SIZE = NFTconfig['STATS_TEXT_SIZE']['default'];

            StreamStats.#$container.setAttribute('data-position', PREF_POSITION);
            StreamStats.#$container.setAttribute('data-transparent', PREF_TRANSPARENT);
            StreamStats.#$container.style.opacity = PREF_OPACITY + '%';
            StreamStats.#$container.style.fontSize = PREF_TEXT_SIZE;
        }

        static hideSettingsUi() {
            StreamStats.#$settings.style.display = 'none';
        }

        static toggleSettingsUi() {
            const display = StreamStats.#$settings.style.display;
            StreamStats.#$settings.style.display = display === 'block' ? 'none' : 'block';
            screenClicktohide();
            if (naifeitian.isDivTopOrBottomOutOfBounds(".better-xcloud-stats-settings")) {
                $(".better-xcloud-stats-settings").css("height", "90%");
            } else {
                $(".better-xcloud-stats-settings").css("height", "");
            }
        }

        static render() {
            if (StreamStats.#$container) {
                return;
            }

            const CE = createElement;
            StreamStats.#$container = CE('div', { 'class': 'better-xcloud-stats-bar better-xcloud-gone' },
                CE('div', { 'class': 'stats_fps' }, CE('label', {}, '帧率'),
                    StreamStats.#$fps = CE('span', {}, 0)),
                CE('div', { 'class': 'stats_rtt' }, CE('label', {}, '延迟'),
                    StreamStats.#$rtt = CE('span', {}, '0ms')),
                CE('div', { 'class': 'stats_dt' }, CE('label', {}, '解码'),
                    StreamStats.#$dt = CE('span', {}, '0ms')),
                CE('div', { 'class': 'stats_br' }, CE('label', {}, '码率'),
                    StreamStats.#$br = CE('span', {}, '0ms')),
                CE('div', { 'class': 'stats_pl' }, CE('label', {}, '丢包'),
                    StreamStats.#$pl = CE('span', {}, '0ms')),
                CE('div', { 'class': 'stats_fl' }, CE('label', {}, '丢帧'),
                    StreamStats.#$fl = CE('span', {}, '0ms')));

            let clicked_count = 0;
            StreamStats.#$container.addEventListener('ontouchstart' in document ? 'touchstart' : 'mousedown', function (e) {
                clicked_count++;
                setTimeout(function () {
                    clicked_count = 0;
                }, 500);

                if (clicked_count > 1) {
                    //双击
                    StreamStats.toggleSettingsUi();
                    clicked_count = 0;
                }
            }, false);

            document.documentElement.appendChild(StreamStats.#$container);

            const refreshFunc = e => {
                StreamStats.#refreshStyles()
            };
            const $position = naifeitian.toElement(NFTconfig['STATS_POSITION'], refreshFunc);

            let $open_button;
            const $showStartup = naifeitian.toElement(NFTconfig['STATS_SHOW_WHEN_PLAYING'], refreshFunc);
            const $transparent = naifeitian.toElement(NFTconfig['STATS_TRANSPARENT'], refreshFunc);
            const $formatting = naifeitian.toElement(NFTconfig['STATS_CONDITIONAL_FORMATTING'], refreshFunc);
            const $opacity = naifeitian.toElement(NFTconfig['STATS_OPACITY'], refreshFunc);
            const $textSize = naifeitian.toElement(NFTconfig['STATS_TEXT_SIZE'], refreshFunc);
            const $slideopen = naifeitian.toElement(NFTconfig['STATS_SLIDE_OPEN'], refreshFunc);
            const $stats_info = naifeitian.toElement(NFTconfig['stats_info'], refreshFunc);
            StreamStats.#$settings = CE('div', { 'class': 'better-xcloud-stats-settings' },
                CE('b', {}, '流监控设置'),
                CE('div', {},
                    CE('label', { 'for': `xcloud_setting_NFTconfig['STATS_SHOW_WHEN_PLAYING']` }, '自启动'),
                    $showStartup
                ),
                CE('div', {},
                    CE('label', {}, '位置'),
                    $position
                ),
                CE('div', {},
                    CE('label', {}, '统计信息'),
                    $stats_info
                ),
                CE('div', {},
                    CE('label', {}, '字体大小'),
                    $textSize
                ),
                CE('div', {},
                    CE('label', { 'for': `xcloud_setting_STATS_OPACITY` }, '透明度 (10-100%)'),
                    $opacity
                ),
                CE('div', {},
                    CE('label', { 'for': `xcloud_setting_STATS_TRANSPARENT` }, '背景透明'),
                    $transparent
                ),
                CE('div', {},
                    CE('label', { 'for': `xcloud_setting_STATS_CONDITIONAL_FORMATTING` }, '数值颜色'),
                    $formatting
                ),
                CE('div', {},
                    CE('label', { 'for': `xcloud_setting_STATS_SLIDE_OPEN` }, '仅悬浮窗展开时打开'),
                    $slideopen
                ),
                $open_button = CE('button', { 'id': 'xcloud_setting_STATS_BUTTON' }, '启动监控'));

            $open_button.addEventListener('click', () => {

                if (StreamStats.status()) {
                    //需关闭
                    StreamStats.stop();

                } else {
                    //需启动
                    StreamStats.start();

                }

            });
            document.documentElement.appendChild(StreamStats.#$settings);

            let stats_info_sortedEntries = Object.entries(NFTconfig['stats_info']).sort((a, b) => a[1][1] - b[1][1]);

            let infos = [];

            stats_info_sortedEntries.forEach(entry => {

                //entry[1][0]     是否选中
                //entry[1][1]     顺序
                //entry[1][2]     名字
                let tempDom = $(".stats_" + entry[0]).clone();

                if (entry[1][0]) {
                    tempDom.css("display", "block");
                } else {
                    tempDom.css("display", "none");
                }
                tempDom.css("border-right", "2px solid #fff");
                infos.push(tempDom);
                $(".stats_" + entry[0]).remove();

            });
            infos.forEach(function (item, index, array) {
                $(".better-xcloud-stats-bar").append(item);
            });
            $('.better-xcloud-stats-bar > *')
                .filter(function () {
                    return $(this).css('display') === 'block';
                })
                .last().css("border-right", '0px');


            //流统计信息事件

            let draggedItemIndex = -1;
            let draggedItem;
            let draggedItemClone; // 声明一个变量来存储被拖拽元素的副本
            let isClick = false;
            let isTouchstart = false;
            let touchTimeout;
            // 当拖拽开始时,添加拖拽中的样式,并设置拖拽数据
            $('.drag-handle').on('dragstart', function (event) {
                draggedItem = $(this);
                draggedItemIndex = draggedItem.index();

            });

            // 当拖拽对象在可放置区域上方移动时触发
            $('.drag-handle').on('dragover', function (event) {
                event.preventDefault();
                $('.drag-handle').removeClass('drag-over');

                $(this).addClass('drag-over');

                var placeholder = $('.placeholder');
                var target = $(this);

                if (event.originalEvent.clientY < target.offset().top + target.outerHeight() / 2) {
                    placeholder.insertBefore(target);
                } else {
                    placeholder.insertAfter(target);
                }
                if (!placeholder.prev().hasClass("dragging") && !placeholder.next().hasClass("dragging")) {
                    placeholder.show();
                }

            });

            // 当拖拽对象离开可放置区域时触发
            $('.drag-handle').on('dragleave', function (event) {
                $(this).removeClass('drag-over');

            });

            // 当拖拽对象被松开触发
            $('.drag-handle').on('dragend', function (event) {

                var droppedIndex = $('.placeholder').index();

                var draggedItem = $('.drag-handle').eq(draggedItemIndex);
                draggedItem.insertBefore($('.placeholder'));

                draggedItem.removeClass('dragging').hide().fadeIn();



                $('.drag-handle').removeClass('drag-over');
                $('.drag-handle').removeClass('dragging');

                $('.placeholder').hide();
                draggedItemClone?.remove(); // 移除被拖拽元素的副本

                let drag_index = 0;
                let infos = [];
                $('.drag-handle').each(function (index, d) {
                    let name = $(d).attr("data-name");
                    if (name != undefined) {
                        drag_index = drag_index + 1;
                        NFTconfig['stats_info'][name][1] = drag_index;
                        $(this).attr("data-index", drag_index);
                        let tempDom = $(".stats_" + name).clone();

                        if (!NFTconfig['stats_info'][name][0]) {
                            //隐藏
                            tempDom.css("display", "none");
                        } else {
                            tempDom.css("display", "block");
                        }
                        tempDom.css("border-right", "2px solid #fff");
                        infos.push(tempDom);
                        $(".stats_" + name).remove();
                    }
                });
                naifeitian.setValue("stats_infoGM", NFTconfig['stats_info']);

                infos.forEach(function (item, index, array) {
                    $(".better-xcloud-stats-bar").append(item);
                });
                $('.better-xcloud-stats-bar > *')
                    .filter(function () {
                        return $(this).css('display') === 'block';
                    })
                    .last().css("border-right", '0px');


            });


            // 添加触摸事件
            $('.drag-handle').on('touchstart', function (event) {
                isTouchstart = true;
                draggedItem = $(this);
                draggedItemIndex = $(this).index();

                touchTimeout = setTimeout(function () {
                    isClick = false;
                    if ($(".dragged-copy-item").length == 0) {
                        // 创建被拖拽元素的副本
                        draggedItemClone = draggedItem.clone().addClass('stats-container dragged-copy-item');
                        $('.better-xcloud-stats-settings').after(draggedItemClone);
                    } else {
                        draggedItemClone = $(".dragged-copy-item");
                    }

                }, 200);
                isClick = true;
            });

            $('.stats-container').on('touchmove', function (event) {
                event.preventDefault();
                if (isClick) { return; }
                draggedItem.addClass("dragging");

                $(".dragged-copy-item").css("display", "block");

                const touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];

                var previousElement = $(".better-xcloud-stats-settings");

                // 计算右边界位置
                var rightEdgeOfPrevious = previousElement.offset().left + previousElement.outerWidth() - 100;

                // 更新被拖拽元素的副本位置
                draggedItemClone.css({
                    top: touch.clientY - draggedItemClone.outerHeight() / 2 + 'px',
                    left: rightEdgeOfPrevious + 'px'
                });

                $('.drag-handle').each(function () {
                    if ($(this).hasClass("dragged-copy-item")) { return }
                    const itemOffset = $(this).offset().top;
                    const itemHeight = $(this).outerHeight();
                    if (draggedItem.is($(this))) return true;
                    let placeholder = $('.placeholder');
                    if (touch.clientY > itemOffset && touch.clientY < itemOffset + itemHeight) {
                        $(this).addClass('drag-over');


                        if (touch.clientY < itemOffset + itemHeight / 4) {
                            placeholder.insertBefore($(this));
                        } else {
                            placeholder.insertAfter($(this));
                        }
                        if (!placeholder.prev().hasClass("dragging") && !placeholder.next().hasClass("dragging")) {
                            placeholder.css("display", "block");
                        }

                        return false;
                    } else {
                        $(this).removeClass("drag-over");

                    }
                });
            });

            $('.stats-container').on('touchend', function (event) {
                isTouchstart = false;
                if (isClick) { return; }
                var droppedIndex = $('.placeholder').index();

                var draggedItem = $('.drag-handle').eq(draggedItemIndex);
                draggedItem.insertBefore($('.placeholder'));

                draggedItem.removeClass('dragging').hide().fadeIn();

                $('.drag-handle').removeClass('drag-over');
                $('.drag-handle').removeClass('dragging');

                $('.placeholder').hide();
                draggedItemClone.remove();

                let drag_index = 0;
                let infos = [];
                $('.drag-handle').each(function (index, d) {
                    let name = $(d).attr("data-name");
                    if (name != undefined) {
                        drag_index = drag_index + 1;
                        NFTconfig['stats_info'][name][1] = drag_index;
                        $(this).attr("data-index", drag_index);
                        let tempDom = $(".stats_" + name).clone();
                        if (!NFTconfig['stats_info'][name][0]) {
                            //隐藏
                            tempDom.css("display", "none");
                        } else {
                            tempDom.css("display", "block");
                        }
                        tempDom.css("border-right", "2px solid #fff");
                        infos.push(tempDom);
                        $(".stats_" + name).remove();
                    }
                });
                naifeitian.setValue("stats_infoGM", NFTconfig['stats_info']);

                infos.forEach(function (item, index, array) {
                    $(".better-xcloud-stats-bar").append(item);
                });
                $('.better-xcloud-stats-bar > *')
                    .filter(function () {
                        return $(this).css('display') === 'block';
                    })
                    .last().css("border-right", '0px');
            });

            // 点击事件,选中/取消选中按钮
            $('.drag-handle').on('click', function (event) {
                if (!isClick) {
                    if (isTouchstart) {
                        return;
                    }
                }
                if ($(this).hasClass('stats-selected')) {
                    //取消
                    $(this).removeClass('stats-selected');
                    $(this).addClass('stats-delete');
                    NFTconfig['stats_info'][$(this).attr("data-name")][0] = false;

                } else {
                    //启用
                    $(this).addClass('stats-selected');
                    $(this).removeClass('stats-delete');
                    NFTconfig['stats_info'][$(this).attr("data-name")][0] = true;
                }
                naifeitian.setValue("stats_infoGM", NFTconfig['stats_info']);

                let stats_info_sortedEntries = Object.entries(NFTconfig['stats_info']).sort((a, b) => a[1][1] - b[1][1]);

                stats_info_sortedEntries.forEach(entry => {
                    //entry[1][0]     是否选中
                    //entry[1][1]     顺序
                    //entry[1][2]     名字
                    if (entry[1][0]) {
                        $(".stats_" + entry[0]).css("display", "block");
                    } else {
                        $(".stats_" + entry[0]).css("display", "none");
                    }
                    $(".stats_" + entry[0]).css("border-right", "2px solid #fff");
                })
                $('.better-xcloud-stats-bar > *')
                    .filter(function () {
                        return $(this).css('display') === 'block';
                    })
                    .last().css("border-right", '0px');

            });

            StreamStats.#refreshStyles();
        }
    }
    function numberPicker(key, suffix = '', disabled = false, range = true) {

        const setting = key.name;
        let value = key.default;
        let $text, $decBtn, $incBtn, $range;

        const MIN = key.min;
        const MAX = key.max;

        const CE = createElement;
        const $wrapper = CE('div', {},
            $decBtn = CE('button', { 'data-type': 'dec' }, '-'),
            $text = CE('span', {}, value + suffix),
            $incBtn = CE('button', { 'data-type': 'inc' }, '+'),
        );
        if (range) {
            $range = CE('input', { 'type': 'range', 'style': "width:100px", 'min': 0, 'max': 150, 'value': value });
            $range.addEventListener('input', e => {
                value = parseInt(e.target.value);

                $text.textContent = value + "%";

                key['default'] = value;

                naifeitian.setValue(key['name'], key);

                updateVideoPlayerCss();


            });
            $wrapper.appendChild($range);

        }
        if (disabled) {
            $incBtn.disabled = true;
            $incBtn.classList.add('better-xcloud-hidden');

            $decBtn.disabled = true;
            $decBtn.classList.add('better-xcloud-hidden');
            return $wrapper;
        }

        let interval;
        let isHolding = false;

        const onClick = e => {
            if (isHolding) {
                e.preventDefault();
                isHolding = false;

                return;
            }

            const btnType = e.target.getAttribute('data-type');
            if (btnType === 'dec') {
                value = (value <= MIN) ? MIN : value - 1;

            } else {
                value = (value >= MAX) ? MAX : value + 1;
            }
            $($range).val(value);
            $text.textContent = value + suffix;

            key['default'] = value;

            naifeitian.setValue(key['name'], key);

            updateVideoPlayerCss();

            isHolding = false;
        }

        const onMouseDown = e => {
            isHolding = true;

            const args = arguments;
            interval = setInterval(() => {
                const event = new Event('click');
                event.arguments = args;

                e.target.dispatchEvent(event);
            }, 200);
        };

        const onMouseUp = e => {
            clearInterval(interval);
            isHolding = false;
        };

        $decBtn.addEventListener('click', onClick);
        $decBtn.addEventListener('mousedown', onMouseDown);
        $decBtn.addEventListener('mouseup', onMouseUp);
        $decBtn.addEventListener('touchstart', onMouseDown);
        $decBtn.addEventListener('touchend', onMouseUp);

        $incBtn.addEventListener('click', onClick);
        $incBtn.addEventListener('mousedown', onMouseDown);
        $incBtn.addEventListener('mouseup', onMouseUp);
        $incBtn.addEventListener('touchstart', onMouseDown);
        $incBtn.addEventListener('touchend', onMouseUp);

        return $wrapper;
    }

    function setupVideoSettingsBar() {
        const CE = createElement;
        let $stretchInp;
        const refreshFunc = e => {
            updateVideoPlayerCss();
        };
        const $stretch = naifeitian.toElement(NFTconfig['video_stretch'], refreshFunc);
        const $wrapper = CE('div', { 'class': 'better-xcloud-quick-settings-bar' },
            CE('div', {},
                CE('label', { 'for': 'better-xcloud-quick-setting-stretch' }, '去黑边'),
                $stretch),
            CE('div', {},
                CE('label', {}, '清晰'),
                numberPicker(NFTconfig['VIDEO_CLARITY'], '', naifeitian.isSafari(), false)),
            CE('div', {},
                CE('label', {}, '饱和'),
                numberPicker(NFTconfig['VIDEO_SATURATION'], '%')),
            CE('div', {},
                CE('label', {}, '对比'),
                numberPicker(NFTconfig['VIDEO_CONTRAST'], '%')),
            CE('div', {},
                CE('label', {}, '亮度'),
                numberPicker(NFTconfig['VIDEO_BRIGHTNESS'], '%'))
        );


        $stretch.addEventListener('change', e => {
            if (e.target.value == 'setting') {
                $('#video_stretch_x_y').css('display', 'block');
            } else {
                $('#video_stretch_x_y').css('display', 'none');
            }
            NFTconfig['video_stretch'].default = e.target.value;
            naifeitian.setValue('video_stretchGM', NFTconfig['video_stretch']);
            updateVideoPlayerCss();
        });

        document.documentElement.appendChild($wrapper);
        if ($stretch.id == 'xcloud_setting_video_stretchGM') {
            let dom = $('#xcloud_setting_video_stretchGM');
            dom.after(`<div id="video_stretch_x_y" style="display: ${NFTconfig['video_stretch'].default == 'setting' ? 'block' : 'none'}">
                     <lable>左右
                       <input type=\'text\'class="video_stretch_x_y_Listener" id="video_stretch_x" style="width:35px" value="${NFTconfig['video_stretch_x_y']['x']}"/>
                     </lable><br/>
                     <lable>上下
                       <input type=\'text\'class="video_stretch_x_y_Listener" id="video_stretch_y" style="width:35px" value="${NFTconfig['video_stretch_x_y']['y']}"/>
                     </lable>
                  </div>`);

            $(document).on('blur', '.video_stretch_x_y_Listener', function () {
                let newval = $(this).val();
                if (naifeitian.isNumber($(this).val())) {
                    if ($(this).attr('id') == 'video_stretch_x') {
                        NFTconfig['video_stretch_x_y']['x'] = newval;
                        naifeitian.setValue('video_stretch_x_yGM', NFTconfig['video_stretch_x_y']);
                    } else {
                        NFTconfig['video_stretch_x_y']['y'] = newval;
                        naifeitian.setValue('video_stretch_x_yGM', NFTconfig['video_stretch_x_y']);
                    }
                } else {
                    $(this).val("0");
                    NFTconfig['video_stretch_x_y']['x'] = 0;
                    NFTconfig['video_stretch_x_y']['y'] = 0;
                    naifeitian.setValue('video_stretch_x_yGM', NFTconfig['video_stretch_x_y']);
                }
                updateVideoPlayerCss();
            });
        }
    }
    function cloneStreamHudButton($orgButton, class_, label, svg_icon) {
        const $container = $orgButton.cloneNode(true);
        if (class_ != "") {
            $($container).addClass(class_);
        }


        const $button = $container.querySelector('button');
        $button.setAttribute('title', label);

        const $svg = $button.querySelector('svg');
        $svg.innerHTML = svg_icon;

        const attrs = {
            'fill-rule': 'evenodd',
            'stroke-linecap': 'round',
            'stroke-linejoin': 'round',
            'stroke-width': 2,
            'viewBox': '0 0 32 32'
        };

        for (const attr in attrs) {
            $svg.setAttribute(attr, attrs[attr]);
        }

        return $container;
    }
    function cloneStreamMenuButton($orgButton, label, svg_icon) {
        const $button = $orgButton.cloneNode(true);
        $button.setAttribute('aria-label', label);
        $button.querySelector('div[class*=label]').textContent = label;

        const $svg = $button.querySelector('svg');
        $svg.innerHTML = svg_icon;
        $svg.setAttribute('viewBox', '0 0 32 32');

        return $button;
    }

    function HookProperty(object, property, value) {
        Object.defineProperty(object, property, {
            value: value
        });
    }

    let userAgentOriginal = window.navigator.userAgent;
    try {
        HookProperty(window.navigator, "userAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/999.0.0.0 Safari/537.36 Edg/999.0.0.0");
        HookProperty(window.navigator, "maxTouchPoints", 10);
        if (NFTconfig['disableCheckNetwork'] == 1) {
            Object.defineProperty(window.navigator, 'connection', {
                get: () => undefined,
            });
        }
        HookProperty(window.navigator, "standalone", true);

    } catch (e) { }
    let consoleIp;
    let consolePort;
    const patchIceCandidates = function (...arg) {
        // ICE server candidates
        const request = arg[0];
        const url = (typeof request === 'string') ? request : request.url;

        if (url && url.endsWith('/ice') && url.includes('/sessions/') && request.method === 'GET') {
            const promise = originFetch(...arg);

            return promise.then(response => {
                return response.clone().text().then(text => {
                    if (!text.length) {
                        return response;
                    }

                    const options = {
                        preferIpv6Server: NFTconfig['IPv6'] == 1,
                        consoleIp: consoleIp,
                    };

                    const obj = JSON.parse(text);
                    let exchangeResponse = JSON.parse(obj.exchangeResponse);
                    exchangeResponse = updateIceCandidates(exchangeResponse, options)
                    obj.exchangeResponse = JSON.stringify(exchangeResponse);

                    response.json = () => Promise.resolve(obj);
                    response.text = () => Promise.resolve(JSON.stringify(obj));

                    return response;
                });
            });
        }

        return null;
    }
    const originFetch = window.fetch;
    window.fetch = async (...arg) => {
        let arg0 = arg[0];
        let url = "";
        let isRequest = false;
        switch (typeof arg0) {
            case "object":
                url = arg0.url;
                isRequest = true;
                break;
            case "string":
                url = arg0;
                break;
            default:
                break;
        }

        // 串流
        if (IS_REMOTE_PLAYING && url.includes('/sessions/home') || url.includes('inputconfigs')) {

            canShowOC = true;
            const clone = arg0.clone();

            const headers = {};
            for (const pair of clone.headers.entries()) {
                headers[pair[0]] = pair[1];
            }
            headers['authorization'] = `Bearer ${RemotePlay.XHOME_TOKEN}`;

            const deviceInfo = RemotePlay.BASE_DEVICE_INFO;
            if (NFTconfig['REMOTE_PLAY_RESOLUTION']['default'] == '720p') {
                deviceInfo.dev.os.name = 'android';
            }

            headers['x-ms-device-info'] = JSON.stringify(deviceInfo);

            const opts = {
                method: clone.method,
                headers: headers,
            };

            if (clone.method === 'POST') {
                opts.body = await clone.text();
            }

            const index = arg0.url.indexOf('.xboxlive.com');
            let newUrl = `https://${REMOTE_PLAY_SERVER}.core.gssv-play-prodxhome` + arg0.url.substring(index);

            arg0 = new Request(newUrl, opts);

            arg[0] = arg0;
            url = (typeof request === 'string') ? arg0 : arg0.url;
            // Get console IP
            if (url.includes('/configuration')) {
                const promise = originFetch(...arg);

                return promise.then(response => {
                    return response.clone().json().then(obj => {
                        console.log(obj);
                        consoleIp = obj.serverDetails.ipAddress;
                        consolePort = obj.serverDetails.port;

                        response.json = () => Promise.resolve(obj);

                        return response;
                    });
                });
            }
            if (url.includes('/sessions/home/play')) {
                inGame();
                document.title = document.title.replace('Fortnite', '串流');
            }

            return patchIceCandidates(...arg) || originFetch(...arg);

        }
        if (IS_REMOTE_PLAYING && url.includes('/login/user')) {
            try {
                const clone = arg0.clone();

                const obj = await clone.json();
                obj.offeringId = 'xhome';

                arg0 = new Request('https://xhome.core.gssv-play-prod.xboxlive.com/v2/login/user', {
                    method: 'POST',
                    body: JSON.stringify(obj),
                    headers: {
                        'Content-Type': 'application/json',
                    },
                });

                arg[0] = arg0;
            } catch (e) {
                alert(e);
                console.log(e);
            }

            return originFetch(...arg);
        }
        // ICE server candidates
        const patchedIpv6 = patchIceCandidates(...arg);
        if (patchedIpv6) {
            return patchedIpv6;
        }
        if (!url.includes('xhome.') && url.indexOf('/v2/login/user') > -1) {//xgpuweb.gssv-play-prod.xboxlive.com
            checkIpsuc = true;


            let json = await arg0.json();
            let body = JSON.stringify(json);
            // 处理免代理逻辑
            if (NFTconfig['no_need_VPN_play'] == 1) {

                if (NFTconfig['useCustomfakeIp'] == 1 && naifeitian.isValidIP(NFTconfig['customfakeIp'])) {
                    arg0.headers.set('x-forwarded-for', NFTconfig['customfakeIp']);
                } else {
                    arg0.headers.set('x-forwarded-for', NFTconfig['regionBlock']['options'][NFTconfig['regionBlock']['blockIp']]);
                }
            }

            arg[0] = new Request(url, {
                method: arg0.method,
                headers: arg0.headers,
                body: body,
            });

            const promise = originFetch(...arg);
            if (NFTconfig['useCustomfakeIp'] == 1 && naifeitian.isValidIP(NFTconfig['customfakeIp'])) {
                console.log('免代理成功,已设置为自定义IP【' + NFTconfig['customfakeIp'] + "】");
            } else {
                console.log('免代理成功,已设置为【' + NFTconfig['regionBlock']['blockIp'] + "】");
            }
            RemotePlay.preload();
            return promise.then(response => {
                return response.clone().json().then(json => {
                    //获取服务器列表
                    let newServerList = [];
                    let currentAutoServer;
                    let REMOTE_SERVER_LIST = [];
                    json["offeringSettings"]["regions"].forEach((region) => {
                        REMOTE_SERVER_LIST.push(region.networkTestHostname.split(".")[0]);
                        newServerList.push(region["name"]);
                        if (region["isDefault"] === true) {
                            currentAutoServer = region["name"];
                        }
                    });
                    NFTconfig['REMOTE_SERVER_LIST'] = REMOTE_SERVER_LIST;
                    naifeitian.setValue("REMOTE_SERVER_LISTGM", REMOTE_SERVER_LIST);
                    naifeitian.setValue("blockXcloudServerListGM", newServerList);
                    NFTconfig['blockXcloudServerList'] = newServerList;
                    if (NFTconfig['blockXcloudServerList'].indexOf(NFTconfig['defaultXcloudServer']) == -1) {
                        naifeitian.setValue("defaultXcloudServerGM", "");
                        NFTconfig['defaultXcloudServer'] = "";
                        NFTconfig['blockXcloudServer'] = 0;
                        naifeitian.setValue("blockXcloudServerGM", 0);
                    }
                    if (NFTconfig['blockXcloudServer'] == 1) {
                        console.log('修改服务器开始');
                        json["offeringSettings"]["allowRegionSelection"] = true;
                        let selectedServer = NFTconfig['defaultXcloudServer'];
                        if (selectedServer !== "Auto" && newServerList.includes(selectedServer)) {
                            json["offeringSettings"]["regions"].forEach((region) => {
                                if (region["name"] === selectedServer) {
                                    region["isDefault"] = true;
                                } else {
                                    region["isDefault"] = false;
                                }
                            });
                        }
                        console.log('修改服务器结束');
                    }
                    try {
                        json["offeringSettings"]["regions"].forEach((region) => {
                            if (region.isDefault) {
                                StreamBadges.region = region.name;
                                throw new Error();
                            }
                        });

                    } catch (e) { }

                    response.json = () => Promise.resolve(json);
                    return response;
                });
            });
        } else if (url.indexOf('/cloud/play') > -1) {

            inGame();
            const clone = arg0.clone();
            const body = await clone.json();
            if (NFTconfig['chooseLanguage'] == 1) {
                let selectedLanguage = NFTconfig['xcloud_game_language'];
                if (selectedLanguage == 'Auto') {
                    let parts = window.location.pathname.split('/');
                    let pid = parts[parts.length - 1];
                    try {
                        let res = await fetch(
                            "https://catalog.gamepass.com/products?market=US&language=en-US&hydration=PCInline", {
                            "headers": {
                                "content-type": "application/json;charset=UTF-8",
                            },
                            "body": "{\"Products\":[\"" + pid + "\"]}",
                            "method": "POST",
                            "mode": "cors",
                            "credentials": "omit"
                        });
                        let jsonObj = await res.json();
                        let languageSupport = jsonObj["Products"][pid]["LanguageSupport"]
                        for (let language of Object.keys(default_language_list)) {
                            if (default_language_list[language] in languageSupport) {
                                selectedLanguage = default_language_list[language];
                                break;
                            }
                        }
                        if (selectedLanguage == 'Auto') {
                            //防止接口没有返回支持语言
                            selectedLanguage = NFTconfig['IfErrUsedefaultGameLanguage'];
                            console.log("使用次选语言");
                        }

                    } catch (e) {
                    }
                }
                console.log('语言已设置:【' + selectedLanguage + '】');
                body.settings.locale = selectedLanguage;
            }
            body.settings.osName = NFTconfig['high_bitrate'] == 1 ? 'windows' : 'android';

            const newRequest = new Request(arg0, {
                body: JSON.stringify(body),
            });

            arg[0] = newRequest;
            return originFetch(...arg);


        } else if (url.endsWith('/configuration') && url.includes('/sessions/cloud/') && NFTconfig['autoOpenOC'] == 1 && NFTconfig['disableTouchControls'] == 0) {
            // Enable CustomTouchOverlay

            return new Promise((resolve, reject) => {
                originFetch(...arg).then(res => {
                    res.json().then(json => {
                        // console.error(json);
                        let inputOverrides = JSON.parse(json.clientStreamingConfigOverrides || '{}') || {};
                        inputOverrides.inputConfiguration = {
                            enableTouchInput: true,
                            maxTouchPoints: 10,
                            enableVibration: true
                        };
                        json.clientStreamingConfigOverrides = JSON.stringify(inputOverrides);
                        let cdom = $('#BabylonCanvasContainer-main').children();
                        if (cdom.length > 0) {
                            canShowOC = false;
                        } else {
                            canShowOC = true;
                        }
                        let body = JSON.stringify(json);
                        let newRes = new Response(body, {
                            status: res.status,
                            statusText: res.statusText,
                            headers: res.headers
                        })
                        resolve(newRes);

                        console.log('修改触摸成功')
                    }).catch(err => {
                        reject(err);
                    });
                }).catch(err => {
                    reject(err);
                });
            });
        } else {
            return originFetch(...arg);
        }
    }
    function updateIceCandidates(candidates, options) {
        const pattern = new RegExp(/a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<the_rest>.*)/);

        const lst = [];
        for (let item of candidates) {
            if (item.candidate == 'a=end-of-candidates') {
                continue;
            }

            const groups = pattern.exec(item.candidate).groups;
            lst.push(groups);
        }

        if (options.preferIpv6Server) {
            lst.sort((a, b) => (!a.ip.includes(':') && b.ip.includes(':')) ? 1 : -1);
        }

        const newCandidates = [];
        let foundation = 1;
        lst.forEach(item => {
            item.foundation = foundation;
            item.priority = (foundation == 1) ? 10000 : 1;

            newCandidates.push({
                'candidate': `a=candidate:${item.foundation} 1 UDP ${item.priority} ${item.ip} ${item.the_rest}`,
                'messageType': 'iceCandidate',
                'sdpMLineIndex': '0',
                'sdpMid': '0',
            });

            ++foundation;
        });

        if (options.consoleIp) {
            newCandidates.push({
                'candidate': `a=candidate:${newCandidates.length + 1} 1 UDP 1 ${options.consoleIp} 9002 typ host`,
                'messageType': 'iceCandidate',
                'sdpMLineIndex': '0',
                'sdpMid': '0',
            });
        }

        newCandidates.push({
            'candidate': 'a=end-of-candidates',
            'messageType': 'iceCandidate',
            'sdpMLineIndex': '0',
            'sdpMid': '0',
        });

        return newCandidates;
    }
    function checkCodec() {

        let rtcCodecPreferences = naifeitian.getValue('rtcCodecPreferencesGM');
        let codecs = RTCRtpReceiver.getCapabilities('video').codecs;
        let codesOptions = ['默认', '自动'];
        const codecProfileMap = { "高": "4d", "中": "42e", "低": "420" };
        codecs.forEach((codec, index) => {
            if (codec.mimeType === 'video/H264') {
                for (let key in codecProfileMap) {
                    if (codec.sdpFmtpLine.includes(codecProfileMap[key])) {
                        codesOptions.push(codec.mimeType.substring(6) + key);
                        break;
                    }
                }
            } else {
                codesOptions.push(codec.mimeType.substring(6));
            }
        });

        codesOptions = [...new Set(codesOptions)];

        let sortOrder = ['默认', '自动', 'AV1', 'VP9', 'H265', 'VP8', 'H264高', 'H264中', 'H264低', 'flexfec-03', 'ulpfec', 'rtx', 'red'];
        const customSort = (a, b) => {
            const indexOfA = sortOrder.indexOf(a);
            const indexOfB = sortOrder.indexOf(b);

            if (indexOfA === -1) {
                return 1;
            }
            if (indexOfB === -1) {
                return -1;
            }
            return indexOfA - indexOfB;
        };
        codesOptions.sort(customSort);
        rtcCodecPreferences['options'] = codesOptions;

        if (!rtcCodecPreferences['options'].includes(rtcCodecPreferences['default'])) {
            rtcCodecPreferences['default'] = "默认";
        }
        NFTconfig['rtcCodecPreferences'] = rtcCodecPreferences;
        naifeitian.setValue('rtcCodecPreferencesGM', rtcCodecPreferences);
    }
    checkCodec();



    if (NFTconfig['autoOpenOC'] == 1 && NFTconfig['disableTouchControls'] == 0 && NFTconfig['autoShowTouch']) {
        window.RTCPeerConnection.prototype.originalCreateDataChannelGTC = window.RTCPeerConnection.prototype.createDataChannel;
        window.RTCPeerConnection.prototype.createDataChannel = function (...params) {

            const dc = this.originalCreateDataChannelGTC.apply(this, arguments);
            if (dc.label == "message") {
                dc.addEventListener('open', e => {
                    setTimeout(() => {
                        if (canShowOC) {
                            dc.dispatchEvent(new MessageEvent('message', {
                                data: '{"content":"{\\"layoutId\\":\\"\\"}","target":"/streaming/touchcontrols/showlayoutv2","type":"Message"}'
                            }));
                        }
                    }, 1000);
                });
                dc.addEventListener("message", function (de) {
                    if (typeof (de.data) != "string") { return; }
                    let msgdata = JSON.parse(de.data);
                    if (msgdata.target == "/streaming/touchcontrols/showtitledefault") {
                        let chkItv = setInterval(() => {
                            if (canShowOC == null) { return; }
                            clearInterval(chkItv);
                            if (canShowOC) {
                                dc.dispatchEvent(new MessageEvent('message', {
                                    data: '{"content":"{\\"layoutId\\":\\"\\"}","target":"/streaming/touchcontrols/showlayoutv2","type":"Message"}'
                                }));
                            }

                        }, 1000);
                    }
                });
            }
            return dc;

        }

    }

    // 配置对象,定义每个设置项的信息
    const settingsConfig = [
        {
            label: '选择语言:',
            type: 'radio',
            name: 'chooseLanguage',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'chooseLanguageOn' },
                { value: 0, text: '关', id: 'chooseLanguageOff' }
            ],
            checkedValue: NFTconfig['chooseLanguage'],
            needHr: false
        },
        {
            label: '首选语言:',
            type: 'radio',
            name: 'selectLanguage',
            display: NFTconfig['chooseLanguage'] === 1 ? 'block' : 'none',
            options: Object.keys(default_language_list).map(languageChinese => {
                return {
                    value: default_language_list[languageChinese],
                    text: languageChinese,
                    id: default_language_list[languageChinese]
                };
            }),
            checkedValue: NFTconfig['xcloud_game_language'],
            needHr: false

        },
        {
            label: '次选语言:',
            type: 'radio',
            name: 'IfErrUsedefaultGameLanguage',
            display: NFTconfig['xcloud_game_language'] === 'Auto' ? 'block' : 'none',
            options: Object.keys(default_language_list).map(languageChinese => {
                if (languageChinese == '智能简繁') { return; }
                return {
                    value: default_language_list[languageChinese],
                    text: languageChinese,
                    id: default_language_list[languageChinese] + 'ifErr'
                };

            }),
            checkedValue: NFTconfig['IfErrUsedefaultGameLanguage'],
            needHr: true
        },
        {
            label: '免代理直连:',
            type: 'radio',
            name: 'noNeedVpn',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'noNeedVpnOn' },
                { value: 0, text: '关', id: 'noNeedVpnOff' },
            ],
            checkedValue: NFTconfig['no_need_VPN_play'],
            needHr: false
        },
        {
            label: '选服:',
            type: 'radio',
            name: 'selectRegion',
            display: NFTconfig['no_need_VPN_play'] === 1 ? 'block' : 'none',
            options: Object.keys(NFTconfig['regionBlock']['options']).map(region => {
                return {
                    value: NFTconfig['regionBlock']['options'][region],
                    text: region,
                    id: NFTconfig['regionBlock']['options'][region]
                };
            }),
            checkedValue: NFTconfig['regionBlock']['options'][NFTconfig['regionBlock']['blockIp']],
            needHr: false
        },
        {
            label: '自定义IP:',
            type: 'radio',
            name: 'customfakeIpInput',
            display: NFTconfig['no_need_VPN_play'] === 1 ? 'block' : 'none',
            value: NFTconfig['useCustomfakeIp'],
            needHr: true,
            moreDom: `<input type="radio" class="selectRegionListener settingsBoxInputRadio" style="outline:none;"
        name='selectRegion' id="customfakeIp" value="customfakeIp" ${NFTconfig['useCustomfakeIp'] == 1 ? 'checked' : ''}>
        <label for="customfakeIp" style="padding-right: 7px;">自定义IP:</label>
        <input type='text' style="display: ` + (NFTconfig['useCustomfakeIp'] == 1 ? 'inline' : 'none')
                + `;outline: none;width: 125px;" id="customfakeIpInput" class="customfakeIpListener" value="${NFTconfig['customfakeIp']}" placeholder="请输入IP"/>`

        },
        {
            label: '分辨率:',
            type: 'radio',
            name: 'highBitrate',
            display: 'block',
            options: [
                { value: 1, text: '1080P', id: 'high_bitrateOn' },
                { value: 0, text: '720P', id: 'high_bitrateOff' }
            ],
            checkedValue: NFTconfig['high_bitrate'],
            needHr: true
        },
        {
            label: '浏览器编解码偏好:',
            showLable: true,
            type: 'dropdown',
            name: 'rtcCodecPreferences',
            display: "block",
            options: NFTconfig['rtcCodecPreferences']['options'],
            selectedValue: NFTconfig['rtcCodecPreferences']['default'],
            needHr: true
        },
        {
            label: '禁止检测网络状况:',
            type: 'radio',
            name: 'disableCheckNetwork',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'disableCheckNetworkOn' },
                { value: 0, text: '关', id: 'disableCheckNetworkOff' }
            ],
            checkedValue: NFTconfig['disableCheckNetwork'],
            needHr: true
        },
        {
            label: '强制触控:',
            type: 'radio',
            name: 'autoOpenOC',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'autoOpenOCOn' },
                { value: 0, text: '关', id: 'autoOpenOCOff' }
            ],
            checkedValue: NFTconfig['autoOpenOC'],
            needHr: true,
            moreDom: `<div id="autoShowTouchDom" style="padding-right: 0px;display: ${NFTconfig['autoOpenOC'] == 1 ? 'inline' : 'none'}">
        <input type="checkbox" class="autoShowTouchListener settingsBoxInputRadio" style="outline:none;cursor: pointer;" name='autoShowTouch'
        id="autoShowTouch" ${NFTconfig['autoShowTouch'] == true ? 'checked' : ''}><label for="autoShowTouch" style="cursor: pointer;">自动弹出</label></div>`
        },
        {
            label: '屏蔽触控:',
            type: 'radio',
            name: 'disableTouchControls',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'disableTouchControlsOn' },
                { value: 0, text: '关', id: 'disableTouchControlsOff' },
            ],
            checkedValue: NFTconfig['disableTouchControls'],
            needHr: true
        },
        {
            label: '自动全屏:',
            type: 'radio',
            name: 'autoFullScreen',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'autoFullScreenOn' },
                { value: 0, text: '关', id: 'autoFullScreenOff' }
            ],
            checkedValue: NFTconfig['autoFullScreen'],
            needHr: true
        },
        {
            label: '优先IPv6:',
            type: 'radio',
            name: 'IPv6server',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'IPv6On' },
                { value: 0, text: '关', id: 'IPv6Off' }
            ],
            checkedValue: NFTconfig['IPv6'],
            needHr: true
        }
        ,
        {
            label: '物理服务器:',
            type: 'radio',
            name: 'blockXcloudServer',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'blockXcloudServerOn' },
                { value: 0, text: '关', id: 'blockXcloudServerOff' }
            ],
            checkedValue: NFTconfig['blockXcloudServer'],
            needHr: false
        },
        {
            label: '选择服务器:',
            type: 'dropdown',
            name: 'defaultXcloudServer',
            display: NFTconfig['blockXcloudServer'] === 1 ? "block" : "none",
            options: NFTconfig['blockXcloudServerList'],
            selectedValue: NFTconfig['defaultXcloudServer'],
            needHr: true

        },
        {
            label: '挂机防踢:',
            type: 'radio',
            name: 'antiKick',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'antiKickOn' },
                { value: 0, text: '关', id: 'antiKickOff' }
            ],
            checkedValue: NFTconfig['antiKick'],
            needHr: true

        },
        {
            label: '设置悬浮窗:',
            type: 'radio',
            name: 'noPopSetting',
            display: 'block',
            options: [
                { value: 0, text: '显示', id: 'noPopSettingOff' },
                { value: 1, text: '隐藏', id: 'noPopSettingOn' }
            ],
            checkedValue: NFTconfig['noPopSetting'],
            needHr: true
        },
        {
            label: '开启串流功能:',
            type: 'radio',
            name: 'enableRemotePlay',
            display: 'block',
            options: [
                { value: 1, text: '开', id: 'enableRemotePlayOn' },
                { value: 0, text: '关', id: 'enableRemotePlayOff' }
            ],
            checkedValue: NFTconfig['enableRemotePlay'],
            needHr: true
        }


    ];

    // 函数用于生成单个设置项的HTML
    function generateSettingElement(setting) {
        let settingHTML = `<lable style="display:${setting.display};white-space: nowrap;margin-bottom:0.375rem;" class="${setting.name + 'Dom'}">`;
        if (setting.type === 'radio') {
            if (setting.options != undefined) {
                settingHTML += `<label style="display:block;text-align:left;"><div style="display: inline;">${setting.label}</div>`;
                setting.options.forEach(option => {
                    if (option == null) { return; }

                    settingHTML += `
                <label style="cursor: pointer;"><input type="radio" class="${setting.name + 'Listener'} settingsBoxInputRadio" style="outline:none;" name="${setting.name}"
                id="${option.id}" value="${option.value}" ${option.value === setting.checkedValue ? 'checked' : ''}>${option.text}</label>
            `;
                });
            }
            if (setting.moreDom != undefined) {
                settingHTML += setting.moreDom;
            }
            settingHTML += '</label>';
        } else if (setting.type === 'text') {
            settingHTML += `<label style="display: block;text-align:left;"><div style="display: inline;">${setting.label}</div>`;
            settingHTML += `
            <input type="text" style="display: inline;outline: none;width: 125px;" id="${setting.name}" class="${setting.name}Listener" value="${setting.value}" placeholder="请输入${setting.label}"/>
        `;
            settingHTML += `</label>`;
        } else if (setting.type === 'dropdown') {
            if (setting.showLable == true) {
                settingHTML += `<label style="display: block;text-align:left;${setting.css}"><div style="display: inline;">${setting.label}</div>`;
            }
            if (setting.options.length == undefined) {
                setting.options = Object.keys(setting.options);
            }
            settingHTML += `
            <select style="outline: none;margin-bottom:5px;" class="${setting.name + 'Listener'}">
                ${setting.options.map(option => `<option value="${option}" ${option === setting.selectedValue ? 'selected' : ''}>${option}</option>`).join('')}
            </select>
        `;

            if (setting.moreDom != undefined) {
                settingHTML += setting.moreDom;
            }
        }

        settingHTML += `</lable>`;

        if (setting.needHr) {
            settingHTML += `<hr style="background-color: black;width:95%" />`
        }
        return settingHTML;
    }
    function generateSettingsPage() {
        let settingsHTML = `
        <div style="padding: 10px;color: black;display:none;" class="settingsBackgroud" id="settingsBackgroud">
            <div class="settingsBox"><span class="blink-text" onclick="window.location.href='https://greasyfork.org/zh-CN/scripts/455741';">更新咯~</span>
    `;
        settingsConfig.forEach(setting => {
            settingsHTML += generateSettingElement(setting);
        });

        settingsHTML += `
                <button class="closeSetting1 closeSetting2" style="outline: none;">关闭</button>
                <div style="text-align: right;margin-top: 8px;font-size: 16px;">
                    <label>捐赠:</label>
                    <a style="margin-right:15px;outline: none;color: #107c10;text-decoration: underline;" href="https://greasyfork.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMVNFQVE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--14c278e3f72d104cff50bf130d4039229fc25a6b/wx.png?locale=zh-CN">微信</a>
                    <a style="outline: none;color: #107c10;text-decoration: underline;" href="https://greasyfork.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMU9FQVE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--5fc08aaa8407cc6099654d65455b7966bf2c60ee/alipay.png?locale=zh-CN">支付宝</a>
                </div>
            </div>
        </div>
    `;

        return settingsHTML;
    }
    let needrefresh = 0;
    function initSettingBox() {
        $('body').append(generateSettingsPage());

        //确定
        $(document).on('click', '.closeSetting1', function () {

            naifeitian.hideSetting();
            if (needrefresh == 1) {
                history.go(0);
            }
        });

        //开启串流
        $(document).on('click', '.enableRemotePlayListener', function () {
            needrefresh = 1;
            naifeitian.setValue('enableRemotePlayGM', $(this).val());
            $('.closeSetting1').text('确定');
        });
        //设置悬浮窗
        $(document).on('click', '.noPopSettingListener', function () {
            naifeitian.setValue('noPopSettingGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //挂机防踢
        $(document).on('click', '.antiKickListener', function () {
            needrefresh = 1;
            naifeitian.setValue('antiKickGM', $(this).val());
            $('.closeSetting1').text('确定');
        });
        //ipv6
        $(document).on('click', '.IPv6serverListener', function () {
            naifeitian.setValue('IPv6GM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //选择服务器change
        $(document).on('change', '.defaultXcloudServerListener', function () {
            naifeitian.setValue('defaultXcloudServerGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //物理服务器
        $(document).on('click', '.blockXcloudServerListener', function () {
            if ($(this).val() == 0) {
                $('.defaultXcloudServerDom').css('display', 'none');
            } else {
                $('.defaultXcloudServerDom').css('display', 'block');
            }
            naifeitian.setValue('blockXcloudServerGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });

        //自动全屏
        $(document).on('click', '.autoFullScreenListener', function () {
            naifeitian.setValue('autoFullScreenGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //屏蔽触控
        $(document).on('click', '.disableTouchControlsListener', function () {
            if ($(this).val() == 1) {
                if (!confirm("确定要屏蔽触控吗?")) {
                    $('#disableTouchControlsOff').click();
                    return;
                }
                $('#autoOpenOCOff').click();
            }

            needrefresh = 1;
            naifeitian.setValue('disableTouchControlsGM', $(this).val());
            $('.closeSetting1').text('确定');
        });

        //自动弹出
        $(document).on('change', '.autoShowTouchListener', function () {
            let newVal = $(this).attr('checked') == 'checked';
            if (newVal) {
                $(this).removeAttr('checked');
            } else {
                $(this).attr('checked');
            }
            naifeitian.setValue('autoShowTouchGM', !newVal);
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //强制触控
        $(document).on('click', '.autoOpenOCListener', function () {

            if ($(this).val() == 0) {
                $('#autoShowTouchDom').css('display', 'none');
            } else {
                $('#autoShowTouchDom').css('display', 'inline');
                $('#disableTouchControlsOff').click();
            }

            naifeitian.setValue('autoOpenOCGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });

        //禁止检测网络
        $(document).on('click', '.disableCheckNetworkListener', function () {
            naifeitian.setValue('disableCheckNetworkGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //浏览器编解码偏好
        $(document).on('change', '.rtcCodecPreferencesListener', function () {
            NFTconfig['rtcCodecPreferences']['default'] = $(this).val();
            naifeitian.setValue('rtcCodecPreferencesGM', NFTconfig['rtcCodecPreferences']);
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //分辨率
        $(document).on('click', '.highBitrateListener', function () {
            naifeitian.setValue('high_bitrateGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });


        //自定义ip输入框
        $(document).on('blur', '.customfakeIpListener', function () {
            if (naifeitian.isValidIP($(this).val())) {
                naifeitian.setValue('customfakeIpGM', $(this).val());
            } else {
                $(this).val("");
                naifeitian.setValue('customfakeIpGM', '');
                alert('IP格式错误!');
                return;
            }
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //选服
        $(document).on('click', '.selectRegionListener', function () {
            if ($(this).val() == 'customfakeIp') {
                naifeitian.setValue('useCustomfakeIpGM', 1);
                $('#customfakeIpInput').css('display', 'inline');
            } else {
                NFTconfig['regionBlock']['blockIp'] = Object.keys(NFTconfig['regionBlock']['options']).find(key => NFTconfig['regionBlock']['options'][key] === $(this).val());
                naifeitian.setValue('regionBlockGM', NFTconfig['regionBlock']);
                naifeitian.setValue('useCustomfakeIpGM', 0);
                $('#customfakeIpInput').css('display', 'none');
            }
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });

        //免代理直连
        $(document).on('click', '.noNeedVpnListener', function () {
            if ($(this).val() == 0) {
                $('.selectRegionDom').css('display', 'none');;
                $('.customfakeIpInputDom').css('display', 'none');
            } else {
                $('.selectRegionDom').css('display', 'block');
                $('.customfakeIpInputDom').css('display', 'block');
            }
            naifeitian.setValue('no_need_VPN_playGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });

        //智能简繁错误
        $(document).on('click', '.IfErrUsedefaultGameLanguageListener', function () {
            naifeitian.setValue('IfErrUsedefaultGameLanguageGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
        //语言
        $(document).on('click', '.selectLanguageListener', function () {
            if ($(this).val() != 'Auto') {
                $('.IfErrUsedefaultGameLanguageDom').css('display', 'none');
            } else {
                $('.IfErrUsedefaultGameLanguageDom').css('display', 'block');
            }
            naifeitian.setValue('xcloud_game_languageGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });

        //选择语言
        $(document).on('click', '.chooseLanguageListener', function () {
            if ($(this).val() == 0) {
                $('.selectLanguageDom').css('display', 'none');
                $('.IfErrUsedefaultGameLanguageDom').css('display', 'none');
            } else {
                $('.selectLanguageDom').css('display', 'block');

                if (naifeitian.getValue('xcloud_game_languageGM') == 'Auto') {
                    $('.IfErrUsedefaultGameLanguageDom').css('display', 'block');
                }
            }
            naifeitian.setValue('chooseLanguageGM', $(this).val());
            needrefresh = 1;
            $('.closeSetting1').text('确定');
        });
    }
    //手势显隐触控
    function initSlideHide() {
        var gestureArea = $("<div></div>");
        gestureArea.attr("id", "touchControllerEventArea");
        $(document.documentElement).append(gestureArea);

        gestureArea = $("#touchControllerEventArea");
        let startX, startY, endX, endY;
        let threshold = 60; // 手势滑动的阈值
        gestureArea.on("touchstart", function (e) {
            startX = e.originalEvent.touches[0].clientX;
            startY = e.originalEvent.touches[0].clientY;
        });
        gestureArea.on("touchmove", function (e) {
            endX = e.originalEvent.touches[0].clientX;
            endY = e.originalEvent.touches[0].clientY;
        });
        gestureArea.on("touchend", function (e) {
            if (startX !== undefined && startY !== undefined && endX !== undefined && endY !== undefined) {
                const deltaX = endX - startX;
                const deltaY = endY - startY;
                if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > threshold) {
                    if (deltaX < 0) {
                        // 左滑
                        $('#BabylonCanvasContainer-main').css('display', 'none');
                        $('#MultiTouchSurface').css('display', 'none');
                        e.preventDefault();
                    } else {
                        // 右滑
                        $('#BabylonCanvasContainer-main').css('display', 'block');
                        $('#MultiTouchSurface').css('display', 'block');
                        e.preventDefault();
                    }
                }
            }
        });

    }

    async function checkUPD() {
        try {
            const response = await fetch("https://greasyfork.org/zh-CN/scripts/455741-xbox-cloud-gaming%E4%BC%98%E5%8C%96%E6%95%B4%E5%90%88/versions");
            const data = await response.text();

            let historyVersion = $(data).find('.history_versions')[0];
            let hli = $($(historyVersion).find('li .version-number > a')[0]);
            let version = hli.text();
            if (nftxboxversion != version) {
                $('head').append('<style>.blink-text{ display:block!important }</style>');
            }
        } catch (error) {
            //console.error('Fetch error:', error);
        }
    }

    $(document).ready(function () {
        //检测Thank you for your interest
        let checkTYFYIInterval = setInterval(() => {
            if (checkIpsuc) {
                clearInterval(checkTYFYIInterval);
                return;
            }
            var title = $("[class*='UnsupportedMarketPage-module__title']");
            if (title.length > 0) {
                console.log("脚本检测到免代理没有成功,自动刷新中");
                title.text("脚本检测到免代理没有成功,自动刷新中");
                history.go(0);
                clearInterval(checkTYFYIInterval);
            }

        }, 5000);

        setTimeout(function () {

            if (NFTconfig['noPopSetting'] == 0) {
                $('body').append(`<div id="popSetting" style="display:block">⚙️ 设置</div>`);
                $(document).on('click', '#popSetting', function () {
                    naifeitian.showSetting();
                });
            }
            checkUPD();
            initSettingBox();
            updateVideoPlayerCss();
            StreamStats.render();
            setupVideoSettingsBar();
            initSlideHide();
        }, 2000);

    });

    let timer;
    let mousehidding = false;
    $(document).mousemove(function () {
        if (mousehidding) {
            mousehidding = false;
            return;
        }
        if (timer) {
            clearTimeout(timer);
            timer = 0;
        }
        $('html').css({
            cursor: ''
        });
        timer = setTimeout(function () {
            mousehidding = true;
            $('html').css({
                cursor: 'none'
            });
        }, 2000);
    });


    let _pushState = window.history.pushState;
    window.history.pushState = function () {

        setTimeout(RemotePlay.detect, 10);
        if (NFTconfig['noPopSetting'] == 0) {
            if (arguments[2].substring(arguments[2].length, arguments[2].length - 5) == '/play') {
                exitGame();
            } else {
                $('#popSetting').css('display', 'none');
            }
        }
        if (arguments[2].indexOf("/play/games/") > -1) {
            let timeout = 0;
            let checkPlayInterval = setInterval(() => {
                var playButtons = $("[class*='PlayButton-module__playButton'][disabled]");
                if (playButtons.length > 0) {
                    playButtons.text("该游戏无法在" + [NFTconfig['regionBlock']['blockIp']] + "游玩,请使用脚本切换其他服尝试");
                    clearInterval(checkPlayInterval);

                }
                if (timeout == 10) {
                    clearInterval(checkPlayInterval);
                }
                timeout++;

            }, 1333);
        }

        if ((!arguments[2].includes("/play/launch/") && !arguments[2].includes('/remote-play'))) {
            exitGame();
        }
        if(arguments[2].includes("/play/launch/")){
            $($("div[class^='StreamGateDialog-module__scrollable']")[0]).css("display","none");
        }

        if (arguments[2].includes("/https")) {
            if (window.location.href.includes("/remote-play")) {
                exitGame();
            }
            setTimeout(() => { history.go(-1) }, 10);
            return;
        }
        if (arguments[2].includes("/dev-tools/direct-connect")) {
            exitGame();
            setTimeout(() => { history.go(-1) }, 10);
            return
        }

        return _pushState.apply(this, arguments);
    }

    function getVideoPlayerFilterStyle() {
        const filters = [];

        const clarity = NFTconfig['VIDEO_CLARITY']['default'];
        if (clarity != 0) {
            const level = 7 - (clarity - 1); // 5,6,7
            const matrix = `0 -1 0 -1 ${level} -1 0 -1 0`;
            document.getElementById('better-xcloud-filter-clarity-matrix').setAttributeNS(null, 'kernelMatrix', matrix);

            filters.push(`url(#better-xcloud-filter-clarity)`);
        }

        const saturation = NFTconfig['VIDEO_SATURATION']['default'];
        if (saturation != 100) {
            filters.push(`saturate(${saturation}%)`);
        }

        const contrast = NFTconfig['VIDEO_CONTRAST']['default'];
        if (contrast != 100) {
            filters.push(`contrast(${contrast}%)`);
        }

        const brightness = NFTconfig['VIDEO_BRIGHTNESS']['default'];
        if (brightness != 100) {
            filters.push(`brightness(${brightness}%)`);
        }

        return filters.join(' ');
    }


    function updateVideoPlayerCss() {
        let $elm = document.getElementById('better-xcloud-video-css');
        if (!$elm) {
            const CE = createElement;

            $elm = CE('style', { id: 'better-xcloud-video-css' });
            document.documentElement.appendChild($elm);

            // Setup SVG filters
            const $svg = CE('svg', {
                'id': 'better-xcloud-video-filters',
                'xmlns': 'http://www.w3.org/2000/svg',
                'class': 'better-xcloud-gone',
            }, CE('defs', { 'xmlns': 'http://www.w3.org/2000/svg' },
                CE('filter', { 'id': 'better-xcloud-filter-clarity', 'xmlns': 'http://www.w3.org/2000/svg' },
                    CE('feConvolveMatrix', { 'id': 'better-xcloud-filter-clarity-matrix', 'order': '3', 'xmlns': 'http://www.w3.org/2000/svg' }))
            )
            );
            document.documentElement.appendChild($svg);
        }

        let filters = getVideoPlayerFilterStyle();
        let css = '';
        if (filters) {
            css += `filter: ${filters} !important;`;
        }

        if (NFTconfig['video_stretch'].default == 'fill') {
            css += 'object-fit: fill !important;';
        }

        if (NFTconfig['video_stretch'].default == 'setting') {
            css += `transform: scaleX(` + (NFTconfig['video_stretch_x_y'].x * 1 + 1) + `) scaleY(` + (NFTconfig['video_stretch_x_y'].y * 1 + 1) + `) !important;`;
        }

        if (css) {
            css = `#game-stream video {${css}}`;
        }

        $elm.textContent = css;
    }
    function screenClicktohide() {
        const $screen = document.querySelector('#PageContent section[class*=PureScreens]');
        const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar');
        const $parent = $screen.parentElement;
        const hideQuickBarFunc = e => {
            e.stopPropagation();
            if (e.target != $parent && e.target.id !== 'MultiTouchSurface' && !e.target.querySelector('#BabylonCanvasContainer-main')) {
                return;
            }

            // Hide Quick settings bar
            $quickBar.style.display = 'none';
            $('.better-xcloud-stats-settings').css("display", "none");

            $parent.removeEventListener('click', hideQuickBarFunc);
            $parent.removeEventListener('touchstart', hideQuickBarFunc);

            if (e.target.id === 'MultiTouchSurface') {
                e.target.removeEventListener('touchstart', hideQuickBarFunc);
            }
        }
        $parent.addEventListener('click', hideQuickBarFunc);
        $parent.addEventListener('touchstart', hideQuickBarFunc);

    }
    //插入按钮
    function injectVideoSettingsButton() {
        const $screen = document.querySelector('#PageContent section[class*=PureScreens]');
        if (!$screen) {
            return;
        }

        if ($screen.xObserving) {
            return;
        }

        $screen.xObserving = true;
        const $parent = $screen.parentElement;
        const $quickBar = document.querySelector('.better-xcloud-quick-settings-bar');
        screenClicktohide();
        let $btnStreamStats;
        let $btnVideoSettings_HD;
        const observer = new MutationObserver(mutationList => {
            mutationList.forEach(item => {
                if (item.type !== 'childList') {
                    return;
                }

                item.addedNodes.forEach(async node => {

                    if (IS_REMOTE_PLAYING) {
                        try {
                            let btn = $(node).find('button[class*=PopupScreen-module__button][data-auto-focus=false]');
                            if ($(btn).length > 0) {
                                $(btn).click();
                                throw new Error("巴啦啦能量-呼尼拉-魔仙变身!");
                            }
                        } catch (e) { }
                    }

                    if (!node.className || !node.className.startsWith('StreamMenu')) {
                        return;
                    }

                    const $orgButton = node.querySelector('div > div > button');
                    if (!$orgButton) {
                        return;
                    }

                    // 创建视频调整
                    const $btnVideoSettings = cloneStreamMenuButton($orgButton, '视频调整', ICON_VIDEO_SETTINGS);
                    $btnVideoSettings.addEventListener('click', e => {
                        e.preventDefault();
                        e.stopPropagation();

                        // Show Quick settings bar
                        $quickBar.style.display = 'flex';

                        $parent.addEventListener('click', screenClicktohide());
                        $parent.addEventListener('touchstart', screenClicktohide());

                        const $touchSurface = document.getElementById('MultiTouchSurface');
                        $touchSurface && $touchSurface.style.display != 'none' && $touchSurface.addEventListener('touchstart', screenClicktohide());
                    });
                    // Add button at the beginning
                    $orgButton.parentElement.insertBefore($btnVideoSettings, $orgButton.parentElement.firstChild);

                    // Hide Quick bar when closing HUD
                    const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
                    $btnCloseHud.addEventListener('click', e => {
                        $quickBar.style.display = 'none';
                    });

                    // 创建流监控
                    const $btnStreamStats = cloneStreamMenuButton($orgButton, '流监控', ICON_HD_STREAM_STATS);
                    $btnStreamStats.addEventListener('click', e => {
                        e.preventDefault();
                        e.stopPropagation();

                        // Toggle Stream Stats
                        StreamStats.toggle();
                    });

                    // Insert after Video Settings button
                    $orgButton.parentElement.insertBefore($btnStreamStats, $btnVideoSettings);

                    //menu图标样式
                    if (StreamStats.status()) {
                        $('.ICON_HD_STREAM_STATS_ON').css("display", 'block');
                        $('.ICON_HD_STREAM_STATS_OFF').css("display", 'none');
                    } else {
                        $('.ICON_HD_STREAM_STATS_ON').css("display", 'none');
                        $('.ICON_HD_STREAM_STATS_OFF').css("display", 'block');
                    }
                    //桥
                    const $menu = document.querySelector('div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module]');
                    //const streamBadgesElement = await StreamBadges.render();
                    $menu.appendChild(await StreamBadges.render());

                    //$menu.insertAdjacentElement('afterend', streamBadgesElement);

                });
            });

            mutationList.forEach(item => {
                if (item.type !== 'childList') {
                    return;
                }

                item.removedNodes.forEach($node => {
                    if (!$node || !$node.className || !$node.className.startsWith) {
                        return;
                    }

                });

                item.addedNodes.forEach(async $node => {
                    if (!$node || !$node.className) {
                        return;
                    }

                    if ($node.className.startsWith('Overlay-module_') || $node.className.startsWith('InProgressScreen')) {
                        $node = $node.querySelector('#StreamHud');
                    }

                    if (!$node || ($node.id || '') !== 'StreamHud') {
                        return;
                    }

                    $(document).on('transitionend', '#StreamHud', function () {
                        if ($('#StreamHud').css('left') == '0px') {
                            $('.hd-stream-setting').removeClass("hd-stream-setting-hide");
                        } else {
                            $('.hd-stream-setting').addClass("hd-stream-setting-hide");
                        }
                        if (!NFTconfig['STATS_SLIDE_OPEN']['default']) { return; }
                        if ($('#StreamHud').css('left') == '0px') {
                            if (!StreamStats.status()) {
                                StreamStats.start();
                            }
                        } else {
                            StreamStats.stop();
                        }
                    });

                    // Get the second last button
                    const $orgButton = $node.querySelector('div[class^=HUDButton]');
                    if (!$orgButton) {
                        return;
                    }

                    // 流监控设置
                    if (!$btnStreamStats) {
                        $btnStreamStats = cloneStreamHudButton($orgButton, "hd-stream-setting", "流监控设置", ICON_HD_STREAM_STATS);
                        $btnStreamStats.addEventListener('click', e => {
                            e.preventDefault();
                            StreamStats.toggleSettingsUi();
                        });
                    }

                    // 视频调整        
                    if (!$btnVideoSettings_HD) {
                        $btnVideoSettings_HD = cloneStreamHudButton($orgButton, "hd-stream-setting", '视频调整', ICON_HD_VIDEO_SETTINGS);
                        $btnVideoSettings_HD.addEventListener('click', e => {
                            e.preventDefault();
                            e.stopPropagation();

                            // Show Quick settings bar
                            $quickBar.style.display = 'flex';

                            $parent.addEventListener('click', screenClicktohide());
                            $parent.addEventListener('touchstart', screenClicktohide());

                            const $touchSurface = document.getElementById('MultiTouchSurface');
                            $touchSurface && $touchSurface.style.display != 'none' && $touchSurface.addEventListener('touchstart', screenClicktohide());
                        });
                    }
                    // Insert buttons after Stream Settings button
                    $orgButton.parentElement.insertBefore($btnStreamStats, $orgButton.parentElement.lastElementChild);
                    $orgButton.parentElement.insertBefore($btnVideoSettings_HD, $btnStreamStats);
                    if ($('#StreamHud').css('left') == '0px' && NFTconfig['STATS_SLIDE_OPEN']['default']) {
                        StreamStats.start();
                    }
                    if (StreamStats.status()) {
                        $('.ICON_HD_STREAM_STATS_ON').css("display", 'block');
                        $('.ICON_HD_STREAM_STATS_OFF').css("display", 'none');
                    } else {
                        $('.ICON_HD_STREAM_STATS_ON').css("display", 'none');
                        $('.ICON_HD_STREAM_STATS_OFF').css("display", 'block');
                    }
                    // Move the Dots button to the beginning
                    const $dotsButton = $orgButton.parentElement.lastElementChild;
                    $dotsButton.parentElement.insertBefore($dotsButton, $dotsButton.parentElement.firstElementChild);

                });
            });
        });
        observer.observe($screen, { subtree: true, childList: true });
    }



    function patchVideoApi() {

        // Show video player when it's ready
        let showFunc;
        showFunc = function () {

            this.removeEventListener('playing', showFunc);

            if (!this.videoWidth) {
                return;
            }

            onStreamStarted(this);
            STREAM_WEBRTC?.getStats().then(stats => {

                if (NFTconfig['STATS_SHOW_WHEN_PLAYING']['default']) {
                    StreamStats.start();
                }
            });
        }
        HTMLMediaElement.prototype.orgPlay = HTMLMediaElement.prototype.play;
        HTMLMediaElement.prototype.play = function () {
            if (letmeOb && NFTconfig['antiKick'] == 1) {
                const divElement = $('div[data-testid="ui-container"]')[0];
                const observer = new MutationObserver(function (mutations) {
                    try {
                        mutations.forEach(function (mutation) {
                            if (mutation.type === 'childList') {
                                mutation.addedNodes.forEach(function (addedNode) {
                                    let btn = $(addedNode).find('button[data-auto-focus="true"]');
                                    if ($(btn).length > 0 && btn.parent().children().length == 1) {
                                        $(btn).click();
                                        throw new Error("巴啦啦能量-呼尼拉-魔仙变身!");
                                    }
                                });
                            }
                        });
                    } catch (e) { }
                });

                setTimeout(() => {
                    observer.observe(divElement, { childList: true, subtree: true });
                    console.log('antiKick已部署');
                }, 1000 * 20);
                letmeOb = false;
            }
            if (this.className && this.className.startsWith('XboxSplashVideo')) {
                this.volume = 0;
                this.style.display = 'none';
                this.dispatchEvent(new Event('ended'));

                return {
                    catch: () => { },
                };
                return nativePlay.apply(this);
            }

            this.addEventListener('playing', showFunc);
            injectVideoSettingsButton();
            return this.orgPlay.apply(this);
        };
    }

    function onStreamStarted($video) {

        StreamBadges.resolution = { width: $video.videoWidth, height: $video.videoHeight };
        StreamBadges.startTimestamp = +new Date;

        // Get battery level
        try {
            navigator.getBattery && navigator.getBattery().then(bm => {
                StreamBadges.startBatteryLevel = Math.round(bm.level * 100);
            });


            STREAM_WEBRTC.getStats().then(stats => {
                const allVideoCodecs = {};
                let videoCodecId;

                const allAudioCodecs = {};
                let audioCodecId;

                const allCandidates = {};
                let candidateId;

                stats.forEach(stat => {
                    if (stat.type == 'codec') {
                        const mimeType = stat.mimeType.split('/');
                        if (mimeType[0] === 'video') {
                            // Store all video stats
                            allVideoCodecs[stat.id] = stat;
                        } else if (mimeType[0] === 'audio') {
                            // Store all audio stats
                            allAudioCodecs[stat.id] = stat;
                        }
                    } else if (stat.type === 'inbound-rtp' && stat.packetsReceived > 0) {
                        // Get the codecId of the video/audio track currently being used
                        if (stat.kind === 'video') {
                            videoCodecId = stat.codecId;
                        } else if (stat.kind === 'audio') {
                            audioCodecId = stat.codecId;
                        }
                    } else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
                        candidateId = stat.remoteCandidateId;
                    } else if (stat.type === 'remote-candidate') {
                        allCandidates[stat.id] = stat.address;
                    }
                });

                // Get video codec from codecId
                if (videoCodecId) {
                    const videoStat = allVideoCodecs[videoCodecId];
                    const video = {
                        codec: videoStat.mimeType.substring(6),
                    };

                    if (video.codec === 'H264') {
                        const match = /profile-level-id=([0-9a-f]{6})/.exec(videoStat.sdpFmtpLine);
                        video.profile = match ? match[1] : null;
                    }

                    StreamBadges.video = video;
                }

                // Get audio codec from codecId
                if (audioCodecId) {
                    const audioStat = allAudioCodecs[audioCodecId];
                    StreamBadges.audio = {
                        codec: audioStat.mimeType.substring(6),
                        bitrate: audioStat.clockRate,
                    }
                }
                // Get server type
                if (candidateId) {
                    //console.log(candidateId, allCandidates);
                    StreamBadges.ipv6 = allCandidates[candidateId].includes(':');
                }

            });
        } catch (e) { }

    }
    function moveCodecToIndex(array, currentIndex, targetIndex, element) {
        array.splice(currentIndex, 1);
        array.splice(targetIndex, 0, element);
    }
    function customizeRtcCodecs() {

        const customCodecProfile = NFTconfig['rtcCodecPreferences']['default'];

        if (customCodecProfile === '默认') {
            console.log("customizeRtcCodecs:默认");
            return;
        }
        if (typeof RTCRtpTransceiver === 'undefined' || !('setCodecPreferences' in RTCRtpTransceiver.prototype)) {
            return false;
        }

        let codecProfilePrefix = "";
        let codecProfileLevelId = "";
        let codecMimeType = "";
        const codecProfileMap = { "264": { "高": "4d", "中": "42e", "低": "420" } };

        if (customCodecProfile.includes("264")) {
            const codecLevel = Object.keys(codecProfileMap["264"]).find(level => customCodecProfile.includes(level));
            if (codecLevel) {
                codecProfilePrefix = codecProfileMap["264"][codecLevel];
                codecProfileLevelId = `profile-level-id=${codecProfilePrefix}`;
            }
        } else {
            codecMimeType = "video/" + customCodecProfile;
        }

        RTCRtpTransceiver.prototype.originalSetCodecPreferences = RTCRtpTransceiver.prototype.setCodecPreferences;
        RTCRtpTransceiver.prototype.setCodecPreferences = function (codecs) {
            let customizedCodecs = null;
            if (customCodecProfile === '自动') {

                let a = [];
                let b = [];
                let c = [];
                let d = [];
                codecs.slice().forEach((item) => {
                    if (item.mimeType == 'video/H264') {
                        if (item.sdpFmtpLine.indexOf('id=4d') > -1) {
                            a.push(item);
                        } else if (item.sdpFmtpLine.indexOf('id=42e') > -1) {
                            b.push(item);
                        } else if (item.sdpFmtpLine.indexOf('id=420') > -1) {
                            c.push(item);
                        } else {
                            d.push(item);
                        }
                    } else {
                        d.push(item);
                    }
                });
                customizedCodecs = a.concat(b, c, d);

            } else {
                customizedCodecs = codecs.slice();
                let insertionIndex = 0;

                customizedCodecs.forEach((codec, index) => {
                    if (codecProfileLevelId !== '' && codec.sdpFmtpLine && codec.sdpFmtpLine.includes(codecProfileLevelId)) {
                        moveCodecToIndex(customizedCodecs, index, insertionIndex, codec);
                        insertionIndex++;
                    } else if (codec.mimeType === codecMimeType) {
                        moveCodecToIndex(customizedCodecs, index, insertionIndex, codec);
                        insertionIndex++;
                    }
                });
            }

            try {
                this.originalSetCodecPreferences.apply(this, [customizedCodecs]);
                console.log("编解码偏好配置成功");
            } catch (error) {
                console.log("无法修改编解码配置,将使用默认设置");
                this.originalSetCodecPreferences.apply(this, [codecs]);
            }
        }
    }

    customizeRtcCodecs();
    patchVideoApi();

    let mslogotimeOut = 0;
    function mslogoClickevent(mslogoInterval, s) {
        let mslogodom = $($('header>div>div>button')[1]);
        if (mslogodom.length > 0) {
            clearInterval(mslogoInterval);
            mslogodom = mslogodom.next();
            if (mslogodom.text() == ("⚙️ 设置" + nftxboxversion)) { return; }
            mslogodom.removeAttr('href');
            mslogodom.css("color", 'white');
            mslogodom.text("⚙️ 设置" + nftxboxversion);
            mslogodom.click(() => {
                naifeitian.showSetting();
            });
            setTimeout(() => { mslogoClickevent(mslogoInterval) }, 5000);
        }
        mslogotimeOut = mslogotimeOut + 1;
        if (mslogotimeOut > 10) {
            mslogotimeOut = 0;
            clearInterval(mslogoInterval);
        }
    }
    let mslogoInterval = setInterval(() => {
        mslogoClickevent(mslogoInterval, 3000);
    }, 1000);

    class Dialog {
        constructor(title, className, $content, onClose) {
            const CE = createElement;

            // Create dialog overlay
            this.$overlay = document.querySelector('.bx-dialog-overlay');
            if (!this.$overlay) {
                this.$overlay = CE('div', { 'class': 'bx-dialog-overlay bx-gone' });
                document.documentElement.appendChild(this.$overlay);
            }

            let $close;
            this.onClose = onClose;
            this.$dialog = CE('div', { 'class': `bx-dialog ${className} bx-gone` },
                CE('b', {}, title),
                CE('div', { 'class': 'bx-dialog-content' }, $content),
                $close = CE('button', {}, "关闭"));

            $close.addEventListener('click', e => {
                this.hide(e);
            });
            document.documentElement.appendChild(this.$dialog);
        }

        show() {
            this.$dialog.classList.remove('bx-gone');
            this.$overlay.classList.remove('bx-gone');
        }

        hide(e) {
            this.$dialog.classList.add('bx-gone');
            this.$overlay.classList.add('bx-gone');
            this.onClose && this.onClose(e);
        }

        toggle() {
            this.$dialog.classList.toggle('bx-gone');
            this.$overlay.classList.toggle('bx-gone');
        }

        preload() {
            this.$dialog.classList.add('bx-gone');
            this.$overlay.classList.add('bx-gone');
        }
    }
    let REMOTE_PLAY_CONFIG;
    let IS_REMOTE_PLAYING;
    let REMOTE_PLAY_SERVER;

    class RemotePlay {
        static XCLOUD_TOKEN;
        static XHOME_TOKEN;
        static #CONSOLES;

        static #STATE_LABELS = {
            'On': "已开机",
            'Off': "已关机",
            'ConnectedStandby': "待机中",
            'Unknown': "未知",
        };

        static get BASE_DEVICE_INFO() {
            return {
                appInfo: {
                    env: {
                        clientAppId: window.location.host,
                        clientAppType: 'browser',
                        clientAppVersion: '21.1.98',
                        clientSdkVersion: '8.5.3',
                        httpEnvironment: 'prod',
                        sdkInstallId: '',
                    },
                },
                dev: {
                    displayInfo: {
                        dimensions: {
                            widthInPixels: 1920,
                            heightInPixels: 1080,
                        },
                        pixelDensity: {
                            dpiX: 1,
                            dpiY: 1,
                        },
                    },
                    hw: {
                        make: 'Microsoft',
                        model: 'unknown',
                        sdktype: 'web',
                    },
                    os: {
                        name: 'windows',
                        ver: '22631.2715',
                        platform: 'desktop',
                    },
                    browser: {
                        browserName: 'chrome',
                        browserVersion: '119.0',
                    },
                },
            };
        }

        static #dialog;
        static #$content;
        static #$consoles;

        static #initialize() {
            if (RemotePlay.#$content) {
                return;
            }
            const CE = createElement;
            RemotePlay.#$content = CE('div', {}, "获取控制台列表");
            RemotePlay.#dialog = new Dialog(("串流"), '', RemotePlay.#$content);

            RemotePlay.#getXhomeToken(() => {
                RemotePlay.#getConsolesList(() => {
                    console.log(RemotePlay.#CONSOLES);
                    RemotePlay.#renderConsoles();
                });
            });
        }

        static #renderConsoles() {
            const CE = createElement;

            const $fragment = document.createDocumentFragment();

            if (!RemotePlay.#CONSOLES || RemotePlay.#CONSOLES.length === 0) {
                $fragment.appendChild(CE('span', {}, "未找到主机"));
            } else {
                const $settingNote = CE('p', {});

                const resolutions = [1080, 720];
                const currentResolution = NFTconfig['REMOTE_PLAY_RESOLUTION']['default'];
                const $resolutionSelect = CE('select', {});
                for (const resolution of resolutions) {
                    const value = `${resolution}p`;

                    const $option = CE('option', { 'value': value }, value);
                    if (currentResolution === value) {
                        $option.selected = true;
                    }

                    $resolutionSelect.appendChild($option);
                }
                $resolutionSelect.addEventListener('change', e => {
                    const value = $resolutionSelect.value;

                    $settingNote.textContent = value === '1080p' ? '✅ ' + "可串流xbox360游戏" : '❌ ' + "不可串流xbox360游戏";

                    NFTconfig['REMOTE_PLAY_RESOLUTION']['default'] = value;
                    naifeitian.setValue(NFTconfig['REMOTE_PLAY_RESOLUTION']['name'], NFTconfig['REMOTE_PLAY_RESOLUTION']);
                });
                $resolutionSelect.dispatchEvent(new Event('change'));

                const $qualitySettings = CE('div', { 'class': 'bx-remote-play-settings' },
                    CE('div', {},
                        CE('label', {}, "目标分辨率", $settingNote),
                        $resolutionSelect,
                    )
                );

                $fragment.appendChild($qualitySettings);
            }

            for (let con of RemotePlay.#CONSOLES) {
                let $connectButton;
                const $child = CE('div', { 'class': 'bx-remote-play-device-wrapper' },
                    CE('div', { 'class': 'bx-remote-play-device-info' },
                        CE('div', {},
                            CE('span', { 'class': 'bx-remote-play-device-name' }, con.deviceName),
                            CE('span', { 'class': 'bx-remote-play-console-type' }, con.consoleType)
                        ),
                        CE('div', { 'class': 'bx-remote-play-power-state' }, RemotePlay.#STATE_LABELS[con.powerState]),
                    ),
                    $connectButton = CE('button', { 'class': 'bx-primary-button bx-no-margin' }, "连接"),
                );

                $connectButton.addEventListener('click', e => {
                    REMOTE_PLAY_CONFIG = {
                        serverId: con.serverId,
                    };
                    window.BX_REMOTE_PLAY_CONFIG = REMOTE_PLAY_CONFIG;

                    const url = window.location.href.substring(0, 31) + '/launch/fortnite/BT5P2X999VH2#remote-play';

                    const $pageContent = document.getElementById('PageContent');
                    const $anchor = CE('a', { href: url, class: 'bx-hidden', style: 'position:absolute;top:-9990px;left:-9999px' }, '');
                    $anchor.addEventListener('click', e => {
                        setTimeout(() => {
                            $pageContent.removeChild($anchor);
                        }, 1000);
                    });

                    $pageContent.appendChild($anchor);
                    $anchor.click();

                    RemotePlay.#dialog.hide();
                });
                $fragment.appendChild($child);
            }

            RemotePlay.#$content.parentElement.replaceChild($fragment, RemotePlay.#$content);
        }

        static detect() {

            IS_REMOTE_PLAYING = window.location.pathname.includes('/remote-play') || window.location.hash.startsWith('#remote-play');
            if (IS_REMOTE_PLAYING) {
                window.BX_REMOTE_PLAY_CONFIG = REMOTE_PLAY_CONFIG;
                // 移除 /launch/...
                window.history.replaceState({}, '', 'https://www.xbox.com/' + location.pathname.substring(1, 6) + '/remote-play');

            } else {
                window.BX_REMOTE_PLAY_CONFIG = null;
            }
        }

        static #getXhomeToken(callback) {
            if (RemotePlay.XHOME_TOKEN) {
                callback();
                return;
            }

            let GSSV_TOKEN;
            try {
                const xboxUserInfo = JSON.parse(localStorage.getItem('xboxcom_xbl_user_info'));
                GSSV_TOKEN = xboxUserInfo.tokens['http://gssv.xboxlive.com/'].token;
            } catch (e) {
                for (let i = 0; i < localStorage.length; i++) {
                    const key = localStorage.key(i);

                    if (key.startsWith('Auth.User.')) {
                        const json = JSON.parse(localStorage.getItem(key));

                        GSSV_TOKEN = json.tokens.find(token => token.relyingParty.includes('gssv.xboxlive.com'))?.tokenData.token;

                        if (GSSV_TOKEN) {
                            break;
                        }
                    }
                }
            }

            fetch('https://xhome.gssv-play-prod.xboxlive.com/v2/login/user', {
                method: 'POST',
                body: JSON.stringify({
                    offeringId: 'xhome',
                    token: GSSV_TOKEN,
                }),
                headers: {
                    'Content-Type': 'application/json; charset=utf-8',
                },
            }).then(resp => resp.json())
                .then(json => {
                    RemotePlay.XHOME_TOKEN = json.gsToken;
                    callback();
                });
        }
        //获取xbox列表
        static async #getConsolesList(callback) {
            if (RemotePlay.#CONSOLES) {
                callback();
                return;
            }

            let servers;
            if (!REMOTE_PLAY_SERVER) {
                if (NFTconfig['REMOTE_SERVER_LIST'].length == 0) {
                    servers = ['wus2', 'eus', 'uks', 'ejp'];
                } else {
                    servers = NFTconfig['REMOTE_SERVER_LIST'];
                }

            } else {
                servers = REMOTE_PLAY_SERVER;
            }

            const options = {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${RemotePlay.XHOME_TOKEN}`,
                },
            };

            const controller = new AbortController();
            const signal = controller.signal;

            let foundResponse = false;

            const promises = NFTconfig['REMOTE_SERVER_LIST'].map(async (server) => {
                const url = `https://${server}.core.gssv-play-prodxhome.xboxlive.com/v6/servers/home?mr=50`;

                try {
                    const response = await fetch(url, { ...options, signal });
                    const json = await response.json();
                    if (json.results && !foundResponse) {
                        foundResponse = true;
                        RemotePlay.#CONSOLES = json.results;
                        REMOTE_PLAY_SERVER = server;

                        // 取消剩余请求
                        controller.abort();

                        return 'Found response';
                    }

                } catch (error) {
                     console.log(`请求至服务器 ${server} 时遇到错误,已忽略:`, error);
                }
            });


            // 等待所有请求完成
            const results=await Promise.allSettled(promises);


            // 检查是否有请求成功
            const successfulResult = results.find(result => result.status === 'fulfilled' && result.value === 'Found response');

            if (successfulResult) {
                callback();
            } else {
                RemotePlay.#CONSOLES = [];
            }

        }

        static preload() {
            RemotePlay.#initialize();
            RemotePlay.#dialog.preload();
        }


        static showDialog() {
            RemotePlay.#initialize();
            RemotePlay.#dialog.show();
        }
    }
    function bindmslogoevent() {
        let divElement = $('#gamepass-root > div > div');
        if (divElement.length == 0) {
            setTimeout(() => {
                bindmslogoevent();
            }, 2333);
            return;
        }
        divElement = divElement.get(0);
        let mslogodom = $(divElement).children('header').find('a[href]');
        if (mslogodom.length == 0) {
            setTimeout(() => {
                bindmslogoevent();
            }, 2333);
            return;
        }
        if (mslogodom.length > 0) { mslogodom = $(mslogodom.get(0)); }
        let linkElement = $("a:contains('⚙️ 设置" + nftxboxversion + "')");
        for (let i = 0; i < linkElement.length; i++) {
            let ele = linkElement.get(i);
            if ($(ele).attr('class').indexOf('button') > -1) {
                return;
            }
        }
        mslogodom.removeAttr('href');
        mslogodom.css("color", 'white');
        mslogodom.text("⚙️ 设置" + nftxboxversion);
        mslogodom.click(() => {
            naifeitian.showSetting();
        });

        if (NFTconfig['enableRemotePlay'] == 1) {
            let remotePlayBtn = $('.bx-remote-play-button');
            if (remotePlayBtn.length > 0) { return; }
            //添加串流按钮
            var targetElement = $("[title*='Account Settings']");

            var newButton = $(`<button class="bx-remote-play-button" title="远程串流"><svg fill="none" stroke="#fff" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" viewBox="0 0 32 32"><g transform="matrix(.492308 0 0 .581818 -14.7692 -11.6364)"><clipPath id="A"><path d="M30 20h65v55H30z"></path></clipPath><g clip-path="url(#A)"><g transform="matrix(.395211 0 0 .334409 11.913 7.01124)"><g transform="matrix(.555556 0 0 .555556 57.8889 -20.2417)" fill="none" stroke="#fff" stroke-width="13.88"><path d="M200 140.564c-42.045-33.285-101.955-33.285-144 0M168 165c-23.783-17.3-56.217-17.3-80 0"></path></g><g transform="matrix(-.555556 0 0 -.555556 200.111 262.393)"><g transform="matrix(1 0 0 1 0 11.5642)"><path d="M200 129c-17.342-13.728-37.723-21.795-58.636-24.198C111.574 101.378 80.703 109.444 56 129" fill="none" stroke="#fff" stroke-width="13.88"></path></g><path d="M168 165c-23.783-17.3-56.217-17.3-80 0" fill="none" stroke="#fff" stroke-width="13.88"></path></g><g transform="matrix(.75 0 0 .75 32 32)"><path d="M24 72h208v93.881H24z" fill="none" stroke="#fff" stroke-linejoin="miter" stroke-width="9.485"></path><circle cx="188" cy="128" r="12" stroke-width="10" transform="matrix(.708333 0 0 .708333 71.8333 12.8333)"></circle><path d="M24.358 103.5h110" fill="none" stroke="#fff" stroke-linecap="butt" stroke-width="10.282"></path></g></g></g></g></svg></button>`);
            newButton.on("click", function () {
                RemotePlay.showDialog();
            });

            newButton.insertBefore(targetElement);
        }
        setTimeout(() => { bindmslogoevent() }, 5000);
    }

    bindmslogoevent();

    if (window.location.pathname.includes('/play/')) {
        NFTconfig['PATCH_ORDERS'] = NFTconfig['PATCH_ORDERS'].concat(NFTconfig['PLAYING_PATCH_ORDERS']);
    } else {
        NFTconfig['PATCH_ORDERS'].push(['loadingEndingChunks']);
    }
    naifeitian.patchFunctionBind();
    RemotePlay.detect();


    if (window.location.pathname.toLocaleLowerCase() == '/zh-cn/play') {
        window.location.href = "https://www.xbox.com/en-us/play";
    }
    if (window.location.href.endsWith('consoles/remote-play') || window.location.href.endsWith('/remote-play')) {
        //https://www.xbox.com/en-US/consoles/remote-play
        let jurl = window.location.href.replace('/consoles', '');
        jurl = window.location.href.replace('/remote-play', '/play');
        window.location.href = jurl;
    }

    if (window.location.href.endsWith('play/dev-tools')) {
      window.location.href="/play"
    }



    RTCPeerConnection.prototype.orgAddIceCandidate = RTCPeerConnection.prototype.addIceCandidate;

    RTCPeerConnection.prototype.addIceCandidate = function (...args) {
        STREAM_WEBRTC = this;
        return this.orgAddIceCandidate(...args);
    };


    function addCss() {

        let popCss = `

#popSetting {
width: 76px;
height: 33px;
background: #fff;
position: absolute;
  top: 30%;
  cursor: pointer;
box-sizing: border-box;
background-size: 100% 100%;
overflow: hidden;
  font-family: Arial;
font-size: 18px;
line-height: 30px;
font-weight: bold;
color: #000000bf;
border: 2px solid;
border-radius: 10px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none ;
}
.better-xcloud-hidden {
  visibility: hidden !important;
}
.hd-stream-setting-hide{
    pointer-events: none !important;
}
div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]) {
    opacity: 0;
    position: absolute;
    top: -9999px;
    left: -9999px;
}
.bx-remote-play-button {
    height: auto;
    margin-right: 8px !important;
    position: relative;
    background-color: transparent;
    border:0px;
    border-radius: 50%;
}

.bx-remote-play-button svg {
    width: 28px;
    height: 46px;
}
.bx-remote-play-button:hover {
    background-color: #515863;
}

.better-xcloud-stats-bar {
  display: block;
  user-select: none;
  position: fixed;
  top: 0;
  background-color: #000;
  color: #fff;
  font-family: Consolas, "Courier New", Courier, monospace;
  font-size: 0.9rem;
  padding-left: 8px;
  z-index: 1000;
  text-wrap: nowrap;
}

.better-xcloud-stats-bar[data-position=top-left] {
  left: 20px;
}

.better-xcloud-stats-bar[data-position=top-right] {
  right: 0;
}

.better-xcloud-stats-bar[data-position=top-center] {
  transform: translate(-50%, 0);
  left: 50%;
}

.better-xcloud-stats-bar[data-transparent=true] {
  background: none;
  filter: drop-shadow(1px 0 0 #000) drop-shadow(-1px 0 0 #000) drop-shadow(0 1px 0 #000) drop-shadow(0 -1px 0 #000);
}

.better-xcloud-stats-bar label {
  margin: 0 8px 0 0;
  font-family: Bahnschrift, Arial, Helvetica, sans-serif;
  font-size: inherit;
  font-weight: bold;
  vertical-align: middle;
}

.better-xcloud-stats-bar span {
  min-width: 60px;
  display: inline-block;
  text-align: right;
  padding-right: 8px;
  margin-right: 8px;
  border-right: 2px solid #fff;
  vertical-align: middle;
}
.better-xcloud-stats-bar div {
    min-width: 60px;
    display: inline-block;
    text-align: right;
    padding-right: 8px;
    margin-right: 8px;
    border-right: 2px solid #fff;
    vertical-align: middle;
    float:left
  }

.better-xcloud-stats-bar span[data-grade=good] {
  color: #6bffff;
}

.better-xcloud-stats-bar span[data-grade=ok] {
  color: #fff16b;
}

.better-xcloud-stats-bar span[data-grade=bad] {
  color: #ff5f5f;
}

.better-xcloud-stats-bar span:first-of-type {
  min-width: 30px;
}

.better-xcloud-stats-bar span:last-of-type {
  border: 0;
  margin-right: 0;
}
.better-xcloud-stats-bar div:last-of-type {
    border: 0;
    margin-right: 0;
  }

.better-xcloud-stats-settings {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-right: -50%;
  transform: translate(-50%, -50%);
  width: 420px;
  padding: 20px;
  border-radius: 8px;
  z-index: 500;
  background: #1a1b1e;
  color: #fff;
  font-weight: 400;
  font-size: 16px;
  font-family: "Segoe UI", Arial, Helvetica, sans-serif;
  box-shadow: 0 0 6px #000;
  user-select: none;
  overflow-y: auto;
}

.better-xcloud-stats-settings *:focus {
  outline: none !important;
}

.better-xcloud-stats-settings > b {
  color: #fff;
  display: block;
  font-family: Bahnschrift, Arial, Helvetica, sans-serif;
  font-size: 26px;
  font-weight: 400;
  line-height: 32px;
  margin-bottom: 12px;
}

.better-xcloud-stats-settings > div {
  display: flex;
  margin-bottom: 8px;
  padding: 2px 4px;
}

.better-xcloud-stats-settings label {
  flex: 1;
  margin-bottom: 0;
  align-self: center;
}

.better-xcloud-stats-settings button {
  padding: 8px 32px;
  margin: 20px auto 0;
  border: none;
  border-radius: 4px;
  display: block;
  background-color: #2d3036;
  text-align: center;
  color: white;
  text-transform: uppercase;
  font-family: Bahnschrift, Arial, Helvetica, sans-serif;
  font-weight: 400;
  line-height: 18px;
  font-size: 14px;
}

@media (hover: hover) {
  .better-xcloud-stats-settings button:hover {
      background-color: #515863;
  }
}

.better-xcloud-stats-settings button:focus {
  background-color: #515863;
}

.better-xcloud-gone {
  display: none !important;
}

.better-xcloud-quick-settings-bar {
  display: none;
  user-select: none;
  -webkit-user-select: none;
  position: fixed;
  bottom: 10%;
  left: 50%;
  transform: translate(-50%, 0);
  z-index: 9999;
  padding: 16px;
  width: 600px;
  background: #1a1b1e;
  color: #fff;
  border-radius: 8px 8px 0 0;
  font-weight: 400;
  font-size: 14px;
  font-family: Bahnschrift, Arial, Helvetica, sans-serif;
  text-align: center;
  box-shadow: 0px 0px 6px #000;
  opacity: 0.95;
}

.better-xcloud-quick-settings-bar *:focus {
  outline: none !important;
}

.better-xcloud-quick-settings-bar > div {
  flex: 1;
}

.better-xcloud-quick-settings-bar label {
  font-size: 16px;
  display: block;
  margin-bottom: 8px;
}

.better-xcloud-quick-settings-bar input {
  width: 22px;
  height: 22px;
}

.better-xcloud-quick-settings-bar button {
  border: none;
  width: 22px;
  height: 22px;
  margin: 0 4px;
  line-height: 22px;
  background-color: #515151;
  color: #fff;
  border-radius: 4px;
}

@media (hover: hover) {
  .better-xcloud-quick-settings-bar button:hover {
      background-color: #414141;
      color: white;
  }
}

.better-xcloud-quick-settings-bar button:active {
      background-color: #414141;
      color: white;
  }

.better-xcloud-quick-settings-bar span {
  display: inline-block;
  width: 40px;
  font-weight: bold;
  font-family: Consolas, "Courier New", Courier, monospace;
}


.closeSetting1 {
    color: #0099CC;
    background: transparent;
    border: 2px solid #0099CC;
    border-radius: 6px;
    border: none;
    color: white;
    padding: 3px 13px;
    text-align: center;
    display: inline-block;
    font-size: 16px;
    margin: 4px 2px;
    -webkit-transition-duration: 0.4s; /* Safari */
    transition-duration: 0.4s;
    cursor: pointer;
    text-decoration: none;
    text-transform: uppercase;
   }
    .closeSetting2 {
    background-color: white;
    color: black;
    border: 2px solid #008CBA;
    display: block;
    margin: 0 auto;
    margin-top: 5px;
   }
  .closeSetting2:hover {
    background-color: #008CBA;
    color: white;
   }
  .settingsBackgroud{
      position: fixed;
      left: 0;
      top: 0;
      background: #0000;
      width: 100%;
      height: 90vh;
      overflow: scroll;
      z-index:8888;
    }
    .settingsBox{
      position: relative;
      background: wheat;
      width: fit-content;
              height: fit-content;
      border-radius: 5px;
      margin: 5% auto;
              padding: 10px;
              font-family: '微软雅黑';
              line-height: 22px;
              top:5%;
              z-index:8889;
    }
         .settingsBoxInputRadio{
              background-color: initial;
              cursor: pointer;
              appearance: auto;
              box-sizing: border-box;
              margin: 3px 3px 0px 5px;
              padding: initial;
              padding-top: initial;
              padding-right: initial;
              padding-bottom: initial;
              padding-left: initial;
              border: initial;
              -webkit-appearance: checkbox;
              accent-color: dodgerblue;
          }

          #StreamHud >div{
      background-color:rgba(255,0,0,0)!important;
      }

      #StreamHud >button{
      background-color:rgba(0,0,0,0)!important;
      }
      #StreamHud >button > div{
      opacity:0.3!important;
      }

      #touchControllerEventArea {
    pointer-events: auto;
    position: fixed;
    bottom: 0;
    right: 0;
    width: 33%;
    height: 6vh;
    z-index: 5678;
    background-color: rgba(0, 0, 0, 0);
    }
.better-xcloud-badges {
  position: absolute;
  margin-left: 0px;
  user-select: none;
  -webkit-user-select: none;
  bottom: 0px;
  display: none;
}
/* 横屏 */
@media screen and (orientation: landscape) {
  .better-xcloud-badges {
    display: block; /* 显示 */
  }
}

/* 竖屏 */
@media screen and (orientation: portrait) {
  .better-xcloud-badges {
    display: none; /* 隐藏 */
  }
}

button[class*=BaseItem-module__container] {
    height:fit-content;
}
div[class*=Menu-module__scrollable] {
    height:225px;
}
.better-xcloud-badge {
    border: none;
    display: inline-block;
    line-height: 24px;
    color: #fff;
    font-family: Bahnschrift Semibold, Arial, Helvetica, sans-serif;
    font-size: 14px;
    font-weight: 400;
    margin: 0 8px 8px 0;
    box-shadow: 0px 0px 6px #000;
    border-radius: 4px;
}

.better-xcloud-badge-name {
    background-color: #2d3036;
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px 0 0 4px;
    text-transform: uppercase;
}

.better-xcloud-badge-value {
    background-color: grey;
    display: inline-block;
    padding: 2px 8px;
    border-radius: 0 4px 4px 0;
}

.better-xcloud-badge-battery[data-charging=true] span:first-of-type::after {
    content: ' ⚡️';
}

div[class*=NotFocusedDialog-module__container] {
    display:none
}
@keyframes blink {
      20% {color: blueviolet; }
      50% { color: blue; }
      100% { color: green; }
    }

    .blink-text {
      font-size: 15px;
      font-weight: bold;
      animation: blink 3s infinite;
      float: right;
      cursor: pointer;
      display:none
    }
    .remote-play-button {
    background-color: transparent;
    border: none;
    color: white;
    font-weight: bold;
    line-height: 30px;
    border-radius: 4px;
    padding: 8px;
}

.remote-play-button:hover, .remote-play-button:focus {
    background-color: #515863;
}
.bx-dialog-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--bx-dialog-overlay-z-index);
    background: black;
    opacity: 50%;
}

.bx-dialog {
    display: flex;
    flex-flow: column;
    max-height: 90vh;
    position: fixed;
    top: 50%;
    left: 50%;
    margin-right: -50%;
    transform: translate(-50%, -50%);
    max-width: 410px;
    width:95%;
    padding: 20px;
    border-radius: 8px;
    z-index: var(--bx-dialog-z-index);
    background: #1a1b1e;
    color: #fff;
    font-weight: 400;
    font-size: 16px;
    font-family: var(--bx-normal-font);
    box-shadow: 0 0 6px #000;
    user-select: none;
    -webkit-user-select: none;
}

.bx-dialog *:focus {
    outline: none !important;
}

.bx-dialog > b {
    color: #fff;
    display: block;
    font-family: var(--bx-title-font);
    font-size: 26px;
    font-weight: 400;
    line-height: 32px;
    margin-bottom: 12px;
}

.bx-dialog > div {
    overflow: auto;
    padding: 2px 0;
}

.bx-dialog > button {
    padding: 8px 32px;
    margin: 20px auto 0;
    border: none;
    border-radius: 4px;
    display: block;
    background-color: #2d3036;
    text-align: center;
    color: white;
    text-transform: uppercase;
    font-family: var(--bx-title-font);
    font-weight: 400;
    line-height: 18px;
    font-size: 14px;
}
.bx-gone {
    display: none !important;
}
.bx-remote-play-settings {
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid #2d2d2d;
}

.bx-remote-play-settings > div {
    display: flex;
}

.bx-remote-play-settings label {
    flex: 1;
}

.bx-remote-play-settings label p {
    margin: 4px 0 0;
    padding: 0;
    color: #888;
    font-size: 12px;
}

.bx-remote-play-settings input {
    display: block;
    margin: 0 auto;
}

.bx-remote-play-settings span {
    font-weight: bold;
    font-size: 18px;
    display: block;
    margin-bottom: 8px;
    text-align: center;
}
.bx-remote-play-device-name {
    font-size: 20px;
    font-weight: bold;
    display: inline-block;
    vertical-align: middle;
}
.bx-remote-play-console-type {
    font-size: 12px;
    background: #888;
    color: #fff;
    display: inline-block;
    border-radius: 14px;
    padding: 2px 10px;
    margin-left: 8px;
    vertical-align: middle;
}

.bx-remote-play-power-state {
    color: #888;
    font-size: 14px;
}
.bx-remote-play-power-state {
    color: #888;
    font-size: 14px;
}
.bx-primary-button {
    padding: 8px 32px;
    margin: 10px auto 0;
    border: none;
    border-radius: 4px;
    display: block;
    background-color: #044e2a;
    text-align: center;
    color: white;
    text-transform: uppercase;
    font-family: var(--bx-title-font);
    font-weight: 400;
    font-size: 14px;
    line-height: 24px;
}

@media (hover: hover) {
    .bx-primary-button:hover {
        background-color: #00753c;
    }
}

.bx-primary-button:focus {
    background-color: #00753c;
}

.bx-primary-button:active {
    background-color: #00753c;
}

.bx-primary-button[disabled] {
    background: #393939;
    color: #a2a2a2;
}
.bx-no-margin {
    margin: 0 !important;
}
.bx-remote-play-device-info {
    flex: 1;
    padding: 4px 0;
}
.bx-remote-play-device-wrapper {
    display: flex;
    margin-bottom: 8px;
}

.bx-remote-play-device-wrapper:not(:last-child) {
  margin-bottom: 14px;
}
#xcloud_setting_STATS_BUTTON{
    background-color: cadetblue;
    width:100%;
}
.stats-container {
    width: 30%;
    border:1px solid white;
}

.drag-handle {
    cursor: pointer;
    margin: 5px;
    padding: 6px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background-color: #f9f9f9;
    transition: background-color 0.2s ease;
    position: relative;
    color:black;
    height: 30px;
}

.drag-handle::after {
    content: "≡";
    position: absolute;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    color: #999;
}

.drag-handle.drag-over {
    background-color: #e0f0ff;
}

.drag-handle.dragging {
    opacity: 0.5;
}

.drag-handle.stats-selected {
    background-color: #cbffcd;
}
.drag-handle.stats-delete {
    text-decoration: line-through;
}

.placeholder {
    border: 1px dashed #ccc;
    border-radius: 5px;
    margin: 5px;
    height: 30px;
    display: none;
    background-color:#c5c5c5!important;
}

/* 新增样式,用于被拖拽元素的副本 */
.dragged-copy-item {
    position: absolute;
    z-index: 1000;
    pointer-events: none;
    /* 防止被拖拽元素副本干扰其他事件 */
    display: none;
    width:100px
}
`;
        if (NFTconfig['disableTouchControls'] == 1) {
            popCss += `
#MultiTouchSurface, #BabylonCanvasContainer-main {
  display: none !important;
}

`};

        let xfbasicStyle = document.createElement('style');
        xfbasicStyle.innerHTML = popCss;
        let docxf = document.head || document.documentElement;
        docxf.appendChild(xfbasicStyle);
    }
    addCss();
    crturl = window.location.href;
    console.log("all done");


})();