links: show links in live previews and preserve fragments in links

* Use wikipage.content hook event so that links are also applied on
content added through JS tools like live preview, real time preview,
DiscussionTools, etc. (Consider checking the diff with whitespaces
ignored.)
* Preserve fragments (anchors) in links by using mw.Title getUrl().

Bug: T368166
Change-Id: Ib3a4d03abf7401c39bc3261ca2056cce482edb13
This commit is contained in:
Siddharth VP 2024-10-12 21:40:56 +05:30
parent 73f8f3ef71
commit e1a747987f
2 changed files with 53 additions and 49 deletions

View file

@ -43,7 +43,7 @@ $( () => {
title = mw.Title.newFromText( pageName, 10 );
}
if ( title ) {
link.href = mw.util.getUrl( title.toText() );
link.href = title.getUrl();
link.title = title.toText();
}
if ( link.href ) {
@ -61,12 +61,15 @@ $( () => {
}
const commentClasses = [ 'c', 'c1', 'cm' ];
Array.from( document.getElementsByClassName( 'mw-highlight' ) ).forEach( ( codeBlock ) => {
commentClasses.forEach( ( commentClass ) => {
Array.from( codeBlock.getElementsByClassName( commentClass ) ).forEach( ( node ) => {
processComment( node.firstChild, node );
mw.hook( 'wikipage.content' ).add( ( $content ) => {
$content.find( '.mw-highlight' ).get().forEach( ( codeBlock ) => {
commentClasses.forEach( ( commentClass ) => {
Array.from( codeBlock.getElementsByClassName( commentClass ) ).forEach( ( node ) => {
processComment( node.firstChild, node );
} );
} );
} );
} );
} );
} );

View file

@ -1,12 +1,8 @@
$( () => {
const classes = {
singleQuoteString: 's1', doubleQuoteString: 's2'
};
function addLink( element, page ) {
function addLink( element, title ) {
const link = document.createElement( 'a' );
link.href = mw.util.getUrl( page );
link.href = title.getUrl();
link.title = title.toText();
// put text node from element inside link
const firstChild = element.firstChild;
if ( !( firstChild instanceof Text ) ) {
@ -23,45 +19,50 @@ $( () => {
'mw.loadJsonData': () => true
};
const stringNodes = Array.from( document.getElementsByClassName( classes.singleQuoteString ) )
.concat( Array.from( document.getElementsByClassName( classes.doubleQuoteString ) ) );
mw.hook( 'wikipage.content' ).add( ( $content ) => {
stringNodes.forEach( ( node ) => {
if ( !node.nextElementSibling ||
!node.nextElementSibling.firstChild ||
!node.nextElementSibling.firstChild.nodeValue ||
node.nextElementSibling.firstChild.nodeValue.indexOf( ')' ) !== 0 ) {
return;
}
if ( !node.previousElementSibling || !node.previousElementSibling.firstChild ||
node.previousElementSibling.firstChild.nodeValue !== '(' ) {
return;
}
Object.keys( parametersToLink ).forEach( ( invocation ) => {
const parts = invocation.split( '.' );
let partIdx = parts.length - 1;
let curNode = node.previousElementSibling && node.previousElementSibling.previousElementSibling;
while ( partIdx >= 0 ) {
if ( !curNode || curNode.firstChild.nodeValue !== parts[ partIdx ] ) {
return;
}
if ( partIdx === 0 ) {
break;
}
const prev = curNode.previousElementSibling;
if ( !prev || prev.firstChild.nodeValue !== '.' ) {
return;
}
curNode = prev.previousElementSibling;
partIdx--;
// s1 is the class applied by Pygments to single-quoted strings
// s2 is the class applied by Pygments to double-quoted strings
const stringNodes = $content.find( '.s1' ).get()
.concat( $content.find( '.s2' ).get() );
stringNodes.forEach( ( node ) => {
if ( !node.nextElementSibling ||
!node.nextElementSibling.firstChild ||
!node.nextElementSibling.firstChild.nodeValue ||
node.nextElementSibling.firstChild.nodeValue.indexOf( ')' ) !== 0 ) {
return;
}
const page = node.firstChild.nodeValue.slice( 1, -1 );
const condition = parametersToLink[ invocation ];
const title = mw.Title.newFromText( page );
if ( title && condition( title ) ) {
addLink( node, title.toText() );
if ( !node.previousElementSibling || !node.previousElementSibling.firstChild ||
node.previousElementSibling.firstChild.nodeValue !== '(' ) {
return;
}
Object.keys( parametersToLink ).forEach( ( invocation ) => {
const parts = invocation.split( '.' );
let partIdx = parts.length - 1;
let curNode = node.previousElementSibling && node.previousElementSibling.previousElementSibling;
while ( partIdx >= 0 ) {
if ( !curNode || curNode.firstChild.nodeValue !== parts[ partIdx ] ) {
return;
}
if ( partIdx === 0 ) {
break;
}
const prev = curNode.previousElementSibling;
if ( !prev || prev.firstChild.nodeValue !== '.' ) {
return;
}
curNode = prev.previousElementSibling;
partIdx--;
}
const page = node.firstChild.nodeValue.slice( 1, -1 );
const condition = parametersToLink[ invocation ];
const title = mw.Title.newFromText( page );
if ( title && condition( title ) ) {
addLink( node, title );
}
} );
} );
} );
} );
} );