@@ -18,6 +18,7 @@ limitations under the License.
1818*/
1919
2020import React , { ReactNode } from 'react' ;
21+ import ReactDOM from 'react-dom' ;
2122import sanitizeHtml from 'sanitize-html' ;
2223import cheerio from 'cheerio' ;
2324import classNames from 'classnames' ;
@@ -35,6 +36,8 @@ import { getEmojiFromUnicode } from "./emoji";
3536import { mediaFromMxc } from "./customisations/Media" ;
3637import { ELEMENT_URL_PATTERN , options as linkifyMatrixOptions } from './linkify-matrix' ;
3738import { stripHTMLReply , stripPlainReply } from './utils/Reply' ;
39+ import TextWithTooltip from './components/views/elements/TextWithTooltip' ;
40+ import PlatformPeg from './PlatformPeg' ;
3841
3942// Anything outside the basic multilingual plane will be a surrogate pair
4043const SURROGATE_PAIR_PATTERN = / ( [ \ud800 - \udbff ] ) ( [ \udc00 - \udfff ] ) / ;
@@ -635,6 +638,57 @@ export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatri
635638 return sanitizeHtml ( linkifyString ( dirtyHtml , options ) , sanitizeHtmlParams ) ;
636639}
637640
641+ const getAbsoluteUrl = ( ( ) => {
642+ let a : HTMLAnchorElement ;
643+
644+ return ( url : string ) => {
645+ if ( ! a ) {
646+ a = document . createElement ( 'a' ) ;
647+ }
648+ a . href = url ;
649+ return a . href ;
650+ } ;
651+ } ) ( ) ;
652+
653+ /**
654+ * Recurses depth-first through a DOM tree, adding tooltip previews for link elements.
655+ *
656+ * @param {Element[] } rootNodes - a list of sibling DOM nodes to traverse to try
657+ * to add tooltips.
658+ * @param {Element[] } ignoredNodes: a list of nodes to not recurse into.
659+ */
660+ export function tooltipifyLinks ( rootNodes : ArrayLike < Element > , ignoredNodes : Element [ ] ) {
661+ if ( ! PlatformPeg . get ( ) . needsUrlTooltips ( ) ) {
662+ return ;
663+ }
664+
665+ let node = rootNodes [ 0 ] ;
666+
667+ while ( node ) {
668+ let tooltipified = false ;
669+
670+ if ( ignoredNodes . indexOf ( node ) >= 0 ) {
671+ node = node . nextSibling as Element ;
672+ continue ;
673+ }
674+
675+ if ( node . tagName === "A" && node . getAttribute ( "href" ) && node . getAttribute ( "href" ) != node . textContent . trim ( ) ) {
676+ const href = node . getAttribute ( "href" ) ;
677+ const tooltip = < TextWithTooltip tooltip = { getAbsoluteUrl ( href ) } >
678+ < span dangerouslySetInnerHTML = { { __html : node . innerHTML } } />
679+ </ TextWithTooltip > ;
680+ ReactDOM . render ( tooltip , node ) ;
681+ tooltipified = true ;
682+ }
683+
684+ if ( node . childNodes && node . childNodes . length && ! tooltipified ) {
685+ tooltipifyLinks ( node . childNodes as NodeListOf < Element > , ignoredNodes ) ;
686+ }
687+
688+ node = node . nextSibling as Element ;
689+ }
690+ }
691+
638692/**
639693 * Returns if a node is a block element or not.
640694 * Only takes html nodes into account that are allowed in matrix messages.
0 commit comments