Convenient SZU

【使用前先看介绍/有问题可反馈】便捷深大 (Convenient SZU):适配深圳大学内部网多个网页的辅助脚本。内部网首页左上角增加 `宿舍用电查询/校园网络续费/登录Dr.com/体育场馆预订/百度文库/知网/表格下载专区/软件下载专区` 入口以及增加绑定个人信息窗口,免去进入 `Blackboard/学业完成查询/办事大厅卡片` 的繁琐步骤,自动登录 `Blackboard/办事大厅/Dr.com/校园网络续费` 等页面,自动填写需要登陆的页面的账号密码,【公文通】页面自动移除水印,【办事大厅】页面增加【修读课程统计下载】,【网上评教】页面增加【一键五星+评价】,【成长记录】页面增加【学期专业排名】,【转专业】页面增加【转专业成绩显示】,【学业完成查询】页面增加【彩虹地毯】,【宿舍用电查询】可自动记忆填写的宿舍信息并可自动显示近 20 天用电记录。

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name            Convenient SZU
// @namespace       http://tampermonkey.net/
// @version         1.1.9
// @description     【使用前先看介绍/有问题可反馈】便捷深大 (Convenient SZU):适配深圳大学内部网多个网页的辅助脚本。内部网首页左上角增加 `宿舍用电查询/校园网络续费/登录Dr.com/体育场馆预订/百度文库/知网/表格下载专区/软件下载专区` 入口以及增加绑定个人信息窗口,免去进入 `Blackboard/学业完成查询/办事大厅卡片` 的繁琐步骤,自动登录 `Blackboard/办事大厅/Dr.com/校园网络续费` 等页面,自动填写需要登陆的页面的账号密码,【公文通】页面自动移除水印,【办事大厅】页面增加【修读课程统计下载】,【网上评教】页面增加【一键五星+评价】,【成长记录】页面增加【学期专业排名】,【转专业】页面增加【转专业成绩显示】,【学业完成查询】页面增加【彩虹地毯】,【宿舍用电查询】可自动记忆填写的宿舍信息并可自动显示近 20 天用电记录。
// @author          cc
// @match           https://elearning.szu.edu.cn/*
// @match           https://authserver.szu.edu.cn/*
// @match           https://drcom.szu.edu.cn/*
// @match           https://self.szu.edu.cn/*
// @match           https://www1.szu.edu.cn/*
// @match           http://ehall.szu.edu.cn/*
// @match           http://bkxk.szu.edu.cn/*
// @match           https://*.webvpn.szu.edu.cn/*
// @match           172.30.255.2/*
// @match           172.30.255.42/*
// @match           http://192.168.84.3:9090/cgcSims/*
// @grant           GM_setValue
// @grant           GM_getValue
// @require         https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require         https://greasyfork.org/scripts/422854-bubble-message.js
// @run-at          document-end
// @noframes
// ==/UserScript==
// noinspection HttpUrlsUsage,NonAsciiCharacters,SpellCheckingInspection,JSUnresolvedFunction,JSUnresolvedVariable,DuplicatedCode

(function() {
    'use strict'
    const __VERSION__ = "1.1.9";

    /**
     * 生成 HTML 元素,支持生成的同时绑定属性样式事件文本
     * @param {string} tagName 标签
     * @param {object} attributes 属性映射表(属性值只能为 string ),默认为 {}
     * @param {object} config 配置映射表(属性值可以不是 string ),默认为 {}
     * @param {string|object} style 样式映射表,可以直接传入 string ,默认为 {}
     * @param {object} events 事件映射表(属性值只能为 function ),默认为 {}
     * @return {HTMLElement} HTML 元素
     */
    function makeElement(tagName, attributes={}, config={}, style={}, events={}) {
        if (typeof style === 'string')
            style = Object.fromEntries(style.trim().split(/\s*;\s*/).filter((pair) => pair.includes(':')).map((pair) => pair.split(/\s*:\s*/)));
        let el = document.createElement(tagName);
        Object.entries(attributes).forEach((entry) => el.setAttribute(entry[0], String(entry[1])));
        Object.entries(config).forEach((entry) => el[entry[0]] = entry[1]);
        Object.entries(style).forEach((entry) => el.style[entry[0]] = entry[1]);
        Object.entries(events).forEach((event) => el.addEventListener(event[0], event[1]));
        return el;
    }

    /**
     * 在某元素前插入元素
     * @param {HTMLElement} newChild 待插入的新元素
     * @param {HTMLElement} refChild 引用的定位元素
     * @param {number} offset 向前的偏移,可以为任意整数,默认为 1
     * @return {boolean} 是否插入成功
     */
    function insertBefore(newChild, refChild, offset=1) {
        if (offset === 0) {
            refChild.parentElement.insertBefore(newChild, refChild);
            refChild.remove();
            return true;
        }
        while (offset > 1 && refChild) {
            refChild = refChild.previousElementSibling;
            offset--;
        }
        while (offset < 0 && refChild) {
            refChild = refChild.nextElementSibling;
            offset++;
        }
        if (offset === 0) {
            if (refChild) {
                refChild.parentElement.insertBefore(newChild, refChild);
            } else {
                refChild.parentElement.appendChild(newChild);
            }
            return true;
        } else if (refChild) {
            refChild.parentElement.insertBefore(newChild, refChild);
            return true;
        }
        return false;
    }

    /**
     * 监控 HTML 元素变化
     * @param {HTMLElement} node 待监控的元素
     * @param {object|array[string]} options 可选监控类型,格式为 `{key: boolean, ...}`,key 的取值参照 MutationObserver
     * @param {function} callback 回调函数
     * @return {MutationObserver} MutationObserver 对象,可以通过 MutationObserver.disconnect() 取消监听
     */
    function monitor (node, options, callback) {
        if (Array.isArray(options))
            options = Object.fromEntries(options.map((option) => [option, true]));
        let observer = new MutationObserver(callback);
        observer.observe(node, options);
        return observer;
    }

    /**
     * 等待条件成立后执行任务
     * @param {function} task 待执行的任务
     * @param {function} cond 执行任务需要满足的条件
     * @param {number} timeout 自旋时间
     * @param {object} thisArg 为 task 绑定的 this 参数
     * @param {array} args 为 task 绑定的函数参数
     * @return {undefined}
     */
    function execUntil(task, cond, timeout=250, thisArg=null, ...args) {
        if (cond()) {
            task.apply(thisArg, ...args);
        } else {
            setTimeout(() => {
                execUntil(task, cond, timeout, thisArg, ...args);
            }, timeout);
        }
    }

    /**
     * 过滤对象数组中的对象的键
     * @param {array[object]} array 需要过滤键的对象数组
     * @param {string|array[string]} key 需要过滤的对象的键
     * @return {array[object]} 过滤键后的对象数组
     */
    function filterKey(array, key) {
        key = typeof key === 'string' ? [key] : key;
        let newArray = [];
        for (let i = 0; i < array.length; i++) {
            let obj = {};
            for (let j = 0; j < key.length; j++)
                obj[key[j]] = array[i][key[j]];
            newArray.push(obj);
        }
        return newArray;
    }

    /**
     * 重命名对象数组中的对象的键
     * @param {array[object]} array 需要重命名键的对象数组
     * @param {array[string]} keyPair 对象的旧新键对
     * @return {array[object]} 重命名键后的对象数组
     */
    function renameKey(array, keyPair) {
        array = Array.from(array);
        let entries = Object.entries(keyPair);
        array.forEach((obj) => {
            for (let entry of entries) {
                if (obj.hasOwnProperty(entry[0])) {
                    obj[entry[1]] = obj[entry[0]];
                    delete obj[entry[0]];
                }
            }
        });
        return array;
    }

    /**
     * 数组排序,支持原地修改,支持逆序排序,支持关键字排序
     * @param {array} array 待排序数组
     * @param {string|function|array[string|function]} key 用于排序的关键字
     * @param {boolean|array[boolean]} reverse 是否逆序,默认为 false
     * @param {boolean} inplace 是否就地修改,默认为 false
     * @return {array} 排序后的数组
     */
    function sorted(array, key, reverse=false, inplace=false) {
        let arr = inplace ? array : Array.from(array);
        if (typeof key === 'undefined') {
            arr = arr.sort();
            if (reverse)
                arr = arr.reverse();
            return arr;
        }
        key = Array.isArray(key) ? key : [key];
        reverse = Array.isArray(reverse) ? reverse : Array(key.length).fill(Boolean(reverse));
        for (let i = 0; i < key.length; i++) {
            let k = key[i];
            let rev = 1 - 2 * Number(reverse[i]);
            if (typeof k === 'string') {
                arr.sort((a, b) => {
                    if (a[k] > b[k])
                        return rev;
                    else if (a[k] < b[k])
                        return -rev;
                    return 0;
                });
            } else if (typeof k === 'function') {
                arr.sort((a, b) => {
                    let [ka, kb] = [k(a), k(b)];
                    if (ka > kb)
                        return rev;
                    else if (ka < kb)
                        return -rev;
                    return 0;
                });
            }
        }
        return arr;
    }

    /**
     * 下载文件
     * @param {string} href 文件链接
     * @param {string} fileName 文件名
     * @return {undefined}
     */
    function downloadFile(href, fileName) {
        let a = document.createElement('a');
        a.download = fileName || 'default';
        a.target = '_blank';
        a.href = href;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    /**
     * 合并多个数组的元素至单个数组
     * @param {array} array 包含多个含有多个元素的数组的数组
     * @return {array} 合并后的数组
     */
    function merge(array) {
        let ans = [];
        for (let e of array) {
            if (Array.isArray(e))
                ans = ans.concat(e);
            else
                ans.push(e);
        }
        return ans;
    }

    /**
     * 数字求和
     * @param {array[number]} array 数字数组
     * @return {number} 元素的和
     */
    function sum(array) {
        let ans = 0;
        for (let num of array)
            ans += num;
        return ans;
    }

    /**
     * 数字求最大
     * @param {array[number]} array 数字数组
     * @return {number} 元素的最大值
     */
    function max(array) {
        let ans = array[0];
        for (let num of array)
            if (num > ans)
                ans = num;
        return ans;
    }

    /**
     * 判断数字是否在一个范围内
     * @param {number} num 需要判断的数字
     * @param {number} start 左端点
     * @param {number} end 右端点
     * @param {boolean} endPoint 是否包含尤端典,默认为 true
     * @return {boolean} 判断结果
     */
    function inRange(num, start, end, endPoint=true) {
        end = endPoint ? end : end - 1;
        return start <= num && num <= end;
    }

    let account = GM_getValue('account');
    let hasUpdatedInfo = false;
    if (!account) {
        account = { cid: '', uid: '', pwd: '' };
        GM_setValue('account', account);
    } else {
        hasUpdatedInfo = account.cid && account.uid && account.pwd;
    }

    // if access ready
    $(document).ready(function () {
        console.log(`Convenient SZU version ${__VERSION__}`);
        let bm = new BubbleMessage();

        // home page
        if (location.host.match(/www1.*?\.szu\.edu\.cn/)) {
            if (location.href.endsWith('.szu.edu.cn/')) {
                let td = document.querySelector('table table table tbody tr td');
                let a_drcom_dom = document.getElementById('drcom_dom');
                if (td && !a_drcom_dom) {
                    function setExtension () {
                        td.querySelectorAll('img').forEach(e => e.remove());
                        td.firstChild.remove();
                        td.firstChild.remove();
                        td.style.padding = '5px 20px';
                        td.innerHTML += '<br>';
                        td.appendChild(makeElement('a', {
                            href: 'http://192.168.84.3:9090/cgcSims/',
                            id: 'electricity',
                        }, {
                            innerHTML: '宿舍用电查询',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'https://self.szu.edu.cn/self/',
                            id: 'network',
                        }, {
                            innerHTML: '|校园网络续费',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'http://172.30.255.42/a79.htm',
                            id: 'drcom-auth',
                        }, {
                            innerHTML: '|登录 Dr.com',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.innerHTML += '<br>';
                        td.appendChild(makeElement('a', {
                            href: 'http://ehall.szu.edu.cn/publicapp/sys/tycgyyxt/index.do',
                            id: 'venue-yh',
                        }, {
                            innerHTML: '体育场馆预订(粤海校区)',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'http://ehall.szu.edu.cn/publicappxl/sys/xlxqtycgyy/index.do',
                            id: 'venue-lh',
                        }, {
                            innerHTML: '|体育场馆预订(丽湖校区)',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.innerHTML += '<br>';
                        td.appendChild(makeElement('a', {
                            href: 'http://www.lib.szu.edu.cn/er/baidu-wenku',
                            id: 'wenku',
                        }, {
                            innerHTML: '百度文库',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'http://www.lib.szu.edu.cn/er/cnki',
                            id: 'zhiwang',
                        }, {
                            innerHTML: '|知网',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'https://jwb.szu.edu.cn/xzzq1/jxyxs.htm',
                            id: 'doc-download',
                        }, {
                            innerHTML: '|表格下载专区',
                        }, {
                            'cursor': 'pointer',
                        }));
                        td.appendChild(makeElement('a', {
                            href: 'https://www1.szu.edu.cn/nc/view.asp?id=64',
                            id: 'software-download',
                        }, {
                            innerHTML: '|软件下载专区',
                        }, {
                            'cursor': 'pointer',
                        }));
                    }
                    function setInput () {
                        let next_tr = document.querySelector('tbody tbody tbody tr:nth-child(2)');
                        let tr = makeElement('tr', {
                            id: 'inform',
                        }, {}, {
                            'display': 'flex',
                            'flex-wrap': 'wrap',
                            'justify-content': 'flex-start',
                        });
                        let uid = makeElement('input', {
                            id: 'uid',
                            type: 'number',
                            minlength: '10',
                            maxlength: '10',
                            placeholder: '请输入 10 位数学号',
                        });
                        let cid = makeElement('input', {
                            id: 'cid',
                            type: 'number',
                            minlength: '6',
                            maxlength: '6',
                            placeholder: '请输入 6 位数校园卡号',
                        });
                        let pwd = makeElement('input', {
                            id: 'pwd',
                            type: 'password',
                            placeholder: '请输入统一认证登录密码',
                        });
                        let btn = makeElement('button', {}, {
                            innerHTML: '更新信息',
                        }, {
                            'cursor': 'pointer',
                        }, {
                            click: function () {
                                let uid_val = document.getElementById('uid').value;
                                let cid_val = document.getElementById('cid').value;
                                let pwd_val = document.getElementById('pwd').value;
                                if (!uid_val.match(/^\d{10}$/)) {
                                    bm.message({
                                        type: 'warning',
                                        message: '学号必须为 10 位数',
                                        duration: 2000,
                                    });
                                    return false;
                                }
                                if (!cid_val.match(/^\d{6}$/)) {
                                    bm.message({
                                        type: 'warning',
                                        message: '校园卡号必须为 6 位数',
                                        duration: 2000,
                                    });
                                    return false;
                                }
                                if (!pwd_val) {
                                    bm.message({
                                        type: 'warning',
                                        message: '密码不能为空',
                                        duration: 2000,
                                    });
                                    return false;
                                }
                                account.uid = uid_val;
                                account.cid = cid_val;
                                account.pwd = pwd_val;
                                GM_setValue('account', account);
                                bm.message({
                                    type: 'success',
                                    message: '信息更新成功',
                                    duration: 2000,
                                })
                            }
                        });
                        if (account.uid) uid.value = account.uid;
                        if (account.cid) cid.value = account.cid;
                        if (account.pwd) pwd.value = account.pwd;
                        tr.appendChild(uid);
                        tr.appendChild(cid);
                        tr.appendChild(pwd);
                        tr.appendChild(btn);
                        insertBefore(tr, next_tr);
                        document.head.appendChild(makeElement('style', {}, {
                            innerText: `
								#inform > * {
									width: 160px;
									margin-left: 20px;
									margin-top: 5px;
									padding: 0 5px;
								}
								input::-webkit-outer-spin-button,
								input::-webkit-inner-spin-button {
									-webkit-appearance: none !important;
									margin: 0;
								}
							`
                        }));
                        let win = document.querySelector('tbody tbody tr:last-child');
                        let lst = win.lastElementChild.querySelector('tr:nth-child(2)');
                        lst.style.height = `${lst.offsetHeight + tr.offsetHeight + 55}px`;
                    }
                    setExtension();
                    setInput();
                }
            } else if (location.href.indexOf('/board/infolist') >= 0) {
                function meets (_ct, _in) {
                    for (let it of _in)
                        if (_ct.match(it))
                            return true;
                    return false;
                }
                function generateCheckbox (_id, _ct, _fn) {
                    let checkbox = makeElement('input', {
                        id: _id,
                        type: 'checkbox',
                        checked: 'false',
                    }, {}, {
                        'margin-right': '5px',
                    }, {
                        change: _fn,
                    });
                    let label = makeElement('label', {
                        for: _id,
                    }, {
                        innerHTML: _ct,
                    });
                    let container = makeElement('span', {}, {}, {
                        'font-size': '13px',
                        'display': 'inline-flex',
                        'align-items': 'center',
                        'margin-right': '10px',
                        'position': 'relative',
                        'top': '2px',
                    });
                    container.appendChild(checkbox);
                    container.appendChild((label));
                    return container;
                }
                function setCheckbox () {
                    let checkbox_show_only_college = generateCheckbox('show-only-college', '只看学院学部', function (event) {
                        let _in = [/.*?学院.*/, /.*?学部.*/];
                        let title_container = document.querySelectorAll('[valign=top]')[3];
                        let articles = [...title_container.querySelectorAll('table>tbody>tr')].slice(2);
                        let not_meets_articles = articles.filter((el) => !meets(el.querySelector('td:nth-child(3)>a').innerText, _in));
                        let not_meets_depts = [...document.querySelectorAll('select[name=from_username]>option')].filter((el) => !meets(el.value, _in));
                        if (event.target.checked) {
                            not_meets_depts.forEach((el) => el.style.display = 'none');
                            not_meets_articles.forEach((el) => el.style.display = 'none');
                        } else {
                            not_meets_depts.forEach((el) => el.style.display = '');
                            not_meets_articles.forEach((el) => el.style.display = '');
                        }
                    });
                    let next_el = document.querySelector('select[name=dayy]');
                    let td = next_el.parentElement;
                    td.style.width = '500px';
                    td.insertBefore(checkbox_show_only_college, next_el);
                }
                function updateSelect () {
                    // 只看学院学部
                    let show_el = document.querySelector('input#show-only-college');
                    show_el.addEventListener('change', (event) => {
                        account.boardShowOnlyChecked = event.target.checked;
                        GM_setValue('account', account);
                    })
                    if (typeof account.boardShowOnlyChecked !== 'boolean') {
                        account.boardShowOnlyChecked = show_el.checked;
                        GM_setValue('account', account);
                    } else if (show_el.checked !== account.boardShowOnlyChecked) {
                        show_el.click();
                    }
                    // 发文时间
                    let dayy_el = document.querySelector('select[name=dayy]');
                    dayy_el.addEventListener('change', function(event) {
                        account.boardDayySelectedIndex = event.target.selectedIndex;
                        GM_setValue('account', account);
                    });
                    if (typeof account.boardDayySelectedIndex !== 'number') {
                        account.boardDayySelectedIndex = dayy_el.selectedIndex;
                        GM_setValue('account', account);
                    } else if (dayy_el.selectedIndex !== account.boardDayySelectedIndex) {
                        dayy_el.selectedIndex = account.boardDayySelectedIndex;
                    }
                    // 发文单位
                    let dept_el = document.querySelector('select[name=from_username]');
                    dept_el.addEventListener('change', function(event) {
                        account.boardDeptSelectedIndex = event.target.selectedIndex;
                        GM_setValue('account', account);
                    });
                    if (typeof account.boardDeptSelectedIndex !== 'number') {
                        account.boardDeptSelectedIndex = dept_el.selectedIndex;
                        GM_setValue('account', account);
                    } else if (dept_el.selectedIndex !== account.boardDeptSelectedIndex) {
                        dept_el.selectedIndex = account.boardDeptSelectedIndex;
                    }
                    // 搜索关键词
                    let kw_el = document.querySelector('input[name=keyword]')
                    kw_el.addEventListener('input', (event) => {
                        account.boardKeywordValue = event.target.value
                        GM_setValue('account', account)
                    })
                    if (typeof account.boardKeywordValue !== 'string') {
                        account.boardKeywordValue = kw_el.getAttribute('value')
                        GM_setValue('account', account)
                    } else if (kw_el.getAttribute('value') !== account.boardKeywordValue) {
                        kw_el.setAttribute('value', account.boardKeywordValue)
                        kw_el.value = account.boardKeywordValue
                    }
                }
                setCheckbox();
                updateSelect();
            }
        } else if (!hasUpdatedInfo) {
            return;
        }

        // function pages
        if (location.host === 'www1.szu.edu.cn') {
            if (location.href.includes('/board/view.asp')) {
                window.onload = function() {
                    let banner = document.querySelector('table tbody tr td table tbody tr td table tbody tr td p font');
                    if (banner) banner.remove();
                    let watermarks = document.querySelectorAll('.mark_div');
                    watermarks.forEach((watermark) => {
                        watermark.remove();
                    });
                };
            }
        } else if (location.host.includes("elearning")) {
            if (location.href.endsWith('.szu.edu.cn/') || location.href.endsWith('.szu.edu.cn/webapps/login/')) {
                let span = document.querySelector('table table table tr td a span');
                if (span) span.click();
            } else if (location.href.includes('webapps/portal/')) {
                let observer = monitor(document.body, ['childList'], function(events) {
                    let done = false;
                    for (let event of events) {
                        for (let node of event.addedNodes) {
                            if (node instanceof HTMLElement && node.classList.contains('lb-wrapper')) {
                                execUntil(() => {
                                    node.querySelector('#agree_button').click();
                                    observer.disconnect();
                                    done = true;
                                }, () => {
                                    return !done && node.querySelector('#agree_button');
                                }, 250);
                            }
                        }
                    }
                });
            }
        } else if (location.host === 'authserver.szu.edu.cn') {
            let username_el = document.getElementById('username');
            let password_el = document.getElementById('password');
            let helper_el = $('.iCheck-helper');
            let captchaResponse_el = document.querySelector('p#cpatchaDiv #captchaResponse');
            let button_el = document.querySelector('button');
            if (username_el && password_el && button_el && helper_el.length && !captchaResponse_el) {
                username_el.value = account.cid;
                password_el.value = account.pwd;
                helper_el.click();
                button_el.click();
            }
        } else if (location.host === 'ehall.szu.edu.cn') {
            function insertTabButton() {
                let ampDesktopNav = $('#ampDesktopNav')[0];
                if (!ampDesktopNav)
                    return;
                function courseClassSorted(courses) {
                    function getCourseClassPriority (course) {
                        let priority = ['基本通识', '专业核心', '专业限选', '专业选修', '扩展通识', '自然科学', '生命科学', '社会科学', '中华文化', '人文艺术', '创新创业', '个性课程', '基本实践'];
                        for (let i = 0; i < priority.length; i++)
                            if (course.indexOf(priority[i]) >= 0)
                                return i;
                        return priority.length;
                    }
                    let courseInfo = courses.map(c => ({
                        course: c,
                        priority: getCourseClassPriority(c),
                    }));
                    return sorted(courseInfo, 'priority').map((o) => o.course);
                }
                function downloadCourseStatistics() {
                    $.ajax({
                        method: 'POST',
                        url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
                        data: {
                            BYNJDM: '-',
                        },
                    }).then((res) => {
                        let extendedCourseClasses = ['自然科学类', '生命科学类', '社会科学类', '中华文化类', '人文艺术类', '创新创业类'];
                        let numChinese = ['一', '二', '三', '四', '五', '六', '七', '八'];
                        let courseClassesObj = res.datas.cxscfakz.rows;
                        courseClassesObj = filterKey(courseClassesObj, ['KZM', 'KZH', 'PYFADM', 'YQXF', 'WCXF', 'YQMS', 'WCMS', 'YQWCKZS', 'WCKZS', 'FKZH']);
                        let allCourseClasses = courseClassesObj.filter((c) => c['FKZH'] !== '-1').map((c) => c['KZM']);
                        let statCourseClasses = allCourseClasses.filter((c) => !extendedCourseClasses.includes(c));
                        let extendedCourseClass = allCourseClasses.find((c) => c.includes('扩展通识'));
                        let cmap = Object.fromEntries([[extendedCourseClass, courseClassesObj.find(c => c['KZM'].includes('扩展通识'))]]);
                        allCourseClasses = allCourseClasses.filter((c) => !c.includes('扩展通识'));
                        let recommendClasses = statCourseClasses.filter((c) => c.indexOf('个性课程') < 0);
                        courseClassesObj = courseClassesObj.filter((c) => allCourseClasses.includes(c['KZM']) && c['FKZH'] !== '-1');
                        cmap = Object.assign(cmap, Object.fromEntries(courseClassesObj.map((c) => [c['KZM'], c])));
                        let keys = ['课程类型', '要求学分', '已修学分', '要求门数', '已修门数', '要求类别数', '已修类别数'];
                        let progressContent = '课程类型,要求学分,已修学分,要求门数,已修门数,要求类别数,已修类别数\n';
                        statCourseClasses = courseClassSorted(statCourseClasses);
                        statCourseClasses.map((statCourseClass) => {
                            let c = cmap[statCourseClass];
                            return {
                                '课程类型': statCourseClass,
                                '要求学分': c['YQXF'],
                                '已修学分': c['WCXF'],
                                '要求门数': c['YQMS'] === null ? '0' : String(c['YQMS']),
                                '已修门数': c['WCMS'] === null ? '0' : String(c['WCMS']),
                                '要求类别数': c['YQWCKZS'] === null ? '0' : String(c['YQWCKZS']),
                                '已修类别数': c['WCKZS'] === null ? '0' : String(c['WCKZS']),
                            };
                        }).forEach((p) => progressContent += keys.map((k) => p[k]).join(',') + '\n');
                        let reqs = courseClassesObj.map((courseClass) => $.ajax({
                            method: 'POST',
                            url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakzkc.do',
                            data: {
                                BYNJDM: '-',
                                KZH: courseClass.KZH,
                                PYFADM: courseClass.PYFADM,
                                pageSize: 999,
                                pageNumber: 1,
                            },
                        }).then((res) => {
                            let courseList = res.datas.cxscfakzkc.rows;
                            courseList = filterKey(courseList, ['KCM', 'CJ', 'XF', 'SFTG_DISPLAY', 'XNXQDM', 'KCXZDM_DISPLAY', 'KCH', 'BZ']);
                            courseList = renameKey(courseList, {
                                'KCM': '课程名',
                                'CJ': '成绩',
                                'XF': '学分',
                                'SFTG_DISPLAY': '是否通过',
                                'XNXQDM': '学年学期',
                                'KCXZDM_DISPLAY': '课程性质',
                                'BZ': '备注'
                            });
                            let clreqs = [];
                            courseList.forEach((course) => {
                                course['课程类型'] = courseClass['KZM'];
                                course['成绩'] = course['成绩'] || '';
                                course['学年学期'] = course['学年学期'] || '';
                                course['备注'] = course['备注'] || '';
                                if (course['备注'].match(/[^\d\s:a-zA-z\-]/g) === null || course['备注'].match(/见.*?备注.*/) !== null) course['备注'] = '';
                                clreqs.push($.ajax({
                                    method: 'POST',
                                    url: 'http://ehall.szu.edu.cn/jwapp/sys/qxfacx/modules/pyfacxepg/kzkccx.do',
                                    data: {
                                        KZH: courseClass.KZH,
                                        PYFADM: courseClass.PYFADM,
                                        KCH: course.KCH,
                                        pageSize: 1,
                                        pageNumber: 1,
                                    },
                                }).then((res) => {
                                    let recSem = '';
                                    if (res.datas.kzkccx.rows.length > 0) {
                                        let remark = res.datas.kzkccx.rows[0].BZ;
                                        recSem = res.datas.kzkccx.rows[0].XDXQ || '';
                                        if (
                                            typeof remark === 'string' &&
                                            remark.length > 0 &&
                                            !course['备注'].length &&
                                            (remark.match(/[^\d\s:a-zA-z\-]/g) !== null && remark.match(/见.*?备注.*/) === null))
                                            course['备注'] = remark;
                                    }
                                    course['建议修读学期'] = recSem;
                                    delete course.KCH;
                                    return course;
                                }).fail(err => {
                                    console.error(err);
                                }));
                            })
                            return Promise.all(clreqs).then((res) => [courseClass['KZM'], res]);
                        }).fail((err) => {
                            console.error(err);
                        }));
                        Promise.all(reqs).then((res) => {
                            let courses = Object.fromEntries(res);
                            let gradeMap = {'A+': 4.5, 'A': 4.0, 'B+': 3.5, 'B': 3.0, 'C+': 2.5, 'C': 2.0, 'D': 1.0, 'F': 0.0};
                            let semester = Array.from(new Set(merge(Object.values(courses)).map((o) => o['学年学期']))).filter(s => Boolean(s));
                            semester.sort();
                            let earliestYear = new Date().getFullYear();
                            if (semester.length) earliestYear = Number(semester[0].slice(0, 4));
                            let semesterIndexToDate = (year, semesterIndex) => {
                                semesterIndex = Number(semesterIndex);
                                let recommendYear = year + ((semesterIndex - 1) >> 1);
                                let recommendGrade = 2 - (semesterIndex & 1);
                                return `${recommendYear}-${recommendYear + 1}-${recommendGrade}`;
                            };
                            let semesterDateToChinese = (year, semesterDate) => {
                                let allNums = semesterDate.match(/\d+/g);
                                let yearIndex = Number(allNums[0]) - year;
                                let gradeChinese = `大${numChinese[yearIndex]}${['上', '下'][Number(allNums[2]) - 1]}`;
                                return `${semesterDate} (${gradeChinese})`;
                            };
                            let semesterGrade = {};
                            semester.forEach((s) => semesterGrade[s] = { semester: s, allScore: 0, getScore: 0, avgScore: 0, acaScore: 0, acgScore: 0, acvScore: 0 });
                            merge(Object.values(courses)).forEach((course) => {
                                if (course['学年学期']) {
                                    semesterGrade[course['学年学期']]['allScore'] += course['学分'];
                                    semesterGrade[course['学年学期']]['getScore'] += course['学分'] * gradeMap[course['成绩']];
                                }
                            })
                            semesterGrade = sorted(Object.values(semesterGrade), 'semester');
                            for (let i = 0; i < semesterGrade.length; i++) {
                                semesterGrade[i].avgScore = parseFloat(`${semesterGrade[i].getScore / semesterGrade[i].allScore}`).toFixed(2);
                                semesterGrade[i].acaScore = semesterGrade[i].allScore;
                                semesterGrade[i].acgScore = semesterGrade[i].getScore;
                                for (let j = 0; j < i; j++) {
                                    semesterGrade[i].acaScore += semesterGrade[j].allScore;
                                    semesterGrade[i].acgScore += semesterGrade[j].getScore;
                                }
                                semesterGrade[i].acvScore = parseFloat(`${semesterGrade[i].acgScore / semesterGrade[i].acaScore}`).toFixed(2);
                                semesterGrade[i].semester = semesterDateToChinese(earliestYear, semesterGrade[i].semester);
                            }
                            recommendClasses.forEach((recommendClass) => {
                                let courseClassesIndex = courseClassesObj.findIndex((x) => x['KZM'] === recommendClass);
                                if (courseClassesIndex >= 0) {
                                    courses[recommendClass].forEach((c) => {
                                        let recommendSemester = '';
                                        if (c['建议修读学期']) recommendSemester = semesterIndexToDate(earliestYear, c['建议修读学期']);
                                        c['建议修读学期'] = recommendSemester;
                                    });
                                }
                            })
                            let orderedCourses = [];
                            allCourseClasses = courseClassSorted(allCourseClasses);
                            allCourseClasses.forEach((courseClass) => {
                                if (Array.isArray(courses[courseClass]) && courses[courseClass].length > 0) {
                                    courses[courseClass] = sorted(courses[courseClass], ['课程名', '建议修读学期', '学年学期', '是否通过'], [false, false, false, true]);
                                    orderedCourses = orderedCourses.concat(courses[courseClass]);
                                }
                            });
                            let suggestCourses = [];
                            let notPassCourses = orderedCourses.filter((c) => c['是否通过'] !== '通过');
                            recommendClasses = courseClassSorted(recommendClasses)
                            recommendClasses.forEach((recommendClass) => {
                                let tmpCourseList = notPassCourses.filter(c => c['课程类型'] === recommendClass);
                                tmpCourseList = sorted(tmpCourseList, '建议修读学期');
                                if (recommendClass === '专业选修课') {
                                    let tmpLimitCourseList = tmpCourseList.filter((c) => c['备注'].includes('限选'));
                                    let tmpFreeCourseList = tmpCourseList.filter((c) => !c['备注'].includes('限选'));
                                    tmpCourseList = tmpLimitCourseList.concat(tmpFreeCourseList);
                                }
                                suggestCourses = suggestCourses.concat(tmpCourseList);
                            })
                            orderedCourses.forEach((c) => {
                                if (c['学年学期']) c['学年学期'] = semesterDateToChinese(earliestYear, c['学年学期']);
                                if (c['建议修读学期']) c['建议修读学期'] = semesterDateToChinese(earliestYear, c['建议修读学期']);
                            });
                            let gradeContent = '学年学期,学期学分,学期GPA,累计学分,累计GPA\n';
                            for (let grade of semesterGrade)
                                gradeContent += `${grade.semester},${grade.allScore},${grade.avgScore},${grade.acaScore},${grade.acvScore}\n`;
                            let courseContent = '课程名,学分,成绩,是否通过,学年学期,建议修读学期,课程类型,课程性质,备注\n';
                            for (let course of orderedCourses)
                                courseContent += `${course['课程名']},${course['学分']},${course['成绩']},${course['是否通过']},${course['学年学期']},${course['建议修读学期']},${course['课程类型']},${course['课程性质']},${course['备注']}\n`;
                            let suggestContent = '以下是根据数据自动生成的推荐修读课程,仅供参考\n';
                            suggestContent += '课程名,学分,成绩,是否通过,学年学期,建议修读学期,课程类型,课程性质,备注\n';
                            for (let course of suggestCourses)
                                suggestContent += `${course['课程名']},${course['学分']},${course['成绩']},${course['是否通过']},${course['学年学期']},${course['建议修读学期']},${course['课程类型']},${course['课程性质']},${course['备注']}\n`;
                            let csvContent = `${courseContent}\n${gradeContent}\n${progressContent}\n${suggestContent}\n`;
                            let blob = new Blob(['\ufeff' + csvContent], {type: 'text/csv;charset=utf-8;'});
                            let fileName = '修读课程统计.csv';
                            downloadFile(URL.createObjectURL(blob), fileName);
                            bm.message({
                                type: 'success',
                                message: '修读课程统计表格生成成功',
                                duration: 2000,
                            });
                        });
                    }).fail((err) => {
                        console.error(err);
                    });
                }
                function setTab() {
                    let stuServeCenter = ampDesktopNav.firstElementChild;
                    let div = makeElement('div', {
                        id: 'download-training-program',
                        class: stuServeCenter.className.replace(/\s?amp-active/, ''),
                        title: '修读课程统计下载',
                    }, {
                        innerHTML: '修读课程统计下载',
                    }, {}, {
                        click: function () {
                            let _this = this;
                            $.ajax({
                                method: 'POST',
                                url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
                                data: {
                                    BYNJDM: '-',
                                },
                            }).then((res) => {
                                let rows = res.datas.cxscfakz.rows;
                                let i = 0;
                                while (!rows[i]['KZM'].includes('基本通识课') && i < rows.length)
                                    i++;
                                let courseClass = rows[i];
                                $.ajax({
                                    method: 'POST',
                                    url: 'http://ehall.szu.edu.cn/jwapp/sys/qxfacx/modules/pyfacxepg/kzkccx.do',
                                    data: {
                                        KZH: courseClass.KZH,
                                        PYFADM: courseClass.PYFADM,
                                        pageSize: 10,
                                        pageNumber: 1,
                                        BYNJDM: '-',
                                    },
                                }).then(() => {
                                    setTimeout(() => {
                                        $('#ampDesktopNav')[0].firstElementChild.click();
                                    }, 50);
                                    downloadCourseStatistics();
                                }).fail(() => {
                                    console.log('fail (code: -2)');
                                    bm.message({
                                        type: 'info',
                                        message: `将跳转至"全校培养方案查询",跳转后请手动关闭打开的页面`,
                                        duration: 2000,
                                    });
                                    setTimeout(() => {
                                        let btn = $('[amp-title=全校培养方案查询]')[0];
                                        btn.setAttribute('amp-unviewabledescription', 'true');
                                        btn.click();
                                        setTimeout(() => {
                                            _this.click();
                                        }, 50);
                                    }, 3000);
                                });
                            }).fail(() => {
                                console.log('fail (code: -1)');
                                bm.message({
                                    type: 'info',
                                    message: `将跳转至"学业完成查询",跳转后请手动关闭打开的页面`,
                                    duration: 2000,
                                });
                                setTimeout(() => {
                                    let btn = $('.card-bus-content [amp-title=学业完成查询]')[0]
                                    btn.setAttribute('amp-unviewabledescription', 'true');
                                    btn.click();
                                    setTimeout(() => {
                                        _this.click();
                                    }, 50);
                                }, 3000);
                            });
                        },
                    });
                    insertBefore(div, stuServeCenter, -1);
                }
                execUntil(setTab, () => {
                    return $('#ampDesktopNav')[0].firstElementChild;
                });
            }
            let ampHasNoLogin = document.getElementById('ampHasNoLogin');
            if (ampHasNoLogin && sessionStorage.ampUserId === 'guest') {
                ampHasNoLogin.click();
                insertTabButton();
            }
            if (sessionStorage.ampUserId !== 'guest') {
                if (location.href.includes('/jwapp/sys/czjl')) {
                    // 成长记录
                    execUntil(() => {
                        let el = document.getElementsByClassName('czjl-sixItem-container')[0];
                        el.innerHTML = el.innerHTML.replace(/<!--\s*/g, '').replace(/\s*-->+/g, '');
                    }, () => {
                        return document.getElementsByClassName('czjl-sixItem-container')[0];
                    });
                } else if (location.href.includes('/jwapp/sys/jwwspj')) {
                    // 网上评教
                    execUntil(() => {
                        let title = document.getElementsByClassName('timu-title')[0];
                        let btn = makeElement('button', {
                            id: 'quick-set',
                        }, {
                            innerHTML: '一键五星+评价',
                        }, {
                            'border': '0',
                            'width': '300px',
                            'height': '40px',
                            'margin-left': '10px',
                            'fontW-weight': 'bold',
                            'font-size': '16px',
                            'color': 'white',
                            'background-color': '#d22e2e',
                        }, {
                            click: function () {
                                let submit_btn = document.querySelector('.saveBtn [data-action=提交]');
                                if (submit_btn && submit_btn.getAttribute('disabled') !== null) {
                                    bm.message({
                                        type: 'warning',
                                        message: '你已经评教过了',
                                        duration: 2000,
                                    });
                                } else if (submit_btn) {
                                    $('[data-x-bl=100]').toArray().forEach(s => s.firstElementChild.click());
                                    $('textarea').val(prompt('请提供一个默认的教师评价'));
                                }
                                return false;
                            }
                        });
                        insertBefore(btn, title);
                    }, () => {
                        let title = document.getElementsByClassName('timu-title')[0];
                        let quickSet = document.getElementById('quick-set');
                        return title && !quickSet;
                    });
                } else if (location.href.includes('/new/index.html')) {
                    // 办事大厅
                    if ($('#ampDesktopNav')[0] && !$('#download-training-program')[0])
                        insertTabButton();
                } else if (location.href.includes('/jwapp/sys/xywccx')) {
                    // 学业完成查询
                    function insertBarButton () {
                        let scoreMaps = {
                            'A+': 4.5,
                            'A': 4.0,
                            'B+': 3.5,
                            'B': 3.0,
                            'C+': 2.5,
                            'C': 2.0,
                            'D': 1.0,
                            'F': 0.0,
                        }
                        function setList (courses, sem) {
                            function createInfo () {
                                let studentName = sessionStorage.ampUserName;
                                let weightedScore = courses.map((c) => parseFloat(c['学分']) * scoreMaps[c['成绩']]);
                                let score = courses.map((c) => parseFloat(c['学分']));
                                let avgScore = parseFloat(`${sum(weightedScore) / sum(score)}`).toFixed(2);
                                return makeElement('div', {}, {
                                    innerHTML: `
                                        <p style="margin-top: 40px">${studentName} 同学,你本学期的绩点是:</p>
                                        <p style="font-size: 120px color: #fff306 height: 120px margin-top: 30px"><i>${avgScore}</i></p>
                                        <p style="margin: 70px 0 60px">下学期再接再厉哦!</p>
                                    `,
                                }, {
                                    'font-size': '30px',
                                    'background-color': '#fe5f5e',
                                    'margin-bottom': '20px',
                                    'width': '640px',
                                    'display': 'flex',
                                    'flex-direction': 'column',
                                    'align-items': 'center',
                                    'color': 'white',
                                });
                            }
                            function createTable () {
                                return makeElement('div', {
                                    id: 'rainbow-grade-table',
                                }, {}, {
                                    'width': 'max-content',
                                    'height': 'max-content',
                                    'display': 'flex',
                                    'flex-direction': 'column',
                                    'margin': '200px',
                                    'align-items': 'center',
                                    'background-color': 'white',
                                    'line-height': 'normal',
                                });
                            }
                            function createItem (course) {
                                function createText () {
                                    let text = HTMLElement.$mkel(
                                        'div', {}, {
                                            innerHTML: `
													<p style="
														overflow: hidden;
														text-overflow: ellipsis;
														white-space: nowrap;
													">课程名:${course['课程名']}</p>
													<p>课程号:${course['课程号']}</p>
													<p>学分:${course['学分']}</p>
													<p>课程类别:${course['课程类别']}</p>
												`,
                                        }, {
                                            'width': '400px',
                                            'font-size': '28px',
                                            'padding-left': '20px',
                                        }
                                    )
                                    let container = HTMLElement.$mkel(
                                        'div', {}, {}, {
                                            'display': 'flex',
                                            'flex-direction': 'column',
                                            'align-items': 'center',
                                            'justify-content': 'center',
                                        }
                                    )
                                    container.appendChild(text)
                                    return container
                                }
                                function createScore () {
                                    let text = HTMLElement.$mkel(
                                        'div', {}, { innerHTML: course['成绩'] }, {
                                            'font-size': '90px',
                                        },
                                    )
                                    let container = HTMLElement.$mkel(
                                        'div', {}, {}, {
                                            'display': 'flex',
                                            'flex-direction': 'column',
                                            'align-items': 'center',
                                            'justify-content': 'center',
                                            'width': '160px',
                                        }
                                    )
                                    container.appendChild(text)
                                    return container
                                }
                                function createCard () {
                                    let cmap = {
                                        'A+': '#e7322f',
                                        'A': '#fe5f5e',
                                        'B+': '#7648d9',
                                        'B': '#4d89d7',
                                        'C+': '#ff7905',
                                        'C': '#ffa303',
                                        'D': '#219b3e',
                                        'F': '#4e4e4e',
                                    };
                                    let bgc = cmap[course['成绩']];
                                    let card = makeElement('div', {}, {}, {
                                        'display': 'flex',
                                        'flex-direction': 'row',
                                        'align-items': 'center',
                                        'justify-content': 'space-between',
                                        'width': '600px',
                                        'height': '200px',
                                        'margin-bottom': '20px',
                                        'border-radius': '12px',
                                        'background-color': bgc,
                                        'color': 'white',
                                    });
                                    let text = createText();
                                    let score = createScore();
                                    card.appendChild(text);
                                    card.appendChild(score);
                                    return card;
                                }
                                return createCard();
                            }
                            let semList = Array.from(new Set(courses.map((c) => c['学年学期']).filter((c) => c)));
                            if (sem && !semList.includes(sem)) {
                                bm.message({
                                    type: 'warning',
                                    message: '输入不合法,默认生成最新学期的彩虹地毯',
                                    duration: 2000,
                                });
                                sem = max(semList);
                            }
                            if (!sem) sem = max(semList);
                            courses = courses.filter((c) => c['学年学期'] === sem);
                            courses = sorted(courses, [(c) => parseFloat(c['学分']), (c) => scoreMaps[c['成绩']]], [true, true]);
                            let table = createTable();
                            table.appendChild(createInfo());
                            courses.forEach((course) => table.appendChild(createItem(course)));
                            insertBefore(table, $('.bh-paper-pile-body')[0]);
                        }
                        function downloadList () {
                            function callbackfn (canvas) {
                                downloadFile(canvas.toDataURL('image/png', 1.0), '彩虹地毯.png');
                            }
                            function afterGetHtml2canvas() {
                                html2canvas($('#rainbow-grade-table')[0], { dpi: window.devicePixelRatio }).then(callbackfn);
                            }
                            if (typeof html2canvas === 'undefined') {
                                $.getScript('https://html2canvas.hertzen.com/dist/html2canvas.js', afterGetHtml2canvas);
                            } else {
                                afterGetHtml2canvas();
                            }
                        }
                        function setBar (courses) {
                            if ($('#xywcqk_chdt')[0])
                                return;
                            let bar = $('.bh-paper-pile-dialog-container')[0];
                            if (!bar)
                                return;
                            bar.style.width = `${bar.offsetWidth + 100}px`;
                            let a = makeElement('a', {
                                id: 'xywcqk_chdt',
                            }, {
                                innerHTML: '彩虹地毯',
                            }, {
                                'font-size': '14px',
                                'margin-left': '20px',
                                'cursor': 'pointer',
                            });
                            let last = $('#xywcqk_bxqxk')[0];
                            insertBefore(a, last);
                            insertBefore(last, a);
                            a.addEventListener('click', function () {
                                if ($('#rainbow-grade-table')[0])
                                    return;
                                let semester = prompt(`请输入需要生成彩虹地毯的学年学期,格式为 'yyyy-yyyy-n' ,如 '2020-2021-2' 表示2020学年至2021学年第2学期,若输入不合法或不输入,将默认生成最新学期的彩虹地毯`);
                                while (semester && !/^\d{4}-\d{4}-\d$/.exec(semester))
                                    semester = prompt(`输入格式必须为 'yyyy-yyyy-n'`);
                                if (!semester) {
                                    bm.message({
                                        type: 'warning',
                                        message: '未输入,默认生成最新学期的彩虹地毯',
                                        duration: 2000,
                                    });
                                    semester = undefined;
                                } else {
                                    let [yearStart, yearEnd, num] = semester.match(/\d+/g);
                                    if ((parseInt(yearEnd) - parseInt(yearStart)) !== 1 || !inRange(parseInt(num), 1, 2)) {
                                        bm.message({
                                            type: 'warning',
                                            message: '输入不合法,默认生成最新学期的彩虹地毯',
                                            duration: 2000,
                                        });
                                        semester = undefined;
                                    }
                                }
                                setList(courses, semester);
                                downloadList();
                            });
                        }
                        if (!window.courseClasses) {
                            $.ajax({
                                method: 'POST',
                                url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
                                data: {
                                    BYNJDM: '-',
                                },
                            }).then((res) => {
                                let courseClasses = res.datas.cxscfakz.rows;
                                window.courseClasses = courseClasses;
                                courseClasses = filterKey(courseClasses, ['KZM', 'KZH', 'PYFADM', 'YQXF', 'WCXF', 'YQMS', 'WCMS', 'YQWCKZS', 'WCKZS', 'FKZH']);
                                courseClasses = courseClasses.filter((c) => c['FKZH'] !== '-1');
                                Promise.all(courseClasses.map((courseClass) => {
                                    return $.ajax({
                                        method: 'POST',
                                        url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakzkc.do',
                                        data: {
                                            BYNJDM: '-',
                                            KZH: courseClass.KZH,
                                            PYFADM: courseClass.PYFADM,
                                            pageSize: 999,
                                            pageNumber: 1,
                                        },
                                    }).then((res) => {
                                        let courseList = res.datas.cxscfakzkc.rows;
                                        courseList = filterKey(courseList, ['KCM', 'KCH', 'CJ', 'XF', 'KCLBDM_DISPLAY', 'SFTG_DISPLAY', 'XNXQDM']);
                                        courseList = renameKey(courseList, {
                                            'KCM': '课程名',
                                            'KCH': '课程号',
                                            'CJ': '成绩',
                                            'XF': '学分',
                                            'KCLBDM_DISPLAY': '课程类别',
                                            'SFTG_DISPLAY': '是否通过',
                                            'XNXQDM': '学年学期'
                                        });
                                        courseList.forEach((c) => {
                                            c['成绩'] = c['成绩'] || 'F';
                                            c['学分'] = parseFloat(c['学分']).toFixed(2);
                                        })
                                        return courseList;
                                    })
                                })).then((res) => {
                                    setBar(merge(res));
                                    window.courseClasses = undefined;
                                });
                            });
                        }
                    }
                    monitor(document.body, ['childList'], function(events) {
                        for (let event of events)
                            for (let node of event.addedNodes)
                                if (node.classList.contains('bh-paper-pile-dialog'))
                                    insertBarButton();
                    });
                } else if (location.href.includes('/jwapp/sys/zzy')) {
                    // 转专业
                    function addCJ () {
                        if (document.getElementById('convenient-szu-zzy-cj'))
                            return;
                        $.ajax({
                            type: 'POST',
                            url: 'http://ehall.szu.edu.cn/jwapp/sys/zzy/modules/xszzysq/cxxszzybmsq.do',
                            data: { XH: localStorage.ampUserId },
                        }).then((res) => {
                            let info = res.datas.cxxszzybmsq.rows[0];
                            if (info) {
                                let bar = document.createElement('span');
                                bar.setAttribute('id', 'convenient-szu-zzy-cj');
                                if (info['GGKCJ']) bar.innerHTML += `公共课成绩:${info['GGKCJ']}&emsp;`;
                                if (info['ZYKCJ']) bar.innerHTML += `专业课成绩:${info['ZYKCJ']}&emsp;`;
                                if (info['ZCJ']) bar.innerHTML += `总成绩:${info['ZCJ']}&emsp;`;
                                if (bar.innerHTML.length > 0) {
                                    execUntil(() => {
                                        document.querySelector('.bh-text-center.bh-pull-left').appendChild(bar);
                                    }, () => {
                                        return document.querySelector('.bh-text-center.bh-pull-left');
                                    });
                                }
                            }
                        })
                    }
                    if (location.href.endsWith('#/xszzysq'))
                        addCJ();
                    window.addEventListener('hashchange', function() {
                        if (location.href.endsWith('#/xszzysq'))
                            addCJ();
                    });
                }
            }
            execUntil(() => {
                monitor($('#ampTabContentItem0')[0], ['childList', 'subtree'], function() {
                    $('.appFlag.widget-app-item').attr('amp-unviewabledescription', 'true');
                    $('.appFlag.amp-app-card-hover-big').attr('amp-unviewabledescription', 'true');
                });
            }, () => {
                return $('#ampTabContentItem0')[0];
            });
            let ampServiceCenterSearchApps = $('#ampServiceCenterSearchApps')[0];
            if (ampServiceCenterSearchApps) {
                monitor(ampServiceCenterSearchApps, ['childList', 'subtree'], function() {
                    $('.appFlag.widget-app-item').attr('amp-unviewabledescription', 'true');
                    $('.appFlag.amp-app-card-hover-big').attr('amp-unviewabledescription', 'true');
                })
            }
        } else if (location.host === '172.30.255.2') {
            if (location.href.includes('.htm')) {
                let username = document.getElementById('username');
                let password = document.getElementById('password');
                let submit = document.querySelector('#submit[type=submit]');
                if (username && password && submit) {
                    username.value = account.cid;
                    password.value = account.pwd;
                    submit.click();
                }
            }
        } else if (location.host === '172.30.255.42') {
            if (location.href.includes('.htm')) {
                execUntil(() => {
                    let username = document.querySelector('input.edit_lobo_cell[name=DDDDD]');
                    let password = document.querySelector('input.edit_lobo_cell[name=upass]');
                    let submit = document.querySelector('input.edit_lobo_cell[name="0MKKey"]');
                    let savePassword = document.querySelector('input.edit_lobo_cell[name=savePassword]');
                    username.value = account.cid;
                    password.value = account.pwd;
                    if (savePassword) {
                        savePassword.checked = true;
                    }
                    submit.click();
                }, () => {
                    let username = document.querySelector('input.edit_lobo_cell[name=DDDDD]');
                    let password = document.querySelector('input.edit_lobo_cell[name=upass]');
                    let submit = document.querySelector('input.edit_lobo_cell[name="0MKKey"]');
                    return username && password && submit;
                });
            }
        } else if (location.host === '192.168.84.3:9090') {
            let client = document.querySelector('[name=client]');
            let buildingName = document.querySelector('[name=buildingId]');
            let roomName = document.querySelector('[name=roomName]');
            let beginTime = document.querySelector('#beginTime');
            let endTime = document.querySelector('#endTime');
            if (client && buildingName && roomName) {
                if (account.clientSelectedIndex !== client.selectedIndex) {
                    account.clientSelectedIndex = client.selectedIndex;
                    account.client = client.value;
                    delete account.buildingNameSelectedIndex;
                    delete account.buildingId;
                    delete account.roomName;
                    GM_setValue('account', account);
                }
                if (account.buildingId)
                    buildingName.selectedIndex = account.buildingNameSelectedIndex;
                buildingName.removeAttribute('onchange');
                buildingName.onchange = function () {
                    account.buildingNameSelectedIndex = this.selectedIndex;
                    account.buildingId = this.options[this.selectedIndex].value;
                    GM_setValue('account', account);
                };
                if (account.roomName) roomName.value = account.roomName;
                roomName.oninput = function () {
                    account.roomName = this.value;
                    GM_setValue('account', account);
                };
            } else if (beginTime && endTime && location.href.indexOf('login.do') >= 0) {
                let toDate = (date) => date.toLocaleDateString().replace(/\//g, '-');
                let now = new Date();
                let twentyDaysAgo = new Date(now.getTime() - 1000 * 86400 * 19);
                beginTime.value = toDate(twentyDaysAgo);
                endTime.value = toDate(now);
                document.querySelector('[name=type]').selectedIndex = 1;
                document.querySelector('[type=submit]').click();
            }
        } else if (location.host === 'drcom.szu.edu.cn') {
            if (location.href.includes('.htm')) {
                let username_el = document.querySelector('input[name=DDDDD]');
                let password_el = document.querySelector('input[name=upass]');
                let submit_el = document.querySelector('input[type=submit]');
                if (username_el && password_el && submit_el) {
                    username_el.value = account.cid;
                    password_el.value = account.pwd;
                    submit_el.click();
                }
            }
        } else if (location.host.match(/bkxk.*?\.szu\.edu\.cn/)) {
            let loginName_el = document.getElementById('loginName');
            let loginPwd_el = document.getElementById('loginPwd');
            if (loginName_el && loginPwd_el) {
                loginName_el.value = account.uid;
                loginPwd_el.value = account.pwd;
            }
        } else if (location.host === 'self.szu.edu.cn') {
            let account_el = document.getElementById('account');
            let pass_el = document.getElementById('pass');
            let submit_el = document.querySelector('input[type=submit]');
            if (account_el && pass_el && submit_el) {
                account_el.value = account.cid;
                pass_el.value = account.pwd;
                submit_el.click();
            }
        } else if (location.host.match(/authserver.*?\.webvpn.szu.edu.cn/)) {
            let username_el = document.getElementById('username');
            let password_el = document.getElementById('password');
            let helper_el = document.querySelector('.iCheck-helper');
            let button_submit = document.querySelector('button[type=submit]');
            if (username_el && password_el && helper_el && button_submit) {
                helper_el.click();
                username_el.setAttribute('value', account.cid);
                password_el.setAttribute('value', account.pwd);
                button_submit.click();
            }
        }
    });
})();