2021-05-17 15:43:49 +00:00
|
|
|
/**
|
2022-07-12 09:55:54 +00:00
|
|
|
* Container for the entire transclusion dialog sidebar, may contain a single or
|
|
|
|
* multiple templates or raw wikitext snippets.
|
2021-05-17 15:43:49 +00:00
|
|
|
*
|
|
|
|
* @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-09-21 08:35:36 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget = function VeUiMWTransclusionOutlineWidget() {
|
2021-05-17 15:43:49 +00:00
|
|
|
// Parent constructor
|
2021-09-21 08:35:36 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.super.call( this, {
|
|
|
|
classes: [ 've-ui-mwTransclusionOutlineWidget' ]
|
2021-09-10 19:18:59 +00:00
|
|
|
} );
|
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 */
|
|
|
|
|
2021-09-21 08:35:36 +00:00
|
|
|
OO.inheritClass( ve.ui.MWTransclusionOutlineWidget, OO.ui.Widget );
|
2021-06-17 11:20:50 +00:00
|
|
|
|
|
|
|
/* Events */
|
|
|
|
|
2021-08-05 12:36:10 +00:00
|
|
|
/**
|
2024-04-29 11:52:40 +00:00
|
|
|
* @event ve.ui.MWTransclusionOutlineWidget#filterPagesByName
|
2021-08-31 08:05:25 +00:00
|
|
|
* @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
|
|
|
/**
|
2022-07-04 08:41:37 +00:00
|
|
|
* Respond to the intent to select a sidebar item
|
|
|
|
*
|
2024-04-29 11:52:40 +00:00
|
|
|
* @event ve.ui.MWTransclusionOutlineWidget#sidebarItemSelected
|
2021-08-31 08:05:25 +00:00
|
|
|
* @param {string} pageName Unique id of the {@see OO.ui.BookletLayout} page, e.g. something like
|
|
|
|
* "part_1" or "part_1/param1".
|
2022-07-12 18:10:02 +00:00
|
|
|
* @param {boolean} [soft] If true, don't focus the content pane. Defaults to false.
|
2021-08-19 11:02:11 +00:00
|
|
|
*/
|
|
|
|
|
2022-07-04 08:41:37 +00:00
|
|
|
/* Methods */
|
|
|
|
|
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-09-21 08:35:36 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.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
|
|
|
}
|
2021-07-09 15:33:16 +00:00
|
|
|
if ( added ) {
|
2022-02-22 10:37:21 +00:00
|
|
|
this.addPartWidget( added, newPosition, removed );
|
2021-06-17 11:20:50 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-30 14:32:39 +00:00
|
|
|
/**
|
|
|
|
* Handle spacebar in a part header
|
|
|
|
*
|
2022-07-12 18:10:02 +00:00
|
|
|
* @param {string} pageName
|
2024-04-29 18:14:26 +00:00
|
|
|
* @fires ve.ui.MWTransclusionOutlineWidget#sidebarItemSelected
|
2022-06-30 14:32:39 +00:00
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.onTransclusionPartSoftSelected = function ( pageName ) {
|
2022-07-15 12:30:26 +00:00
|
|
|
this.emit( 'sidebarItemSelected', pageName, true );
|
2022-06-30 14:32:39 +00:00
|
|
|
};
|
|
|
|
|
2021-06-17 11:20:50 +00:00
|
|
|
/**
|
|
|
|
* @private
|
2021-07-09 15:33:16 +00:00
|
|
|
* @param {ve.dm.MWTransclusionPartModel} part
|
2021-06-17 11:20:50 +00:00
|
|
|
*/
|
2021-09-21 08:35:36 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.removePartWidget = function ( part ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const id = part.getId();
|
2021-08-27 16:22:37 +00:00
|
|
|
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]
|
2022-02-22 10:37:21 +00:00
|
|
|
* @param {ve.dm.MWTransclusionPartModel|null} [removed]
|
2024-04-29 18:14:26 +00:00
|
|
|
* @fires ve.ui.MWTransclusionOutlineWidget#filterPagesByName
|
2021-07-09 15:33:16 +00:00
|
|
|
*/
|
2022-02-22 10:37:21 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.addPartWidget = function ( part, newPosition, removed ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const keys = Object.keys( this.partWidgets ),
|
2022-08-17 11:48:14 +00:00
|
|
|
onlyPart = keys.length === 1 && this.partWidgets[ keys[ 0 ] ];
|
|
|
|
if ( onlyPart instanceof ve.ui.MWTransclusionOutlineTemplateWidget ) {
|
|
|
|
// To recalculate the height of the sticky header when we enter multi-part mode
|
|
|
|
onlyPart.recalculateStickyHeaderHeight();
|
|
|
|
}
|
2021-07-09 15:33:16 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
let widget;
|
2021-07-09 15:33:16 +00:00
|
|
|
if ( part instanceof ve.dm.MWTemplateModel ) {
|
2022-02-22 10:37:21 +00:00
|
|
|
widget = new ve.ui.MWTransclusionOutlineTemplateWidget( part, removed instanceof ve.dm.MWTemplatePlaceholderModel );
|
2021-08-05 12:36:10 +00:00
|
|
|
// This forwards events from the nested ve.ui.MWTransclusionOutlineTemplateWidget upwards.
|
2021-08-31 10:35:26 +00:00
|
|
|
widget.connect( this, {
|
2022-06-30 15:27:55 +00:00
|
|
|
filterParametersById: 'onFilterParametersByName'
|
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, {
|
2022-06-30 14:32:39 +00:00
|
|
|
transclusionPartSoftSelected: 'onTransclusionPartSoftSelected',
|
2022-07-18 12:45:51 +00:00
|
|
|
transclusionOutlineItemSelected: [ 'emit', 'sidebarItemSelected' ]
|
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 );
|
|
|
|
}
|
2022-08-30 09:36:02 +00:00
|
|
|
|
|
|
|
if ( widget instanceof ve.ui.MWTransclusionOutlineTemplateWidget ) {
|
|
|
|
// We can do this only after the widget is visible on screen
|
|
|
|
widget.recalculateStickyHeaderHeight();
|
|
|
|
}
|
2021-06-17 11:20:50 +00:00
|
|
|
};
|
2021-07-13 11:02:26 +00:00
|
|
|
|
2022-02-03 12:30:29 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.hideAllUnusedParameters = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
for ( const id in this.partWidgets ) {
|
|
|
|
const partWidget = this.partWidgets[ id ];
|
2022-02-03 12:30:29 +00:00
|
|
|
if ( partWidget instanceof ve.ui.MWTransclusionOutlineTemplateWidget &&
|
|
|
|
partWidget.toggleUnusedWidget
|
|
|
|
) {
|
|
|
|
partWidget.toggleUnusedWidget.toggleUnusedParameters( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-08-17 11:48:14 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.initializeAllStickyHeaderHeights = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
for ( const id in this.partWidgets ) {
|
|
|
|
const partWidget = this.partWidgets[ id ];
|
2022-08-17 11:48:14 +00:00
|
|
|
if ( partWidget instanceof ve.ui.MWTransclusionOutlineTemplateWidget ) {
|
|
|
|
partWidget.recalculateStickyHeaderHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-08-27 17:07:17 +00:00
|
|
|
/**
|
|
|
|
* This is inspired by {@see OO.ui.SelectWidget.selectItem}, but isn't one.
|
|
|
|
*
|
2022-07-05 12:37:12 +00:00
|
|
|
* @param {string} [pageName] Symbolic name of page. Omit to remove current selection.
|
2021-08-27 17:07:17 +00:00
|
|
|
*/
|
2021-09-30 11:08:10 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.setSelectionByPageName = function ( pageName ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const selectedPartId = pageName ? pageName.split( '/', 1 )[ 0 ] : null,
|
2022-07-05 12:37:12 +00:00
|
|
|
isParameter = pageName ? pageName.length > selectedPartId.length : false;
|
2021-09-21 09:32:32 +00:00
|
|
|
|
2024-05-21 14:22:56 +00:00
|
|
|
for ( const partId in this.partWidgets ) {
|
|
|
|
const partWidget = this.partWidgets[ partId ],
|
2022-06-30 07:19:43 +00:00
|
|
|
selected = partId === pageName;
|
2021-09-21 09:32:32 +00:00
|
|
|
|
2022-07-18 10:48:45 +00:00
|
|
|
partWidget.setSelected( selected );
|
2022-07-06 12:58:55 +00:00
|
|
|
if ( selected && !isParameter ) {
|
|
|
|
partWidget.scrollElementIntoView();
|
2021-09-13 11:42:20 +00:00
|
|
|
}
|
2021-09-21 09:32:32 +00:00
|
|
|
|
2022-06-30 07:19:43 +00:00
|
|
|
if ( partWidget instanceof ve.ui.MWTransclusionOutlineTemplateWidget ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const selectedParamName = ( partId === selectedPartId && isParameter ) ?
|
2022-06-30 07:19:43 +00:00
|
|
|
pageName.slice( selectedPartId.length + 1 ) : null;
|
2022-07-09 06:54:35 +00:00
|
|
|
partWidget.setParameter( selectedParamName );
|
2021-09-21 09:32:32 +00:00
|
|
|
}
|
2021-09-13 11:42:20 +00:00
|
|
|
}
|
2021-08-27 17:07:17 +00:00
|
|
|
};
|
|
|
|
|
2022-06-15 14:47:54 +00:00
|
|
|
/**
|
|
|
|
* @param {string} pageName
|
|
|
|
* @param {boolean} hasValue
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.toggleHasValueByPageName = function ( pageName, hasValue ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
const idParts = pageName.split( '/', 2 ),
|
2022-06-15 14:47:54 +00:00
|
|
|
templatePartWidget = this.partWidgets[ idParts[ 0 ] ];
|
|
|
|
|
|
|
|
templatePartWidget.toggleHasValue( idParts[ 1 ], hasValue );
|
|
|
|
};
|
|
|
|
|
2022-07-25 14:43:28 +00:00
|
|
|
/**
|
|
|
|
* Checks if the provided DOM element belongs to the DOM structure of one of the top-level
|
|
|
|
* {@see ve.ui.MWTransclusionOutlinePartWidget}s, and returns its id. Useful for e.g. mouse click or
|
|
|
|
* keyboard handlers.
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} element
|
|
|
|
* @return {string|undefined} Always a top-level part id, e.g. "part_0"
|
|
|
|
*/
|
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.findPartIdContainingElement = function ( element ) {
|
|
|
|
if ( element ) {
|
2024-05-21 14:22:56 +00:00
|
|
|
for ( const id in this.partWidgets ) {
|
|
|
|
const part = this.partWidgets[ id ];
|
2022-07-25 14:43:28 +00:00
|
|
|
if ( $.contains( part.$element[ 0 ], element ) ) {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-16 12:37:34 +00:00
|
|
|
/**
|
|
|
|
* Removes all {@see ve.ui.MWTransclusionOutlinePartWidget}, i.e. empties the list.
|
|
|
|
*/
|
2021-09-21 08:35:36 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.clear = function () {
|
2024-05-21 14:22:56 +00:00
|
|
|
for ( const id in this.partWidgets ) {
|
2021-08-27 16:22:37 +00:00
|
|
|
this.partWidgets[ id ]
|
|
|
|
.disconnect( this )
|
|
|
|
.$element.remove();
|
2021-07-16 12:37:34 +00:00
|
|
|
}
|
|
|
|
this.partWidgets = {};
|
|
|
|
};
|
2022-06-30 15:27:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
2022-07-12 18:10:02 +00:00
|
|
|
* @param {Object.<string,boolean>} visibility
|
2024-04-29 18:14:26 +00:00
|
|
|
* @fires ve.ui.MWTransclusionOutlineWidget#filterPagesByName
|
2022-06-30 15:27:55 +00:00
|
|
|
*/
|
2022-07-12 18:10:02 +00:00
|
|
|
ve.ui.MWTransclusionOutlineWidget.prototype.onFilterParametersByName = function ( visibility ) {
|
|
|
|
this.emit( 'filterPagesByName', visibility );
|
2022-06-30 15:27:55 +00:00
|
|
|
this.setSelectionByPageName();
|
|
|
|
};
|