User:逆襲的天邪鬼/Twinkle-lzh/twinklespeedy.js

// // vim: set noet sts=0 sw=8:

(function($){

/* **************************************** *** twinklespeedy.js: CSD module **************************************** * Mode of invocation:    Tab ("CSD") * Active on:             Non-special, existing pages * Config directives in:  TwinkleConfig * * NOTE FOR DEVELOPERS: *  If adding a new criterion, add it to the appropriate places at the top of *   twinkleconfig.js. Also check out the default values of the CSD preferences *  in twinkle.js, and add your new criterion to those if you think it would be *   good. */

Twinkle.speedy = function twinklespeedy { // Disable on: // * special pages // * Flow pages // * non-existent pages if (mw.config.get('wgNamespaceNumber') < 0 || mw.config.get('wgPageContentModel') === 'flow-board' || !mw.config.get('wgArticleId')) { return; }

Twinkle.addPortletLink( Twinkle.speedy.callback, "速刪", "tw-csd", "速刪" ); };

// This function is run when the CSD tab/header link is clicked Twinkle.speedy.callback = function twinklespeedyCallback { Twinkle.speedy.initDialog(Morebits.userIsInGroup( 'sysop' ) ? Twinkle.speedy.callback.evaluateSysop : Twinkle.speedy.callback.evaluateUser, true); };

// Used by unlink feature Twinkle.speedy.dialog = null;

// The speedy criteria list can be in one of several modes Twinkle.speedy.mode = { sysopSingleSubmit: 1, // radio buttons, no subgroups, submit when "Submit" button is clicked sysopRadioClick: 2, // radio buttons, no subgroups, submit when a radio button is clicked sysopMultipleSubmit: 3, // check boxes, subgroups, "Submit" button already present sysopMultipleRadioClick: 4, // check boxes, subgroups, need to add a "Submit" button userMultipleSubmit: 5, // check boxes, subgroups, "Submit" button already pressent userMultipleRadioClick: 6, // check boxes, subgroups, need to add a "Submit" button userSingleSubmit: 7, // radio buttons, subgroups, submit when "Submit" button is clicked userSingleRadioClick: 8, // radio buttons, subgroups, submit when a radio button is clicked

// are we in "delete page" mode? // (sysops can access both "delete page" [sysop] and "tag page only" [user] modes) isSysop: function twinklespeedyModeIsSysop(mode) { return mode === Twinkle.speedy.mode.sysopSingleSubmit || mode === Twinkle.speedy.mode.sysopMultipleSubmit || mode === Twinkle.speedy.mode.sysopRadioClick || mode === Twinkle.speedy.mode.sysopMultipleRadioClick; },   // do we have a "Submit" button once the form is created? hasSubmitButton: function twinklespeedyModeHasSubmitButton(mode) { return mode === Twinkle.speedy.mode.sysopSingleSubmit || mode === Twinkle.speedy.mode.sysopMultipleSubmit || mode === Twinkle.speedy.mode.sysopMultipleRadioClick || mode === Twinkle.speedy.mode.userMultipleSubmit || mode === Twinkle.speedy.mode.userMultipleRadioClick || mode === Twinkle.speedy.mode.userSingleSubmit; },   // is db-multiple the outcome here? isMultiple: function twinklespeedyModeIsMultiple(mode) { return mode === Twinkle.speedy.mode.userMultipleSubmit || mode === Twinkle.speedy.mode.sysopMultipleSubmit || mode === Twinkle.speedy.mode.userMultipleRadioClick || mode === Twinkle.speedy.mode.sysopMultipleRadioClick; }, };

// Prepares the speedy deletion dialog and displays it Twinkle.speedy.initDialog = function twinklespeedyInitDialog(callbackfunc) { var dialog; Twinkle.speedy.dialog = new Morebits.simpleWindow( Twinkle.getPref('speedyWindowWidth'), Twinkle.getPref('speedyWindowHeight') ); dialog = Twinkle.speedy.dialog; dialog.setTitle( "選擇速刪理由" ); dialog.setScriptName( "Twinkle" ); // dialog.addFooterLink( "快速删除方针", "WP:CSD" ); // dialog.addFooterLink( "Twinkle帮助", "WP:TW/DOC#speedy" );

var form = new Morebits.quickForm( callbackfunc, (Twinkle.getPref('speedySelectionStyle') === 'radioClick' ? 'change' : null) ); if( Morebits.userIsInGroup( 'sysop' ) ) { form.append( {               type: 'checkbox',                list: [                    {                        label: '只標記，不刪除',                        value: 'tag_only',                        name: 'tag_only',                        tooltip: '如果您只想標記此頁面而不是刪除',                        checked : Twinkle.getPref('deleteSysopDefaultToTag'),                        event: function( event ) {                            var cForm = event.target.form;                            var cChecked = event.target.checked;                            // enable/disable talk page checkbox                            if (cForm.talkpage) {                                cForm.talkpage.disabled = cChecked;                                cForm.talkpage.checked = !cChecked && Twinkle.getPref('deleteTalkPageOnDelete');                            }                            // enable/disable redirects checkbox cForm.redirects.disabled = cChecked; cForm.redirects.checked = !cChecked; // enable/disable delete multiple cForm.delmultiple.disabled = cChecked; cForm.delmultiple.checked = false; // enable/disable open talk page checkbox cForm.openusertalk.disabled = cChecked; cForm.openusertalk.checked = false;

// enable/disable notify checkbox // cForm.notify.disabled = !cChecked; // cForm.notify.checked = cChecked; // enable/disable multiple cForm.multiple.disabled = !cChecked; cForm.multiple.checked = false;

Twinkle.speedy.callback.modeChanged(cForm);

event.stopPropagation; }                   }                ]            } );

var deleteOptions = form.append( {               type: 'div',                name: 'delete_options'            } ); deleteOptions.append( {               type: 'header',                label: '刪除相關設定'            } ); if (mw.config.get('wgNamespaceNumber') % 2 === 0 && (mw.config.get('wgNamespaceNumber') !== 2 || (/\//).test(mw.config.get('wgTitle')))) { // hide option for user pages, to avoid accidentally deleting user talk page deleteOptions.append( {               type: 'checkbox',                list: [                    {                        label: '刪除討論頁',                        value: 'talkpage',                        name: 'talkpage',                        tooltip: "刪除時附帶刪除此頁面的討論頁. ",                        checked: Twinkle.getPref('deleteTalkPageOnDelete'),                        disabled: Twinkle.getPref('deleteSysopDefaultToTag'),                        event: function( event ) {                            event.stopPropagation;                        }                    }                ]            } ); }       deleteOptions.append( {                type: 'checkbox',                list: [                    {                        label: '刪除重定向',                        value: 'redirects',                        name: 'redirects',                        tooltip: "刪除到此頁的重定向. ",                        checked: Twinkle.getPref('deleteRedirectsOnDelete'),                        disabled: Twinkle.getPref('deleteSysopDefaultToTag'),                        event: function( event ) {                            event.stopPropagation;                        }                    }                ]            } ); deleteOptions.append( {           type: 'checkbox',            list: [                {                    label: '應用多個理由刪除',                    value: 'delmultiple',                    name: 'delmultiple',                    tooltip: "您可選擇應用於該頁的多个理由. ",                    event: function( event ) {                        Twinkle.speedy.callback.modeChanged( event.target.form );                        event.stopPropagation;                    }                }            ]        } ); deleteOptions.append( {               type: 'checkbox',                list: [                    {                        label: '開啟用戶對話頁',                        value: 'openusertalk',                        name: 'openusertalk',                        tooltip: '此項的預設值為開啟您的對話頁設定. 在您選擇應用多條理由刪除時此項將保持不變. ',                        checked : false                    }                ]            } ); }

var tagOptions = form.append( {           type: 'div',            name: 'tag_options'        } );

if( Morebits.userIsInGroup( 'sysop' ) ) { tagOptions.append( {               type: 'header',                label: '標記相關設定'            } ); }

/*   tagOptions.append( {            type: 'checkbox',            list: [                {                    label: '如可能，通知建立者',                    value: 'notify',                    name: 'notify',                    tooltip: "如果開啟，提請刪除時將會在建立者的對話頁加入一个通知模板. ",                    checked: !Morebits.userIsInGroup( 'sysop' ) || Twinkle.getPref('deleteSysopDefaultToTag'),                    disabled: Morebits.userIsInGroup( 'sysop' ) && !Twinkle.getPref('deleteSysopDefaultToTag'),                    event: function( event ) {                        event.stopPropagation;                    }                }            ]        } ); tagOptions.append( {           type: 'checkbox',            list: [                {                    label: '應用多个理由',                    value: 'multiple',                    name: 'multiple',                    tooltip: "您可選擇應用於該頁的多個理由. ",                    disabled: Morebits.userIsInGroup( 'sysop' ) && !Twinkle.getPref('deleteSysopDefaultToTag'),                    event: function( event ) {                        Twinkle.speedy.callback.modeChanged( event.target.form );                        event.stopPropagation;                    }                }            ]        } );

form.append( {           type: 'div',            name: 'work_area',            label: '初始化CSD模块失败，请重试，或将这报告给Twinkle开发者. '        } );

if( Twinkle.getPref( 'speedySelectionStyle' ) !== 'radioClick' ) { form.append( { type: 'submit' } ); }

var result = form.render; dialog.setContent( result ); dialog.display;

Twinkle.speedy.callback.modeChanged( result );

// if sysop, check if CSD is already on the page and fill in custom rationale if (Morebits.userIsInGroup('sysop') && $("#delete-reason").length) { var customOption = $("input[name=csd][value=reason]")[0];

if (Twinkle.getPref('speedySelectionStyle') !== 'radioClick') { // force listeners to re-init customOption.click; customOption.parentNode.appendChild(customOption.subgroup); }

customOption.subgroup.querySelector('input').value = decodeURIComponent($("#delete-reason").text).replace(/\+/g, ' '); } };

Twinkle.speedy.callback.getMode = function twinklespeedyCallbackGetMode(form) { var mode = Twinkle.speedy.mode.userSingleSubmit; if (form.tag_only && !form.tag_only.checked) { if (form.delmultiple.checked) { mode = Twinkle.speedy.mode.sysopMultipleSubmit; } else { mode = Twinkle.speedy.mode.sysopSingleSubmit; }   } else { if (form.multiple.checked) { mode = Twinkle.speedy.mode.userMultipleSubmit; } else { mode = Twinkle.speedy.mode.userSingleSubmit; }   }    if (Twinkle.getPref('speedySelectionStyle') === 'radioClick') { mode++; }

return mode; };

Twinkle.speedy.callback.modeChanged = function twinklespeedyCallbackModeChanged(form) { var namespace = mw.config.get('wgNamespaceNumber');

// first figure out what mode we're in   var mode = Twinkle.speedy.callback.getMode(form);

if (Twinkle.speedy.mode.isSysop(mode)) { $("[name=delete_options]").show; $("[name=tag_options]").hide; } else { $("[name=delete_options]").hide; $("[name=tag_options]").show; }

var work_area = new Morebits.quickForm.element( {           type: 'div',            name: 'work_area'        } );

if (mode === Twinkle.speedy.mode.userMultipleRadioClick || mode === Twinkle.speedy.mode.sysopMultipleRadioClick) { var evaluateType = Twinkle.speedy.mode.isSysop(mode) ? 'evaluateSysop' : 'evaluateUser';

work_area.append( {               type: 'div',                label: '當選擇完成後，點選：'            } ); work_area.append( {               type: 'button',                name: 'submit-multiple',                label: '提交',                event: function( event ) {                    Twinkle.speedy.callback[evaluateType]( event );                    event.stopPropagation;                }            } ); }

var radioOrCheckbox = (Twinkle.speedy.mode.isMultiple(mode) ? 'checkbox' : 'radio');

if (Twinkle.speedy.mode.isSysop(mode) && !Twinkle.speedy.mode.isMultiple(mode)) { work_area.append( { type: 'header', label: '自訂理由' } ); work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(Twinkle.speedy.customRationale, mode) } ); }

switch (namespace) { case 0: // article work_area.append( { type: 'header', label: '條目' } ); work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(Twinkle.speedy.articleList, mode) } ); break;

case 14: // category work_area.append( { type: 'header', label: '分類' } ); work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(Twinkle.speedy.categoryList, mode) } ); break;

default: break; }

// custom rationale lives under general criteria when tagging var generalCriteria = Twinkle.speedy.generalList; if(!Twinkle.speedy.mode.isSysop(mode)) { generalCriteria = Twinkle.speedy.customRationale.concat(generalCriteria); }   work_area.append( { type: 'header', label: '常規' } ); work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(generalCriteria, mode) });

if (mw.config.get('wgIsRedirect') || Morebits.userIsInGroup('sysop')) { work_area.append( { type: 'header', label: '重定向' } ); if (namespace === 0 || namespace === 118) { work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(Twinkle.speedy.redirectArticleList, mode) } ); }       work_area.append( { type: radioOrCheckbox, name: 'csd', list: Twinkle.speedy.generateCsdList(Twinkle.speedy.redirectList, mode) } ); }

var old_area = Morebits.quickForm.getElements(form, "work_area")[0]; form.replaceChild(work_area.render, old_area); };

Twinkle.speedy.generateCsdList = function twinklespeedyGenerateCsdList(list, mode) { // mode switches var isSysop = Twinkle.speedy.mode.isSysop(mode); var multiple = Twinkle.speedy.mode.isMultiple(mode); var hasSubmitButton = Twinkle.speedy.mode.hasSubmitButton(mode);

var openSubgroupHandler = function(e) { $(e.target.form).find('input').prop('disabled', true); $(e.target.form).children.css('color', 'gray'); $(e.target).parent.css('color', 'black').find('input').prop('disabled', false); $(e.target).parent.find('input:text')[0].focus; e.stopPropagation; };   var submitSubgroupHandler = function(e) { var evaluateType = Twinkle.speedy.mode.isSysop(mode) ? 'evaluateSysop' : 'evaluateUser'; Twinkle.speedy.callback[evaluateType](e); e.stopPropagation; };

return $.map(list, function(critElement) {       var criterion = $.extend({}, critElement);

if (multiple) { if (criterion.hideWhenMultiple) { return null; }           if (criterion.hideSubgroupWhenMultiple) { criterion.subgroup = null; }       } else { if (criterion.hideWhenSingle) { return null; }           if (criterion.hideSubgroupWhenSingle) { criterion.subgroup = null; }       }

if (isSysop) { if (criterion.hideWhenSysop) { return null; }           if (criterion.hideSubgroupWhenSysop) { criterion.subgroup = null; }       } else { if (criterion.hideWhenUser) { return null; }           if (criterion.hideSubgroupWhenUser) { criterion.subgroup = null; }       }

if (mw.config.get('wgIsRedirect') && criterion.hideWhenRedirect) { return null; }

if (criterion.subgroup && !hasSubmitButton) { if ($.isArray(criterion.subgroup)) { criterion.subgroup.push({                   type: 'button',                    name: 'submit',                    label: '提交',                    event: submitSubgroupHandler                }); } else { criterion.subgroup = [ criterion.subgroup, {                       type: 'button', name: 'submit', // ends up being called "csd.submit" so this is OK                        label: '提交', event: submitSubgroupHandler }               ];            }            // FIXME: does this do anything? criterion.event = openSubgroupHandler; }

if ( isSysop ) { var originalEvent = criterion.event; criterion.event = function(e) { if (multiple) return originalEvent(e);

var normalizedCriterion = Twinkle.speedy.normalizeHash[e.target.value]; $('[name=openusertalk]').prop('checked',                       Twinkle.getPref('openUserTalkPageOnSpeedyDelete').indexOf(normalizedCriterion) !== -1                    ); if ( originalEvent ) { return originalEvent(e); }           };        }

return criterion; }); };

Twinkle.speedy.customRationale = [ {       label: '自訂理由' + (Morebits.userIsInGroup('sysop') ? '（自訂刪除理由）' : ''), value: 'reason', tooltip: '該頁應明顯不適合維基大典. 如有疑問，請使用「議刪」而非「速刪」. ',       subgroup: { name: 'reason_1', type: 'input', label: '理由：', size: 60 },       hideWhenMultiple: true } ];

Twinkle.speedy.articleList = [ {       label: '極短且不知所云', value: '極短且不知所云', },   {        label: '生不立傳', value: '生不立傳', },   {        label: '非文言', value: '非文言' }, ];

Twinkle.speedy.categoryList = [ {       label: '空類', value: '空類', tooltip: '不適用於維護性質的類別. '   } ];

Twinkle.speedy.generalList = [ {       label: '作者請求', value: '作者請求', tooltip: '僅在頁面沒有其他作者（或者只有提刪者本人有實質編輯）的情況下適用. '   },    {        label: '毀我大典', value: '毀我大典', },   {        label: '廣告宣傳', value: '廣告宣傳', },   {        label: 'G8: 管理员因技术原因删除页面', value: 'g8', tooltip: '包括解封用户后删除用户页、因用户夺取而删除、删除MediaWiki页面、因移动请求而删除页面. ',       hideWhenUser: true },   {        label: 'G10: 原作者清空页面或提出删除，且贡献者只有一人', value: 'g10', tooltip: '对条目内容无实际修改的除外；提请须出于善意，及附有合理原因. ',       subgroup: { name: 'g10_rationale', type: 'input', label: '可选的解释：', tooltip: '比如作者在哪里请求了删除. ',           size: 60 },       hideSubgroupWhenSysop: true },   {        label: 'G11: 明显的广告宣传页面，或只有相关人物或团体的联系方法的页面', value: 'g11', tooltip: '页面只收宣传之用，并须完全重写才能贴合百科全书要求. 须注意，仅仅以某公司或产品为主题的条目，并不直接导致其自然满足此速删标准. '   },    {        label: 'G12: 未列明来源且语调负面的生者传记', value: 'g12', tooltip: '注意是未列明来源且语调负面，必须2项均符合. '   },    {        label: 'G13: 明显、拙劣的机器翻译', value: 'g13' },   {        label: 'G14: 超过两周没有进行任何翻译的非现代标准汉语页面', value: 'g14', tooltip: '包括所有未翻译的外语、汉语方言以及文言文. ',       hideWhenUser: true },   {        label: 'G15: 孤立页面，比如没有主页面的讨论页、指向空页面的重定向等', value: 'g15', tooltip: '包括以下几种类型：1. 没有对应文件的文件页面；2. 没有对应母页面的子页面，用户页子页面除外；3. 指向不存在页面的重定向；4. 没有对应内容页面的讨论页，讨论页存档和用户讨论页除外；5. 不存在注册用户的用户页及用户页子页面，随用户更名产生的用户页重定向除外. 请在删除时注意有无将内容移至他处的必要. 不包括在主页面挂有模板的讨论页. '   },    {        label: 'G16: 因为主页面侵权而创建的临时页面仍然侵权', value: 'g16', hideWhenUser: true } ];

Twinkle.speedy.redirectArticleList = [ {       label: 'R2: 跨名字空间重定向. ',       value: 'r2', tooltip: '由条目的名字空间重定向至非条目名字空间，或将用户页移出条目名字空间时遗留的重定向. '   } ];

Twinkle.speedy.redirectList = [ {       label: 'R3: 格式错误，或明显笔误的重定向. ',       value: 'r3', tooltip: '非一眼能看出的拼写错误和翻译或标题用字的争议应交由存废讨论处理. ',       subgroup: { name: 'r3_type', type: 'select', label: '适用类别：', list: [ { label: '请选择', value: '' }, { label: '标题繁简混用', value: '标题繁简混用. ' },               { label: '消歧义使用的括号或空格错误', value: '消歧义使用的括号或空格错误. ' },               { label: '间隔号使用错误', value: '间隔号使用错误. ' },               { label: '标题中使用非常见的错别字', value: '标题中使用非常见的错别字. ' },               { label: '移动侵权页面的临时页后所产生的重定向', value: '移动侵权页面的临时页后所产生的重定向. ' }           ]        },        hideSubgroupWhenSysop: true },   {        label: 'R5: 指向本身的重定向或循环的重定向. ',       value: 'r5', tooltip: '如A→B→C→……→A. '   } ];

Twinkle.speedy.normalizeHash = { 'reason': 'db', 'multiple': 'multiple', 'multiple-finish': 'multiple-finish', 'g1': 'g1', 'g2': 'g2', 'g3': 'g3', 'g5': 'g5', 'g8': 'g8', 'g10': 'g10', 'g11': 'g11', 'g12': 'g12', 'g13': 'g13', 'g14': 'g14', 'g15': 'g15', 'g16': 'g16', 'a1': 'a1', 'a2': 'a2', 'a3': 'a3', 'a5': 'a5', 'a6': 'a6', 'r2': 'r2', 'r3': 'r3', 'r5': 'r5', 'f1': 'f1', 'f3': 'f3', 'f4': 'f4', 'f5': 'f5', 'f6': 'f6', 'f7': 'f7', 'o1': 'o1', 'o3': 'o3', 'o4': 'o4' };

// keep this synched with MediaWiki:Deletereason-dropdown Twinkle.speedy.reasonHash = { 'reason': '', // General 'g1': '无实际内容', 'g2': '测试页', 'g3': '破坏', 'g5': '曾经依存废讨论被删除的重建内容', 'g8': '技术原因', 'g10': '作者请求', 'g11': '广告或宣传', 'g12': '未列明来源或违反生者传记的负面内容', 'g13': '明显且拙劣的机器翻译', 'g14': '超过两周没有翻译的非现代标准汉语页面', 'g15': '孤立页面', 'g16': '临时页面依然侵权', // Articles 'a1': '非常短而无定义或内容', 'a2': '内容只包含参考、链接、模板或/及分类', 'a3': '与其他中文维基计划内容相同的文章', 'a5': '条目建立时之内容即与其他现有条目内容相同', 'a6': '复制自其他维基百科语言版本', // Redirects 'r2': '跨名字空间重定向', 'r3': '标题错误的重定向', 'r5': '指向本身的重定向或循环的重定向', // Images and media 'f1': '重复的图片', 'f3': 'Category:未知版权的档案', 'f4': 'Category:來源不明檔案', 'f5': '已有高分辨率的图片取代', 'f6': '孤立而没有被条目使用的非自由版权图片', 'f7': 'Category:与维基共享资源重复的档案', // User pages 'o1': '用户请求删除自己的用户页或其子页面', 'o3': '匿名用户的讨论页', // Categories 'o4': '空的类别' // Templates // Portals };

Twinkle.speedy.callbacks = { getTemplateCodeAndParams: function(params) { var code, parameters, i;       if (params.normalizeds.length > 1) { code = ""; } else { parameters = params.templateParams[0] || []; code = ""; params.utparams = Twinkle.speedy.getUserTalkParameters(params.normalizeds[0], parameters); }

return [code, params.utparams]; },

parseWikitext: function(wikitext, callback) { var query = { action: "parse", prop: "text", pst: "true", text: wikitext, contentmodel: "wikitext", title: mw.config.get("wgPageName") };

var statusIndicator = new Morebits.status( '构造删除理由' ); var api = new Morebits.wiki.api( '解析删除模板', query, function(apiObj) {               var reason = decodeURIComponent($(apiObj.getXML.querySelector('text').childNodes[0].nodeValue).find('#delete-reason').text).replace(/\+/g, ' ');                if (!reason) {                    statusIndicator.warn( '未能从删除模板生成删除理由' );                } else {                    statusIndicator.info( '完成' );                }                callback(reason);            }, statusIndicator); api.post; },

sysop: { main: function( params ) { var reason;

if (!params.normalizeds.length && params.normalizeds[0] === 'db') { reason = prompt("输入删除理由：", ""); Twinkle.speedy.callbacks.sysop.deletePage( reason, params ); } else { var code = Twinkle.speedy.callbacks.getTemplateCodeAndParams(params)[0]; Twinkle.speedy.callbacks.parseWikitext(code, function(reason) {                   if (params.promptForSummary) {                        reason = prompt("输入删除理由，或点击确定以接受自动生成的：", presetReason);                    }                    Twinkle.speedy.callbacks.sysop.deletePage( reason, params );                }); }       },        deletePage: function( reason, params ) { var thispage = new Morebits.wiki.page( mw.config.get('wgPageName'), "删除页面" );

if (reason === null) { return Morebits.status.error("询问理由", "用户取消操作. "); } else if (!reason || !reason.replace(/^\s*/, "").replace(/\s*$/, "")) { return Morebits.status.error("询问理由", "你不给我理由…我就…不管了…"); }           thispage.setEditSummary( reason + Twinkle.getPref('deletionSummaryAd') ); thispage.deletePage(function {               thispage.getStatusElement.info("完成");                Twinkle.speedy.callbacks.sysop.deleteTalk( params );            });

// look up initial contributor. If prompting user for deletion reason, just display a link. // Otherwise open the talk page directly if( params.openUserTalk ) { thispage.setCallbackParameters( params ); thispage.lookupCreator( Twinkle.speedy.callbacks.sysop.openUserTalkPage ); }       },        deleteTalk: function( params ) { // delete talk page if (params.deleteTalkPage &&                   params.normalized !== 'f7' &&                    params.normalized !== 'o1' &&                    document.getElementById( 'ca-talk' ).className !== 'new') { var talkpage = new Morebits.wiki.page( Morebits.wikipedia.namespaces[ mw.config.get('wgNamespaceNumber') + 1 ] + ':' + mw.config.get('wgTitle'), "删除讨论页" ); talkpage.setEditSummary('G15: 孤立页面: 已删除页面“' + Morebits.pageNameNorm + "”的讨论页" + Twinkle.getPref('deletionSummaryAd')); talkpage.deletePage; // this is ugly, but because of the architecture of wiki.api, it is needed // (otherwise success/failure messages for the previous action would be suppressed) window.setTimeout(function { Twinkle.speedy.callbacks.sysop.deleteRedirects( params ); }, 1800); } else { Twinkle.speedy.callbacks.sysop.deleteRedirects( params ); }       },        deleteRedirects: function( params ) { // delete redirects if (params.deleteRedirects) { var query = { 'action': 'query', 'titles': mw.config.get('wgPageName'), 'prop': 'redirects', 'rdlimit': 5000 // 500 is max for normal users, 5000 for bots and sysops };               var wikipedia_api = new Morebits.wiki.api( '取得重定向列表…', query, Twinkle.speedy.callbacks.sysop.deleteRedirectsMain,                    new Morebits.status( '删除重定向' ) ); wikipedia_api.params = params; wikipedia_api.post; }

// prompt for protect on G11 var $link, $bigtext; if (params.normalized === 'g11') { $link = $('', {                   'href': '#',                    'text': '点击这里施行保护',                    'css': { 'fontSize': '130%', 'fontWeight': 'bold' },                    'click': function{                        Morebits.wiki.actionCompleted.redirect = null;                        Twinkle.speedy.dialog.close;                        mw.config.set('wgArticleId', 0);                        Twinkle.protect.callback;                    }                }); $bigtext = $(' ', {                   'text': '白纸保护该页',                    'css': { 'fontSize': '130%', 'fontWeight': 'bold' }                }); Morebits.status.info($bigtext[0], $link[0]); }

// promote Unlink tool var $link, $bigtext; if( mw.config.get('wgNamespaceNumber') === 6 && params.normalized !== 'f7' ) { $link = $('', {                   'href': '#',                    'text': '点击这里前往反链工具',                    'css': { 'fontWeight': 'bold' },                    'click': function{                        Morebits.wiki.actionCompleted.redirect = null;                        Twinkle.speedy.dialog.close;                        Twinkle.unlink.callback("取消对已删除文件 " + Morebits.pageNameNorm + " 的使用");                    }                }); $bigtext = $(' ', {                   'text': '取消对已删除文件的使用',                    'css': { 'fontWeight': 'bold' }                }); Morebits.status.info($bigtext[0], $link[0]); } else if (params.normalized !== 'f7') { $link = $('', {                   'href': '#',                    'text': '点击这里前往反链工具',                    'css': { 'fontWeight': 'bold' },                    'click': function{                        Morebits.wiki.actionCompleted.redirect = null;                        Twinkle.speedy.dialog.close;                        Twinkle.unlink.callback("取消对已删除页面 " + Morebits.pageNameNorm + " 的链接");                    }                }); $bigtext = $(' ', {                   'text': '取消对已删除页面的链接',                    'css': { 'fontWeight': 'bold' }                }); Morebits.status.info($bigtext[0], $link[0]); }       },        openUserTalkPage: function( pageobj ) { pageobj.getStatusElement.unlink; // don't need it anymore var user = pageobj.getCreator; var params = pageobj.getCallbackParameters;

var query = { 'title': 'User talk:' + user, 'action': 'edit', 'preview': 'yes', 'vanarticle': Morebits.pageNameNorm };

if (params.normalized === 'db' || Twinkle.getPref("promptForSpeedyDeletionSummary").indexOf(params.normalized) !== -1) { // provide a link to the user talk page var $link, $bigtext; $link = $('', {                   'href': mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ),                    'text': '点此打开User talk:' + user,                    'target': '_blank',                    'css': { 'fontSize': '130%', 'fontWeight': 'bold' }                }); $bigtext = $(' ', {                   'text': '通知页面创建者',                    'css': { 'fontSize': '130%', 'fontWeight': 'bold' }                }); Morebits.status.info($bigtext[0], $link[0]); } else { // open the initial contributor's talk page var statusIndicator = new Morebits.status('打开用户' + user + '对话页编辑表单', '打开中…');

switch( Twinkle.getPref('userTalkPageMode') ) { case 'tab': window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_blank' ); break; case 'blank': window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_blank', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' ); break; case 'window': /* falls through */ default: window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ),                       ( window.name === 'twinklewarnwindow' ? '_blank' : 'twinklewarnwindow' ),                       'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' ); break; }

statusIndicator.info( '完成' ); }       },        deleteRedirectsMain: function( apiobj ) { var xmlDoc = apiobj.getXML; var $snapshot = $(xmlDoc).find('redirects rd'); var total = $snapshot.length; var statusIndicator = apiobj.statelem;

if( !total ) { statusIndicator.status("未发现重定向"); return; }

statusIndicator.status("0%");

var current = 0; var onsuccess = function( apiobjInner ) { var now = parseInt( 100 * (++current)/total, 10 ) + '%'; statusIndicator.update( now ); apiobjInner.statelem.unlink; if( current >= total ) { statusIndicator.info( now + '（完成）' ); Morebits.wiki.removeCheckpoint; }           };

Morebits.wiki.addCheckpoint;

$snapshot.each(function(key, value) {               var title = $(value).attr('title');                var page = new Morebits.wiki.page(title, '删除重定向 "' + title + '"');                page.setEditSummary('G15: 孤立页面: 重定向到已删除页面“' + Morebits.pageNameNorm + "”" + Twinkle.getPref('deletionSummaryAd'));                page.deletePage(onsuccess);            }); }   },

user: { main: function(pageobj) { var statelem = pageobj.getStatusElement;

if (!pageobj.exists) { statelem.error( "页面不存在，可能已被删除" ); return; }

var text = pageobj.getPageText; var params = pageobj.getCallbackParameters;

statelem.status( '检查页面已有标记…' );

// check for existing deletion tags var tag = /(?:\{\{\s*(db|d|delete|db-.*?)(?:\s*\||\s*\}\}))/i.exec( text ); if ( tag ) { statelem.error( [ Morebits.htmlNode( 'strong', tag[1] ), " 已被置于页面中. " ] ); return; }

var copyvio = /(?:\{\{\s*(copyvio)[^{}]*?\}\})/i.exec( text ); if ( copyvio ) { statelem.error( '页面中已有版权验证模板. ' ); return; }

var xfd = /(?:\{\{([rsaiftcmv]fd|md1|proposed deletion)[^{}]*?\}\})/i.exec( text ); if ( xfd && !confirm( "删除相关模板已被置于页面中，您是否仍想添加一个快速删除模板？" ) ) { statelem.error( '页面已被提交至存废讨论. ' ); return; }

// given the params, builds the template and also adds the user talk page parameters to the params that were passed in           // returns => [ wikitext, utparams] var buildData = Twinkle.speedy.callbacks.getTemplateCodeAndParams(params), code = buildData[0]; params.utparams = buildData[1];

var thispage = new Morebits.wiki.page(mw.config.get('wgPageName')); // patrol the page, if reached from Special:NewPages if( Twinkle.getPref('markSpeedyPagesAsPatrolled') ) { thispage.patrol; }

// Wrap SD template in noinclude tags if we are in template space. // Won't work with userboxes in userspace, or any other transcluded page outside template space if (mw.config.get('wgNamespaceNumber') === 10) { // Template: code = " " + code + " "; }

// Remove tags that become superfluous with this action text = text.replace(/\{\{\s*([Nn]ew unreviewed article|[Uu]nreviewed|[Uu]serspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/g, ""); if (mw.config.get('wgNamespaceNumber') === 6) { // remove "move to Commons" tag - deletion-tagged files cannot be moved to Commons text = text.replace(/\{\{(mtc|(copy |move )?to ?commons|move to wikimedia commons|copy to wikimedia commons)[^}]*\}\}/gi, ""); }

// Generate edit summary for edit var editsummary; if (params.normalizeds.length > 1) { editsummary = '请求快速删除（'; $.each(params.normalizeds, function(index, norm) {                   editsummary += 'CSD ' + norm.toUpperCase + '、';                }); editsummary = editsummary.substr(0, editsummary.length - 1); // remove trailing comma editsummary += '）. ';           } else if (params.normalizeds[0] === "db") { editsummary = '请求快速删除：' + params.templateParams[0]["1"]; } else { editsummary = "请求快速删除（CSD " + params.normalizeds[0].toUpperCase + "）"; }

pageobj.setPageText(code + "\n" + text); pageobj.setEditSummary(editsummary + Twinkle.getPref('summaryAd')); pageobj.setWatchlist(params.watch); pageobj.setCreateOption('nocreate'); pageobj.save(Twinkle.speedy.callbacks.user.tagComplete); },

tagComplete: function(pageobj) { var params = pageobj.getCallbackParameters;

// Notification to first contributor if (params.usertalk) { var callback = function(pageobj) { var initialContrib = pageobj.getCreator;

// disallow warning yourself if (initialContrib === mw.config.get('wgUserName')) { Morebits.status.warn("您（" + initialContrib + "）创建了该页，跳过通知");

// don't notify users when their user talk page is nominated } else if (initialContrib === mw.config.get('wgTitle') && mw.config.get('wgNamespaceNumber') === 3) { Morebits.status.warn("通知页面创建者：用户创建了自己的对话页");

} else { var talkPageName = 'User talk:' + initialContrib; Morebits.wiki.flow.check(talkPageName, function {                            var flowpage = new Morebits.wiki.flow(talkPageName, "通知页面创建者（" + initialContrib + "）");                            flowpage.setTopic( + Morebits.pageNameNorm + '的快速删除通知');                            flowpage.setContent();                            flowpage.newTopic;                        }, function {                            var usertalkpage = new Morebits.wiki.page(talkPageName, "通知页面创建者（" + initialContrib + "）"),                                notifytext, i;

notifytext = "\n--~";

var editsummary = "通知："; if (params.normalizeds.indexOf("g12") === -1) { // no article name in summary for G10 deletions editsummary += "页面" + Morebits.pageNameNorm + ""; } else { editsummary += "一攻击性页面"; }                           editsummary += "快速删除提名";

usertalkpage.setAppendText(notifytext); usertalkpage.setEditSummary(editsummary + Twinkle.getPref('summaryAd')); usertalkpage.setCreateOption('recreate'); usertalkpage.setFollowRedirect(true); usertalkpage.append; });                   }

// add this nomination to the user's userspace log, if the user has enabled it                   if (params.lognomination) { Twinkle.speedy.callbacks.user.addToLog(params, initialContrib); }               };                var thispage = new Morebits.wiki.page(Morebits.pageNameNorm); thispage.lookupCreator(callback); }           // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name else if (params.lognomination) { Twinkle.speedy.callbacks.user.addToLog(params, null); }       },

// note: this code is also invoked from twinkleimage // the params used are: //  for CSD: params.values, params.normalizeds  (note: normalizeds is an array) //  for DI: params.fromDI = true, params.templatename, params.normalized  (note: normalized is a string) addToLog: function(params, initialContrib) { var wikipedia_page = new Morebits.wiki.page("User:" + mw.config.get('wgUserName') + "/" + Twinkle.getPref('speedyLogPageName'), "添加项目到用户日志"); params.logInitialContrib = initialContrib; wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.speedy.callbacks.user.saveLog); },

saveLog: function(pageobj) { var text = pageobj.getPageText; var params = pageobj.getCallbackParameters;

var appendText = "";

// add blurb if log page doesn't exist if (!pageobj.exists) { appendText += "这是该用户使用Twinkle的速删模块做出的快速删除提名列表. \n\n" + "如果您不再想保留此日志，请在参数设置中关掉，并" + "使用CSD O1提交快速删除. \n"; if (Morebits.userIsInGroup("sysop")) { appendText += "\n此日志并不记录用Twinkle直接执行的删除. \n"; }           }

// create monthly header var date = new Date; var headerRe = new RegExp("^==+\\s*" + date.getUTCFullYear + "\\s*年\\s*" + (date.getUTCMonth + 1) + "\\s*月\\s*==+", "m"); if (!headerRe.exec(text)) { appendText += "\n\n=== " + date.getUTCFullYear + "年" + (date.getUTCMonth + 1) + "月 ==="; }

appendText += "\n# " + Morebits.pageNameNorm + ": "; if (params.fromDI) { appendText += "图版CSD " + params.normalized.toUpperCase + "（" + params.templatename + "）"; } else { if (params.normalizeds.length > 1) { appendText += "多个理由（"; $.each(params.normalizeds, function(index, norm) {                       appendText += "" + norm.toUpperCase + '、';                    }); appendText = appendText.substr(0, appendText.length - 1); // remove trailing comma appendText += '）'; } else if (params.normalizeds[0] === "db") { appendText += "自定义理由"; } else { appendText += "CSD " + params.normalizeds[0].toUpperCase + ""; }           }

if (params.logInitialContrib) { appendText += "；通知"; }           appendText += " \n";

pageobj.setAppendText(appendText); pageobj.setEditSummary("记录对" + Morebits.pageNameNorm + "的快速删除提名" + Twinkle.getPref('summaryAd')); pageobj.setCreateOption("recreate"); pageobj.append; }   } };

// validate subgroups in the form passed into the speedy deletion tag Twinkle.speedy.getParameters = function twinklespeedyGetParameters(form, values) { var parameters = [];

$.each(values, function(index, value) {       var currentParams = [];        switch (value) {            case 'reason':                if (form["csd.reason_1"]) {                    var dbrationale = form["csd.reason_1"].value;                    if (!dbrationale || !dbrationale.trim) {                        alert( '自定义理由：请指定理由. ' );                       parameters = null;                        return false;                    }                    currentParams["1"] = dbrationale;                }                break;

case 'a5': if (form["csd.a5_pagename"]) { var otherpage = form["csd.a5_pagename"].value; if (!otherpage || !otherpage.trim) { alert( 'CSD A5：请提供现有条目的名称. ' ); parameters = null; return false; }                   currentParams.pagename = otherpage; }               break;

case 'g5': if (form["csd.g5_1"]) { var deldisc = form["csd.g5_1"].value; if (deldisc) { if (deldisc.substring(0, 9) !== "Wikipedia" &&                           deldisc.substring(0, 3) !== "WP:" &&                            deldisc.substring(0, 5) !== "维基百科:" &&                            deldisc.substring(0, 5) !== "維基百科:") { alert( 'CSD G5：您提供的讨论页名必须以“Wikipedia:”开头. ' ); parameters = null; return false; }                       currentParams["1"] = deldisc; }               }                break;

case 'g10': if (form["csd.g10_rationale"] && form["csd.g10_rationale"].value) { currentParams.rationale = form["csd.g10_rationale"].value; }               break;

case 'f1': if (form["csd.f1_filename"]) { var redimage = form["csd.f1_filename"].value; if (!redimage || !redimage.trim) { alert( 'CSD F1：请提供另一文件的名称. ' ); parameters = null; return false; }                   currentParams.filename = redimage.replace(/^\s*(Image|File|文件|檔案):/i, ""); }               break;

case 'f5': if (form["csd.f5_filename"]) { var redimage = form["csd.f5_filename"].value; if (!redimage || !redimage.trim) { alert( 'CSD F5：请提供另一文件的名称. ' ); parameters = null; return false; }                   currentParams.filename = redimage.replace(/^\s*(Image|File|文件|檔案):/i, ""); }               break;

case 'f7': if (form["csd.f7_filename"]) { var filename = form["csd.f7_filename"].value; if (filename && filename !== Morebits.pageNameNorm) { if (filename.indexOf("Image:") === 0 || filename.indexOf("File:") === 0 ||                           filename.indexOf("文件:") === 0 || filename.indexOf("檔案:") === 0) { currentParams["1"] = filename; } else { currentParams["1"] = "File:" + filename; }                   }                }                break;

case 'r3': if (form["csd.r3_type"]) { var redirtype = form["csd.r3_type"].value; if (!redirtype) { alert( 'CSD R3：请选择适用类别. ' ); parameters = null; return false; }                   currentParams["1"] = redirtype; }               break;

default: break; }       parameters.push(currentParams); });   return parameters; };

// function for processing talk page notification template parameters Twinkle.speedy.getUserTalkParameters = function twinklespeedyGetUserTalkParameters(normalized, parameters) { var utparams = []; switch (normalized) { default: break; }   return utparams; };

Twinkle.speedy.resolveCsdValues = function twinklespeedyResolveCsdValues(e) { var values = (e.target.form ? e.target.form : e.target).getChecked('csd'); if (values.length === 0) { alert( "请选择一个理据！" ); return null; }   return values; };

Twinkle.speedy.callback.evaluateSysop = function twinklespeedyCallbackEvaluateSysop(e) { var form = (e.target.form ? e.target.form : e.target);

if (e.target.type === "checkbox" || e.target.type === "text" ||           e.target.type === "select") { return; }

var tag_only = form.tag_only; if( tag_only && tag_only.checked ) { Twinkle.speedy.callback.evaluateUser(e); return; }

var values = Twinkle.speedy.resolveCsdValues(e); if (!values) { return; }

var normalizeds = values.map(function(value) {       return Twinkle.speedy.normalizeHash[ value ];    });

// analyse each criterion to determine whether to watch the page, prompt for summary, or open user talk page var watchPage, promptForSummary; normalizeds.forEach(function(norm) {       if (Twinkle.getPref("watchSpeedyPages").indexOf(norm) !== -1) {            watchPage = true;        }        if (Twinkle.getPref("promptForSpeedyDeletionSummary").indexOf(norm) !== -1) {            promptForSummary = true;        }    });

var params = { values: values, normalizeds: normalizeds, watch: watchPage, deleteTalkPage: form.talkpage && form.talkpage.checked, deleteRedirects: form.redirects.checked, openUserTalk: form.openusertalk.checked, promptForSummary: promptForSummary, templateParams: Twinkle.speedy.getParameters( form, values ) };   if(!params.templateParams) { return; }

Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( form );

Twinkle.speedy.callbacks.sysop.main( params ); };

Twinkle.speedy.callback.evaluateUser = function twinklespeedyCallbackEvaluateUser(e) { var form = (e.target.form ? e.target.form : e.target);

if (e.target.type === "checkbox" || e.target.type === "text" ||           e.target.type === "select") { return; }

var values = Twinkle.speedy.resolveCsdValues(e); if (!values) { return; }   //var multiple = form.multiple.checked; var normalizeds = []; $.each(values, function(index, value) {       var norm = Twinkle.speedy.normalizeHash[ value ];

normalizeds.push(norm); });

// analyse each criterion to determine whether to watch the page/notify the creator var watchPage = false; $.each(normalizeds, function(index, norm) {       if (Twinkle.getPref('watchSpeedyPages').indexOf(norm) !== -1) {            watchPage = true;            return false;  // break        }    });

var notifyuser = false; if (form.notify.checked) { $.each(normalizeds, function(index, norm) {           if (Twinkle.getPref('notifyUserOnSpeedyDeletionNomination').indexOf(norm) !== -1) {                notifyuser = true;                return false;  // break            }        }); }

var welcomeuser = false; if (notifyuser) { $.each(normalizeds, function(index, norm) {           if (Twinkle.getPref('welcomeUserOnSpeedyDeletionNotification').indexOf(norm) !== -1) {                welcomeuser = true;                return false;  // break            }        }); }

var csdlog = false; if (Twinkle.getPref('logSpeedyNominations')) { $.each(normalizeds, function(index, norm) {           if (Twinkle.getPref('noLogOnSpeedyNomination').indexOf(norm) === -1) {                csdlog = true;                return false;  // break            }        }); }

var params = { values: values, normalizeds: normalizeds, watch: watchPage, // usertalk: notifyuser, welcomeuser: welcomeuser, lognomination: csdlog, templateParams: Twinkle.speedy.getParameters( form, values ) };   if (!params.templateParams) { return; }

Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( form );

Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName'); Morebits.wiki.actionCompleted.notice = "标记完成";

var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "标记页面"); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.speedy.callbacks.user.main); }; })(jQuery);

//