feat: allow tab name to be parsed

This is an experimental config and can lead to performance issue and unexpected behavior.

Closes: #35
This commit is contained in:
alistair3149 2024-04-24 15:13:08 -04:00
parent 6e67dd2abb
commit 7b1c319e24
No known key found for this signature in database
5 changed files with 66 additions and 27 deletions

View file

@ -52,6 +52,7 @@
"name": "ext.tabberNeue.legacy/config.json",
"config": {
"enableAnimation": "TabberNeueEnableAnimation",
"parseTabName": "TabberNeueParseTabName",
"updateLocationOnTabChange": "TabberNeueUpdateLocationOnTabChange"
}
}
@ -128,6 +129,11 @@
},
"config_prefix": "wg",
"config": {
"TabberNeueParseTabName": {
"value": false,
"description": "Parse tab name as wikitext. This can have a performance impact and cause unexpected behaviors.",
"public": true
},
"TabberNeueUseCodex": {
"value": false,
"description": "Use Codex to render Tabber. It is experimental and many features might not work as expected.",

View file

@ -36,6 +36,8 @@ class Tabber {
private static $useCodex = false;
private static $parseTabName = false;
/**
* Parser callback for <tabber> tag
*
@ -47,7 +49,9 @@ class Tabber {
* @return string HTML
*/
public static function parserHook( ?string $input, array $args, Parser $parser, PPFrame $frame ) {
self::$useCodex = MediaWikiServices::getInstance()->getMainConfig()->get( 'TabberNeueUseCodex' );
$config = MediaWikiServices::getInstance()->getMainConfig();
self::$parseTabName = $config->get( 'TabberNeueParseTabName' );
self::$useCodex = $config->get( 'TabberNeueUseCodex' );
$html = self::render( $input ?? '', $parser, $frame );
@ -119,31 +123,48 @@ class Tabber {
// Use array_pad to make sure at least 2 array values are always returned
[ $tabName, $tabBody ] = array_pad( explode( '=', $tab, 2 ), 2, '' );
// Use language converter to get variant title and also escape html
$tabName = $parser->getTargetLanguageConverter()->convertHtml( trim( $tabName ) );
$tabName = trim( $tabName );
$tabBody = trim( $tabBody );
// A nested tabber which should return json in codex
if ( self::$useCodex && strpos( $tabBody, '{{#tag:tabber' ) !== false ) {
self::$isNested = true;
$tabBody = $parser->recursiveTagParse( $tabBody, $frame );
self::$isNested = false;
// The outermost tabber that must be parsed fully in codex for correct json
} elseif ( self::$useCodex ) {
$tabBody = $parser->recursiveTagParseFully( $tabBody, $frame );
// Normal mode
} else {
$tabBody = $parser->recursiveTagParse( $tabBody, $frame );
// Codex mode
if ( self::$useCodex ) {
// Use language converter to get variant title and also escape html
$tabName = $parser->getTargetLanguageConverter()->convertHtml( $tabName );
// A nested tabber which should return json in codex
if ( strpos( $tabBody, '{{#tag:tabber' ) !== false ) {
self::$isNested = true;
$tabBody = $parser->recursiveTagParse( $tabBody, $frame );
self::$isNested = false;
// The outermost tabber that must be parsed fully in codex for correct json
} else {
$tabBody = $parser->recursiveTagParseFully( $tabBody, $frame );
}
if ( self::$isNested ) {
return json_encode( [
'label' => $tabName,
'content' => $tabBody
],
JSON_THROW_ON_ERROR
);
}
}
if ( self::$useCodex && self::$isNested ) {
return json_encode( [
'label' => $tabName,
'content' => $tabBody
],
JSON_THROW_ON_ERROR
);
// Legacy mode
if ( self::$parseTabName ) {
$tabName = $parser->recursiveTagParseFully( $tabName );
// Remove outer paragraph tags
if ( substr( $tabName, 0, 3 ) == '<p>' ) {
$tabName = substr( $tabName, 3 );
}
if ( substr( $tabName, -4 ) == '</p>' ) {
$tabName = substr( $tabName, 0, -4 );
}
$tabName = htmlentities( $tabName );
} else {
$tabName = $parser->getTargetLanguageConverter()->convertHtml( $tabName );
}
$tabBody = $parser->recursiveTagParse( $tabBody, $frame );
// If $tabBody does not have any HTML element (i.e. just a text node), wrap it in <p/>
if ( $tabBody && $tabBody[0] !== '<' ) {
@ -151,6 +172,6 @@ class Tabber {
}
return '<article class="tabber__panel" data-title="' . $tabName .
'">' . $tabBody . '</article>';
'">' . $tabBody . '</article>';
}
}

View file

@ -51,6 +51,7 @@ module.exports = exports = defineComponent( {
},
methods: {
isChildTabber() {
// eslint-disable-next-line es-x/no-array-prototype-includes
return Array.isArray( this.html ) || this.html.includes( '{"label":' );
},
parse() {

View file

@ -28,10 +28,22 @@ function initTabber( tabber, count ) {
fragment = new DocumentFragment(),
hashList = [];
const getTextFromHtml = function ( html ) {
const tmp = document.createElement( 'div' );
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText;
};
Array.prototype.forEach.call( tabPanels, function ( tabPanel ) {
const
title = tabPanel.getAttribute( 'data-title' ),
tab = document.createElement( 'a' );
const tab = document.createElement( 'a' );
let title = tabPanel.getAttribute( 'data-title' );
if ( config && config.parseTabName ) {
tab.innerHTML = title;
title = getTextFromHtml( title );
} else {
tab.innerText = title;
}
let hash = mw.util.escapeIdForAttribute( title ) + '-' + count;
@ -54,7 +66,6 @@ function initTabber( tabber, count ) {
tabPanel.setAttribute( 'aria-labelledby', 'tab-' + hash );
tabPanel.setAttribute( 'aria-hidden', true );
tab.innerText = title;
tab.classList.add( 'tabber__tab' );
tab.setAttribute( 'role', 'tab' );
tab.setAttribute( 'href', '#' + hash );

2
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "mediawiki-extensions-TabberNeue",
"name": "TabberNeue",
"lockfileVersion": 2,
"requires": true,
"packages": {