RichText¶
Overview¶
Displays rich text by interpreting the input text as HTML, supporting a limited set of tags that prevent any security vulnerabilities. It does this by parsing the input HTML and splitting it into multiple Text children recursively.
NOTE: Encoding HTML entities is required, and malformed HTML is not accepted.
NOTE: Currently it can line-wrap at the start and end of tags. This will probably be fixed in the future to only potentially break on whitespace.
It supports the following markup and features in the string content (in addition to other options as listed in RICH_TEXT_OPTION_KEYS): - <a href="{{placeholder}}"> for links (pass in { links: { placeholder: ACTUAL_HREF } }) - <b> and <strong> for bold text - <i> and <em> for italic text - <sub> and <sup> for subscripts / superscripts - <u> for underlined text - <s> for strikethrough text - <span> tags with a dir="ltr" / dir="rtl" attribute - <br> for explicit line breaks - <node id="id"> for embedding a Node into the text (pass in { nodes: { id: NODE } }), with optional align attribute - Custom Scenery wrapping around arbitrary tags, e.g. <blur>...</blur>, pass in { tags: { blur: ... } }, see below - Unicode bidirectional marks (present in PhET strings) for full RTL support - CSS style="..." attributes, with color and font settings, see https://github.com/phetsims/scenery/issues/807
Examples from the scenery-phet demo:
new RichText( 'RichText can have <b>bold</b> and <i>italic</i> text.' ), new RichText( 'Can do H<sub>2</sub>O (A<sub>sub</sub> and A<sup>sup</sup>), or nesting: x<sup>2<sup>2</sup></sup>' ), new RichText( 'Additionally: <span style="color: blue;">color</span>, <span style="font-size: 30px;">sizes</span>, <span style="font-family: serif;">faces</span>, <s>strikethrough</s>, and <u>underline</u>' ), new RichText( 'These <b><em>can</em> <u><span style="color: red;">be</span> mixed<sup>1</sup></u></b>.' ), new RichText( '\u202aHandles bidirectional text: \u202b<span style="color: #0a0;">مقابض</span> النص ثنائي <b>الاتجاه</b><sub>2</sub>\u202c\u202c' ), new RichText( '\u202b\u062a\u0633\u062a (\u0632\u0628\u0627\u0646)\u202c' ), new RichText( 'HTML entities need to be escaped, like & and <.' ), new RichText( 'Supports <a href="{{phetWebsite}}"><em>links</em> with <b>markup</b></a>, and <a href="{{callback}}">links that call functions</a>.', { links: { phetWebsite: 'https://phet.colorado.edu', callback: function() { console.log( 'Link was clicked' ); } } } ), new RichText( 'Or also <a href="https://phet.colorado.edu">links directly in the string</a>.', { links: true } ), new RichText( 'Links not found <a href="{{bogus}}">are ignored</a> for security.' ), new HBox( { spacing: 30, children: [ new RichText( 'Multi-line text with the<br>separator <br> and <a href="https://phet.colorado.edu">handles<br>links</a> and other <b>tags<br>across lines</b>', { links: true } ), new RichText( 'Supposedly RichText supports line wrapping. Here is a lineWrap of 300, which should probably wrap multiple times here', { lineWrap: 300 } ) ] } )
@author Jonathan Olson <jonathan.olson@colorado.edu>
Class RichText¶
Constructor¶
new RichText( string : string | number | TReadOnlyProperty<string>, providedOptions? : RichTextOptions )¶
Instance Methods¶
setStringProperty( newTarget : TReadOnlyProperty<string> | null ) : this¶
See documentation for Node.setVisibleProperty, except this is for the text string.
NOTE: Setting the .string after passing a truly read-only Property will fail at runtime. We choose to allow passing in read-only Properties for convenience.
getStringProperty() : TProperty<string>¶
Like Node.getVisibleProperty, but for the text string. Note this is not the same as the Property provided in setStringProperty. Thus is the nature of TinyForwardingProperty.
getPhetioMouseHitTarget( fromLinking ) : PhetioObject | 'phetioNotSelectable'¶
RichText supports a "string" selection mode, in which it will map to its stringProperty (if applicable), otherwise is uses the default mouse-hit target from the supertype.
initializePhetioObject( baseOptions : Partial<PhetioObjectOptions>, providedOptions : RichTextOptions )¶
See documentation and comments in Node.initializePhetioObject
dispose()¶
Releases references.
setString( string : string | number ) : this¶
Sets the string displayed by our node.
NOTE: Encoding HTML entities is required, and malformed HTML is not accepted.
@param string - The string to display. If it's a number, it will be cast to a string
getString() : string¶
Returns the string displayed by our text Node.
setBoundsMethod( method : TextBoundsMethod ) : this¶
Sets the method that is used to determine bounds from the text. See Text.setBoundsMethod for details
getBoundsMethod() : TextBoundsMethod¶
Returns the current method to estimate the bounds of the text. See setBoundsMethod() for more information.
setFont( font : Font | string ) : this¶
Sets the font of our node.
getFont() : Font | string¶
Returns the current Font
setFill( fill : TPaint ) : this¶
Sets the fill of our text.
getFill() : TPaint¶
Returns the current fill.
setStroke( stroke : TPaint ) : this¶
Sets the stroke of our text.
getStroke() : TPaint¶
Returns the current stroke.
setLineWidth( lineWidth : number ) : this¶
Sets the lineWidth of our text.
getLineWidth() : number¶
Returns the current lineWidth.
setSubScale( subScale : number ) : this¶
Sets the scale (relative to 1) of any string under subscript (<sub>) elements.
getSubScale() : number¶
Returns the scale (relative to 1) of any string under subscript (<sub>) elements.
setSubXSpacing( subXSpacing : number ) : this¶
Sets the horizontal spacing before any subscript (<sub>) elements.
getSubXSpacing() : number¶
Returns the horizontal spacing before any subscript (<sub>) elements.
setSubYOffset( subYOffset : number ) : this¶
Sets the adjustment offset to the vertical placement of any subscript (<sub>) elements.
getSubYOffset() : number¶
Returns the adjustment offset to the vertical placement of any subscript (<sub>) elements.
setSupScale( supScale : number ) : this¶
Sets the scale (relative to 1) of any string under superscript (<sup>) elements.
getSupScale() : number¶
Returns the scale (relative to 1) of any string under superscript (<sup>) elements.
setSupXSpacing( supXSpacing : number ) : this¶
Sets the horizontal spacing before any superscript (<sup>) elements.
getSupXSpacing() : number¶
Returns the horizontal spacing before any superscript (<sup>) elements.
setSupYOffset( supYOffset : number ) : this¶
Sets the adjustment offset to the vertical placement of any superscript (<sup>) elements.
getSupYOffset() : number¶
Returns the adjustment offset to the vertical placement of any superscript (<sup>) elements.
setCapHeightScale( capHeightScale : number ) : this¶
Sets the expected cap height (baseline to top of capital letters) as a scale of the detected distance from the baseline to the top of the text bounds.
getCapHeightScale() : number¶
Returns the expected cap height (baseline to top of capital letters) as a scale of the detected distance from the baseline to the top of the text bounds.
setUnderlineLineWidth( underlineLineWidth : number ) : this¶
Sets the lineWidth of underline lines.
getUnderlineLineWidth() : number¶
Returns the lineWidth of underline lines.
setUnderlineHeightScale( underlineHeightScale : number ) : this¶
Sets the underline height adjustment as a proportion of the detected distance from the baseline to the top of the text bounds.
getUnderlineHeightScale() : number¶
Returns the underline height adjustment as a proportion of the detected distance from the baseline to the top of the text bounds.
setStrikethroughLineWidth( strikethroughLineWidth : number ) : this¶
Sets the lineWidth of strikethrough lines.
getStrikethroughLineWidth() : number¶
Returns the lineWidth of strikethrough lines.
setStrikethroughHeightScale( strikethroughHeightScale : number ) : this¶
Sets the strikethrough height adjustment as a proportion of the detected distance from the baseline to the top of the text bounds.
getStrikethroughHeightScale() : number¶
Returns the strikethrough height adjustment as a proportion of the detected distance from the baseline to the top of the text bounds.
setLinkFill( linkFill : TPaint ) : this¶
Sets the color of links. If null, no fill will be overridden.
getLinkFill() : TPaint¶
Returns the color of links.
setLinkEventsHandled( linkEventsHandled : boolean ) : this¶
Sets whether link clicks will call event.handle().
getLinkEventsHandled() : boolean¶
Returns whether link events will be handled.
setLinks( links : RichTextLinks ) : this¶
getLinks() : RichTextLinks¶
Returns whether link events will be handled.
setNodes( nodes : Record<string, Node> ) : this¶
getNodes() : Record<string, Node>¶
setTags( tags : Record<string, ( node: Node ) => Node> ) : this¶
getTags() : Record<string, ( node: Node ) => Node>¶
setReplaceNewlines( replaceNewlines : boolean ) : this¶
Sets whether newlines are replaced with <br>
getReplaceNewlines() : boolean¶
setAlign( align : RichTextAlign ) : this¶
Sets the alignment of text (only relevant if there are multiple lines).
getAlign() : RichTextAlign¶
Returns the current alignment of the text (only relevant if there are multiple lines).
setLeading( leading : number ) : this¶
Sets the leading (spacing between lines)
getLeading() : number¶
Returns the leading (spacing between lines)
setLineWrap( lineWrap : RequiredOption<SelfOptions, 'lineWrap'> ) : this¶
Sets the line wrap width for the text (or null if none is desired). Lines longer than this length will wrap automatically to the next line.
@param lineWrap - If it's a number, it should be greater than 0.
getLineWrap() : RequiredOption<SelfOptions, 'lineWrap'>¶
Returns the line wrap width.
mutate( options? : RichTextOptions ) : this¶
Static Methods¶
stringWithFont( str : string, font : Font ) : string¶
Returns a wrapped version of the string with a font specifier that uses the given font object.
NOTE: Does an approximation of some font values (using <b> or <i>), and cannot force the lack of those if it is included in bold/italic exterior tags.
himalayaElementToString( element : HimalayaNode ) : string¶
Stringifies an HTML subtree defined by the given element.
himalayaElementToAccessibleString( element : HimalayaNode ) : string¶
Stringifies an HTML subtree defined by the given element, but removing certain tags that we don't need for accessibility (like <a>, <span>, etc.), and adding in tags we do want (see ACCESSIBLE_TAGS).
getAccessibleStringProperty( stringProperty : TReadOnlyProperty<string> ) : TReadOnlyProperty<string>¶
Transforms a given string with HTML markup into a string suitable for screen readers. Preserves basic styling tags while removing non-accessible markup.
contentToString( content : string, isLTR? : boolean ) : string¶
Takes the element.content from himalaya, unescapes HTML entities, and applies the proper directional tags.
See https://github.com/phetsims/scenery-phet/issues/315
Static Properties¶
STRING_PROPERTY_TANDEM_NAME¶
(readonly)
Text and RichText currently use the same tandem name for their stringProperty.
RichTextIO : IOType¶
Type RichTextAlign¶
"left" | "center" | "right"
Type RichTextHref¶
( () => void ) | string
Type RichTextLinks¶
RichTextLinksObject | true
Type RichTextOptions¶
- boundsMethod?: TextBoundsMethod
Sets how bounds are determined for text - font?: Font | string
Sets the font for the text - fill?: TPaint
Sets the fill of the text - stroke?: TPaint
Sets the stroke around the text - lineWidth?: number
Sets the lineWidth around the text - subScale?: number
Sets the scale of any subscript elements - subXSpacing?: number
Sets horizontal spacing before any subscript elements - subYOffset?: number
Sets vertical offset for any subscript elements - supScale?: number
Sets the scale for any superscript elements - supXSpacing?: number
Sets the horizontal offset before any superscript elements - supYOffset?: number
Sets the vertical offset for any superscript elements - capHeightScale?: number
Sets the expected cap height cap height (baseline to top of capital letters) as a scale - underlineLineWidth?: number
Sets the line width for underlines - underlineHeightScale?: number
Sets the underline height as a scale relative to text bounds height - strikethroughLineWidth?: number
Sets line width for strikethrough - strikethroughHeightScale?: number
Sets height of strikethrough as a scale relative to text bounds height - linkFill?: TPaint
Sets the fill for links within the text - linkEventsHandled?: boolean
Sets whether link clicks will call event.handle() - links?: RichTextLinks
Sets the map of href placeholder => actual href/callback used for links. However, if set to true ({boolean}) as a full object, links in the string will not be mapped, but will be directly added.
For instance, the default is to map hrefs for security purposes:
new RichText( '<a href="{{alink}}">content</a>', { links: { alink: 'https://phet.colorado.edu' } } );
But links with an href not matching will be ignored. This can be avoided by passing links: true to directly embed links:
new RichText( '<a href="https://phet.colorado.edu">content</a>', { links: true } );
Callbacks (instead of a URL) are also supported, e.g.:
new RichText( '<a href="{{acallback}}">content</a>', { links: { acallback: function() { console.log( 'clicked' ) } } } );
See https://github.com/phetsims/scenery-phet/issues/316 for more information. - nodes?: Record<string, Node>
A map of string => Node, where <node id="string"/>
will get replaced by the given Node (DAG supported)
For example:
new RichText( 'This is a <node id="test"/>', { nodes: { test: new Text( 'Node' ) } }
Alignment is also supported, with the align attribute (center/top/bottom/origin). This alignment is in relation to the current text/font size in the HTML where the <node> tag is placed. An example:
new RichText( 'This is a <node id="test" align="top"/>', { nodes: { test: new Text( 'Node' ) } } NOTE: When alignment isn't supplied, origin is used as a default. Origin means "y=0 is placed at the baseline of the text". - tags?: Record<string, ( node: Node ) => Node>
A map of string => Node replacement function ( node: Node ) => Node, where RichText will "render" line content inside the tag, and then pass it to the function to replace the content of the Node. Each tag function should return a new Node.
For example:
new RichText( 'There is <blue>blue text</blue> and <blur>blurry text</blur>', {
tags: {
blur: node => new Node( { children: [ node ], filters: [ new GaussianBlur( 1.5 ) ] } ),
blue: node => new Node( { children: [ Rectangle.bounds( node.bounds, { fill: '#88f' } ), node ] } )
}
} )
NOTE: This does NOT affect the layout or bounds of the resulting content, since the layout is done BEFORE this wrapping is done. If we ever need the wrapping to be done BEFORE, the wrapping will need to be called many times when line-wrapping is done.
Here is a more in-depth example with many elements:
new RichText( 'This is a test with <blue>blue</blue> text being <blue>wrapped in a blue color that should support line wrap</blue>. Text can also be <translucent>more transparent</translucent>, or can be <blur>blurred</blur> or have <shadow>drop shadow</shadow>. Tags can be <blue>repeated or <blur>nested</blur></blue>', {
lineWrap: 300,
tags: {
blur: node => {
return new Node( {
children: [ node ],
filters: [ new GaussianBlur( 1.5 ) ]
} );
},
shadow: node => {
return new Node( {
children: [ node ],
filters: [ new DropShadow( new Vector2( 2, 1 ), 2, 'red' ) ]
} );
},
translucent: node => {
return new Node( {
children: [ node ],
opacity: 0.5
} );
},
blue: node => {
return new Node( {
children: [
Rectangle.bounds( node.bounds.dilated( 1 ), { fill: '#88f' } ),
node
]
} );
}
}
} )
- replaceNewlines?: boolean
Will replace newlines (\n
) with <br>, similar to the old MultiLineText (defaults to false) - align?: RichTextAlign
Sets text alignment if there are multiple lines - leading?: number
Sets the spacing between lines if there are multiple lines - lineWrap?: number | "stretch" | null
Sets width of text before creating a new line. When set to 'stretch' controls whether its WidthSizable. In this case it will use the preferred width to determine the line wrap. - stringProperty?: TReadOnlyProperty<string> | null
Sets forwarding of the stringProperty, see setStringProperty() for more documentation - stringPropertyOptions?: PropertyOptions<string>
- string?: string | number
Sets the string to be displayed by this Node - & NodeOptions
Source Code¶
See the source for RichText.ts in the scenery repository.