2021-05-17 15:43:49 +00:00
|
|
|
/*!
|
|
|
|
* VisualEditor user interface MWTransclusionOutlineContainerWidget class.
|
|
|
|
*
|
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Container for transclusion, may contain a single or multiple templates.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @extends OO.ui.Widget
|
|
|
|
*
|
|
|
|
* @constructor
|
2021-07-09 15:33:16 +00:00
|
|
|
* @property {Object.<string,ve.ui.MWTransclusionOutlinePartWidget>} partWidgets Map of top-level
|
|
|
|
* items currently visible in this container, indexed by part id
|
2021-05-17 15:43:49 +00:00
|
|
|
*/
|
2021-08-19 11:02:11 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget = function VeUiMWTransclusionOutlineContainerWidget() {
|
2021-05-17 15:43:49 +00:00
|
|
|
// Parent constructor
|
2021-09-10 19:18:59 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.super.call( this, {
|
|
|
|
classes: [ 've-ui-mwTransclusionOutlineContainerWidget' ]
|
|
|
|
} );
|
2021-05-17 15:43:49 +00:00
|
|
|
|
|
|
|
// Initialization
|
2021-07-09 15:33:16 +00:00
|
|
|
this.partWidgets = {};
|
2021-05-17 15:43:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
|
|
|
OO.inheritClass( ve.ui.MWTransclusionOutlineContainerWidget, OO.ui.Widget );
|
2021-06-17 11:20:50 +00:00
|
|
|
|
|
|
|
/* Events */
|
|
|
|
|
2021-08-05 12:36:10 +00:00
|
|
|
/**
|
2021-08-31 08:05:25 +00:00
|
|
|
* @event filterPagesByName
|
|
|
|
* @param {Object.<string,boolean>} visibility Keyed by unique id of the {@see OO.ui.BookletLayout}
|
|
|
|
* page, e.g. something like "part_1/param1".
|
2021-08-05 12:36:10 +00:00
|
|
|
*/
|
|
|
|
|
2021-08-19 11:02:11 +00:00
|
|
|
/**
|
2021-08-31 08:05:25 +00:00
|
|
|
* @event focusPageByName
|
|
|
|
* @param {string} pageName Unique id of the {@see OO.ui.BookletLayout} page, e.g. something like
|
|
|
|
* "part_1" or "part_1/param1".
|
2021-08-19 11:02:11 +00:00
|
|
|
*/
|
|
|
|
|
2021-09-02 16:09:30 +00:00
|
|
|
/**
|
|
|
|
* @event updateOutlineControlButtons
|
|
|
|
* @param {string} pageName Unique id of the {@see OO.ui.BookletLayout} page, e.g. something like
|
|
|
|
* "part_1" or "part_1/param1".
|
|
|
|
*/
|
|
|
|
|
2021-06-17 11:20:50 +00:00
|
|
|
/**
|
2021-07-07 08:04:40 +00:00
|
|
|
* @param {ve.dm.MWTransclusionPartModel|null} removed Removed part
|
|
|
|
* @param {ve.dm.MWTransclusionPartModel|null} added Added part
|
2021-07-15 15:07:21 +00:00
|
|
|
* @param {number} [newPosition]
|
2021-06-17 11:20:50 +00:00
|
|
|
*/
|
2021-07-15 15:07:21 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.onReplacePart = function ( removed, added, newPosition ) {
|
2021-07-09 15:33:16 +00:00
|
|
|
if ( removed ) {
|
|
|
|
this.removePartWidget( removed );
|
2021-06-17 11:20:50 +00:00
|
|
|
}
|
|
|
|
// TODO: reselect if active part was in a removed template
|
|
|
|
|
2021-07-09 15:33:16 +00:00
|
|
|
if ( added ) {
|
2021-07-15 15:07:21 +00:00
|
|
|
this.addPartWidget( added, newPosition );
|
2021-06-17 11:20:50 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-13 07:27:22 +00:00
|
|
|
/**
|
2021-07-15 15:07:21 +00:00
|
|
|
* @param {ve.dm.MWTransclusionModel} transclusionModel
|
2021-07-13 07:27:22 +00:00
|
|
|
*/
|
2021-07-15 15:07:21 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.onTransclusionModelChange = function ( transclusionModel ) {
|
|
|
|
var newOrder = transclusionModel.getParts();
|
2021-07-13 07:27:22 +00:00
|
|
|
|
|
|
|
for ( var i = 0; i < newOrder.length; i++ ) {
|
|
|
|
var expectedWidget = this.partWidgets[ newOrder[ i ].getId() ],
|
|
|
|
$expectedElement = expectedWidget && expectedWidget.$element,
|
|
|
|
$currentElement = this.$element.children().eq( i );
|
|
|
|
|
|
|
|
if ( !$currentElement.is( $expectedElement ) ) {
|
|
|
|
// Move each widget to the correct position if it wasn't there before
|
|
|
|
$currentElement.before( $expectedElement );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-08-27 16:22:37 +00:00
|
|
|
/**
|
2021-08-27 17:07:17 +00:00
|
|
|
* @private
|
2021-09-16 08:42:13 +00:00
|
|
|
* @param {string} pageName
|
2021-08-31 08:05:25 +00:00
|
|
|
* @fires focusPageByName
|
2021-08-27 16:22:37 +00:00
|
|
|
*/
|
2021-09-16 08:42:13 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.onTransclusionPartSelected = function ( pageName ) {
|
|
|
|
this.emit( 'focusPageByName', pageName );
|
2021-08-27 16:22:37 +00:00
|
|
|
};
|
|
|
|
|
2021-06-17 11:20:50 +00:00
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
2021-07-09 15:33:16 +00:00
|
|
|
* @param {ve.dm.MWTransclusionPartModel} part
|
2021-06-17 11:20:50 +00:00
|
|
|
*/
|
2021-07-09 15:33:16 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.removePartWidget = function ( part ) {
|
2021-08-27 16:22:37 +00:00
|
|
|
var id = part.getId();
|
|
|
|
if ( id in this.partWidgets ) {
|
|
|
|
this.partWidgets[ id ]
|
|
|
|
.disconnect( this )
|
|
|
|
.$element.remove();
|
|
|
|
delete this.partWidgets[ id ];
|
2021-07-09 15:33:16 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @param {ve.dm.MWTransclusionPartModel} part
|
2021-07-15 15:07:21 +00:00
|
|
|
* @param {number} [newPosition]
|
2021-08-31 08:05:25 +00:00
|
|
|
* @fires filterPagesByName
|
2021-07-09 15:33:16 +00:00
|
|
|
*/
|
2021-07-15 15:07:21 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.addPartWidget = function ( part, newPosition ) {
|
2021-07-09 15:33:16 +00:00
|
|
|
var widget;
|
|
|
|
|
|
|
|
if ( part instanceof ve.dm.MWTemplateModel ) {
|
|
|
|
widget = new ve.ui.MWTransclusionOutlineTemplateWidget( part );
|
2021-08-05 12:36:10 +00:00
|
|
|
// This forwards events from the nested ve.ui.MWTransclusionOutlineTemplateWidget upwards.
|
2021-08-11 06:06:22 +00:00
|
|
|
// The array syntax is a way to call `this.emit( 'filterParameters' )`.
|
2021-08-31 10:35:26 +00:00
|
|
|
widget.connect( this, {
|
2021-08-31 08:05:25 +00:00
|
|
|
// We can forward these events as is. The parameter's unique ids are reused as page
|
|
|
|
// names in {@see ve.ui.MWTemplateDialog.onAddParameter}.
|
|
|
|
focusTemplateParameterById: [ 'emit', 'focusPageByName' ],
|
|
|
|
filterParametersById: [ 'emit', 'filterPagesByName' ]
|
2021-08-31 10:35:26 +00:00
|
|
|
} );
|
2021-07-09 15:33:16 +00:00
|
|
|
} else if ( part instanceof ve.dm.MWTemplatePlaceholderModel ) {
|
|
|
|
widget = new ve.ui.MWTransclusionOutlinePlaceholderWidget( part );
|
|
|
|
} else if ( part instanceof ve.dm.MWTransclusionContentModel ) {
|
|
|
|
widget = new ve.ui.MWTransclusionOutlineWikitextWidget( part );
|
|
|
|
}
|
|
|
|
|
2021-08-31 10:35:26 +00:00
|
|
|
widget.connect( this, {
|
2021-09-21 09:32:32 +00:00
|
|
|
transclusionPartSoftSelected: 'selectPartByPageName',
|
2021-08-31 08:05:25 +00:00
|
|
|
transclusionPartSelected: 'onTransclusionPartSelected'
|
2021-08-31 10:35:26 +00:00
|
|
|
} );
|
2021-07-13 11:02:26 +00:00
|
|
|
|
2021-07-09 15:33:16 +00:00
|
|
|
this.partWidgets[ part.getId() ] = widget;
|
2021-07-15 15:07:21 +00:00
|
|
|
if ( typeof newPosition === 'number' && newPosition < this.$element.children().length ) {
|
|
|
|
this.$element.children().eq( newPosition ).before( widget.$element );
|
|
|
|
} else {
|
|
|
|
this.$element.append( widget.$element );
|
|
|
|
}
|
2021-06-17 11:20:50 +00:00
|
|
|
};
|
2021-07-13 11:02:26 +00:00
|
|
|
|
2021-08-27 17:07:17 +00:00
|
|
|
/**
|
|
|
|
* This is inspired by {@see OO.ui.SelectWidget.selectItem}, but isn't one.
|
|
|
|
*
|
2021-09-21 09:32:32 +00:00
|
|
|
* @param {string} pageName
|
2021-08-27 17:07:17 +00:00
|
|
|
*/
|
2021-09-21 09:32:32 +00:00
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.selectPartByPageName = function ( pageName ) {
|
|
|
|
var partId = pageName.split( '/', 1 )[ 0 ],
|
2021-09-23 11:12:50 +00:00
|
|
|
isParameterId = pageName.length > partId.length,
|
2021-09-21 09:32:32 +00:00
|
|
|
changed = false;
|
|
|
|
|
2021-08-27 17:07:17 +00:00
|
|
|
for ( var id in this.partWidgets ) {
|
2021-09-21 09:32:32 +00:00
|
|
|
var partWidget = this.partWidgets[ id ],
|
|
|
|
selected = id === partId;
|
|
|
|
|
|
|
|
if ( partWidget.isSelected() !== selected ) {
|
|
|
|
partWidget.setSelected( selected );
|
2021-09-23 11:12:50 +00:00
|
|
|
if ( selected && !isParameterId ) {
|
2021-09-20 15:27:58 +00:00
|
|
|
partWidget.scrollElementIntoView();
|
|
|
|
}
|
2021-09-13 11:42:20 +00:00
|
|
|
changed = true;
|
|
|
|
}
|
2021-09-21 09:32:32 +00:00
|
|
|
|
|
|
|
if ( selected &&
|
|
|
|
partWidget instanceof ve.ui.MWTransclusionOutlineTemplateWidget &&
|
2021-09-23 11:12:50 +00:00
|
|
|
isParameterId
|
2021-09-21 09:32:32 +00:00
|
|
|
) {
|
|
|
|
var paramName = pageName.slice( partId.length + 1 );
|
|
|
|
partWidget.highlightParameter( paramName );
|
|
|
|
}
|
2021-09-13 11:42:20 +00:00
|
|
|
}
|
2021-09-21 09:32:32 +00:00
|
|
|
|
2021-09-13 11:42:20 +00:00
|
|
|
if ( changed ) {
|
|
|
|
this.emit( 'updateOutlineControlButtons', partId );
|
2021-08-27 17:07:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-08-27 16:22:37 +00:00
|
|
|
/**
|
|
|
|
* This is inspired by {@see OO.ui.SelectWidget.findSelectedItem}, but isn't one.
|
|
|
|
*
|
|
|
|
* @return {string|undefined}
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.findSelectedPartId = function () {
|
|
|
|
for ( var id in this.partWidgets ) {
|
|
|
|
var part = this.partWidgets[ id ];
|
|
|
|
if ( part.isSelected() ) {
|
|
|
|
return part.getData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-16 12:37:34 +00:00
|
|
|
/**
|
|
|
|
* Removes all {@see ve.ui.MWTransclusionOutlinePartWidget}, i.e. empties the list.
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionOutlineContainerWidget.prototype.clear = function () {
|
2021-08-27 16:22:37 +00:00
|
|
|
for ( var id in this.partWidgets ) {
|
|
|
|
this.partWidgets[ id ]
|
|
|
|
.disconnect( this )
|
|
|
|
.$element.remove();
|
2021-07-16 12:37:34 +00:00
|
|
|
}
|
|
|
|
this.partWidgets = {};
|
|
|
|
};
|