跳转到内容

MediaWiki:Gadget-noteTA.js/core.js

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Internet Explorer或Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
  • Opera:Ctrl-F5
// <nowiki>
/* jshint esversion: 8 */
// Covert From https://zh.wikipedia.org/w/index.php?title=MediaWiki:Gadget-noteTA.js&oldid=63601886
$(async function () {
	await mw.loader.using(['ext.gadget.HanAssist']);
	const api = new mw.Api();

	/** @type {Map<string, OO.ui.ProcessDialog>} */
	const viewerMap = new Map();
	const windowManager = new OO.ui.WindowManager();
	windowManager.modal = false;
	windowManager.$element.appendTo(document.body);

	/**
	 * @param {any} value
	 * @param {string} valueName 
	 * @return {asserts value} 
	 */
	function assert(value, valueName) {
		if (!value) {
			throw new Error(`Assert Fail, ${valueName} == null.`);
		}
	}

	class ApiRetryFailError extends Error {
		name = 'ApiRetryFailError';
		/** @type {string[]} */
		errors;

		/**
		 * @param {string[]} errors
		 */
		constructor(errors) {
			super(`Api calls failed ${errors.length} time(s) in a row.`);
			this.errors = errors;
		}

		toJQuery() {
			const errorCount = this.errors.length;
			return $('<div>')
				.attr({
					class: 'error'
				})
				.append(
					$('<p>')
						.text(mw.libs.HanAssist.localize({
							hans: `Api 调用连续失败 ${errorCount} 次,${errorCount} 次调用的错误分别为:`,
							hant: `Api 調用連續失敗 ${errorCount} 次,${errorCount} 次調用的錯誤分別為:`,
							other: `Api calls failed ${errorCount} time(s) in a row. Errors: `
						})),
					$('<ol>')
						.append(this.errors.map(v => $('<li>').append(v.split('\n').map(v => $('<p>').text(v)))))
				);
		}
	}

	/**
	 * @typedef {{ [K in keyof C]: C[K] extends (...args: any[]) => any ? K : never; }[keyof C]} GetClassMethods
	 * @template C
	 */

	/**
	 * @template {GetClassMethods<mw.Api>} M
	 * @param {M} method
	 * @param {Parameters<mw.Api[M]>} args
	 * @return {Promise<Awaited<ReturnType<mw.Api[M]>>>}
	 */
	async function retryApiRequest(method, ...args) {
		/** @type {string[]} */
		const errors = [];
		for (let i = 0; i < 3; i++) {
			try {
				return await api[method](...args);
			} catch (error) {
				console.error(error);
				if (error && typeof error === 'object' && 'stack' in error) {
					errors.push(error.stack);
				} else {
					errors.push(String(error));
				}
			}
		}

		throw new ApiRetryFailError(errors);
	}

	/**
	 * @template T
	 * @param {Promise<T>} promise
	 * @return {JQuery.Promise<T>}
	 */
	function nativePromiseToJQueryDeferred(promise) {
		const deferred = $.Deferred();
		promise.then(deferred.resolve, deferred.reject);
		return deferred;
	}

	/**
	 * @param {string} hash
	 */
	function getViewer(hash) {
		if (viewerMap.has(hash)) {
			const viewer = viewerMap.get(hash);
			assert(viewer, 'viewer');
			return viewer;
		}

		const dom = document.getElementById(`noteTA-${hash}`);
		if (!dom) {
			throw new Error(`Can\'t get Element "#noteTA-${hash}".`);
		}
		const $dom = $(dom);

		class NoteTAViewer extends OO.ui.ProcessDialog {
			/** @type {string} */
			hash;
			/** @type {OO.ui.PanelLayout} */
			content;
			/** @type {boolean} */
			collapse;
			/** @type {JQuery} */
			$realContent;

			/** @type {string?} */
			noteTAParseText;

			constructor() {
				super({
					size: 'larger'
				});
				this.hash = hash;
				this.dataIsLoaded = false;
				this.collapse = true;

				this.$realContent = $('<div>');
			}

			initialize() {
				super.initialize();

				this.content = new OO.ui.PanelLayout({
					padded: true,
					expanded: false
				});
				this.$realContent.appendTo(this.content.$element);

				this.$body.append(this.content.$element);

				return this;
			}

			async getNoteTAParseText() {
				if (this.noteTAParseText) {
					return this.noteTAParseText;
				}

				const $noteTAtitle = $dom.find('.noteTA-title');
				const actualTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
				let wikitext = '';

				if ($noteTAtitle.length) {
					var titleConv = $noteTAtitle.attr('data-noteta-code');
					assert(titleConv, 'titleConv');
					var titleDesc = $noteTAtitle.attr('data-noteta-desc');
					if (titleDesc) {
						titleDesc = '(' + titleDesc + ')';
					} else {
						titleDesc = '';
					}
					wikitext += '<span style="float: right;">{{edit|' + actualTitle + '|section=0}}</span>\n';
					wikitext += '; 本文使用[[Help:中文维基百科的繁简、地区词处理#條目標題|标题手工转换]]\n';
					wikitext += '* 转换标题为:-{D|' + titleConv + '}-' + titleDesc + '\n';
					wikitext += '* 实际标题为:-{R|' + actualTitle + '}-;当前显示为:-{|' + titleConv + '}-\n';
				} else {
					const resultHtml = await retryApiRequest('parse', '{{noteTA/multititle|' + actualTitle + '}}', {
						title: actualTitle,
						variant: 'zh'
					});
					const $multiTitle = $($.parseHTML(resultHtml)).find('.noteTA-multititle');

					if ($multiTitle.length) {
						/** @type {Record<string, string[]>} */
						const textVariant = {};
						/** @type {Record<string, string|null>} */
						const variantText = {};
						wikitext += '; 本文[[Help:中文维基百科的繁简、地区词处理#條目標題|标题可能经过转换]]\n* 转换标题为:';
						$multiTitle.children().each(function () {
							const $li = $(this);
							const variant = $li.attr('data-noteta-multititle-variant');
							assert(variant, 'variant');
							const text = $li.text().trim();
							variantText[variant] = text;
							if (textVariant[text]) {
								textVariant[text].push(variant);
							} else {
								textVariant[text] = [variant];
							}
						});

						const multiTitle = [];
						const titleConverted = variantText[mw.config.get('wgUserVariant')];
						for (var variant in variantText) {
							var text = variantText[variant];
							if (text === null) {
								continue;
							}

							const variants = textVariant[text];

							for (const variant of textVariant[text]) {
								variantText[variant] = null;
							}

							const variantsName = variants.map((variant) => `-{R|{{MediaWiki:Variantname-${variant}}}}-`).join('、');
							multiTitle.push(variantsName + ':-{R|' + text + '}-');
						}
						wikitext += multiTitle.join(';');
						wikitext += '\n* 实际标题为:-{R|' + actualTitle + '}-;当前显示为:-{R|' + titleConverted + '}-\n';
					}
				}

				const $noteTAgroups = $dom.find('.noteTA-group > *[data-noteta-group]');
				if ($noteTAgroups.length > 1) {
					this.collapse = true;
				}
				for (const ele of $noteTAgroups) {
					const $ele = $(ele);
					switch ($ele.attr('data-noteta-group-source')) {
						case 'template':
							wikitext += '{{CGroup/' + $ele.attr('data-noteta-group') + '}}\n';
							break;
						case 'module':
							wikitext += '{{#invoke:CGroupViewer|dialog|' + $ele.attr('data-noteta-group') + '}}\n';
							break;
						case 'none':
							wikitext += '; 本文使用的公共转换组“' + $ele.attr('data-noteta-group') + '”尚未创建\n';
							wikitext += '* {{edit|Module:CGroup/' + $ele.attr('data-noteta-group') + '|创建公共转换组“' + $ele.attr('data-noteta-group') + '”}}\n';
							break;
						default:
							wikitext += '; 未知公共转换组“' + $ele.attr('data-noteta-group') + '”来源“' + $ele.attr('data-noteta-group-source') + '”\n';
					}
				}

				var $noteTAlocal = $dom.find('.noteTA-local');
				if ($noteTAlocal.length) {
					this.collapse = true;
					wikitext += '<span style="float: right;">{{edit|' + actualTitle + '|section=0}}</span>\n';
					wikitext += '; 本文使用[[Help:中文维基百科的繁简、地区词处理#控制自动转换的代碼|全文手工转换]]\n';
					const $noteTAlocals = $noteTAlocal.children('*[data-noteta-code]');
					$noteTAlocals.each(function () {
						var $this = $(this);
						var localConv = $this.attr('data-noteta-code');
						var localDesc = $this.attr('data-noteta-desc');
						if (localDesc) {
							localDesc = '(' + localDesc + ')';
						} else {
							localDesc = '';
						}
						wikitext += '* -{D|' + localConv + '}-' + localDesc + '当前显示为:-{' + localConv + '}-\n';
					});
				}

				wikitext += '{{noteTA/footer}}\n';

				this.noteTAParseText = wikitext;
				return wikitext;
			}

			async doExecute() {
				if (this.dataIsLoaded) {
					return;
				}

				this.$realContent.empty().append(
					$('<p>').text(mw.libs.HanAssist.localize({ hans: '正在加载...', hant: '正在載入...' }))
				);

				try {
					const wikitext = await this.getNoteTAParseText();

					const parsedHtml = await retryApiRequest('parse', wikitext, {
						title: 'Template:CGroup/-',
						variant: mw.config.get('wgUserVariant')
					});

					this.$realContent.empty().html(parsedHtml);
					this.$realContent.find('.mw-collapsible').makeCollapsible();

					this.dataIsLoaded = true;
				} catch (error) {
					if (error instanceof ApiRetryFailError) {
						throw new OO.ui.Error(error.toJQuery(), { recoverable: true });
					} else {
						throw new OO.ui.Error(String(error), { recoverable: false });
					}
				}
			}

			async doExecuteWrap() {
				if (!this.executePromise) {
					this.executePromise = this.doExecute();
					delete this.lastError;

					try {
						await this.executePromise;
					} catch (error) {
						if (error instanceof OO.ui.Error) {
							this.lastError = error;
						} else {
							throw error;
						}
					} finally {
						delete this.executePromise;
					}
				} else {
					try {
						await this.executePromise;
					} catch (error) {
						if (!(error instanceof OO.ui.Error)) {
							throw error;
						}
					}
				}
			}

			getSetupProcess(data) {
				return super.getSetupProcess(data).next(() => {
					this.doExecuteWrap();
					this.executeAction('main');
				});
			}

			getActionProcess(action) {
				return super.getActionProcess(action)
					.next(() => {
						if (action === 'main') {
							return nativePromiseToJQueryDeferred(this.doExecuteWrap());
						}
					})
					.next(() => {
						if (action === 'main' && this.lastError) {
							return this.lastError;
						}

						return super.getActionProcess(action).execute();
					});
			}

			getBodyHeight() {
				return Math.floor(($(window).height() || document.body.scrollHeight) * 0.8);
			}
		}

		if (!NoteTAViewer.static || NoteTAViewer.static === OO.ui.ProcessDialog.static) {
			NoteTAViewer.static = Object.assign({}, OO.ui.ProcessDialog.static);
		}

		NoteTAViewer.static.name = 'NoteTALoader-' + hash;
		NoteTAViewer.static.title = mw.libs.HanAssist.localize({ hans: '字词转换', hant: '字詞轉換' });
		NoteTAViewer.static.actions = [
			{
				label: mw.msg('ooui-dialog-process-dismiss'),
				flags: 'safe'
			}
		];

		const viewer = new NoteTAViewer();
		windowManager.addWindows([viewer]);
		viewerMap.set(hash, viewer);

		return viewer;
	}

	async function resetAllViewer() {
		viewerMap.clear();
		await windowManager.clearWindows();
	}

	function noteTAViewer() {
		resetAllViewer();

		for (const ele of $('.mw-indicator[id^=mw-indicator-noteTA-]')) {
			const hash = ele.id.replace(/^mw-indicator-noteTA-/, '');
			$(ele).on('click', (e) => {
				e.preventDefault();
				getViewer(hash).open();
			});
		}
	}
	noteTAViewer.get = getViewer;
	noteTAViewer.reset = resetAllViewer;
	
	mw.libs.noteTAViewer = noteTAViewer;

	mw.hook('wikipage.content').add(function ($content) {
		noteTAViewer();
	});
});
// </nowiki>
Cookie帮助我们提供我们的服务。通过使用我们的服务,您同意我们使用cookie。