User:ESanders (WMF)/commentlinks.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * Converts comment timestamps in a link which copies a
 * hash link to that specific comment to the clipboard.
 */
mw.hook( 'wikipage.content' ).add( ( $container ) => {
	if ( !mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) ) {
		return;
	}

	if ( !$container.is( '#mw-content-text' ) ) {
		return;
	}

	// Don't run again if timestamp links already on the page
	if ( $container.find( '.ext-discussiontools-init-timestamplink' ).length ) {
		return;
	}

	// Ideally would only ever be added once
	mw.util.addCSS( '.ext-discussiontools-init-timestamplink, .ext-discussiontools-init-timestamplink:visited, .ext-discussiontools-init-timestamplink:active { color: #666; }' );

	mw.loader.using( 'ext.discussionTools.init' ).then( () => {
		// eslint-disable-next-line no-jquery/no-global-selector
		const canonicalUrl = $( 'link[rel=canonical]' ).attr( 'href' ) || '';

		const Parser = mw.loader.require( 'ext.discussionTools.init' ).Parser,
			parser = new Parser( mw.loader.require( 'ext.discussionTools.init' ).parserData ),
			result = parser.parse(
				document.getElementById( 'mw-content-text' ),
				mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) )
			),
			comments = result.getCommentItems(),
			timestampRegexps = parser.getLocalTimestampRegexps();

		function searchNode( node ) {
			let matchAndNode = null;
			if ( node.nodeType === Node.ELEMENT_NODE ) {
				Array.prototype.some.call( node.childNodes, ( n ) => {
					matchAndNode = searchNode( n );
					if ( matchAndNode ) {
						return true;
					}
					return false;
				} );
			} else if ( node.nodeType === Node.TEXT_NODE ) {
				const match = parser.findTimestamp( node, timestampRegexps );
				if ( match ) {
					matchAndNode = {
						match,
						node
					};
				}
			}
			return matchAndNode;
		}

		comments.forEach( ( comment ) => {
			comment.signatureRanges.forEach( ( signatureRange ) => {
				const matchAndNode = searchNode( signatureRange.endContainer );
				if ( !matchAndNode ) {
					return;
				}
				const { match, node } = matchAndNode;

				const textNode = node.splitText( match.matchData.index );
				textNode.splitText( match.matchData[ 0 ].length );

				const $link = $( '<a>' )
					.attr( 'href', canonicalUrl + '#' + comment.id )
					.addClass( 'ext-discussiontools-init-timestamplink' )
					.append( textNode )
					.on( 'click', ( e ) => {
						const $win = $( window );
						const scrollTop = $win.scrollTop();
						const $tmpInput = $( '<input>' )
							.val( e.currentTarget.href )
							.addClass( 'noime' )
							.css( {
								position: 'fixed',
								top: 0
							} )
							.appendTo( 'body' )
							.trigger( 'focus' );
						$tmpInput[ 0 ].setSelectionRange( 0, e.currentTarget.href.length );
						let copied;
						try {
							copied = document.execCommand( 'copy' );
						} catch ( e ) {
							copied = false;
						}
						if ( copied ) {
							mw.notify( 'Link to comment copied to clipboard.' );
						}
						$tmpInput.remove();
						// Restore scroll position
						requestAnimationFrame( () => {
							$win.scrollTop( scrollTop );
						} );
					} );

				node.parentNode.insertBefore( $link[ 0 ], node.nextSibling );
			} );
		} );
	} );
} );