MediaWiki:Gadget-oldNoteTA.js:修订间差异
跳转到导航
跳转到搜索
添加的内容 删除的内容
小无编辑摘要 |
标签:已被回退 |
||
第1行: | 第1行: | ||
// 复制自<https://zh.wikipedia.org/w/index.php?title=MediaWiki:Gadget-noteTA.js&oldid=82085204>,按CC BY-SA 4.0 |
|||
/** |
|||
* NoteTAViewer |
|||
* @description Viewer for NoteTA-related templates and modules, original code from https://zh.wikipedia.org/wiki/MediaWiki:Gadget-noteTA.js . |
|||
* @modifier Winston Sung |
|||
* @license CC BY-SA 3.0 |
|||
* @external "jQuery 1.12.4" |
|||
* @external "jQuery UI 1.12.1" |
|||
* @external "I18n-js" |
|||
*/ |
|||
// <nowiki> |
// <nowiki> |
||
/**! |
|||
/* mw.loader.load('https://code.jquery.com/jquery-1.12.4.js'); */ |
|||
* _________________________________________________________________________________ |
|||
mw.loader.load('https://code.jquery.com/ui/1.12.1/jquery-ui.js'); |
|||
* | | |
|||
mw.loader.load('/zh/wiki/MediaWiki:Gadget-NoteTA.css?action=raw&ctype=text/css', 'text/css' ); |
|||
* | === 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(); |
|||
noteTAViewer = ( function() { $( function() { |
|||
var api = null; |
|||
var init = function( hash ) { |
|||
var $dialog = $( '<div class="noteTA-dialog" />' ); |
|||
$dialog.html( '<div class="mw-ajax-loader" style="margin-top: 48px;" />' ); |
|||
$dialog.dialog( { |
|||
title: wgULS( '字词转换', '字詞轉換' ) |
|||
} ); |
|||
api = new mw.Api(); |
|||
run( $dialog, hash ); |
|||
return $dialog; |
|||
}, run = function( $dialog, hash ) { |
|||
var wikitext = ''; |
|||
var $dom = $( '#noteTA-' + hash ); |
|||
var collapse = true; |
|||
var actualTitle = mw.config.get( 'wgPageName' ).replace( /_/g, ' ' ); |
|||
/** @type {Map<string, OO.ui.ProcessDialog>} */ |
|||
var parse = function() { |
|||
const viewerMap = new Map(); |
|||
api.post( { |
|||
const windowManager = new OO.ui.WindowManager(); |
|||
action: 'parse', |
|||
windowManager.$element.appendTo(document.body); |
|||
title: 'Template:CGroup/-', |
|||
text: wikitext, |
|||
prop: 'text', |
|||
variant: mw.config.get( 'wgUserVariant' ) |
|||
} ).done( function( results ) { |
|||
$dialog.html( results.parse.text['*'] ); |
|||
if ( collapse ) { |
|||
/* $dialog.find( '.mw-collapsible' ).makeCollapsible(); */ |
|||
$dialog.find( '.mw-collapsible-toggle' ).on( 'click.mw-collapse', function( e ) { |
|||
var $collapsibleContent = $( this ).parent( '.mw-collapsible' ).find( '.mw-collapsible-content' ); |
|||
setTimeout( function() { |
|||
$collapsibleContent.promise().done( function() { |
|||
$dialog.dialog( 'option', 'position', 'center' ); |
|||
} ); |
|||
}, 0 ); |
|||
} ); |
|||
} |
|||
$dialog.dialog( 'option', 'width', Math.round( $( window ).width() * 0.8 ) ); |
|||
$dialog.css( 'max-height', Math.round( $( window ).height() * 0.8 ) + 'px' ); |
|||
$dialog.dialog( 'option', 'position', 'center' ); |
|||
} ).fail( parse ); |
|||
}, maybeTitle = parse; |
|||
/** |
|||
var $noteTAtitle = $dom.find( '.noteTA-title' ); |
|||
* @param {any} value |
|||
if ( $noteTAtitle.length ) { |
|||
* @param {string} valueName |
|||
var titleConv = $noteTAtitle.attr( 'data-noteta-code' ); |
|||
* @return {asserts value} |
|||
var titleDesc = $noteTAtitle.attr( 'data-noteta-desc' ); |
|||
*/ |
|||
if ( titleDesc ) { |
|||
function assert(value, valueName) { |
|||
titleDesc = '(' + titleDesc + ')'; |
|||
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 { |
} else { |
||
previousErrors.push(String(error)); |
|||
titleDesc = ''; |
|||
} |
} |
||
retryApiRequestES6Warp(method, args, --count, previousErrors) |
|||
wikitext += '<span class="plainlinks" style="float: right;">[{{fullurl:' + actualTitle + '|action=edit§ion=0}} {{int:edit}}]</span>\n'; |
|||
.then(deferred.resolve, deferred.reject); |
|||
wikitext += '; 本文使用标题手工转换\n'; |
|||
}); |
|||
wikitext += '* 转换标题为:-{D|' + titleConv + '}-' + titleDesc + '\n'; |
|||
return deferred; |
|||
wikitext += '* 实际标题为:-{R|' + actualTitle + '}-\n'; |
|||
} |
|||
wikitext += '* 当前显示为:-{|' + titleConv + '}-\n'; |
|||
} else { |
|||
/** |
|||
maybeTitle = function() { |
|||
* @template {GetClassMethods<mw.Api>} M |
|||
api.post( { |
|||
* @param {M} method |
|||
action: 'parse', |
|||
* @param {Parameters<mw.Api[M]>} args |
|||
title: actualTitle, |
|||
* @return {Promise<Awaited<ReturnType<mw.Api[M]>>>} |
|||
text: '{{noteTA/multititle|' + actualTitle + '}}', |
|||
*/ |
|||
prop: 'text', |
|||
function retryApiRequest(method, ...args) { |
|||
variant: 'zh' |
|||
return retryApiRequestES6Warp(method, args); |
|||
} ).done( function( results ) { |
|||
} |
|||
var $multititle = $( results.parse.text['*'] ).find( '.noteTA-multititle' ); |
|||
if ( $multititle.length ) { |
|||
/** |
|||
var textVariant = {}, variantText = {}, multititleText = ''; |
|||
* @param {string} hash |
|||
$multititle.children().each( function() { |
|||
*/ |
|||
var $li = $( this ); |
|||
function getViewer(hash) { |
|||
var variant = $li.attr( 'data-noteta-multititle-variant' ); |
|||
if (viewerMap.has(hash)) { |
|||
var text = $li.text(); |
|||
const viewer = viewerMap.get(hash); |
|||
variantText[variant] = text; |
|||
assert(viewer, 'viewer'); |
|||
if ( textVariant[text] ) { |
|||
return viewer; |
|||
textVariant[text].push( variant ); |
|||
} |
|||
} else { |
|||
textVariant[text] = [ variant ]; |
|||
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 = []; |
|||
multititleText += '; 本文标题可能经过转换\n'; |
|||
const titleConverted = variantText[mw.config.get('wgUserVariant')]; |
|||
multititleText += '* 转换标题为:'; |
|||
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'; |
|||
$.each( variants, function() { |
|||
} |
|||
variantText[this] = null; |
|||
titleDeferred.resolve(); |
|||
}).catch(titleDeferred.reject); |
|||
var variantsName = $.map( variants, function( variant ) { |
|||
} |
|||
return '-{R|{{MediaWiki:Variantname-' + variant + '}}}-'; |
|||
} ).join( '、' ); |
|||
const deferred = $.Deferred(); |
|||
multititle.push( variantsName + ':-{R|' + text + '}-' ); |
|||
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'; |
|||
} |
} |
||
multititleText += multititle.join( ';' ); |
|||
multititleText += '\n* 实际标题为:-{R|' + actualTitle + '}-\n* 当前显示为:-{R|' + titleConverted + '}-\n'; |
|||
wikitext = multititleText + wikitext; |
|||
} |
} |
||
parse(); |
|||
} ).fail( maybeTitle ); |
|||
}; |
|||
} |
|||
const $noteTAlocal = $dom.find('.noteTA-local'); |
|||
if ( |
if ($noteTAlocal.length) { |
||
collapse = true; |
this.collapse = true; |
||
wikitext += '<span style="float: right;">{{edit|' + actualTitle + '|section=0}}</span>\n'; |
|||
} |
|||
wikitext += '; 本文使用[[Help:中文维基百科的繁简、地区词处理#控制自动转换的代碼|全文手工转换]]\n'; |
|||
$noteTAgroups.each( function() { |
|||
const $noteTAlocals = $noteTAlocal.children('*[data-noteta-code]'); |
|||
var $this = $( this ), text = ''; |
|||
for (const that of $noteTAlocals) { |
|||
switch ( $this.attr( 'data-noteta-group-source' ) ) { |
|||
const $this = $(that); |
|||
case 'template': |
|||
const localConv = $this.attr('data-noteta-code'); |
|||
let localDesc = $this.attr('data-noteta-desc'); |
|||
break; |
|||
if (localDesc) { |
|||
case 'module': |
|||
localDesc = '(' + localDesc + ')'; |
|||
wikitext += '{{#invoke:CGroupViewer|dialog|' + $this.attr( 'data-noteta-group' ) + '}}\n'; |
|||
} else { |
|||
localDesc = ''; |
|||
} |
|||
wikitext += '; 本文使用的公共转换组「' + $this.attr( 'data-noteta-group' ) + '」尚未创建\n'; |
|||
wikitext += '* -{D|' + localConv + '}-' + localDesc + '当前显示为:-{' + localConv + '}-\n'; |
|||
wikitext += '* <span class="plainlinks">[{{fullurl:Module:CGroup/' + $this.attr( 'data-noteta-group' ) + '|action=edit}} 创建公共转换组「' + $this.attr( 'data-noteta-group' ) + '」]</span>\n'; |
|||
} |
|||
} |
|||
wikitext += '; 未知公共转换组「' + $this.attr( 'data-noteta-group' ) + '」来源「' + $this.attr( 'data-noteta-group-source' ) + '」\n'; |
|||
wikitext += '{{noteTA/footer}}\n'; |
|||
this.noteTAParseText = wikitext; |
|||
deferred.resolve(wikitext); |
|||
}).catch(deferred.reject); |
|||
return deferred; |
|||
} |
} |
||
} ); |
|||
doExecute() { |
|||
var $noteTAlocal = $dom.find( '.noteTA-local' ); |
|||
if ( |
if (this.dataIsLoaded) { |
||
return $.Deferred().resolve(); |
|||
collapse = true; |
|||
wikitext += '<span style="float: right;"><span class="noprint" style="white-space:nowrap; font-size:smaller">[{{fullurl:' + actualTitle + '|action=edit§ion=0}} {{int:edit}}]</span></span>\n'; |
|||
wikitext += '; 本文使用全文手工转换\n'; |
|||
var $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'; |
|||
} ); |
|||
} |
|||
this.$realContent.empty().append( |
|||
/* wikitext += '{{转换/末尾}}\n'; */ |
|||
$('<p>').text(HanAssist.conv({ hans: '正在加载...', hant: '正在載入...' })) |
|||
maybeTitle(); |
|||
); |
|||
}; |
|||
return this.getNoteTAParseText() |
|||
$( '.mw-indicator[id^=mw-indicator-noteTA-]' ) |
|||
.then(wikitext => retryApiRequest('parse', wikitext, { |
|||
.css( 'cursor', 'pointer' ) |
|||
title: 'Template:CGroup/-', |
|||
.each( function() { |
|||
variant: mw.config.get('wgUserVariant') |
|||
var $dialog = null; |
|||
})) |
|||
var $this = $( this ); |
|||
.then(parsedHtml => { |
|||
var hash = $this.attr( 'id' ).replace( /^mw-indicator-noteTA-/, '' ); |
|||
this.$realContent.empty().html(parsedHtml); |
|||
this.$realContent.find('.mw-collapsible').makeCollapsible(); |
|||
if ( $dialog === null ) { |
|||
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 { |
} 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: 'primary' |
|||
} |
|||
]; |
|||
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; |
|||
} |
|||
$vectorNoteTATab.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); |
|||
} else { |
|||
$ele.css('cursor', 'pointer'); |
|||
} |
|||
$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(); |
|||
mw.hook('wikipage.content').add( function ( $content ) { |
|||
}); |
|||
setTimeout("noteTAViewer();", 0); |
|||
}); |
}); |
||
// </nowiki> |
// </nowiki> |
2024年6月22日 (六) 04:46的版本
// 复制自<https://zh.wikipedia.org/w/index.php?title=MediaWiki:Gadget-noteTA.js&oldid=82085204>,按CC BY-SA 4.0
// <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: 'primary'
}
];
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;
}
$vectorNoteTATab.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);
} else {
$ele.css('cursor', 'pointer');
}
$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>