mediawiki-extensions-Visual.../modules/ve-mw/ui/widgets/ve.ui.MWTransclusionOutlineTemplateWidget.js

252 lines
7.6 KiB
JavaScript
Raw Normal View History

/*!
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Container for template, as rendered in the template dialog sidebar.
*
* @class
* @extends ve.ui.MWTransclusionOutlinePartWidget
*
* @constructor
* @param {ve.dm.MWTemplateModel} template
*/
ve.ui.MWTransclusionOutlineTemplateWidget = function VeUiMWTransclusionOutlineTemplateWidget( template ) {
var spec = template.getSpec();
// Parent constructor
ve.ui.MWTransclusionOutlineTemplateWidget.super.call( this, template, {
icon: 'puzzle',
label: spec.getLabel()
} );
// Initialization
this.templateModel = template.connect( this, {
add: 'onAddParameter',
remove: 'onRemoveParameter'
} );
var widget = this;
var checkboxes = this.templateModel
.getAllParametersOrdered()
.filter( function ( paramName ) {
if ( spec.isParameterDeprecated( paramName ) && !template.hasParameter( paramName ) ) {
return false;
}
// Don't create a checkbox for ve.ui.MWParameterPlaceholderPage
return paramName;
} )
.map( function ( paramName ) {
return widget.createCheckbox( paramName );
} );
this.searchWidget = new OO.ui.SearchInputWidget( {
placeholder: ve.msg( 'visualeditor-dialog-transclusion-filter-placeholder' ),
classes: [ 've-ui-mwTransclusionOutlineTemplateWidget-searchWidget' ]
} ).connect( this, {
change: 'filterParameters'
} ).toggle( false );
this.infoWidget = new OO.ui.LabelWidget( {
label: new OO.ui.HtmlSnippet( ve.msg( 'visualeditor-dialog-transclusion-filter-no-match' ) ),
classes: [ 've-ui-mwTransclusionOutlineTemplateWidget-no-match' ]
} ).toggle( false );
this.parameters = new OO.ui.FieldsetLayout()
.connect( this, { change: 'onCheckboxListChange' } );
this.parameters.addItems( checkboxes );
this.$element.append(
this.searchWidget.$element,
this.infoWidget.$element,
this.parameters.$element
);
};
/* Inheritance */
OO.inheritClass( ve.ui.MWTransclusionOutlineTemplateWidget, ve.ui.MWTransclusionOutlinePartWidget );
/* Events */
/**
* @event selectParameter
* @param {string} paramId Unique id of the parameter, e.g. something like "part_1/param1"
*/
/**
* Triggered when the user uses the search widget at the top to filter the list of parameters.
*
* @event filterParameters
* @param {Object.<string,boolean>} visibility Keyed by unique id of the parameter, e.g. something
* like "part_1/param1". Note this lists only parameters that are currently in use.
*/
/* Methods */
/**
* @private
* @param {string} paramName
* @return {ve.ui.MWTemplateOutlineParameterCheckboxLayout}
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.createCheckbox = function ( paramName ) {
var spec = this.templateModel.getSpec();
return new ve.ui.MWTemplateOutlineParameterCheckboxLayout( {
required: spec.isParameterRequired( paramName ),
label: spec.getParameterLabel( paramName ),
data: paramName,
selected: this.templateModel.hasParameter( paramName )
} ).connect( this, {
parameterSelectionChanged: 'onParameterSelectionChanged',
parameterFocused: 'onParameterFocused'
} );
};
/**
* @private
* @param {ve.ui.MWTemplateOutlineParameterCheckboxLayout} checkbox
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.insertCheckboxAtCanonicalPosition = function ( checkbox ) {
var paramName = checkbox.getData(),
insertAt = 0,
// Note this might include parameters that don't have a checkbox, e.g. deprecated
allParamNames = this.templateModel.getAllParametersOrdered();
for ( var i = 0; i < allParamNames.length; i++ ) {
if ( allParamNames[ i ] === paramName || !this.parameters.items[ insertAt ] ) {
break;
} else if ( this.parameters.items[ insertAt ].getData() === allParamNames[ i ] ) {
insertAt++;
}
}
this.parameters.addItems( [ checkbox ], insertAt );
};
/**
* Handles a template model add event {@see ve.dm.MWTemplateModel}.
* Triggered when a parameter is added to the template model.
*
* @private
* @param {ve.dm.MWParameterModel} param
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.onAddParameter = function ( param ) {
var paramName = param.getName();
// The placeholder (currently) doesn't get a corresponding item in the sidebar
if ( !paramName ) {
return;
}
// All parameters known via the spec already have a checkbox
var checkbox = this.parameters.findItemFromData( paramName );
if ( !checkbox ) {
checkbox = this.createCheckbox( paramName );
this.insertCheckboxAtCanonicalPosition( checkbox );
// Make sure an active filter is applied to the new checkbox as well
var filter = this.searchWidget.getValue();
if ( filter ) {
this.filterParameters( filter );
}
}
checkbox.setSelected( true, true );
// Reset filter, but only if it hides the relevant checkbox
if ( !checkbox.isVisible() ) {
this.searchWidget.setValue( '' );
}
};
/**
* Handles a template model remove event {@see ve.dm.MWTemplateModel}.
* Triggered when a parameter is removed from the template model.
*
* @private
* @param {ve.dm.MWParameterModel} param
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.onRemoveParameter = function ( param ) {
var checkbox = this.parameters.findItemFromData( param.getName() );
if ( checkbox ) {
checkbox.setSelected( false, true );
}
};
/**
* Handles a parameter checkbox change event {@see ve.ui.MWTemplateOutlineParameterCheckboxLayout}
*
* @private
* @param {string} paramName
* @param {boolean} selected
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.onParameterSelectionChanged = function ( paramName, selected ) {
var param = this.templateModel.getParameter( paramName );
if ( !selected ) {
this.templateModel.removeParameter( param );
} else if ( !param ) {
this.templateModel.addParameter( new ve.dm.MWParameterModel( this.templateModel, paramName ) );
}
};
/**
* @private
* @param {string} paramName
* @fires selectParameter
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.onParameterFocused = function ( paramName ) {
var param = this.templateModel.getParameter( paramName );
if ( param ) {
this.emit( 'selectParameter', param.getId() );
}
};
/**
* @private
* @param {OO.ui.Element[]} items
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.onCheckboxListChange = function ( items ) {
this.searchWidget.toggle( items.length >= 1 );
};
/**
* Narrows the list of checkboxes down to parameters that match the user's input. The search
* algorithm is modelled after {@see ve.ui.MWParameterSearchWidget.buildIndex}. We search the
* parameter's primary name, aliases, label, and description. But not e.g. the example value.
*
* @private
* @param {string} query user input
* @fires filterParameters
*/
ve.ui.MWTransclusionOutlineTemplateWidget.prototype.filterParameters = function ( query ) {
var self = this,
template = this.templateModel,
spec = this.templateModel.getSpec(),
visibility = {},
nothingFound = true;
query = query.trim().toLowerCase();
// Note: We can't really cache this because the list of know parameters can change any time
this.parameters.items.forEach( function ( checkbox ) {
var paramName = checkbox.getData(),
placesToSearch = [
spec.getPrimaryParameterName( paramName ),
spec.getParameterLabel( paramName ),
spec.getParameterDescription( paramName )
].concat( spec.getParameterAliases( paramName ) );
var foundSomeMatch = placesToSearch.some( function ( term ) {
return term && term.toLowerCase().indexOf( query ) !== -1;
} );
checkbox.toggle( foundSomeMatch );
nothingFound = nothingFound && !foundSomeMatch;
var param = template.getParameter( paramName );
if ( param ) {
visibility[ param.getId() ] = foundSomeMatch;
}
} );
this.infoWidget.toggle( nothingFound );
self.emit( 'filterParameters', visibility );
};