User:ESanders (WMF)/commentlinks.js
Appearance
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)
- Edge: Hold Ctrl while clicking Refresh, or 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 );
} );
} );
} );
} );