MediaWiki:Gadget-aew-liststyles.js
Erscheinungsbild
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/*
* Gadget: aew-liststyles.js — Entwurf v0.7
* Ziel: VE-Tool „Liststyle –“ für UL (Aufzählung) – Anzeige per CSS-Klasse aew-ul--dash
* Phase 1: Anzeige live im VE und im View (CSS).
* Phase 2: Persistenz – Klasse via VE-Datenmodell setzen (Transaction), damit sie im View erhalten bleibt.
*/
(function ( mw, $ ) {
'use strict';
function debug(msg){ try{ mw.log('[aew-liststyles] ' + msg); }catch(e){} }
// ----------------------------------------------
// Helper: nächste UL/OL relativ zur Cursor-Position (DOM)
// ----------------------------------------------
function findNearestListInSurface( surface ) {
try {
var sel = window.getSelection && window.getSelection();
if ( !sel || !sel.anchorNode ) return null;
var node = sel.anchorNode.nodeType === 3 ? sel.anchorNode.parentNode : sel.anchorNode;
if ( node && node.closest ) {
var list = node.closest('ul,ol');
return list || null;
}
} catch (e) {}
return null;
}
// ----------------------------------------------
// Persistenz: Klasse im VE-Datenmodell setzen (List-Node)
// ----------------------------------------------
function applyDashClassPersistent( surface ) {
try {
var model = surface.getModel && surface.getModel();
if ( !model ) return false;
var dmDoc = model.getDocument && model.getDocument();
var sel = model.getSelection && model.getSelection();
if ( !dmDoc || !sel || !sel.getRange ) return false;
// Nächsten Listenknoten (DM) finden
var start = sel.getRange().start;
var node = dmDoc.getBranchNodeFromOffset( start );
var listNode = null;
while ( node && node.getType ) {
if ( node.getType() === 'list' ) { listNode = node; break; }
node = node.getParent && node.getParent();
}
if ( !listNode ) return false;
// Klassen via originalClasses pflegen (VE konserviert unbekannte Klassen darüber)
var attrs = ve.copy( listNode.getAttributes && listNode.getAttributes() ) || {};
var orig = ( attrs.originalClasses || '' ).trim();
var set = orig ? orig.split(/\s+/) : [];
var idx = set.indexOf('aew-ul--dash');
if ( idx === -1 ) { set.push('aew-ul--dash'); } else { set.splice(idx, 1); }
attrs.originalClasses = set.length ? set.join(' ') : null;
var tx = ve.dm.TransactionBuilder.static.newFromAttributeChanges(
dmDoc,
listNode.getOuterRange().start,
attrs
);
surface.change( tx );
return true;
} catch (e) {
debug('persist fail: ' + e);
return false;
}
}
// ----------------------------------------------
// Tool-Definition: „Liststyle –“
// ----------------------------------------------
function registerTool() {
if ( !window.ve || !window.OO ) { return; }
if ( ve.ui.toolFactory.lookup('aewListStyleDash') ) { return; }
function AewListStyleDashTool() { AewListStyleDashTool.super.apply(this, arguments); }
OO.inheritClass( AewListStyleDashTool, ve.ui.Tool );
AewListStyleDashTool.static.name = 'aewListStyleDash';
AewListStyleDashTool.static.group = 'structure';
AewListStyleDashTool.static.icon = 'listBullet';
AewListStyleDashTool.static.title = 'Liststyle –';
AewListStyleDashTool.static.autoAddToCatchall = true;
AewListStyleDashTool.prototype.onSelect = function () {
var surface = ve.init.target && ve.init.target.getSurface && ve.init.target.getSurface();
if ( !surface ) { this.setActive(false); return; }
// 1) Persistente Änderung im VE-Modell versuchen
var ok = applyDashClassPersistent( surface );
// 2) DOM-Fallback (nur falls nötig, für sofortige Sichtbarkeit)
if ( !ok ) {
var listNode = findNearestListInSurface( surface );
if ( !listNode ) { mw.notify('Bitte Cursor in eine Liste setzen.', { type:'warn' }); this.setActive(false); return; }
if ( listNode.nodeName.toLowerCase() !== 'ul' ) { mw.notify('„–“ gilt für Aufzählungslisten (UL).', { type:'warn' }); this.setActive(false); return; }
listNode.classList.toggle('aew-ul--dash');
}
mw.notify('Liststyle „–“ ' + ( ok ? 'persistiert.' : 'gesetzt (nur sichtbar).' ), { type: ok ? 'info' : 'warn' } );
this.setActive(false);
};
AewListStyleDashTool.prototype.onUpdateState = function () {
var surface = ve.init.target && ve.init.target.getSurface && ve.init.target.getSurface();
var enabled = false;
if ( surface ) {
// Primär: VE-DM (list/listItem, style === 'bullet')
try {
var model = surface.getModel();
var sel = model && model.getSelection && model.getSelection();
if ( sel && sel.getRange ) {
var dmDoc = model.getDocument();
var start = sel.getRange().start;
var n = dmDoc.getBranchNodeFromOffset( start );
while ( n && n.getType ) {
var t = n.getType && n.getType();
if ( t === 'list' ) {
var st = n.getAttribute ? n.getAttribute('style') : ( n.getAttributes && n.getAttributes().style );
enabled = ( st === 'bullet' ); break;
}
if ( t === 'listItem' ) {
var p = n.getParent && n.getParent();
if ( p && p.getType && p.getType() === 'list' ) {
var st2 = p.getAttribute ? p.getAttribute('style') : ( p.getAttributes && p.getAttributes().style );
enabled = ( st2 === 'bullet' ); break;
}
}
n = n.getParent ? n.getParent() : null;
}
}
} catch (e) {}
// Sekundär: DOM-Fallback
if ( !enabled ) {
try {
var selDom = window.getSelection && window.getSelection();
if ( selDom && selDom.anchorNode ) {
var dn = selDom.anchorNode.nodeType === 3 ? selDom.anchorNode.parentNode : selDom.anchorNode;
enabled = !!( dn && dn.closest && dn.closest('ul') );
}
} catch (e2) {}
}
}
this.setDisabled( !enabled );
this.setActive( false );
};
ve.ui.toolFactory.register( AewListStyleDashTool );
debug('Tool registriert');
}
// ----------------------------------------------
// Toolbar-Gruppe ggf. nachträglich hinzufügen (wenn Gadget spät lädt)
// ----------------------------------------------
function addToolbarGroupIfNeeded() {
if ( !window.ve || !ve.init || !ve.init.target ) { return; }
var toolbar = ve.init.target.getToolbar && ve.init.target.getToolbar();
if ( !toolbar ) { return; }
try {
var group = new OO.ui.ListToolGroup( toolbar, { title: 'Listenstil', include: [ 'aewListStyleDash' ] } );
toolbar.addItems( [ group ] );
toolbar.emit( 'updateState' );
debug('ToolGroup hinzugefügt');
} catch (e) {}
}
// Feature-Flag: ::marker-Unterstützung
function setMarkerSupportClass(){
try{
if ( window.CSS && CSS.supports && CSS.supports('selector(::marker)') ) {
document.documentElement.classList.add('aew-supports-marker');
} else {
document.documentElement.classList.remove('aew-supports-marker');
}
}catch(e){}
}
// 1) VE-Plugin-Mechanismus: vor Toolbar-Aufbau registrieren, wenn möglich
mw.hook('ve.loadModules').add( function ( addPlugin ) {
addPlugin( function () { registerTool(); } );
} );
// 2) Lesemodus + VE-Start
mw.hook('wikipage.content').add( setMarkerSupportClass );
mw.hook('ve.activationComplete').add( function () {
setMarkerSupportClass();
registerTool();
addToolbarGroupIfNeeded();
} );
})( window.mediaWiki, window.jQuery );