注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
// <nowiki> /**! * _________________________________________________________________________________ * | | * | === WARNING: GLOBAL GADGET FILE === | * | Changes to this page affect many users. | * | Please discuss changes on the talk page, [[WP:VPT]] before editing. | * |_________________________________________________________________________________| * * Covert From https://zh.wikipedia.org/w/index.php?title=MediaWiki:Gadget-noteTA.js&oldid=63601886 * @author [[User:SunAfterRain]] */ $(() => { const HanAssist = require('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.$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} == false.`); } } class ApiRetryFailError extends Error { /** * @param {string[]} errors */ constructor(errors) { super(`Api calls failed ${errors.length} time(s) in a row.`); this.name = 'ApiRetryFailError'; this.errors = errors; } toJQuery() { const errorCount = this.errors.length; return $('<div>') .attr({ class: 'error' }) .append( $('<p>') .text(HanAssist.conv({ 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 * @param {number} count * @param {string[]} previousErrors * @return {Promise<Awaited<ReturnType<mw.Api[M]>>>} */ function retryApiRequestES6Warp(method, args, count = 3, previousErrors = []) { if (!count) { return $.Deferred().reject(new ApiRetryFailError(previousErrors)); } const deferred = $.Deferred(); api[method](...args).then(deferred.resolve, error => { console.error(error); if (error && typeof error === 'object' && 'stack' in error) { previousErrors.push(error.stack); } else { previousErrors.push(String(error)); } retryApiRequestES6Warp(method, args, --count, previousErrors) .then(deferred.resolve, deferred.reject); }); return deferred; } /** * @template {GetClassMethods<mw.Api>} M * @param {M} method * @param {Parameters<mw.Api[M]>} args * @return {Promise<Awaited<ReturnType<mw.Api[M]>>>} */ function retryApiRequest(method, ...args) { return retryApiRequestES6Warp(method, args); } /** * @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 { constructor() { super({ size: 'larger' }); this.hash = hash; this.dataIsLoaded = false; this.collapse = true; this.$realContent = $('<div>'); this.mutationObserver = new MutationObserver(() => { this.updateSize(); }); this.mutationObserver.observe(this.$realContent.get(0), { subtree: true, childList: true }); } 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; } destroy() { this.mutationObserver.disconnect(); } getNoteTAParseText() { if (this.noteTAParseText) { return $.Deferred().resolve(this.noteTAParseText); } const $noteTAtitle = $dom.find('.noteTA-title'); const actualTitle = mw.config.get('wgPageName').replace(/_/g, ' '); let wikitext = ''; const titleDeferred = $.Deferred(); if ($noteTAtitle.length) { const titleConv = $noteTAtitle.attr('data-noteta-code'); assert(titleConv, 'titleConv'); let 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'; titleDeferred.resolve(); } else { retryApiRequest('parse', '{{noteTA/multititle|' + actualTitle + '}}', { title: actualTitle, variant: 'zh' }).then(resultHtml => { 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* 转换标题为:'; for (const li of $multiTitle.children()) { const $li = $(li); 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 (const variant in variantText) { const text = variantText[variant]; if (text === null) { continue; } const variants = textVariant[text]; if (!variants) { continue; } for (const variant of variants) { 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'; } titleDeferred.resolve(); }).catch(titleDeferred.reject); } const deferred = $.Deferred(); titleDeferred.then(() => { 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'; } } const $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]'); for (const that of $noteTAlocals) { const $this = $(that); const localConv = $this.attr('data-noteta-code'); let 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; deferred.resolve(wikitext); }).catch(deferred.reject); return deferred; } doExecute() { if (this.dataIsLoaded) { return $.Deferred().resolve(); } this.$realContent.empty().append( $('<p>').text(HanAssist.conv({ hans: '正在加载...', hant: '正在載入...' })) ); return this.getNoteTAParseText() .then(wikitext => retryApiRequest('parse', wikitext, { title: 'Template:CGroup/-', variant: mw.config.get('wgUserVariant') })) .then(parsedHtml => { this.$realContent.empty().html(parsedHtml); this.$realContent.find('.mw-collapsible').makeCollapsible(); this.updateSize(); 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 }); } }); } doExecuteWrap() { if (!this.executePromise) { this.executePromise = this.doExecute(); delete this.lastError; const deferred = $.Deferred(); this.executePromise .then(deferred.resolve) .catch(error => { if (error instanceof OO.ui.Error) { this.lastError = error; } else { deferred.reject(error); } }) .always(() => { delete this.executePromise; }); return deferred; } else { const deferred = $.Deferred(); this.executePromise .then(deferred.resolve) .catch(error => { if (!(error instanceof OO.ui.Error)) { deferred.reject(error); } else { deferred.resolve(); } }) .always(() => { delete this.executePromise; }); return deferred; } } getSetupProcess(data) { return super.getSetupProcess(data).next(() => { this.doExecuteWrap(); this.executeAction('main'); }); } getActionProcess(action) { return super.getActionProcess(action) .next(() => { if (action === 'main') { return this.doExecuteWrap(); } }) .next(() => { if (action === 'main' && this.lastError) { return this.lastError; } return super.getActionProcess(action).execute(); }); } } NoteTAViewer.static = Object.create( OO.ui.ProcessDialog.static ); NoteTAViewer.static.name = 'NoteTALoader-' + hash; NoteTAViewer.static.title = HanAssist.conv({ 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; } function resetAllViewer() { for (const viewer of viewerMap.values()) { viewer.destroy(); } viewerMap.clear(); windowManager.clearWindows(); } const skin = mw.config.get('skin'); let portletId = null; const xNoteTAViewer = 'x-noteTA-viewer'; let globalInit = () => {}; let globalDeinit = () => {}; if (skin === 'vector') { portletId = 'p-noteTA'; let $vectorNoteTATab; globalInit = () => { if ($vectorNoteTATab) { return; } $vectorNoteTATab = $(mw.util.addPortlet(portletId)); $('#p-variants').after( $vectorNoteTATab .removeClass(['mw-portlet-p-noteTA']) .addClass(['mw-portlet-noteTA', 'vector-menu-tabs', 'vector-menu-tabs-legacy']) ); }; globalDeinit = () => { if (!$vectorNoteTATab) { return; } $noteTATab.find('ul').empty(); mw.util.hidePortlet(portletId); }; } else if (skin === 'vector-2022') { portletId = 'p-associated-pages'; globalDeinit = () => { $(document.getElementsByClassName(xNoteTAViewer)).remove(); }; } function addPortletItem(hash) { const $portlet = $(mw.util.addPortletLink( portletId, '#', '汉/漢', `ca-noteTA-${hash}` )); $portlet .addClass(xNoteTAViewer) .find('a') .empty() .append( $('<div>') .append( $('<span>') .css({ padding: '1px 3px', background: '#d3e3f4', color: '#000000', height: '85%' }).text('汉'), $('<span>') .css({ padding: '1px 3px', background: '#e9e9e9', color: '#434343', height: '85%' }).text('漢') ) ); return $portlet; } function noteTAViewer() { resetAllViewer(); globalDeinit(); globalInit(); for (const ele of $('.mw-indicator[id^=mw-indicator-noteTA-]')) { const hash = ele.id.replace(/^mw-indicator-noteTA-/, ''); let $ele = $(ele); if (portletId) { $ele.hide(); $ele = addPortletItem(hash); } $ele.on('click', (e) => { e.preventDefault(); getViewer(hash).open(); }); } } noteTAViewer.get = getViewer; noteTAViewer.reset = resetAllViewer; noteTAViewer.globalInit = globalInit; noteTAViewer.globalDeinit = globalDeinit; noteTAViewer.addPortletItem = addPortletItem; mw.libs.noteTAViewer = noteTAViewer; mw.hook('wikipage.content').add(function ($content) { noteTAViewer(); }); }); // </nowiki>