PortableInfobox/resources/PortableInfoboxBuilder.js
CosmicAlpha 4fcb828705
Add more support to PortableInfoboxBuilder (#124)
Adds support for `<header>` and `<navigation>` tags
2024-05-11 17:49:11 -06:00

307 lines
8.6 KiB
JavaScript

(function (window, $) {
'use strict';
const CLASS_ITEMSELECTED = 'pi-ib-itemselected';
const MSG_PREFIX = 'infoboxbuilder-';
const XSL_STYLESHEET = new DOMParser().parseFromString(
'<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\
<xsl:output indent="yes" omit-xml-declaration="yes"/>\
<xsl:template match="node()|@*">\
<xsl:copy>\
<xsl:apply-templates select="node()|@*"/>\
</xsl:copy>\
</xsl:template>\
</xsl:stylesheet>',
'text/xml'
);
window.mediaWiki.PortableInfoboxBuilder = window.mediaWiki.PortableInfoboxBuilder || {};
var Nodes = window.mediaWiki.PortableInfoboxBuilder.Nodes;
class PortableInfoboxBuilder {
constructor( config ) {
this.config = config;
this.api = new mw.Api();
this.xmlSerializer = new XMLSerializer();
if ( window.XSLTProcessor ) {
this.xsltProcessor = new XSLTProcessor();
this.xsltProcessor.importStylesheet( XSL_STYLESHEET );
}
this.infobox = new Nodes.NodeInfobox( this );
this.infobox.children = [
Nodes.Node.factory( this.infobox.markupDoc, 'title' ),
Nodes.Node.factory( this.infobox.markupDoc, 'media' ),
Nodes.Node.factory( this.infobox.markupDoc, 'header' ),
Nodes.Node.factory( this.infobox.markupDoc, 'data' ),
Nodes.Node.factory( this.infobox.markupDoc, 'data' )
];
mw.hook( 'portableinfoboxbuilder.nodeselect' ).add( ( node ) => this.select( node ) );
this.$element = this.buildUI();
}
msg( key, params = [], optional = false ) {
if ( !( params instanceof Array ) ) {
params = [ params ];
}
params.unshift( MSG_PREFIX + key );
let msg = mw.message.apply( mw, params );
if ( optional && !msg.exists() ) {
return undefined;
}
return msg.text()
}
buildUI() {
var menuLayout = new OO.ui.MenuLayout( { menuPosition: 'after' } ),
self = this;
menuLayout.$menu.append(
this.buildNewNodesMenu(),
this.buildNodeMenu(),
this.buildActionsMenu()
);
menuLayout.$content.append(
new OO.ui.PanelLayout( { padded: true } ).$element.append(
this.infobox.html()
).click( function( e ) {
if( e.target != this ) { return; }
self.deselect()
} )
);
this.$element = new OO.ui.PanelLayout( {
framed: true,
expanded: false,
id: 'mw-infoboxbuilder'
} ).$element.append( menuLayout.$element );
return this.$element;
}
buildActionsMenu() {
return new OO.ui.PanelLayout( { padded: true, expanded: false } ).$element.append(
new OO.ui.ButtonWidget( {
label: this.msg( 'action-clear' ),
icon: 'trash',
flags: [ 'primary', 'destructive' ]
} ).on( 'click', () => { this.clearInfobox() } ).$element,
new OO.ui.ButtonWidget( {
label: this.msg( 'action-publish' ),
icon: 'check',
flags: [ 'primary', 'progressive' ]
} ).on( 'click', () => { this.publishInfobox() } ).$element
)
}
buildNewNodesMenu() {
let menu = new OO.ui.PanelLayout( { padded: true, expanded: false } ),
$menu = menu.$element.append(
new OO.ui.LabelWidget( { label: this.msg( 'action-addnode' ) } ).$element
);
Nodes.NODE_LIST.forEach( ( e ) => {
$menu.append(
new OO.ui.ButtonWidget( { label: this.msg( 'node-' + e ) } )
.on( 'click', () => this.addInfoboxElem( e ) )
.$element
)
} );
return $menu;
}
buildNodeMenu() {
this.nodeInputSource = new OO.ui.TextInputWidget( {
placeholder: this.msg( 'nodeparam-source' ),
disabled: true
} );
this.nodeInputLabel = new OO.ui.TextInputWidget( {
placeholder: this.msg( 'nodeparam-label' ),
disabled: true
} );
this.nodeInputDefault = new OO.ui.TextInputWidget( {
placeholder: this.msg( 'nodeparam-default' ),
disabled: true
} );
this.nodeInputValue = new OO.ui.TextInputWidget( {
placeholder: this.msg( 'nodeparam-value' ),
disabled: true
} );
this.deleteNodeButton = new OO.ui.ButtonWidget( {
label: this.msg( 'action-deletenode' ),
icon: 'trash',
disabled: true
} ).on( 'click', () => this.deleteSelectedNode() );
return new OO.ui.PanelLayout( { padded: true, expanded: false } ).$element.append(
new OO.ui.FieldLayout( this.nodeInputSource, {
label: this.msg( 'nodeparam-source' ),
align: 'top',
help: this.msg( 'nodeparamhelp-source', [], true ),
disabled: true
} ).$element,
new OO.ui.FieldLayout( this.nodeInputLabel, {
label: this.msg( 'nodeparam-label' ),
align: 'top',
help: this.msg( 'nodeparamhelp-label', [], true ),
disabled: true
} ).$element,
new OO.ui.FieldLayout( this.nodeInputDefault, {
label: this.msg( 'nodeparam-default' ),
align: 'top',
help: this.msg( 'nodeparamhelp-default', [], true ),
disabled: true
} ).$element,
new OO.ui.FieldLayout( this.nodeInputValue, {
label: this.msg( 'nodeparam-value' ),
align: 'top',
help: this.msg( 'nodeparamhelp-value', [], true ),
disabled: true
} ).$element,
this.deleteNodeButton.$element
)
}
toggleNodeMenu( supports = false ) {
if ( supports ) {
this.deleteNodeButton.setDisabled( false );
} else {
this.deleteNodeButton.setDisabled( true );
supports = {};
}
this.toggleNodeMenuWidget( this.nodeInputSource, supports.source, 'source' );
this.toggleNodeMenuWidget( this.nodeInputLabel, supports.label, 'label' );
this.toggleNodeMenuWidget( this.nodeInputDefault, supports.default, 'default' );
this.toggleNodeMenuWidget( this.nodeInputValue, supports.value, 'value' );
}
toggleNodeMenuWidget( widget, enabled, param ) {
widget.off( 'change' ).setDisabled( !enabled );
if ( enabled ) {
widget
.setValue( this.selectedNode.params[param] )
.on( 'change', ( value ) => this.selectedNode.changeParam( param, value ) );
}
}
addInfoboxElem( type ) {
this.deselect();
this.infobox.createChildren( type );
}
select( node ) {
if( this.selectedNode !== undefined ) {
this.selectedNode.deselect();
}
this.selectedNode = node;
this.toggleNodeMenu( node.supports() );
this.infobox.element.classList.add( CLASS_ITEMSELECTED );
}
deselect() {
if( this.selectedNode === undefined ) {
return;
}
this.selectedNode.deselect();
this.selectedNode = undefined;
this.toggleNodeMenu();
this.infobox.element.classList.remove( CLASS_ITEMSELECTED );
}
deleteSelectedNode() {
this.infobox.removeChildren( this.selectedNode );
this.deselect();
}
clearInfobox() {
OO.ui.confirm( mw.message( 'confirmable-confirm', mw.config.get("wgUserName") ).text() )
.done( ( confirmed ) => {
if ( confirmed ) {
this.deselect();
this.infobox.clearChildren();
}
}
);
}
getInfoboxMarkup() {
let markup = this.infobox.markup();
if ( this.xsltProcessor ) {
let transformed = this.xsltProcessor.transformToDocument( markup );
if ( transformed ) {
let result = this.xmlSerializer.serializeToString( transformed )
.replace( /(?<=^ *) /mg, '\t' );
if ( result.indexOf( '<transformiix:result' ) > -1 ) {
return '<infobox>' + result.substring( result.indexOf( '>' ) + 1, result.lastIndexOf( '<' ) ) + '</infobox>';
}
return result;
}
}
return this.xmlSerializer.serializeToString( markup );
}
publishInfobox() {
OO.ui.prompt( this.msg( 'templatename' ), {
size: 'large',
textInput: {
placeholder: this.msg( 'templatename' ),
value: this.config.title,
required: true
}
} ).done( ( title ) => {
if ( title === null || title.trim() === '' ) {
return;
}
let template = mw.config.get( 'wgFormattedNamespaces' )[10],
namespace = ( title.substring( 0, template.length ) !== template ? ( template + ':' ) : '' );
this.api.postWithToken( 'csrf', {
action: 'edit',
title: namespace + title,
text: this.getInfoboxMarkup(),
summary: this.msg( 'editsummary' ),
notminor: true,
recreate: true,
createonly: true
} ).done( () => {
window.location.assign( mw.config.get( 'wgArticlePath' ).replace( '$1', namespace + title ) )
} ).fail( ( code, err ) => {
OO.ui.alert(
err.error && err.error.info ? this.msg( 'editerror', err.error.info ) :
this.msg( 'editerrorunknown' ),
{ size: 'large' }
);
} );
} );
}
}
window.mediaWiki.PortableInfoboxBuilder.Builder = PortableInfoboxBuilder;
mw.loader.using( 'oojs-ui-core' ).done( function() {
let content = document.getElementById( 'mw-infoboxbuilder' );
if ( content ) {
let builder = new PortableInfoboxBuilder( {
title: content.dataset.title
} );
content.replaceWith( builder.$element[0] );
}
} );
})(window, jQuery);