MediaWiki:Gadget-warning.js
Megjegyzés: közzététel után frissítened kell a böngésződ gyorsítótárát, hogy lásd a változásokat.
- Firefox / Safari: tartsd lenyomva a Shift gombot és kattints a Frissítés gombra a címsorban, vagy használd a Ctrl–F5 vagy Ctrl–R (Macen ⌘–R) billentyűkombinációt
- Google Chrome: használd a Ctrl–Shift–R (Macen ⌘–Shift–R) billentyűkombinációt
- Edge: tartsd nyomva a Ctrl-t, és kattints a Frissítés gombra, vagy nyomj Ctrl–F5-öt
/**
* @name Járőrscript
* @fileOverview Gyakran használt sablonok
*
* @author [[user:Báthory Péter]]
* hibajelentés, ötletek, kérések ide: [[Szerkesztővita:BáthoryPéter]]
*
*
* @typedef {object} WarningTemplateParamInput
* @property {string} label
* @property {string|null} [description=null]
* @property {boolean} [required=false]
*
* @typedef {Required<WarningTemplateParamInput>} WarningTemplateParam
*
*
* @typedef {object} WarningTemplateInput
* @property {string} name Unique identifier of the warning
* @property {number[]} ns Namespaces the warning should appear in
* @property {string} template The warning content with MediaWiki message syntax (e.g. `{{foo|param=$1}}`)
* @property {string} comment Edit summary used when adding the warning
* @property {string} type The insertion type of the warning
* @property {boolean} [autosave=true] Whether the page should be automatically saved after adding the template,
* or the user has to press the save button themselves
* @property {WarningTemplateParamInput[]} [params=[]] Parameters used in the template
* @property {string|null} [special=null]
*
* @typedef {Required<WarningTemplateInput> & {params: WarningTemplateParam[]}} WarningTemplate
*/
mw.loader.using( 'mediawiki.util', function () {
if ( mw.util.getParamValue( 'printable' ) === 'yes' || mw.config.get( 'wgIsMainPage' ) ) {
return false;
}
/** @type {WarningTemplate[]} */
var warningTemplates = [],
defaultList = [];
mw.messages.set( {
'gadget-warning-label-monobook': 'Sablonok▼',
'gadget-warning-label-vector': 'S',
'gadget-warning-tooltip': 'Gyakran használt sablonok',
'gadget-warning-save': 'Beszúrás',
'gadget-warning-cancel': 'Mégse',
'gadget-warning-noconfig': 'Nincs konfiguráció',
'gadget-warning-noconfig-body': 'A járőrscript [[$1|konfigurációja]] nem található. Ha nem a magyar Wikipédián használod, másold át [$2 onnan].',
'gadget-warning-invalid-json': 'Érvénytelen JSON',
'gadget-warning-invalid-json-body': 'A járőrscript JSON-konfigurációja szintaktikai hibát tartalmaz.',
'gadget-warning-bad-json': 'Hibás JSON',
'gadget-warning-bad-json-body': 'A járőrscript JSON-konfigurációja szemantikai hibát tartalmaz.',
'gadget-warning-bad-template-data': 'Hibás sablonadatok',
'gadget-warning-bad-template-data-body': 'A járőrscript egy vagy több sablonjának adatai hibásak, ezek nem jelennek meg a listában.',
'gadget-warning-bad-template-list': 'Hibás sablonlista',
'gadget-warning-bad-template-list-body': 'A járőrscript alapértelmezett sablonlistájában egy vagy több ismeretlen sablon szerepel. Ha saját sablonlistád van, ellenőrizd a neveket.',
'gadget-warning-bad-template-name': 'Hibás sablonnév',
'gadget-warning-bad-template-name-body': 'A járőrscriptnek megadott sablon (<nowiki>$1</nowiki>) nem létezik. Kérjük, jelezd a hibát [$2 műszaki kocsmafalon].',
'gadget-warning-page-missing': 'A lap nem létezik',
'gadget-warning-page-missing-body': 'A lap nem létezik (lehet, hogy közben törölve lett), ezért a járőrscript nem mentette el.'
} );
/**
*
* @param {string} key The message key
* @param {'error'|'warn'|'info'|'success'|null} [type='error'] The message severity
* @param {any[]} [params] Any parameters to the body message
*/
function notify( key, type, params ) {
params = params || [];
params.unshift( 'gadget-warning-' + key + '-body' );
mw.notify(
mw.message.apply( undefined, params ),
{
title: mw.msg( 'gadget-warning-' + key ),
type: type || 'error'
}
);
}
/**
* Validate the templates JSON and store
* valid template informations in `warningTemplates`.
*
* @param {WarningTemplateInput[]} templates The JSON config's template list as a JavaScript array
* @return {0|1|2} Error level:
* - 0: everything OK
* - 1: invalid template description(s), some or all templates have not been exported
* - 2: fatal error, argument not an array, no data exported
*/
function validateTemplates( templates ) {
var i, j, ns, params, param,
knownTemplates = [],
state = 0;
/**
* @template T
* @param {T} obj
* @param {keyof T} name
* @returns {boolean}
*/
function checkProp( obj, name ) {
return Object.prototype.hasOwnProperty.call( obj, name );
}
/**
* @param {number} i
* @param {WarningTemplateInput} tl
*/
function checkTemplate( i, tl ) {
if (
$.isPlainObject( tl )
&& ( checkProp( tl, 'name' ) && typeof tl.name === 'string' && knownTemplates.indexOf( tl.name ) === -1 )
&& ( checkProp( tl, 'ns' ) && $.isArray( tl.ns ) )
&& ( checkProp( tl, 'template' ) && typeof tl.template === 'string' )
&& ( checkProp( tl, 'comment' ) && typeof tl.comment === 'string' )
&& ( checkProp( tl, 'type' ) && typeof tl.type === 'string' )
&& ( !checkProp( tl, 'autosave' ) || typeof tl.autosave === 'boolean' )
&& ( !checkProp( tl, 'params' ) || $.isArray( tl.params ) )
&& ( !checkProp( tl, 'special' ) || typeof tl.special === 'string' )
) {
ns = [];
for ( j = 0; j < tl.ns.length; ++j ) {
if ( typeof tl.ns[ j ] === 'number' ) {
ns.push( tl.ns[ j ] );
} else {
mw.log.warn( 'warning.js: ' + tl.name + ': invalid namespace "' + ns[ j ] + '"' );
return 1;
}
}
params = [];
if ( tl.params ) {
for ( j = 0; j < tl.params.length; ++j ) {
param = tl.params[ j ];
if (
$.isPlainObject( param )
&& ( checkProp( param, 'label' ) && typeof param.label === 'string' )
&& ( !checkProp( param, 'description' ) || typeof param.description === 'string' || param.description === null )
&& ( !checkProp( param, 'required' ) || typeof param.required === 'boolean' )
) {
params.push( {
label: param.label,
description: param.description || null,
required: param.required || false
} );
} else {
mw.log.warn( 'warning.js: ' + tl.name + ': invalid parameter #' + j );
return 1;
}
}
}
warningTemplates.push( {
name: tl.name,
ns: ns,
template: tl.template,
comment: tl.comment,
type: tl.type,
autosave: checkProp( tl, 'autosave' ) ? tl.autosave : true,
params: params,
special: checkProp( tl, 'special' ) ? tl.special : null
} );
} else {
mw.log.warn( 'warning.js: invalid template #' + i );
}
}
if ( !$.isArray( templates ) ) {
return 2;
}
for ( i = 0; i < templates.length; ++i ) {
checkTemplate( i, templates[ i ] );
}
return 0;
}
/**
* Validate the default templates list and store `warningTemplates` indexes
* for valid names in `defaultList`. This must be run after validateTemplates()
* terminated, or some valid keys may be marked as invalid.
*
* @param {Array} sitewide The JSON config's default list as a JavaScript array
* @return {0|1|2} Error level:
* - 0: everything OK
* - 1: invalid template key(s), some or all indexes have not been exported
* - 2: fatal error, argument not an array, no data exported
*/
function validateDefaultList( sitewide ) {
var list = window.hasOwnProperty( 'jarorSablonLista' ) ? jarorSablonLista : sitewide,
/** @type {0|1} */
status = 0,
i, j;
if ( !$.isArray( list ) ) {
return 2;
}
for ( i = 0; i < list.length; ++i ) {
j = 0;
while ( j < warningTemplates.length && warningTemplates[ j ].name !== list[ i ] ) {
++j;
}
if ( j < warningTemplates.length ) {
defaultList.push( j );
} else {
mw.log.warn( 'warning.js: unknown template "' + list[ i ] + '"' );
status = 1;
}
}
return status;
}
/*Sablonok listája. Megadandó tulajdonságok:
* 'ns': melyik névtérben jelenjen meg (wgNamespaceNumber). Szögletes zárójelek között vesszővel elválasztva kell felsorolni
* a névterek számát. (A negatív értékűt nem rakja ki magától, arról külön kód gondoskodik)
* 'template': beillesztendő sablon
* 'comment': szerkesztési összefoglaló
* 'autosave': azonnali mentés (true/false)
* 'type': működési mód: 'pre'=elejére, 'post'=végére, 'del'=tartalom törlése, 'comment'=tartalom kikommentezése
* 'param1':
* 'param2': (opcionális) paraméter leírása (akkor kell, ha a beillesztendőben van %1, %2)
*/
/**
* Load and validate data, and run postprocessing functions if
* the validation is successful.
* @param {(() => void)[]} functionsToRun The functions to run
* after page load if valid data is found.
*/
function loadData( functionsToRun ) {
function success( content ) {
var state;
if ( !$.isPlainObject( content ) ) {
return fail( null, 'semantic-error', 'Root element of JSON data must be an object.' );
}
state = content.hasOwnProperty( 'templates' ) ? validateTemplates( content.templates ) : 2;
if ( state === 2 ) {
return fail( null, 'semantic-error', 'The `templates` property must be an array.' );
} else if ( state === 1 ) {
notify( 'bad-template-data', 'warn' );
}
state = content.hasOwnProperty( 'defaultlist' ) ? validateDefaultList( content.defaultlist ) : 2;
if ( state === 2 ) {
return fail( null, 'semantic-error', 'The `defaultlist` property must be an array.' );
} else if ( state === 1 ) {
notify( 'bad-template-list', 'warn' );
}
for ( var i = 0; i < functionsToRun.length; ++i ) {
$( functionsToRun[ i ] );
}
}
function fail( xhr, code, msg ) {
var title, message;
if ( code == 'error' && msg == 'Not Found' ) {
notify(
'noconfig',
null,
[ 'MediaWiki:Gadget-warning.json', 'https://hu.wiki.x.io/wiki/MediaWiki:Gadget-warning.json' ]
);
mw.log.warn( 'Gadget-warning.js: No config' );
mw.log.warn( xhr );
} else if ( code == 'parsererror' ) {
notify( 'invalid-json' );
mw.log.warn( 'Gadget-warning.js: Invalid JSON' );
mw.log.warn( xhr );
mw.log.warn( msg );
} else if ( code == 'semantic-error' ) {
notify( 'bad-json' );
mw.log.warn( 'Gadget-warning.js: Bad JSON: ' + msg );
}
}
var url = mw.util.getUrl('MediaWiki:Gadget-warning.json', {action: 'raw'});
$.get(url, null, null, 'json').done(success).fail(fail);
}
/**
* Megállapítja, hogy van-e jogosultságunk a lap szerkesztésére
* @return {boolean} True, ha jogosultak vagyunk szerkeszteni a lapot
*/
function mayEdit() {
var L,
restrictions = mw.config.get( 'wgRestrictionEdit' ) || mw.config.get( 'wgRestrictionCreate' ) || [],
groups = mw.config.get( 'wgUserGroups' );
for ( var i = 0; i < restrictions.length; ++i ) {
L = false;
for ( var j = 0; j < groups.length; ++j ) {
if ( restrictions[i] === groups[j] ) {
L = true;
}
}
if ( !L ) {
return false;
}
}
return true;
}
/**
* Toggle the template list.
* @param {JQuery.Event} e jQuery Event object
* @param {string} [showall] If `all`, show all available templates
*/
function jaror_show(e, showall) {
e.preventDefault();
showall = showall || e.data || null;
// Ha nyitva volt, bezárja.
// Ha nyitva volt, de "all" paraméterrel lett meghívva, bezárja és kinyitja újra all paraméterrel
if ( $( '#jarorSablonLista' ).length ) {
$( '#jarorSablonLista' ).remove();
if ( !showall ) {
return true;
}
}
/**
* Prompt for template parameters.
*
* @param {string} tlName The template name
* @param {WarningTemplateParam[]} paramsList The parameter data
* @return {JQuery.Promise<string[]>} Resolved with given parameters,
* or rejected if user clicks the "Cancel" button
*/
function promptParams( tlName, paramsList ) {
var deferred = $.Deferred();
/**
* @param {number} i
* @param {WarningTemplateParam} paramData
* @returns {OO.ui.FieldLayout}
*/
function getParamField( i, paramData ) {
var input = new OO.ui.TextInputWidget( {
name: 'params[]',
autofocus: (i === 0),
required: paramData.required
} );
return new OO.ui.FieldLayout(
input,
{
label: paramData.label,
help: paramData.description
}
);
}
mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-windows' ] )
.done( function () {
function ParamsDialog( config ) {
ParamsDialog.super.call( this, config );
}
OO.inheritClass( ParamsDialog, OO.ui.ProcessDialog );
// Specify a name for .addWindows()
ParamsDialog.static.name = 'warningJsParamsDialog';
// Specify a static title and actions.
ParamsDialog.static.title = '{' + '{' + tlName + '}}';
ParamsDialog.static.actions = [
{ action: 'save', label: mw.msg( 'gadget-warning-save' ), flags: 'primary' },
{ action: 'cancel', label: mw.msg( 'gadget-warning-cancel' ), flags: 'safe' }
];
ParamsDialog.prototype.initialize = function () {
ParamsDialog.super.prototype.initialize.apply( this, arguments );
this.panel = new OO.ui.PanelLayout( { padded: true, expanded: false, classes: [ 'warningJsPanelLayout' ] } );
this.fieldset = new OO.ui.FieldsetLayout();
this.content = new OO.ui.FormLayout( {
items: [ this.fieldset ],
action: mw.util.wikiScript(),
method: 'get'
} );
this.fields = [];
for ( var i = 0; i < paramsList.length; ++i ) {
this.fields.push( getParamField( i, paramsList[ i ] ) );
}
this.fields.push( new OO.ui.HiddenInputWidget( {
name: 'action',
value: 'submit'
} ) );
this.fields.push( new OO.ui.HiddenInputWidget( {
name: 'title',
value: mw.config.get( 'wgPageName' )
} ) );
this.fields.push( new OO.ui.HiddenInputWidget( {
name: 'jaror',
value: tlName
} ) );
this.content.addItems( this.fields );
this.panel.$element.append( this.content.$element );
this.$body.append( this.panel.$element );
};
ParamsDialog.prototype.getReadyProcess = function () {
var dialog = this;
return new OO.ui.Process( function () {
dialog.fields[ 0 ].$element.find( 'input' ).focus();
} );
}
ParamsDialog.prototype.getActionProcess = function ( action ) {
var dialog = this;
if ( action === 'save' ) {
return new OO.ui.Process( function () {
var params = [];
for ( var i = 0; i < dialog.fields.length; ++i ) {
if ( dialog.fields[ i ] instanceof OO.ui.FieldLayout ) {
params.push( dialog.fields[ i ].fieldWidget.value );
}
}
deferred.resolve( params );
dialog.close( { action: action } );
} );
}
if ( action === 'cancel' ) {
return new OO.ui.Process( function () {
deferred.reject();
dialog.close( { action: action } );
} );
}
// Fall back to parent handler
return ParamsDialog.super.prototype.getActionProcess.call( this, action );
};
var paramsDialog = new ParamsDialog( { size: 'large' } );
var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
windowManager.addWindows( [ paramsDialog ] );
windowManager.openWindow( paramsDialog );
} )
.fail( deferred.reject );
return deferred.promise();
}
/**
* Navigate to the edit window.
*
* @param {string} tlName The template name
* @param {string[]} [params] The filled template parameters
*/
function navigate( tlName, params ) {
var urlparams = {
action: 'submit',
jaror: tlName,
params: params
};
document.location = mw.util.getUrl( null, urlparams );
}
/**
* A sablon adatai alapján elkészíti az URL paramétereket, majd átugrik szerkesztőmódba.
* Ha a sablon igényel felhasználói inputot, azokat egy prompt ablakkal bekéri
* @param {JQuery.Event} e jQuery Event objektum
* @returns {boolean|void} False, ha egy promptban mégsemre kattintott, vagy ha nem kapott Event.data objektumot
*/
function jarorClick( e ) {
e.preventDefault();
var
/** @type {number} */
i,
/** @type {WarningTemplate} */
tl,
/** @type {string} */
tlName;
if (e.data) {
tlName = e.data.tlName;
for ( i = 0; i < warningTemplates.length; ++i ) {
if ( warningTemplates[ i ].name === tlName ) {
tl = warningTemplates[ i ];
break;
}
}
} else {
return false;
}
if ( tl.params.length ) {
promptParams( tlName, tl.params )
.done( function ( params ) {
navigate( tlName, params );
} );
} else {
navigate( tlName );
}
}
/**
* Get template objects for templates listed in `defaultList`
* @return {WarningTemplate[]}
*/
function getListedTemplates() {
/** @type {WarningTemplate[]} */
var ret = [];
for ( var i = 0; i < defaultList.length; ++i ) {
ret.push( warningTemplates[ defaultList[ i ] ] );
}
return ret;
}
var $warningsNode = $("<div>").attr('id','jarorSablonLista');
var templateObjects = ( showall === 'all' ) ? warningTemplates : getListedTemplates();
// Show templates present in `templateObjects`
for ( var i = 0; i < templateObjects.length; ++i ) {
var tl = templateObjects[ i ];
if ( tl.ns.indexOf( mw.config.get( 'wgNamespaceNumber' ) ) > -1 ) {
$( '<a>' )
.attr( 'title', tl.template )
.text( tl.name )
.click( { tlName: tl.name }, jarorClick )
.appendTo( $warningsNode );
$warningsNode.append( ' | ' );
}
}
if ( showall !== 'all' ) {
$( '<a>' )
.addClass( 'showall' )
.text( '(összes)' )
.click( 'all', jaror_show )
.appendTo( $warningsNode );
}
mw.util.$content.prepend( $warningsNode );
// Welcome templates on non-existent user talk pages
//Megíratlan szerkesztői vitalapokhoz üdvözlő sablonok
if (
mw.config.get( 'wgNamespaceNumber' ) === 3
&& (
mw.config.get( 'wgArticleId' ) === 0
|| ( mw.config.get( 'wgAction' ) === 'edit' && $( '#wpTextbox1' ).val() === '' )
)
) {
// Search for IPv4 or IPv6 address (v6 source: home.deds.nl/~aeron/regex/)
var ip_reg = /(^(?:\d{1,3}\.){3}\d{1,3}$)|(^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$)/i;
var $welcomeNode = $( '<div>' ).attr( 'id', 'udvozloSablonok' );
var is_anon = ip_reg.test( mw.config.get( 'wgTitle' ) );
var first = true;
for ( i = 0; i < warningTemplates.length; ++i ) {
if ( warningTemplates[ i ].special === 'welcome-' + ( is_anon ? 'anon' : 'registered' ) ) {
if ( first ) {
first = false;
} else {
$welcomeNode.append( ' | ' );
}
$( '<a>' )
.text( warningTemplates[ i ].name )
.click( { tlName: warningTemplates[ i ].name }, jarorClick )
.appendTo( $welcomeNode );
}
}
$warningsNode.prepend( $welcomeNode );
}
}
/**
* Add the templates banner to the page.
*/
function addBanner() {
var portletId = 'p-cactions',
text = mw.msg( 'gadget-warning-label-monobook' ),
previousElementId = null;
switch ( mw.config.get( 'skin' ) ) {
case 'vector':
case 'vector-2022':
break; // doesn't work, see T313666
// Vector has separate menus for visible (p-views) and hidden (p-cactions) actions
portletId = 'p-views';
text = mw.msg( 'gadget-warning-label-vector' );
break;
case 'minerva':
portletId = 'p-tb';
break;
}
var menuItem = mw.util.addPortletLink( portletId, '#', text, 'ca-jaror',
mw.msg( 'gadget-warning-tooltip' ), 'o', previousElementId );
$( menuItem ).click( jaror_show );
}
/**
* Insert a template in the textbox.
* TODO: currently we force using the 2003/2010 wikitext editor by sending
* the user to `action=submit` instead of `action=edit`. Rather than making
* the user use the editor we like, we should support the editor the user
* likes: 2017WTE, VisualEditor, MobileFrontend editor overlay...
*/
function insertTemplate() {
var tlName = mw.util.getParamValue( 'jaror' ),
$tb = $( '#wpTextbox1' );
if ( !$tb.length || !$( '#wpSummary' ).length ) {
mw.log.warn( 'Gadget-warning.js: `#wpTextbox1` or `#wpSummary` not found' );
return;
}
/** @type {WarningTemplate} */
var tl;
for ( var i = 0; i < warningTemplates.length; ++i ) {
if ( warningTemplates[ i ].name === tlName ) {
tl = warningTemplates[ i ];
break;
}
}
if ( !tl ) {
notify( 'bad-template-name', null, [ tlName, 'https://hu.wiki.x.io/wiki/Kocsmafal_(műszaki)' ] );
return;
}
var m,
argv = [ tl.template ],
param_re = /[&?]params(?:%5B%5D|\[\])=([^&]*)/g,
href = location.href.replace( /#.*$/, '' );
do {
m = param_re.exec( href );
if ( m ) {
argv.push( decodeURIComponent( m[ 1 ].replace( /\+/g, '%20' ) ) );
}
} while ( m !== null );
var template = mw.format.apply( null, argv );
// Insert location (prepend, append, replace, comment out)
switch ( tl.type ) {
case 'pre':
$tb.val( template + '\n' + $tb.val() );
break;
case 'post':
$tb.val( $tb.val() + template + '\n' );
break;
case 'del':
$tb.val( template + '\n' );
break;
case 'comment':
$tb.val( template + '\n\n<!--\n' + $tb.val() + '-->\n' );
break;
}
$( '#wpSummary' ).val( tl.comment );
if ( tl.autosave ) {
// Create new page only in talk spaces
if ( mw.config.get( 'wgArticleId' ) > 0 || mw.config.get( 'wgNamespaceNumber' ) % 2 ) {
if ( !window.jarorNoAutosave ) {
window.onbeforeunload = null;
// FIXME: no save while testing
// $tb.closest( 'form' ).submit();
mw.log( 'Gadget-warning.js would have saved automatically' );
} else {
mw.log( 'Gadget-warning.js would have saved' );
}
} else {
notify( 'page-missing', 'warn' );
}
}
}
function init() {
var functionsToRun = [];
var namespaceNumber = mw.config.get( 'wgNamespaceNumber' ),
action = mw.config.get( 'wgAction' );
if (
mayEdit()
&& (
( [ 0, 1, 4, 6, 118 ].indexOf( namespaceNumber ) > -1 && action === 'view' )
|| namespaceNumber == 3
)
) {
functionsToRun.push( addBanner );
}
// Evaluate, insert template. This runs on new page load after clicking on a template
if ( mw.util.getParamValue( 'jaror' ) !== null && ( action === 'edit' || action === 'submit' ) ) {
functionsToRun.push( insertTemplate );
}
if ( functionsToRun.length ) {
loadData( functionsToRun );
}
}
init();
} );