“MediaWiki:Gadget-site-lib.js”的版本间差异

删除733字节 、​ 2023年3月17日 (五) 13:19
// 使用Wikiplus小工具快速编辑
(// 使用Wikiplus小工具快速编辑)
标签移动版网页编辑 移动版编辑
 
(未显示同一用户的21个中间版本)
* @Description: LLWiki定义的常用函数,桌面版、手机版均可用,部分函数可能需要额外的JS库
* @Functions: 1. 繁简转换函数(wgULS, wgUCS)
* 2. 杂项(pagenamee, addMobileLinks, isModule, apiFailure)
* 3. API标准方法(timedQuery, timedParse, standardQuery, sectionQuery, safeEdit, safeRedirect)
* 4. OOUI标准方法(confirm, prompt, dialog, tipsy, menu)
* 5. moment标准方法(convertTimezone)
* @Document: [[LLWiki:管理员技术手册]]
* @Author: 无特殊说明时均为[[User:Bhsd]]
// 用于触发依赖jQuery的小部件,请勿改动
window.dispatchEvent( new Event('jquery') );
 
Object.fromEntries = Object.fromEntries || function(entries) {
if (!(typeof entries === 'object' && Symbol.iterator in entries)) {
throw new TypeError('Object.fromEntries只接受iterable作为参数!');
}
var obj = {};
Array.from(entries).forEach(function(entry) {
obj[entry[0]] = entry[1];
});
return obj;
};
 
const pagename = mw.config.get('wgPageName'),
revid = mw.config.get( 'wgRevisionId' ),
cid = mw.config.get( 'wgCurRevisionId' ),
wgUL = mw.config.get( 'wgUserLanguage' ), // 界面语言
wgUC = mw.config.get( 'wgUserVariant' ); // 内容语言
 
/**
 
mw.messages.set( wgULS({
'gadget-lib-fail': '无法获得$1!错误信息:$2', 'gadget-lib-force': '获取历史版本的段落Wikitext必需force参数!',
'gadget-lib-page': '页面', 'gadget-lib-latest': '最新修订', 'gadget-lib-createFail': '创建失败!错误原因:$1',
'gadget-lib-conflict1': '编辑冲突!编辑内容已自动备份,请刷新页面后加载备份并重试。', 'gadget-lib-exist': '页面是否存在',
'gadget-lib-conflict2': '编辑冲突!请备份您的编辑内容后刷新页面重试。', 'gadget-lib-createSuccess': '创建成功!',
'gadget-lib-editFail': '编辑失败!错误信息:$1'
}, {
'gadget-lib-fail': '無法獲得$1!錯誤信息:$2', 'gadget-lib-force': '獲取歷史版本的段落Wikitext必需force參數!',
'gadget-lib-page': '頁面', 'gadget-lib-latest': '最新修訂', 'gadget-lib-createFail': '創建失敗!錯誤原因:$1',
'gadget-lib-conflict1': '編輯衝突!編輯內容已自動備份,請刷新頁面後加載備份並重試。', 'gadget-lib-exist': '頁面是否存在',
'gadget-lib-conflict2': '編輯衝突!請備份您的編輯內容後刷新頁面重試。', 'gadget-lib-createSuccess': '創建成功!',
'gadget-lib-editFail': '編輯失敗!錯誤信息:$1'
}) );
 
*/
const mobileLink = function(ele) {
return $('<a>', {
return $('<a>', {href: ele.href, html: [ $('<i>', {class: "fa fa-" + (ele.icon || 'arrow-circle-right')}),
href: ele.href, class: "fa fa-" + (ele.icon || 'arrow-circle-right'), html: $('<span>', {text: ele.msg ? mw.msg( ele.msg ) : ele.text})
] }).wrap( '<li>' ).parent().attr( ele.attr || {} )[0];
};
mw.addMobileLinks = function(link) { return Array.isArray( link ) ? link.map( mobileLink ) : mobileLink( link ); };
*/
mw.isModule = function(name, flag) {
const fullname = (flag ? 'ext.gadget.' : '') + name;
return ['loading', 'loaded', 'executing', 'ready'].includes( mw.loader.getState( fullname ) );
};
 
*/
mw.apiFailure = function(reason, topic) {
mw.notify( mw.msg('gadget-lib-fail', topic, reason), {type: 'error', autoHideSeconds: 'long', tag: 'apiFailure'} );
};
 
*/
mw.timedQuery = function(api, params, topic) {
console.log('API request: 查询' + topic);
const now = mw.now();
return api.get( $.extend({action: 'query', formatversion: 2}, params) ).then(function(data) {
console.log('End API request: 已获得' + topic + ',用时 ' + (mw.now() - now) + ' ms');
return data;
}, function(reason) {
mw.apiFailure(reason, topic);
throw reason;
});
};
 
*/
mw.timedParse = function(api, params, topic) {
console.log('API request: 解析' + topic);
const now = mw.now();
return api.post( $.extend({action: 'parse', prop: 'text', disablelimitreport: 1, disableeditsection: 1,
title: pagename, pst: 1, formatversion: 2}, params) ).then(function(data) {
console.log('End API request: 已获得' + topic + ',用时 ' + (mw.now() - now) + ' ms');
return data;
}, function(reason) {
mw.apiFailure(reason, topic);
throw reason;
});
};
 
*/
mw.standardQuery = function(api) {
mw.request = mw.request || mw.timedQuery(api, {revids: revid, prop: 'revisions', rvprop: 'content'},
mw.msg('gadget-lib-page') + 'Wikitext');
return mw.request;
};
 
*/
mw.sectionQuery = function(api, section, force) {
if (!force && revid < cid) {
mw.notify( mw.msg('gadget-lib-force'), {type: 'warn', autoHideSeconds: 'long', tag: 'historySection'});
return Promise.reject( 'historySection' );
}
}
section = section || 0;
mw.sections = mw.sections || [];
mw.sections[section] = mw.sections[section] || mw.timedQuery(api, {action: 'parse', oldid: revid,
prop: 'wikitext|sections', section: section}, '段落Wikitext');
return mw.sections[section];
};
 
* @Dependencies: mediawiki.api
* @Param {mw.Api} api, mw.Api对象
* @Param {Number} curRevid, 最新修订编号(默认为当前页面)
* @Param {Object} params, API参数对象
* @Param {Boolean} flag, 是否启用自动备份(可选,只会影响错误信息)
* @Return {Promise} Promise对象,API请求失败时抛出revisionQueryFailure或editFailure,编辑冲突时抛出editConflict
*/
mw.safeEdit = function(api, curRevid, params, flag) {
console.debug('API request: 开始提交编辑');
return mw.timedQuery(api, {prop: 'info', titles: params.title, pageids: params.pageid},
const now = mw.now();
mw.msg('gadget-lib-latest')).then(function(data) {
return api.postWithEditToken( $.extend({action: 'edit', baserevid: cid}, params) ).then(function() {
// 未创建的页面lastrevid返回undefined,因此下式同样为false
console.debug('End API request: 编辑成功!用时 ' + (mw.now() - now) + ' ms');
if (data.query.pages[0].lastrevid > (curRevid || cid)) {
}, function(reason) {
mw.notify( mw.msg('gadget-lib-conflict' + (flag ? 2 : 1)),
if (reason == 'editconflict') {
{type: 'error', autoHideSeconds: 'long', tag: 'editConflict'} );
mw.notify( mw.msg('gadget-lib-conflict' + (flag ? 2 : 1)),
throw 'editConflict';
{type: 'error', autoHideSeconds: 'long', tag: 'editConflict'} );
}
throw 'editConflict';
console.log('API request: 开始提交编辑');
}
const now = mw.now();
mw.notify( mw.msg('gadget-lib-editFail', reason),
return api.postWithEditToken( $.extend({action: 'edit'}, params) ).then(function() {
{type: 'error', autoHideSeconds: 'long', tag: 'apiFailure'} );
console.log('End API request: 编辑成功!用时 ' + (mw.now() - now) + ' ms');
throw 'editFailure';
}, function(reason) {
});
mw.notify( mw.msg('gadget-lib-editFail', reason),
{type: 'error', autoHideSeconds: 'long', tag: 'apiFailure'} );
throw 'editFailure';
});
}, function() { throw 'revisionQueryFailure'; }); // mw.timedQuery已通知错误信息
};
 
*/
mw.safeRedirect = function(api, title, target, summary) {
return mw.timedQuery(api, {titles: title, converttitles: 1}, mw.msg('gadget-lib-exist')).then(function(x) {
const converted = x.query.pages[0],
ctitle = converted.title;
if (!converted.missing) {
mw.notify([
mw.msg('gadget-lib-page'),
// 这个href会出现在地址栏,因此手动填入短地址'/zh'而非使用mw.util.getUrl
$('<a>', {text: ctitle, href: '/zh?redirect=no&title=' + mw.util.wikiUrlencode( ctitle )}),
'已存在!'
], {type: 'error', autoHideSeconds: 'long'});
throw 'pageExists';
}
}
console.log('API request: 开始新建重定向');
const now = mw.now();
api.create(title, {summary: summary}, '#重定向 [[' + (target || pagename) + ']]').then(function() {
console.log('End API request: 成功新建重定向,用时 ' + (mw.now() - now) + ' ms');
mw.notify( mw.msg('gadget-lib-createSuccess'), {type: 'success'} );
}, function(reason) {
mw.notify( mw.msg('gadget-lib-createFail', reason),
{type: 'error', autoHideSeconds: 'long', tag: 'apiFailure'} );
throw 'createFailure';
});
}, function() { throw 'queryFailure'; }); // mw.timedQuery已通知错误信息
};
 
*/
mw.confirm = function(text, flags) {
return OO.ui.confirm(text, {actions: [{label: "否"}, {label: "是", flags: flags, action: 'accept'}]});
};
 
*/
mw.prompt = function(text, flags, config) {
return OO.ui.prompt(text, {actions: [ {label: mw.msg('ooui-dialog-message-reject')},
{label: mw.msg('ooui-dialog-message-accept'), flags: flags, action: 'accept'} ], textInput: config});
};
 
* @Param {jQuery} $message, 文字(可选)
* @Param {jQuery} $title, 标题(可选)
* @Param {Object} config,其他对话框设置(可选)
* @Return {Promise} Promise对象
* @Return {Promise} Promise对象,resolve返回的action或是reject字符串"noAction"
*/
mw.dialog = function(dialog, actions, $message, $title, config) {
// 一个WindowManager只能打开一个MessageDialog
if (!dialog.getManager()) {
const manager = new OO.ui.WindowManager();
manager.$element.appendTo( document.body );
manager.addWindows( [dialog] );
}
}
dialog.message.$label.html( $message ); // undefined不更新message
dialog.title.$label.html( $title ); // undefined不更新title
const open return= dialog.open( $.extend({actions: actions}, config).opening.then(function( ) {;
open.opening.then(function() {
// 使href生效
// 使href生效
dialog.attachedActions.forEach(function(ele) {
actions.filter(function(ele) { return ele.$button.off( 'click'href; }).clickforEach(function(ele) { dialog.close(); });
ele.$button.off( 'click' ).click(function() { dialog.close(); });
});
});
});
// 防止再次打開時丟失事件
if ($message instanceof jQuery) { open.closing.then(function() { $message.detach(); }); }
else if (Array.isArray( $message )) {
open.closing.then(function() {
$message.forEach(function($ele) { if ($ele instanceof jQuery) { $ele.detach(); } });
});
}
return open.closed.then(function(data) {
if (data) { return data.action; }
throw 'noAction';
});
};
 
* @Param {Object} params, 参数(可选)
* @Param {jQuery} $content, 标签对象(可选)
* @Return {OO.ui.PopupWidget}
*/
mw.tipsy = function($container, target, params, $content) {
const $label = $('<span>'),
// 这里不用PopupWidget自带的autoClose功能,因为效果很奇怪;默认样式见[[mediawiki:gadget-site-styles.css]]
popup = new OO.ui.PopupWidget( $.extend({$content: $content ? $content.append( $label ) : $label,
padded: true, width: null, classes: ['mw-tipsy']}, params) );
popup.$element.appendTo( document.body );
// jQuery的mouseenter和mouseleave实际上是mouseover和mouseout
// 手机浏览器支持见[https://patrickhlauke.github.io/touch/tests/results/]
// $container不能是body,否则iOS无效([https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html])
$(container).on('mouseenter', target, function() {
const $this = $(this);
var title = this.title;
// 不能寫成$this.data('title', title)
if (title) { $this.attr('data-title', title).removeAttr( 'title' ); }
else { title = $this.data('title') || ''; }
if $label.text( !title) ){ return; }
$label.text( title );
popup.toggle( true ).setFloatableContainer( $this );
popup.toggle( true ).setFloatableContainer( $this );
}).on('mouseleave', target, function() { popup.toggle( false ); });
}).on('mouseleave', target, function() { popup.toggle( false ); });
return popup;
};
 
/**
* @Function: 生成一个仿OOUI样式标准化的下拉菜单
* @Dependencies: oojs-ui-core, ext.gadget.site-styles
* @Param {Object[]} options, 形如{text, msgicon, icondata, href, target, click, data}的菜单项
* @Param {String} text, 文本,必须独一无二
* @Param {String} msg, mw.messages的键值
* @Param {String} icon, FontAwesome的图标名称(仅限fas类,可选)
* @Param {Object} data, 数据(可选,默认为text)
* @Param {String} href, 链接(可选)
* @Param {String} target(可选)
* @Param {Function} click, 单击事件(可选)
* @Param {Object} dataconfig, 数据菜单设置(可选)
* @Param {ObjectBoolean} attrunselectable, 菜单外层容器属性是否不可选中(可选)
* @Return {OO.ui.MenuSelectWidget}
* @Param {Number} multiSelect, 允许的选择数(可选)
* @Return {jQuery} 菜单外层容器,注意初始化时不可连锁
*/
mw.menu = function(options, attrconfig, multiSelectunselectable) {
const hasIcon = options.some(function(elee) { return elee.icon; }),
$menu hasClick = $('<div>', $.extend({html: options.mapsome(function(elee) { return e.href || e.click; }),
menu = new OO.ui.MenuSelectWidget( $.extend({ classes: ['site-menu'], hideWhenOutOfView: false,
const $child = $('<a>', {href: ele.href, target: ele.target, class: ele.selected ? 'site-menu-selected' : '', html: [
items: options.map(function(e) {
hasIcon ? $('<i>', {class: 'fa' + (ele.icon ? 'fa-' + ele.icon : '')}) : null,
const widget = new OO.ui.MenuOptionWidget({label: e.text, data: e.data || e.text});
ele.msg ? mw.msg( ele.msg ) : ele.text
// 不使用OO.ui.HtmlSnippet,防止污染label属性
]}).click( ele.click );
if (hasIcon) { widget.$label.prepend( $('<i>', {class: 'fa' + (e.icon ? ' fa-' + e.icon : '')}) ); }
if ($.isPlainObject( ele.data )) { $child.data( ele.data ); }
return widget;
else { $child.data('data', ele.data); }
})
return $child;
}, config) );
}), class: 'site-menu', tabindex: -1}, attr)).extend({ open: function() {
options.filter(function(e) { return e.selected; }).forEach(function(e) { menu.selectItemByLabel( e.text ); });
this.slideDown( 'fast' ).focus();
menu.$element.appendTo( document.body );
return this;
// 有时完全不需要记录选项的功能
} }).extend(multiSelect && { getSelected: function() { return this.children( '.site-menu-selected' ); },
if (unselectable) { menu.on('toggle', function(visible) { if (visible) { menu.unselectItem(); } }); }
clearSelected: function() {
if (!hasClick) { return menu; }
this.getSelected().removeClass( 'site-menu-selected' );
return menu.on('choose', function(item, selected) {
return this;
if (!selected) { return; }
},
const option = options.find(function(e) { return e.text == item.getLabel(); });
setSelected: function(child) {
if (option.click) { option.click(); }
const $child = $(child); // child已经是jQuery了也没关系
if (option.href) { location.href = option.href; }
if (this[0] != $child.parent()[0]) { return; }
});
if (multiSelect == 1) { this.clearSelected(); } // 单选
$child.addClass( 'site-menu-selected' );
return this;
},
getData: function() { return this.getSelected().map(function() { return $(this).data( 'data' ); }).toArray(); }
}).blur(function() { $(this).slideUp( 'fast' ); }).on('click', 'a', function() {
$menu.slideUp( 'fast' );
if (multiSelect) { $menu.setSelected( this ); }
});
return $menu;
};
 
*/
mw.convertTimezone = function(then, timezone) {
if (timezone === '' || timezone === null) { timezone = undefined; } // 防止isNaN返回false
then = then || moment();
if (isNaN( timezone )) { // 时区名称
const date = new Date(then);
return [ timezone ? moment( date.toLocaleString('ia', {timeZone: timezone} ), 'DD-MM-YYYY HH:mm') : then,
date.toLocaleString('en-us', {timeZone: timezone, year: '2-digit', timeZoneName: 'short'}).slice(4) ];
} else { // UTC偏移量
return [ moment.utc( then ).add(timezone, 'hour'),
'UTC' + (timezone == '0' ? '' : (timezone > 0 ? '+' : '') + timezone)];
}
}
};
//</nowiki>
16,874

个编辑