MediaWiki:Gadget-FDVEDiagramLink.js: Difference between revisions

Warn loudly if the VE classes are missing instead of failing silently
Restore "Change which diagram is shown" for template-wrapped embeds, where the page name is now an editable parameter
 
(2 intermediate revisions by the same user not shown)
Line 65: Line 65:
* upgrade reverts that patch.
* upgrade reverts that patch.
*
*
target.wt       "#display_diagram:Mermaid:Some_Page"
* Two shapes are recognised:
target.function "display_diagram"
{{Diagram|page=Mermaid:Foo}}    -> params.page.wt       (VE-editable)
*  {{#display_diagram:Mermaid:Foo}} -> target.wt after ':'  (legacy, still valid)
*
*
* @param {ve.dm.MWTransclusionNode} model
* @param {ve.dm.MWTransclusionNode} model
Line 72: Line 73:
*/
*/
function diagramPageFromModel( model ) {
function diagramPageFromModel( model ) {
var mwData, parts, i, target, wt, colon;
var info = diagramInfoFromModel( model );
return info ? info.page : null;
}
 
/**
* @param {ve.dm.MWTransclusionNode} model
* @return {Object|null} { page: string, isTemplate: boolean }, or null.
*  isTemplate distinguishes {{Diagram|page=}} - whose page name is an
*  editable parameter - from a bare {{#display_diagram:}}, whose page name
*  is part of the parser-function target and cannot be edited in VE.
*/
function diagramInfoFromModel( model ) {
var mwData, parts, i, part, target, params, wt, colon;


try {
try {
Line 85: Line 98:


for ( i = 0; i < parts.length; i++ ) {
for ( i = 0; i < parts.length; i++ ) {
target = parts[ i ] && parts[ i ].template && parts[ i ].template.target;
part = parts[ i ] && parts[ i ].template;
if ( !target || target.function !== 'display_diagram' ) {
target = part && part.target;
if ( !target ) {
continue;
continue;
}
}
wt = String( target.wt || '' );
 
colon = wt.indexOf( ':' );
// Shape 1 - {{Diagram|page=Mermaid:Foo}}. Parsoid gives a template part
if ( colon === -1 ) {
// a target.href and NO target.function, putting the page name in
// params.page. This is the shape VE can actually edit - parameters are
// editable, a parser-function target is not - which is the whole reason
// Template:Diagram exists.
if ( normaliseTemplateName( target ) === WRAPPER_TEMPLATE ) {
params = part.params;
wt = params && params.page && params.page.wt;
wt = ( wt === undefined || wt === null ) ? '' : String( wt ).trim();
if ( wt ) {
return { page: wt, isTemplate: true };
}
continue;
continue;
}
}
// Everything after "#display_diagram:" is the diagram page, which
 
// itself contains a namespace colon.
// Shape 2 - a bare {{#display_diagram:Mermaid:Foo}}. Still supported:
wt = wt.slice( colon + 1 ).trim();
// embeds predating the template use it, and it remains legal wikitext.
if ( wt ) {
if ( target.function === 'display_diagram' ) {
return wt;
wt = String( target.wt || '' );
colon = wt.indexOf( ':' );
if ( colon === -1 ) {
continue;
}
// Everything after "#display_diagram:" is the diagram page, which
// itself contains a namespace colon.
wt = wt.slice( colon + 1 ).trim();
if ( wt ) {
return { page: wt, isTemplate: false };
}
}
}
}
}
return null;
return null;
}
// Name of the wrapper template, normalised (see normaliseTemplateName).
var WRAPPER_TEMPLATE = 'diagram';
/**
* Reduce a transclusion target to a bare, comparable template name.
* Parsoid hrefs look like "./Template:Diagram"; hand-written wikitext may say
* "Template:Diagram", "diagram" or use underscores.
*
* @param {Object} target
* @return {string} lower-cased name with no namespace prefix
*/
function normaliseTemplateName( target ) {
return String( ( target && ( target.href || target.wt ) ) || '' )
.replace( /^\.\//, '' )
.replace( /^Template:/i, '' )
.replace( /_/g, ' ' )
.trim()
.toLowerCase();
}
}


Line 160: Line 214:
FDDiagramContextItem.prototype.renderBody = function () {
FDDiagramContextItem.prototype.renderBody = function () {
var item = this,
var item = this,
page = diagramPageFromModel( this.model ),
info = diagramInfoFromModel( this.model ),
url = this.getDiagramEditUrl(),
url = this.getDiagramEditUrl(),
$edit, $change;
$edit, $change;


if ( !page || !url ) {
if ( !info || !url ) {
// Should be unreachable - isCompatibleWith already required a page.
// Should be unreachable - isCompatibleWith already required a page.
FDDiagramContextItem.super.prototype.renderBody.call( this );
FDDiagramContextItem.super.prototype.renderBody.call( this );
Line 170: Line 224:
}
}


// A plain anchor is fine here: the context popup is outside the
// Plain anchors are fine here: the context popup lives OUTSIDE the
// contenteditable surface, so VE does not suppress the click.
// contenteditable surface, so VE does not suppress the click the way it
// does inside the node itself.
$edit = $( '<a>' )
$edit = $( '<a>' )
.addClass( 'fd-ve-diagramContextItem-edit' )
.addClass( 'fd-ve-diagramContextItem-edit' )
.attr( { target: '_blank', rel: 'noopener' } )
.attr( { target: '_blank', rel: 'noopener' } )
.text( 'Edit “' + page.replace( /_/g, ' ' ) + '” ↗' );
.text( 'Edit “' + info.page.replace( /_/g, ' ' ) + '” ↗' );
ve.setAttributeSafe( $edit[ 0 ], 'href', url, '#' );
ve.setAttributeSafe( $edit[ 0 ], 'href', url, '#' );


// Overriding onEditButtonClick costs the normal route to changing which
this.$body.empty().append( $( '<div>' ).append( $edit ) );
// diagram is embedded, so offer it explicitly.
$change = $( '<a>' )
.addClass( 'fd-ve-diagramContextItem-change' )
.attr( 'href', '#' )
.text( 'Change which diagram is shown' )
.on( 'click', function ( e ) {
e.preventDefault();
ve.ui.MWTransclusionContextItem.prototype.onEditButtonClick.call( item );
} );


this.$body.empty().append(
// Offered ONLY for {{Diagram|page=}}, where the page name is a real
$( '<div>' ).append( $edit ),
// template parameter that VE's transclusion dialog can edit. For a bare
$( '<div>' ).append( $change )
// {{#display_diagram:}} the same dialog opens empty - the page name is part
);
// of the parser-function target, and VE has no setter for a target - so
// showing this there would be a control that looks actionable and is not.
//
// This item replaces the stock transclusion context item (ModeledFactory
// keeps only the most specific class in an inheritance chain), so without
// this link there is no route at all to the parameter dialog.
if ( info.isTemplate ) {
$change = $( '<a>' )
.addClass( 'fd-ve-diagramContextItem-change' )
.attr( 'href', '#' )
.text( 'Change which diagram is shown' )
.on( 'click', function ( e ) {
e.preventDefault();
ve.ui.MWTransclusionContextItem.prototype.onEditButtonClick.call( item );
} );
this.$body.append( $( '<div>' ).append( $change ) );
}
};
};