User:WhitePhosphorus/js/dykc.js

/** * THIS SCRIPT IS A TEST VERSION. DO NOT USE IT UNLESS YOU HAVE CONTACTED ME. */

(function($, mw) {

'use strict';

// Insert something after a substring which is matched by the RegExp object. var addAfterRegex = function(src, toAdd, regex) { var rst = regex.exec(src); if (rst === null) return null; var index = rst.index + rst[0].length; return src.substring(0, index) + toAdd + src.substring(index, src.length); };

var site = { DYKCPage: 'Wikipedia:新条目推荐/候选', apiUrl: mw.util.wikiScript('api'), parse: function(wikitext, extraPara, doneFunc, failFunc) { var dataObj = { action: 'parse', title: 'A', text: wikitext, prop: 'text', format: 'json' };		$.extend(dataObj, extraPara); $.ajax({			url: site.apiUrl,			data: dataObj		}).done(function (data) {			var parsedHTML = null;			if (data.parse) {				if (data.parse.text) {					parsedHTML = $.trim(data.parse.text['*']);				}			}			if (parsedHTML !== null) {				if ($.isFunction(doneFunc))					doneFunc(parsedHTML);			} else {				if ($.isFunction(failFunc))					failFunc(data && data.error && data.error.code ? data.error.code : 'unknown');			}		}).fail(function(jqXHR, textStatus, errorThrown) {			console.log('Error when parsing wikitext: ' + errorThrown);			if ($.isFunction(failFunc))				failFunc('network');		}); },	loadPage: function(pageTitle, doneFunc, failFunc) { $.ajax({			url: site.apiUrl,			data: {				action: 'query',				prop: 'revisions',				titles: pageTitle,				rvprop: 'content',				format: 'json'			}		}).done(function(data) {			var content = null;			if (data.query) {				if (data.query.pages) {					for (var id in data.query.pages) {						if (data.query.pages[id]) {							var page = data.query.pages[id];							if (page.revisions && page.revisions.length) {								if (page.revisions[0]['*']) {									content = page.revisions[0]['*'];								}							}						}					}				}			}			if (content !== null) {				if ($.isFunction(doneFunc))					doneFunc(content);			} else {				if ($.isFunction(failFunc))					failFunc('missing');			}		}).fail(function(jqXHR, textStatus, errorThrown) {			console.log('Error when loading page ' + pageTitle + ': ' + errorThrown);			if ($.isFunction(failFunc))				failFunc('network');		}); },	edit: function(pageTitle, newRevision, doneFunc, failFunc) { $.ajax({			url: site.apiUrl,			dataType: 'json',			type: 'POST',			data: {				action: 'edit',				title: pageTitle,				summary: msg.edit_summary,				text: newRevision,				token: mw.user.tokens.get('editToken'),				format: 'json'			}		}).done(function(data) {			if (data && data.edit && data.edit.result === 'Success') {				if ($.isFunction(doneFunc))					doneFunc(content);			} else {				if ($.isFunction(failFunc)) {					if (data && data.error) {						failFunc(data.error.code ? data.error.code : 'unknown');					} else if (data && data.edit && data.edit.code) {						var code = data.edit.code;						if (code === 'abusefilter-disallowed' || code == 'abusefilter-warning') {							failFunc(code, data.edit.warning);						} else {							failFunc(code);						}					} else {						failFunc('unknown');					}				}			}		}).fail(function(jqXHR, textStatus, errorThrown) {			console.log('Error when editing page ' + pageTitle + ': ' + errorThrown);			if ($.isFunction(failFunc))				failFunc('network');		}); },	post: function(image) { dialog.dl.dialog('close'); if (dialog.rst && dialog.rst.dialog('isOpen')) dialog.rst.dialog('close'); dialog.rst = $(' ' + msg.msg_posting + ' ').dialog({			title: msg.dialog_title		}); var dykcArr = [ '==== ====',				'{'+'{ subst:#invoke:Template:DYKEntry|normalize', ' | article = ' + article.title, ' | question = ' + dialog.config.dykcQuestion, ' | image = ' + image, ' | type = ' + dialog.config.dykcType, ' | author = ' + dialog.config.dykcAuthor, ' | nominator = {'+'{subst:REVISIONUSER}}', ' | timestamp = {'+'{subst:#time:U}}', '}}',				''			],			remark = $.trim(dialog.config.dykcRemarks), remarkStr = (remark.length ? '** {' + '{Remark}}：' + remark + '\n' : ''), dykcStr = dykcArr.join('\n') + remarkStr; site.loadPage(site.DYKCPage, function(revision) {			var	curDate = new Date,				curMD = (curDate.getMonth+1) + '月' + curDate.getDate + '日',				dateRe = new RegExp('===\\s*' + curMD + '\\s*===[\\r\\n]*'),				newText = addAfterRegex(revision, dykcStr, dateRe),				succeed = true;			if (newText === null) {				dykcStr = '=== ' + curMD + ' ===\n' + dykcStr;				newText = addAfterRegex(revision, dykcStr, / \s*(<!\-\-[\s\S]*?\-\->)?[\r\n]*/);				succeed = (newText !== null);			}			if (!succeed) {				window.prompt(msg.dialog_error_format, dykcStr);				if (dialog.rst && dialog.rst.dialog('isOpen'))					dialog.rst.dialog('close');				return null;			}			site.edit(site.DYKCPage, newText, function { dialog.rst.html(' ' + msg.msg_post_complete +					'' + msg.msg_dykc + '' + msg.msg_close_this + ' '); }, function(code, warning) { if (warning !== undefined) { warning = ' 您的编辑已被过滤器拦截. ' +						' 单击返回提交表单 ' + warning; dialog.rst.html(warning); $('dykcAFReturn').click(function(e) {						e.preventDefault;						dialog.rst.dialog('close');						dialog.dl.dialog('open');					}); } else { window.prompt(msg.dialog_error(code), dykcStr); if (dialog.rst && dialog.rst.dialog('isOpen')) dialog.rst.dialog('close'); }			});		});	} };

var article = { title: mw.config.get('wgPageName'), images: null, // parsed html of images used by the article imageTitles: null // filename (e.g. File:foo.jpg) of images used by the article };

var img = { // Check if there is a in the parsed html. isExists: function(imgHTML) { return $(imgHTML).find('img').length !== 0; },	// Check if the image exists on commons.wikimedia.org, not on local wiki, // for most files on local wiki are non-free and copyrighted. isInCommons: function(imgHTML) { return ~imgHTML.indexOf('//upload.wikimedia.org/wikipedia/commons/'); },	// Normalize input data to something like *File:Foo.jpg* or *Foo.jpg*. normalize: function(image, ns) { var rst = /^\[\[:?(.*?)(\|.*?)?\]\]/.exec(image); if (rst !== null) { image = rst[1]; }		rst = /^(file|image|文件|[檔档]案)\s*:/i.exec(image); if (rst === null && ns) { image = 'File:' + image; } else if (rst !== null && !ns) { image = image.substring(rst[0].length, image.length); }		return image; } };

var msg;

switch (mw.config.get('wgUserLanguage')) { case 'zh-cn': case 'zh-hans': case 'zh-my': case 'zh-sg': msg = { link_title: '提交新条目推荐候选', edit_summary: '半自动提交新条目推荐候选', msg_question: '问题', msg_type: '类型', msg_loading_images: '正在加载条目中的图片……', msg_loading_images_failed: '无法加载条目中的图片！', msg_no_required_para: '您没有填写以下必填项目：\n\n', msg_are_you_sure: '\n\n确定要提交吗？', msg_posting: '正在提交，请起身活动一下吧^_^', msg_post_complete: '提交完成. 您可以前往', msg_dykc: '推荐页', msg_close_this: '查看，或关闭此对话框留在本页. ',			dialog_title: '提交新条目推荐候选', dialog_button_submit: '提交', dialog_button_cancel: '取消', dialog_button_preview: '预览', dialog_question: '问题（必填）：', dialog_image: '图片（可选）：', dialog_other_image: '使用下面的图片', dialog_type: '类型（必填，建议用英文）：', dialog_author: '主编（若非一人主编或主编非注册用户，请留空）：', dialog_remarks: '其他说明（可选，如果填写则需签名）：', dialog_preview: '预览：', dialog_warning_img_invalid: '警告：图片不存在！', dialog_warning_img_not_in_commons: '警告：图片不存在于维基共享资源，很可能是合理使用而不能用于首页展示！', dialog_warning_img_unused: '警告：图片未被条目使用，不能用于首页展示！', dialog_warning_qus_no_marks: '警告：问题中没有出现问号“？”，请加入之，或修正半角问号“?”. ',			dialog_warning_qus_not_bold: '警告：问题中没有加粗条目名！', dialog_auto_formatting: '为选中部分加粗、加链接', dialog_auto_bolding: '自动修复', dialog_warning_qus_no_links: '警告：问题中没有到条目的内部链接，请检查是否繁简不同或使用了重定向. ',			dialog_error: function(code) { return '很抱歉本工具出现问题，未能自动推荐该条目. \n' + '您可前往 User talk:WhitePhosphorus 回报出现问题的条目、时间和以下错误原因：' + code + '. \n请按下Ctrl+C或command+C来复制下面的代码并手动推荐：' }, dialog_error_format: '很抱歉因为无法识别新条目推荐候选页的格式，故未能自动推荐该条目. \n' + '您可前往 User talk:WhitePhosphorus 回报出现问题的条目和时间. \n' + '请按下Ctrl+C或command+C来复制下面的代码并手动推荐：' };		break; default: msg = { link_title: '提交新條目推薦候選', edit_summary: '半自動提交新條目推薦候選', msg_question: '問題', msg_type: '類型', msg_loading_images: '正在加載條目中的圖片……', msg_loading_images_failed: '無法加載條目中的圖片！', msg_no_required_para: '您沒有填寫以下必填項目：\n\n', msg_are_you_sure: '\n\n確定要提交嗎？', msg_posting: '正在提交，請起身活動一下吧^_^', msg_post_complete: '提交完成. 您可以前往', msg_dykc: '推薦頁', msg_close_this: '查看，或關閉此對話框留在本頁. ',			dialog_title: '提交新條目推薦候選', dialog_button_submit: '提交', dialog_button_cancel: '取消', dialog_button_preview: '預覽', dialog_question: '問題（必填）：', dialog_image: '圖片（可選）：', dialog_other_image: '使用下面的圖片', dialog_type: '類型（必填，建議用英文）：', dialog_author: '主編（若非一人主編或主編非註冊用戶，請留空）：', dialog_remarks: '其他說明（可選，如果填寫則需簽名）：', dialog_preview: '預覽：', dialog_warning_img_invalid: '警告：圖片不存在！', dialog_warning_img_not_in_commons: '警告：圖片不存在於維基共享資源，很可能是合理使用而不能用於首頁展示！', dialog_warning_img_unused: '警告：圖片未被條目使用，不能用於首頁展示！', dialog_warning_qus_no_marks: '警告：問題中沒有出現問號“？”，請加入之，或修正半角問號“?”. ',			dialog_warning_qus_not_bold: '警告：問題中沒有加粗條目名！', dialog_auto_formatting: '爲選中部分加粗、加鏈接', dialog_auto_bolding: '自動修復', dialog_warning_qus_no_links: '警告：問題中沒有到條目的內部鏈接，請檢查是否繁簡不同或使用了重新導向. ',			dialog_error: function(code) { return '很抱歉本工具出現問題，未能自動推薦該條目. \n' + '您可前往 User talk:WhitePhosphorus 回報出現問題的條目、時間和以下錯誤原因：' + code + '. \n請按下Ctrl+C或command+C來複制下面的代碼並手動推薦：' }, dialog_error_format: '很抱歉因爲無法識別新條目推薦候選頁的格式，故未能自动推荐该条目. \n' + '您可前往 User talk:WhitePhosphorus 回报出现问题的条目和时间. \n' + '请按下Ctrl+C或command+C来复制下面的代码并手动推荐：' }; }

var dialog = { dl: null, rst: null, config: {}, $: {},	loadJQueryObjs: function { // Cache often-used JQuery objects. dialog.$ = { dykcDialog: $('#dykcDialog'), dykcQuestion: $('#dykcQuestion'), dykcImage: $('#dykcImage'), dykcOtherImage: $('#dykcOtherImage'), dykcType: $('#dykcType'), dykcAuthor: $('#dykcAuthor'), dykcRemarks: $('#dykcRemarks'), previewImage: $('#previewImage'), previewQuestion: $('#previewQuestion'), previewRemarks: $('#previewRemarks'), warning: $('p[id*="warning"]'), warningImg: $('p[id*="warningImg"]'), warningQus: $('p[id*="warningQus"]'), warningImgInvalid: $('#warningImgInvalid'), warningImgUnused: $('#warningImgUnused'), warningImgNotInCommons: $('#warningImgNotInCommons'), warningQusNoMarks: $('#warningQusNoMarks'), warningQusNotBold: $('#warningQusNotBold'), warningQusNoLinks: $('#warningQusNoLinks'), loadingArticleImages: $('#loadingArticleImages'), autoFormatting: $('#autoFormatting'), autoBolding: $('#autoBolding') };	},	updateConfig: function { // Cache everything when the dialog is closed. dialog.$.dykcDialog.find("input[id*='dykc'], textarea[id*='dykc'], select[id*='dykc']").each(function(i) {			dialog.config[this.id] = $(this).val;		}); dialog.$.dykcDialog.find("div[id*='preview'], li[id*='preview']").each(function(i) {			dialog.config[this.id] = $(this).html;		}); dialog.$.warning.each(function(i) {			dialog.config[this.id] = $(this).is(":visible");		}); },	loadCSS: function { dialog.$.dykcDialog.find("select").css("max-width", "97%").css('margin-left', '3%'); dialog.$.dykcDialog.find("input, textarea").css("width", "97%").css('margin-left', '3%'); dialog.$.previewQuestion.css('padding-right', '100px'); dialog.$.previewRemarks.css('padding-right', '100px'); dialog.$.dykcDialog.find("p[id*=warning]:not(a)").addClass('error'); // Do not show that when web browser does not support the property if (dialog.$.dykcQuestion[0].selectionStart === undefined) { dialog.$.autoFormatting.hide; }	},	loadConfig: function { // Load everything we cached before. dialog.$.warning.hide; for (var k in dialog.config) { if (dialog.config.hasOwnProperty(k)) { if (k.startsWith('preview')) { dialog.$[k].html(dialog.config[k]); } else if (k.startsWith('warning')) { if (dialog.config[k]) dialog.$[k].show; else dialog.$[k].hide; } else { dialog.$[k].val(dialog.config[k]); }			}		}		// #previewRemarks is hidden by default. if (dialog.config.previewRemarks && dialog.config.previewRemarks.length) dialog.$.previewRemarks.show;

if (article.images === null) { // The images are still loading. dialog.$.loadingArticleImages.show; dialog.$.dykcImage.hide; }

dialog.loadCSS; },	bindEvents: function { // When question is modified, do not show the warnings. dialog.$.dykcQuestion.on('input selectionchange propertychange', function {			dialog.$.warningQus.hide;		}); dialog.$.dykcImage.on('change', function {			dialog.previewImage;		}); dialog.$.autoFormatting.on('click', function {			dialog.autoFormatting;		}); dialog.$.autoBolding.on('click', function {			dialog.autoBolding;		}); dialog.$.dykcOtherImage.on('keypress', function {			dialog.selectOtherImage;		}); },	init: function { dialog.loadJQueryObjs; dialog.bindEvents; dialog.loadConfig; },	previewImage: function { dialog.$.warningImg.hide; dialog.$.previewImage.html(""); if (article.images !== null) { var index = dialog.$.dykcImage.val; if (index.length) { dialog.$.previewImage.html(article.images[parseInt(index)]); if (!img.isInCommons(article.images[parseInt(index)])) { dialog.$.warningImgNotInCommons.show; }			} else { var image = $.trim(dialog.$.dykcOtherImage.val); if (!image.length) return null; image = '80px'; site.parse(image, {					disablelimitreport: '1'				}, function (data) {					if (!img.isExists(data)) {						dialog.$.warningImgInvalid.show;					} else {						if (!img.isInCommons(data))							dialog.$.warningImgNotInCommons.show;						data = $(data)[0].outerHTML;						if (article.images.indexOf(data) === -1)							dialog.$.warningImgUnused.show;						dialog.$.previewImage.html(data);					}				}); }		}	},	previewRemarks: function { dialog.$.previewRemarks .html("") .hide; var text = $.trim(dialog.$.dykcRemarks.val); if (!text.length) return null; site.parse('{'+'{Remark}}：' + text, {			pst: '1',			disablelimitreport: '1'		}, function (data) {			dialog.$.previewRemarks				.html(data)				.show;		}); },	previewQuestion: function { var qusWikitext = dialog.$.dykcQuestion.val; site.parse(qusWikitext, {			disablelimitreport: '1'		}, function (qusHTML) {			dialog.$.previewQuestion.html(qusHTML);			dialog.$.warningQus.hide;			var $qus = $(qusHTML);			if (qusHTML.indexOf('？') === -1) {				dialog.$.warningQusNoMarks.show;			}			var linkSelector = 'a[href="' + wgArticlePath.replace('$1', encodeURI(article.title.replace(/ /g, '_'))) + '"]';			if ($qus.find(linkSelector).length === 0) {				dialog.$.warningQusNoLinks.show;			} else if ($qus.find('b').find(linkSelector).length === 0) {				dialog.$.warningQusNotBold.show;			}		}); },	// Called when the user types something in the input box #otherImage. // The user may want to manually specify an image so we select 'other_image' for him. selectOtherImage: function { dialog.$.dykcImage.val(""); dialog.$.previewImage.html(""); dialog.$.warningImg.hide; },	// Find a link like *title* and replace them with *title*. // Something like *'foo'* will be parsed to *'foo'* by MediaWiki. autoBolding: function { var text = dialog.$.dykcQuestion.val, escapedTitle = article.title.replace(/[-\/\\^$*+?.|[\]{}]/g, '\\$&'), titleRe = new RegExp("(\\[\\[:?" + escapedTitle + "(\\|.*?)?\\]\\])"); dialog.$.dykcQuestion.val(text.replace(titleRe, "$1")); dialog.$.warningQus.hide; },	// Wrap selected text with ** and **. autoFormatting: function { var start = dialog.$.dykcQuestion[0].selectionStart, end = dialog.$.dykcQuestion[0].selectionEnd, qus = dialog.$.dykcQuestion.val; if (start !== undefined) { if (start === end) return null; var pre = qus.substring(0, start), tar = qus.substring(start, end), suf = qus.substring(end, qus.length); dialog.$.dykcQuestion.val(pre + "" + tar + "" + suf); dialog.$.warningQus.hide; // set cursor at the end of selected string dialog.$.dykcQuestion[0].selectionStart = dialog.$.dykcQuestion[0].selectionEnd = end + 11 + article.title.length; }	} };

// Load all images used by the article. // When the dialog is loaded, those images will be listed in the select box. var loadArticleImages = function { $.ajax({		url: site.apiUrl,		data: {			action: 'query',			prop: 'images|revisions',			titles: article.title,			rvprop: 'content',			imlimit: 'max', // 500 should be enough.			format: 'json'		}	}).done(function (data) {		var allImageTitles = [], revision = '';		if (data.query) {			var pages = data.query.pages;			for (var pageid in pages) {				var page = pages[pageid];				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]['*']) {					revision = page.revisions[0]['*'];				}			}		}		// The filename (without prefix 'File:') must appear in article text.		// Or if we failed to get the article text, do not filter anything.		allImageTitles = allImageTitles.filter(function (e) { return !revision.length || ~revision.indexOf(e.substring(5, e.length)); });		var imageTitlesStr = '\n\n',			filterArr = [];		site.parse(imageTitlesStr, { disablelimitreport: '1' }, function (data) { data = $(' ' + data + ' '); article.images = data.find('p, div').toArray.map(function(e) {				return e.outerHTML;			}).filter(function(e, i) {				filterArr[i] = img.isExists(e);				return filterArr[i];			}); article.imageTitles = allImageTitles.filter(function(e, i) { return filterArr[i]; }); $('#loadingArticleImages').hide; $('#dykcImage').show; }, function (code) { $('#loadingArticleImages').val(msg.msg_loading_images_failed + '（' + code + '）'); });	}).fail(function(jqXHR, textStatus, errorThrown) {		console.log('Error when loading images of ' + article.title + ': ' + errorThrown);		$('#loadingArticleImages').val(msg.msg_loading_images_failed + 'queryImages');	}); };

var addDYKCLink = function { $(mw.util.addPortletLink( "p-tb", "#",		msg.link_title )).click(function(e) {		e.preventDefault;		// List images used by the article.		var imageTitleArr = article.imageTitles.map(function (t, i) { return '' + t + ' '; });		// This is our html source code of the dialog box.		// CSS is set in *dialog.loadCSS* and some events are bound in *dialog.bindEvents*.		var html =			' ' +			msg.dialog_question +			' ' +			msg.dialog_auto_formatting + ' ' +			' ' +			msg.dialog_image + ' ' + msg.msg_loading_images + ' ' +			'' +			imageTitleArr.join('') +			'' + msg.dialog_other_image + ' ' +			' ' +			' ' +			msg.dialog_type + ' ' + ' ' +			msg.dialog_author + ' ' +			' ' +			msg.dialog_remarks + ' ' + ' ' +			msg.dialog_preview + ' ' +			' ' +			'</li><ul><li id="previewRemarks" style="display:none"></li></ul></ul>' + '<p id="warningImgInvalid">' + msg.dialog_warning_img_invalid + ' ' + '<p id="warningImgNotInCommons">' + msg.dialog_warning_img_not_in_commons + ' ' + '<p id="warningImgUnused">' + msg.dialog_warning_img_unused + ' ' + '<p id="warningQusNoMarks">' + msg.dialog_warning_qus_no_marks + ' ' + '<p id="warningQusNotBold">' + msg.dialog_warning_qus_not_bold + '&gt;&gt; ' + msg.dialog_auto_bolding + '</a>' + ' ' + '<p id="warningQusNoLinks">' + msg.dialog_warning_qus_no_links + ' ' + ' ';		if (dialog.dl) { if (!dialog.dl.dialog('isOpen')) dialog.dl.html(html).dialog("open"); } else { dialog.dl = $(html).dialog({				title: msg.dialog_title,				minWidth: 515,				minHeight: 175,				beforeClose: dialog.updateConfig,				buttons: [					{						text: msg.dialog_button_preview,						click: function {							dialog.previewImage;							dialog.previewQuestion;							dialog.previewRemarks;						}					},					{						text: msg.dialog_button_submit,						click: function {							dialog.updateConfig;							var empty_para = [];							if (!dialog.config.dykcQuestion.length)								empty_para.push(msg.msg_question);							if (!dialog.config.dykcType.length)								empty_para.push(msg.msg_type);							if (empty_para.length) {								if (!window.confirm(msg.msg_no_required_para +									empty_para.join('\n') + msg.msg_are_you_sure)) {									return null;								}							}							var image = dialog.config.dykcOtherImage;							if (dialog.config.dykcImage.length)								image = article.imageTitles[parseInt(dialog.config.dykcImage)]; site.post(image); }					},					{						text: msg.dialog_button_cancel, click: function { $(this).dialog('close'); } }				]			});		}		dialog.init;	}); };

if (mw.config.get('wgCanonicalNamespace') === '' && mw.config.get('wgArticleId') !== 0) { loadArticleImages; addDYKCLink; }

})(jQuery, mw);