Twinkle mobile test adapter v3: diagnostic banner |
Twinkle mobile test adapter v4: pre-create p-cactions in overflow menu |
||
| Line 1: | Line 1: | ||
// Twinkle mobile TEST adapter | // Twinkle mobile TEST adapter v4 - personal test only | ||
// Page: User:Bharatwiki Socrates/minerva.js (Minerva skin only) | // Page: User:Bharatwiki Socrates/minerva.js (Minerva skin only) | ||
// | // Root cause (found in Twinkle's own code + v3 census): on any skin | ||
// | // other than Vector/Timeless, Twinkle does NOT build its own menu. | ||
// | // It sends every module link to a portlet called "p-cactions", which | ||
// | // Minerva does not render, so the links go nowhere. | ||
// | // v4 fix: BEFORE loading Twinkle, create a real #p-cactions element | ||
// | // inside Minerva's overflow (More) menu list (ul#p-tb), as a "Twinkle" | ||
// item with a collapsible sub-list. Twinkle's links then land in that | |||
// sub-list, where they can be seen and tapped. | |||
// All user-facing strings in English. | |||
( function () { | ( function () { | ||
| Line 16: | Line 19: | ||
} | } | ||
window.twinkleMobileTestLoaded = true; | window.twinkleMobileTestLoaded = true; | ||
var indexPhp = mw.util.wikiScript( 'index' ); | var indexPhp = mw.util.wikiScript( 'index' ); | ||
| Line 63: | Line 40: | ||
'.morebits-dialog { max-width: 96vw !important; }' + | '.morebits-dialog { max-width: 96vw !important; }' + | ||
'.morebits-dialog-content { overflow-x: auto; }' + | '.morebits-dialog-content { overflow-x: auto; }' + | ||
'.select2-container { max-width: 90vw; }' | '.select2-container { max-width: 90vw; }' + | ||
'#p-cactions .twinkle-mobile-sub { list-style: none;' + | |||
' margin: 0; padding: 0 0 0 16px; }' + | |||
'#p-cactions .twinkle-mobile-sub li a { display: block;' + | |||
' padding: 10px 8px; color: #202122; text-decoration: none; }' + | |||
'#twinkle-mobile-panel { position: fixed; top: 56px; left: 2vw;' + | |||
' width: 96vw; max-height: 70vh; overflow-y: auto; z-index: 999;' + | |||
' background: #fff; border: 1px solid #a2a9b1; border-radius: 4px;' + | |||
' box-shadow: 0 4px 16px rgba(0,0,0,0.3); padding: 6px 0; }' + | |||
'#twinkle-mobile-panel ul { list-style: none; margin: 0;' + | |||
' padding: 0; }' + | |||
'#twinkle-mobile-panel li a { display: block;' + | |||
' padding: 10px 16px; color: #202122; text-decoration: none; }' | |||
); | ); | ||
rawCss( 'MediaWiki:Gadget-Twinkle.css' ); | rawCss( 'MediaWiki:Gadget-Twinkle.css' ); | ||
| Line 69: | Line 58: | ||
rawCss( 'MediaWiki:Gadget-select2.min.css' ); | rawCss( 'MediaWiki:Gadget-select2.min.css' ); | ||
function | function createPortlet() { | ||
if ( | if ( document.getElementById( 'p-cactions' ) ) { | ||
return ' | return true; | ||
} | |||
var overflow = document.querySelector( 'ul#p-tb' ) || | |||
document.querySelector( '.page-actions-overflow-list' ); | |||
if ( overflow ) { | |||
var li = document.createElement( 'li' ); | |||
li.className = 'toggle-list-item'; | |||
li.id = 'p-cactions'; | |||
var head = document.createElement( 'a' ); | |||
head.className = 'toggle-list-item__anchor'; | |||
head.href = '#'; | |||
var label = document.createElement( 'span' ); | |||
label.className = 'toggle-list-item__label'; | |||
label.textContent = 'Twinkle'; | |||
head.appendChild( label ); | |||
var sub = document.createElement( 'ul' ); | |||
sub.className = 'twinkle-mobile-sub'; | |||
sub.style.display = 'none'; | |||
head.addEventListener( 'click', function ( e ) { | |||
e.preventDefault(); | |||
sub.style.display = | |||
sub.style.display === 'none' ? 'block' : 'none'; | |||
} ); | |||
li.appendChild( head ); | |||
li.appendChild( sub ); | |||
overflow.appendChild( li ); | |||
return true; | |||
} | } | ||
var | // Fallback for pages without an overflow menu: | ||
var | // a panel that Twinkle links can fill, opened by a TW button. | ||
var panel = document.createElement( 'div' ); | |||
panel.id = 'twinkle-mobile-panel'; | |||
panel.style.display = 'none'; | |||
' | var wrap = document.createElement( 'div' ); | ||
' | wrap.id = 'p-cactions'; | ||
var list = document.createElement( 'ul' ); | |||
wrap.appendChild( list ); | |||
panel.appendChild( wrap ); | |||
document.body.appendChild( panel ); | |||
var btn = document.createElement( 'button' ); | |||
btn.textContent = 'TW'; | |||
btn.style.cssText = 'position:fixed;right:12px;bottom:64px;' + | |||
'z-index:998;padding:10px 14px;border-radius:20px;' + | |||
'background:#202122;color:#fff;border:0;'; | |||
btn.addEventListener( 'click', function () { | |||
panel.style.display = | |||
panel.style.display === 'none' ? 'block' : 'none'; | |||
} ); | |||
document.body.appendChild( btn ); | |||
return true; | |||
} | } | ||
function | |||
function checkResult( tries ) { | |||
var landed = document.querySelectorAll( | |||
'#p-cactions li[id^="twinkle-"]' ).length; | |||
if ( landed ) { | |||
mw.notify( 'Twinkle is ready: open the More menu and tap ' + | |||
'Twinkle (' + landed + ' options)', { type: 'info' } ); | |||
return; | |||
} | } | ||
if ( | if ( tries > 0 ) { | ||
setTimeout( function () { checkResult( tries - 1 ); }, 2000 ); | |||
return; | |||
} | } | ||
mw.notify( 'Twinkle loaded but its links did not arrive (v4)', | |||
{ type: 'error' } ); | |||
} | |||
function start() { | |||
createPortlet(); | |||
mw.loader.using( [ | |||
'mediawiki.util', 'mediawiki.api', 'mediawiki.storage', | |||
'mediawiki.Title', 'mediawiki.user', 'jquery.ui' | |||
] ).then( function () { | |||
return loadScript( 'MediaWiki:Gadget-select2.min.js' ); | |||
} ).then( function () { | |||
return loadScript( 'MediaWiki:Gadget-morebits.js' ); | |||
} ).then( function () { | |||
return loadScript( 'MediaWiki:Gadget-Twinkle.js' ); | |||
} ).then( function () { | |||
checkResult( 10 ); | |||
} ).catch( function ( err ) { | |||
mw.notify( 'Twinkle mobile test load failed: ' + err, | |||
{ type: 'error' } ); | |||
} ); | |||
} | } | ||
if ( document.readyState === 'loading' ) { | |||
document.addEventListener( 'DOMContentLoaded', start ); | |||
} else { | |||
start(); | |||
} | |||
} | |||
} | |||
}() ); | }() ); | ||
Revision as of 05:10, 1 November 2026
// Twinkle mobile TEST adapter v4 - personal test only
// Page: User:Bharatwiki Socrates/minerva.js (Minerva skin only)
// Root cause (found in Twinkle's own code + v3 census): on any skin
// other than Vector/Timeless, Twinkle does NOT build its own menu.
// It sends every module link to a portlet called "p-cactions", which
// Minerva does not render, so the links go nowhere.
// v4 fix: BEFORE loading Twinkle, create a real #p-cactions element
// inside Minerva's overflow (More) menu list (ul#p-tb), as a "Twinkle"
// item with a collapsible sub-list. Twinkle's links then land in that
// sub-list, where they can be seen and tapped.
// All user-facing strings in English.
( function () {
if ( mw.config.get( 'skin' ) !== 'minerva' ) {
return;
}
if ( window.twinkleMobileTestLoaded ) {
return;
}
window.twinkleMobileTestLoaded = true;
var indexPhp = mw.util.wikiScript( 'index' );
function rawUrl( title ) {
return indexPhp + '?title=' + encodeURIComponent( title ) +
'&action=raw&ctype=text/javascript';
}
function rawCss( title ) {
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.href = indexPhp + '?title=' + encodeURIComponent( title ) +
'&action=raw&ctype=text/css';
document.head.appendChild( link );
}
function loadScript( title ) {
return $.getScript( rawUrl( title ) );
}
mw.util.addCSS(
'.ui-dialog { max-width: 96vw !important; left: 2vw !important; }' +
'.morebits-dialog { max-width: 96vw !important; }' +
'.morebits-dialog-content { overflow-x: auto; }' +
'.select2-container { max-width: 90vw; }' +
'#p-cactions .twinkle-mobile-sub { list-style: none;' +
' margin: 0; padding: 0 0 0 16px; }' +
'#p-cactions .twinkle-mobile-sub li a { display: block;' +
' padding: 10px 8px; color: #202122; text-decoration: none; }' +
'#twinkle-mobile-panel { position: fixed; top: 56px; left: 2vw;' +
' width: 96vw; max-height: 70vh; overflow-y: auto; z-index: 999;' +
' background: #fff; border: 1px solid #a2a9b1; border-radius: 4px;' +
' box-shadow: 0 4px 16px rgba(0,0,0,0.3); padding: 6px 0; }' +
'#twinkle-mobile-panel ul { list-style: none; margin: 0;' +
' padding: 0; }' +
'#twinkle-mobile-panel li a { display: block;' +
' padding: 10px 16px; color: #202122; text-decoration: none; }'
);
rawCss( 'MediaWiki:Gadget-Twinkle.css' );
rawCss( 'MediaWiki:Gadget-morebits.css' );
rawCss( 'MediaWiki:Gadget-select2.min.css' );
function createPortlet() {
if ( document.getElementById( 'p-cactions' ) ) {
return true;
}
var overflow = document.querySelector( 'ul#p-tb' ) ||
document.querySelector( '.page-actions-overflow-list' );
if ( overflow ) {
var li = document.createElement( 'li' );
li.className = 'toggle-list-item';
li.id = 'p-cactions';
var head = document.createElement( 'a' );
head.className = 'toggle-list-item__anchor';
head.href = '#';
var label = document.createElement( 'span' );
label.className = 'toggle-list-item__label';
label.textContent = 'Twinkle';
head.appendChild( label );
var sub = document.createElement( 'ul' );
sub.className = 'twinkle-mobile-sub';
sub.style.display = 'none';
head.addEventListener( 'click', function ( e ) {
e.preventDefault();
sub.style.display =
sub.style.display === 'none' ? 'block' : 'none';
} );
li.appendChild( head );
li.appendChild( sub );
overflow.appendChild( li );
return true;
}
// Fallback for pages without an overflow menu:
// a panel that Twinkle links can fill, opened by a TW button.
var panel = document.createElement( 'div' );
panel.id = 'twinkle-mobile-panel';
panel.style.display = 'none';
var wrap = document.createElement( 'div' );
wrap.id = 'p-cactions';
var list = document.createElement( 'ul' );
wrap.appendChild( list );
panel.appendChild( wrap );
document.body.appendChild( panel );
var btn = document.createElement( 'button' );
btn.textContent = 'TW';
btn.style.cssText = 'position:fixed;right:12px;bottom:64px;' +
'z-index:998;padding:10px 14px;border-radius:20px;' +
'background:#202122;color:#fff;border:0;';
btn.addEventListener( 'click', function () {
panel.style.display =
panel.style.display === 'none' ? 'block' : 'none';
} );
document.body.appendChild( btn );
return true;
}
function checkResult( tries ) {
var landed = document.querySelectorAll(
'#p-cactions li[id^="twinkle-"]' ).length;
if ( landed ) {
mw.notify( 'Twinkle is ready: open the More menu and tap ' +
'Twinkle (' + landed + ' options)', { type: 'info' } );
return;
}
if ( tries > 0 ) {
setTimeout( function () { checkResult( tries - 1 ); }, 2000 );
return;
}
mw.notify( 'Twinkle loaded but its links did not arrive (v4)',
{ type: 'error' } );
}
function start() {
createPortlet();
mw.loader.using( [
'mediawiki.util', 'mediawiki.api', 'mediawiki.storage',
'mediawiki.Title', 'mediawiki.user', 'jquery.ui'
] ).then( function () {
return loadScript( 'MediaWiki:Gadget-select2.min.js' );
} ).then( function () {
return loadScript( 'MediaWiki:Gadget-morebits.js' );
} ).then( function () {
return loadScript( 'MediaWiki:Gadget-Twinkle.js' );
} ).then( function () {
checkResult( 10 );
} ).catch( function ( err ) {
mw.notify( 'Twinkle mobile test load failed: ' + err,
{ type: 'error' } );
} );
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', start );
} else {
start();
}
}() );