mediawiki-skins-Vector/resources/skins.vector.es6/linkHijack.js

72 lines
1.9 KiB
JavaScript
Raw Normal View History

/**
* Appends a query param to the `href` attribute of any clicked anchor element
* or anchor element that is the parent of a clicked HTMLElement. Links that
* lead to different origins or have the same pathname and have a hash fragment
* are ignored.
*
* @param {string} key
* @param {string} value
* @return {Function} A cleanup function is returned that
* removes any event listeners that were added.
*/
function linkHijack( key, value ) {
/**
* @param {MouseEvent} e
*/
function handleClick( e ) {
if ( !e.target || !( e.target instanceof HTMLElement ) ) {
return;
}
const anchor = e.target.closest( 'a' );
if ( !anchor ) {
return;
}
let locationUrl;
let oldUrl;
try {
// eslint-disable-next-line compat/compat
locationUrl = new URL( location.href );
// eslint-disable-next-line compat/compat
oldUrl = new URL( anchor.href );
} catch ( error ) {
// A TypeError may be thrown for invalid URLs. In that case, return
// gracefully.
return;
}
if (
locationUrl.origin !== oldUrl.origin ||
( locationUrl.pathname === oldUrl.pathname && oldUrl.hash )
) {
// Return early if link leads to host outside the current one or if the
// url contains a pathname that is the same as the current one and also
// has a hash fragment (e.g. this occurs with links in the TOC and we
// don't want to trigger a refresh of the page by appending a query
// param).
return;
}
// eslint-disable-next-line compat/compat
const params = new URLSearchParams( oldUrl.search );
if ( !params.has( key ) ) {
params.append( key, value );
}
// eslint-disable-next-line compat/compat
const newUrl = new URL( `${oldUrl.origin}${oldUrl.pathname}?${params}${oldUrl.hash}` );
anchor.setAttribute( 'href', newUrl.toString() );
}
document.body.addEventListener( 'click', handleClick );
return () => {
document.body.removeEventListener( 'click', handleClick );
};
}
module.exports = linkHijack;