minerva-online assistant

此脚本能更方便使用minerva-online平台,可在顶端菜单栏右下角的按钮处设置功能开关,并查看功能详情

当前为 2023-04-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         minerva-online assistant
// @namespace    https://space.bilibili.com/17846288
// @version      3.0.7
// @license      MIT
// @description  此脚本能更方便使用minerva-online平台,可在顶端菜单栏右下角的按钮处设置功能开关,并查看功能详情
// @author       inoki
// @match        https://www.minerva-online.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      fanyi.baidu.com
// @run-at       document-start
// @noframes
// ==/UserScript==

/* VersionInfo 企业微信文档:https://doc.weixin.qq.com/doc/w2_AOMADQamAG8fAzy6aF1RWWmEc2ZhG?scode=AMwAwgcrABEDCapPcV
外观效果:优化下拉选择框的夜间模式效果
附件下载:与alias=inoki.va页面联动优化效果
*/

/*jshint esversion: 9*/

(()=>{
    'use strict';


    const SET={
        0:{
            'id':0,
            'name':'置顶置底',
            'func':()=>{GOTOPBOTTOM();},
            'unfunc':()=>{unGOTOPBOTTOM();},
            'detail':
            `在平台域名所有可滚动页面生效,页面右下方添加【∨】/【∧】按钮<br>
            【∨】/【∧】左键点击会根据页面滚动方向自动置顶或置底,按钮样式可在代码中自定义中修改<br>
            【∨】/【∧】右键点击会在左侧生成【>】按钮,再次右键点击会删除【>】图标<br>
            【>】生成时会记录当前页面位置,点击【>】将回到所记录的页面位置`,
            'switch':1
        },
        1:{
            'id':1,
            'name':'菜单遮罩',
            'func':()=>{COVERMENU();},
            'unfunc':()=>{unCOVERMENU();},
            'detail':
            `在有顶端菜单栏的页面生效<br>
            让菜单栏需要点击一次后才可展开,防止鼠标经过时误触<br>
            (默认关闭)`,
            'switch':0
        },
        2:{
            'id':2,
            'name':'附件下载',
            'func':()=>{DOWNLOADFILE();},
            'unfunc':()=>{unDOWNLOADFILE();},
            'detail':
            `在问卷管理页面生效,每份报告前添加【↓】按钮<br>
            【↓】点击可加载附件列表<br>
            【√】点击可下载全部附件,之后会变为【〇】<br>
            【×】点击可关闭附件列表<br>
            附件名点击可下载单个附件,鼠标悬停可预览图片<br>
            【删除全部附件】点击可将此报告全部附件标记为删除`,
            'switch':1
        },
        3:{
            'id':3,
            'name':'扣分标记',
            'func':()=>{MARKQUESTION();},
            'unfunc':()=>{unMARKQUESTION();},
            'detail':
            `在单店报告页面生效,可醒目标记扣分或N/A的题目,方便快速检查相关题评论<br>
            将题目中勾选扣分和N/A项标色,选项更改后需保存报告才会刷新标记<br>
            可在上方设置扣分(默认为红)和N/A(默认为绿)的标记颜色,点击【√】保存更改<br>
            颜色更改后关闭再开启此功能可在报告页面即时刷新颜色<br>
            星期选项与日期不匹配时也将以扣分颜色标记,匹配时将在后方显示绿色√`,
            'switch':1
        },
        4:{
            'id':4,
            'name':'评论编辑',
            'func':()=>{COMMENTEDIT();},
            'unfunc':()=>{unCOMMENTEDIT();},
            'detail':
            `在单店报告页面生效,右下方【问卷图标】<img src=https://www.minerva-online.com/images/icons/menu/x16/survet.png>按钮展开操作界面<br>
            使用前请注意阅读操作界面最上方的【点击获取提示】<br>
            【匹配/替换内容】框内输入内容将即时显示匹配的评论框数,并标灰评论框且在上方标记^^,鼠标悬停灰色评论框可预览替换后内容<br>
            【匹配内容】支持正则表达式(详见【点击获取提示】),可Ctrl+F使用浏览器自带功能搜索^^标记,以快速定位匹配到的评论框<br>
            【一键替换】点击可将所有匹配到的评论框内容修改为替换后内容,此时鼠标悬停灰色评论框可预览修改前内容<br>
            【首字母大写】点击可智能将所有句首英文字母变为大写,显示修改过的评论框数并标灰,此时鼠标悬停灰色评论框可预览修改前内容<br>
            【评论翻译】点击会调用百度翻译,在每个评论框下方输出目标语言翻译,点击↑可将下方内容添加至评论框`,
            'switch':1
        },
        5:{
            'id':5,
            'name':'验证输出',
            'func':()=>{VERIFYEXPORT();},
            'unfunc':()=>{unVERIFYEXPORT();},
            'detail':
            `在问卷管理页面生效,表头上方添加【验证输出勾选的报告】按钮<br>
            【验证输出勾选的报告】点击并确认后会验证输出当前页面勾选的所有报告,成功输出的报告下方小窗口会显示绿色提示<br>
            (电脑配置较低时一次输出太多份可能导致页面卡死,请根据浏览器最多同时能开几个报告页面量力而行,默认关闭)`,
            'switch':0
        },
        6:{
            'id':6,
            'name':'定制汇总',
            'func':()=>{CUSTOMROLLUP();},
            'unfunc':()=>{unCUSTOMROLLUP();},
            'detail':
            `在定制汇总页面生效,在汇总表格上方添加功能按钮<br>
            【复制表格】点击可一键复制表格全部内容,方便复制到excel等软件中编辑<br>
            【复制表格】右侧下拉框选择“分数后+%”时,仅在Pivot table界面下生效,点击【复制表格】执行复制前会为所有数据后添加%<br>
            【精确Pts%】点击可在表格右侧添加一列Pts/PtsOf的比值,并根据右侧下拉框选择的数字,进行相应小数位数的四舍五入<br>
            【精确Pts%】需要Pts和PtsOf列同时存在才能正常生效,用以避免默认Pts%的2位小数舍入可能造成的偏差<br>
            【选项统计】点击可自动统计各架构各题选项的数量与占比,并在表格下方的新增行中展示(QuestionText前[AD]标识),百分比值根据左侧下拉框数字四舍五入<br>
            【选项统计】需要QuestionText和AnswerText和#Surveys列同时存在才能正常生效,数量显示在末尾括号内,百分比值显示在#Survsys格<br>
            【选项统计】参与统计的仅为QuestionText/AnswerText左侧非隐藏列和非隐藏行,可在隐藏不必要的列或行后重新点击按钮刷新统计<br>
            【选项统计】在点击问题选项最右侧按钮<img src=https://www.minerva-online.com/images/icons/filtersv2/answers.png style="object-position:-16px 0;object-fit:none;width:16px;height:16px">加载选项后,可在统计中显示数量为0的选项`,
            'switch':1
        },
        7:{
            'id':7,
            'name':'报告存档',
            'func':()=>{SURVEYSAVES();},
            'unfunc':()=>{unSURVEYSAVES();},
            'detail':
            `在单店报告页面生效,右下方【书本图标】<img src=https://www.minerva-online.com/images/icons/menu/x16/KB-icon.png>按钮展开操作界面,可查看自动/手动存档列表<br>
            【存档】点击可进行手动存档,每次对报告内容进行修改时,将在本地进行自动存档<br>
            【预览】点击可查看存档内容,并对需要读档写入的题目进行勾选<br>
            【读档】点击可将选中的存档全部内容写入到当前报告,或只写入预览界面勾选的题目<br>
            【删除】点击可删除选中的存档,自动/手动存档上限各为10个,超出时自动删除此类最早存档<br>
            (“评论编辑”功能造成的修改不会触发自动存档,可在修改后点击任意评论框触发自动存档)`,
            'switch':1
        },
        /*
        8:{
            'id':8,
            'name':'PDF命名',
            'func':()=>{PDFRENAME();},
            'unfunc':()=>{unPDFRENAME();},
            'detail':
            `在“以PDF格式下载”的转化页面生效,在高级页添加【命名并下载(全部)】按钮<br>
            可在下方FIle Name处自定义命名格式,在下拉框选择需要的命名元素(无须点merge)<br>
            【命名并下载】点击可下载单个PDF,并按File Name处的自定义命名格式命名<br>
            【命名并下载全部】点击相当于一键点击了所有【命名并下载】<br>
            (默认关闭,因为平台已于<a target=_blank href=https://www.minerva-online.com/document.asp?alias=knowledgebase#/article/005bcf16-8530-4437-b4a4-b671ad3db56f>2022.10更新</a>中支持了中文命名)`,
            'switch':0
        },
        */
        9:{
            'id':9,
            'name':'优化表头',
            'func':()=>{BETTERTHEAD();},
            'unfunc':()=>{unBETTERTHEAD();},
            'detail':
            `在所有含有页面滚动时自动冻结表头功能的页面生效(例:问卷管理)<br>
            点击表头上方【标题】行可显示表格所有列的表头内容,根据其勾选状态与否可显示/隐藏对应列<br>
            优化冻结表头的表现,使冻结的表头不再像原先那样闪烁,且在冻结状态下也能执行排序/筛选功能<br>
            (优化冻结表头在部分浏览器可能不支持,若无效建议使用最新版chrome/edge浏览器体验)`,
            'switch':1
        },
        10:{
            'id':10,
            'name':'外观效果',
            'func':()=>{CSSEFFECT();},
            'unfunc':()=>{unCSSEFFECT();},
            'detail':
            `对所有网页外观效果进行调整<br>
            【夜间模式】开启后整体页面主色调变为黑色,在某些场景下更护眼<br>
            (目前夜间模式为较粗略的反色处理,如有视觉效果差的地方请反馈,后续将调整)<br>
            【隐藏logo】开启后隐藏左上角Minerva&Co的logo区域,节省页面空间`,
            'switch':1
        },
    };
    unsafeWindow.MA_SET=SET;


    //先执行外观效果功能
    var style;
    if(GM_getValue(SET[10].name,SET[10].switch)) SET[10].func();

    //DOM加载后开始执行其余功能
    var $;
    document.addEventListener('DOMContentLoaded',()=>{
        //filemanager页面不执行
        if(document.location.href.includes('alias=filemanager')){
            unsafeWindow.userIsEnterpriseAdmin=true;
            return;
        }
        //如网页无jQuery或版本低于1.7则引入1.8.2
        $=unsafeWindow.jQuery;
        try{
            console.log($.fn.jquery);
            $().on();//jQuery 1.7版本后才有$().on()
            init();
        }catch(e){
            const jq=document.createElement('script');
            jq.src='/lib/jquery/jquery-1.8.2.min.js';//'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js';
            document.head.appendChild(jq);
            jq.onload=()=>{
                $=unsafeWindow.jQuery;
                init();
            };
        }
        collaborationEnterpriseAdmin();
    });

    //用于打印脚本简介
    unsafeWindow.MA_logInfo=()=>{
        let info='',i=0;
        for(let s in SET){
            info+=++i+' '+SET[s].name+':\n';
            info+=SET[s].detail.replaceAll(/ {2,}/g,'').replaceAll('<br>','');
            info+='\n\n';
        }
        console.log(info);
        return info;
    };


    /*在顶端菜单栏添加MOassist设置按钮*/
    function init(){
        console.log('jQuery',$.fn.jquery);
        for(let i in SET) if(GM_getValue(SET[i].name,SET[i].switch)) SET[i].func();//执行开启的功能
        const menu=$('div#menu');
        if(menu.length){
            menu.find('ul.tools').append(`
            <li class="MOassist">
                <a class="toolsLink">
                    <div class="iconTools" style="background: url('/images/icons/menu/x16/tools-settings.png')"></div>
                    <ul class="textArea" style="visibility:visible; display:none">
                        <li>MO助手设置</li>
                    </ul>
                    <ul id="MOoption" class="innerItemFirst" style="z-index: 11; display:none; top:25px; right:0px"></ul>
                </a>
            </li>
            `);
            menu.find('li.MOassist').on('click',function(){
                MOListSwitch($(this).find('ul#MOoption'));
            }).find('ul#MOoption').on('click',e=>{
                e.stopPropagation();//让之后添加的功能列表不继承click事件
            });
            mouseHover(menu.find('li.MOassist'));
        }
    }

    //功能列表开关
    function MOListSwitch(ul){
        if(ul.css('display')==='none'){
            if(!ul.children().length) initOptions($('div#menu'))
            ul.stop().slideDown(200);
        }else{
            ul.stop().slideUp(200);
        }
    }

    //导入所有功能列表并显示开关状态
    function initOptions(menu){
        for(let i in SET){
            menu.find('ul#MOoption').append(`
            <li id="MOoptions" class="MOassist" style="width:100%">
                <div class="menuItemText" style="color:#4C5057">${SET[i].name}</div>
                <input type=checkbox id=${SET[i].id} class=menuIconSmall>
                <ul class="textArea" style="visibility:visible; display: none; margin-top:0px; right:120px">
                    <li style="padding:0px 5px !important">${SET[i].detail}</li>
                </ul>
            </li>`);
            if(GM_getValue(SET[i].name,SET[i].switch)) menu.find('input#'+SET[i].id).prop('checked',true);//打勾开启状态的功能
        }
        //根据是否选中即时启用或卸载功能并记录开关状态
        menu.find('li#MOoptions').on('click',function(e){
            const checkbox=$(this).children('input:checkbox');
            const id=$(checkbox).attr('id');
            if(GM_getValue(SET[id].name,SET[id].switch)){
                SET[id].unfunc();
                $(checkbox).prop('checked',false);
                GM_setValue(SET[id].name,0);
            }else{
                SET[id].func();
                $(checkbox).prop('checked',true);
                GM_setValue(SET[id].name,1);
            }
        }).children('ul.textArea').on('click',e=>{
            e.stopPropagation();
        });
        mouseHover(menu.find('li#MOoptions'));
        setMarkQuestionColor(menu);
        setCSSEffectOption(menu);
    }

    //鼠标聚焦时显示详情 【https://www.minerva-online.com/portal/menu/js/v2/menuRender.js?version=21-08 createToolOption : 】
    function mouseHover(ele){
        ele.hover(function(){
            $(this).find('ul:first').stop().show(200);
        },function(){
            $(this).find('ul:first').stop().hide(200);
        });
    }

    //添加扣分标记颜色设置界面
    function setMarkQuestionColor(menu){
        menu.find('input#3.menuIconSmall').next('ul').prepend(`<div style="padding:0px 5px">
        <b id=de>扣分颜色:</b>
        <form id=de>
            <input type=radio name=de value=red>红
            <input type=radio name=de value=orange>橙
            <input type=radio name=de value=yellow>黄
            <input type=radio name=de value=green>绿
            <input type=radio name=de value=blue>蓝
            <input type=radio name=de value=purple>紫
            <input type=radio name=de value=custom>自定义
            <input type=color class=selectedColor>
            <input type=button class=rm-btn value=√ title=保存 style="padding:1px 6px">
        </form>
        <b id=na>N/A颜色:</b>
        <form id=na>
            <input type=radio name=na value=red>红
            <input type=radio name=na value=orange>橙
            <input type=radio name=na value=yellow>黄
            <input type=radio name=na value=green>绿
            <input type=radio name=na value=blue>蓝
            <input type=radio name=na value=purple>紫
            <input type=radio name=na value=custom>自定义
            <input type=color class=selectedColor>
            <input type=button class=rm-btn value=√ title=保存 style="padding:1px 6px">
        </form>
        </div>`);
        //颜色选项初始化
        menu.find('form#de,form#na').each(function(){
            const curColor= $(this).attr('id')==='de'?
                  GM_getValue($(this).prev().text(),'red') : GM_getValue($(this).prev().text(),'green');
            $(this).prev().css('color',curColor);
            $(this).children('.selectedColor').attr('id',curColor);
            if(!curColor.includes('#')){
                $(this).children('input[value='+curColor+']').attr('checked',true);
                $(this).children('.selectedColor').hide();
            }else{
                $(this).children('input[value=custom]').attr('checked',true);
                $(this).children('.selectedColor').val(curColor);
            }
        });
        //点击选项颜色改变
        menu.find('form#de,form#na').children(':radio').on('click',function(){
            if($(this).val()==='custom'){
                $(this).next().show();
                $(this).next().attr('id',$(this).next().val());
            }else{
                $(this).nextAll('.selectedColor').hide();
                $(this).nextAll('.selectedColor').attr('id',$(this).val());
            }
            $(this).parent().prev().css('color',$(this).nextAll('.selectedColor').attr('id'));
        });
        //自定义颜色改变
        menu.find('form#de,form#na').children('.selectedColor').on('input',function(){
            $(this).attr('id',$(this).val());
            $(this).parent().prev().css('color',$(this).val());
        });
        //确认更改
        menu.find('form#de,form#na').children(':button').on('click',function(){
            GM_setValue($(this).parent().prev().text(),$(this).prev().attr('id'));
            if(!$(this).next().is('b')){
                $(this).after('<b>保存成功</b>');
                setTimeout(()=>{
                    $(this).next().remove();
                },3e3);
            }
        });
    }

    //添加扣分标记颜色设置界面
    function setCSSEffectOption(menu){
        menu.find('input#10.menuIconSmall').next('ul').prepend(`<div id=CSSEffectOption>
        <input type=checkbox value=夜间模式>夜间模式
        <input type=checkbox value=隐藏logo>隐藏logo
        </div>`);
        const oCE=SET[10];
        const name_=oCE.name+'_';
        menu.find('div#CSSEffectOption input').each(function(){
            const value=this.value;
            if(GM_getValue(name_+value,0)) $(this).prop('checked',true);//初始化勾选状态
            $(this).on('click',function(){
                if(GM_getValue(name_+value,0)){
                    $(this).prop('checked',false);
                    GM_setValue(name_+value,0);
                }else{
                    $(this).prop('checked',true);
                    GM_setValue(name_+value,1);
                }
                if(GM_getValue(oCE.name,oCE.switch)) oCE.func();
            });
        });
    }
    /*在顶端菜单栏添加MOassist设置按钮*/


    /*获取app.collaboration页面Enterprise.Admin权限*/
    function collaborationEnterpriseAdmin(){
        if(!document.location.href.includes('alias=app.collaboration')) return;
        const funcName=['renderSyncLog','renderImportLog'];
        for(let n of funcName) eval('unsafeWindow.'+n+'='+unsafeWindow[n].toString().replace('if (isEnterpriseAdmin)','').replace('function '+n,'function'));
    }
    /*获取app.collaboration页面Enterprise.Admin权限*/


    /*置顶置底*/
    function GOTOPBOTTOM(){
        $(window).on('scroll.gotopbottom',()=>{//如页面后续因为内容增加而能滚动的场合触发
            if($('div#goTopBottom').length===0) GOTOPBOTTOM();
        });
        const scrollBar=$(document).height()>(window.innerHeight+1||document.documentElement.clientHeight);//如有滚动条
        if(!scrollBar||document.location.href.includes('alias=knowledgebase')) return;//knowledgebase页面自带置顶按钮,不启用
        const goTopBottomButton=document.createElement('div');
        const toggleButton=document.createElement('img');
        $(toggleButton).appendTo(goTopBottomButton);
        $(goTopBottomButton).appendTo('body');
        $(goTopBottomButton).css({position:'fixed',zIndex:1e4}).attr('id','goTopBottom');
        $(toggleButton).css({display:'block',cursor:'pointer'}).attr('src','/knowledgebase/images/arrow_back_to_top.svg');//按钮显示图片(向下箭头)
        $(goTopBottomButton).attr('title','置顶置底');

        //以下按钮参数可自定义修改
        goTopBottomButton.style.bottom='50px';//按钮距离网页底部50px
        goTopBottomButton.style.right='30px';//按钮距离网页右边30px
        toggleButton.style.width='25px';//按钮图片宽25px
        toggleButton.style.height='25px';//按钮图片高25px
        toggleButton.style.opacity=0.5;//按钮不透明度,0.0(完全透明)到1.0(完全不透明)
        toggleButton.style.backgroundColor='grey';//按钮背景颜色,也可使用在excel等软件的自定义颜色界面的16进制代码
        const clickScrollTime=500;//点击按钮时,网页滚动到顶部或底部需要的时间,500毫秒

        //点击按钮时网页滚动到顶部或底部
        let scrollDirection='down';
        toggleButton.addEventListener('click',()=>{
            if(scrollDirection==='up'){
                $('html,body').animate({scrollTop:0},clickScrollTime);
            }else{
                $('html,body').animate({scrollTop:$(document).height()},clickScrollTime);
            }
        });
        //右键按钮记录页面位置
        const lock=$(goTopBottomButton).clone().attr({id:'goTopBottomLock',title:'回到记录位置'}).css({right:'60px',display:'none'}).appendTo('body');
        lock.children('img').css('transform','rotate(270deg)');
        goTopBottomButton.onmouseup=function(e){
            if(e.button===2){
                if(lock.css('display')==='none'){
                    const x=window.pageXOffset;
                    const y=window.pageYOffset;
                    lock[0].onclick=()=>{scrollTo(x,y);};
                    lock.show();
                }else{
                    lock.hide();
                }
            }
        };
        goTopBottomButton.oncontextmenu=()=>{return false;};
        //页面滚动监听
        let scrollAction=window.pageYOffset;
        $(window).scroll(()=>{
            const diffY=scrollAction-window.pageYOffset;
            scrollAction=window.pageYOffset;
            scrollDirection= diffY<0? 'down' : 'up';
            toggleButton.style.transform= diffY<0? 'rotate(0deg)' : 'rotate(180deg)';
            if(getScrollTop()===0){
                scrollDirection='down';
                toggleButton.style.transform='rotate(0deg)';
            }
            if(getScrollTop()+window.innerHeight+20>=$(document).height()){
                scrollDirection='up';
                toggleButton.style.transform='rotate(180deg)';
            }
        });
    }

    //获取垂直方向滑动距离
    function getScrollTop(){
        let scrollTop=0;
        if(document.documentElement&&document.documentElement.scrollTop){
            scrollTop=document.documentElement.scrollTop;
        }else if(document.body){
            scrollTop=document.body.scrollTop;
        }
        return scrollTop;
    }

    //卸载置顶置底
    function unGOTOPBOTTOM(){
        $(window).off('scroll.gotopbottom');
        if($('div#goTopBottom,div#goTopBottomLock').length) $('div#goTopBottom,div#goTopBottomLock').remove();
    }
    /*置顶置底*/


    /*菜单遮罩*/
    function COVERMENU(){
        if($('div#menu').length){
            //若存在menu则添加cover层
            const menu=$('div#menu')[0];
            const zidx=getComputedStyle(menu).zIndex;
            const cover = document.createElement('div');
            cover.className = 'layout';
            cover.style = 'top:'+menu.style.top+';opacity:0.3;z-index:'+zidx+';right:10%';
            //点击时将cover层下置
            cover.addEventListener('click',()=>{
                cover.style.zIndex = -1;
            });
            //离开menu时cover层还原
            menu.addEventListener('mouseleave',()=>{
                cover.style.zIndex = zidx;
            });
            //cover层位置跟随menu 【https://www.minerva-online.com/portal/menu/js/v2/menuRender.js?version=21-08 onScrollEventHandler : 】
            $(window).on('scroll.covermenu',function(){
                const SM=unsafeWindow.SM;
                if(!SM) return;
                SM.ui.parentContainer=cover;
                SM.ui.onScrollEventHandler();
                SM.ui.parentContainer=menu;
            });
            $(cover).appendTo($('body')[0]).attr('id','cover');
        }
    }

    //卸载菜单遮罩
    function unCOVERMENU(){
        if($('div#cover').length){
            $('div#cover').remove();
            $(window).off('scroll.covermenu');
        }
    }
    /*菜单遮罩*/


    /*附件下载*/
    function DOWNLOADFILE(){
        if(document.location.href.includes('alias=smngr.surveyexplorer')&&$('tr.persist-header').length){
            $('tr.persist-header').each(function(){
                $(this).children().first().after($(this).children().first().clone(true).attr('class','downloadFile'));
            });
            $('table#reporttable').find(':checkbox').each(function(){//checkbox后添加下载按钮
                const surveyid=$(this).val();
                $(this).parent().after(`
                <td>
                    <div id=${surveyid} class=downloadFile>
                        <button type=button id=download class=rm-btn title=加载附件列表>↓</button>
                    </div>
                </td>`);
                $(this).parent().next().find('button#download').one('click',function(){
                    DownloadButton($(this).parent(),surveyid);//将$('div.downloadFile')传参为df
                });
            });
        }
        //兼容inoki.va页面
        if(document.location.href.includes('alias=inoki.va')){
            if($('table#reporttable tr').length){
                $('table#reporttable>thead>tr').each(function(){
                    $(this).children().first().before($(this).children().first().clone(true).attr('class','downloadFile'));
                    $(this).children().first().text('附件下载');
                });
                $('table#reporttable').find('tbody>tr').each(function(){
                    const surveyid=$(this).attr('id');
                    $(this).prepend(`
                        <td>
                            <div class=downloadFile>
                                <button type=button id=download class=rm-btn title=加载附件列表>↓</button>
                            </div>
                        </td>`);
                    $(this).find('button#download').one('click',function(){
                        DownloadButton($(this).parent(),surveyid);
                    });
                });
            }
        }
    }

    //获取附件列表
    function DownloadButton(df,surveyid){
        //if(!surveyid) surveyid=df.parents('tr').attr('id');
        df.find('button#download').hide();
        df.append('<b id=loading>......</b>');
        $.get(`
        /open/data.asp?post={
            "action":"exec",
            "JSONPath":"dataset.data.3",
            "dataset":{"datasetname":"/Apps/SM/Survey/SurveyInstanceGetData"},
            "parameters":[{"name":"SurveyInstanceID","value":"${surveyid}"}]
        }`,(data,status)=>{//调用API获取当前survey数据[SurveyInstanceGetData]
            if(status==='success'){
                if(Array.isArray(data)){
                    const filedata=data;
                    const fileno=filedata.length;
                    df.append(`
                    <ol id=filelist>
                        <b>\t#=${fileno}</b>
                        <table></table>
                    </ol>
                    `);
                    if(fileno>0){
                        const ol=df.find('ol#filelist');
                        $('<button type=button id=downloadAll class="rm-btn rm-btn-default" title=下载全部>√</button>').prependTo(ol)
                            .on('click',()=>{
                            DownloadAll(df);
                        });
                        $('<button type=button id=deleteAll class="rm-btn rm-btn-default">删除全部附件</button>').appendTo(ol)
                            .on('click',()=>{
                            DeleteAll(df,surveyid);
                        });
                        const tb=ol.children('table');
                        for(let i in filedata){
                            const filename=filedata[i].FileName+'.'+filedata[i].FileExtension;
                            const fileid=filedata[i].AttachmentID;
                            const fileurl='/mystservices/Attachments/getAttachment.asp?Attachment='+fileid+'&Password='+filedata[i].Password+'';
                            let filesize=Number(filedata[i].FileSizeInBytes)/1024;
                            filesize= (filesize>1024)? (filesize/1024).toFixed(2)+' MB' : filesize.toFixed(2)+' KB';
                            $('<tr id='+fileid+'>').appendTo(tb).append(`
                            <td>
                                <li>
                                    <a id=${filedata[i].AttachmentType} class=mailboxlink href=${fileurl}>${filename}</a>
                                </li>
                            </td>
                            <td>${filesize}</td>
                            <td>QID:${filedata[i].ProtoQuestionID}</td>
                            `);
                        }
                        df.find('a#I,a#V').mouseenter(function(){
                            FilePreview(1,$(this).attr('href'));
                        }).mouseleave(()=>{
                            FilePreview(0);
                        });
                    }
                }else{
                    df.append('<b>登录失效!</b>');
                }
            }else{
                df.append('<b>网络错误!</b>');
            }
            DownloadButton0(df,surveyid);
        },'json');
    }

    //预览附件图片
    function FilePreview(show,src){
        if(show){
            const imgid=src.split('=')[2];
            if($('img#'+imgid+'.filepreview').length===0){
                $('<div><img id='+imgid+' class=filepreview></img></div>').appendTo('body');
                $('img#'+imgid+'.filepreview').attr('src',src+'&getThumbnail=1').css('height','200px')//视频附件预览图u&getThumbnail=1
                    .parent().css({position:'fixed',zIndex:1e4,height:'200px',background:'url(/images/icons/filtersv2/loading06.gif)'});
            }
            $('img#'+imgid+'.filepreview').parent().css({top:event.clientY-200,left:event.clientX+100});
            $('img#'+imgid+'.filepreview').show();
        }else{
            $('img.filepreview').hide();
        }
    }

    //按钮变为关闭
    function DownloadButton0(df,surveyid){
        df.find('b#loading').remove();
        df.find('button#download').text('×').attr('title','关闭附件列表').show().one('click',()=>{
            DownloadButton1(df,surveyid);
        });
    }

    //按钮重置为初始
    function DownloadButton1(df,surveyid){
        const passid=[];
        df.find('a#I,a#V').each(function(){
            passid.push($(this).attr('href').split('=')[2]);
        });
        for(let id of passid) $('img#'+id).parent().remove();
        df.find('ol,b').remove();
        df.find('button#download').text('↓').attr('title','加载附件列表').one('click',()=>{
            DownloadButton(df,surveyid);
        });
    }

    //下载全部
    function DownloadAll(df){
        df.find('button#downloadAll').text('〇').hide();
        const iframe=df.find('ol#filelist iframe');
        const a=df.find('ol#filelist a');
        if(iframe.length) iframe.remove();
        setTimeout(()=>{
            df.find('button#downloadAll').show();
        },1e3*a.length);//有几个附件就隐藏按钮几秒
        a.each(function(){
            $('<iframe src='+$(this).attr('href')+'>').appendTo(this).hide();
        });
    }
    /*
    //不知是不是因为下载地址有重定向的关系,GM_download效率低下,弃用
    function DownloadAll(df){
        const a=df.find('ol#filelist a');
        let loaded=0;
        df.find('button#downloadAll').text('〇').hide();
        df.find('ol#filelist>p,ol#filelist>br').remove();
        df.find('ol#filelist>b').text(`\t#=${loaded}/${a.length}\t下载中...`);
        a.each(function(){
            GM_download({
                'url':$(this).attr('href'),
                'name':df.parent().nextAll().eq(3)[0].innerText+'—'+df.parent().nextAll().eq(7)[0].innerText.replace('\n',' ')+'—'+$(this).text(),
                'onload':()=>{
                    loaded++;
                    if(loaded===a.length){
                        df.find('ol#filelist>b').text(`\t#=${loaded}/${a.length}\t下载完成!`);
                        df.find('button#downloadAll').show();
                    }else{
                        df.find('ol#filelist>b').text(`\t#=${loaded}/${a.length}\t下载中...`);
                    }
                },
                'onerror':()=>{
                    df.find('ol#filelist>b').after('<br><p>'+$(this).text()+'\t下载错误!</p>');
                    df.find('button#downloadAll').show();
                },
                'ontimeout':()=>{
                    df.find('ol#filelist>b').after('<br><p>'+$(this).text()+'\t下载超时!</p>');
                    df.find('button#downloadAll').show();
                }
            });
        });
    }
    */

    function DeleteAll(df,surveyid){
        const attid=[];
        df.find('a').each(function(){
            attid.push(this.href.match(/(?<=\=)\d+/)[0]);
        });
        console.log(attid,surveyid);
        const apply=confirm('请确认是否将此报告所有附件标记为删除\n(可在more中恢复,确认后可刷新页面或再次加载附件列表查看)');
        if(apply){
            //模拟手动禁用单个附件发送post请求,但请求数量较多
            for(let i of attid){
                $.post(`/document.asp?alias=survey.disableimage&ImageID=${i}&InstanceID=${surveyid}`,'ref=&step=2&comment=');
            }
            df.children('button#download').click();
        }
    }
    /*
    //以API实现,缺点是不会在more中留下用以恢复的记录,优点是请求数量较少
    function DeleteAll(df){
        let csv='';
        df.find('a').each(function(){
            csv+=this.href.match(/(?<=\=)\d+/)[0]+',';
        });
        csv=csv.slice(0,-1);
        console.log(csv);
        const apply=confirm('请确认是否要删除此报告所有附件\n(无法恢复注意备份,确认后可刷新页面或再次加载附件列表查看)');
        if(apply){
            //调用API将当前survey的所有附件标记为删除[SurveyAttachmentsMarkAttachmentsForDelete]
            $.get(`
            /open/data.asp?post={
                "action":"exec",
                "dataset":{"datasetname":"/Apps/SM/Media Hub/SurveyAttachmentsMarkAttachmentsForDelete"},
                "parameters":[{"name":"CsvList","value":"${csv}"}]
            }`);
            df.children('button#download').click();
        }
    }
    */

    //卸载附件下载
    function unDOWNLOADFILE(){
        if ($('div.downloadFile').length&&$('td.downloadFile').length){
            $('td.downloadFile').remove();
            $('div.downloadFile').each(function(){
                $(this).parent().remove();
            });
        }
    }
    /*附件下载*/


    /*扣分标记*/
    function MARKQUESTION(){
        if(unsafeWindow.OSM||!$('form#frmSurvey').length) return;//如v3问卷或预览等界面不运行
        if(document.location.href.includes('alias=survey.view')){
            $('span.surveyansweroption').each(function(){
                if($(this).prev('input').is(':checked')){
                    if($(this).prev('input').val()==='__na__'){
                        $(this).css('color',GM_getValue('N/A颜色:','green'));//默认标绿N/A项
                    }
                }
            });
            //获取所有扣分的题目
            const qidmark=[];
            const check=checkDayofWeek();
            if(check.isWrong) qidmark.push('Q'+check.qid+'ANS');
            markDayofWeek(check.isWrong,check.ele);
            //v3问卷/mystservices/v2new/getSurvey.asp?InstanceID=请求后获取数据:unsafeWindow.Open.stringToObject(OSMRenderingInit.toString().match(/(?<=OSMRendering_SurveyInstance_Preview\(){.+}(?=, document)/)[0])
            $.get('/mystservices/v2new/getSurvey.asp?InstanceID='+$('input#instanceID').val(),(data,status)=>{
                if (status==='success'){
                    $(data).find('nobr').each(function(){
                        const score=$(this).text();
                        if(score!=''&&!score.includes('%')){//排除空值与section总分
                            const pts=score.split('/');
                            if(Number(pts[0])<Number(pts[1])){
                                const QidANS=$(this).parents('td.surveyquestioncell').prev().find('div').attr('id');
                                qidmark.push(QidANS);
                            }
                        }
                    });
                    for(let i in qidmark){
                        $('div#'+qidmark[i]).find('span.surveyansweroption').css('color',GM_getValue('扣分颜色:','red'));//默认标红扣分项
                    }
                }
            });
        }
    }

    //检查所选星期和日期是否不一致
    function checkDayofWeek(){
        const qtx=['星期','Day of the week','Day of the Week','Day of week','Day 星期','周几',' 星期'];//lee问卷[ID:1316]的星期题前有空格
        const atx=['星期一','Monday'];
        let isWrong,ele,qid,start;
        $('span.surveyquestion').each(function(){
            for(let q of qtx) if($(this).text().startsWith(q)) ele=$(this);
        });
        if(ele) qid=ele.parents('a.surveyquestionnobreak').attr('name').replace('qstn','');
        if(qid){
            const t=$('input[name=HEAD_DATE]').val().split('-');
            if(t.length===3){
                const day=new Date(t[0],t[1]-1,t[2]).getDay();
                const ans=unsafeWindow.sm_getmultipleanswer(qid);
                for(let a of atx) if($('input#'+qid+'R1').next('span').text().startsWith(a)) start=1;
                if(start&&!day&&day+7!=ans) isWrong=1;//如星期一开头&&应为星期日&&0+7!=7
                if(start&&day&&day!=ans) isWrong=1;
                if(!start&&day+1!=ans) isWrong=1;
            }
        }
        return {isWrong:isWrong,qid:qid,ele:ele};
    }

    //标记星期是否正确
    function markDayofWeek(isWrong,ele){
        if(!ele) return;
        if(isWrong){
            ele.after('<b id=dayofweek style=color:red>×</b>');
        }else{
            ele.after('<b id=dayofweek style=color:green>√</b>');
        }
    }

    //卸载扣分标记
    function unMARKQUESTION(){
        if(unsafeWindow.OSM||!$('form#frmSurvey').length) return;
        if(document.location.href.includes('alias=survey.view')){
            $('span.surveyansweroption').css('color','');
            $('b#dayofweek').remove();
        }
    }
    /*扣分标记*/


    /*评论编辑*/
    function COMMENTEDIT(){
        if(unsafeWindow.OSM||!$('form#frmSurvey').length) return;//如v3问卷或预览等界面不运行
        if(document.location.href.includes('alias=survey.view')){
            if(unsafeWindow.updrowH) $('textarea.surveycomment').each(function(){unsafeWindow.updrowH(this);});//等页面自身执行到调整评论框高度过于缓慢
            $('<div id=commentEdit title=评论编辑>').appendTo($('body')[0])
                .css({position:'fixed',zIndex:1e4+1,
                      right:'30px',bottom:'80px',height:'25px',width:'25px',
                      background:'#808080'});
            //插入图标
            $('<img src=/images/icons/menu/x32/survet.png>').appendTo('div#commentEdit').css({width:'inherit',transform:'scale(0.8)'});
            //插入操作界面并赋值为ce
            $('<div id=commentFunc title>').appendTo('div#commentEdit')
                .css({position:'fixed',right:'60px',bottom:'50px'}).hide()
                .on('click',e=>{
                e.stopPropagation();//阻止子元素执行父元素click事件
            });
            const ce=$('div#commentFunc');
            $('div#commentEdit').on('click',function(){
                commentEditSwitch(ce);
            });
            //提示开关
            $('<b id=hint style=cursor:pointer>【点击获取提示】</b>').appendTo(ce)
                .on('click',function(){
                hintSwitch($(this));
            });
            //评论匹配与替换
            $('<button type=button id=replaceAll class=surveyBottomButton>一键替换</button>').appendTo(ce)
                .before('<textarea id=find placeholder=匹配内容></textarea><b id=commentMark style=cursor:pointer title=切换所有评论框标红状态>↓↓↓</b><textarea id=replace placeholder=替换内容></textarea>')
                .before('<b id=findNum>#</b>');
            $('textarea#find,textarea#replace').on('keydown',e=>{
                e.stopPropagation();//阻止页面自带keydown事件修改textarea的class值
            }).on('input',function(){
                commentMatch(ce);
            });
            $('b#commentMark').on('click',()=>{
                commentMark(ce);
            });
            $('button#replaceAll').on('click',()=>{
                execReplace(ce);
            });
            //首字母大写
            $('<button type=button id=initialUpper class=surveyBottomButton>首字母大写</button>').appendTo(ce)
                .on('click',()=>{
                initialUpper(ce);
            });
            //评论翻译
            $(`
            <button type=button id=commentTrans class=surveyBottomButton>评论翻译</button>
            <select id=toLang>
              <option value=en>→英文</option>
              <option value=jp>→日文</option>
              <option value=zh>→中文(简体)</option>
              <option value=cht>→中文(繁体)</option>
              <option value=yue>→中文(粤语)</option>
              <option value=f→j>繁体→简体</option>
            </select>
            `).appendTo(ce);
            ce.find('button#commentTrans').on('click',()=>{
                commentTranslate($('select#toLang option:selected').val());
            });
            ce.children().css({display:'block',textAlign:'center',margin:'5px auto'});
        }
    }

    //评论替换开关
    function commentEditSwitch(ce){
        if(ce.css('display')==='none'){
            ce.show();
            commentMatch(ce);
        }else{
            ce.hide();
            markMatchComment(0,$('textarea.surveycomment,textarea.active'));
        }
    }

    //插入或移除提示
    function hintSwitch(hs){
        if(hs.children().length){
            hs.text('【点击获取提示】').children().remove();
        }else{
            hs.text('【点击关闭提示】')
                .append('<a class=mailboxlink target=_blank href=https://tool.oschina.net/uploads/apidocs/jquery/regexp.html>匹配支持正则表达式</a>')
                .append('<a class=mailboxlink target=_blank href=https://c.runoob.com/front-end/854>正则表达式测试</a>')
                .append(`
                <ol>
                    <li>正则实例:[。|.]$ 可匹配末尾处中英文句号;^[a-z] 可匹配开头处小写字母;甲|乙|丙 可匹配甲或乙或丙</li>
                    <li>可当作一般替换使用,如需替换一些特殊字符(\^$*+?.等,参照第一个链接中所列字符),请在前面使用\\标记转义,避免识别为正则表达</li>
                    <li>评论框激活后按Ctrl可切换评论框是否标红,标红的评论框将被排除在修改范围之外,点击两框间的↓↓↓可快速切换全部评论框标红与否</li>
                </ol>
                `);
            hs.children().css('display','block').on('click',e=>{
                e.stopPropagation();
            });
            hs.find('li').css({textAlign:'left',width:'200px'});
        }
    }

    //判断此评论框是否隐藏
    function isHide(ele){
        const qvState=unsafeWindow.sm_questionvisibility_state;
        if(qvState&&qvState[ele.name.replaceAll('C','')]==='hide'||ele.style.display==='none'){
            return 1;
        }else{
            return 0;
        }
    }

    //切换所有评论框标记与否
    function commentMark(ce){
        $('textarea.surveycomment,textarea.active').each(function(){
            if(isHide(this)) return;
            if($(this).attr('class')==='active'){
                $(this).attr('class','surveycomment');
            }else{
                $(this).attr('class','active');
            }
        });
        commentMatch(ce);
    }

    //即时标记匹配到的评论框,预览替换后内容
    function commentMatch(ce){
        const f=getFind(ce);
        let n=0;
        markMatchComment(0,$('textarea.active'));
        $('textarea.surveycomment').each(function(){
            if(isHide(this)) return;
            const rc=getReplacedComment(ce,f,$(this).val());
            if(rc!==false){
                markMatchComment(1,$(this),rc);
                n++;
            }else{
                markMatchComment(0,$(this));
            }
        });
        ce.find('b#findNum').text('#='+n);
    }

    //匹配评论框标记处理
    function markMatchComment(hasText,ele,text,isBefore){
        ele.each(function(){
            if(hasText){
                text= isBefore? '之前为:\n'+text : '替换为:\n'+text;
                $(this).css('background','lightgrey').attr('title',text);
                if($(this).prev().attr('id')){
                    $(this).prev('div#mark').show();
                }else{
                    $(this).before('<div id=mark>^^</div>');
                }
            }else{
                $(this).css('background','').attr('title','');
                $(this).prev('div#mark').hide();
            }
        });
    }

    //判断并返回匹配值与类型
    function getFind(ce){
        let find,isRE;
        try{//若不是正则表达式,按普通字符处理
            find=new RegExp(ce.find('textarea#find').val(),'gm');
            isRE=1;
        }catch(e){
            find=ce.find('textarea#find').val();
        }
        return [find,isRE];
    }

    //判断并返回替换后评论或空
    function getReplacedComment(ce,f,text){
        const index= f[1]? text.search(f[0]) : text.indexOf(f[0]);//search只接受正则 : indexOf只接受字符
        if(index>=0){
            return text.replaceAll(f[0],ce.find('textarea#replace').val());
        }else{
            return false;
        }
    }

    //进行评论替换
    function execReplace(ce){
        const f=getFind(ce);
        $('textarea.surveycomment').each(function(){
            if(isHide(this)) return;
            const text=$(this).val();
            const rc=getReplacedComment(ce,f,text);
            if(rc!==false){
                markMatchComment(1,$(this),text,1);//记录修改前内容
                $(this).val(rc);
                unsafeWindow.updrowH(this);//调用页面自带函数来调整评论框高度
            }
        });
    }

    //首字母大写
    function initialUpper(ce){
        let n=0;
        $('textarea.surveycomment').each(function(){
            if(isHide(this)) return;
            const match=new Set($(this).val().match(/(^|\.|\?|!)("|'|) *[a-z]/gm));
            if(match.size){
                let text=$(this).val();
                markMatchComment(1,$(this),text,1);//记录大写前内容
                for(let m of match){
                    const r=new RegExp(`(^|\\.|\\?|!)("|'|) *`+m,'gm');
                    text=text.replaceAll(r,m.toUpperCase());
                }
                $(this).val(text);
                n++;
            }else{
                markMatchComment(0,$(this));
            }
        });
        ce.find('b#findNum').text('#='+n);
    }

    //调用百度翻译进行评论翻译
    async function commentTranslate(toLang){
        await translate_baidu_startup();
        $('textarea.surveycomment').each(async function(){
            if(isHide(this)) return;
            if($(this).val()&&$(this).next().is('input:hidden')){
                $(this).after('<textarea id=trans style=overflow:hidden rows='+$(this).attr('rows')+' cols='+$(this).attr('cols')+'>')
                    .after('<button type=button class=attachmentBtn style="display:block;height:1.5em;width:1.5em;margin:5px 0">↑</button>');
                $(this).next('button').on('click',function(){
                    const prev=$(this).prev().val();
                    const next=$(this).next().val();
                    $(this).prev().val(prev+'\n\n'+next);
                    unsafeWindow.updrowH($(this).prev()[0]);
                    $(this).next().remove();
                    $(this).remove();
                });
            }
            if($(this).val()){
                const fromLang= toLang==='f→j'? 'cht' : null;
                toLang= toLang==='f→j'? 'zh' : toLang;
                const translated=await translate_baidu(toLang,$(this).val(),fromLang);
                $(this).next().next('textarea').val(translated);
                unsafeWindow.updrowH($(this).next().next('textarea')[0]);//调用页面自带函数来调整评论框高度
            }
        });
        $('textarea#trans').on('keydown',e=>{
            e.stopPropagation();//阻止页面自带keydown事件修改textarea的class值
        });
    }

    //百度翻译 参考https://greasyfork.org/scripts/378277
    async function translate_baidu_startup(){
        if(window.sessionStorage.getItem('baidu_gtk')&&window.sessionStorage.getItem('baidu_token')) return;
        const options={
            method:'GET',
            url:'https://fanyi.baidu.com',
        };
        const res=await Request(options);
        window.sessionStorage.setItem('baidu_gtk',/window\.gtk = "(.*?)"/.exec(res.responseText)[1]);
        window.sessionStorage.setItem('baidu_token',/token: '(.*?)'/.exec(res.responseText)[1]);
    }

    async function translate_baidu(toLang,raw,fromLang){
        if(!fromLang){
            fromLang=await check_lang(raw);
        }
        const proc_raw= raw.length>30? (raw.substr(0,10)+raw.substr(~~(raw.length/2)-5,10)+raw.substr(-10)) : raw;//process
        const tk_key=window.sessionStorage.getItem('baidu_gtk');
        const token=window.sessionStorage.getItem('baidu_token');//get token
        const options={
            method:"POST",
            url:'https://fanyi.baidu.com/v2transapi',
            data:`from=${fromLang}&to=${toLang}&query=${encodeURIComponent(raw)}&transtype=translang&simple_means_flag=3&sign=${tk(proc_raw,tk_key)}&token=${token}&domain=common`,
            headers:{
                "referer":'https://fanyi.baidu.com',
                'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
            },
        };
        return await BaseTranslate('百度翻译',raw,options,res=>JSON.parse(res).trans_result.data.map(item=>item.dst).join('\n'));
    }

    async function check_lang(raw){
        const options={
            method:"POST",
            url:'https://fanyi.baidu.com/langdetect',
            data:'query='+encodeURIComponent(raw.replace(/[\uD800-\uDBFF]$/,'').slice(0,50)),
            headers:{
                'Content-Type':'application/x-www-form-urlencoded',
            }
        };
        const res=await Request(options);
        try{
            return JSON.parse(res.responseText).lan;
        }catch(e){
            console.log(e);
            return;
        }
    }

    //根据翻译字符获取sign值
    function tk(a,b){
        var d=b.split('.');
        b=Number(d[0]) || 0;
        for(var e=[],f=0,g=0;g<a.length;g++){
            var k=a.charCodeAt(g);
            128>k?
                e[f++]=k : (
                2048>k?
                e[f++]=k>>6|192 : (
                    55296==(k&64512)&&g+1<a.length&&56320==(a.charCodeAt(g+1)&64512)? (
                        k=65536+((k&1023)<<10)+(a.charCodeAt(++g)&1023),
                        e[f++]=k>>18|240,
                        e[f++]=k>>12&63|128
                    ) :
                    e[f++]=k>>12|224,
                    e[f++]=k>>6&63|128
                ),
                e[f++]=k&63|128
            );
        }
        a=b;
        for(f=0;f<e.length;f++) a=Fo(a+e[f],'+-a^+6');
        a=Fo(a,'+-3^+b+-f');
        a^=Number(d[1])||0;
        0>a&&(a=(a&2147483647)+2147483648);
        a%=1E6;
        return a.toString()+'.'+(a^b);
    }

    function Fo(a,b){
        for(var c=0;c<b.length-2;c+=3){
            var d=b.charAt(c+2);
            d= "a"<=d? d.charCodeAt(0)-87 : Number(d);
            d= "+"==b.charAt(c + 1)? a>>>d : a<<d;
            a= "+"==b.charAt(c)? a+d&4294967295 : a^d;
        }
        return a;
    }

    //异步请求包装工具
    async function PromiseRetryWrap(task,options,...values){
        const {RetryTimes,ErrProcesser}=options||{};
        let retryTimes=RetryTimes||5;
        const usedErrProcesser=ErrProcesser||(err=>{throw err;});
        if(!task)return;
        while(true){
            try{
                return await task(...values);
            }catch(e){
                if(!--retryTimes){
                    console.log(e);
                    return usedErrProcesser(e);
                }
            }
        }
    }

    async function BaseTranslate(name,raw,options,processer){
        const toDo=async ()=>{
            let tmp;
            try{
                const data=await Request(options);
                tmp=data.responseText;
                const result=await processer(tmp);
                if(result) window.sessionStorage.setItem(name+'-'+raw,result);
                return result;
            }catch(e){
                throw {
                    responseText: tmp,
                    err: e
                };
            }
        };
        return await PromiseRetryWrap(toDo,{RetryTimes:3,ErrProcesser:()=>"翻译出错"});
    }

    function Request(options){
        return new Promise((reslove,reject)=>GM_xmlhttpRequest({...options,onload:reslove,onerror:reject}));
    }
    /*评论编辑*/


    //卸载评论编辑
    function unCOMMENTEDIT(){
        if(unsafeWindow.OSM) return;
        if($('div#commentEdit').length) $('div#commentEdit').remove();
    }
    /*评论编辑*/


    /*验证输出*/
    function VERIFYEXPORT(){
        if (document.location.href.includes('alias=smngr.surveyexplorer')&&$('div#filterdiv').length){
            $('div#filterdiv').before('<button type=button id=verifyExport class="rm-btn rm-btn-default">验证输出勾选的报告</button>');
            $('button#verifyExport').css({margin:'10px 0px',display:'block'}).on('click',()=>{
                verifyExportAll();
            });
        }
    }

    //验证输出全部报告
    function verifyExportAll(){
        const apply=confirm('请确认是否要验证输出当前页面勾选的所有报告\n(电脑配置低请勿一次性输出过多报告)');
        if(apply){
            $('table#reporttable>tbody>tr').each(function(){
                if($(this).css('display')!='none'&&$(this).find('input:checkbox').eq(0).is(':checked')){
                    const a=$(this).find('td>a.mailboxlink').eq(0);
                    const src=a.attr('href').replace('document.asp?alias=survey.view&','importmngr/ImportSurveys/ImportSurveysFrame.asp?');
                    const iframe=document.createElement('iframe');
                    iframe.src=src;
                    iframe.style='height:80px;width:250px';
                    a.after(iframe);
                    iframe.onload=function(){
                        if(this.src.includes('ImportSurveysFrame.asp')){
                            const doc=$(this).contents();
                            doc.find('div#addInfo,div#menu,div#pathContainer').remove();
                            if(doc.find('input#scrverN').is(':checked')&&doc.find('input#questVerN').is(':checked')){//前2点均为否时才进行操作
                                doc.find('input#scrverY').click();
                                doc.find('input#questVerY').click();
                                doc.find('button#save').click();
                            }else if(this.contentWindow.surveyImportSurveySubmitted){
                                $(this).before('<div style=background:green;width:250px><img src=/images/icons/xp16/accept-hover.png>报告已成功执行上线</div>');
                                $(this).remove();
                            }else{
                                $(this).before('<div style=width:250px>报告原本已处于上线状态</div>');
                            }
                        }
                    };
                }
            });
            alert('请耐心等待所有小窗口加载完成,显示绿色保存成功提示后,再刷新页面检查是否全部验证输出成功');
        }
    }

    //卸载验证输出
    function unVERIFYEXPORT(){
        if($('button#verifyExport').length) $('button#verifyExport').remove();
    }
    /*验证输出*/


    /*定制汇总*/
    function CUSTOMROLLUP(){
        if(document.location.href.includes('alias=clientaccess.customrollups')){
            if($('table.reporttable,table.incCrossTabTableClass').length){
                const cr=document.createElement('div');
                cr.id='CRfunction';
                $('div.q2r_tableCaptionDiv,table.incCrossTabTableClass').before(cr);
                //复制表格
                $('<button type=button id=copyBtn>复制表格</button>').appendTo(cr)
                    .on('click',function(){
                    if($(this).next('select#percentSign').val()==='+%'){//给Pivot table形式的数据后添加%
                        $('table.incCrossTabTableClass td.incCrossTabTableBodyValuesClass').each(function(){
                            if(!$(this).attr('rowspan')) $(this).text($(this).text().replace('%','')+'%');
                        });
                    }else{
                        $('table.incCrossTabTableClass td.incCrossTabTableBodyValuesClass').each(function(){
                            if(!$(this).attr('rowspan')) $(this).text($(this).text().replace('%',''));
                        });
                    }
                    window.getSelection().removeAllRanges();
                    const content=$(cr).parent().find('table#reporttable,table.incCrossTabTableClass').children('tbody')[0].innerText;
                    if($(this).prevAll('textarea').length===0){
                        $(this).before('<textarea>');
                    }
                    $(this).prev('textarea').show();
                    $(this).prev('textarea').val(content).select();
                    document.execCommand('copy');
                    $(this).prev('textarea').hide();
                    window.getSelection().removeAllRanges();
                    document.execCommand('copy')?
                        $(this).text('复制成功!') : $(this).text('复制失败...');
                    setTimeout(()=>{
                        $(this).text('复制表格');
                    },3e3);
                });
                $(`<select id=percentSign title="此选项仅针对Pivot table(自定义表格)界面下,复制时为分数后添加%,在Tabular(默认表格)界面下无效">
                    <option value=+%>分数后+%</option>
                    <option value=无>无</option>
                </select><br>`).appendTo(cr);
                //精确pts%
                $('<button type=button id=ptsPercPlus title=根据Pts和PtsOf列精确计算pts%值>精确pts%</button>').appendTo(cr)
                    .on('click',function(){
                    const td=$(cr).parent().find('table#reporttable>thead>tr>td');
                    let pts,ptsOf;
                    for(let i=0;i<td.length;i++){
                        if(td.eq(i).text()==='Pts') pts=i;
                        if(td.eq(i).text()==='Pts Of') ptsOf=i;
                    }
                    if(pts&&ptsOf){
                        $('#reporttable tr').each(function(){
                            const last=$(this).children('td:last-child');
                            if(last.attr('class')!=='ptsPercPlus') last.after(last.clone(true).attr('class','ptsPercPlus'));
                            const tar=$(this).children('td.ptsPercPlus');
                            if($(this).attr('class')==='persist-header'){
                                tar.text('Pts%+');
                            }else{
                                const score=$(this).children().eq(pts).children().text()/$(this).children().eq(ptsOf).children().text();//只取<span>中的数据
                                tar.text((score*100).toFixed($(cr).children('select#toFixed').val())+'%');
                            }
                        });
                        $(this).text('计算成功!');
                    }else{
                        $(this).text('缺少关键列!');
                    }
                    setTimeout(()=>{
                        $(this).text('精确pts%');
                    },3e3);
                });
                $('<select id=toFixed title=此选项对【精确pts%】和【选项统计】同时生效>').appendTo(cr);
                const sel=$(cr).children('select#toFixed');
                for(let i=0;i<6;i++) sel.append(`<option value=${i}>保留${i}位小数</option>`);
                sel.children().eq(1).prop('selected',true);
                //选项统计
                $('<button type=button id=answerDistribution title=根据QuestionText和AnswerText和#Surveys列统计各架构各题各选项数量和占比>选项统计</button>').appendTo(cr)
                    .on('click',function(){
                    $(cr).parent().find('table#reporttable>tbody>tr.ad').remove();
                    //获取各关键列位置及隐藏情况
                    const thtd=$(cr).parent().find('table#reporttable>thead>tr>td');
                    let QText,AText,surveyNum;
                    const hideCol=[],colIndex=[];//QuestionText/AnswerText列中靠左的所有左侧的未隐藏列都将记录至colIndex并参与统计
                    for(let i=0;i<thtd.length;i++){
                        if(thtd.eq(i).text()==='Question Text') QText=i;
                        else if(thtd.eq(i).text()==='Answer Text') AText=i;
                        else if(thtd.eq(i).text()==='# Surveys') surveyNum=i;
                        else if(QText===undefined&&AText===undefined&&thtd.eq(i).css('display')!='none') colIndex.push(i);
                        else if(thtd.eq(i).css('display')==='none') hideCol.push(i);
                    }
                    if(QText>=0&&AText>=0&&surveyNum>=0){
                        //先显示可能被隐藏的关键列
                        const input=$('div#hideOption input');
                        if(input.length) for(let i of [QText,AText,surveyNum]) if(!input.eq(i).is(':checked')) toggleCol(input.eq(i).prop('checked',true)[0]);
                        //开始统计
                        const AD={};
                        $('table#reporttable>tbody>tr').each(function(){
                            if(this.style.display==='none') return;
                            const tbtd=$(this).children();
                            const Q=checkKey(AD,tbtd.eq(QText).text().trim());
                            let BUText='';
                            for(let c of colIndex) BUText+=tbtd.eq(c).text()+';';
                            const BU=checkKey(Q,BUText);
                            const num=Number(tbtd.eq(surveyNum).children('span').text());
                            if(!BU.num) Object.defineProperty(BU,'num',{value:0,enumerable:false,writable:true});//将作为分母的num设为不可枚举
                            BU.num+=num;
                            for(let a of tbtd.eq(AText).text().split(';')){
                                if(!BU[a]) BU[a]=0;
                                BU[a]+=num;
                            }
                        });
                        //检测是否已加载选项并补充样本量为0的选项
                        const selectedQ=$('input[name=fieldForCheckingFilter]').val().match(/(?<=4:).+(?= 5:)/),QnA={};
                        let selectedArr=[];
                        const PCmsFilterQuestion=$('select#PCmsFilterQuestionsSelectQuestion');
                        if(selectedQ) selectedArr=selectedQ[0].split('%2C');
                        else{
                            PCmsFilterQuestion.find('optgroup>option').filter(function(){return !this.value.includes(',');}).each(function(){
                                selectedArr.push(this.value);
                            });
                        }
                        for(let s of selectedArr){
                            const Q=PCmsFilterQuestion.find(`[value=${s}]`).text().trim();
                            QnA[Q]=[];
                            PCmsFilterQuestion.find(`[value*=_${s}_]`).each(function(){
                                QnA[Q].push(this.innerText.trim().substr(1));//去除多余空格和前面的'-'
                            });
                            const QnA_Q=QnA[Q],AD_Q=AD[Q];//将缺少的选项赋值为0
                            for(let a in QnA_Q) for(let bu in AD_Q) if(!Object.keys(AD_Q[bu]).includes(QnA_Q[a])) AD_Q[bu][QnA_Q[a]]=0;
                        }
                        //输出统计结果
                        for(let q in AD){
                            for(let bu in AD[q]){
                                const buArr=bu.split(';');
                                for(let a in AD[q][bu]){
                                    let tr='<tr class=ad>',n=0;
                                    for(let i=0;i<thtd.length;i++){
                                        if(hideCol.includes(i)) tr+='<td style=display:none></td>';
                                        else if(colIndex.includes(i)) tr+=`<td>${buArr[n++]}</td>`;
                                        else if(QText===i) tr+=`<td>[AD] ${q} (${AD[q][bu].num})</td>`;
                                        else if(AText===i) tr+=`<td>${a} (${AD[q][bu][a]})</td>`;
                                        else if(surveyNum===i) tr+=`<td>${(AD[q][bu][a]/AD[q][bu].num*100).toFixed($(cr).children('select#toFixed').val())}%</td>`;
                                        else tr+='<td></td>';
                                    }
                                    tr+='</tr>';
                                    $('table#reporttable>tbody').append(tr);
                                }
                            }
                        }
                        $(this).text('统计成功!');
                    }else{
                        $(this).text('缺少关键列!');
                    }
                    setTimeout(()=>{
                        $(this).text('选项统计');
                    },3e3);
                });
                //整体css调整
                $(cr).children('button').css({margin:'5px',font:'inherit'});
            }
        }
    }

    function checkKey(obj,key){
        if(!obj[key]) obj[key]={};
        return obj[key];
    }

    //卸载定制汇总
    function unCUSTOMROLLUP(){
        if($('div#CRfunction').length) $('div#CRfunction').remove();
    }
    /*定制汇总*/


    /*报告存档*/
    function SURVEYSAVES(){
        if(unsafeWindow.OSM||!$('form#frmSurvey').length) return;
        if(document.location.href.includes('alias=survey.view')){
            //插入操作界面并赋值为ssl
            $('<div id=surveySaves title=报告存档>').appendTo($('body')[0])
                .css({position:'fixed','zIndex':1e4,
                      right:'30px',bottom:'110px',height:'25px',width:'25px',
                      background:'#808080'});
            $('<img src=/images/icons/menu/x16/KB-icon.png>').appendTo('div#surveySaves').css({width:'inherit',transform:'scale(0.8)'});
            $('<div id=surveySavesList title>').appendTo('div#surveySaves')
                .css({position:'fixed',right:'60px',bottom:'50px',background:'lightgrey'}).hide()
                .on('click',e=>{
                e.stopPropagation();//阻止子元素执行父元素click事件
            });
            const ssl=$('div#surveySavesList');
            ssl.append(`
            <div style=margin:5px>
                <button type=button id=Show class=rm-btn>预览</button>
                <button type=button id=Load class=rm-btn>读档</button>
                <button type=button id=Save class=rm-btn>存档</button>
                <button type=button id=Dele class=rm-btn>删除</button>
            </div>
            <div id=tab style=margin:5px>
                <label><input type=radio name=tab value=autosaves checked>自动存档</label>
                <label><input type=radio name=tab value=selfsaves>手动存档</label>
            </div>
            <table cellpadding=5 style=margin:5px>
                <thead id=saves>
                    <tr>
                        <td></td>
                        <td>保存时间</td>
                        <td>问卷标题</td>
                        <td>店铺ID</td>
                        <td>报告ID</td>
                    </tr>
                </thead>
                <thead id=questions>
                    <tr>
                        <td><input type=checkbox name=questions checked></td>
                        <td>QID</td>
                        <td>题目</td>
                        <td>选项</td>
                        <td>评论</td>
                    </tr>
                </thead>
                <tbody id=autosaves></tbody>
                <tbody id=selfsaves></tbody>
                <tbody id=questions style=height:300px;overflow:scroll></tbody>
            </table>
            `);
            ssl.find('thead,tbody').css('display','block');
            ssl.find('#selfsaves,#questions').hide();
            //存档列表开关
            $('div#surveySaves').on('click',function(){
                if(ssl.css('display')==='none'){
                    ssl.show();
                    refreshList(ssl);
                }else{
                    ssl.hide();
                }
            });
            //按钮功能
            ssl.find('button#Show').on('click',()=>{
                showData(ssl);
            });
            ssl.find('button#Load').on('click',()=>{
                loadData(ssl);
            });
            ssl.find('button#Save').on('click',()=>{
                saveData(ssl,'selfsaves');
            });
            ssl.find('button#Dele').on('click',()=>{
                deleteData(ssl);
            });
            //切换存档列表页
            ssl.find('div#tab').on('click',function(){
                ssl.find('input[name=saves]:checked').prop('checked',false);//将存档的选中状态重置
                const checked=$(this).find('input:checked').val();
                const unchecked= checked==='autosaves'? 'selfsaves' : 'autosaves';
                $(this).parent().find('tbody#'+unchecked).hide();
                $(this).parent().find('tbody#'+checked).show();
                refreshList(ssl,checked);
                theadWidth(ssl.find('thead#saves'),ssl.find('tbody#'+checked));
            });
            //预览界面一键勾选与取消
            ssl.find('thead#questions input').on('click',function(){
                if($(this).is(':checked')){
                    $(this).parents('table').find('input[name=questions]').prop('checked',true);
                }else{
                    $(this).parents('table').find('input[name=questions]').prop('checked',false);
                }
            });
            //监听表单内点击和评论失焦触发存档事件
            let Form=$('form#frmSurvey').serialize();
            $('form#frmSurvey').on('click.autosave',()=>{
                Form=autoSave(ssl,Form);
            });
            $('textarea.surveycomment,textarea.active').on('blur.autosave',()=>{
                Form=autoSave(ssl,Form);
            });
        }
    }

    //表单变化时自动存档
    function autoSave(ssl,prvForm){
        const curForm=$('form#frmSurvey').serialize();
        if(prvForm!=curForm){//如表单数据改变
            saveData(ssl,'autosaves');
        }
        return curForm;
    }

    //刷新存档列表
    function refreshList(ssl,fromSave){
        if(ssl.css('display')==='none') return;//界面隐藏时不刷新
        if(!fromSave) fromSave=ssl.find('input[name=tab]:checked').val();//无参数自动判断当前页
        const curChecked=ssl.find('input[name=saves]:checked').parent('td').next().text();//记录当前选中存档
        ssl.find('tbody#'+fromSave).empty();
        const saves=GM_getValue(fromSave,{});
        const k=Object.keys(saves);
        if(k.length){
            for(let i in k){
                const line= i%2===1? 'reporttable_odd' : 'reporttable_even';
                ssl.find('tbody#'+fromSave).prepend(`
                <tr class=${line}>
                    <td><input type=radio name=saves></td>
                    <td>${k[i]}</td>
                    <td>${saves[k[i]].surveytitle}</td>
                    <td>${saves[k[i]].formhead.LOCATION}</td>
                    <td>${saves[k[i]].surveyid}</td>
                </tr>
                `);
                if(curChecked===k[i]) ssl.find('tbody#'+fromSave).children('tr:first').find('input').prop('checked',true);//还原存档选中状态
            }
            //如为当前存档页则同步表头宽度
            if(ssl.find('input[name=tab]:checked').val()===fromSave){
                theadWidth(ssl.find('thead#saves'),ssl.find('tbody#'+fromSave));
            }
        }else{
            const w=getComputedStyle(ssl.find('tbody#'+fromSave)[0]).width;
            ssl.find('tbody#'+fromSave).append('<td style=width:'+w+';text-align:center>无数据!</td>');
        }
    }

    //预览
    function showData(ssl){
        if(ssl.find('thead#questions').css('display')==='none'){
            const [save]=checkSave(ssl);
            if(!save) return;
            const head=save.formhead;
            for(let h in head){
                const line='reporttable_odd';
                ssl.find('tbody#questions').append(`
                <tr class=${line}>
                    <td><input type=checkbox name=questions></td>
                    <td colspan=2>${h}</td>
                    <td colspan=2>${head[h]}</td>
                </tr>
                `);
            }
            const body=save.formbody;
            for(let b of body){
                if(!Object.keys(b).includes('ans')) b.ans='×';
                if(!Object.keys(b).includes('cmt')) b.cmt='×';
                const line='reporttable_even';
                ssl.find('tbody#questions').append(`
                <tr class=${line}>
                    <td><input type=checkbox name=questions></td>
                    <td>${b.qid}</td>
                    <td>${b.qtx}</td>
                    <td>${b.ans}</td>
                    <td>${b.cmt}</td>
                </tr>
                `);
            }
            if(ssl.find('thead#questions input').is(':checked')){
                ssl.find('tbody#questions input').prop('checked',true);
            }else{
                ssl.find('tbody#questions input').prop('checked',false);
            }
            ssl.find('button#Show').text('←');
            ssl.find('button#Save,button#Dele,div#tab,[id$=saves]').hide();
            ssl.find('table #questions').show();
            theadWidth(ssl.find('thead#questions'),ssl.find('tbody#questions'));//在显示后同步宽度,隐藏时会默认为auto

        }else{
            ssl.find('tbody#questions').empty();
            ssl.find('button#Show').text('预览');
            ssl.find('table #questions').hide();
            ssl.find('button#Save,button#Dele,div#tab,#saves,tbody#'+$('input[name=tab]:checked').val()).show();
        }
    }

    //读档
    function loadData(ssl){
        const [save]=checkSave(ssl);
        if(!save) return;
        //如当前报告id与存档报告id不相等,询问是否执行
        if(save.surveyid!=$('input#instanceID').val()){
            //将文本,是否确认,确认后执行的方法传给feedback(ssl,text,ifConfirm,yesFunc)生成确认提示
            feedback(
                ssl,
                '报告ID不同,仅会修改当前报告中和存档中相匹配的题目,是否继续?',
                1,
                ()=>{execLoadData(ssl,save);}
            );
        }else{
            execLoadData(ssl,save);
        }
    }

    //执行读档
    function execLoadData(ssl,save){
        const qidArr=[];
        if(ssl.find('thead#questions').css('display')!='none'){
            if(ssl.find('input[name=questions]:checked').length===0){
                feedback(ssl,'请勾选题目!');
                return;
            }
            ssl.find('input[name=questions]:checked').each(function(){
                qidArr.push($(this).parent().next().text());
            });
        }
        const setAnswer=unsafeWindow.sm_setmultipleanswer;
        const setComment=unsafeWindow.sm_setcomment;
        const head=save.formhead;
        let count=0;
        for(let h in head){
            if(qidArr.length&&!qidArr.includes(h)) continue;
            if(h==='DATE'){
                if(head[h]===''){
                    $('select#dsYear').children(':first').prop('selected',true);
                    $('select#dsMonth').children(':first').prop('selected',true);
                    $('select#dsDay').children(':first').prop('selected',true);
                }else{
                    const date=head[h].split('-');
                    $('select#dsYear').children('[value='+date[0]+']').prop('selected',true);
                    $('select#dsMonth').children('[value='+date[1]+']').prev().prop('selected',true);
                    $('select#dsDay').children('[value='+date[2]+']').prev().prop('selected',true);
                }
            }
            if(h==='TIME'){
                if(head[h]===''){
                    $('select#tsHoursHEAD_TIME').children(':first').prop('selected',true);
                    $('select#tsMinutesHEAD_TIME').children(':first').prop('selected',true);
                }else{
                    const time=head[h].split(':');
                    $('select#tsHoursHEAD_TIME').children('[value='+time[0]+']').prop('selected',true);
                    $('select#tsMinutesHEAD_TIME').children('[value='+time[1]+']').prop('selected',true);
                }
            }
            if(h==='TIMEOUT'){
                if(head[h]===''){
                    $('select#tsHoursHEAD_TIMEOUT').children(':first').prop('selected',true);
                    $('select#tsMinutesHEAD_TIMEOUT').children(':first').prop('selected',true);
                }else{
                    const time=head[h].split(':');
                    $('select#tsHoursHEAD_TIMEOUT').children('[value='+time[0]+']').prop('selected',true);
                    $('select#tsMinutesHEAD_TIMEOUT').children('[value='+time[1]+']').prop('selected',true);
                }
            }
            if($('input[name=HEAD_'+h+']').length){
                $('input[name=HEAD_'+h+']').val(head[h]);
                count++;
            }
        }
        const body=save.formbody;
        for(let b of body){
            if(qidArr.length&&!qidArr.includes(b.qid)||$('textarea#C'+b.qid+'C').length===0) continue;//跨问卷读档时跳过没有的题防止updrowH()报错
            const key=Object.keys(b);
            if(key.includes('ans')) setAnswer(b.qid,b.ans);
            if(key.includes('cmt')){
                setComment(b.qid,b.cmt);
                unsafeWindow.updrowH(document.querySelector('textarea#C'+b.qid+'C'));//调整评论框高度
            }
            count++;
        }
        feedback(ssl,'读档成功。(共修改'+count+'题)');
    }


    //存档
    function saveData(ssl,toSave){
        const save={
            'surveytitle':$('h1.surveytitle').text(),
            'surveyid':$('input#instanceID').val(),
            'formhead':{
                'LOCATION':$('input[name=HEAD_LOCATION]').val()||$('table.visualization_HEAD_LOCATION').find('lookup').text().split(' - ')[0],
                'SHOPPER':$('input[name=HEAD_SHOPPER]').val(),
                'DATE':$('input[name=HEAD_DATE]').val(),
                'TIME':$('input[name=HEAD_TIME]').val(),
                'TIMEOUT':$('input[name=HEAD_TIMEOUT]').val()
            },
            'formbody':[]
        };
        const getAnswer=unsafeWindow.sm_getmultipleanswer;
        const getComment=unsafeWindow.sm_getcomment;
        $('a.surveyquestionnobreak').each(function(){
            const qtx=$(this).find('span.surveyquestion').text();
            const qid=$(this).attr('name').replace('qstn','');
            const ans= $('input[name=Q'+qid+'Q]').length? getAnswer(qid) : undefined;
            //ans 已填写:'1,2'||'__na__';未填写:'';无选项列:undefined
            const cmt= getComment(qid);
            //cmt 已填写:'xxx';未填写:'';无评论框:undefined
            save.formbody.push({
                'qtx':qtx,
                'qid':qid,
                'ans':ans,
                'cmt':cmt
            });
        });
        const saves=GM_getValue(toSave,{});
        saves[new Date().toLocaleString()]=save;
        //存档超过10个覆盖最早存档
        if(Object.keys(saves).length>10) delete saves[Object.keys(saves)[0]];
        GM_setValue(toSave,saves);
        refreshList(ssl,toSave);
        if(toSave==='autosaves') feedback(ssl,'自动存档成功。');
        if(toSave==='selfsaves') feedback(ssl,'手动存档成功。');
    }

    //删除
    function deleteData(ssl){
        const [save,time,saves,fromSave]=checkSave(ssl);
        if(!save) return;
        delete saves[time];
        GM_setValue(fromSave,saves);
        refreshList(ssl);
        feedback(ssl,'删除成功。');

    }

    //检查存档有效性
    function checkSave(ssl){
        if(ssl.find('input[name=saves]:checked').length===0){
            feedback(ssl,'请选择存档!');
            return [];
        }
        const fromSave=ssl.find('input[name=saves]:checked').parents('tbody').attr('id');
        const saves=GM_getValue(fromSave,{});
        const time=ssl.find('input[name=saves]:checked').parent().next().text();
        if(!Object.keys(saves).includes(time)){
            feedback(ssl,'无此存档!(可能已被覆盖)');
            return [];
        }
        return [saves[time],time,saves,fromSave];
    }

    //操作反馈提示
    function feedback(ssl,text,ifConfirm,yesFunc){
        if(ssl.css('display')==='none') return;//界面隐藏时不提示
        if(ssl.find('b#feedback').length===0){
            ssl.find('div#tab').before('<b id=feedback style=margin:5px></b>');
        }
        const b=ssl.find('b#feedback');
        b.text(text).append('<br>');
        if(ifConfirm){
            $('<input type=button value=√ class="rm-btn rm-btn-default">').appendTo(b)
                .on('click',function(){
                $(this).parents('b#feedback').remove();
                yesFunc();
            });
            $('<input type=button value=× class="rm-btn rm-btn-default">').appendTo(b)
                .on('click',function(){
                $(this).parents('b#feedback').remove();
            });
            b.children('input:button').css('margin','5px');
        }else{
            setTimeout(()=>{
                b.remove();
            },3e3);
        }
    }

    //将表头宽度同步为表身
    function theadWidth(thead,tbody){
        if(tbody.find('td').text()==='无数据!') return;
        thead.find('tr td').each(function(){
            const i=$(this).index();
            const w=getComputedStyle(tbody.children('tr:last').children()[i]).width;
            $(this).css('width',w);
        });
    }

    //卸载报告存档
    function unSURVEYSAVES(){
        if(unsafeWindow.OSM||!$('form#frmSurvey').length) return;
        if($('div#surveySaves').length) $('div#surveySaves').remove();
        if(document.location.href.includes('alias=survey.view')){
            $('form#frmSurvey').off('click.autosave');
            $('textarea.surveycomment,textarea.active').off('blur.autosave');
        }
    }
    /*报告存档*/


    /*PDF命名
    function PDFRENAME(){
        if(!document.location.href.includes('exportMultiplePDF.asp')) return;
        $('table#pdfconvertProcessTableAdv tr.reporttable_thead').append('<th id=downloadAll>');
        $('<button style=font-size:10px;width:100px>命名并下载全部</button>').appendTo('table#pdfconvertProcessTableAdv th#downloadAll')
            .on('click',function(){
            $(this).parent().parent().nextAll('tr').find('button').click();
        });
        $('table#pdfconvertProcessTableAdv a[id^=dlLinkAdv]').each(function(){
            const a=$(this);
            const surveyid=a.attr('id').replace('dlLinkAdv','');
            a.parent().after('<td id=download>');
            const td=a.parent().next('td#download');
            $('<button style=font-size:10px;width:100px>命名并下载</button>').appendTo(td)
                .on('click',function(){
                if(a.attr('href')==='/surveyexport_pdf/done.asp?docID='){
                    $(this).text('请等待转化完成');
                    setTimeout(()=>{
                        $(this).text('命名并下载');
                    },1e3);
                }else{
                    $(this).text('下载中...');
                    fetchName(a,surveyid,$(this),0);
                }
            });
        });
    }

    function fetchName(a,surveyid,self,isInternal){
        let name=$('textarea#naming_convention').val();
        const field=new Set(name.match(/(?<=#\$FIELD\[)[A-Za-z0-9\._]+(?=\]\$#)/g));
        if(field.size){
            const fieldDict= isInternal? {
                'DateSubmitted':'SurveyDateOrDueDate',
                'TimeSubmitted':'SurveyDateOrDueDate',
                'Campaign':'CampaignName',
                'CustomProperty001':'LocationCustomPropertyValue001',
                'CustomProperty002':'LocationCustomPropertyValue002',
                'CustomProperty003':'LocationCustomPropertyValue003',
                'CustomProperty004':'LocationCustomPropertyValue004',
                'CustomProperty005':'LocationCustomPropertyValue005',
                'CustomProperty006':'LocationCustomPropertyValue006',
                'CustomProperty007':'LocationCustomPropertyValue007',
                'CustomProperty008':'LocationCustomPropertyValue008',
                'CustomProperty009':'LocationCustomPropertyValue009',
                'CustomProperty010':'LocationCustomPropertyValue010',
                'ExportServicePointsScored':'PrecalcScore',
                'ExportServicePointsPossible':'PrecalcScoreOutOf'
            } : {
                'SurveyInstanceID':'InstanceID',
                'DateSubmitted':'Date',
                'TimeSubmitted':'Time',
                'LocationId':'Loc ID',
                'LocationName':'Location Name',
                'LocationAddress':'Location Address 1',
                'LocationCity':'Location City',
                'LocationCounty':'Location County',
                'LocationState_Region':'Location State/Region',
                'LocationPostalCode':'Location Postal Code',
                'LocationCountry':'Location Country',
                'SurveyTitle':'Title',
                'CustomProperty001':'CustLocationProperty001',
                'CustomProperty002':'CustLocationProperty002',
                'CustomProperty003':'CustLocationProperty003',
                'CustomProperty004':'CustLocationProperty004',
                'CustomProperty005':'CustLocationProperty005',
                'CustomProperty006':'CustLocationProperty006',
                'CustomProperty007':'CustLocationProperty007',
                'CustomProperty008':'CustLocationProperty008',
                'CustomProperty009':'CustLocationProperty009',
                'CustomProperty010':'CustLocationProperty010',
                'ExportServicePointsScored':'PrecalcPts',
                'ExportServicePointsPossible':'PrecalcPtsOf',
                'ExportServiceScorePercentXX':'PrecalcScorePctXX',
                'ExportServiceScorePercentXX.X':'PrecalcScorePctXX.X',
                'ExportServiceScorePercentXX.XX':'PrecalcScorePctXX.XX'
            };
            const qSet=new Set();
            for(let i of field){
                if(isInternal){
                    if(i.includes('ServiceScorePercentXX')){
                        qSet.add('PrecalcScore');
                        qSet.add('PrecalcScoreOutOf');
                        continue;
                    }
                }
                if(fieldDict[i]) i=fieldDict[i];
                qSet.add(i);
            }
            let query='';
            for(let i of qSet) query+='['+i+'],';
            query=query.slice(0,-1);
            const api= isInternal? '/Apps/SM/APIv2/Query/Operations/Operations' : '/Apps/SM/APIv2/Query/ClientAnalytics/ClientAnalytics';
            $.get(`
            /open/data.asp?post={
                "action":"exec",
                "dataset":{"datasetname":"${api}"},
                "parameters":[
                    {"name":"QuerySpecification","value":"${query}"},
                    {"name":"SurveyInstanceIDs","value":"${surveyid}"}
                ]
            }`,data=>{//调用API获取当前survey数据(客户端和内部端由于权限不同需要使用不同api)
                if(!isInternal&&data.dataset.data[0].length===0){//如客户端api返回无数据则用内部端再尝试
                    fetchName(a,surveyid,self,1);
                }else if(isInternal&&data.dataset.data[0].length===0){//如内部端api返回无数据则报错
                    download(a.attr('href'),name+'.pdf',()=>{self.text('无法获取命名数据');});
                    return;
                }
                const info=data.dataset.data[0][0];
                for(let i of field){
                    const tmp=i;
                    if(fieldDict[i]) i=fieldDict[i];
                    let r;
                    if(isInternal&&tmp.includes('Submitted')){
                        if(tmp==='DateSubmitted') r=info[i].split(' ')[0];
                        if(tmp==='TimeSubmitted') r=info[i].split(' ')[1];
                    }else if(tmp.includes('ServiceScorePercentXX')){
                        if(isInternal){
                            r=Number(info.PrecalcScore)/Number(info.PrecalcScoreOutOf)*100;
                            if(tmp==='ExportServiceScorePercentXX') r=r.toFixed(0)+'%';
                            if(tmp==='ExportServiceScorePercentXX.X') r=r.toFixed(1)+'%';
                            if(tmp==='ExportServiceScorePercentXX.XX') r=r.toFixed(2)+'%';
                        }else{
                            r=info[i]+'%';
                        }
                    }else{
                        r=info[i];
                    }
                    name=name.replaceAll(`#$FIELD[${tmp}]$#`,r);
                }
                download(a.attr('href'),name+'.pdf',()=>{self.text('命名并下载〇');});
            },'json');
        }else{
            download(a.attr('href'),name+'.pdf',()=>{self.text('命名并下载〇');});
        }
    }

    function download(url,filename,done){//done()为执行完毕后执行的函数
        getBlob(url,function(blob){
            saveAs(blob,filename,done);
        });
    }

    function getBlob(url,cb){
        const xhr=new XMLHttpRequest();
        xhr.open('GET',url,true);
        xhr.responseType='blob';
        xhr.onload=function(){
            if (xhr.status === 200){
                cb(xhr.response);
            }
        };
        xhr.send();
    }

    function saveAs(blob,filename,done){
        if (window.navigator.msSaveOrOpenBlob){//Firefox&IE
            navigator.msSaveBlob(blob,filename);
        }else{
            const link=document.createElement('a');
            const body=document.querySelector('body');
            link.href=window.URL.createObjectURL(blob);
            link.download=filename;
            link.style.display='none';
            body.appendChild(link);
            link.click();
            body.removeChild(link);
            window.URL.revokeObjectURL(link.href);
        }
        done();
    }

    //卸载PDF命名
    function unPDFRENAME(){
        if(!document.location.href.includes('exportMultiplePDF.asp')) return;
        $('table#pdfconvertProcessTableAdv th#downloadAll').remove();
        $('table#pdfconvertProcessTableAdv td#download').remove();
    }
    /*PDF命名*/


    /*优化表头*/
    function BETTERTHEAD(){
        if($('table.sticky-thead,#rollupTable').length){
            $('table.sticky-thead,#rollupTable').each(function(){
                execBetterThead(this);
            });
        }
        if(!$('table.persist-area').length&&!document.location.href.includes('alias=client.analysiscustomrollups.3.0')) return;
        unsafeWindow.betterThead_observer=new MutationObserver(function(mutationsList){
            for(let m of mutationsList) execBetterThead(m.target);
        });
        unsafeWindow.betterThead_observer.observe(document.body, {childList: true, subtree: true});
    }

    //根据情况执行优化表头
    function execBetterThead(ele){
        let oTable,oDiv;
        if(ele.className==='sticky-thead'){
            oTable=$(ele).prev('table.sticky-enabled');
            oTable.children('thead').css({position:'sticky',top:$('#menu').height()||0});
            $(ele).hide();
            oDiv=oTable.parents('div.q2r_tableHolder').children('[class^=q2r_tableCaptionDiv]');
            if(oDiv.attr('class')==='q2r_tableCaptionDiv_without_text') oDiv.attr('class','q2r_tableCaptionDiv');
        }
        if(ele.id==='rollupTable'){
            oTable=$(ele).find('table#smReporttableTable');
            oDiv=oTable.prev();
        }
        if(!oTable||!oTable.length||!oDiv||!oDiv.length) return;
        if(oDiv[0].settle) return;
        oDiv[0].settle=1;
        oDiv.css('cursor','pointer').attr('title','显示/隐藏列').on('click.tableCaptionDivFunc',function(){
            tableCaptionDivFunc(this,oTable);
        });
    }

    //表头标题点击功能
    function tableCaptionDivFunc(oDiv,oTable){
        const next=$(oDiv).next();
        if(next.attr('id')==='hideOption'){
            if(next.css('display')==='none'){
                if(oTable.find('thead>tr>td').length===next.find('tr').length) next.show();
                else{
                    next.remove();
                    tableCaptionDivFunc(oDiv,oTable);
                }
            }else{
                next.hide();
            }
        }else{
            let hideOption='<div id=hideOption><table><thead class=persist-header>',i=0;
            const show=[];
            oTable.find('thead>tr>td').each(function(){
                if(this.style.display!='none') show.push(i);
                hideOption+=`<tr>
                                 <td class=theadTD><input type=checkbox value=${i++}></td>
                                 <td class=theadTD>${this.textContent}</td>
                             </tr>`;
            });
            next.before(hideOption);
            next.prev('div#hideOption').css({position:'absolute',/*top:tableCaptioDiv.clientHeight,*/zIndex:1,overflow:'scroll',maxHeight:'300px',background:'white'})
                .find('input').on('click',function(){
                toggleCol(this,oTable);
            });
            for(let s of show) next.prev('div#hideOption').find('input').eq(s).prop('checked',true);
        }
    }

    //显示隐藏列
    function toggleCol(checkbox,oTable){
        if($(checkbox).is(':checked')){
            oTable.find('tr').each(function(){
                $(this).children().eq(checkbox.value).show();
            });
        }else{
            oTable.find('tr').each(function(){
                $(this).children().eq(checkbox.value).hide();
            });
        }
    }

    //卸载优化表头
    function unBETTERTHEAD(){
        $('table.sticky-enabled>thead').css({position:'',top:''});
        $('table.sticky-thead').show();
        if($('div#hideOption').length){
            $('div#hideOption').find('input').each(function(){
                if(!$(this).is(':checked')) this.click();
            });
            $('div#hideOption').remove();
        }
        const oDiv1=$('table.sticky-enabled').parents('div.q2r_tableHolder').children('[class^=q2r_tableCaptionDiv]').css('cursor','').attr('title',null).off('click.tableCaptionDivFunc');
        const oDiv2=$('table#smReporttableTable').parent('div#resultsTable').children('div#smAnalysisCustomRollupsTableDiv').css('cursor','').attr('title',null).off('click.tableCaptionDivFunc');
        $(oDiv1,oDiv2).each(function(){delete this.settle;});
        if(unsafeWindow.betterThead_observer) unsafeWindow.betterThead_observer.disconnect();
    }
    /*优化表头*/


    /*外观效果*/
    function CSSEFFECT(){
        iframeInject();
        let css='';
        if(GM_getValue('外观效果_夜间模式',0)){
            const darkExclEle=[
                'img',
                'video',
                'option',
                'canvas',
                'div#menu',
                'div#cover',
                'ul.innerItemFirst',
                'ul.textArea',
                'code>iframe',
                '#ResourceSectionDiv>iframe',
                'div#graphicPlaceHolder',
            ];
            const darkExtraCSS='ul.textArea img{filter:none}option{background:black !important;color:white}';
            css+='html,'+darkExclEle.toString()+'{filter:invert(1) hue-rotate(180deg)}'+darkExtraCSS;
            if(document.location.href.includes('surveyexport_pdf/exportMultiplePDF.asp')) css+='body{background:white}'
        }
        if(GM_getValue('外观效果_隐藏logo',0)) css+='#sm_headerLogoTable{display:none}#addInfo{height:0 !important}#menu,#cover{top:0 !important}';
        if(style&&$('#'+style.id).length&&css===style.textContent) return;
        if(style) style.remove();
        style=GM_addStyle(css);
    }

    //为特定iframe注入css
    function iframeInject(){
        const list={
            'iframe#id_iframe_select_language':'img{filter:invert(1) hue-rotate(180deg)}',
        };
        for(let selector in list){
            const iframe=document.querySelector(selector);
            if(!iframe) continue;
            if(iframe.darkCSS) continue;
            iframe.addEventListener('load',()=>{
                const style=document.createElement('style');
                style.textContent=list[selector];
                iframe.contentDocument.head.append(style);
            });
            iframe.darkCSS=true;
        }
    }

    //卸载外观效果
    function unCSSEFFECT(){
        if(style) style.remove();
    }
    /*外观效果*/


})();