PageSourceSearch

https://www.boce.com/statics/web_v5/js/index_v5.js?v=20261002141502293

js boce.com collected 2026-10-02 06:18:40 UTC 174,025 bytes, 4,913 lines download raw bytes

1var batchSearch = $(".banner_key_input textarea").length == 1 ? true : false;
2var batchSize = 50;  //批量查询最大数量
3var wsCode = 0;
4var result = [];
5var resultArr = [];
6var initData = [];
7var initMoreData = [];
8var domainArr = [];
9var web_history_obj = [];
10var checkOneAjax = null;
11var map_data = [] // 暂时存储地图数据
12var hand_close_ws = false; // 手动点击停止检测
13var ws = null;
14var wsObj = [] // 节点ws对象
15var wsCheck = {
16    timeout: 300000,
17    timeoutObj: [],
18    reset: function (target) {
19        clearTimeout(this.timeoutObj[target]);
20    },
21    start: function (target) {
22        this.timeoutObj[target] = setTimeout(function () {
23            wsObj[target].close();
24            console.log('超时了...');
25        }, this.timeout)
26    }
27}
28const nodeName = {
29    0: "全部",
30    1: "电信",
31    2: "移动",
32    3: "联通",
33    //10: "海外",
34    //11: "港澳台",
35    hua_dong: "华东",
36    hua_bei: "华北",
37    hua_nan: "华南",
38    hua_zhong: "华中",
39    xi_bei: "西北",
40    xi_nan: "西南",
41    dong_bei: "东北",
42    //hai_wai:"海外",
43}
44const areaNodeName = {
45    0: "全部",
46    asia: "亚洲",
47    north_america: "北美洲",
48    south_america: "南美洲",
49    europe: "欧洲",
50    africa: "非洲",
51    oceania: "大洋洲",
52}
53var nodesLen = 0; //全部节点数量
54var getResultLen = 0; //已响应节点数量
55var linkOnCount = 0 //当前重连次数
56var linkCount = 2 // 最大重连次数
57var againRwaId = [] // 更新时原来的time_id
58var tool_text = {
59    "http": "网站测速",
60    "http_overseas": "海外测速",
61    "ping_overseas": "海外PING测速",
62    "dns_overseas": "海外DNS测速",
63    "ping": "PING测速",
64    "tcping": "Tcping测速",
65    "udping": "Udping检测",
66    "dns": "DNS测速",
67    "hijack": "劫持检测",
68    "wall": "拦截检测",
69    "pollute": "污染检测",
70    "ssl": "SSL检测",
71    "ipv6": "IPv6检测",
72    "icp": "ICP备案查询",
73    "whois": "Whois查询",
74    "traceroute": "路由查询",
75    "qq": "QQ拦截",
76    "wechat": "微信拦截",
77    "blacklist": "域名黑名单",
78    "web_history": "建站历史",
79    "ip_state": "IP段连通性",
80    "ip_city": "IP地址查询",
81    "ssl_conversion": "SL证书格式转化工具",
82    "ssl_chain": "SSL证书链下载",
83    "complex": "网站综合查询",
84}
85var isp_color = {
86    "1": "#9ccc65",
87    "2": "#00acc1",
88    "3": "#ffba57",
89    "10": "#d181ff",
90    "11": "#ff5252",
91}
92var overseas_color = {
93    "asia": "#9ccc65",
94    "africa": "#00acc1",
95    "north_america": "#ffba57",
96    "south_america": "#d181ff",
97    "europe": "#ff5252",
98    "oceania": "#6492F5",
99}
100var time = 2000;
101var sponsor = `<a href="https://www.51dns.com/" target="_blank" class="c-primary tooltip" rel="noopener nofollow" data-tooltip-arrow="left" data-tooltip="帝恩思">帝恩思</a>`;
102var trMorelen = {
103    http: 7,
104    http_overseas: 7,
105    ping: 6,
106    ping_overseas: 6,
107    dns_overseas: 6,
108    tcping: 6,
109    udping: 6,
110    dns: 6,
111    ip_state: 4,
112    blacklist: 3,
113    qq: 3,
114    wechat: 3,
115    icp: 5,
116    ipv6: 6,
117    pollute: 4,
118    ssl: 4,
119    wall: 3,
120    whois: 7,
121    web_history: 8,
122    ip_whois: 8,
123    ip_city: 7,
124    hijack: 5,
125};
126var trlen = {
127    http: 9,
128    http_overseas: 9,
129    ping_overseas: 9,
130    dns_overseas: 6,
131    ping: 9,
132    tcping: 9,
133    udping: 8,
134    dns: 6,
135    hijack: 6,
136    ip_state: 5,
137    pingContinue: 10,
138};
139var timeIntval, HTTP_map, setAdAjax;
140var httpCodeTips = {
141    0: '失败',
142    200: `<b>成功</b> - 服务器成功返回网页`,
143    301: '<b>永久移动</b> - 表示资源已永久移动,浏览器会自动跳转到新URL。',
144    302: '<b>临时移动</b> - 资源临时移动,浏览器会暂时转向新URL,原URL可能还会使用。',
145    304: '<b>临时移动</b> - 资源未改变,浏览器可以使用缓存版本,节省带宽。',
146    400: '<b>错误请求</b> - 客户端发送的请求有语法问题,服务器无法处理。',
147    401: '<b>未授权</b> - 需要用户认证才能访问资源,缺少认证信息则拒绝访问。',
148    403: '<b>禁止</b> - 服务器理解请求,但拒绝执行,可能是权限不足。',
149    404: '<b>未找到</b> - 请求的资源在服务器上不存在,无法找到。',
150    500: '<b>服务器内部错误</b>
150 - 服务器内部出现错误,无法完成请求,开发人员要检查代码。',
151    501: '<b>尚未实施</b> - 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。',
152    502: '<b>错误网关</b> - 作为网关或代理的服务器收到无效响应,不能处理请求。',
153    503: '<b>服务不可用</b> - 服务器暂时无法提供服务,可能是维护或过载。',
154    504: '<b>网关超时</b> - 网关或代理服务器在规定时间内没收到上游响应。',
155    505: '<b>HTTP 版本不受支持</b> - 服务器不支持请求中所用的 HTTP 协议版本。',
156};
157
158let commonTools = [
159    {
160    value: 'http',
161    name: '网站测速',
162    count: 0 ,
163},{
164    value: 'wall',
165    name: '拦截检测',
166    count: 0 ,
167},{
168    value: 'hijack',
169    name: '劫持检测',
170    count: 0 ,
171},{
172    value: 'pollute',
173    name: '污染检测',
174    count: 0 ,
175},{
176    value: 'blacklist',
177    name: '备案黑名单',
178    count: 0 ,
179},{
180    value: 'qq',
181    name: 'QQ拦截',
182    count: 0 ,
183},{
184    value: 'complex',
185    name: '网站综合查询',
186    count: 0 ,
187},{
188    value: 'speed',
189    name: '宽带测速',
190    count: 0 ,
191},{
192    value: 'wechat',
193    name: '微信拦截',
194    count: 0 ,
195},{
196    value: 'http_overseas',
197    name: '海外测速',
198    count: 0 ,
199},{
200    value: 'ping_overseas',
201    name: '海外PING测速',
202    count: 0 ,
203},{
204    value: 'dns_overseas',
205    name: '海外DNS测速',
206    count: 0 ,
207},{
208    value: 'ipv6',
209    name: 'IPv6网站检测',
210    count: 0 ,
211},{
212    value: 'icp',
213    name: 'ICP备案查询',
214    count: 0 ,
215},{
216    value: 'whois',
217    name: 'Whois查询',
218    count: 0 ,
219},{
220    value: 'web_history',
221    name: '建站历史',
222    count: 0 ,
223},{
224    value: 'traceroute',
225    name: '路由查询',
226    count: 0 ,
227},{
228    value: 'dns',
229    name: 'DNS测速',
230    count: 0 ,
231},{
232    value: 'ip',
233    name: 'IP查询',
234    count: 0 ,
235},{
236    value: 'ip_state',
237    name: 'IP状态检测',
238    count: 0 ,
239},{
240    value: 'ip_city',
241    name: 'IP地址查询',
242    count: 0 ,
243},{
244    value: 'ssl',
245    name: 'SSL检测',
246    count: 0 ,
247},{
248    value: 'ssl_conversion',
249    name: 'SSL证书格式转换',
250    count: 0 ,
251},{
252    value: 'ssl_chain',
253    name: 'SSL证书链下载/修复',
254    count: 0 ,
255},{
256    value: 'ip_whois',
257    name: 'IP Whois查询',
258    count: 0 ,
259},{
260    value: 'ping',
261    name: 'PING测速',
262    count: 0 ,
263},{
264    value: 'tcping',
265    name: 'Tcping测速',
266    count: 0 ,
267},{
268    value: 'ping_continue',
269    name: 'PING持续测速',
270    count: 0 ,
271},{
272    value: 'tcping_continue',
273    name: 'TCPing持续测速',
274    count: 0 ,
275},{
276    value: 'http_batch',
277    name: '批量网站测速',
278    count: 0 ,
279},{
280    value: 'ping_batch',
281    name: '批量PING测速',
282    count: 0 ,
283},{
284    value: 'tcping_batch',
285    name: '批量TCPing测速',
286    count: 0 ,
287},{
288    value: 'dns_batch',
289    name: '批量DNS测速',
290    count: 0 ,
291},{
292    value: 'http_v6',
293    name: 'IPv6网站测速',
294    count: 0 ,
295},{
296    value: 'ping_v6',
297    name: 'IPv6 PING测速',
298    count: 0 ,
299},{
300    value: 'tcping_v6',
301    name: 'IPv6 TCPing测速',
302    count: 0 ,
303}]
304
305let recommendedTools = [
306{
307    value: 'ip_city',
308    name: 'IP地址查询',
309},
310{
311    value: 'http',
312    name: '网站测速',
313},{
314    value: 'wall',
315    name: '拦截检测',
316},{
317    value: 'pollute',
318    name: '污染检测',
319},{
320    value: 'hijack',
321    name: '劫持检测',
322},{
323    value: 'qq',
324    name: 'QQ拦截',
325},{
326    value: 'complex',
327    name: '网站综合查询',
328},{
329    value: 'speed',
330    name: '宽带测速',
331},{
332    value: 'wechat',
333    name: '微信拦截',
334},{
335    value: 'blacklist',
336    name: '备案黑名单',
337},{
338    value: 'icp',
339    name: 'ICP备案查询',
340},{
341    value: 'dns',
342    name: 'DNS测速',
343},{
344    value: 'ip_state',
345    name: 'IP状态检测',
346},{
347    value: 'ip_city',
348    name: 'IP地址查询',
349},{
350    value: 'ssl',
351    name: 'SSL检测',
352},{
353    value: 'whois',
354    name: 'Whois查询',
355}];
356
357$(function () {
358    const timeoutLightningKey = 'map_timeout_lightning_show';
359    $('.map-timeout-lightning-switch').prop('checked', localStorage.getItem(timeoutLightningKey) !== '0');
360
361    $(document).on('change', '.map-timeout-lightning-switch', function () {
362        const timeoutLightningKey = 'map_timeout_lightning_show';
363        localStorage.setItem(timeoutLightningKey, $(this).is(':checked') ? '1' : '0');
364        chartMap(search_type, map_data);
365    });
366
367    $(document).on('click', '.http_report', function () {
368        let url = location.href;
369        if (!isLogin(url)) {
370            return false;
371        }
372        $.post("/http/report/remain", {
373            _token
374        }, function (res) {
375            if(res.code == 0){
376                if (res.data.is_vip) {
377                    get_report_generate()
378                } else {
379                    Popup.failure(`您当前剩余${res.data.left_count}次智能诊断,开通VIP可享受不限次智能诊断`, "提示", function () {
380                        get_report_generate()
381                    },'', function () {
382                        $(".vip_yes").click()
383                    },'','', '开通VIP')
384                }
385            }
386        })
387    });
388
389});
390
391function get_report_generate() {
392    $.post("/http/report/generate", {
393        check_type: 'http',
394        time_id: $(".banner_submit").attr("data-timeid") ,
395        _token
396    }, function (res) {
397        if (res.code==0) {
398            Popup.success("报告生成中,请稍后在检测历史中查看(预计1分钟)")
399            $('.http_report_box').html(`智能故障诊断 <a class="btn btn-mini ml-8" href="${res.data.report_path}" target="_blank">查看报告</a>`)
400        } else {
401            Popup.error(res.message)
402        }
403
404    }).fail(function() {
405
406    })
407}
408
409
410// 更新页面显示
411processCommonTools();
412
413// 处理工具使用统计和显示
414function processCommonTools() {
415    let storedTools = localStorage.getItem("commonTools");
416    let finalTools = [];
417
418    if (storedTools) {
419        // 如果localStorage中有数据
420        let parsedTools = JSON.parse(storedTools);
421
422        // 创建映射以便快速查找
423        let toolMap = {};
424        parsedTools.forEach(tool => {
425            toolMap[tool.value] = tool;
426        });
427
428        // 合并默认工具和存储的工具,确保所有工具都有默认值
429        commonTools.forEach(defaultTool => {
430            if (toolMap[defaultTool.value]) {
431                // 如果存储中有该工具,使用存储的值
432                finalTools.push(toolMap[defaultTool.value]);
433            } else {
434                // 如果存储中没有该工具,使用默认值
435                finalTools.push(defaultTool);
436            }
437        });
438
439        // 如果存储中有额外的工具,也添加进来
440        parsedTools.forEach(storedTool => {
441            if (!commonTools.some(tool => tool.value === storedTool.value)) {
442                finalTools.push(storedTool);
443            }
444        });
445
446        // 按使用次数排序并取前6个
447        finalTools.sort((a, b) => b.count - a.count);
448        finalTools = finalTools.slice(0, 6);
449    } else {
450        // 如果localStorage中没有数据,使用默认的前6个工具
451        finalTools = commonTools.slice(0, 6);
452    }
453
454    let htm1 = '';
455    let htm2 = '';
456    let tools = [];
457    finalTools.forEach(item => {
458        htm1 += `<span><a href="/${item.value}" title="${item.name}">${item.name}</a></span>`;
459        tools.push(item.value);
460    });
461    $(".index-goods-diy1").html(htm1);
462
463    let i=0
464    recommendedTools.forEach(item => {
465        if(!tools.includes(item.value) && i<6){
466            htm2 += `<span><a href="/${item.value}" title="${item.name}">${item.name}</a></span>`;
467            i++;
468        }
469    });
470    $(".index-goods-diy2").html(htm2);
471
472}
473
474// 更新工具使用次数的函数
475function updateToolCount(obj) {
476    let storedTools = localStorage.getItem("commonTools");
477    let tools = storedTools ? JSON.parse(storedTools) : [];
478
479    // 查找工具是否已存在
480    let toolExists = false;
481    let type=obj.type
482    if (obj.batch && ['http','ping','tcping','dns'].includes(obj.type)){
483        type=obj.type+'_batch'
484    }
485    if (obj.continue && ['ping','tcping'].includes(obj.type)){
486        type=obj.type+'_continue'
487    }
488    if (obj.v6 && ['http','ping','tcping'].includes(obj.type)){
489        type=obj.type+'_v6'
490    }
491    for (let i = 0; i < tools.length; i++) {
492        if (tools[i].value === type) {
493            tools[i].count = (tools[i].count || 0) + 1;
494            toolExists = true;
495            break;
496        }
497    }
498
499    // 如果工具不存在,从默认工具中查找并添加
500    if (!toolExists) {
501        const defaultTool = commonTools.find(tool => tool.value === type);
502        if (defaultTool) {
503            tools.push({
504                value: defaultTool.value,
505                name: defaultTool.name,
506                count: 1
507            });
508        }
509    }
510
511    localStorage.setItem("commonTools", JSON.stringify(tools));
512}
513
514
515$(document).on('mouseover', '.httpCode', function () {
516    if ($(this).attr('data-code') > 0) {
517        const $this = $(this);
518        if (!$this.find('.httpCodeTips').length) {
519            $(this).html(`${$(this).attr('data-code')}<div class="httpCodeTips">${httpCodeTips[$(this).attr('data-code')]}(<a href="https://www.boce.com/help/5404.html" target="_blank" class="color-blue">查看状态码含义</a>)</div>`)
520        }
521    }
522})
523
524$(document).on("click", ".pointPopup .PopupClose", function () {
525    testCloseLoading()
526    configGetFun()
527})
528
529$(document).on('click', '.exploit', function () {
530    Popup.warning("工具正在开发中,敬请期待!")
531})
532
533//表格多选(全选)
534$(document).on('click', '.checkbox-all', function () {
535    if ($(this).prop('checked')) {
536        $('.checkbox-g').prop('checked', true);
537    } else {
538        $('.checkbox-g').prop('checked', false);
539    }
540    if ($(".checkedDomain").length > 0) {
541        let obj = $(".node-data-tbody input");
542        let hostArr = [];
543        obj.each(function (index, el) {
544            if (obj.eq(index).prop('checked')) {
545                hostArr.push(obj.eq(index).parent('td').next().text());
546            }
547        });
548        $(".checkedDomain").html(hostArr.join('<br>'));
549    }
550});
551
552//表格多选(单选)
553$(document).on('click', '.checkbox-g', function () {
554    let len = $('.node-data-tbody .checkbox-g').length;
555    let checkedLen = $('.node-data-tbody .checkbox-g:checked').length;
556    if (len == checkedLen) {
557        $('.checkbox-all').prop('checked', true);
558    } else {
559        $('.checkbox-all').prop('checked', false);
560    }
561
562    if ($(".checkedDomain").length > 0) {
563        //选中就host加入hostArr数组,取消就删除hostArr数组中的host
564        let host = $(this).parents('tr').find('td').eq(1).text();
565        let hostArr = [];
566        if ($(".checkedDomain").html() != '') {
567            hostArr = $(".checkedDomain").html().split('<br>');
568        }
569        if ($(this).prop("checked")) {
570            hostArr.push(host);
571        } else {
572            hostArr.splice($.inArray(host, hostArr), 1);
573        }
574        $(".checkedDomain").html(hostArr.join('<br>'));
575    }
576
577});
578
579//跳转详情页
580$(document).on('click', '.detail-row', function () {
581    let task_id = $(this).parents("tr").find(".checkbox-g").val()
582    let href = `${shareUrlPrefix}/${task_id}.html`
583    const linkEL = document.createElement("a")
584    linkEL.setAttribute("target", "_blank")
585    linkEL.setAttribute("href", href)
586    linkEL.click()
587});
588
589$(document).on('click', '.banner-node-btn', function () {
590    $('.banner-node-dialog').show()
591});
592
593
594$(document).on('click', '.error-popup-tabs span', function () {
595    let index = $(this).index();
596    $(this).addClass('on').siblings().removeClass('on');
597    $('.error-popup-cont').hide().eq(index).show();
598});
599
600$(document).on('click', '.duration-card', function () {
601    $(this).addClass('on').siblings().removeClass('on');
602});
603
604$(".duration-vip-pay").click(function () {
605    let duration = $(this).prev().find(".duration-card.on").attr("data-duration");
606    let url = `/user/#/order/vip?duration=${duration}`;
607    if (!isLogin(url)) {
608        return false;
609    }
610    iframePopupOpen({
611        url,
612    });
613})
614
615function isLogin(url, target = true) {
616    let userInfo = localStorage.getItem("boceAdmin_userInfo");
617    if (userInfo) {
618        return true;
619    } else {
620        if (url) {
621            if (target) {
622                iframePopupOpen({
623                    url: `/user/#/login?link=1`,
624                    path: url
625                });
626            } else {
627                iframePopupOpen({
628                    url: `/user/#/login`,
629                    path: url
630                });
631            }
632        } else {
633            iframePopupOpen({
634                url: `/user/#/login`,
635            });
636        }
637        return false;
638    }
639}
640
641/* banner 修改选中节点 */
642$(document).on('click', '.banner-node-list span', function () {
643    let num = 1;
644    if (batchSearch) {
645        num = 10
646    }
647    if (search_type == 'traceroute' || $("banner-form-box dd a.on").attr('data-type') == 'traceroute') {
648        $(".banner-node-list span").removeClass('on')
649        $(".banner-node-select").html("");
650    }
651    $(this).toggleClass('on');
652    /*if ($(".banner-node-list span.on").length > num) {
653        Popup.error(`最多选择${num}个检测节点`)
654        $(this).removeClass('on')
655        return;
656    }*/
657    if ($(".banner-node-list span.on").length == 0) {
658        $(".banner-node-select").html("<div class='color-999'>请选择左侧节点</div>");
659    } else if ($(".banner-node-select span").length == 0) {
660        $(".banner-node-select").html("");
661    }
662    if ($(this).hasClass('on')) {
663        $(".banner-node-select").append(`<span>
664                                            ${$(this).text()}
665                                            <i class="iconfont iconX" data-id="${$(this).attr('data-id')}"></i>
666                                        </span>`);
667    } else {
668        $(".banner-node-select span i[data-id='" + $(this).attr('data-id') + "']").parent().remove();
669    }
670    $(".banner-node-btn span").text($(".banner-node-list span.on").length);
671});
672
673/* 删除选中节点 */
674$('html').on("click", ".banner-node-select span i", function () {
675    let id = $(this).attr('data-id');
676    $(".banner-node-list span[data-id='" + id + "']").removeClass('on');
677    $(this).parents('span').remove();
678    $(".banner-node-btn span").text($(".banner-node-list span.on").length);
679    if ($(".banner-node-list span.on").length == 0) {
680        $(".banner-node-select").html("<div class='color-999'>请选择左侧节点</div>");
681    }
682});
683
684/* 清空选中的节点 */
685$('html').on("click", ".banner-node-clear", function () {
686    $(".banner-node-list span").removeClass('on');
687    $(".banner-node-select").html("<div class='color-999'>请选择左侧节点</div>");
688    $(".banner-node-btn span").text(0);
689});
690
691/* 清空选中的节点 */
692$('html').on("click", ".banner-node-close", function () {
693    $(".banner-node-dialog").hide();
694});
695
696
697
698/* 搜索节点 */
699$('html').on("input", ".banner-node-input input", function () {
700    search_node()
701});
702
703/* 搜索节点 */
704$('html').on("click", ".banner-node-input i", function () {
705    search_node()
706});
707
708/*banner 显示高级选项*/
709$(document).on('click', '.banner_more', function () {
710    $(this).parents(".banner").toggleClass('on')
711})
712
713$(document).on('click', '.banner_isp_all', function () {
714    if ($(this).prop('checked')) {
715        $('.banner_isp').prop('checked', true);
716    } else {
717        $('.banner_isp').prop('checked', false);
718    }
719    //过滤节点
720    if ($(this).parents(".banner-node-filter").length){
721        search_node()
722    }
723})
724
725$(document).on('click', '.banner_isp', function () {
726    let num = $('.banner_isp:checked').length
727    if (num == $('.banner_isp').length) {
728        $('.banner_isp_all').prop('checked', true);
729    } else {
730        $('.banner_isp_all').prop('checked', false);
731    }
732
733    //过滤节点
734    if ($(this).parents(".banner-node-filter").length){
735        search_node()
736    }
737})
738
739$(document).on('click', '.banner_node_type', function () {
740    $(this).parents(".banner_isp_box").find(".banner_node_type").prop("checked", false)
741    $(this).prop("checked", true)
742    //过滤节点
743    if ($(this).parents(".banner-node-filter").length){
744        search_node()
745    }
746})
747
748$(document).on('click', '.allSelect', function () {
749    //根据当前的选中状态,控制所有显示的节点选中或取消选中
750    const isChecked = $(this).prop("checked");
751    const visibleSpans = $(".banner-node-list span:visible");
752
753    visibleSpans.each(function() {
754        const $span = $(this);
755        // 如果全选且当前未选中,则点击选中
756        if (isChecked && !$span.hasClass('on')) {
757            $span.click();
758        }
759        // 如果取消全选且当前已选中,则点击取消选中
760        else if (!isChecked && $span.hasClass('on')) {
761            $span.click();
762        }
763    });
764})
765
766
767function search_node() {
768    let isp = getIsp(".banner-node-filter");
769    let node_type = $(".banner_node_type:checked").val();
770    let nodeName = $(".banner-node-input input").val().trim()
771
772    $('.banner-node-list span').show()
773    $.each($('.banner-node-list span'), function (i) {
774        let _this = $('.banner-node-list span').eq(i)
775        if (isp.length > 0 && !isp.includes(_this.attr('data-isp'))) {
776            _this.hide()
777        }
778        if (node_type && node_type !== "") {
779            if ((node_type == "1" && _this.attr('data-idc')=='false') || (node_type == "2" && _this.attr('data-router')=='false')) {
780                _this.hide()
781                if(_this.hasClass('on')){
782                    _this.click()
783                }
784            }
785        }
786        if ( nodeName && _this.text().indexOf(nodeName) == -1) {
787            _this.hide()
788        }
789
790    })
791
792    if($(".banner-node-list span:visible").length == 0){
793        $(".banner-node-cont").addClass('noSearchData')
794    }else{
795        $(".banner-node-cont").removeClass('noSearchData')
796    }
797
798}
799
800/* 重新分配节点  */
801$('html').on("click", ".banner-node-rand", function () {
802    bannerNodeRand()
803});
804
805function bannerNodeRand() {
806    let node_type = $(".banner_node_type:checked").val();
807    $(".banner-node-list span").removeClass('on');
808    $(".banner-node-select").html("");
809    $('.banner-node-cont').addClass('noSearchData');
810
811    let targets = ["电信", "移动", "联通"];
812    if (search_type == 'traceroute' || location.pathname == '/') {
813        targets = ["电信"];
814    }
815
816    // 1. 按运营商分组存储节点
817    const nodesByIsp = {};
818    targets.forEach(isp => {
819        nodesByIsp[isp] = [];
820    });
821
822    // 2. 筛选出目标运营商的所有节点
823    $.each(allNodes, function(index, item) {
824        const name = item.isp_name;
825        if (targets.includes(name) && ( (node_type == "1" && item.has_idc) || (node_type == "2" && item.has_router) )) {
826            nodesByIsp[name].push(item);
827        }
828    });
829
830    // 3. 每个运营商随机选一个节点并模拟点击
831    $.each(nodesByIsp, function(isp, nodes) {
832        if (nodes.length > 0) {
833            // 生成随机索引(0到节点数量-1之间)
834            const randomIndex = Math.floor(Math.random() * nodes.length);
835            const randomNode = nodes[randomIndex];
836            // 模拟点击随机节点
837            $(`.banner-node-list span[data-id="${randomNode.id}"]`).click();
838            $(".banner-node-cont").removeClass('noSearchData');
839        }
840    });
841}
842
843//按下去就执行空白处隐藏推荐的下拉框
844$(document).on('mousedown', function (event) {
845    let targetElement = event.target;
846    if ($(targetElement).parents('.banner-dns').length == 0) {
847        $('.banner-dns-list').hide();
848    }
849    if ($(targetElement).parents('.dropdown-cont').length == 0) {
850        $('.dropdown-menu').hide();
851    }
852    if ($(targetElement).parents('.orderBy').length == 0) {
853        $('.orderBySelect').hide();
854    }
855    if ($(targetElement).parents('.banner_key_input').length == 0) {
856        $('.banner_storage').hide();
857    }
858    if ($(targetElement).parents('.banner-node').length == 0) {
859        $('.banner-node-dialog').hide();
860    }
861});
862
863$(document).on('click', '.spanMoney,.footerVipTxt', function () {
864    event.preventDefault();
865    if ($(".footerUserTxt").css('display') == 'none' && $(".spanMoney").attr('close') != 1) {
866        if (setAdAjax) {
867            setAdAjax.abort()
868        }
869        setAdAjax = $.ajax({
870            "url": "/apiHtml/member/config/set",
871            "type": "post",
872            "data": JSON.stringify({
873                "type": "ad",
874                "content": true
875            }),
876            "contentType": "application/json",
877            "dataType": "json",
878            "success": function (res) {
879                if (res.code == 0) {
880                    $('.money_box_m,.money_box,.money_left,.money_right,.money_login_cont,.money_vip_popup,.menuList1b').hide();
881                    $(".spanMoney").attr('close', 1)
882                } else {
883                    Popup.error(res.message)
884                }
885            }
886        })
887
888    } else {
889        $('.money_box_m,.money_box,.money_left,.money_right,.money_login_cont,.money_vip_popup,.menuList1b').hide();
890
891        var expireTime = new Date();
892        expireTime.setTime(expireTime.getTime() + (60 * 60 * 1000)); // 当前时间 + 60秒
893        $.cookie('hide_ad_cooldown', '1', { expires: expireTime, path: '/' });
894    }
895    footerPosition();   //控制页脚位置
896    return false;
897})
898
899
900$(document).on('click', '.banner-form-box dd a', function () {
901    if (location.pathname != '/') {
902        return true
903    }
904    let type = $(this).attr('data-type')
905    $(this).addClass('on').siblings().removeClass('on');
906
907    if (['http', 'ping', 'dns', 'hijack'].includes(type)) {
908        $(".banner_batch").show().attr("href", `${versionUrlPrefix}/${type}_batch`);
909    } else if (['pollute', 'wall', 'icp', 'ip_city'].includes(type)) {
910        $(".banner_batch").show().attr("href", `${versionUrlPrefix}/${type}`);
911    } else {
912        $(".banner_batch").hide();
913    }
914    $(".banner_continue").hide()
915    if (['http', 'http_overseas'].includes(type)) {
916        $('.banner_value').attr('placeholder', '请输入有效的URL地址,例如:boce.com')
917    } else if (['ping','ping_overseas'].includes(type)) {
918        $('.banner_value').attr('placeholder', '请输入有效的域名或IP,例如:boce.com')
919        $(".banner_continue").show().attr("href", `${versionUrlPrefix}/${type}_continue`);
920    } else if (['ip_city'].includes(type)) {
921        $('.banner_value').attr('placeholder', '请输入有效的IP地址,例如:1.1.1.1')
922    } else if (['dns','dns_overseas', 'hijack', 'wall', 'pollute', 'ssl', 'ipv6', 'icp', 'whois', 'hijack', 'qq', 'wechat', 'blacklist'].includes(type)) {
923        $('.banner_value').attr('placeholder', '请输入有效的域名,例如:boce.com')
924    }
925    if (type == 'traceroute') {
926        $(".banner-node").show();
927    } else {
928        $(".banner-node").hide();
929    }
930    if (type == 'http') {
931        $(".banner_more").show();
932    } else {
933        $(".banner_more").hide();
934        $(".banner").removeClass('on');
935    }
936
937    if (['http', 'ping', 'dns'].includes(type)) {
938        $(".banner_v6").show().attr("href", `${versionUrlPrefix}/${type}_v6`);
939    } else {
940        $(".banner_v6").hide();
941    }
942
943    $(".banner-param .banner_isp_box,.banner_dns_box,.banner_record_box,.banner_continent_box,.indexGoTask").hide();
944    if (['http', 'ping', 'tcping', 'dns', 'wall', 'pollute', 'ssl', 'hijack'].includes(type)) {
945        $(".indexGoTask").attr('href', `/user/#/task/${type}`);
946        $(".banner-param .indexGoTask").show();
947    }
948
949    if (type == 'http') {
950        $(".banner-param .banner_isp_box").show();
951    } else if (type == 'http_overseas') {
952        $(".banner_continent_box").show();
953    } else if (type == 'ping_overseas') {
954        $(".banner_continent_box").show();
955    } else if (type == 'dns_overseas') {
956        $(".banner_continent_box").show();
957        $(".banner_dns_box").show();
958        $(".banner_record_box").show();
959    } else if (type == 'ping') {
960        $(".banner-param .banner_isp_box").show();
961    } else if (type == 'dns') {
962        $(".banner-param .banner_isp_box").show();
963        $(".banner_dns_box").show();
964        $(".banner_record_box").show();
965    }
966
967})
968
969$(document).on('click', '.action_submit', function () {
970
971    // 立即移除action_submit防止重复点击
972    $('.banner_submit').removeClass('action_submit');
973
974    let type = ''
975    let random = $(".banner_submit").attr('data-random')
976    if (random && wsObj[random] && wsObj[random]['readyState'] != 3) {
977        wsObj[random].close();
978    }
979
980    if (location.pathname == '/') {
981        type = $(".banner-form-box dd a.on").attr('data-type')
982    } else {
983        type = search_type
984    }
985    let params = getParame(type)
986    if ($(this).attr('data-type') == 'batch') {
987        params['batch'] = true
988    }
989    if ($(this).attr('data-type') == 'continue') {
990        params['continue'] = true
991    }
992    if ($(this).attr('data-refresh')) {
993        params['refresh'] = true
994    }
995    if ($(this).attr('data-target')) {
996        params['target'] = true
997    }
998    if($(".screen-header").length > 0){     //大屏直接跑build_url接口
999        build_url(params);
1000    }else{
1001        if(params.type=='speed'){
1002            sliderCheck(params)
1003        }else{
1004            buildUrlFun(params)
1005        }
1006    }
1007
1008    //添加开始检测点击事件统计
1009    if ($('[name=old_type]').prop('checked')) {
1010        sendUstatEvent('使用旧版批量检测')
1011    }
1012    if (is_continue) {
1013        sendUstatEvent(search_type+'_continue')
1014    }else if (is_v6) {
1015        sendUstatEvent(search_type+'_v6')
1016    }else if (isBatch) {
1017        sendUstatEvent(search_type+'_batch')
1018    }else{
1019        sendUstatEvent(search_type)
1020    }
1021
1022})
1023
1024var isTouchDevice = 'ontouchend' in document || navigator.maxTouchPoints > 0;
1025// 1. 点击切换下拉菜单(移动端 & PC端点击)
1026$(document).on('click', '.select', function (e) {
1027    e.stopPropagation();
1028    $('.select').not(this).removeClass('on'); // 关闭其他下拉框
1029    $(this).toggleClass('on');
1030});
1031
1032// 2. PC 端鼠标 Hover 效果(移动端直接跳过,避免冲突)
1033if (!isTouchDevice) {
1034    $(document).on('mouseenter', '.select', function () {
1035        $(this).addClass('on');
1036    }).on('mouseleave', '.select', function () {
1037        $(this).removeClass('on');
1038    });
1039}
1040
1041// 3. 点击选项处理
1042$(document).on('click', '.select dd a', function (e) {
1043    e.stopPropagation(); // 阻止向上冒泡触发 .select 的 click
1044    if ($(this).hasClass('disabled')) {
1045        return false;
1046    }
1047
1048    $(this).parents('.select').find('dt').text($(this).text());
1049    $(this).parents('.select').removeClass('on');
1050});
1051
1052// 4. 点击页面空白处收起所有下拉菜单
1053$(document).on('click', function () {
1054    $('.select').removeClass('on');
1055});
1056
1057//提取域名
1058$(document).on('click', '.input_tool_domain', function () {
1059    if (getHost() == '') {
1060        Popup.error("输入框无数据")
1061        return false
1062    }
1063    let host = []
1064    let bool = false
1065    if (['http','http_overseas', 'ping_overseas', 'dns_overseas', 'ping', 'tcping', 'udping', 'dns', 'hijack', 'traceroute'].includes(search_type)) {
1066        if (batchSearch) {
1067            host = getBatchHost().data
1068        } else {
1069            bool = true
1070            host = [getHost()]
1071        }
1072    } else {
1073        host = getBatchHost().data
1074    }
1075    $.post(getDomainUrl, {
1076        host: host,
1077        type: $(this).attr('data-type'),
1078        _token
1079    }, function (res) {
1080        if (bool) {
1081            $("[name='host']").val(res.data[0])
1082        } else {
1083            $("[name='host']").val(res.data.join("\n"))
1084        }
1085        Popup.success("提取成功")
1086    })
1087});
1088
1089//去重
1090$(document).on('click', '.input_tool_deWeight', function () {
1091    if (getHost() == '') {
1092        Popup.error("输入框无数据")
1093        return false
1094    }
1095    let arr = getBatchHost().data
1096    let unique = Array.from(new Set(arr));
1097    $(".banner_textarea").val(unique.join("\n"))
1098    Popup.success("去重成功")
1099});
1100
1101//清空
1102$(document).on('click', '.input_tool_clear', function () {
1103    if (getHost() == '') {
1104        Popup.error("输入框无数据")
1105        return false
1106    }
1107    $('[name=host]').val("")
1108    Popup.success("已清空全部内容")
1109});
1110
1111
1112$(document).on('click', '.node_row_close', function () {
1113    $(this).parents('tr').remove();
1114})
1115
1116$(document).on('focus', '[name=host]', function () {
1117    bannerStorageHtml(() => {
1118        $(".banner_storage").show()
1119    })
1120})
1121
1122bannerStorageHtml(() => {
1123    if($(".banner_storage_list p").length > 0){
1124        let ipType = ['ip_whois','ip_state','ip_city']
1125        // 遍历所有p标签,找到第一个IP格式的data-domain并点击
1126        $(".banner_storage_list p").each(function() {
1127            let domainVal = $(this).attr("data-domain");
1128            if(ipType.includes(search_type)){
1129                if (checkIp(domainVal)) {
1130                    if($('input[name=host]').val() ==''){
1131                        $(this).find("span").click();
1132                    }
1133                    return false; // 找到后跳出循环
1134                }
1135            }else{
1136                if (!checkIp(domainVal)) {
1137                    if($('input[name=host]').val() ==''){
1138                        $(this).find("span").click();
1139                    }
1140                    return false; // 找到后跳出循环
1141                }
1142            }
1143        });
1144    }
1145})
1146
1147function bannerStorageHtml(callback) {
1148    let arr = JSON.parse(localStorage.getItem('toolKey'))
1149    if (arr && arr.length > 0) {
1150        let htm = ''
1151        arr.forEach(item => {
1152            if (item && item.includes("\n")) {
1153                let val = item.split("\n")
1154                if (batchSearch) {
1155                    htm += `<p data-domain="${item}"><span>${val[0]} 等${val.length}个目标</span><i class="iconfont iconshanchu" title="删除"></i></p>`
1156                } else {
1157                    htm += `<p data-domain="${val[0]}"><span>${val[0]} 等${val.length}个目标</span><i class="iconfont iconshanchu" title="删除"></i></p>`
1158                }
1159            } else if (item) {
1160                htm += `<p data-domain="${item}"><span>${item}</span><i class="iconfont iconshanchu" title="删除"></i></p>`
1161            }
1162        })
1163        $(".banner_storage_list").html(htm)
1164        setTimeout(() => {
1165            callback()
1166        }, 1)
1167    }
1168}
1169
1170$('.index-tool-nav span').click(function () {
1171    $(this).addClass("on").siblings().removeClass("on")
1172    $(".index-tool-row").eq($(this).index()).addClass("on").siblings().removeClass("on")
1173})
1174
1175$(".banner_storage_list").on('click', 'p span', function () {
1176    //event.stopPropagation();
1177    $("[name=host]").val($(this).parent().attr("data-domain"))
1178    $(".banner_storage").hide()
1179})
1180
1181$(".banner_storage_list").on('mousedown', 'p i', function () {
1182    event.stopPropagation();
1183    if (localStorage.getItem("toolKey")) {
1184        let storedRecords = [];
1185        storedRecords = JSON.parse(localStorage.getItem("toolKey"));
1186        let index = $(this).parent().index()
1187        storedRecords.splice(index, 1);
1188        localStorage.setItem("toolKey", JSON.stringify(storedRecords));
1189        $(this).parent().remove()
1190    }
1191})
1192
1193$(".storageClear").on('click', 'span', function () {
1194    event.stopPropagation();
1195    localStorage.removeItem('toolKey')
1196    Popup.success("清除成功")
1197    $(".banner_storage").hide()
1198})
1199
1200//存储检测的值
1201function local_storage() {
1202    let value = getBatchHost().data.join("\n")
1203    let storedRecords = [];
1204    if (localStorage.getItem("toolKey")) {
1205        storedRecords = JSON.parse(localStorage.getItem("toolKey"));
1206    }
1207    // 判断输入的值是否已存在,如果存在则删除之前的记录
1208    let index = storedRecords.indexOf(value);
1209    if (index !== -1) {
1210        storedRecords.splice(index, 1);
1211    }
1212    storedRecords.unshift(value);
1213    // 如果记录数量超过五条,移除最后一条记录
1214    if (storedRecords.length > 5) {
1215        storedRecords.pop();
1216    }
1217    localStorage.setItem("toolKey", JSON.stringify(storedRecords));
1218};
1219
1220//存储检测的节点
1221function traceroute_node_storage() {
1222    let id = $(".banner-node-list span.on").attr("data-id")
1223    localStorage.setItem("tracerouteNodeId", id);
1224};
1225
1226//存储批量检测的节点
1227function batch_node_storage() {
1228    let ids = getBatchNodeIds()
1229    localStorage.setItem("batchNodeId", JSON.stringify(ids));
1230}
1231
1232$(document).on('focus', '[name=dns_server_name]', function () {
1233    $(this).next(".banner-dns-list").show()
1234})
1235
1236$(".banner-dns-list").on('mousedown', 'p', function () {
1237    $(this).parents(".banner-dns-list").prev().val($(this).attr("ip"))
1238    $(this).parents(".banner-dns-list").hide()
1239})
1240
1241
1242//回车事件绑定
1243$(document).on('keyup', 'input[name=host]', function () {
1244    if (event.keyCode == "13") {
1245        $('.action_submit').click();
1246    }
1247})
1248
1249$(document).on('click', '.map-tab span', function () {
1250    $(this).addClass('on').siblings().removeClass('on')
1251    $(".node-error-box-" + $(this).attr("data-type")).show().siblings().hide()
1252})
1253
1254$(document).on('mouseenter', '.node-data-tbody tr', function () {
1255    let htm = ``
1256    if (is_v6) {
1257        htm = `<i class="iconfont iconbanshou"></i>
1258                <div>
1259                    <ul>
1260                        <li><a href="javascript:;" class="tool_ip" data-type="feedback"><i class="iconfont iconjiance font-14"></i> IP纠错</a></li>
1261                        <li><a href="javascript:;" class="tool_ip" data-type="http" data-v6="1"><i class="iconfont iconjiance font-14"></i> IPv6 网站测速</a></li>
1262                        <li><a href="javascript:;" class="tool_ip" data-type="ping" data-v6="1"><i class="iconfont iconjiance font-14"></i> IPv6 Ping</a></li>
1263                        <li><a href="javascript:;" class="tool_ip" data-type="tcping" data-v6="1"><i class="iconfont iconjiance font-14"></i> IPv6 TCPing</a></li>
1264                        <li><a href="javascript:;" class="tool_ip" data-type="traceroute" data-v6="1"><i class="iconfont iconjiance font-14"></i>IPv6 路由查询</a></li>
1265                    </ul>
1266                </div>`
1267    } else {
1268        htm = `<i class="iconfont iconbanshou"></i>
1269                <div>
1270                    <ul>
1271                        <li><a href="javascript:;" class="tool_ip" data-type="feedback"><i class="iconfont iconjiance font-14"></i> IP纠错</a></li>
1272                        <li><a href="javascript:;" class="tool_ip" data-type="http"><i class="iconfont iconjiance font-14"></i> 网站测速</a></li>
1273                        <li><a href="javascript:;" class="tool_ip" data-type="ping"><i class="iconfont iconjiance font-14"></i> Ping</a></li>
1274                        <li><a href="javascript:;" class="tool_ip" data-type="ping" data-continue="1"><i class="iconfont iconjiance font-14"></i> 持续Ping</a></li>
1275                        <li><a href="javascript:;" class="tool_ip" data-type="tcping"><i class="iconfont iconjiance font-14"></i> TCPing</a></li>
1276                        <li><a href="javascript:;" class="tool_ip" data-type="tcping" data-continue="1"><i class="iconfont iconjiance font-14"></i> 持续TCPing</a></li>
1277                        <li><a href="javascript:;" class="tool_ip" data-type="traceroute"><i class="iconfont iconjiance font-14"></i> 路由查询</a></li>
1278                    </ul>
1279                </div>`
1280    }
1281
1282    $(this).find(".hover-button").html(htm)
1283}).on('mouseleave', '.node-data-tbody', function () {
1284    $(this).find('.hover-button').html('')
1285});
1286
1287$(document).on('click', '.tool_ip', function () {
1288    let ip = $(this).parents('td').find('span.ip_text').text()
1289    let nodeId = $(this).parents('tr').find('.reTest').attr('data-id')
1290    let type = $(this).attr('data-type')
1291    let obj = getParame(type)
1292    obj.target = true
1293    obj.host = [ip]
1294
1295    if (type == 'feedback') {
1296        location.href = '/ip_feedback/?ip=' + ip
1297        return
1298    }
1299    if (type == 'traceroute') {
1300        obj.node_ids = [nodeId];
1301    }
1302    if ($(this).attr('data-continue')) {
1303        obj.continue = true
1304    }
1305    if ($(this).attr('data-v6')) {
1306        obj.v6 = true
1307    }
1308    buildUrlFun(obj)
1309})
1310
1311$(document).on('click', '.downloadPic', function () {
1312    if ($(this).hasClass('disabled')) {
1313        return false
1314    }
1315    if (!$(this).attr('data-done')) {
1316        Popup.warning("请完成检测后再截图保存")
1317        return false
1318    }
1319    if (['http','http_overseas', 'ping_overseas', 'dns_overseas', 'ping', 'tcping', 'udping', 'dns', 'hijack'].includes(search_type)) {
1320        $(".downPicCheck").prop("checked", false)
1321        $('.popupPic').show();
1322    } else {
1323        downloadHtm()
1324    }
1325})
1326
1327$(document).on('click', '.downPicSub', function () {
1328    downloadHtm()
1329})
1330
1331function downloadHtm() {
1332    if ($(".map_data_show span").text() == '全部展开') {
1333        $(".map_data_show").click()
1334    }
1335    Popup.success("截图生成中,请耐心等待~")
1336    $.each($('.downPicCheck'), function (i) {
1337        if ($('.downPicCheck').eq(i).prop('checked') == true) {
1338            $("." + $('.downPicCheck').eq(i).val()).addClass("on")
1339        }
1340    })
1341    $('.popupPic,.money_box,.ipv6_det01a').hide();
1342    $(".downPic").addClass("disabled").html(`<i class="iconfont iconjiazaizhong "></i> 正在生成`)
1343    setTimeout(() => {
1344        downloadPic()
1345    }, 500)
1346}
1347
1348/*banner 下载图片*/
1349function downloadPic() {
1350    let myElement = document.getElementById('tool_cont');
1351    domtoimage.toPng(myElement, {
1352        filter: function (node) {
1353            return node.className != 'money_box' && node.className != 'loading' && node.className != 'money_box pt10'
1354        }, bgcolor: '#fff'
1355    })
1356        .then(function (blob) {
1357            let watermarkText = "BOCE.COM"; // 水印文本内容
1358            let img = new Image();
1359            img.src = blob;
1360
1361            img.onload = function () {
1362                let canvas = document.createElement("canvas");
1363                canvas.width = img.width;
1364                canvas.height = img.height;
1365
1366                let ctx = canvas.getContext("2d");
1367                ctx.drawImage(img, 0, 0);
1368
1369                // 设置水印样式
1370                ctx.font = "bold 8px Arial";
1371                ctx.fillStyle = "rgba(18, 139 ,255 ,0.04)"; // 水印文本颜色及透明度
1372                //ctx.translate(canvas.width / 2, canvas.height / 2); // 平移至画布中心
1373                ctx.translate(0, -(canvas.height / 2)); // 平移至画布
1374                ctx.rotate(16 * Math.PI / 180); // 逆时针旋转 30 度
1375                ctx.scale(4, 4); // 放大
1376
1377                let watermarkWidth = ctx.measureText(watermarkText).width;
1378                let watermarkHeight = 10; // 水印文本高度
1379
1380                // 循环绘制水印
1381                for (let x = 0; x < canvas.width; x += watermarkWidth + 52) {
1382                    for (let y = 0; y < canvas.height; y += watermarkHeight + 52) {
1383                        ctx.fillText(watermarkText, x, y); // 绘制水印文本
1384                    }
1385                }
1386
1387                let watermarkedDataUrl = canvas.toDataURL("image/png");
1388                let link = document.createElement("a");
1389                link.href = watermarkedDataUrl;
1390
1391                let name = ''
1392                let hostVal = $("[name=host]").val();
1393                if (hostVal.includes("\n")) {
1394                    let arrVal = hostVal.split("\n")
1395                    name = `${arrVal[0]}等${arrVal.length}个域名${tool_text[search_type]}结果.png`
1396                } else {
1397                    if (is_continue) {
1398                        name = `${hostVal}持续${tool_text[search_type]}结果.png`
1399                    } else {
1400                        name = `${hostVal}${tool_text[search_type]}结果.png`
1401                    }
1402
1403                }
1404
1405                link.download = name;
1406                link.click();
1407
1408                window.URL.revokeObjectURL(blob);
1409                blob = null;
1410            };
1411
1412            $(".ip_text,.ip_from").removeClass("on")
1413            $(".money_box,.ipv6_det01a").show()
1414            $(".downPic").removeClass("disabled").html(`下载截图`)
1415        })
1416        .catch(function (error) {
1417            Popup.error("截图生成失败,请重试~")
1418            $(".downPic").removeClass("disabled").html(`下载截图`)
1419        });
1420}
1421
1422//DNS 切换污染地图显示
1423$(document).on("click", ".echarts_tabs span", function () {
1424    $(this).addClass("btn-primary").siblings().removeClass("btn-primary");
1425    $(".echarts_pollute").hide()
1426    if ($(this).attr("data-type") === 'dns') {
1427        $(".echarts_pollute_pre").hide()
1428        $(".map-timeout-lightning-box").show()
1429    } else {
1430        $(".echarts_pollute_pre").show()
1431        $(".map-timeout-lightning-box").hide()
1432    }
1433    /* 修改中国地图值 */
1434    let dataMapArr = [];
1435    if ($(this).attr('data-type') == 'dns') {
1436        dataMapArr = [...initData.provinceArr];
1437    } else {
1438        if ($(".user-login").is(":visible")) {
1439            $(".echarts_pollute").show()
1440        } else {
1441            dataMapArr = [...initData.pollute];
1442        }
1443    }
1444    chartMap($(this).attr('data-type'), dataMapArr);
1445})
1446
1447/*筛选IP占比运营商*/
1448$(document).on("click", ".ip-isp-tab span", function () {
1449    let isp = 0
1450    let fruitTotal = [];
1451    $(this).addClass("on").siblings().removeClass("on");
1452    if ($(this).attr("data-package") == "pollute") {
1453        initData.ipPre.forEach(val => {
1454            if (val['is_pollute']) {
1455                fruitTotal.push(val);
1456            }
1457        });
1458    } else {
1459        isp = $(this).attr('data-isp') * 1;
1460        initData.ipPre.forEach(val => {
1461            if (isp == 0 || val['isp'].indexOf(isp) != -1) {
1462                fruitTotal.push(val);
1463            }
1464        });
1465    }
1466    fruitTotal = sortByDescNum(fruitTotal, 'pre');
1467    if (fruitTotal.length == 0) {
1468        $(".ip-isp-list").html(`<div class="empty-g">暂无数据</div>`);
1469        return;
1470    }
1471    init_isp(fruitTotal);
1472
1473})
1474
1475/*筛选 建站历史 周期*/
1476$(document).on("click", ".table-tab-webhistory span", function () {
1477
1478    if ($(this).hasClass("disabled")) {
1479        return
1480    }
1481
1482    $(this).addClass("on").siblings().removeClass("on");
1483    if ($(this).parent().attr("data-type") == 'year' && $(this).data('value') == 0) {
1484        $(".table-tab-webhistory[data-type='month'] span").removeClass("on").eq(0).addClass("on");
1485    }
1486    let obj = {
1487        agregate: Number($(".table-tab-webhistory[data-type='type'] span.on").attr('data-value')),
1488        year: Number($(".table-tab-webhistory[data-type='year'] span.on").attr('data-value')),
1489        month: Number($(".table-tab-webhistory[data-type='month'] span.on").attr('data-value'))
1490    }
1491    if (obj.year > 0) {
1492        $(".table-tab-month").show()
1493    } else {
1494        $(".table-tab-month").hide()
1495    }
1496
1497    let parentType = $(this).parent().attr("data-type");
1498    if (parentType == 'year') {
1499        $(".calender-canvas li").removeClass("on");
1500        $(".calender-canvas li[data-year='" + $(this).data('value') + "']").addClass("on");
1501    }
1502
1503    getPageDate(1, obj)
1504
1505})
1506
1507/*筛选 建站历史 年份*/
1508$(".calender-canvas").on("click", "li", function () {
1509    $(this).addClass("on").siblings().removeClass("on")
1510    $(".table-tab-webhistory span[data-value='" + $(this).attr("data-year") + "']").click()
1511})
1512
1513/*M端 节点查看更多*/
1514$(document).on('click', '.node_error_more', function () {
1515    let type = $(this).attr("data-type")
1516    if ($(this).find('font').text() == "点击查看更多") {
1517        $(this).find('font').text("点击收起更多")
1518        $(this).find('i').addClass("iconrate-up")
1519        if (type == "table") {
1520            $(this).parents("tbody").find("tr:gt(3)").show()
1521        } else {
1522            $(this).parent().find("span:gt(14)").show()
1523        }
1524    } else {
1525        $(this).find('font').text("点击查看更多")
1526        $(this).find('i').removeClass("iconrate-up").addClass("iconarrow-bottom")
1527        if (type == "table") {
1528            $(this).parents("tbody").find("tr:gt(3)").hide()
1529            $(this).parents("tbody").find("tr:last-child").show()
1530        } else {
1531            $(this).parent().find("span:gt(14)").hide()
1532        }
1533    }
1534})
1535
1536/*M端 表格查看更多*/
1537$(document).on('click', '.table_tr_show', function () {
1538    if ($(".table_tr_show span").text() == "点击查看更多") {
1539        $(".table_tr_show span").text("点击收起更多")
1540        $(".table_tr_show i").addClass("iconrate-up")
1541        $(".table_cont tr").show()
1542    } else {
1543        $(".table_tr_show span").text("点击查看更多")
1544        $(".table_tr_show i").removeClass("iconrate-up").addClass("iconarrow-bottom")
1545        $('.table_cont tbody tr').filter(":gt(9)").hide();
1546    }
1547})
1548
1549/*显示 IP占比详情*/
1550$(document).on("click", ".map_data_show", function () {
1551    if ($(this).find('i').hasClass('iconsort_desc')) {
1552        $(".ip-isp-item.hide").addClass("show").removeClass("hide");
1553        $(this).find('i').removeClass('iconsort_desc').addClass('iconrate-up');
1554        $(this).find('span').text('收起全部')
1555    } else {
1556        $(".ip-isp-item.show").addClass("hide").removeClass("show");
1557        $(this).find('i').removeClass('iconrate-up').addClass('iconsort_desc');
1558        $(this).find('span').text('展开全部')
1559    }
1560})
1561
1562/*点击 过滤IP*/
1563$(document).on("click", ".ip-isp-item", function () {
1564    $(".table-tab span").removeClass("on");
1565    $(".table-params-add").remove();
1566    $(".table-params-item").append(`<span class="table-params-add on" data-isp="104">${$(this).find('span').text()} <font class="iconfont iconX font-12 ml-4"></font></span>`);
1567    tableParamsData();
1568})
1569
1570/*删除详情筛选*/
1571$(document).on("click", ".table-params-add font", function () {
1572    $(".table-tab span").removeClass("on");
1573    $(".table-tab-all span").addClass("on");
1574    $(this).parent().remove();
1575    tableParamsData();
1576})
1577
1578/*筛选表格数据*/
1579$(document).on("click", ".table-tab span", function () {
1580    $(".table-params-add").remove()
1581    let isp = $(this).attr('data-isp') * 1;
1582    if (isp == 0) {
1583        $(".table-tab a,.table-tab span").removeClass("on");
1584        $(this).addClass("on");
1585        for (let i = 0; i < $(".table-params .dropdown-cont").length; i++) {
1586            let item = $(".table-params .dropdown-cont").eq(i)
1587            item.find('.dropdown-text').find('label').text(item.find('.dropdown-item').eq(0).text());
1588            item.find('.dropdown-item').removeClass('on').eq(0).addClass('on');
1589        }
1590    } else if (isp > 0) {
1591        $(".table-tab-all").removeClass("on");
1592        $(this).siblings().removeClass("on");
1593        if ($(this).hasClass("on")) {
1594            $(this).removeClass("on")
1595        } else {
1596            $(this).addClass("on")
1597        }
1598    }
1599
1600    tableParamsData();
1601
1602})
1603
1604/*筛选持续测速表格数据*/
1605$(document).on("click", ".table-tab-continue span", function () {
1606    $(this).addClass("on").siblings().removeClass("on");
1607    let val = $(this).attr('data-val') * 1;
1608    if (val == 0) {
1609        $(".node-data-tbody tr").show();
1610    } else {
1611        $(".node-data-tbody tr").hide();
1612        $(".node-data-tbody tr").each(function (i) {
1613            let text = $(".node-data-tbody tr").eq(i).find('.loss_value').eq(0).text();
1614            if (val == 10 && text > 0 && text <= 10) {
1615                $(".node-data-tbody tr").eq(i).show();
1616            }
1617            if (val == 11 && text > 10 && text <= 50) {
1618                $(".node-data-tbody tr").eq(i).show();
1619            }
1620            if (val == 50 && text > 50) {
1621                $(".node-data-tbody tr").eq(i).show();
1622            }
1623            if (val == 101 && text == 0) {
1624                $(".node-data-tbody tr").eq(i).show();
1625            }
1626        })
1627    }
1628})
1629
1630/*劫持检测表格数据*/
1631$(document).on("click", ".table-tab-hijack span", function () {
1632    if ($(".table-tab-hijack").hasClass("disabled")) {
1633        return false
1634    }
1635    $(this).addClass("on").siblings().removeClass("on");
1636    let val = $(this).attr('data-val') * 1;
1637    $(".node-data-tbody tr").hide();
1638    if (val == 0) {
1639        $(".node-data-tbody tr").show();
1640    } else {
1641        if (val == 1) {
1642            $(".table_cont tr .type_error").parents("tr").show()
1643        } else if (val == 2) {
1644            $(".table_cont tr .type_true").parents("tr").show()
1645        } else if (val == 3) {
1646            $(".table_cont tr .type_null").parents("tr").show()
1647        } else if (val == 4) {
1648            $(".table_cont tr .type_301").parents("tr").show()
1649        }
1650    }
1651})
1652
1653$(".table-params .dropdown-item").click(function () {
1654    $(".table-tab-all").removeClass("on");
1655    setTimeout(() => {
1656        tableParamsData();
1657    }, 10);
1658});
1659
1660function getTimeFormat(timeStr) {
1661    const year = timeStr.substr(0, 4);
1662    const month = timeStr.substr(4, 2);
1663    const day = timeStr.substr(6, 2);
1664    const hour = timeStr.substr(8, 2);
1665    const minute = timeStr.substr(10, 2);
1666    const second = timeStr.substr(12, 2);
1667
1668    return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
1669}
1670
1671function tableParamsData() {
1672
1673    let data = getTableParamsData();
1674
1675    $('.query_export span').text(data.length);
1676
1677    $('.table_cont tbody').html('');
1678
1679    init_result(data, false);
1680
1681}
1682
1683/*获取表格筛选之后的数据*/
1684function getTableParamsData() {
1685
1686    let errCode = $(".table-tab span.on").attr("data-isp");
1687    let area = $(".table-select-area .dropdown-item.on").attr("data-value");
1688    let tableParams = {
1689        errCode: errCode ? parseInt(errCode) : undefined,
1690        area: area == '0' ? 0 : area,
1691        province: parseInt($(".table-select-province .dropdown-item.on").attr("data-value")),
1692        isp: parseInt($(".table-select-isp .dropdown-item.on").attr("data-value"))
1693    };
1694
1695    let filteredResult = result.filter(item => {
1696        if (tableParams.errCode == 0) {
1697            return true;
1698        } else {
1699            if (tableParams.area && item.area !== tableParams.area) {
1700                return false;
1701            }
1702            if (tableParams.province && item.province !== tableParams.province) {
1703                return false;
1704            }
1705            if (tableParams.isp && item.isp !== tableParams.isp) {
1706                return false;
1707            }
1708
1709            if (tableParams.errCode == 100) {   //未响应
1710                if (item.ip != "" && item.query_time != "*" && item.httpCode != 0) {
1711                    return false;
1712                }
1713            } else if (tableParams.errCode == 101) { //被污染节点
1714                let pollute_ip = item.is_pollute_ip;
1715                if (typeof pollute_ip == 'string') {
1716                    pollute_ip = JSON.parse(item.is_pollute_ip);
1717                }
1718                let t = false;
1719                Object.keys(pollute_ip).forEach(key => {
1720                    if (pollute_ip[key]) {
1721                        t = true;
1722                    }
1723                })
1724                return t;
1725
1726            } else if (tableParams.errCode == 102) { //ping 丢包节点
1727                if (item.packet_loss == 0) {
1728                    return false;
1729                }
1730            } else if (tableParams.errCode == 103) { //http 状态码异常
1731                if (item.httpCode != 0 && item.httpCode != '000') {
1732                    return false;
1733                }
1734            } else if (tableParams.errCode == 400) { //http 状态码异常
1735                if (item.httpCode < 400 || item.httpCode >= 499) {
1736                    return false;
1737                }
1738            } else if (tableParams.errCode == 500) { //http 状态码异常
1739                if (item.httpCode < 500) {
1740                    return false;
1741                }
1742            } else if (tableParams.errCode == 104) { //过滤 IP
1743                let ip = $(".table-params-add").text().trim();
1744                if (ip == '*' || ip == '解析失败' || ip == '响应超时') {
1745                    ip = '';
1746                }
1747                if (search_type == 'dns') {
1748                    if (item.addIps.indexOf(ip) == -1) {
1749                        return false;
1750                    }
1751                } else {
1752                    if (item.ip != ip) {
1753                        return false;
1754                    }
1755                }
1756            }
1757            return true;
1758        }
1759    });
1760
1761    return filteredResult;
1762
1763}
1764
1765/* 表格单行操作 */
1766$(document).on('click', '.reTest', function () {
1767    let id = $(this).attr('data-id')
1768    let sid = $(this).attr('data-sid')
1769    let type = $(this).attr('data-type')
1770    let tdLen = 0
1771    if($("#checkPage").val()){
1772        tdLen = trlen[search_type] - 1
1773    }else{
1774        tdLen = trlen[search_type]
1775    }
1776    $('#remark_contact_' + id).remove();
1777    if ($(this).hasClass('on')) {
1778        $(this).removeClass('on')
1779    } else {
1780        $(this).addClass('on').siblings().removeClass('on')
1781        if (type == "head") {
1782            let html = `<tr class='remark_contact' id='remark_contact_${id}'>
1783                        <td colspan='${tdLen}'>
1784                            ${$('#contact_' + id).html()}
1785                            <p class="node_row_close">收起 <i class="iconfont iconrate-up"></i></p>
1786                        </td>
1787                    </tr>`;
1788            if ($('#remark_contact_' + id).length > 0) {
1789                $('#remark_contact_' + id).find("td").html(`${$('#contact_' + id).html()}<p class="node_row_close">收起 <i class="iconfont iconrate-up"></i></p>`);
1790            } else {
1791                $('.tr_' + id).after(html);
1792            }
1793        } else {
1794            let obj = {
1795                host: getBatchHost().data,
1796                node_id_value: id,
1797                get_token: 1,
1798                type,
1799                create_task: 1,
1800            }
1801
1802            if ($('[name=v6]').val()) {
1803                obj.v6 = true;
1804            }
1805            if (type === 'http') {
1806                obj.node_type = $('.banner-param [name=node_type]:checked').val();
1807                obj.isp = getIsp();
1808                obj.http_resolve = $.trim($("[name='http_resolve']").val());
1809                if ($('.banner_advanced').length > 0) {
1810                    obj.init_params = getHttpInitParams();
1811                }
1812            }
1813            if (type === 'ping' || type === 'tcping' || type === 'udping') {
1814                obj.node_type = $('.banner-param [name=node_type]:checked').val();
1815                obj.isp = getIsp();
1816            }
1817            if (type === 'dns') {
1818                obj.node_type = $('.banner-param [name=node_type]:checked').val();
1819                obj.isp = getIsp();
1820                obj.dns_server_name = $.trim($("input[name=dns_server_name]").val());
1821                obj.dns_type = $(".banner-dns-type a.on").attr('data-value');
1822            }
1823
1824            if ($(this).text() == "重试") {
1825
1826                $('.tr_' + id).find('td').eq(1).html(`<i class="loadingRow"></i>`)
1827                $('.tr_' + id).find('td:gt(1)').html(`-`)
1828                obj['again'] = true
1829
1830            } else {
1831                let html = "<tr class='remark_contact addtr' data-end='0' id='remark_contact_" + id + "'><td colspan='" + tdLen + "'>正在加载数据,请稍候…</td></tr>";
1832                $('.tr_' + id).after(html);
1833            }
1834
1835            obj['node_id'] = {
1836                [id]: sid
1837            }
1838            if ((type == 'http' || type == 'ping') && is_v6 == 1) {
1839                obj['v6'] = true
1840            }
1841            if (type == 'dns' && is_v6 == 1) {
1842                obj['dns_type'] = 'AAAA'
1843            }
1844            //checkOne(obj)
1845
1846            build_url(obj)
1847        }
1848    }
1849
1850})
1851
1852/* 手机端表格单行操作 */
1853$(document).on('click', '.table-row-more', function () {
1854    let id = $(this).attr('data-id');
1855    if ($('#remark_contact_' + id).length > 0) {
1856        $('#remark_contact_' + id).remove();
1857        $(this).html('详情 <b class="iconfont iconarrow-bottom"></b>');
1858    } else {
1859        let html = `<tr class='remark_contact' id='remark_contact_${id}'>
1860                            <td colspan='${trlen[search_type]}'>
1861                                ${$('#contact_mini_' + id).html()}
1862                            </td>
1863                        </tr>`;
1864        $('.tr_' + id).after(html);
1865        $(this).html('收起 <b class="iconfont iconrate-up"></b>');
1866    }
1867})
1868
1869/*路由查询选择器事件*/
1870$('html').on("click", ".banner_input .checkbox", function () {
1871    if ($(".checkbox:checked").length > 3) {
1872        Popup.error('最多选择3个检测节点')
1873        $(this).prop("checked", false)
1874        return;
1875    }
1876    let checkNode = [];
1877    $(".checkbox:checked").each(function (i) {
1878        checkNode[i] = {
1879            id: $(this).val(),
1880            text: $(this).next().text(),
1881        };
1882    });
1883    if (checkNode.length > 0) {
1884        let htm = ``
1885        checkNode.forEach(item => {
1886            htm += `<span><i class="iconfont iconX" data-id="${item.id}"></i>${item.text}</span>`
1887        })
1888        $(".select dt").html(htm);
1889    } else {
1890        $(".select dt").text("选择检测节点");
1891    }
1892    let ww = 760 - $(".select").width();
1893    $(".banner_value").css("width", ww + "px");
1894
1895});
1896
1897/*操作批量复选框事件*/
1898$('html').on("click", ".node_checked span i", function () {
1899    let id = $(this).attr('data-id');
1900    $("#btn" + id).click();
1901});
1902
1903/*显示检测节点排序*/
1904$('html').on("click", ".orderBy1", function () {
1905    $('.orderBySelect').toggle();
1906});
1907
1908/*表格排序升序和降序*/
1909$("html").on('click', '.orderBy span', function () {
1910    $(".orderBy span").removeClass("on");
1911    $(this).addClass("on");
1912    let data = [];
1913    let arr = [];
1914    let field = $(this).attr('data-order');
1915    let types = $(this).attr('data-type');
1916    if (batchSearch) {
1917        arr = tableDataBatch;
1918    } else if (Object.keys(domainArr).length > 1) {
1919        arr = JSON.parse(JSON.stringify(initMoreData));
1920    } else {
1921        arr = getTableParamsData();
1922    }
1923
1924    if ($(this).hasClass("after")) {
1925        if (types == "str") {
1926            data = sortByDescStr(arr, field);
1927        } else {
1928            data = sortByDescNum(arr, field);
1929        }
1930    } else {
1931        if (types == "str") {
1932            if (field == 'isp_name') {
1933                data = sortByAscStrIsp(arr, 'origin_from');
1934            } else if (field == 'area') {
1935                data = sortByAscEnglish(arr, 'area');
1936            } else {
1937                data = sortByAscStr(arr, field);
1938            }
1939
1940        } else {
1941            data = sortByAscNum(arr, field);
1942        }
1943    }
1944
1945    if (types == "str") {
1946        $(".orderBySelect").hide()
1947    }
1948
1949    $('.table_cont tbody').html('');
1950    if (batchSearch) {
1951        buildHtmlOrderBy(data)
1952    } else {
1953        init_result(data, false);
1954    }
1955})
1956
1957/*导出检测完成后的报表*/
1958function exportFun() {
1959    if ($('.table_cont tbody').find('tr').length == 0) {
1960        Popup.error('暂无导出数据');
1961        return false;
1962    }
1963    if ($('.banner_submit').hasClass("stop_test")) {
1964        Popup.error('检测正在执行中,请稍后再试');
1965        return false;
1966    }
1967    if (getBatchHost()['data'].length != $(".node-data-tbody tr").length) {
1968        Popup.error('当前任务后台仍在处理中,本次导出结果可能不完整,请稍候几分钟后重新点击导出。');
1969    }
1970
1971    if ($('.export').hasClass('disabled')) {
1972        return false;
1973    }
1974
1975    $(".export").addClass("disabled").html(`<span class="iconfont iconjiazaizhong"></span> 下载中`)
1976
1977    //如果是ip_city检测类型就js直接下载,不需要执行ajax
1978    if (search_type === "ip_city") {
1979        JSExportFun()
1980        return;
1981    }
1982
1983    let taskArr = [];
1984    if (batchSearch) {
1985        let obj = $(".node-data-tbody input");
1986        if (getTableId().length == 0) {
1987            obj.each(function (index, el) {
1988                taskArr.push(obj.eq(index).val());
1989            });
1990        } else {
1991            obj.each(function (index, el) {
1992                if (obj.eq(index).prop('checked')) {
1993                    taskArr.push(obj.eq(index).val());
1994                }
1995            });
1996        }
1997        if (taskArr.length == 0) {
1998            taskArr = $(".banner_submit").attr('data-timeId')
1999        }
2000    } else {
2001        taskArr = $(".banner_submit").attr('data-timeId')
2002    }
2003
2004    $.ajax({
2005        url: exportUrl,
2006        type: 'POST',
2007        xhrFields: {
2008            responseType: 'blob',
2009            getResponse: true
2010        },
2011        data: {
2012            "task_id": taskArr,
2013            "batch_all": batchSearch,
2014            _token
2015        },
2016        success: function (res, status, xhr) {
2017            $('.export').removeClass('disabled').html(`导出列表`);
2018            if (res.code) {
2019                Popup.success(res.message);
2020            } else {
2021                let fileName = '';
2022                let contentDisposition = xhr.getResponseHeader('Content-Disposition')
2023                if (contentDisposition && /filename\*=utf-8\'\'([^;]+)/i.test(contentDisposition)) {
2024                    fileName = RegExp.$1;
2025                }
2026
2027                if (fileName) {
2028                    const url = window.URL.createObjectURL(new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}))
2029                    const link = document.createElement('a')
2030                    link.style.display = 'none'
2031                    link.href = url
2032                    link.setAttribute('download', decodeURIComponent(fileName))
2033                    document.body.appendChild(link)
2034                    link.click()
2035                    document.body.removeChild(link)
2036                }
2037            }
2038        },
2039        error: function (err) {
2040            $('.export').removeClass('disabled').html(`导出列表`);
2041            Popup.error('导出失败');
2042        }
2043    })
2044
2045}
2046
2047/*纯js导出表格数据*/
2048function JSExportFun() {
2049    let $table = $('.table_cont:visible').first();
2050    if (!$table.length) {
2051        $table = $('.table_cont').first();
2052    }
2053    if (!$table.length) {
2054        $('.export').removeClass('disabled').html(`导出列表`);
2055        Popup.warning('暂无导出数据');
2056        return false;
2057    }
2058
2059    let hasChecked = $table.find('tbody .checkbox-g:checked').length > 0;
2060
2061    let ignoreColMap = {};
2062    $table.find('tr').each(function () {
2063        $(this).children('th,td').each(function (idx) {
2064            if ($(this).hasClass('noExlTD')) {
2065                ignoreColMap[idx] = true;
2066            }
2067        });
2068    });
2069
2070    let exportColLen = 0;
2071    $table.find('tr').first().children('th,td').each(function (idx) {
2072        if (!ignoreColMap[idx]) {
2073            exportColLen++;
2074        }
2075    });
2076
2077    let exportRows = [];
2078    $table.find('tr').each(function () {
2079        let $tr = $(this);
2080        if ($tr.closest('tbody').length > 0 && hasChecked && !$tr.find('.checkbox-g').prop('checked')) {
2081            return;
2082        }
2083
2084        let rowHtml = '';
2085        let cellCount = 0;
2086        $tr.children('th,td').each(function (idx) {
2087            if (ignoreColMap[idx]) {
2088                return;
2089            }
2090            let tagName = this.tagName.toLowerCase();
2091            let cellHtml = $(this).html();
2092            if ($.trim($(this).text()) === '' && (!cellHtml || $.trim(cellHtml) === '')) {
2093                cellHtml = '&nbsp;';
2094            }
2095            rowHtml += `<${tagName} style="border:0.5pt solid #d9d9d9;">${cellHtml}</${tagName}>`;
2096            cellCount++;
2097        });
2098
2099        while (cellCount < exportColLen) {
2100            rowHtml += `<td>&nbsp;</td>`;
2101            cellCount++;
2102        }
2103
2104        if ($.trim(rowHtml) !== '') {
2105            exportRows.push(`<tr>${rowHtml}</tr>`);
2106        }
2107    });
2108
2109    if (exportRows.length === 0) {
2110        $('.export').removeClass('disabled').html(`导出列表`);
2111        Popup.warning('暂无导出数据');
2112        return false;
2113    }
2114
2115    let tableHtml = `<table style="border-collapse:collapse;">${exportRows.join('')}</table>`;
2116    let excelHtml = `
2117            <html xmlns:o="urn:schemas-microsoft-com:office:office"
2118                  xmlns:x="urn:schemas-microsoft-com:office:excel"
2119                  xmlns="http://www.w3.org/TR/REC-html40">
2120            <head>
2121                <meta charset="UTF-8">
2122            </head>
2123            <body>${tableHtml}</body>
2124            </html>
2125        `;
2126
2127    let ipList = [];
2128    $table.find('tbody tr').each(function () {
2129        if (hasChecked && !$(this).find('.checkbox-g').prop('checked')) {
2130            return;
2131        }
2132        let ipTxt = $.trim($(this).children('td').eq(1).text());
2133        if (ipTxt) {
2134            ipList.push(ipTxt);
2135        }
2136    });
2137    let firstIp = ipList.length > 0 ? ipList[0] : 'IP';
2138    let ipCount = ipList.length;
2139    let fileName = `${firstIp}等${ipCount}个IP_IP地址查询结果.xlsx`;
2140
2141    let blob = new Blob(['\ufeff' + excelHtml], {type: 'application/vnd.ms-excel'});
2142    let link = document.createElement('a');
2143    link.style.display = 'none';
2144    link.href = window.URL.createObjectURL(blob);
2145    link.download = fileName;
2146    document.body.appendChild(link);
2147    link.click();
2148    document.body.removeChild(link);
2149    window.URL.revokeObjectURL(link.href);
2150
2151    $('.export').removeClass('disabled').html(`导出列表`);
2152}
2153
2154
2155/*批量 失败重查*/
2156function batchErrAgainFun() {
2157    if ($('.table_cont .sourceNodesLen').length == 0) {
2158        Popup.warning('暂无失败数据');
2159        return false;
2160    }
2161    let trs = $(".table_cont tbody tr")
2162    $.each(trs, function (index, item) {
2163        if ($(item).find('.sourceNodesLen').length > 0) {
2164            $(item).find(".update-row").click()
2165        }
2166    })
2167}
2168
2169/*批量 查看  type:all=全部网站,err=异常网站*/
2170function batchViewFun(type = 'all') {
2171    if ($('.table_cont tbody').find('tr').length == 0) {
2172        Popup.warning('暂无数据');
2173        return false;
2174    }
2175    Popup.failure('确认浏览器批量弹出多个窗口查看检测结果?', '提示', function () {
2176        let trs = $(".table_cont tbody tr")
2177        $.each(trs, function (index, item) {
2178            if (type == 'all') {
2179                //如果有勾选,就只查看勾选的,否则查看全部
2180                if (getTableId().length > 0) {
2181                    if ($(item).find('.checkbox-g').prop('checked')) {
2182                        $(item).find(".detail-row").click()
2183                    }
2184                } else {
2185                    $(item).find(".detail-row").click()
2186                }
2187            } else {
2188                if ($(item).find(".errNodeCount").text() > 0) {
2189                    $(item).find(".detail-row").click()
2190                }
2191            }
2192        })
2193    });
2194}
2195
2196/*历史页面变量幅值*/
2197function historyInit(data) {
2198    initData = data;
2199    if (search_type !== 'traceroute') {
2200        result = initData.result;
2201    }
2202    chartMap(search_type, initData.provinceArr)
2203    errorNodeFun()
2204    $(".query_export font,.query_export span").text(result.length);
2205    if (search_type === 'dns') {
2206        $('.echarts_pollute_pre span').text(data.pollute_per);
2207    }
2208}
2209
2210function getHttpInitParams() {
2211    let maxRedirectsInput = $.trim($("[name='max_redirects']").val());
2212    let maxRedirects = maxRedirectsInput === '' ? 0 : Number(maxRedirectsInput);
2213
2214    return {
2215        http_version: $('.banner_advanced [name=http_version]:checked').val() || '',
2216        http_resolve: $.trim($("[name='http_resolve']").val()),
2217        method: $('.banner_advanced [name=method]:checked').val() || 'GET',
2218        referer: $.trim($("[name='referer']").val()),
2219        user_agent: $.trim($("[name='user_agent']").val()),
2220        cookie: $.trim($("[name='cookie']").val()),
2221        max_redirects: maxRedirects,
2222    }
2223}
2224
2225/* 获取参数条件对象 */
2226function getParame(type) {
2227    let random = Math.random().toString(36).slice(-10)
2228    $(".banner_submit").attr('data-random', random)
2229    let obj = {
2230        type,
2231        random,
2232        host: getBatchHost().data,
2233        create_task: 1
2234    }
2235    if ($('[name=v6]').val()) {
2236        obj.v6 = true;
2237    }
2238
2239    if ($('[name=old_type]').prop('checked')) {
2240        obj.old_type = true;
2241    }
2242
2243    if (type === 'http') {
2244        //node_type获取radio选中的值
2245        obj.node_type = $('.banner-param [name=node_type]:checked').val();
2246        obj.isp = getIsp();
2247        obj.http_resolve = $.trim($("[name='http_resolve']").val());
2248        if ($('.banner_advanced').length > 0) {
2249            obj.init_params = getHttpInitParams();
2250        }
2251    }
2252    if (type === 'http_overseas') {
2253        obj.http_resolve = $.trim($("[name='http_resolve']").val());
2254        obj.continent = getContinent();
2255        obj.isp = ['10']
2256    }
2257    if (type === 'ping_overseas') {
2258        obj.continent = getContinent();
2259        obj.isp = ['10']
2260    }
2261    if (type === 'dns_overseas') {
2262        obj.dns_server_name = $.trim($("input[name=dns_server_name]").val());
2263        obj.dns_type = $(".banner-dns-type a.on").attr('data-value');
2264        obj.continent = getContinent();
2265        obj.isp = ['10']
2266    }
2267    if (type === 'ping' || type === 'tcping' || type === 'udping') {
2268        obj.node_type = $('.banner-param [name=node_type]:checked').val();
2269        obj.isp = getIsp();
2270    }
2271    if (type === 'dns') {
2272        obj.node_type = $('.banner-param [name=node_type]:checked').val();
2273        obj.isp = getIsp();
2274        obj.dns_server_name = $.trim($("input[name=dns_server_name]").val());
2275        obj.dns_type = $(".banner-dns-type a.on").attr('data-value');
2276    }
2277    if (type === 'traceroute' || type === 'mtr') {
2278        obj.node_ids = getBatchNodeIds();
2279        traceroute_node_storage()
2280    }
2281    if ((type === search_type && is_detail) || search_type=='ssl_chain' || search_type=='ssl_conversion' || search_type=='speed') {
2282        obj.get_token = 1;
2283    }
2284
2285    if (type === 'ssl_chain') {
2286        obj.host= [$.trim($("input[name=domain]").val())]
2287        obj.content= $.trim($("#upload-text").val())
2288        obj.operate_type= $(".operate_type span.on").attr("data-type")
2289        obj.fix_file_type= $(".fix_file_type span.btn-primary").attr("data-type")
2290    }
2291
2292    if (type === 'ssl_conversion') {
2293        let type_tabs = $('.type_tabs span.btn-primary').attr("data-type")
2294        obj.convert_to= type_tabs
2295        obj.file_pwd= $("[name='file_pwd']").val()
2296        obj.private_pwd= $("[name='private_pwd']").val()
2297    }
2298
2299    if ($(".screen-header").length > 0) {
2300        obj.all_nodes = 1
2301    }
2302
2303    return obj
2304}
2305
2306/*开始检测点击事件*/
2307function buildUrlFun(obj) {
2308
2309    let wall_host = getBatchHost();
2310    if (wall_host.code != 200) {
2311        if (obj.type == 'ip' || obj.type == 'ip_whois' || obj.type == 'ip_state' || obj.type == 'ip_city') {
2312            Popup.error('请输入有效的IP地址!');
2313        } else {
2314            Popup.error('请输入有效的URL地址!');
2315        }
2316        resetSubmitButton();
2317        return;
2318    }
2319
2320    if ((obj.type == 'dns' || obj.type == 'dns_overseas') && obj.dns_server_name) {
2321        let is_dns_server = isURL(obj.dns_server_name);
2322        if (!is_dns_server) {
2323            Popup.error('指定DNS格式错误!');
2324            resetSubmitButton();
2325            return;
2326        }
2327    }
2328
2329    if (obj.type === 'http' && obj.init_params) {
2330        let maxRedirects = obj.init_params.max_redirects;
2331        if (!Number.isInteger(maxRedirects) || maxRedirects < 0 || maxRedirects > 5) {
2332            Popup.error('最大重定向次数必须为0~5之间的整数!');
2333            resetSubmitButton();
2334            return false;
2335        }
2336    }
2337
2338    if (obj.type == 'ip_state' && wall_host.data.length > 10) {
2339        Popup.error('IP状态检测一次最多批量检测10个目标');
2340        resetSubmitButton();
2341        return;
2342    }
2343
2344    if (obj.type == 'traceroute' && obj.node_ids.length == 0) {
2345        Popup.error('请选择检测节点');
2346        resetSubmitButton();
2347        return false;
2348    }
2349
2350    /*if (['http', 'ping', 'tcping', 'udping', 'dns'].includes(obj.type) && obj.batch) {
2351        if ($(".banner-node-list span.on").length == 0) {
2352            Popup.error('请选择检测节点');
2353            return false;
2354        } else {
2355            obj.node_ids = getBatchNodeIds();
2356            batch_node_storage()
2357        }
2358    }*/
2359
2360    sliderCheck(obj)
2361
2362}
2363
2364/**
2365 * 滑块验证检查函数
2366 */
2367function sliderCheck(obj) {
2368    /*if (!obj.rowUpdate && !obj.node_id_value) {  //非更新单行数据且非单个节点查询
2369        $(".banner_submit").removeClass('action_submit')
2370    }*/
2371    let encryptedHex = encryptFun(JSON.stringify(obj));
2372    $.get("/slider_check", {
2373        params: encryptedHex,
2374        _token
2375    }, function (res) {
2376        if (res.data.need_check) {
2377            web_history_obj = obj
2378            openCaptcha();  //打开验证码弹窗
2379        } else {
2380            build_url(obj);
2381        }
2382    }).fail(function() {
2383        resetSubmitButton();
2384    })
2385}
2386
2387
2388function build_url(obj, captchaParam) {
2389    // 判断条件(家庭节点不支持港澳台)
2390    if (obj.node_type === "2") {
2391        // 检查是否包含10或者11
2392        const has10Or11 = obj.isp.includes("10") || obj.isp.includes("11");
2393        // 检查是否不包含1、2、3、11中的任何一个
2394        const noInvalidValues2 = !obj.isp.some(val => ["1", "2", "3"].includes(val));
2395        if (has10Or11 && noInvalidValues2 && obj.type === "tcping") {
2396            Popup.error('TCPing测速家庭节点暂未开放海外及港澳台地区')
2397            resetSubmitButton();
2398            return false
2399        }
2400
2401        // 检查是否包含11
2402        const has11 = obj.isp.includes("11");
2403        // 检查是否不包含1、2、3、10中的任何一个
2404        const noInvalidValues1 = !obj.isp.some(val => ["1", "2", "3", "10"].includes(val));
2405        if (has11 && noInvalidValues1) {
2406            Popup.error('家庭节点暂未开放港澳台地区')
2407            resetSubmitButton();
2408            return false
2409        }
2410    }
2411
2412    linkOnCount=0;
2413
2414    obj.captcha = captchaParam?.captcha
2415    let encryptedHex = encryptFun(JSON.stringify(obj));
2416    $.post(buidUrl, {
2417        params: encryptedHex,
2418        _token
2419    }, function (res) {
2420        updateToolCount(obj);
2421
2422        if (captchaParam) {
2423            // 1.向后端发起业务请求,获取验证码验证结果和业务结果
2424            let result = true
2425            if (res.data.err == -2) {
2426                result = false
2427                captcha.hide()
2428                Popup.showmsg(res.msg);
2429            } else if (res.data.err == -1) {
2430                result = false
2431            }
2432
2433            let verifyResult = {
2434                captchaResult: result,// 验证码验证是否通过,boolean类型,必选
2435            };
2436            // 调用回调函数,传入验证结果
2437            captchaParam.callback(verifyResult);
2438            if (result) {
2439                captcha.hide()
2440            } else {
2441                resetSubmitButton();
2442                return false
2443            }
2444        }
2445
2446        if (res.code !== 1){
2447            local_storage() // 本地存储检测域名
2448        }
2449
2450        if (res.code == 200) {
2451
2452            if (res.data.point > 0 && res.data.need_pay_point_notice){
2453                Popup.failure(`您的免费检测次数已用尽,继续检测将会消耗您的波点(当前剩余波点<span class="color-blue">${res.data.total_point_format}</span>),请确认是否继续检测?<div style="position: absolute;top: 154px;z-index: 2;">
2454                <label class="checkbox-control">
2455                    <input type="checkbox" class="checkbox-control-input pointCheckbox">
2456                    <span class="checkbox-control-label">不再提示</span>
2457                </label></div>`,'提示', function () {
2458                    BuildNext(obj, res)
2459                    configGetFun()
2460                },'',function (){
2461                    testCloseLoading()
2462                    configGetFun()
2463                },'','继续检测','取消检测','pointPopup')
2464            }else{
2465                BuildNext(obj, res)
2466            }
2467
2468        } else {
2469
2470            testCloseLoading()
2471
2472            if (res.data.domain_num) {
2473                $(".error-popup").addClass("on")
2474                $(".error-popup-msg span").html(res.message)
2475                if(res.data.vip_id==0){
2476                    $(".error-popup-box .error-popup-login").hide();
2477                    $(".error-popup-box .goLogin").show();
2478                }else{
2479                    $(".error-popup-box .error-popup-login").show();
2480                    $(".error-popup-box .goLogin").hide();
2481                }
2482            } else {
2483                Popup.error(res.message)
2484            }
2485
2486            /*大屏展示处理*/
2487            if($(".screen-header").length > 0){
2488                $(".domainPage1").show()
2489                $(".domainPage2").hide()
2490            }
2491
2492        }
2493    }).fail(function() {
2494        resetSubmitButton();
2495    })
2496}
2497
2498function BuildNext(obj, res) {
2499    if (obj.node_id_value) {  // 单个节点查询
2500        task_token = [res.data.task_token]
2501        checkOne(obj)
2502    } else {
2503        task_token = res.data.task_token
2504        if (obj.rowUpdate) {  //更新单行数据
2505            if (['http', 'ping', 'tcping', 'udping', 'dns', 'hijack'].includes(obj.type) && batchSearch) {
2506                batchCheck(obj)
2507            } else {
2508                checkUpdate(obj)
2509            }
2510        } else {
2511            let isBatch = res.data.isBatch
2512            let loadState = false   //页面是否刷新
2513            if ((isBatch && $(".table_cont_more").length == 1) || (!isBatch && $(".table_cont_one").length == 1)) {
2514                loadState = true
2515            }
2516            //判断必须跳转的情况(排行榜页面和加速页面必须刷新)
2517            if($("#checkPage").val() || ($("[name=old_type]").length == 1 && !obj.old_type && $(".batch_node_box").length == 0) || ($("[name=old_type]").length == 1 && obj.old_type && $(".batch_node_box").length > 0)){
2518                loadState = false
2519            }
2520
2521            if ((loadState && task_token !== '' && $(".emptyBox").length == 0 && !obj.target) || obj.type == 'speed' || (obj.type == 'complex' && $(".screen-header").length==1)) {
2522
2523                checkMember['data'] = res.data
2524                checkMember['code'] = 0
2525                resultArr = []
2526                $(".map-tab span").eq(0).click()
2527                $(".node-cont-item thead").show()
2528                $(".node-cont-list .notData").remove()
2529                $(".orderBy span").removeClass('on')
2530                $(".table-tab-all").click()
2531                $(".tool-progress-bar-inner").text('0%').css('width', '0%')
2532                $(".remark_contact").remove()
2533                $(".queryTimeNode,.polluteNode,.httpCodeNode400,.httpCodeNode500,.packetLossNode,.httpCodeNode").text(0)
2534                $(".check-time").text(getNowFormatDate())
2535                $(".notRegisterDomain,.checkedDomain,.holdDomain,.notHoldDomain,.notRegister").html('')
2536                $(".check-host").text(res.data.host[0])
2537                $("[name='host']").val(res.data.host.join("\n"))
2538
2539                let parameter = getParame(obj.type)
2540                parameter.continue = obj.continue
2541                if (obj.type == 'ip_state') {
2542                    $(".table_cont_one").hide()
2543                    listCount = res.data.listCount
2544                    nodeCount = res.data.nodeCount
2545                }else{
2546                    parameter.batch = obj.batch
2547                }
2548                //parameter.confirm_point_cost = 0
2549                if (['http', 'http_overseas', 'ping_overseas', 'dns_overseas', 'ping', 'tcping', 'udping', 'dns', 'hijack'].includes(obj.type)) {
2550                    if (parameter.batch) {
2551                        nodes = res.data.nodes
2552                        nodeChecked = res.data.info.node_ids
2553                        init_table_batch(nodes, res.data.host, res.data.matchHost)
2554                    } else {
2555                        init_result_one(res.data.nodes, parameter.continue ? 'pingContinue' : '')
2556                        if (obj.continue) {
2557                            canvas = {}
2558                            context = {}
2559                            tableData = {}
2560                            rowLoadCount = {}
2561                            res.data.nodes.forEach((item) => {
2562                                canvas[item.id] = document.getElementById('myCanvas_' + item.id);
2563                                context[item.id] = canvas[item.id].getContext('2d');
2564                                tableData[item.id] = [];
2565                            })
2566                        }
2567                    }
2568                    SubmitPublic(parameter)
2569                }
2570
2571                if (obj.type == 'wall') {
2572                    wallLoadData(parameter)
2573                } else if (obj.type == 'speed') {
2574                    bandSpeedLoadData(parameter)
2575                } else if (obj.type == 'pollute') {
2576                    polluteLoadData(parameter)
2577                } else if (obj.type == 'qq' || obj.type == 'wechat' || obj.type == 'blacklist') {
2578                    qqLoadData(parameter)
2579                } else if (obj.type == 'ssl') {
2580                    sslLoadData(parameter)
2581                } else if (obj.type == 'ipv6') {
2582                    ipv6LoadData(parameter)
2583                } else if (obj.type == 'whois') {
2584                    whoisLoadData(parameter)
2585                } else if (obj.type == 'web_history') {
2586                    webHistoryLoadData(parameter)
2587                } else if (obj.type == 'ip_whois') {
2588                    ipWhoisLoadData(parameter)
2589                } else if (obj.type == 'ip_city') {
2590                    ipCityLoadData(parameter)
2591                } else if (obj.type == 'icp') {
2592                    icpLoadData(parameter)
2593                } else if (obj.type == 'ssl_chain') {
2594                    chainLoadData(parameter)
2595                } else if (obj.type == 'ip_state') {
2596                    SubmitPublic(parameter)
2597                } else if (obj.type == 'complex') {
2598                    complexLoadData(parameter)
2599                } else if (obj.type == 'web_pref') {
2600                    webPrefLoadData(parameter)
2601                } else if (obj.type == 'traceroute') {
2602                    tableInit(30)
2603                    continueTime = null;
2604                    tableData = {}
2605                    rowLoadCount = {};
2606
2607                    mapCcountry = "china"
2608                    scatterDate = []
2609                    lastArray = []
2610
2611                    lanMax = 0
2612                    ttlMin = 30
2613                    SubmitPublic(parameter)
2614                    mapInit()
2615                    $(".continue_txt").text(`检测节点:${res.data.nodes[0]['node_name']}`)
2616                }
2617
2618            } else {
2619                let formHtm = document.createElement("form")
2620                formHtm.className = "toolBuildForm"
2621                $("body").append(formHtm)
2622                let form = $(".toolBuildForm")
2623                form.attr('action', res.data.url);
2624                form.attr('method', 'post');
2625                if (obj.target) {
2626                    let ua = navigator.userAgent;
2627                    let ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
2628                        isIphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
2629                        isAndroid = ua.match(/(Android)\s+([\d.]+)/),
2630                        weixin = ua.match(/MicroMessenger/),
2631                        isMobile = isIphone || isAndroid || weixin;
2632                    if (!isMobile) {
2633                        form.attr('target', '_blank');
2634                    }
2635                    $('.banner_submit').addClass('action_submit').removeClass('stop_test').text('开始检测');
2636                }
2637                Object.keys(obj).forEach(key => {
2638                    if (Array.isArray(obj[key])) {
2639                        obj[key].forEach(item => {
2640                            form.append($("<input>").attr('name', key + '[]').val(item));
2641                        })
2642                    } else if (key === 'init_params' && typeof obj[key] === 'object') {
2643                        form.append($("<input>").attr('name', key).val(JSON.stringify(obj[key])));
2644                    } else {
2645                        form.append($("<input>").attr('name', key).val(obj[key]));
2646                    }
2647                })
2648                form.append($("<input>").attr('name', '_token').val(_token));
2649                //form.append($("<input>").attr('name', 'confirm_point_cost').val(0));
2650                $("body").append(form)
2651
2652                form.submit();
2653                form.remove();
2654                return false;
2655            }
2656        }
2657    }
2658}
2659
2660function configGetFun(){
2661    if ($(".pointCheckbox").prop("checked") == true) {
2662        $.post(`/apiHtml/member/config/set`,{
2663            "content":"point_use_tip",
2664            "type":"notice"
2665        }, function (res) {
2666        })
2667    }
2668}
2669
2670/*第一次访问接口,加载空数据占位*/
2671function SubmitPublic(obj) {
2672    $(".hijack_return").hide()
2673    $(".ip_state_return").hide()
2674    loadingOpen()
2675    if (obj.continue || obj.type == 'traceroute') {
2676        continueCheck(obj)
2677    } else if (obj.batch) {
2678        batchCheck(obj)
2679    } else {
2680        chartMap(obj.type, []);
2681        maxMinNodeInitHtml(obj);
2682        check(obj)
2683    }
2684}
2685
2686// 失败重连
2687function reConnect(callback) {
2688    linkOnCount++
2689    // 没连接上会⼀直重连,设置最大请求次数避免请求过多
2690    if (linkOnCount > linkCount) {
2691        testCloseLoading(false)
2692        Popup.error('检测失败,请联系管理员!')
2693        return;
2694    }
2695    setTimeout(() => {
2696        callback()
2697    }, 1000)
2698}
2699
2700function fieldFun(type=search_type) {
2701    const fieldMap = {
2702        'http': 'totalCost',
2703        'http_overseas': 'totalCost',
2704        'complex': 'totalCost',
2705        'ping': 'avg',
2706        'ping_overseas': 'avg',
2707        'tcping': 'avg',
2708        'udping': 'avg',
2709        'dns': 'query_time',
2710        'dns_overseas': 'query_time'
2711    };
2712    return fieldMap[type];
2713}
2714
2715function getIpPre(arr) {
2716    let data = arr.reduce((pre, curr) => {
2717        pre['ipPre'] = pre['ipPre'] || {};
2718        pre['name'] = pre['name'] || {};
2719
2720        if (
2721            search_type == "dns" ||
2722            search_type == "dns_overseas" ||
2723            search_type == "complex"
2724        ) {
2725            // 判断 curr.addIps 是否是数组
2726            if (Array.isArray(curr.addIps)) {
2727                curr.addIps.forEach((item) => {
2728                    if (!pre['ipPre'][item]) {
2729                        pre['ipPre'][item] = {
2730                            count: 1,
2731                            isp: [curr.isp],
2732                            is_pollute: curr.is_pollute_ip[item],
2733                            percentage: parseInt((1 / arr.length) * 100 * 1000) / 1000
2734                        };
2735                    } else {
2736                        pre['ipPre'][item].count++;
2737
2738                        pre['ipPre'][item].isp.includes(curr.isp)
2739                            ? ''
2740                            : pre['ipPre'][item].isp.push(curr.isp);
2741
2742                        pre['ipPre'][item].is_pollute =
2743                            pre['ipPre'][item].is_pollute
2744                                ? true
2745                                : curr.is_pollute_ip[item];
2746
2747                        pre['ipPre'][item].percentage =
2748                            parseInt(
2749                                (pre['ipPre'][item].count / arr.length) *
2750                                100 *
2751                                1000
2752                            ) / 1000;
2753                    }
2754                });
2755            }
2756
2757        } else {
2758            let ip = curr.ip;
2759
2760            // 计算百分比
2761            if (!pre['ipPre'][ip]) {
2762                pre['ipPre'][ip] = {
2763                    count: 1,
2764                    isp: [curr.isp],
2765                    is_pollute: curr.is_pollute_ip[ip],
2766                    percentage: parseInt((1 / arr.length) * 100 * 1000) / 1000
2767                };
2768            } else {
2769                pre['ipPre'][ip].count++;
2770
2771                pre['ipPre'][ip].isp.includes(curr.isp)
2772                    ? ''
2773                    : pre['ipPre'][ip].isp.push(curr.isp);
2774
2775                pre['ipPre'][ip].is_pollute =
2776                    pre['ipPre'][ip].is_pollute
2777                        ? true
2778                        : curr.is_pollute_ip[ip];
2779
2780                pre['ipPre'][ip].percentage =
2781                    parseInt(
2782                        (pre['ipPre'][ip].count / arr.length) *
2783                        100 *
2784                        1000
2785                    ) / 1000;
2786            }
2787        }
2788
2789        let name = curr.province_name;
2790        let polluteCount = 0;
2791
2792        Object.keys(curr['is_pollute_ip']).forEach((key) => {
2793            if (curr['is_pollute_ip'][key]) {
2794                polluteCount++;
2795            }
2796        });
2797
2798        let currentValue = curr[fieldFun()];
2799
2800        // 统计省份数据
2801        if (!pre['name'][name]) {
2802            pre['name'][name] = {
2803                count: currentValue > 0 ? 1 : 0,
2804                province_name: curr.province_name,
2805                sum: currentValue > 0 ? currentValue : 0,
2806                totalCost: currentValue > 0 ? currentValue : 0,
2807
2808                // 新增:记录最小值
2809                min: currentValue > 0 ? currentValue : 0,
2810
2811                area: curr.area,
2812
2813                sum2: polluteCount,
2814                province_name2: curr.province_name,
2815            };
2816        } else {
2817            if (currentValue > 0) {
2818                pre['name'][name].count++;
2819                pre['name'][name].sum += currentValue;
2820
2821                // 更新最小值
2822                if (
2823                    pre['name'][name].min === 0 ||
2824                    currentValue < pre['name'][name].min
2825                ) {
2826                    pre['name'][name].min = currentValue;
2827                }
2828            }
2829
2830            pre['name'][name].province_name.includes(curr.province_name)
2831                ? ''
2832                : pre['name'][name].province_name.push(curr.province_name);
2833
2834            pre['name'][name].totalCost =
2835                pre['name'][name].count > 0
2836                    ? pre['name'][name].sum / pre['name'][name].count
2837                    : 0;
2838
2839            pre['name'][name].sum2 += polluteCount;
2840        }
2841
2842        return pre;
2843    }, {});
2844
2845    let newArr = {
2846        ipPre: [],
2847        provinceArr: [],
2848        pollute: [],
2849        ispCount: {},
2850        polluteIpCount: 0,
2851    };
2852
2853    Object.keys(data['ipPre']).forEach((key) => {
2854        let item = data['ipPre'][key];
2855
2856        let obj = {
2857            ip: key,
2858            pre: item.percentage,
2859            is_pollute: item.is_pollute,
2860            isp: item.isp
2861        };
2862
2863        newArr['ipPre'].push(obj);
2864
2865        item.isp.forEach((isp) => {
2866            if (!newArr['ispCount'][isp]) {
2867                newArr['ispCount'][isp] = 1;
2868            } else {
2869                newArr['ispCount'][isp]++;
2870            }
2871        });
2872
2873        if (item.is_pollute) {
2874            newArr['polluteIpCount']++;
2875        }
2876    });
2877
2878    // 将 IP 结果按照百分比从高到低排序
2879    newArr['ipPre'].sort(function (a, b) {
2880        return b['pre'] - a['pre'];
2881    });
2882
2883    Object.keys(data['name']).forEach((key) => {
2884        let obj = {
2885            name: data['name'][key].province_name,
2886            seven_area_name: data['name'][key].area,
2887
2888            // 原来的平均值
2889            /*value: data['name'][key].totalCost > 0
2890                ? Number(data['name'][key].totalCost.toFixed(3))
2891                : 0,*/
2892
2893            // 最小值
2894            value: data['name'][key].min > 0
2895                ? Number(data['name'][key].min.toFixed(3))
2896                : 0
2897        };
2898
2899        newArr['provinceArr'].push(obj);
2900
2901        let obj2 = {
2902            name: data['name'][key].province_name2,
2903            seven_area_name: data['name'][key].area,
2904            value: data['name'][key].sum2 + 1
2905        };
2906
2907        newArr['pollute'].push(obj2);
2908    });
2909
2910    // 省份平均值从高到低排序
2911    newArr['provinceArr'].sort(function (a, b) {
2912        return b['value'] - a['value'];
2913    });
2914
2915    // 污染数据从高到低排序
2916    newArr['pollute'].sort(function (a, b) {
2917        return b['value'] - a['value'];
2918    });
2919
2920    return newArr;
2921}
2922
2923function getProvinceArr(arr) {
2924    let data = arr.reduce((pre, curr) => {
2925        let ip = curr.province_name;
2926        // 计算百分比
2927        if (!pre[ip]) {
2928            pre[ip] = {
2929                count: curr[fieldFun()] > 0 ? 1 : 0,
2930                province_name: curr.province_name,
2931                sum: curr[fieldFun()] > 0 ? curr[fieldFun()] : 0,
2932                totalCost: curr[fieldFun()] > 0 ? curr[fieldFun()] : 0,
2933                area: curr.area
2934            };
2935        } else {
2936            if (curr[fieldFun()] > 0) {
2937                pre[ip].count++;
2938                pre[ip].sum += curr[fieldFun()];
2939            }
2940            pre[ip].province_name.includes(curr.province_name) ? '' : pre[ip].province_name.push(curr.province_name)
2941            pre[ip].totalCost = pre[ip].sum / pre[ip].count
2942        }
2943        return pre;
2944    }, {});
2945
2946
2947    let newArr = []
2948    Object.keys(data).forEach((key) => {
2949        let obj = {
2950            name: data[key].province_name,
2951            seven_area_name: data[key].area,
2952            value: data[key].totalCost > 0 ? Number(data[key].totalCost.toFixed(3)) : 0
2953        }
2954        newArr.push(obj)
2955    });
2956
2957    // 将结果转换为数组,并按照百分比从高到低排序
2958    let newData = newArr.sort(function (a, b) {
2959        return b['value'] - a['value'];
2960    })
2961    return newData
2962}
2963
2964function getPollute(arr) {
2965
2966    let data = arr.reduce((pre, curr) => {
2967        let polluteCount = 0
2968        Object.keys(curr['is_pollute_ip']).forEach((key) => {
2969            if (curr['is_pollute_ip'][key]) {
2970                polluteCount++
2971            }
2972        })
2973        let ip = curr.province_name;
2974        if (!pre[ip]) {
2975            pre[ip] = {
2976                sum: polluteCount,
2977                province_name: curr.province_name,
2978                area: curr.area
2979            };
2980        } else {
2981            pre[ip].sum += polluteCount;
2982        }
2983        return pre;
2984    }, {});
2985
2986    let newArr = []
2987    Object.keys(data).forEach((key) => {
2988        let obj = {
2989            name: data[key].province_name,
2990            seven_area_name: data[key].area,
2991            value: data[key].sum + 1
2992        }
2993        newArr.push(obj)
2994    });
2995    // 将结果转换为数组,并按照百分比从高到低排序
2996    let newData = newArr.sort(function (a, b) {
2997        return b['value'] - a['value'];
2998    })
2999    return newData
3000}
3001
3002function getMinMaxAvg(arr) {
3003    if (arr.length) {
3004        let newArr = []
3005        arr.forEach(item => {
3006            if (item[fieldFun()] > 0) {
3007                newArr.push(item)
3008            }
3009        });
3010        if (newArr.length > 0) {
3011            // 高到低排序
3012            let result = newArr.sort(function (a, b) {
3013                return b[fieldFun()] - a[fieldFun()];
3014            })
3015
3016            let avg = result.reduce((pre, curr) => {
3017                return pre + curr[fieldFun()]
3018            }, 0) / result.length
3019
3020            let minMaxAvg = {
3021                min: {
3022                    node_name: result[result.length - 1].origin_from + result[result.length - 1].isp_name,
3023                    cost: result[result.length - 1][fieldFun()]
3024                },
3025                max: {
3026                    node_name: result[0].origin_from + result[result.length - 1].isp_name,
3027                    cost: result[0][fieldFun()]
3028                },
3029                avg: Number(avg.toFixed(3))
3030            }
3031            return minMaxAvg
3032        }
3033    }
3034}
3035
3036function getIspMaxMin(arr,type=search_type) {
3037    let obg = {
3038        0: [],
3039    }
3040    let newData = {}
3041    let ispCount = {}
3042    arr.forEach((item) => {
3043        if (!obg[item.isp] || !obg[item.isp]) {
3044            obg[item.isp] = []
3045            ispCount[item.isp] = []
3046        }
3047        if (!obg[item.area]) {
3048            if (item.area != 'hai_wai') {
3049                obg[item.area] = []
3050            }
3051        }
3052        if (item[fieldFun(type)] > 0) {
3053            obg[0].push(item)
3054            obg[item.isp].push(item)
3055            if (item.area != 'hai_wai') {
3056                obg[item.area].push(item)
3057            }
3058
3059        }
3060    })
3061    if (obg[0].length > 0) {
3062        Object.keys(obg).forEach((key) => {
3063            // 高到低排序
3064            let result = obg[key].sort(function (a, b) {
3065                return b[fieldFun(type)] - a[fieldFun(type)];
3066            })
3067
3068            let avg = result.reduce((pre, curr) => {
3069                return pre + curr[fieldFun(type)]
3070            }, 0) / result.length
3071            if (result.length > 0) {
3072                let minMaxAvg = {
3073                    isp_name: result[0].isp_name,
3074                    min: {
3075                        node_name: result[result.length - 1].origin_from + result[result.length - 1].isp_name,
3076                        cost: result[result.length - 1][fieldFun(type)]
3077                    },
3078                    max: {
3079                        node_name: result[0].origin_from + result[0].isp_name,
3080                        cost: result[0][fieldFun(type)]
3081                    },
3082                    avg: {
3083                        node_name: '',
3084                        cost: Number(avg.toFixed(3))
3085                    }
3086                }
3087                newData[key] = minMaxAvg
3088            }
3089        });
3090    }
3091    return newData
3092}
3093
3094//数据封装
3095function initDataFun(data) {
3096    let {ipPre, provinceArr, pollute, ispCount, polluteIpCount} = getIpPre(data)
3097    let ispMaxMin = getIspMaxMin(data)
3098    initData = {
3099        ipPre,
3100        ispCount,
3101        polluteIpCount,
3102        ispMaxMin,
3103        provinceArr,
3104        pollute,
3105        result: sortByAscNum(data, 'isp'),
3106        newRow: data.data,
3107        count: data.length,
3108        total: data.length,
3109    }
3110
3111    initHtmlOne()
3112
3113}
3114
3115//渲染单次查询页面
3116function initHtmlOne() {
3117    $(".echarts_tabs span").removeClass("btn-primary").eq(0).addClass("btn-primary")
3118    $(".echarts_pollute").hide()
3119    result = initData.result;
3120    if (search_type !== 'traceroute') {
3121
3122        $('.query_export span,.query_export font').text(result.length);
3123
3124        let is_pollute_ip_arr = []
3125        result.forEach(item => {
3126            if (search_type === 'dns') {
3127                let is_pollute_ip_obj = item.is_pollute_ip
3128                Object.keys(is_pollute_ip_obj).forEach(key => {
3129                    if (is_pollute_ip_obj[key] && is_pollute_ip_arr.indexOf(item.node_id) == -1) {
3130                        is_pollute_ip_arr.push(item.node_id);
3131                    }
3132                })
3133                let is_pollute_ip_pre = is_pollute_ip_arr.length / result.length * 100
3134                $('.echarts_pollute_pre span').text(is_pollute_ip_pre.toFixed(2));
3135            }
3136        })
3137
3138        $('.node-count').text(initData.count);
3139        chartMap(search_type, initData.provinceArr);
3140        maxMinNodeHtml();
3141        nodeErrorHtml();
3142        init_isp(initData.ipPre);
3143        ispHtml()
3144        errorNodeFun()
3145    }
3146    init_result(result, true);
3147
3148}
3149
3150function errorNodeFun() {
3151    let errorNode = {
3152        queryTime: 0,
3153        pollute: 0,
3154        packetLoss: 0,
3155        httpCode: 0,
3156        httpCode400: 0,
3157        httpCode500: 0,
3158    }
3159    result.forEach(item => {
3160        if (item.ip == "" || item.query_time == "*" || item.httpCode == 0 || item.httpCode == '000') {    //http 状态码异常
3161            errorNode.httpCode++
3162        } else if (item.httpCode >= 400 && item.httpCode < 500) {
3163            errorNode.httpCode400++
3164        } else if (item.httpCode >= 500) {
3165            errorNode.httpCode500++
3166        }
3167
3168        if (item.ip == "" || item.query_time == "*" || item.httpCode == 0) {    //未响应
3169            errorNode.queryTime++
3170        }
3171
3172        if (item.packet_loss > 0) {    //丢包节点
3173            errorNode.packetLoss++
3174        }
3175
3176        //被污染节点
3177        let pollute_ip = item.is_pollute_ip;
3178        if (typeof pollute_ip == 'string') {
3179            pollute_ip = JSON.parse(item.is_pollute_ip);
3180        }
3181        try {
3182            Object.keys(pollute_ip).forEach(key => {
3183                if (pollute_ip[key]) {
3184                    errorNode.pollute++
3185                    throw new Error("该节点被污染");
3186                }
3187            })
3188        } catch (e) {
3189        }
3190    })
3191
3192    $(".table-params-item span").hide()
3193    if (errorNode.queryTime > 0) {
3194        $(".queryTimeNode").text(errorNode.queryTime)
3195        $(".table-tab span[data-isp='100']").show()
3196    }
3197    if (errorNode.pollute > 0) {
3198        $(".polluteNode").text(errorNode.pollute)
3199        $(".table-tab span[data-isp='101']").show()
3200    }
3201    if (errorNode.packetLoss > 0) {
3202        $(".packetLossNode").text(errorNode.packetLoss)
3203        $(".table-tab span[data-isp='102']").show()
3204    }
3205    if (errorNode.httpCode400 > 0) {
3206        $(".httpCodeNode400").text(errorNode.httpCode400)
3207        $(".table-tab span[data-isp='400']").show()
3208    }
3209    if (errorNode.httpCode500 > 0) {
3210        $(".httpCodeNode500").text(errorNode.httpCode500)
3211        $(".table-tab span[data-isp='500']").show()
3212    }
3213    if (errorNode.httpCode > 0) {
3214        $(".httpCodeNode").text(errorNode.httpCode)
3215        $(".table-tab span[data-isp='103']").show()
3216    }
3217}
3218
3219function toolCheckInit() {
3220    $(".spinner-border-tool").show()
3221    $(".tool-progress-bar-inner").text('0%').css('width', '0%')
3222}
3223
3224function errorFun(res) {
3225    if (res.code == -3) {
3226        socketError()
3227    } else {
3228        Popup.error(res.message)
3229    }
3230    testCloseLoading();
3231}
3232
3233/* 获取单行查询文本值 */
3234function getHost() {
3235    let val = $.trim($('[name=host]').val());
3236    return urlToLowerCase(val);
3237}
3238
3239/*获取当前时间 年月日 时分秒*/
3240function getNowFormatDate() {
3241    let date = new Date();
3242    let seperator1 = "-";
3243    let seperator2 = ":";
3244    let month = date.getMonth() + 1;
3245    let strDate = date.getDate();
3246    let hours = date.getHours();
3247    let minutes = date.getMinutes();
3248    let seconds = date.getSeconds();
3249    if (month >= 1 && month <= 9) {
3250        month = "0" + month;
3251    }
3252    if (strDate >= 0 && strDate <= 9) {
3253        strDate = "0" + strDate;
3254    }
3255    if (hours >= 0 && hours <= 9) {
3256        hours = "0" + hours;
3257    }
3258    if (minutes >= 0 && minutes <= 9) {
3259        minutes = "0" + minutes;
3260    }
3261    if (seconds >= 0 && seconds <= 9) {
3262        seconds = "0" + seconds;
3263    }
3264    let currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
3265        + " " + hours + seperator2 + minutes
3266        + seperator2 + seconds;
3267    return currentdate;
3268}
3269
3270/*获取多行查询文本值*/
3271function getBatchHost() {
3272    let host = getHost();
3273    let hostArr = host.split('\n');
3274    //去重
3275    hostArr = Array.from(new Set(hostArr));
3276    //去空
3277    hostArr = hostArr.filter(item => item.trim() !== '');
3278    let res = {
3279        code: 200,
3280        data: [],
3281    };
3282    if (hostArr.length < 1) {
3283        res.code = 202
3284        return res;
3285    }
3286    res.text = hostArr.toString();
3287    res.data = hostArr;
3288    return res;
3289}
3290
3291function maxMinNodeHtml() {
3292    $(".node-cont-item thead").removeClass("hide")
3293    Object.keys(initData.ispMaxMin).forEach(key => {
3294        let htm = ``
3295        let item = initData.ispMaxMin[key]
3296        htm += `<td>${ ['http_overseas', 'ping_overseas', 'dns_overseas'].includes(search_type) ? areaNodeName[key] : nodeName[key]}</td>
3297                <td><span>${item.min.node_name}</span><span class="${init_color(item.min.cost, search_type)} ml-4">${init_unit(item.min.cost, search_type)}</span></td>
3298                <td><span>${item.max.node_name}</span><span class="${init_color(item.max.cost, search_type)} ml-4">${init_unit(item.max.cost, search_type)}</span></td>
3299                <td><span class="${init_color(item.avg.cost, search_type)}">${item.avg.cost}${getUnit()}</span></td>`
3300        $(".node-cont-row-" + key).html(htm).removeClass("not-data")
3301    })
3302}
3303
3304function maxMinNodeHtmlNotData() {
3305    if ($(".node-cont-list .not-data").length == 13 && $(".node-cont-list .notData").length == 0) {
3306        $(".node-cont-item thead").hide()
3307        $(".node-cont-table .not-data").remove()
3308        if (is_v6) {
3309            $(".node-cont-list").append(`<tr class="notData">
3310                            <td colspan="4" class="color-666">
3311                                全部IPv6检测节点均未响应,可能是以下原因导致的:<br>
3312                                1、该域名不支持IPv6访问<br>
3313                                2、该域名未注册或已过期<br>
3314                                <a href="https://www.51dns.com/ipv6.html?f=bocePollute" target="_blank">
3315                                    <img src="/statics/web_v5/img/img53.jpg" class="mt-24"></a>
3316                            </td>
3317                        </tr>`)
3318        } else {
3319            $(".node-cont-list").append(`<tr class="notData">
3320                            <td colspan="4" class="color-666">
3321                                全部节点未响应,可能是以下原因导致的:<br>
3322                                1、域名未注册或过期;<br>
3323                                2、DNS服务器故障或DNS服务器配置错误;<br>
3324                                3、网络连接不稳定或后端服务不可用;<br>
3325                                4、本地DNS缓存污染或权威DNS缓存未更新;<br>
3326                                5、安全软件拦截或防火墙设置不当。
3327                            </td>
3328                        </tr>`)
3329        }
3330    } else {
3331        let tr = $(".node-cont-table .not-data")
3332        $.each(tr, function (item) {
3333            tr.eq(item).find('td:gt(0)').remove()
3334            tr.eq(item).append(`<td colspan="3" class="color-999">${search_type == 'http' || search_type == 'http_overseas' ? '无法访问' : '响应超时'}</td>`)
3335        })
3336    }
3337}
3338
3339function maxMinNodeInitHtml(obj) {
3340    let htm = ``
3341    let areaName = ['http_overseas', 'ping_overseas', 'dns_overseas'].includes(search_type) ? ["asia", "africa", "north_america", "south_america", "europe", "oceania"] : ['hua_dong','hua_bei','hua_nan','hua_zhong','xi_bei','xi_nan','dong_bei']
3342
3343    // 检查是否包含运营商(1、2、3)中的任何一个
3344    let hasOperator = obj.isp && obj.isp.some(operator => ['1', '2', '3'].includes(operator))
3345    let visibleRowCount = 0;
3346    const nodeKeyList = ['http_overseas', 'ping_overseas', 'dns_overseas'].includes(search_type) ? Object.keys(areaNodeName) : Object.keys(nodeName)
3347
3348    nodeKeyList.forEach(key => {
3349        let className = ''
3350        if (obj.isp && search_type != 'http_overseas' && search_type != 'ping_overseas' && search_type != 'dns_overseas') {
3351
3352            if (['1', '2', '3', '10', '11'].includes(key)) {
3353                if (!obj.isp.includes(key)) {
3354                    className = 'hide'
3355                }
3356            }
3357            // 如果是区域节点且没有运营商,则隐藏
3358            if (areaName.includes(key) && !hasOperator) {
3359                className = 'hide'
3360            }
3361
3362        }
3363
3364        if (['http_overseas', 'ping_overseas', 'dns_overseas'].includes(search_type) && !obj.continent.includes(key) && key!=0) {
3365            className = 'hide'
3366        }
3367
3368        if (className !== 'hide') {
3369            visibleRowCount++;
3370        }
3371
3372        htm += `<tr class="node-cont-row-${key} not-data ${className}">
3373                    <td>${ ['http_overseas', 'ping_overseas', 'dns_overseas'].includes(search_type) ? areaNodeName[key] : nodeName[key]}</td>
3374                    <td colspan="3" class="color-999">
3375                        <div class="flex-g justify-end">
3376                            <i class="iconfont iconjiazaizhong loadingRow-16"></i>
3377                        </div>
3378                    </td>
3379                </tr>`
3380    })
3381
3382    if (visibleRowCount > 4) {
3383        htm += `<tr class="table_area_more">
3384                <td colspan="4">
3385                    <p class="node_error_more color-blue" style="position: static; height: 0.13rem; margin-top: 0.12rem;" data-type="table">
3386                        <font>点击查看更多</font> <i class="iconfont iconarrow-bottom"></i>
3387                    </p>
3388                </td>
3389            </tr>`
3390    }
3391
3392    $(".node-cont-list").html(htm)
3393}
3394
3395function nodeErrorHtml() {
3396    let arr = {
3397        100: [],
3398        101: [],
3399        102: [],
3400        103: [],
3401        400: [],
3402        500: [],
3403    };
3404    result.forEach(item => {
3405
3406        //未响应
3407        if (item.ip == "" || item.query_time == "*" || item.httpCode == 0) {
3408            arr['100'].push(item);
3409        }
3410
3411        //被污染节点
3412        let pollute_ip = item.is_pollute_ip;
3413        if (typeof pollute_ip == 'string') {
3414            pollute_ip = JSON.parse(item.is_pollute_ip);
3415        }
3416        try {
3417            Object.keys(pollute_ip).forEach(key => {
3418                if (pollute_ip[key]) {
3419                    arr['101'].push(item);
3420                    throw new Error("该节点被污染");
3421                }
3422            })
3423        } catch (e) {
3424        }
3425
3426        if (search_type == 'http' || search_type == 'http_overseas') {
3427            //http 状态码异常
3428            if (item.httpCode >= 500) {
3429                arr['500'].push(item);
3430            } else if (item.httpCode > 400) {
3431                arr['400'].push(item);
3432            }
3433        } else if (search_type == 'ping' || search_type == 'ping_overseas' || search_type == 'tcping' || search_type == 'udping') {
3434            //ping 丢包节点
3435            if (item.packet_loss > 0) {
3436                arr['103'].push(item);
3437            }
3438        }
3439    })
3440
3441    Object.keys(arr).forEach(key => {
3442
3443        $(".map-tab-" + key).addClass("hide")
3444        let htm = ``
3445        arr[key] = sortByAscStr(arr[key], 'node_name')
3446        if (arr[key].length > 0) {
3447            arr[key].forEach(item => {
3448                htm += `<span title="${item.origin_from || item.province_name}${item.isp_name}">${item.origin_from || item.province_name}${item.isp_name}</span>`
3449            })
3450            $(".map-tab-" + key).removeClass("hide")
3451        }
3452        $(".map-tab-" + key + " font").text(arr[key].length)
3453        $(".node-error-box-" + key).html(htm)
3454    })
3455
3456}
3457
3458/*设置运营商ISP的切换*/
3459function ispHtml() {
3460    let htm = `<span class="on" data-isp="0">全部<label>${initData.ipPre.length}</label></span>`;
3461    Object.keys(initData.ispCount).forEach(key => {
3462        if(key != 10){
3463            htm += `<span data-isp="${key}">${nodeName[key]}<label>${initData.ispCount[key]}</label></span>`
3464        }
3465    })
3466    htm += `<span data-type="2" data-package="pollute">被污染IP<label>${initData.polluteIpCount}</label></span>`
3467    $(".ip-isp-tab").html(htm)
3468}
3469
3470/*渲染解析IP占比*/
3471function init_isp(data) {
3472    let html = '';
3473    let ipList = [];
3474    let map_data_show_txt = $(".map_data_show span").text();
3475    data.forEach((item, index) => {
3476        let className = '';
3477        if (index >= 18) {
3478            if (map_data_show_txt == '收起全部'){
3479                className = 'show'
3480            }else{
3481                className = 'hide'
3482            }
3483        }
3484        let ipVal = item['ip']
3485        if (ipVal == '') {
3486            if (search_type == 'http' || search_type == 'http_overseas') {
3487                ipVal = '解析失败'
3488            } else {
3489                ipVal = '响应超时'
3490            }
3491        }
3492        html += `<div class="ip-isp-item ${className}">
3493                    <span class="ip_text ${item['is_pollute'] ? 'color-error' : ''}">${ipVal}</span>
3494                    <label>${item['pre']}%</label>
3495                </div>`
3496        ipList.push(item['ip'])
3497    })
3498    if (data.length > 18) {
3499        if (map_data_show_txt == '收起全部'){
3500            html += `<div class="map_data_show">
3501                    <span>收起全部</span> <i class="iconfont iconrate-up"></i>
3502                </div>`
3503        }else{
3504            html += `<div class="map_data_show">
3505                    <span>全部展开</span> <i class="iconfont iconsort_desc"></i>
3506                </div>`
3507        }
3508    }
3509
3510    if ($(".echarts_tabs span.btn-primary").attr("data-type") == "dns") {
3511        $(".echarts_pollute_pre").hide()
3512    } else {
3513        $(".echarts_pollute_pre").show()
3514    }
3515    $(".ipList").html(ipList.join('<br>'));
3516    if (data.length == 0) {
3517        $(".ip-isp-list").html(`<div class="empty-g">暂无数据</div>`);
3518    } else {
3519        $('.ip-isp-list').html(html);
3520    }
3521}
3522
3523/*显示loading*/
3524function loadingOpen() {
3525    if (['wall', 'pollute', 'ssl', 'ipv6', 'whois', 'qq', 'wechat', 'blacklist', 'hijack', 'ip_state', 'web_history', 'ip_whois', 'ip_city'].includes(search_type)) {
3526        $(".loading").show();
3527    }
3528    if (['wall', 'pollute', 'ssl', 'ipv6', 'whois', 'qq', 'wechat', 'blacklist', 'web_history', 'ip_whois', 'ip_state', 'ip_city'].includes(search_type)) {
3529        $(".dataShow").hide();
3530    }
3531    if (['web_history', 'ip_whois'].includes(search_type)) {
3532        $(".error-box").hide();
3533    }
3534
3535    //$(".mapShow,.dataShow,.money_box").hide();
3536    $('.banner_submit').removeClass('action_submit').addClass('stop_test').text('停止检测');
3537    // 移动端不用设置readonly
3538    if (!IsMobile()) {
3539        $(".banner_value").attr("readonly", true)
3540    }
3541
3542}
3543
3544/*关闭loading*/
3545function loadingClose() {
3546    if (['wall', 'pollute', 'ssl', 'ipv6', 'whois', 'qq', 'wechat', 'blacklist', 'hijack','ip_state', 'web_history', 'ip_whois', 'ip_city'].includes(search_type)) {
3547        $(".loading").hide();
3548    }
3549    //$(".loading").hide();
3550    //$(".mapShow,.dataShow,.money_box").show();
3551}
3552
3553/*关闭文字loading*/
3554function testCloseLoading(type=true) {
3555    $('.loading').hide();
3556
3557    timeLogin = false //关闭登录轮询
3558
3559    if (wsCode != -3) {
3560        if (Object.keys(domainArr).length > 1) {
3561            $(".table_cont_more .sourceNodesLen").html(`<span class="color-error">超时</span>`);
3562            if(search_type === "ip_city"){
3563                $(".table_cont_more tr td:last-child").html(`<a href="javascript:;" class="c-primary update-row">更新</a>`)
3564            }else{
3565                $(".table_cont_more tr td:last-child").html(`
3566                    <a href="javascript:;" class="c-primary detail-row mr-8">详情</a><a href="javascript:;" class="c-primary update-row">更新</a>
3567                `)
3568            }
3569            $(".table_cont_more .code4 .detail-row").addClass('disabled').removeClass('detail-row');
3570        } else {
3571            let v6 = $('[name=v6]').val()
3572            if(hand_close_ws || search_type=="http_overseas" || search_type=="ping_overseas" || search_type=="dns_overseas" || (search_type=="http" && v6) || (search_type=="ping" && v6) || (search_type=="tcping" && v6)){
3573                $(".table_cont .loadingRow").parents("tr").remove();
3574            }else{
3575                /*let htm = `<div class="color-error text-error">
3576                                        节点异常
3577                                        <div class="text-error-tips" style="width: 400px">
3578                                            平台原因节点异常,与检测域名/IP无关,不会扣除检测次数与波点
3579                                        </div>
3580                                    </div>`
3581                $(".table_cont .loadingRow").parent("td").html(htm);*/
3582                $(".table_cont .loadingRow").parents("tr").remove();
3583                hand_close_ws=false
3584            }
3585        }
3586
3587        if (wsCode == 0 && $('.banner_submit').text() == '停止检测' && type) {
3588            setTimeout(() => {
3589                Popup.success("已检测结束")
3590            }, 1000)
3591        }
3592    }
3593
3594    $(".spinner-border-tool").hide()
3595    $('.banner_submit').addClass('action_submit').removeClass('stop_test').text('开始检测');
3596    $(".banner_value").attr("readonly", false)
3597    $(".traceroute_loading").text('暂无数据')
3598    $(".tool-progress-bar-inner").text(`100%`).css('width', `100%`)
3599
3600    let hrefData = location.href
3601    if (search_type == "dns" && hrefData.indexOf("mapBox") != -1) {
3602        $(".echarts_tabs span").eq(1).click()
3603    }
3604
3605}
3606
3607/**
3608 * 恢复提交按钮的可点击状态
3609 * 在所有验证失败、异常中断、请求失败等场景下调用,确保用户可以重新点击
3610 */
3611function resetSubmitButton() {
3612    $('.banner_submit').addClass('action_submit').removeClass('stop_test').text('开始检测');
3613}
3614
3615/*获取banner运营商节点数据*/
3616function getIsp(topClassName='.banner-param') {
3617    return $(topClassName + " [name='isp']:checked").map(function() {
3618        return $(this).val();
3619    }).get();
3620}
3621
3622/*获取banner运营商节点数据*/
3623function getContinent(topClassName='.banner-param') {
3624    return $(topClassName + " [name='continent']:checked").map(function() {
3625        return $(this).val();
3626    }).get();
3627}
3628
3629/*获取检测选中的节点*/
3630function getBatchNodeIds() {
3631    return $(".banner-node-list span.on").map(function() {
3632        return $(this).attr('data-id');
3633    }).get();
3634}
3635
3636/*显示单位*/
3637function init_unit(cost, type) {
3638    if (cost == 999 || cost == null) {
3639        return "超时"
3640    }
3641    if (type == 'http' || type == 'http_overseas') {
3642        return cost + 's';
3643    }
3644    return cost + 'ms'
3645}
3646
3647/*获取单位*/
3648function getUnit(type = search_type) {
3649    const unitMap = {
3650        'http': 's',
3651        'http_overseas': 's',
3652        'ping': 'ms',
3653        'tcping': 'ms',
3654        'udping': 'ms',
3655        'dns': 'ms',
3656        'ping_overseas': 'ms',
3657        'dns_overseas': 'ms',
3658    };
3659    return unitMap[type] || ''; // 默认返回空字符串
3660}
3661
3662/*判断url连接格式是否合法*/
3663function isURL(str_url) {
3664    str_url = urlToLowerCase(str_url);
3665    let regular = /^((https|http|ftp|rtsp|mms)?:\/\/)?[A-Za-z0-9_\u4E00-\u9FA5]+[A-Za-z0-9_\u4E00-\u9FA5\.-]*\.[A-Za-z0-9_\u4E00-\u9FA5]{1,}(.*)$/u
3666    if (regular.test(str_url)) {
3667        return true;
3668    } else {
3669        return false;
3670    }
3671}
3672
3673/*IP格式验证*/
3674function checkIp(ip) {
3675    let ipv4 = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
3676    let ipv6 = /((^|:)([0-9a-fA-F]{0,4})){1,8}(:|$)/;
3677    if (ipv4.test(ip) || ipv6.test(ip)) {
3678        return true;
3679    } else {
3680        return false;
3681    }
3682}
3683
3684/*输入框链接格式自动处理*/
3685function urlToLowerCase(url) {
3686    let arr2 = url.split('\n')
3687    arr2.forEach((item, index) => {
3688        item = $.trim(item.replace(/:/g, ':'))
3689        item = item.replace('//', '||')
3690        let arr = item.split('/')
3691        arr[0] = arr[0].toLowerCase()
3692        item = arr.join('/')
3693        arr2[index] = item.replace('||', '//')
3694    })
3695    return arr2.join('\n')
3696}
3697
3698/*颜色初始化*/
3699function init_color(cost, type) {
3700    if(["http","http_overseas","complex"].includes(type)){
3701        if (cost == 0 || cost == null) {
3702            return ''
3703        } else if (cost < 0.5) {
3704            return 'color42dd3f';
3705        } else if (cost >= 0.5 && cost < 1) {
3706            return 'colorbef663';
3707        } else if (cost >= 1 && cost < 3) {
3708            return 'colorf6ed44';
3709        } else if (cost >= 3 && cost < 10) {
3710            return 'colorf69833';
3711        } else if (cost === '*' || cost >= 10) {
3712            return 'colore61610';
3713        }
3714    }else if(["ping_overseas","ping","tcping","udping"].includes(type)){
3715        if (cost == 0 || cost == null) {
3716            return ''
3717        } else if (cost === '*' || cost == 999 || cost >= 100000) {
3718            return 'colore61610';
3719        } else if (cost <= 50) {
3720            return 'color42dd3f';
3721        } else if (cost <= 100) {
3722            return 'colorbef663';
3723        } else if (cost <= 200) {
3724            return 'colorf6ed44';
3725        } else if (cost <= 250) {
3726            return 'colorf6ed44_actual';
3727        } else if (cost < 100000) {
3728            return 'colorf69833';
3729        }
3730    }else if(["dns"].includes(type)){
3731        if (cost == 0 || cost == null) {
3732            return ''
3733        } else if (cost === '*' || cost == 999 || cost >= 100000) {
3734            return 'colore61610';
3735        } else if (cost <= 30) {
3736            return 'color42dd3f';
3737        } else if (cost <= 50) {
3738            return 'colorbef663';
3739        } else if (cost <= 100) {
3740            return 'colorf6ed44';
3741        } else if (cost <= 200) {
3742            return 'colorf6ed44_actual';
3743        } else if (cost < 100000) {
3744            return 'colorf69833';
3745        } else {
3746            return 'colore61610';
3747        }
3748    }
3749}
3750
3751/* 数字排序 升序 */
3752function sortByAscNum(data, field) {
3753    data.forEach((item) => {
3754        if (field == 'error_node') {
3755            item[field + '_sort'] = item[field];
3756        } else {
3757            item[field + '_sort'] = item[field] > 0 ? item[field] : 9999;
3758        }
3759    })
3760    data.sort(function (a, b) {
3761        return a[field + '_sort'] - b[field + '_sort'];
3762    })
3763    return data;
3764}
3765
3766/* 数字排序 降序 */
3767function sortByDescNum(data, field) {
3768    data.forEach((item) => {
3769        item[field + '_sort'] = item[field] > 0 ? item[field] : 0;
3770    })
3771    data.sort(function (a, b) {
3772        return b[field + '_sort'] - a[field + '_sort'];
3773    })
3774    return data;
3775}
3776
3777/* 文字排序 升序 */
3778function sortByAscStr(data, field) {
3779    data.sort(function compareFunction(a, b) {
3780        return a[field].localeCompare(b[field], "zh");
3781    });
3782    return data;
3783}
3784
3785/* 文字排序 降序 */
3786function sortByDescStr(data, field) {
3787    data.sort(function compareFunction(a, b) {
3788        return b[field].localeCompare(a[field], "zh");
3789    });
3790    return data;
3791}
3792
3793/* 文字排序 升序(运营商) */
3794function sortByAscStrIsp(data, field) {
3795    data.sort(function (a, b) {
3796        let ispComparison = a['isp'] - b['isp'];
3797        if (ispComparison !== 0) {
3798            return ispComparison;
3799        }
3800        return a[field].localeCompare(b[field], "zh");
3801    });
3802    return data;
3803}
3804
3805/* 文字排序 升序(地球大洲) */
3806function sortByAscEnglish(data, field) {
3807    data.sort(function compareFunction(a, b) {
3808        const valA = String(a[field]);
3809        const valB = String(b[field]);
3810        return valA.localeCompare(valB, "en");
3811    });
3812    return data;
3813}
3814
3815/* 获取表格选中ID */
3816function getTableId() {
3817    let obj = $(".node-data-tbody input");
3818    let arr = [];
3819    obj.each(function (index, el) {
3820        if (obj.eq(index).prop('checked')) {
3821            arr.push(obj.eq(index).val());
3822        }
3823    });
3824    return arr;
3825}
3826
3827
3828/**
3829 * 获取当前页面的类型
3830 */
3831function getType(){
3832    // 判断当前是否在首页
3833    if (location.pathname == '/') {
3834        // 在首页时,从导航栏中获取当前选中项的data-type属性值
3835        return $(".banner-form-box dd a.on").attr('data-type')
3836    } else {
3837        // 非首页时,直接返回全局变量search_type的值
3838        return search_type
3839    }
3840}
3841
3842
3843/*地图初始化*/
3844function chartMap(type, myData, idName = "http_map") {
3845    map_data=myData
3846    let newData = [];
3847    let splitList = [];
3848    for (let s of myData) {
3849        if (s.value === 0 || s.value === 999) {
3850            let arr = s;
3851            arr.value = 999999;
3852            newData.push(arr);
3853        } else {
3854            newData.push(s);
3855        }
3856    }
3857
3858    // ==============================
3859    // 超时闪电图标:localStorage 开关,默认开启
3860    // ==============================
3861    const timeoutLightningKey = 'map_timeout_lightning_show';
3862    const showTimeoutLightning = localStorage.getItem(timeoutLightningKey) !== '0';
3863
3864    let timeoutProvinceData = [];
3865
3866    if (showTimeoutLightning && type !== 'pollute' && typeof result !== 'undefined') {
3867        let timeoutProvinceMap = {};
3868
3869        result.forEach(item => {
3870            let provinceName = item['province_name'];
3871            let value = item[fieldFun()];
3872
3873            if (provinceName && (value === '*' || value == 999999)) {
3874                timeoutProvinceMap[provinceName] = true;
3875            }
3876        });
3877
3878        // 省份名称转坐标映射
3879        const provinceCoordMap = {
3880            '北京': [116.40, 40.10],
3881            '天津': [117.35, 39.30],
3882            '上海': [121.47, 31.23],
3883            '重庆': [107.90, 30.10],
3884            '河北': [115.20, 38.20],
3885            '河南': [113.60, 33.90],
3886            '云南': [101.90, 24.80],
3887            '辽宁': [122.50, 41.30],
3888            '黑龙江': [127.70, 47.90],
3889            '湖南': [112.30, 27.60],
3890            '安徽': [117.20, 31.10],
3891            '山东': [118.00, 36.40],
3892            '新疆': [84.80, 41.80],
3893            '江苏': [119.20, 32.90],
3894            '浙江': [120.10, 29.20],
3895            '江西': [115.70, 27.60],
3896            '湖北': [112.30, 30.90],
3897            '广西': [108.30, 23.70],
3898            '甘肃': [103.80, 37.90],
3899            '山西': [112.30, 37.60],
3900            '内蒙古': [113.90, 44.10],
3901            '陕西': [108.80, 35.20],
3902            '吉林': [126.20, 43.60],
3903            '福建': [118.00, 26.00],
3904            '贵州': [106.70, 26.80],
3905            '广东': [113.40, 23.40],
3906            '青海': [96.00, 35.60],
3907            '西藏': [88.80, 31.70],
3908            '四川': [102.80, 30.60],
3909            '宁夏': [106.10, 37.30],
3910            '海南': [109.80, 19.20],
3911            '台湾': [120.90, 23.80],
3912            '香港': [114.17, 22.28],
3913            '澳门': [113.54, 22.19]
3914        };
3915
3916        timeoutProvinceData = Object.keys(timeoutProvinceMap)
3917            .filter(name => provinceCoordMap[name])
3918            .map(name => {
3919                return {
3920                    name: name,
3921                    coord: provinceCoordMap[name],
3922                    value: 1
3923                };
3924            });
3925    }
3926
3927    let map_cost = '';
3928    if (type === 'http' || type === 'http_overseas') {
3929        map_cost = 's';
3930    } else if (type === 'pollute') {
3931        map_cost = '';
3932    } else {
3933        map_cost = 'ms';
3934    }
3935
3936    const splitConfigs = {
3937        http: [
3938            {start: 0, end: 0.5, label: '< 0.5s', color: '#24aa1d'},
3939            {start: 0.5, end: 1, label: '0.5s-1s', color: '#42dd3f'},
3940            {start: 1, end: 3, label: '1s-3s', color: '#bef663'},
3941            {start: 3, end: 10, label: '3s-10s', color: '#f69833'},
3942            {start: 10, end: 999999, label: '> 10s', color: '#e61610'}
3943        ],
3944        ping: [
3945            {start: 0, end: 50, label: '<= 50ms', color: '#24aa1d'},
3946            {start: 50, end: 100, label: '50ms-100ms', color: '#42dd3f'},
3947            {start: 100, end: 200, label: '100ms-200ms', color: '#bef663'},
3948            {start: 200, end: 250, label: '200ms-250ms', color: '#f6ed44'},
3949            {start: 250, end: 99999, label: '> 250ms', color: '#f69833'},
3950            {start: 100000, end: 999999, label: '超时', color: '#e61610'}
3951        ],
3952        dns: [
3953            {start: 0, end: 30, label: '<= 30ms', color: '#24aa1d'},
3954            {start: 30, end: 50, label: '30ms-50ms', color: '#42dd3f'},
3955            {start: 50, end: 100, label: '50ms-100ms', color: '#bef663'},
3956            {start: 100, end: 200, label: '100ms-200ms', color: '#f6ed44'},
3957            {start: 200, end: 99999, label: '> 200ms', color: '#f69833'},
3958            {start: 100000, end: 999999, label: '超时', color: '#e61610'}
3959        ],
3960        pollute: [
3961            {start: 1, end: 1, label: '无污染', color: '#24aa1d'},
3962            {start: 2, end: 3, label: '中度污染', color: '#f6ed44'},
3963            {start: 3, end: 9999999, label: '重度污染', color: '#e61610'}
3964        ]
3965    };
3966
3967    const typeMap = {
3968        'http': 'http',
3969        'http_overseas': 'http',
3970        'ping': 'ping',
3971        'tcping': 'ping',
3972        'udping': 'ping',
3973        'dns': 'dns',
3974        'ping_overseas': 'ping',
3975        'dns_overseas': 'dns',
3976        'pollute': 'pollute'
3977    };
3978
3979    splitList = splitConfigs[typeMap[type]] || [];
3980
3981    let option = {
3982        title: {
3983            text: '',
3984            x: 'center'
3985        },
3986        tooltip: {
3987            trigger: 'item',
3988            position: function (point, params, dom, rect, size) {
3989                var x = point[0];
3990                var y = point[1];
3991                // 屏幕宽度
3992                var viewWidth = size.viewSize[0];
3993                var viewHeight = size.viewSize[1];
3994
3995                // tooltip 自身尺寸
3996                var boxWidth = size.contentSize[0];
3997                var boxHeight = size.contentSize[1];
3998
3999                // 防止右侧超出
4000                if (x + boxWidth > viewWidth) {
4001                    x = viewWidth - boxWidth - 10;
4002                }
4003
4004                // 防止左侧超出
4005                if (x < 10) {
4006                    x = 10;
4007                }
4008
4009                // 防止底部超出
4010                if (y + boxHeight > viewHeight) {
4011                    y = viewHeight - boxHeight - 10;
4012                }
4013
4014                // 防止顶部超出
4015                if (y < 10) {
4016                    y = 10;
4017                }
4018
4019                return [x, y];
4020            },
4021            formatter: function (params) {
4022                if (params.name) {
4023                    if (type === 'pollute') {
4024                        let level = '';
4025                        if (params.value == 1) {
4026                            level = '无污染';
4027                        } else if (params.value == 2) {
4028                            level = '中度污染';
4029                        } else {
4030                            level = '重度污染';
4031                        }
4032                        return params.name + ':' + level;
4033                    } else {
4034                        let tis = [];
4035                        result.forEach(item => {
4036                            if (item['province_name'] == params.data.name) {
4037                                tis.push({
4038                                    name: item['node_name'],
4039                                    province_name: item['origin_from'] || item['province_name'],
4040                                    isp_name: item['isp_name'],
4041                                    continent_type: item['continent_type'] || item['area'],
4042                                    isp: item['isp'],
4043                                    value: item[fieldFun()]
4044                                });
4045                            }
4046                        });
4047
4048                        tis = tis.sort(function (a, b) {
4049                            return a['value'] - b['value'];
4050                        });
4051
4052                        let htm = `<p style="background: #128bff; text-align: center; padding: 4px 0; margin-bottom: 4px;">${params.name}</p>`;
4053                        htm += `<div style="padding:4px 8px;"><p style="line-height: 28px;">最快响应:${params.value == '*' || params.value == 999999 ? '<span class="color-error">超时</span>' : params.value + map_cost}</p>`;
4054
4055                        tis.forEach(item => {
4056                            htm += `<p style="line-height: 28px;">
4057                                        <span style="color: ${['http_overseas','ping_overseas','dns_overseas'].includes(type) ? overseas_color[item.continent_type] : isp_color[item.isp]}" class="mr-4">[${['http_overseas','ping_overseas','dns_overseas'].includes(type) ? areaNodeName[item.continent_type] : item.isp_name}]</span>
4058                                        ${item.province_name}:${item.value == '*' || params.value == 999999 ? '<span class="color-error">超时</span>' : item.value + map_cost}
4059                                    </p>`;
4060                        });
4061
4062                        htm += `</div>`;
4063                        return htm;
4064                    }
4065                }
4066            },
4067        },
4068        dataRange: {
4069            min: 0,
4070            max: 99999,
4071            x: '0%',
4072            bottom: '2%',
4073            splitList: splitList,
4074            //color: ['#ef5968', '#ff9661', '#fcd763', '#c4dd58', '#7ecc6e'],
4075            textStyle: {
4076                color: $(".screen-header").length > 0 ? '#fff' : '#333'
4077            }
4078        },
4079        roamController: {
4080            show: true,
4081            x: 'right',
4082            mapTypeControl: {
4083                'china': true
4084            }
4085        },
4086        series: [{
4087            name: '',
4088            type: 'map',
4089            mapType: ['http_overseas','ping_overseas','dns_overseas'].includes(type) ? 'world' : 'china',
4090            mapLocation: {
4091                x: 'center',
4092                y: 'top',
4093            },
4094            hoverable: false,
4095            roam: false,
4096            zoom: 1.25,
4097            itemStyle: {
4098                normal: {
4099                    label: {
4100                        show: false,
4101                        textStyle: {
4102                            color: "#fff"
4103                        }
4104                    },
4105                    borderColor: 'rgb(232,232,232)',
4106                    borderWidth: 1,
4107                    shadowColor: 'rgba(218,218,218,0.5)',
4108                },
4109                emphasis: {
4110                    label: {
4111                        show: true
4112                    },
4113                    areaColor: '#128bff'
4114                }
4115            },
4116            data: newData,
4117            markPoint: {        // 超时省份闪电图标
4118                symbol: 'path://M576 64L192 576h192l-64 384 384-512H512z',
4119                symbolSize: 12,
4120                silent: true,
4121                itemStyle: {
4122                    normal: {
4123                        color: '#ff0000',
4124                        label: {
4125                            show: false
4126                        }
4127                    }
4128                },
4129                data: timeoutProvinceData
4130            }
4131
4132        }]
4133    };
4134
4135    if ($('#' + idName).length) {
4136        if (HTTP_map) {
4137            HTTP_map.setOption(option, true);
4138        } else {
4139            HTTP_map = echarts.init(document.getElementById(idName));
4140            HTTP_map.setOption(option, true);
4141            HTTP_map.on('click', function (params) {
4142                if (params.name) {
4143                    $('html,body').animate({scrollTop: $('.table_cont_one').offset().top}, 300);
4144                    $(".table-select-province .dropdown-item").removeClass("on");
4145                    $(".table-select-province .dropdown-item").each(function () {
4146                        if ($(this).text() == params.name) {
4147                            $(this).click();
4148                        }
4149                    });
4150                    tableParamsData();
4151                }
4152            });
4153        }
4154        HTTP_map.resize();
4155    }
4156}
4157
4158/*第一次请求显示对应的数据*/
4159function init_result_one(result, continut) {
4160    let type = continut || search_type
4161    init_one(result, type);
4162}
4163
4164/*第一次渲染http、ping、tcping、udping、dns、劫持表格数据*/
4165function init_one(result, type) {
4166
4167    let html = "";
4168    let tdHtml = ""
4169    for (let i = 0; i < trlen[type] - 3; i++) {
4170        tdHtml += `<td>-</td>`
4171    }
4172    result = sortByAscNum(result, 'isp');
4173
4174
4175    //海外测速 每个洲只取前15条显示
4176    if(['http_overseas','ping_overseas','dns_overseas'].includes(type)){
4177        let orderBy = {
4178            asia: 1,
4179            north_america: 2,
4180            south_america: 3,
4181            europe: 4,
4182            africa: 5,
4183            oceania: 6,
4184        }
4185        // 按area(洲)分组,把同一个洲的数据归类到一起
4186        let groupedByArea = {};
4187        result.forEach(item => {
4188            item['orderBy']=orderBy[item.continent_type]
4189            const area = item.continent_type;
4190            if (!groupedByArea[area]) {
4191                groupedByArea[area] = [];
4192            }
4193            if(groupedByArea[area].length < 15){
4194                groupedByArea[area].push(item);
4195            }
4196        });
4197
4198        let flatFinalResult = [];
4199        for (const area in groupedByArea) {
4200            flatFinalResult.push(...groupedByArea[area]);
4201        }
4202
4203        result = flatFinalResult;
4204        result = sortByAscNum(result, 'orderBy');
4205    }
4206
4207    result.forEach(item => {
4208        if (type == 'pingContinue') {
4209            html += `<tr class="tr_${item['id']}">
4210                        <td title="${item['province']}">
4211                            <div class="flex-g">
4212                                <span class="resultTag" style="background:${isp_color[item['isp']]}">${item['isp_name']}</span>
4213                                <span class="resultName" style="width: 60px">${item['province']}</span>
4214                            </div>
4215                        </td>
4216                        <td class="address_${item['id']}"><i class="loadingRow"></i></td>
4217                        <td class="loss_${item['id']}">-</td>
4218                        <td class="send_${item['id']}">-</td>
4219                        <td class="last_${item['id']}">-</td>
4220                        <td class="best_${item['id']}">-</td>
4221                        <td class="worst_${item['id']}">-</td>
4222                        <td class="avg_${item['id']}">-</td>
4223                        <td><canvas class="network" id="myCanvas_${item['id']}" width="200" height="20"></canvas></td>
4224                        <td class="sponsor_${item['id']}">${item['sponsor'] ? item['sponsor'] : sponsor}</td>
4225                    </tr>`
4226        } else {
4227            html += `<tr class="${['http_overseas','ping_overseas','dns_overseas'].includes(type) ? 'overseas_' + item.continent_type:'isp_' + item.isp}">
4228                        <td title="${item['province']}">
4229                            <div class="flex-g">
4230                                <span class="resultTag" style="background:${['http_overseas','ping_overseas','dns_overseas'].includes(type) ? overseas_color[item.continent_type]:isp_color[item.isp]}">
4231                                    ${['http_overseas','ping_overseas','dns_overseas'].includes(type) ? item['continent_name']:item['isp_name']}
4232                                </span>
4233                                -
4234                            </div>
4235                        </td>
4236                        <td><i class="loadingRow"></i></td>
4237                        ${tdHtml}
4238                        <td class="sponsor_${item['id']}">-</td>
4239                    </tr>`
4240        }
4241
4242    })
4243
4244    $('.table_cont_one .node-data-tbody').html(html);
4245    footerPosition();
4246}
4247
4248/*判断需要渲染的表格数据*/
4249function init_result(result, st) {
4250    if (result.length == 0) {
4251        $(".node-data-tbody").html(`<tr>
4252                                        <td colspan="${$('.table_cont thead th').length}"><div class="empty-g">暂无数据</div></td>
4253                                    </tr>`);
4254        return;
4255    }
4256    switch (search_type) {
4257        case 'http':
4258            init_http(result, st);
4259            break;
4260        case 'http_overseas':
4261            init_http(result, st);
4262            break;
4263        case 'dns':
4264            init_dns(result, st);
4265            break;
4266        case 'dns_overseas':
4267            init_dns(result, st);
4268            break;
4269        case 'ping':
4270            init_ping(result, st);
4271            break;
4272        case 'ping_overseas':
4273            init_ping(result, st);
4274            break;
4275        case 'tcping':
4276            init_tcping(result, st);
4277            break;
4278        case 'udping':
4279            init_udping(result, st);
4280            break;
4281        case 'hijack':
4282            init_hijack(result, st);
4283            break;
4284        case 'ip_state':
4285            init_ip_state(result, st);
4286            break;
4287    }
4288}
4289
4290function resetErrorIpTxt(item, splitIpColumns = false) {
4291    let ip_txt = ''
4292    const ipLocationCell = splitIpColumns ? '<td class="ip_location">' + (item['ip_from'] || '') + '</td>' : '';
4293    if (item['ip'] == '') {
4294        ip_txt = `<td class="color-error text-error">
4295                            解析失败
4296                            <div class="text-error-tips" style="width: 462px">
4297                                解析失败的原因:<br>
4298                                1、个别网站域名可能受到政策法规的限制,<span class="color-error">被运营商屏蔽或劫持</span>,导致无法获取到其解析IP。BOCE的国内检测节点均<span class="color-error">采用电信、移动、联通的官方云服务器</span>,检测数据能够真实、准确地呈现出国内多数访客访问目标站点的实际状况。<br>
4299                                2、DNS 服务器故障、DNS 缓存问题、域名解析配置错误。
4300                            </div>
4301                        </td>${ipLocationCell}`
4302    } else if (item['ip'] == '0.0.0.0') {
4303        ip_txt = `<td class="text-error">
4304                            0.0.0.0
4305                            <div class="text-error-tips" style="width: 500px">
4306                                1、运营商屏蔽:可能由于政策法规限制,ISP 将域名解析
4306劫持到 0.0.0.0 以限制访问。<br>
4307                                2、网络干扰:如防火墙或安全策略干扰。<br>
4308                                3、域名配置问题:目标域名在 DNS 系统中被设为 0.0.0.0 以屏蔽访问。<br>
4309                                4、DNS 解析异常:上游 DNS 返回了错误的结果。<br>
4310                            </div>
4311                        </td>${ipLocationCell}`
4312    } else if (item['ip'] == '127.0.0.1') {
4313        ip_txt = `<td class="text-error">
4314                            127.0.0.1
4315                            <div class="text-error-tips" style="width: 510px">
4316                                1、运营商屏蔽:可能由于政策法规限制,ISP 将域名解析劫持到 127.0.0.1 以限制访问。<br>
4317                                2、网络干扰:如防火墙或安全策略干扰。<br>
4318                                3、域名配置问题:目标域名在 DNS 系统中被设为 127.0.0.1 以屏蔽访问。<br>
4319                                4、DNS 解析异常:上游 DNS 返回了错误的结果。<br>
4320                            </div>
4321                        </td>${ipLocationCell}`
4322    } else if(!item['ip']) {
4323        ip_txt = `<td>
4324                    <div class="color-error text-error">
4325                        节点异常
4326                        <div class="text-error-tips" style="width: 400px">
4327                            平台原因节点异常,与检测域名/IP无关,不会扣除检测次数与波点
4328                        </div>
4329                    </div>
4330                  </td>${ipLocationCell}`
4331    } else {
4332        ip_txt = `<td>
4333                    <div class="node_info" title="${item['ip']}">
4334                        <span class="ip_text">${item['ip']}</span>${splitIpColumns ? '' : `<span class="ip_from color-999">(${item['ip_from']})</span>`}
4335                    </div>
4336                    <div class="hover-button">
4337
4338                    </div>
4339                  </td>${splitIpColumns ? ipLocationCell : ''}`
4340    }
4341    return ip_txt
4342}
4343
4344/*渲染http表格数据*/
4345function init_http(result, st) {
4346    let html = '';
4347    result.forEach(item => {
4348        if (st) {
4349            html = '';
4350        } else {
4351            html += `<tr class="tr_${item['node_id']}">`;
4352        }
4353
4354        html += `<td title="${item['origin_from']}">
4355                    <div class="flex-g">
4356                        <span class="resultTag" style="background:${search_type === 'http_overseas'?overseas_color[item.continent_type||item.area]:isp_color[item.isp]}">
4357                            ${search_type === 'http_overseas'?areaNodeName[item.continent_type||item.area]:item['isp_name']}
4358                        </span>
4359                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4360                    </div>
4361                 </td>
4362                 ${resetErrorIpTxt(item, ['http', 'http_overseas'].includes(search_type))}
4363                 <td data-code="${item['httpCode']}" class="httpCode ${item['httpCode'] > 400 || item['httpCode'] == '000' ? 'color-error' : ''}">${item['httpCode'] > 0 ? item['httpCode'] : '失败'}</td>
4364                 <td class="${init_color(item['totalCost'], search_type)}">${item['totalCost']}s</td>
4365                 <td>${item['dnsCost']}s</td>
4366                 <td>${item['connCost']}s</td>
4367                 <td  class="noExlTD" align="center">
4368                    <div id="contact_${item['node_id']}" class="hide">
4369                        <p class="flex-g mb-4"><span class="w80">下载时间:</span>${item['dlCost']}s</p>
4370                        <p class="flex-g mb-4"><span class="w80">下载大小:</span>${item['dlSize']}KB</p>
4371                        <p class="flex-g mb-4"><span class="w80">下载速度:</span>${item['speed']}KB/s</p>
4372                        <div class="flex-g"><span class="w80">HEAD:</span><pre>${item['contact']}</pre></div>
4373                    </div>
4374                    <div id="contact_mini_${item['node_id']}" class="hide">
4375                        检测节点:${item['isp_name']}${item['origin_from']}<br>
4376                        解析IP:${item['ip']}<br>
4377                        响应时间:${item['totalCost']}s<br>
4378                        解析IP所在地:${item['ip_from']}<br>
4379                        状态:${item['httpCode']}<br>
4380                        解析时间:${item['dnsCost']}s<br>
4381                        连接时间:${item['connCost']}s<br>
4382                        下载时间:${item['dlCost']}s<br>
4383                        下载大小:${item['dlSize']}KB<br>
4384                        下载速度:${item['speed']}KB/s<br>
4385                    </div>
4386                    <dl class="select select-mini">
4387                        <dt>详情</dt>
4388                        <dd>
4389                            <a href="javascript:;" class="c-primary reTest" data-type="head" data-id="${item['node_id']}" data-sid="${item['session_id']}">详情</a>
4390                            ${search_type === 'http'?`<a href="javascript:;" class="c-primary reTest" data-type="ping" data-id="${item['node_id']}" data-sid="${item['session_id']}">PING</a>
4391                            <a href="javascript:;" class="c-primary reTest" style="display: ${checkIp(getHost()) ? 'none' : 'black'}"  data-type="dns" data-id="${item['node_id']}" data-sid="${item['session_id']}">DNS</a>
4392                            <a href="javascript:;" class="c-primary reTest" data-type="http" data-id="${item['node_id']}" data-sid="${item['session_id']}">HTTP</a>`:''}
4393                        </dd>
4394                    </dl>
4395                    <div data-id="${item['node_id']}" class="color-blue table-row-more">
4396                        详情 <b class="iconfont iconarrow-bottom"></b>
4397                    </div>
4398                </td>
4399                <td class="noExlTD">
4400                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4401                </td>`;
4402
4403        if (st) {
4404            if(search_type==='http_overseas'){
4405                let overseasRowKey = item['continent_type'] || item['area'];
4406                $(`.node-data-tbody tr.overseas_${overseasRowKey}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4407            }else{
4408                $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4409            }
4410        } else {
4411            html += '</tr>';
4412        }
4413    })
4414    if (!st) {
4415        $('.table_cont tbody').html(html);
4416    }
4417
4418}
4419
4420/*渲染ping表格数据*/
4421function init_ping(result, st) {
4422    let html = '';
4423    result.forEach(item => {
4424        if (st) {
4425            html = '';
4426        } else {
4427            html += `<tr class="tr_${item['node_id']}">`;
4428        }
4429
4430        html += `<td title="${item['origin_from']}">
4431                    <div class="flex-g">
4432                        <span class="resultTag" style="background:${search_type === 'ping_overseas'?overseas_color[item.continent_type||item.area]:isp_color[item.isp]}">
4433                            ${search_type === 'ping_overseas'?areaNodeName[item.continent_type||item.area]:item['isp_name']}
4434                        </span>
4435                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4436                    </div>
4437                 </td>
4438                 ${resetErrorIpTxt(item, ['ping', 'ping_overseas'].includes(search_type))}
4439                 <td>${item['packet_loss']}%</td>
4440                 <td>${item['max']}ms</td>
4441                 <td>${item['min']}ms</td>
4442                 <td class="${init_color(item['avg'], search_type)}">${item['avg']}ms</td>
4443                 <td class="noExlTD" align="center">
4444                    <div id="contact_${item['node_id']}" class="hide">
4445                        <p class="flex-g mb-4"><span class="w80">发送:</span>${item['packets']}</p>
4446                        <p class="flex-g mb-4"><span class="w80">接收:</span>${item['received']}</p>
4447                        <p class="flex-g mb-4"><span class="w80">包大小:</span>${item['size']}</p>
4448                        <div class="flex-g"><span class="w80">详情:</span><pre>${item['contact']}</pre></div>
4449                    </div>
4450                    <div id="contact_mini_${item['node_id']}" class="hide">
4451                        检测节点:${item['isp_name']}${item['origin_from']}<br>
4452                        发送:${item['packets']}<br>
4453                        接受:${item['received']}s<br>
4454                        丢包率:${item['packet_loss']}%<br>
4455                        最大时间:${item['max']}ms<br>
4456                        最小时间:${item['min']}ms<br>
4457                        包大小:${item['size']}<br>
4458                    </div>
4459                    <dl class="select select-mini">
4460                        <dt>详情</dt>
4461                        <dd>
4462                            <a href="javascript:;" class="c-primary reTest" data-type="head" data-id="${item['node_id']}" data-sid="${item['session_id']}">详情</a>
4463                            ${search_type === 'ping'?`<a href="javascript:;" class="c-primary reTest" data-type="http" data-id="${item['node_id']}" data-sid="${item['session_id']}">HTTP</a>
4464                            <a href="javascript:;" class="c-primary reTest" style="display: ${checkIp(getHost()) ? 'none' : 'black'}"  data-type="dns" data-id="${item['node_id']}" data-sid="${item['session_id']}">DNS</a>
4465                            <a href="javascript:;" class="c-primary reTest" data-type="ping" data-id="${item['node_id']}" data-sid="${item['session_id']}">PING</a>`:''}
4466                        </dd>
4467                    </dl>
4468                    <div data-id="${item['node_id']}" class="color-blue table-row-more">
4469                        详情 <b class="iconfont iconarrow-bottom"></b>
4470                    </div>
4471                </td>
4472                <td class="noExlTD">
4473                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4474                </td>`;
4475
4476        if (st) {
4477            if(search_type==='ping_overseas'){
4478                let overseasRowKey = item['continent_type'] || item['area'];
4479                $(`.node-data-tbody tr.overseas_${overseasRowKey}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4480            }else{
4481                $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4482            }
4483        } else {
4484            html += '</tr>';
4485        }
4486    })
4487
4488    if (!st) {
4489        $('.table_cont tbody').html(html);
4490    }
4491}
4492
4493/*渲染tcping表格数据*/
4494function init_tcping(result, st) {
4495    let html = '';
4496    result.forEach(item => {
4497        if (st) {
4498            html = '';
4499        } else {
4500            html += `<tr class="tr_${item['node_id']}">`;
4501        }
4502
4503        html += `<td title="${item['origin_from']}">
4504                    <div class="flex-g">
4505                        <span class="resultTag" style="background:${isp_color[item['isp']]}">${item['isp_name']}</span>
4506                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4507                    </div>
4508                 </td>
4509                 ${resetErrorIpTxt(item, search_type === 'tcping')}
4510                 <td>${item['packet_loss']}%</td>
4511                 <td>${item['max']}ms</td>
4512                 <td>${item['min']}ms</td>
4513                 <td class="${init_color(item['avg'], search_type)}">${item['avg']}ms</td>
4514                 <td class="noExlTD" align="center">
4515                    <div id="contact_${item['node_id']}" class="hide">
4516                        <p class="flex-g mb-4"><span class="w80">发送:</span>${item['packets']}</p>
4517                        <p class="flex-g mb-4"><span class="w80">接收:</span>${item['received']}</p>
4518                        <p class="flex-g mb-4"><span class="w80">包大小:</span>${item['size']}</p>
4519                        <div class="flex-g"><span class="w80">详情:</span><pre>${item['contact']}</pre></div>
4520                    </div>
4521                    <div id="contact_mini_${item['node_id']}" class="hide">
4522                        检测节点:${item['isp_name']}${item['origin_from']}<br>
4523                        发送:${item['packets']}<br>
4524                        接受:${item['received']}s<br>
4525                        丢包率:${item['packet_loss']}%<br>
4526                        最大时间:${item['max']}ms<br>
4527                        最小时间:${item['min']}ms<br>
4528                        包大小:${item['size']}<br>
4529                    </div>
4530                    <dl class="select select-mini">
4531                        <dt>详情</dt>
4532                        <dd>
4533                            <a href="javascript:;" class="c-primary reTest" data-type="head" data-id="${item['node_id']}" data-sid="${item['session_id']}">详情</a>
4534                            <a href="javascript:;" class="c-primary reTest" data-type="http" data-id="${item['node_id']}" data-sid="${item['session_id']}">HTTP</a>
4535                            <a href="javascript:;" class="c-primary reTest" style="display: ${checkIp(getHost()) ? 'none' : 'black'}" data-type="dns" data-id="${item['node_id']}" data-sid="${item['session_id']}">DNS</a>
4536                            <a href="javascript:;" class="c-primary reTest" style="display: none" data-type="tcping" data-id="${item['node_id']}" data-sid="${item['session_id']}">TCPing</a>
4537                        </dd>
4538                    </dl>
4539                    <div data-id="${item['node_id']}" class="color-blue table-row-more">
4540                        详情 <b class="iconfont iconarrow-bottom"></b>
4541                    </div>
4542                </td>
4543                <td class="noExlTD">
4544                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4545                </td>`;
4546
4547        if (st) {
4548            $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4549        } else {
4550            html += '</tr>';
4551        }
4552    })
4553
4554    if (!st) {
4555        $('.table_cont tbody').html(html);
4556    }
4557}
4558
4559/*渲染udping表格数据*/
4560function init_udping(result, st) {
4561    let html = '';
4562    result.forEach(item => {
4563        if (st) {
4564            html = '';
4565        } else {
4566            html += `<tr class="tr_${item['node_id']}">`;
4567        }
4568
4569        html += `<td title="${item['origin_from']}">
4570                    <div class="flex-g">
4571                        <span class="resultTag" style="background:${isp_color[item['isp']]}">${item['isp_name']}</span>
4572                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4573                    </div>
4574                 </td>
4575                 ${resetErrorIpTxt(item)}
4576                 <td>${item['packet_loss']}%</td>
4577                 <td>${item['max']}ms</td>
4578                 <td>${item['min']}ms</td>
4579                 <td class="${init_color(item['avg'], search_type)}">${item['avg']}ms</td>
4580                 <td class="noExlTD" align="center">
4581                    <div id="contact_${item['node_id']}" class="hide">
4582                        <p class="flex-g mb-4"><span class="w80">发送:</span>${item['packets']}</p>
4583                        <p class="flex-g mb-4"><span class="w80">接收:</span>${item['received']}</p>
4584                        <p class="flex-g mb-4"><span class="w80">包大小:</span>${item['size']}</p>
4585                        <div class="flex-g"><span class="w80">详情:</span><pre>${item['contact']}</pre></div>
4586                    </div>
4587                    <dl class="select select-mini">
4588                        <dt>详情</dt>
4589                        <dd>
4590                            <a href="javascript:;" class="c-primary reTest" data-type="head" data-id="${item['node_id']}" data-sid="${item['session_id']}">详情</a>
4591                            <a href="javascript:;" class="c-primary reTest" data-type="http" data-id="${item['node_id']}" data-sid="${item['session_id']}">HTTP</a>
4592                            <a href="javascript:;" class="c-primary reTest" style="display: ${checkIp(getHost()) ? 'none' : 'black'}"  data-type="dns" data-id="${item['node_id']}" data-sid="${item['session_id']}">DNS</a>
4593                            <a href="javascript:;" class="c-primary reTest" style="display: none" data-type="udping" data-id="${item['node_id']}" data-sid="${item['session_id']}">UDPing</a>
4594                        </dd>
4595                    </dl>
4596                </td>
4597                <td class="noExlTD">
4598                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4599                </td>`;
4600
4601        if (st) {
4602            $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4603        } else {
4604            html += '</tr>';
4605        }
4606    })
4607
4608    if (!st) {
4609        $('.table_cont tbody').html(html);
4610    }
4611}
4612
4613/*渲染dns表格数据*/
4614function init_dns(result, st) {
4615    let html = '';
4616    result.forEach(item => {
4617        if (st) {
4618            html = '';
4619        } else {
4620            html += `<tr class="tr_${item['node_id']}">`;
4621        }
4622        html += `<td title="${item['origin_from']}">
4623                    <div class="flex-g">
4624                        <span class="resultTag" style="background:${search_type === 'dns_overseas'?overseas_color[item.continent_type||item.area]:isp_color[item.isp]}">
4625                            ${search_type === 'dns_overseas'?areaNodeName[item.continent_type||item.area]:item['isp_name']}
4626                        </span>
4627                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4628                    </div>
4629                 </td>
4630                 <td class="${init_color(item['query_time'], search_type)}">${item['query_time']}ms</td>
4631                 <td class="text-error">`;
4632        if (item['ips'] && item['ips'].length > 0) {
4633            for (var i = 0; i < item['ips'].length; i++) {
4634
4635                let ht = ``
4636                if (item['ips'][i].indexOf('0.0.0.0') != -1) {
4637                    ht = `<div class="text-error-tips" style="width: 500px">
4638                                1、运营商屏蔽:可能由于政策法规限制,ISP 将域名解析劫持到 0.0.0.0 以限制访问。<br>
4639                                2、网络干扰:如防火墙或安全策略干扰。<br>
4640                                3、域名配置问题:目标域名在 DNS 系统中被设为 0.0.0.0 以屏蔽访问。<br>
4641                                4、DNS 解析异常:上游 DNS 返回了错误的结果。<br>
4642                            </div>`
4643                } else if (item['ips'][i].indexOf('127.0.0.1') != -1) {
4644                    ht = `<div class="text-error-tips" style="width: 510px">
4645                                1、运营商屏蔽:可能由于政策法规限制,ISP 将域名解析劫持到 127.0.0.1 以限制访问。<br>
4646                                2、网络干扰:如防火墙或安全策略干扰。<br>
4647                                3、域名配置问题:目标域名在 DNS 系统中被设为 127.0.0.1 以屏蔽访问ã€
4647‚<br>
4648                                4、DNS 解析异常:上游 DNS 返回了错误的结果。<br>
4649                            </div>`
4650                }
4651
4652                if (item.hasOwnProperty("is_pollute_ip")) {
4653                    let is_pollute_ip = JSON.parse(JSON.stringify(item['is_pollute_ip']))
4654                    html += `<span class="ip_text ${is_pollute_ip[item['addIps'][i]] ? "color-error" : ""}">
4655                                ${item['ips'][i]}
4656                                ${is_pollute_ip[item['addIps'][i]] ? "(被污染)" : ""}
4657                            </span>${ht}<br />`;
4658                } else {
4659                    html += `<span class="ip_text">${item['ips'][i]}</span>${ht}<br />`;
4660                }
4661            }
4662        } else if (item['contact'].indexOf("no servers could be reached") !== -1) {
4663            html += `<span class="color-error">DNS服务器未响应</span>`
4664        } else {
4665            html += `<span class="color-error">没有记录</span>`
4666        }
4667
4668        html += '</td>';
4669        html += `<td>${item['dns'] || '-'}</td>
4670                 <td  class="noExlTD" align="center">
4671                    <div id="contact_${item['node_id']}" class="hide"><pre>${item['contact']}</pre></div>
4672                    <dl class="select select-mini">
4673                        <dt>详情</dt>
4674                        <dd>
4675                            <a href="javascript:;" class="c-primary reTest" data-type="head" data-id="${item['node_id']}" data-sid="${item['session_id']}">详情</a>
4676                            ${search_type === 'dns'?`<a href="javascript:;" class="c-primary reTest" data-type="http" data-id="${item['node_id']}" data-sid="${item['session_id']}">HTTP</a>
4677                            <a href="javascript:;" class="c-primary reTest" data-type="ping" data-id="${item['node_id']}" data-sid="${item['session_id']}">PING</a>
4678                            <a href="javascript:;" class="c-primary reTest" data-type="dns" data-id="${item['node_id']}" data-sid="${item['session_id']}">DNS</a>`:''}
4679                        </dd>
4680                    </dl>
4681                </td>
4682                <td class="noExlTD">
4683                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4684                </td>`
4685        if (st) {
4686            if(search_type==='dns_overseas'){
4687                let overseasRowKey = item['continent_type'] || item['area'];
4688                $(`.node-data-tbody tr.overseas_${overseasRowKey}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4689            }else{
4690                $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4691            }
4692        } else {
4693            html += '</tr>';
4694        }
4695    })
4696
4697    if (!st) {
4698        $('.table_cont tbody').html(html);
4699    }
4700}
4701
4702/*渲染劫持表格数据*/
4703function init_hijack(result, st) {
4704    let html = '';
4705    result.forEach(item => {
4706        if (st) {
4707            html = '';
4708        } else {
4709            html += `<tr class="tr_${item.node_id}">`;
4710        }
4711
4712        html += `<td>
4713                    <div class="flex-g">
4714                        <span class="resultTag" style="background:${isp_color[item['isp']]}">${item['isp_name']}</span>
4715                        <span class="resultName">${item['origin_from'] || item['province_name']}</span>
4716                    </div>
4717                 </td>
4718                 <td class="ip_text">${item['remote_ip']}</td>
4719                 <td class="ip_from">${item['remote_ip_from']}</td>`;
4720
4721        if (item['http_code'] < 0) {
4722            const msgTxt = {
4723                '-100': 'RST注入劫持',
4724                '-101': 'HTTP响应注入',
4725                '-102': 'DNS污染',
4726                '-103': 'DNS伪造响应',
4727                '-104': '连接被重置',
4728                '-105': '网络不可达',
4729                '-106': 'TCP连接失败',
4730                '-107': 'TLS握手失败',
4731                '-108': 'TLS连接异常',
4732                '-109': 'DNS可疑',
4733            }
4734            html += `<td class="type_error">
4735                    ${msgTxt[item['http_code']]?`<a href="/help/7558.html" target="_blank" class="color-error">${msgTxt[item['http_code']]}</a>`:`<span class="color-999">无法访问</span>`}
4736                    </td>`;
4737
4738        } else if (item['hijack_new'] == -1) {
4739            html += `<td class="type_error">
4740                        <span class="color-error">异常</span>
4741                    </td>`;
4742        } else if (item['remote_ip'] == '' || item['http_code'] == 0) {
4743            html += `<td class="type_null">
4744                        <span class="color-999">无法访问</span>
4745                    </td>`;
4746        } else if (item['hijack_new'] == 1) {
4747            html += '<td class="color-success type_true">正常</td>';
4748        } else if (item['hijack_new'] == 2) {
4749            html += '<td class="type_301">301跳转</td>';
4750        }
4751
4752        let hijack_url = 'javascript:;';
4753        if (item['hijack_new'] != 1) {
4754            hijack_url = item['url'];
4755        }
4756
4757        html += `<td><span>${item['hijack_new'] != 1 ? item['url'] : '-'}</span></td>
4758                 <td class="noExlTD">
4759                    ${item['sponsor'] ? item['sponsor'] : sponsor}
4760                 </td>`
4761
4762        if (st) {
4763            $(`.node-data-tbody tr.isp_${item['isp']}`).eq(item['index']).addClass(`tr_${item['node_id']}`).html(html)
4764        } else {
4765            html += '</tr>';
4766        }
4767    })
4768    if (!st) {
4769        $('.table_cont tbody').html(html);
4770    }
4771
4772    $(".hijackError").text($(".type_error").length)
4773    $(".hijackSuccess").text($(".type_true").length)
4774    $(".hijackNull").text($(".type_null").length)
4775    $(".hijack301").text($(".type_301").length)
4776}
4777
4778/*渲染IP段链接表格数据*/
4779function init_ip_state(result, st) {
4780    let html = '';
4781    result = sortByAscNum(result, 'isp');
4782    result.forEach(item => {
4783        html = `<tr class="tr_${item.node_id}">
4784                    <td>${item['ip']}</td>
4785                    <td>${item['ip_from']}</td>
4786                    <td>在 <span style="color:${isp_color[item['isp']]} ">${item['node_name']}</span> 节点状态疑似异常</td>
4787                </tr>`;
4788        $('.node-data-tbody').append(html);
4789    })
4790}
4791
4792/*渲染路由表格数据*/
4793function init_traceroute(result) {
4794    result.forEach(item => {
4795        let html = '';
4796        let dataRs = item['records'];
4797        $.each(dataRs, function (i) {
4798            html += `<tr>
4799                        <td class="tc">${dataRs[i]['count']}</td>
4800                        <td>${dataRs[i]['ip']}</td>
4801                        <td>${dataRs[i]['dns_name'] || ''}</td>
4802                        <td>${dataRs[i]['ip_from']}</td>
4803                        <td>${dataRs[i]['asn']}</td>
4804                        <td>${dataRs[i]['loss']}%</td>
4805                        <td>${dataRs[i]['wrst']}ms</td>
4806                        <td>${dataRs[i]['best']}ms</td>
4807                        <td>${dataRs[i]['avg']}ms</td>
4808                    </tr>`;
4809        });
4810        if (dataRs.length == 0) {
4811            html = `<tr><td colspan='9' class='color-999' style='text-align: center;padding: 16px;font-size: 14px;'>暂无数据</td></tr>`;
4812        }
4813        $('.traceroute_tbody_' + item['node_id']).html(html);
4814    })
4815    footerPosition()
4816}
4817
4818
4819var timeLogin = true
4820var qrcode = {
4821    url: "",
4822    scene: "",
4823    expire: 0,
4824}
4825
4826function getQrcode() {
4827    $(".qrcode-box").addClass('loading-g')
4828    $.post(`/apiHtml/oauth/get`, function (res) {
4829        if (res.code == 0) {
4830            $(".qrcode-box").removeClass('loading-g')
4831            qrcode = res.data;
4832            $('.qrcode-img').attr('src', qrcode.url);
4833            timeLogin = true
4834            startCount()
4835            startCheckLogin()
4836        } else {
4837            Popup.warning(res.message);
4838        }
4839    })
4840}
4841
4842function startCount() {
4843    if (qrcode.expire === 0) {
4844        $(".qrcode-expire").show()
4845        $(".qrcode-img").hide()
4846        return
4847    }
4848    $(".qrcode-expire").hide()
4849    $(".qrcode-img").show()
4850    var clearT = setTimeout(() => {
4851        qrcode.expire--
4852        startCount()
4853    }, 1000)
4854}
4855
4856function startCheckLogin() {
4857    if (qrcode.expire < 1) {
4858        return false;
4859    }
4860    if (!timeLogin) {
4861        return false;
4862    }
4863    $.post(`/apiHtml/oauth/checkLogin`, {
4864        scene: qrcode.scene
4865    }, function (res) {
4866        if (res.code == 0) {
4867            checkLogin(res.data)
4868        } else {
4869            Popup.warning(res.message);
4870        }
4871    })
4872}
4873
4874function checkLogin(data) {
4875    const {status, first} = data
4876    let getItem = {}
4877    if (localStorage.getItem("boceAdmin_setting")) {
4878        getItem = JSON.parse(localStorage.getItem("boceAdmin_setting"))
4879    }
4880    getItem['loginRedirect'] = window.location.pathname
4881    getItem['loginRedirectType'] = 'front'
4882    // 直接登录
4883    if (status === 0) {
4884        if (first) {
4885            iframePopupOpen({
4886                url: `/user/#/simulate-login?link=1&firstLogin=1`,
4887            });
4888        } else {
4889            iframePopupOpen({
4890                url: `/user/#/simulate-login?link=1`,
4891            });
4892        }
4893        localStorage.setItem("boceAdmin_setting", JSON.stringify(getItem));
4894        return
4895    }
4896    // 需要绑定账号
4897    if (status === 3) {
4898        getItem['wechatLoginInit'] = true
4899        localStorage.setItem("boceAdmin_setting", JSON.stringify(getItem));
4900        iframePopupOpen({
4901            url: `/user/#/wx-bind?link=1&scene=${qrcode.scene}`,
4902        });
4903        qrcode.expire = 0
4904        return
4905    }
4906    setTimeout(() => {
4907        startCheckLogin()
4908    }, 1000)
4909}
4910
4911function updateHistoryState(url) {
4912    history.replaceState(null, null, url);
4913}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.