User:Xiplus/js/bulletin-editor.js

维基百科,自由的百科全书

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。

/* global Morebits */
(function() {
	function main() {
		var summarySuffix = ' via [[User:Xiplus/js/bulletin-editor|bulletin-editor]]';
		var flagStart = '请在此行下方编辑 -->';
		var flagEnd = '<!-- 请在此行上方编辑';

		var api = new mw.Api();
		var date = new Morebits.date();
		var bulletinTitle = 'Template:Bulletin';
		var archiveTitle = 'Wikipedia:公告欄/存檔/' + date.format('YYYY年');

		$('#firstHeading').text(wgULS('公告栏编辑器', '公告欄編輯器'));

		$('#be-editor').remove();
		var $wrapper = $('<div>').attr('id', 'be-editor');
		var editorText = $('#wpTextbox1').val();
		$('#bodyContent').html($wrapper);

		if (mw.config.get('wgUserGroups').indexOf('autoconfirmed') === -1) {
			$wrapper.append(
				$('<div>').addClass('errorbox')
					.append(wgULS('您没有权限使用公告栏编辑器。', '您沒有權限使用公告欄編輯器。'))
			);
			return;
		}

		var params = {
			action: 'query',
			format: 'json',
			prop: 'revisions',
			titles: bulletinTitle,
			rvprop: ['content', 'timestamp'],
			formatversion: '2',
			curtimestamp: true,
		};

		if (mw.config.get('wgPageName') === 'Template:Bulletin'
			&& mw.config.get('wgRevisionId') !== 0
			&& mw.config.get('wgRevisionId') !== mw.config.get('wgCurRevisionId')
		) {
			params.rvstartid = mw.config.get('wgRevisionId');

			$wrapper.append(
				$('<div>').addClass('mw-message-box-warning mw-message-box')
					.append($('<b>').text('警告:'))
					.append(wgULS('您正在编辑的是本页的旧版本。如果您保存它的话,在本版本之后的任何修改都会丢失。', '您正在編輯的是本頁的舊版本。如果您保存它的話,在本版本之後的任何修改都會丟失。'))
			);
		}

		api.get(params).done(function(data) {
			var revision = data.query.pages[0].revisions[0];
			var basetimestamp = revision.timestamp;
			var curtimestamp = data.curtimestamp;
			var bulletinText = revision.content;

			if (editorText) {
				bulletinText = editorText;

				$wrapper.append(
					$('<div>').addClass('mw-message-box-warning mw-message-box')
						.append($('<b>').text('警告:'))
						.append(wgULS('已从编辑框加载内容,而非是最新版本内容。', '已從編輯框載入內容,而非是最新版本內容。'))
				);
			}

			var idxStart = bulletinText.indexOf(flagStart);
			var idxEnd = bulletinText.indexOf(flagEnd);
			if (idxStart === -1 || idxEnd === -1) {
				mw.notify(wgULS('无法从文本解析公告位置', '無法從文本解析公告位置'), { type: 'error' });
				return;
			}
			var mainText = bulletinText.substring(idxStart + flagStart.length, idxEnd);
			var re = /{{\s*Bulletin\/item\s*\|/gi;
			var m = re.exec(mainText);

			function copyDataFromParent(item, row) {
				item.find('.be-item-type').val(row.find('.be-row-type').val());
				item.find('.be-item-prefix').val(row.find('.be-row-prefix').val());
				item.find('.be-item-suffix').val(row.find('.be-row-suffix').val());
			}

			function moveToArchive(event) {
				var item = $(event.target).parent();
				copyDataFromParent(item, item.parents('.be-row'));
				$('#be-archiveul').append(item);
			}

			function generateText() {
				var newText = '';

				$.each($('.be-row'), function(_idx, row) {
					row = $(row);

					var itemsText = row.find('.be-item-main')
						.map(function(idx, item) {
							return $(item).val().trim()
						})
						.filter(function(idx, item) {
							return item.length > 0
						});

					if (itemsText.length === 0) {
						return;
					}

					var tempText = '{{bulletin/item|';

					tempText += row.find('.be-row-type').val().trim();

					var prefix = row.find('.be-row-prefix').val().trim();
					var suffix = row.find('.be-row-suffix').val().trim();

					if (!prefix && !suffix && itemsText.length == 1) {
						// No prefix, suffix, and only 1 item
						tempText += '|' + itemsText[0]
					} else {
						if (prefix) {
							tempText += '|prefix=' + prefix;
						}

						tempText += '\n';
						tempText += itemsText.map((_idx, item) => { return '|' + item + '\n' }).get().join('');
						tempText

						if (suffix) {
							tempText += '|suffix=' + suffix;
						}
					}

					tempText += '}}\n';

					newText += tempText;
				});

				return newText;
			}

			function mergeMainText() {
				return bulletinText.substring(0, idxStart + flagStart.length) + '\n' + generateText() + bulletinText.substring(idxEnd);
			}

			function generateArchiveText() {
				var itemsText = $('#be-archive-zone .be-item')
					.filter(function(idx, item) {
						return $(item).find('.be-item-main').val().trim().length > 0;
					})
					.map(function(idx, item) {
						var tempText = '{{bulletin/item|';
						tempText += $(item).find('.be-item-type').val().trim();
						tempText += '|';
						tempText += $(item).find('.be-item-prefix').val().trim();
						tempText += $(item).find('.be-item-main').val().trim();
						tempText += $(item).find('.be-item-suffix').val().trim();
						tempText += '}}~~~~~';

						return tempText;
					});

				return itemsText.get().join('\n');
			}

			function mergeArchiveText(oldtext) {
				var archiveText = generateArchiveText();
				if (archiveText === '') {
					return oldtext;
				}

				var header = date.monthHeaderRegex().exec(oldtext);
				if (header !== null) {
					var idx = oldtext.indexOf(header[0]) + header[0].length;
					return oldtext.substring(0, idx) + '\n' + archiveText + oldtext.substring(idx);
				} else {
					var idx = oldtext.indexOf('\n==');
					if (idx === -1) {
						idx = 0;
					}
					return oldtext.substring(0, idx) + '\n' + date.monthHeader() + '\n' + archiveText + '\n' + oldtext.substring(idx);
				}
			}

			function previewPage() {
				api.post({
					action: 'parse',
					contentmodel: 'wikitext',
					text: mergeMainText(),
					title: bulletinTitle,
					summary: $('#be-summary').val() + summarySuffix,
					prop: 'text',
					formatversion: '2',
				}).done(function(data) {
					$('.be-preview-boxes').hide();
					$('#be-summary-box').show();
					$('#be-summary-body').html(data.parse.parsedsummary);
					$('#be-preview-box').show();
					$('#be-preview-body').html(data.parse.text);
				}).fail(function(error) {
					mw.notify(wgULS('产生预览时发生错误:', '產生預覽時發生錯誤:') + error);
				});
			}

			function diffPage() {
				api.post({
					action: 'query',
					prop: 'revisions',
					titles: bulletinTitle,
					rvdifftotext: mergeMainText(),
					formatversion: '2',
				}).done(function(data) {
					$('.be-preview-boxes').hide();
					$('#be-diff-box').show();
					var diff = data.query.pages[0].revisions[0].diff.body;
					if (diff == '') {
						$('#be-diff-nochange').text(wgULS('公告栏无变更', '公告欄無變更')).show();
						$('#be-diff-body').hide();
					} else {
						$('#be-diff-nochange').hide();
						$('#be-diff-body').html(diff).show();
					}
				}).fail(function(error) {
					mw.notify(wgULS('产生差异时发生错误:', '產生差異時發生錯誤:') + error, { type: 'error' });
				});
			}

			function diffArchive() {
				api.get({
					action: 'query',
					prop: 'revisions',
					rvprop: ['content'],
					titles: archiveTitle,
					formatversion: '2',
				}).done(function(data) {
					var page, text = '';
					page = data.query.pages[0];
					if (!page.missing) {
						text = page.revisions[0].content;
					}

					api.post({
						action: 'query',
						prop: 'revisions',
						titles: archiveTitle,
						rvdifftotext: mergeArchiveText(text),
						formatversion: '2',
					}).done(function(data) {
						$('.be-preview-boxes').hide();
						$('#be-diff-box').show();
						var diff = data.query.pages[0].revisions[0].diff.body;
						if (diff == '') {
							$('#be-diff-nochange').text(wgULS('存档页无变更', '存檔頁無變更')).show();
							$('#be-diff-body').hide();
						} else {
							$('#be-diff-nochange').hide();
							$('#be-diff-body').html(diff).show();
						}
					}).fail(function(error) {
						mw.notify(wgULS('产生差异时发生错误:', '產生差異時發生錯誤:') + error, { type: 'error' });
					});
				}).fail(function(error) {
					mw.notify(wgULS('产生差异时发生错误:', '產生差異時發生錯誤:') + error, { type: 'error' });
				});
			}

			function showEditConflict() {
				$('.be-preview-boxes').hide();
				$('#be-conflict-box').show();
				$('#be-conflict-main').val(mergeMainText());
				$('#be-conflict-archive').val(generateArchiveText());
			}

			function savePage() {
				if (!confirm(wgULS('确认发布变更?', '確認發布變更?'))) {
					mw.notify('已取消操作');
					return;
				}
				api.edit(bulletinTitle, function() {
					return {
						text: mergeMainText(),
						summary: $('#be-summary').val() + summarySuffix,
						basetimestamp: basetimestamp,
						starttimestamp: curtimestamp,
					};
				}).done(function() {
					mw.notify(wgULS('成功保存公告栏', '成功儲存公告欄'));

					if (generateArchiveText() === '') {
						mw.notify(wgULS('没有东西要存档,即将重新加载页面...', '沒有東西要存檔,即將重新載入頁面...'));
						setTimeout(function() { location.reload(); }, 1000);
						return;
					}

					api.edit(archiveTitle, function(revision) {
						return {
							text: mergeArchiveText(revision.content),
							summary: wgULS('存档', '存檔') + summarySuffix,
						};
					}).done(function() {
						mw.notify(wgULS('成功保存存档页,即将重新加载页面...', '成功儲存存檔頁,即將重新載入頁面...'));
						setTimeout(function() { location.reload(); }, 1000);
					}).fail(function(error) {
						mw.notify(wgULS('保存存档页时发生错误:', '儲存存檔頁時發生錯誤:') + error, { type: 'error' });
					});
				}).fail(function(error) {
					if (error === 'editconflict') {
						showEditConflict();
						mw.notify(wgULS('保存公告栏时发生编辑冲突,请从下方复制您的版本并使用传统编辑框解决冲突', '儲存公告欄時發生編輯衝突,請從下方複製您的版本並使用傳統編輯框解決衝突'), { type: 'error' });
					} else {
						mw.notify(wgULS('保存公告栏时发生错误:', '儲存公告欄時發生錯誤:') + error, { type: 'error' });
					}
				});
			}

			var $tbody = $('<tbody>').attr('id', 'be-active-tbody');
			$wrapper.append(
				$('<table>').attr('id', 'be-active-zone').addClass('wikitable')
					.append($('<thead>')
						.append($('<tr>')
							.append($('<th>').css('width', '30px').text(wgULS('类型', '類別'))
								.append($('<img>').attr({
									'src': 'https://upload.wikimedia.org/wikipedia/commons/0/06/OOjs_UI_icon_add.svg',
									'title': wgULS('增加一个公告类型', '增加一個公告類別'),
								}).on('click', function() {
									var $row = createRow().prependTo($tbody);
									createItem().appendTo($row.find('.be-items'));
								})))
							.append($('<th>').css('width', '100%').text(wgULS('公告内容', '公告內容')))
						)
					).append($tbody)
			);

			function createRow(type, prefix, suffix) {
				type = type || '公告';
				prefix = prefix || '';
				suffix = suffix || '';

				var $tr = $('<tr>').addClass('be-row').appendTo($tbody);

				// td 1
				var $type = $('<td>').addClass('be-type-col').appendTo($tr);
				$type.append($('<img>').addClass('be-sortable-row-handle').attr({
					'src': 'https://upload.wikimedia.org/wikipedia/commons/c/ca/OOjs_UI_icon_move.svg',
					'title': wgULS('调整公告类型顺序', '調整公告類別順序'),
				}));
				$type.append($('<br>'));
				$type.append($('<input>').addClass('be-type-text be-row-type').val(type));

				// td 2
				var $items = $('<td>').addClass('be-item-col').appendTo($tr);

				$items.append($('<span>').text('Prefix: '));
				$items.append($('<input>').addClass('be-item-text be-row-prefix').val(prefix));

				$items.append($('<br>'));
				$items.append($('<span>').text('Items: '));
				$items.append($('<img>').attr({
					'src': 'https://upload.wikimedia.org/wikipedia/commons/0/06/OOjs_UI_icon_add.svg',
					'title': wgULS('增加一个公告项目', '增加一個公告項目'),
				}).on('click', function(event) {
					createItem().prependTo($(event.target).parents('.be-item-col').find('.be-items'));
				}));

				$('<ul>').addClass('be-items').appendTo($items);

				$items.append($('<span>').text('Suffix: '));
				$items.append($('<input>').addClass('be-item-text be-row-suffix').val(suffix));

				return $tr;
			}

			function createItem(text) {
				text = text || '';

				var $li = $('<li>').addClass('be-item').appendTo($ul);
				$li.append($('<img>').addClass('be-sortable-item-handle').attr({
					'src': 'https://upload.wikimedia.org/wikipedia/commons/c/ca/OOjs_UI_icon_move.svg',
					'title': wgULS('调整公告项目顺序或移动到其他公告类型', '調整公告項目順序或移動到其他公告類別'),
				}));

				// hidden type input
				$li.append($('<input>').addClass('be-type-text be-item-type'));

				// hidden prefix input
				$li.append($('<input>').addClass('be-item-text be-item-prefix'));

				// item input
				$li.append($('<input>').addClass('be-item-text be-item-main').val(text)
					.attr('placeholder', wgULS('空的项目将在发布变更时自动被忽略', '空的項目將在發布變更時自動被忽略'))
				);

				// hidden suffix input
				$li.append($('<input>').addClass('be-item-text be-item-suffix'));

				// archive button
				$li.append($('<img>').addClass('be-archive-btn').attr({
					'src': 'https://upload.wikimedia.org/wikipedia/commons/7/72/OOjs_UI_icon_tray.svg',
					'title': wgULS('存档', '存檔'),
				}).on('click', moveToArchive));

				return $li;
			}

			while (m) {
				var tem = Morebits.wikitext.parseTemplate(mainText, m.index);
				var row = createRow(
					tem.parameters[1],
					tem.parameters.prefix,
					tem.parameters.suffix
				).appendTo($tbody);

				var $ul = row.find('.be-items');
				for (let i = 2; ; i++) {
					if (Object.hasOwnProperty.call(tem.parameters, i)) {
						createItem(tem.parameters[i]).appendTo($ul);
					} else {
						break;
					}
				}

				m = re.exec(mainText);
			}

			var $archiveZone = $('<div>').attr('id', 'be-archive-zone').appendTo($wrapper);
			$archiveZone.append(document.createTextNode(wgULS('存档至', '存檔至')));
			$archiveZone.append($('<a>').attr({
				href: mw.util.getUrl(archiveTitle),
				target: '_blank',
			}).text(archiveTitle));
			$archiveZone.append(document.createTextNode(wgULS('(发布变更时会自动合并Prefix、Suffix)', '(發布變更時會自動合併Prefix、Suffix)')));
			$archiveZone.append($('<ul>').attr('id', 'be-archiveul').addClass('be-items'));

			$wrapper.append($('<span>').text(wgULS('公告栏编辑摘要:', '公告欄編輯摘要:')));
			$wrapper.append($('<input>').attr('id', 'be-summary'));
			$wrapper.append($('<br>'));

			$wrapper.append($('<button>').addClass('be-button').attr('id', 'be-preview').text(wgULS('公告栏预览', '公告欄預覽')).on('click', previewPage));
			$wrapper.append($('<button>').addClass('be-button').attr('id', 'be-diff-page').text(wgULS('公告栏差异', '公告欄差異')).on('click', diffPage));
			$wrapper.append($('<button>').addClass('be-button').attr('id', 'be-diff-archive').text(wgULS('存档差异', '存檔差異')).on('click', diffArchive));
			$wrapper.append($('<button>').addClass('be-button').attr('id', 'be-publish').text(wgULS('发布变更', '發布變更')).on('click', savePage));

			$wrapper.append($('<span>').text(wgULS('警告:本工具未经测试,您需要复查您的编辑并对于负起完整责任。', '警告:本工具未經測試,您需要複查您的編輯並對於負起完整責任。')).css('color', 'red'));

			var $conflictBox = $('<div>').attr('id', 'be-conflict-box').addClass('be-preview-boxes').hide().appendTo($wrapper);
			$conflictBox.append($('<span>').attr('id', 'be-conflict-label').text(wgULS('发生了编辑冲突!请从下方复制您的版本并使用传统编辑框来完成您的编辑,或是重新加载公告栏编辑器(您之前的变更将丢失)。', '發生了編輯衝突!請從下方複製您的版本並使用傳統編輯框來完成您的編輯,或是重新載入公告欄編輯器(您之前的變更將遺失)。')));
			$conflictBox.append($('<br>'))
			$conflictBox.append($('<span>').text(wgULS('公告栏文字', '公告欄文字')))
			$conflictBox.append($(document.createTextNode('(')))
			$conflictBox.append($('<a>').attr({
				href: mw.util.getUrl(bulletinTitle, { action: 'edit' }),
				target: '_blank',
			}).text(wgULS('编辑', '編輯')))
			$conflictBox.append($(document.createTextNode(')')))
			$conflictBox.append($('<textarea>').attr({ id: 'be-conflict-main', rows: 10 }))
			$conflictBox.append($('<span>').text(wgULS('存档页文字', '存檔頁文字')))
			$conflictBox.append($(document.createTextNode('(')))
			$conflictBox.append($('<a>').attr({
				href: mw.util.getUrl(archiveTitle, { action: 'edit' }),
				target: '_blank',
			}).text(wgULS('编辑', '編輯')))
			$conflictBox.append($(document.createTextNode(')')))
			$conflictBox.append($('<textarea>').attr({ id: 'be-conflict-archive', rows: 5 }))

			var $summaryBox = $('<div>').attr('id', 'be-summary-box').addClass('be-preview-boxes').hide().appendTo($wrapper);
			$summaryBox.append($('<span>').text(wgULS('公告栏编辑摘要预览:', '公告欄編輯摘要預覽:')));
			$summaryBox.append($('<span>').attr('id', 'be-summary-body'));

			var $previewBox = $('<div>').attr('id', 'be-preview-box').addClass('be-preview-boxes').hide().appendTo($wrapper);
			$previewBox.append($('<span>').attr('id', 'be-preview-body'));

			var $diffBox = $(`<div>`).attr('id', 'be-diff-box').addClass('be-preview-boxes').hide().appendTo($wrapper);
			$diffBox.append($('<span>').attr('id', 'be-diff-nochange').hide());
			$diffBox.append($(`<table class="diff">
			<colgroup>
				<col class="diff-marker">
				<col class="diff-content">
				<col class="diff-marker">
				<col class="diff-content">
			</colgroup>
			<tbody id="be-diff-body">
			</tbody>
			</table>`));

			$wrapper.append($('<style>').html(`
			.be-item-col {
				min-width: 600px;
			}
			.be-items {
				list-style-type: none;
				background: #ffffbb;
				min-height: 30px;
			}
			.be-type-text {
				width: 30px;
			}
			.be-item-col .be-item-type,
			.be-item-col .be-item-prefix,
			.be-item-col .be-item-suffix {
				display: none;
			}
			.be-moving .be-item-type,
			.be-moving .be-item-prefix,
			.be-moving .be-item-suffix {
				display: none;
			}
			.be-item-text {
				width: 90%;
			}
			#be-archive-zone .be-archive-btn {
				display: none;
			}
			#be-archive-zone {
				margin-bottom: 16px;
			}
			#be-editor {
				margin-bottom: 16px;
			}
			#be-conflict-box,
			#be-summary-box,
			#be-preview-box,
			#be-diff-box {
				margin-top: 16px;
				border: 1px solid;
			}
			#be-conflict-box {
				background: #fcc;
			}
			#be-conflict-label {
				font-weight: bold;
			}
			#be-summary {
				width: 50%;
			}
			.be-button {
				margin-right: 3px;
			}
			#be-publish {
				color: #fff;
				background-color: #36c;
				border-color: #36c;
			}
			`));

			$(function() {
				var oldList;
				$('.be-items').sortable({
					handle: '.be-sortable-item-handle',
					start: function(_event, ui) {
						oldList = ui.item.parent();
						ui.item.addClass('be-moving');
					},
					stop: function(_event, ui) {
						if ($('#be-active-zone').has(oldList).length && $('#be-archive-zone').has(ui.item).length) {
							copyDataFromParent(ui.item, oldList.parents('.be-row'));
						}
						ui.item.removeClass('be-moving');
					},
					change: function(_event, _ui) {
					},
					connectWith: '.be-items',
				}).disableSelection();
			});
			$('#be-active-tbody').sortable({
				handle: '.be-sortable-row-handle',
			});
		}).fail(function(e) {
			mw.notify(wgULS('加载内容时发生错误:', '載入內容時發生錯誤:') + e, { type: 'error' });
		});
	}

	mw.loader.using(['ext.gadget.site-lib', 'ext.gadget.morebits', 'mediawiki.api', 'mediawiki.diff.styles'], function() {
		if (mw.config.get('wgPageName') === 'Template:Bulletin') {
			var link = mw.util.addPortletLink(
				'p-views', '#', wgULS('可视化编辑', '視覺化編輯'), 'ca-bulletin-editor',
				wgULS('使用公告栏编辑器编辑此页面', '使用公告欄編輯器編輯此頁面'),
				null, '#ca-history'
			);
			$(link).addClass('collapsible');
			$(link).on('click', main);
		}
		if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && /\/bulletin-editor$/.test(mw.config.get('wgPageName'))) {
			main();
		}
	});

})();