/**
* LabelElement is often mixed into other classes to generate a label, which
* helps identify the function of an interface element.
* See the [OOjs UI documentation on MediaWiki] [1] for more information.
*
* [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
*
* @mixin
*
* @param {Object} [config] Configuration options
* @param {jQuery} [config.$label] The label element created by the class. If this
* configuration is omitted, the label element will use a generated `<span>`.
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} [config.label] The label text. The label can be specified
* as a plaintext string, a jQuery selection of elements, or a function that will produce a string
* in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
* [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
*/
OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
// Configuration initialization
config = config || {};
// Properties
this.$label = null;
this.label = null;
// Initialization
this.setLabel( config.label || this.constructor.static.label );
this.setLabelElement( config.$label || $( '<span>' ) );
};
/* Setup */
OO.initClass( OO.ui.mixin.LabelElement );
/* Events */
/**
* @event labelChange
* @param {string} value
*/
/* Static Properties */
/**
* The label text. The label can be specified as a plaintext string, a function that will
* produce a string in the future, or `null` for no label. The static value will
* be overridden if a label is specified with the #label config option.
*
* @static
* @inheritable
* @property {string|Function|null}
*/
OO.ui.mixin.LabelElement.static.label = null;
/* Static methods */
/**
* Highlight the first occurrence of the query in the given text
*
* @param {string} text Text
* @param {string} query Query to find
* @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
* @return {jQuery} Text with the first match of the query
* sub-string wrapped in highlighted span
*/
OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query, compare ) {
var i, tLen, qLen,
offset = -1,
$result = $( '<span>' );
if ( compare ) {
tLen = text.length;
qLen = query.length;
for ( i = 0; offset === -1 && i <= tLen - qLen; i++ ) {
if ( compare( query, text.slice( i, i + qLen ) ) === 0 ) {
offset = i;
}
}
} else {
offset = text.toLowerCase().indexOf( query.toLowerCase() );
}
if ( !query.length || offset === -1 ) {
$result.text( text );
} else {
$result.append(
document.createTextNode( text.slice( 0, offset ) ),
$( '<span>' )
.addClass( 'oo-ui-labelElement-label-highlight' )
.text( text.slice( offset, offset + query.length ) ),
document.createTextNode( text.slice( offset + query.length ) )
);
}
return $result.contents();
};
/* Methods */
/**
* Set the label element.
*
* If an element is already set, it will be cleaned up before setting up the new element.
*
* @param {jQuery} $label Element to use as label
*/
OO.ui.mixin.LabelElement.prototype.setLabelElement = function ( $label ) {
if ( this.$label ) {
this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
}
this.$label = $label.addClass( 'oo-ui-labelElement-label' );
this.setLabelContent( this.label );
};
/**
* Set the label.
*
* An empty string will result in the label being hidden. A string containing only whitespace will
* be converted to a single ` `.
*
* @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
* text; or null for no label
* @chainable
*/
OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
label = ( ( typeof label === 'string' || label instanceof jQuery ) && label.length ) || ( label instanceof OO.ui.HtmlSnippet && label.toString().length ) ? label : null;
if ( this.label !== label ) {
if ( this.$label ) {
this.setLabelContent( label );
}
this.label = label;
this.emit( 'labelChange' );
}
this.$element.toggleClass( 'oo-ui-labelElement', !!this.label );
return this;
};
/**
* Set the label as plain text with a highlighted query
*
* @param {string} text Text label to set
* @param {string} query Substring of text to highlight
* @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
* @chainable
*/
OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query, compare ) {
return this.setLabel( this.constructor.static.highlightQuery( text, query, compare ) );
};
/**
* Get the label.
*
* @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
* text; or null for no label
*/
OO.ui.mixin.LabelElement.prototype.getLabel = function () {
return this.label;
};
/**
* Set the content of the label.
*
* Do not call this method until after the label element has been set by #setLabelElement.
*
* @private
* @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
* text; or null for no label
*/
OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
if ( typeof label === 'string' ) {
if ( label.match( /^\s*$/ ) ) {
// Convert whitespace only string to a single non-breaking space
this.$label.html( ' ' );
} else {
this.$label.text( label );
}
} else if ( label instanceof OO.ui.HtmlSnippet ) {
this.$label.html( label.toString() );
} else if ( label instanceof jQuery ) {
this.$label.empty().append( label );
} else {
this.$label.empty();
}
};