MediaWiki:Common.js
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/*
* 尘月维基 · 排版工具条
* 在源代码编辑器(WikiEditor)上增加:颜色 / 字号 / 对齐 / 插图 / 调图
* 依赖:ext.wikiEditor、oojs-ui-widgets、oojs-ui-windows
*/
( function () {
'use strict';
var COLORS = [
{ label: '红', value: '#d32f2f' },
{ label: '橙', value: '#ef6c00' },
{ label: '黄', value: '#f9a825' },
{ label: '绿', value: '#2e7d32' },
{ label: '青', value: '#00838f' },
{ label: '蓝', value: '#1565c0' },
{ label: '紫', value: '#6b3fa0' },
{ label: '粉', value: '#d81b60' },
{ label: '灰', value: '#666666' },
{ label: '黑', value: '#000000' }
];
var SIZES = [ '80%', '100%', '120%', '150%', '200%', '250%' ];
var WIDTHS = [ '', '100', '150', '200', '250', '300', '400', '500', '600' ];
var ALIGN_OPTIONS = [
{ label: '默认', value: '' },
{ label: '居中', value: 'center' },
{ label: '靠左', value: 'left' },
{ label: '靠右', value: 'right' }
];
var ALIGN_MAP = { '居中': 'center', '居左': 'left', '居右': 'right', '无': '' };
var FILE_RE = /^\s*\[\[\s*(?:File|Image|文件|图像)\s*:\s*([^|\]]+)((?:\|[^[\]]*)*)\s*\]\]\s*$/i;
var ALIGN_RE = /^(?:center|left|right|none|居中|居左|居右|无)$/i;
var DialogClass = null;
var windowManager = null;
/**
* 生成"包裹选中文字"的动作,套用站内模板
*
* @param {string} template 模板名
* @param {string} arg 第一个参数(颜色 / 字号 / 对齐方式)
* @return {Object} WikiEditor action
*/
function wrapAction( template, arg ) {
return {
type: 'encapsulate',
options: {
pre: '{{' + template + '|1=' + arg + '|2=',
peri: '',
post: '}}'
}
};
}
/**
* 把 [{label, value}] 转成 WikiEditor select 需要的 list
*
* @param {Object[]} items
* @param {string} template
* @return {Object}
*/
function makeList( items, template ) {
var list = {};
items.forEach( function ( item, index ) {
list[ 'opt' + index ] = {
label: item.label,
action: wrapAction( template, item.value )
};
} );
return list;
}
/**
* 从 [[File:...]] 代码里解析文件名、宽度、位置、说明
*
* @param {string} text
* @return {Object|null}
*/
function parseFileLink( text ) {
var match = FILE_RE.exec( text || '' );
if ( !match ) {
return null;
}
var parsed = { name: match[ 1 ].trim(), width: '', align: '', caption: '' };
var params = match[ 2 ] ? match[ 2 ].split( '|' ).slice( 1 ) : [];
params.forEach( function ( raw ) {
var param = raw.trim();
if ( !param ) {
return;
}
if ( /^\d+\s*px$/i.test( param ) ) {
parsed.width = String( parseInt( param, 10 ) );
} else if ( ALIGN_RE.test( param ) ) {
parsed.align = Object.prototype.hasOwnProperty.call( ALIGN_MAP, param ) ?
ALIGN_MAP[ param ] : param.toLowerCase();
} else if ( !parsed.caption ) {
parsed.caption = param;
}
} );
return parsed;
}
/**
* 拼出 [[File:名字|宽度px|位置|说明]]
*
* @param {Object} data
* @return {string}
*/
function buildFileLink( data ) {
var parts = [ '[[File:' + data.name ];
if ( data.width ) {
parts.push( data.width + 'px' );
}
if ( data.align ) {
parts.push( data.align );
}
if ( data.caption ) {
parts.push( data.caption );
}
return parts.join( '|' ) + ']]';
}
/**
* 上传页地址
*
* @return {string}
*/
function uploadUrl() {
if ( mw.util && typeof mw.util.getUrl === 'function' ) {
return mw.util.getUrl( 'Special:Upload' );
}
return '/Special:Upload';
}
/**
* 提示条
*
* @param {string} text
*/
function notify( text ) {
mw.loader.using( 'mediawiki.notification' ).then( function () {
mw.notify( text, { type: 'warn', autoHideSeconds: 5 } );
} );
}
/**
* 图片对话框(插入 / 调整共用一个)
*
* @return {Function} 对话框类
*/
function getDialogClass() {
if ( DialogClass ) {
return DialogClass;
}
/**
* @param {Object} config
*/
function ImageDialog( config ) {
ImageDialog.super.call( this, config );
}
OO.inheritClass( ImageDialog, OO.ui.Dialog );
ImageDialog.static.name = 'chenyue-image';
ImageDialog.static.size = 'medium';
ImageDialog.static.title = '图片';
ImageDialog.static.actions = [
{ action: 'apply', label: '确定', flags: [ 'primary', 'progressive' ] },
{ label: '取消', flags: 'safe' }
];
/**
* @param {Object} context
* @param {Object} data
* @param {boolean} isNew
* @return {ImageDialog}
*/
ImageDialog.prototype.setData = function ( context, data, isNew ) {
this.context = context;
this.data = data;
this.isNew = isNew;
return this;
};
ImageDialog.prototype.initialize = function () {
ImageDialog.super.prototype.initialize.apply( this, arguments );
var data = this.data;
this.title.setLabel( this.isNew ? '插入图片' : '调整图片' );
this.nameInput = new OO.ui.TextInputWidget( {
value: data.name,
placeholder: '图片文件名,例如 熊猫.png',
icon: 'image'
} );
this.widthInput = new OO.ui.DropdownInputWidget( {
options: WIDTHS.map( function ( width ) {
return { data: width, label: width ? width + ' 像素宽' : '按原始大小' };
} ),
value: data.width || ''
} );
this.alignInput = new OO.ui.DropdownInputWidget( {
options: ALIGN_OPTIONS.map( function ( option ) {
return { data: option.value, label: option.label };
} ),
value: data.align || ''
} );
this.captionInput = new OO.ui.TextInputWidget( {
value: data.caption,
placeholder: '图片说明,可留空'
} );
var fieldset = new OO.ui.FieldsetLayout();
fieldset.addItems( [
new OO.ui.FieldLayout( this.nameInput, { label: '文件名', align: 'top' } ),
new OO.ui.FieldLayout( this.widthInput, { label: '大小', align: 'top' } ),
new OO.ui.FieldLayout( this.alignInput, { label: '位置', align: 'top' } ),
new OO.ui.FieldLayout( this.captionInput, { label: '说明', align: 'top' } )
] );
var hint = new OO.ui.HtmlSnippet(
'<p class="chenyue-img-hint">还没上传?' +
'<a href="' + uploadUrl() + '" target="_blank" rel="noopener">去上传图片 →</a></p>'
);
var panel = new OO.ui.PanelLayout( { padded: true, expanded: false } );
panel.$element.append( fieldset.$element, hint.$element );
this.$body.append( panel.$element );
};
ImageDialog.prototype.getBodyHeight = function () {
return 380;
};
ImageDialog.prototype.getActionProcess = function ( action ) {
var dialog = this;
if ( action !== 'apply' ) {
return ImageDialog.super.prototype.getActionProcess.call( this, action );
}
return new OO.ui.Process( function () {
var name = dialog.nameInput.getValue().trim()
.replace( /^(?:File|Image|文件|图像)\s*:\s*/i, '' );
if ( !name ) {
dialog.nameInput.setValidityFlag( false );
dialog.nameInput.focus();
return;
}
var text = buildFileLink( {
name: name,
width: dialog.widthInput.getValue(),
align: dialog.alignInput.getValue(),
caption: dialog.captionInput.getValue().trim()
} );
if ( dialog.isNew ) {
dialog.context.$textarea.textSelection( 'insertText', text );
} else {
dialog.context.$textarea.textSelection( 'encapsulateSelection', {
pre: '', peri: text, post: '', replace: true
} );
}
dialog.close( { action: 'apply' } );
} );
};
DialogClass = ImageDialog;
return DialogClass;
}
/**
* 打开图片对话框
*
* @param {Object} context WikiEditor context
* @param {Object|null} parsed 已有图片信息(调整时传入)
*/
function openImageDialog( context, parsed ) {
var isNew = !parsed;
mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows' ] ).then( function () {
if ( !windowManager ) {
windowManager = new OO.ui.WindowManager();
$( document.body ).append( windowManager.$element );
}
var ImageDialog = getDialogClass();
var dialog = new ImageDialog();
dialog.setData( context, parsed || { name: '', width: '', align: '', caption: '' }, isNew );
windowManager.addWindows( [ dialog ] );
windowManager.openWindow( dialog, {} );
dialog.closed.then( function () {
windowManager.removeWindows( [ dialog ] );
} );
} );
}
/**
* 调整选中图片的大小 / 位置
*
* @param {Object} context
*/
function adjustImage( context ) {
var selection = context.$textarea.textSelection( 'getSelection' );
var parsed = parseFileLink( selection );
if ( !parsed ) {
notify( '先选中整段图片代码再点「调图」,例如 [[File:熊猫.png|300px|center]]' );
return;
}
openImageDialog( context, parsed );
}
/**
* 把排版工具条插进 WikiEditor
*/
function addLayoutTools() {
if ( $( '#wikiEditor-ui-toolbar .group-layout' ).length ) {
return;
}
$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
section: 'main',
groups: {
layout: {
tools: {
layoutColor: {
type: 'select',
label: '颜色',
list: makeList( COLORS, '颜色' )
},
layoutSize: {
type: 'select',
label: '字号',
list: makeList( SIZES.map( function ( size ) {
return { label: size, value: size };
} ), '字号' )
},
layoutAlign: {
type: 'select',
label: '对齐',
list: makeList( ALIGN_OPTIONS.filter( function ( option ) {
return option.value !== '';
} ), '对齐' )
},
layoutImage: {
type: 'button',
label: '插图',
action: {
type: 'callback',
execute: function ( context ) {
openImageDialog( context, null );
}
}
},
layoutAdjust: {
type: 'button',
label: '调图',
action: {
type: 'callback',
execute: function ( context ) {
adjustImage( context );
}
}
}
}
}
}
} );
}
/**
* 等 WikiEditor 渲染出工具条后再挂工具组
*/
function bootstrap() {
if ( !document.getElementById( 'wpTextbox1' ) ) {
return;
}
mw.loader.using( 'ext.wikiEditor' ).then( function () {
var tries = 0;
function waitForToolbar() {
if ( document.getElementById( 'wikiEditor-ui-toolbar' ) ) {
addLayoutTools();
return;
}
tries += 1;
if ( tries <= 30 ) {
window.setTimeout( waitForToolbar, 100 );
}
}
waitForToolbar();
} );
}
$( bootstrap );
}() );