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. | ||
* | * | ||
* | * Two shapes are recognised: | ||
* {{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++ ) { | ||
part = parts[ i ] && parts[ i ].template; | |||
if ( !target | target = part && part.target; | ||
if ( !target ) { | |||
continue; | continue; | ||
} | } | ||
// Shape 1 - {{Diagram|page=Mermaid:Foo}}. Parsoid gives a template part | |||
// 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 | |||
// Shape 2 - a bare {{#display_diagram:Mermaid:Foo}}. Still supported: | |||
// embeds predating the template use it, and it remains legal wikitext. | |||
if ( target.function === 'display_diagram' ) { | |||
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, | ||
info = diagramInfoFromModel( this.model ), | |||
url = this.getDiagramEditUrl(), | url = this.getDiagramEditUrl(), | ||
$edit, $change; | $edit, $change; | ||
if ( ! | 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: | ||
} | } | ||
// | // 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, '#' ); | ||
this.$body.empty().append( $( '<div>' ).append( $edit ) ); | |||
this. | // Offered ONLY for {{Diagram|page=}}, where the page name is a real | ||
$( '< | // 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 ) ); | |||
} | |||
}; | }; | ||