Add infobox builder (#37)

This commit is contained in:
Universal Omega 2021-12-17 11:00:14 -07:00 committed by GitHub
parent 4a0bac951a
commit 25c6c90bae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 998 additions and 7 deletions

View file

@ -7,7 +7,7 @@
],
"url": "https://github.com/Universal-Omega/PortableInfobox",
"descriptionmsg": "portable-infobox-desc",
"version": "0.5",
"version": "0.6",
"type": "parserhook",
"license-name": "GPL-3.0-or-later",
"requires": {
@ -53,6 +53,42 @@
"desktop",
"mobile"
]
},
"ext.PortableInfoboxBuilder": {
"scripts": [
"resources/PortableInfoboxBuilderNodes.js",
"resources/PortableInfoboxBuilder.js"
],
"styles": "resources/PortableInfoboxBuilder.less",
"dependencies": [
"mediawiki.jqueryMsg",
"jquery.ui",
"oojs-ui-core",
"oojs-ui-widgets",
"oojs-ui-windows"
],
"messages": [
"confirmable-confirm",
"infoboxbuilder-action-addnode",
"infoboxbuilder-action-clear",
"infoboxbuilder-action-deletenode",
"infoboxbuilder-action-publish",
"infoboxbuilder-editerror",
"infoboxbuilder-editerrorunknown",
"infoboxbuilder-editsummary",
"infoboxbuilder-node-title",
"infoboxbuilder-node-title-value",
"infoboxbuilder-node-title-value-pagename",
"infoboxbuilder-node-data",
"infoboxbuilder-node-data-value-source",
"infoboxbuilder-node-media",
"infoboxbuilder-nodeerror-invalidsource",
"infoboxbuilder-nodeerror-nosourceordefault",
"infoboxbuilder-nodeparam-default",
"infoboxbuilder-nodeparam-label",
"infoboxbuilder-nodeparam-source",
"infoboxbuilder-templatename"
]
}
},
"ResourceFileModulePaths": {
@ -73,7 +109,8 @@
"ApiPortableInfobox": "includes/controllers/ApiPortableInfobox.php",
"ApiQueryPortableInfobox": "includes/controllers/ApiQueryPortableInfobox.php",
"ApiQueryAllInfoboxes": "includes/controllers/ApiQueryAllInfoboxes.php",
"AllInfoboxesQueryPage": "includes/querypage/AllInfoboxesQueryPage.php"
"AllInfoboxesQueryPage": "includes/querypage/AllInfoboxesQueryPage.php",
"SpecialPortableInfoboxBuilder": "includes/specials/SpecialPortableInfoboxBuilder.php"
},
"Hooks": {
"ParserFirstCallInit": "PortableInfoboxParserTagController::parserTagInit",
@ -85,7 +122,8 @@
"ResourceLoaderRegisterModules": "PortableInfoboxHooks::onResourceLoaderRegisterModules"
},
"SpecialPages": {
"AllInfoboxes": "AllInfoboxesQueryPage"
"AllInfoboxes": "AllInfoboxesQueryPage",
"InfoboxBuilder": "SpecialPortableInfoboxBuilder"
},
"APIModules": {
"infobox": "ApiPortableInfobox"

View file

@ -20,5 +20,25 @@
"apihelp-infobox-param-args": "Variable list to use during parse (json format)",
"apihelp-query+allinfoboxes-summary": "List all infoboxes",
"apihelp-query+infobox-summary": "Get infobox metadata",
"apiwarn-infobox-invalidargs": "Args param format is incorrect"
"apiwarn-infobox-invalidargs": "Args param format is incorrect",
"infoboxbuilder": "Infobox builder",
"infoboxbuilder-action-addnode": "Add infobox elements",
"infoboxbuilder-action-clear": "Clear infobox",
"infoboxbuilder-action-deletenode": "Delete element",
"infoboxbuilder-action-publish": "Publish infobox",
"infoboxbuilder-editerror": "An error occured during infobox publishing: $1",
"infoboxbuilder-editerrorunknown": "An unknown error occured during infobox publishing.",
"infoboxbuilder-editsummary": "Infobox created with infobox builder.",
"infoboxbuilder-node-title": "Title",
"infoboxbuilder-node-title-value": "Infobox title",
"infoboxbuilder-node-title-value-pagename": "Page name",
"infoboxbuilder-node-data": "Data",
"infoboxbuilder-node-data-value-source": "Value of $1",
"infoboxbuilder-node-media": "Image",
"infoboxbuilder-nodeerror-invalidsource": "Source parameter is invalid.",
"infoboxbuilder-nodeerror-nosourceordefault": "Element without source parameter or default value won't be displayed.",
"infoboxbuilder-nodeparam-default": "Default value",
"infoboxbuilder-nodeparam-label": "Label",
"infoboxbuilder-nodeparam-source": "Source parameter",
"infoboxbuilder-templatename": "Template name"
}

View file

@ -20,5 +20,25 @@
"apihelp-infobox-param-args": "Lista parametrów do użycia przy parsowaniu (w formacie JSON)",
"apihelp-query+allinfoboxes-summary": "Wymień wszystkie infoboksy",
"apihelp-query+infobox-summary": "Pobierz metadane infoboksu",
"apiwarn-infobox-invalidargs": "Nieprawidłowy format parametru args"
"apiwarn-infobox-invalidargs": "Nieprawidłowy format parametru args",
"infoboxbuilder": "Kreator infoboksów",
"infoboxbuilder-action-addnode": "Dodaj element infoboksu",
"infoboxbuilder-action-clear": "Wyczyść infoboks",
"infoboxbuilder-action-deletenode": "Usuń element",
"infoboxbuilder-action-publish": "Opublikuj infoboks",
"infoboxbuilder-editerror": "Wystąpił błąd podczas publikowania infoboksu: $1",
"infoboxbuilder-editerrorunknown": "Wystąpił nieznany błąd podczas publikowania infoboksu.",
"infoboxbuilder-editsummary": "Infoboks utworzony przy użyciu kreatora infoboksów.",
"infoboxbuilder-node-title": "Tytuł",
"infoboxbuilder-node-title-value": "Tytuł infoboksu",
"infoboxbuilder-node-title-value-pagename": "Tytuł strony",
"infoboxbuilder-node-data": "Wiersz",
"infoboxbuilder-node-data-value-source": "Wartość $1",
"infoboxbuilder-node-media": "Obraz",
"infoboxbuilder-nodeerror-invalidsource": "Parametr zawiera niedozwolone znaki.",
"infoboxbuilder-nodeerror-nosourceordefault": "Element bez parametru lub domyślnej wartości nie będzie się wyświetlał.",
"infoboxbuilder-nodeparam-default": "Domyślna wartosć",
"infoboxbuilder-nodeparam-label": "Etykieta",
"infoboxbuilder-nodeparam-source": "Parametr",
"infoboxbuilder-templatename": "Nazwa szablonu"
}

View file

@ -4,10 +4,12 @@ $specialPageAliases = [];
/** English **/
$specialPageAliases['en'] = [
'AllInfoboxes' => [ 'AllInfoboxes' ]
'AllInfoboxes' => [ 'AllInfoboxes' ],
'InfoboxBuilder' => [ 'InfoboxBuilder' ]
];
/** Polish */
$specialPageAliases['pl'] = [
'AllInfoboxes' => [ 'Wszystkie infoboksy' ]
'AllInfoboxes' => [ 'Wszystkie infoboksy' ],
'InfoboxBuilder' => [ 'Kreator infoboksów' ]
];

View file

@ -0,0 +1,22 @@
<?php
class SpecialPortableInfoboxBuilder extends SpecialPage {
public function __construct() {
parent::__construct( 'InfoboxBuilder' );
$this->mRestriction = $this->getConfig()->get( 'NamespaceProtection' )[NS_TEMPLATE] ?? '';
}
public function execute( $par ) {
$out = $this->getOutput();
$this->setHeaders();
$out->enableOOUI();
$out->addModules( [ 'ext.PortableInfobox.styles', 'ext.PortableInfoboxBuilder' ] );
$out->addHTML(
'<div id="mw-infoboxbuilder" data-title="' . str_replace( '"', '&quot;', $par ) . '">' .
new OOUI\ProgressBarWidget( [ 'progress' => false ] ) .
'</div>'
);
}
}

View file

@ -0,0 +1,284 @@
(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, '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.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,
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' );
}
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.user.getName() ).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 ) {
return this.xmlSerializer.serializeToString( transformed )
.replace( /(?<=^ *) /mg, '\t' );
}
}
console.log( this.xmlSerializer.serializeToString( markup ) );
return this.xmlSerializer.serializeToString( markup );
}
publishInfobox() {
console.log( this.getInfoboxMarkup() );
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;
}
this.api.postWithToken( 'csrf', {
action: 'edit',
title: title,
text: this.getInfoboxMarkup(),
summary: this.msg( 'editsummary' ),
notminor: true
} ).done( () => {
window.location.assign( mw.config.get( 'wgArticlePath' ).replace( '$1', 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);

View file

@ -0,0 +1,170 @@
#mw-infoboxbuilder {
min-height: 700px;
> * {
flex: 1;
}
.oo-ui-menuLayout-content {
/* dirty fix for node warnings/errors */
left: -200px !important;
overflow-y: auto;
.oo-ui-panelLayout {
left: 200px;
display: flex;
}
}
.oo-ui-menuLayout-menu {
display: flex;
flex-direction: column;
border-left: 1px solid #c8ccd1;
> :nth-child(2) {
flex: 1;
display: flex;
flex-direction: column;
> .oo-ui-buttonElement {
margin-top: auto;
padding-top: 10px;
}
}
> * {
&:not(:first-child) {
border-top: 1px solid #c8ccd1;
}
&:not(.oo-ui-popupButtonWidget) > * {
width: 100%;
}
.oo-ui-buttonElement-button {
width: 100%;
}
> .oo-ui-buttonElement:not(:last-child) .oo-ui-buttonElement-button {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
> .oo-ui-buttonElement + .oo-ui-buttonElement .oo-ui-buttonElement-button {
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.oo-ui-fieldLayout-disabled,
.oo-ui-widget-disabled {
display: none;
}
}
}
.portable-infobox {
float: none;
margin: auto;
user-select: none;
.pi-item {
background: inherit;
outline: 2px solid transparent;
outline-offset: -1px;
position: relative;
transition: outline-color .2s;
z-index: 0;
&:hover {
outline-color: #ccc;
z-index: 1;
}
&.pi-ib-selected {
box-shadow: 0 0 20px #aaa;
outline: 2px solid #36c;
z-index: 3;
}
&.pi-ib-warning,
&.pi-ib-error {
overflow: visible;
z-index: 2;
}
&.pi-ib-error {
outline-color: #f00;
}
.pi-ib-error-message {
display: flex;
justify-content: flex-end;
align-items: flex-start;
position: absolute;
top: ~"calc( 50% - 14px )";
right: ~"calc( 100% + 18px )";
min-height: 20px;
filter: drop-shadow( 0 2px 2px rgba( 0, 0, 0, 0.25 ) );
text-align: left;
&::after {
content: '';
display: block;
position: absolute;
left: 100%;
top: 0;
border-left: 14px solid #f00;
border-top: 14px solid transparent;
border-bottom: 14px solid transparent;
}
> .oo-ui-iconElement {
background-color: #f00;
background-size: 20px;
padding: 4px 6px;
height: 20px;
width: 20px;
transition: padding-left .1s;
}
> .oo-ui-labelElement {
flex: 1;
background: #f00;
color: #fff;
font-size: 0;
line-height: 22px;
width: 0;
min-height: 20px;
max-height: 28px;
transition: font-size, width, max-height .1s;
}
}
&.pi-ib-warning .pi-ib-error-message {
> * {
background-color: #ff0;
color: #000;
}
&::after {
border-left-color: #ff0;
}
}
}
&:not(.pi-ib-itemselected) .pi-item:hover .pi-ib-error-message,
& .pi-item.pi-ib-selected .pi-ib-error-message {
> .oo-ui-iconElement {
padding-left: 15px;
}
> .oo-ui-labelElement {
padding: 4px 6px;
max-height: 175px;
font-size: 14px;
width: 175px;
}
}
}
}

View file

@ -0,0 +1,435 @@
(function (window, $) {
'use strict';
const MSG_PREFIX = 'infoboxbuilder-';
const NODE_ATTRIBUTES = [
'accent-color-default',
'accent-color-source',
'accent-color-text-default',
'accent-color-text-source',
'audio',
'image',
'layout',
'source',
'span',
'theme',
'theme-source',
'video'
];
const NODE_CONTENTNODES = [
'label',
'format',
'default'
];
const NODE_IDPREFIX = 'pi-ib-node-';
const NODE_CLASSSELECTED = 'pi-ib-selected';
var nodeId = 0,
dataId = 0;
class NodeValidationError {
constructor( message ) {
this.message = message;
}
render() {
let msg = document.createElement( 'div' );
msg.className = 'pi-ib-error-message';
msg.appendChild( new OO.ui.LabelWidget( { label: this.message } ).$element[0] );
msg.appendChild( this.getIcon() );
return msg;
}
getIcon() {
let icon = new OO.ui.IconWidget( { icon: 'alert', flags: 'primary' } );
// trigger inverted icon variant
icon.isFramed = () => true;
return icon.$element[0];
}
getClass() {
return 'pi-ib-error';
}
}
class NodeValidationWarning extends NodeValidationError {
getIcon() {
return new OO.ui.IconWidget( { icon: 'alert' } ).$element[0];
}
getClass() {
return 'pi-ib-warning';
}
}
class PINode {
constructor( markupDoc, params ) {
if ( this.constructor === PINode ) {
throw new TypeError( 'Use Node.factory() instead, "Node" is an abstract class.' );
}
this.elementClasses = 'pi-item ';
this.elementSelectable = true;
this.elementTag = 'div';
this.markupContentTag = false;
this.markupDoc = markupDoc;
this.markupTag = undefined;
this.id = nodeId++;
this.params = params || this.getDefaultParams();
this.selected = false;
}
static factory( markupDoc, type, params ) {
switch ( type ) {
case 'data':
return new NodeData( markupDoc, params );
case 'title':
return new NodeTitle( markupDoc, params );
case 'media':
return new NodeMedia( markupDoc, params );
case 'infobox':
throw new TypeError( 'Use new NodeInfobox() instead.' );
default:
throw new TypeError( 'Unknown node type "' + type + '"' );
}
}
getDefaultParams() {
return {};
}
html() {
if( this.element ) {
this.element.innerHTML = '';
} else {
let element = document.createElement( this.elementTag );
element.id = NODE_IDPREFIX + this.id;
if ( this.elementSelectable ) {
element.onmousedown = () => this.select();
}
this.element = element;
}
this.element.className = this.elementClasses;
if ( this.selected && this.elementSelectable ) {
this.element.classList.add( NODE_CLASSSELECTED );
}
try {
this.validate();
} catch ( error ) {
if ( error instanceof NodeValidationError ) {
this.element.classList.add( error.getClass() );
this.element.appendChild( error.render() );
} else {
throw error;
}
}
return this.element;
}
markup() {
let node = this.markupDoc.createElement( this.markupTag ),
supports = this.supports();
NODE_ATTRIBUTES.forEach( ( a ) => {
if ( supports[a] && this.params[a] ) {
node.setAttribute( a, this.params[a] );
}
} );
if ( this.markupContentTag && this.params.value ) {
node.appendChild( this.markupDoc.createTextNode( this.params.value ) );
} else {
NODE_CONTENTNODES.forEach( ( n ) => {
if ( supports[n] && this.params[n] ) {
let subnode = this.markupDoc.createElement( n );
subnode.appendChild( this.markupDoc.createTextNode( this.params[n] ) );
node.appendChild( subnode );
}
} );
}
return node;
}
select() {
if ( this.elementSelectable ) {
mw.hook( 'portableinfoboxbuilder.nodeselect' ).fire( this );
this.selected = true;
this.element.classList.add( NODE_CLASSSELECTED );
}
}
deselect() {
if ( this.elementSelectable ) {
this.selected = false;
this.element.classList.remove( NODE_CLASSSELECTED );
}
}
remove() {
$( this.element ).remove();
}
supports() {
return {};
}
validate() {
if ( this.params.source.match( /["|={}]/ ) ) {
throw new NodeValidationError( this.msg( 'nodeerror-invalidsource' ) );
}
if ( !this.params.source && !this.params.default ) {
throw new NodeValidationWarning( this.msg( 'nodeerror-nosourceordefault' ) );
}
return true;
}
changeParam( param, value ) {
if ( this.supports()[param] ) {
this.params[param] = value;
this.html();
}
}
msg( key, params ) {
if ( !( params instanceof Array ) ) {
params = [ params ];
}
params.unshift( MSG_PREFIX + key );
return mw.message.apply( mw, params ).text();
}
}
class NodeData extends PINode {
constructor( markupDoc, params ) {
super( markupDoc, params );
this.elementClasses += 'pi-data pi-item-spacing pi-border-color';
this.markupTag = 'data';
}
getDefaultParams() {
return {
label: this.msg( 'nodeparam-label' ),
source: this.msg( 'node-data' ).toLocaleLowerCase() + ( ++dataId )
};
}
html() {
super.html();
if ( this.params.label ) {
let label = document.createElement( 'h3' );
label.className = 'pi-data-label pi-secondary-font';
label.textContent = this.params.label;
this.element.appendChild( label );
}
let value = document.createElement( 'div' );
value.className = 'pi-data-value pi-font';
// '{{{$1}}}' in msg throws an error with jqueryMsg enabled
value.textContent = this.params.source ?
this.msg( 'node-data-value-source', '{{{' + this.params.source + '}}}' ) :
this.params.default ? this.params.default : '';
this.element.appendChild( value );
return this.element;
}
supports() {
return {
default: true,
format: true,
label: true,
layout: [ 'default' ],
source: true,
span: true
};
}
}
class NodeTitle extends PINode {
constructor( markupDoc, params ) {
super( markupDoc, params );
this.elementTag = 'h2';
this.elementClasses += 'pi-item-spacing pi-title';
this.markupTag = 'title';
}
getDefaultParams() {
return {
source: this.msg( 'node-title' ).toLowerCase(),
default: '{{PAGENAME}}'
};
}
html() {
super.html();
this.element.textContent = this.params.default === '{{PAGENAME}}' ?
this.msg( 'node-title-value-pagename' ) : this.msg( 'node-title-value' );
return this.element;
}
supports() {
return {
source: true,
format: true,
default: true
};
}
}
class NodeMedia extends PINode {
constructor( markupDoc, params ) {
super( markupDoc, params );
this.elementTag = 'figure';
this.elementClasses += 'pi-media pi-image';
this.markupTag = 'image';
}
getDefaultParams() {
return {
source: this.msg( 'node-media' ).toLowerCase()
};
}
html() {
super.html();
let a = document.createElement( 'a' ),
img = document.createElement( 'img' );
a.className = 'image image-thumbnail';
a.appendChild( img );
img.className = 'pi-image-thumbnail';
img.src = mw.config.get( 'wgScriptPath' ) + '/resources/assets/mediawiki.png';
this.element.appendChild( a );
return this.element;
}
supports() {
return {
source: true,
audio: true,
image: true,
video: true,
alt: true,
caption: true,
default: true
};
}
}
class NodeInfobox extends PINode {
constructor( params ) {
super( document.implementation.createDocument( '', '' ), params );
this.children = [];
this.elementClasses = 'portable-infobox pi-background';
this.elementSelectable = false;
this.elementTag = 'aside';
this.markupTag = 'infobox';
}
addChildren( children ) {
if ( children instanceof PINode ) {
this.children.push( children );
this.element.append( children.html() );
}
}
createChildren( type, params ) {
this.addChildren( PINode.factory( this.markupDoc, type, params ) );
}
clearChildren() {
this.children = [];
$( this.element ).empty();
dataId = 0;
}
removeChildren( children ) {
let i = this.children.indexOf( children );
if ( i >= 0 ) {
this.children.splice( i, 1 );
$( children.element ).remove();
}
}
reorderChildren( order, prefixed = false ) {
let newChildren = [];
this.children.forEach( c => {
let i = order.indexOf( ( prefixed ? NODE_IDPREFIX : 0 ) + c.id );
if ( i >= 0 ) {
newChildren[i] = c;
}
} );
this.children = newChildren;
}
html() {
super.html();
this.children.forEach( c => { this.element.appendChild( c.html() ) } );
$( this.element ).sortable( {
axis: 'y',
containment: 'parent',
cursor: 'move',
scroll: false,
tolerance: 'pointer',
deactivate: ( e, ui ) => {
ui.item.attr( 'style', '' );
this.reorderChildren( $( this.element ).sortable( 'toArray' ), true );
}
} );
return this.element;
}
markup() {
let node = super.markup();
this.children.forEach( c => { node.appendChild( c.markup() ) } );
return node;
}
supports() {
return {
'accent-color-default': true,
'accent-color-source': true,
'accent-color-text-default': true,
'accent-color-text-source': true,
'layout': [ 'default', 'stacked' ],
'theme': true,
'theme-source': true
};
}
validate() {}
}
window.mediaWiki.PortableInfoboxBuilder = window.mediaWiki.PortableInfoboxBuilder || {};
window.mediaWiki.PortableInfoboxBuilder.Nodes = {
Node: PINode,
NodeData: NodeData,
NodeMedia: NodeMedia,
NodeTitle: NodeTitle,
NodeInfobox: NodeInfobox,
NODE_LIST: [ 'data', 'title', 'media' ]
};
})(window, jQuery);