diff --git a/extension.json b/extension.json index 016d3869..5e447cb4 100644 --- a/extension.json +++ b/extension.json @@ -38,23 +38,6 @@ "pygments.wrapper.less" ] }, - "ext.pygments.view": { - "scripts": [ - "pygments.linenumbers.js", - "pygments.links.js", - "pygments.copy.js" - ], - "styles": [ - "pygments.copy.css" - ], - "messages": [ - "syntaxhighlight-button-copy", - "syntaxhighlight-button-copied" - ], - "dependencies": [ - "mediawiki.util" - ] - }, "ext.geshi.visualEditor": { "scripts": [ "ve-syntaxhighlight/ve.dm.MWSyntaxHighlightNode.js", @@ -96,7 +79,8 @@ "ParserFirstCallInit": "main", "ContentGetParserOutput": "main", "ApiFormatHighlight": "main", - "SoftwareInfo": "main" + "SoftwareInfo": "main", + "ResourceLoaderRegisterModules": "main" }, "HookHandlers": { "main": { diff --git a/includes/SyntaxHighlight.php b/includes/SyntaxHighlight.php index f396b5be..690c0c0c 100644 --- a/includes/SyntaxHighlight.php +++ b/includes/SyntaxHighlight.php @@ -32,6 +32,8 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Parser\Parser; use MediaWiki\Parser\ParserOutput; use MediaWiki\Parser\Sanitizer; +use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook; +use MediaWiki\ResourceLoader\ResourceLoader; use MediaWiki\Status\Status; use MediaWiki\Title\Title; use ParserOptions; @@ -45,6 +47,7 @@ use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI; class SyntaxHighlight extends ExtensionTagHandler implements ParserFirstCallInitHook, ContentGetParserOutputHook, + ResourceLoaderRegisterModulesHook, ApiFormatHighlightHook, SoftwareInfoHook { @@ -655,4 +658,32 @@ class SyntaxHighlight extends ExtensionTagHandler implements // pass } } + + /** + * Hook to register ext.pygments.view module. + * @param ResourceLoader $rl + */ + public function onResourceLoaderRegisterModules( ResourceLoader $rl ): void { + $rl->register( 'ext.pygments.view', [ + 'localBasePath' => MW_INSTALL_PATH . '/extensions/SyntaxHighlight_GeSHi/modules', + 'scripts' => array_merge( [ + 'pygments.linenumbers.js', + 'pygments.links.js', + 'pygments.copy.js' + ], ExtensionRegistry::getInstance()->isLoaded( 'Scribunto' ) ? [ + 'pygments.links.scribunto.js' + ] : [] ), + 'styles' => [ + 'pygments.copy.css' + ], + 'messages' => [ + 'syntaxhighlight-button-copy', + 'syntaxhighlight-button-copied' + ], + 'dependencies' => [ + 'mediawiki.util', + 'mediawiki.Title' + ] + ] ); + } } diff --git a/modules/pygments.links.scribunto.js b/modules/pygments.links.scribunto.js new file mode 100644 index 00000000..be047890 --- /dev/null +++ b/modules/pygments.links.scribunto.js @@ -0,0 +1,65 @@ +$( () => { + + const classes = { + singleQuoteString: 's1', doubleQuoteString: 's2' + }; + + function addLink( element, page ) { + const link = document.createElement( 'a' ); + link.href = mw.util.getUrl( page ); + // put text node from element inside link + const firstChild = element.firstChild; + if ( !( firstChild instanceof Text ) ) { + throw new TypeError( 'Expected Text object' ); + } + link.appendChild( firstChild ); + element.appendChild( link ); // put link inside syntax-highlighted string + } + + // List of functions whose parameters should be linked if they meet the given condition + const parametersToLink = { + require: ( title ) => title.getNamespaceId() === 828, + 'mw.loadData': ( title ) => title.getNamespaceId() === 828, + 'mw.loadJsonData': () => true + }; + + const stringNodes = Array.from( document.getElementsByClassName( classes.singleQuoteString ) ) + .concat( Array.from( document.getElementsByClassName( classes.doubleQuoteString ) ) ); + + stringNodes.forEach( ( node ) => { + if ( !node.nextElementSibling || !node.nextElementSibling.firstChild || + 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--; + } + 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() ); + } + } ); + } ); + +} );