mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-12-03 11:06:17 +00:00
b84b71af22
Passing srcOffsets which don't actually correspond to actual regions of the source wikitext cause problems in the token offset conversion code. Instead, parse the wikitext as itself, then adjust the TSRs in the DOM tree. Since Gallery isn't ported to PHP (yet), update the automatically-generated Gallery/index.php. The newly-added ContentUtils::shiftDSR() was ported, however. Change-Id: I28f3d3398930733ae2bcf9759e49c45f93bc7190
132 lines
4.6 KiB
JavaScript
132 lines
4.6 KiB
JavaScript
'use strict';
|
|
|
|
const ParsoidExtApi = module.parent.parent.require('./extapi.js').versionCheck('^0.10.0');
|
|
const { ContentUtils, DOMDataUtils, WTUtils, Promise } = ParsoidExtApi;
|
|
|
|
/**
|
|
* Simple token transform version of the Ref extension tag.
|
|
*
|
|
* @class
|
|
*/
|
|
class Ref {
|
|
static toDOM(state, content, args) {
|
|
// Drop nested refs entirely, unless we've explicitly allowed them
|
|
if (state.parseContext.extTag === 'ref' &&
|
|
!(state.parseContext.extTagOpts && state.parseContext.extTagOpts.allowNestedRef)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
// The one supported case for nested refs is from the {{#tag:ref}} parser
|
|
// function. However, we're overly permissive here since we can't
|
|
// distinguish when that's nested in another template.
|
|
// The php preprocessor did our expansion.
|
|
const allowNestedRef = state.parseContext.inTemplate && state.parseContext.extTag !== 'ref';
|
|
|
|
return ParsoidExtApi.parseTokenContentsToDOM(state, args, '', content, {
|
|
// NOTE: sup's content model requires it only contain phrasing
|
|
// content, not flow content. However, since we are building an
|
|
// in-memory DOM which is simply a tree data structure, we can
|
|
// nest flow content in a <sup> tag.
|
|
wrapperTag: 'sup',
|
|
pipelineOpts: {
|
|
extTag: 'ref',
|
|
extTagOpts: {
|
|
allowNestedRef: !!allowNestedRef,
|
|
},
|
|
inTemplate: state.parseContext.inTemplate,
|
|
// FIXME: One-off PHP parser state leak.
|
|
// This needs a better solution.
|
|
inPHPBlock: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
static lintHandler(ref, env, tplInfo, domLinter) {
|
|
// Don't lint the content of ref in ref, since it can lead to cycles
|
|
// using named refs
|
|
if (WTUtils.fromExtensionContent(ref, 'references')) { return ref.nextSibling; }
|
|
|
|
var linkBackId = ref.firstChild.getAttribute('href').replace(/[^#]*#/, '');
|
|
var refNode = ref.ownerDocument.getElementById(linkBackId);
|
|
if (refNode) {
|
|
// Ex: Buggy input wikitext without ref content
|
|
domLinter(refNode.lastChild, env, tplInfo.isTemplated ? tplInfo : null);
|
|
}
|
|
return ref.nextSibling;
|
|
}
|
|
}
|
|
|
|
Ref.serialHandler = {
|
|
handle: Promise.async(function *(node, state, wrapperUnmodified) {
|
|
var startTagSrc = yield state.serializer.serializeExtensionStartTag(node, state);
|
|
var dataMw = DOMDataUtils.getDataMw(node);
|
|
var env = state.env;
|
|
var html;
|
|
if (!dataMw.body) {
|
|
return startTagSrc; // We self-closed this already.
|
|
} else if (typeof dataMw.body.html === 'string') {
|
|
// First look for the extension's content in data-mw.body.html
|
|
html = dataMw.body.html;
|
|
} else if (typeof dataMw.body.id === 'string') {
|
|
// If the body isn't contained in data-mw.body.html, look if
|
|
// there's an element pointed to by body.id.
|
|
var bodyElt = node.ownerDocument.getElementById(dataMw.body.id);
|
|
if (!bodyElt && env.page.editedDoc) {
|
|
// Try to get to it from the main page.
|
|
// This can happen when the <ref> is inside another
|
|
// extension, most commonly inside a <references>.
|
|
// The recursive call to serializeDOM puts us inside
|
|
// inside a new document.
|
|
bodyElt = env.page.editedDoc.getElementById(dataMw.body.id);
|
|
}
|
|
if (bodyElt) {
|
|
// n.b. this is going to drop any diff markers but since
|
|
// the dom differ doesn't traverse into extension content
|
|
// none should exist anyways.
|
|
DOMDataUtils.visitAndStoreDataAttribs(bodyElt);
|
|
html = ContentUtils.toXML(bodyElt, { innerXML: true });
|
|
DOMDataUtils.visitAndLoadDataAttribs(bodyElt);
|
|
} else {
|
|
// Some extra debugging for VisualEditor
|
|
var extraDebug = '';
|
|
var firstA = node.querySelector('a[href]');
|
|
if (firstA && /^#/.test(firstA.getAttribute('href') || '')) {
|
|
var href = firstA.getAttribute('href') || '';
|
|
try {
|
|
var ref = node.ownerDocument.querySelector(href);
|
|
if (ref) {
|
|
extraDebug += ' [own doc: ' + ref.outerHTML + ']';
|
|
}
|
|
ref = env.page.editedDoc.querySelector(href);
|
|
if (ref) {
|
|
extraDebug += ' [main doc: ' + ref.outerHTML + ']';
|
|
}
|
|
} catch (e) { } // eslint-disable-line
|
|
if (!extraDebug) {
|
|
extraDebug = ' [reference ' + href + ' not found]';
|
|
}
|
|
}
|
|
env.log('error/' + dataMw.name,
|
|
'extension src id ' + dataMw.body.id +
|
|
' points to non-existent element for:', node.outerHTML,
|
|
'. More debug info: ', extraDebug);
|
|
return ''; // Drop it!
|
|
}
|
|
} else {
|
|
env.log('error', 'Ref body unavailable for: ' + node.outerHTML);
|
|
return ''; // Drop it!
|
|
}
|
|
var src = yield state.serializer.serializeHTML({
|
|
env: state.env,
|
|
extName: dataMw.name,
|
|
// FIXME: One-off PHP parser state leak.
|
|
// This needs a better solution.
|
|
inPHPBlock: true,
|
|
}, html);
|
|
return startTagSrc + src + '</' + dataMw.name + '>';
|
|
}),
|
|
};
|
|
|
|
module.exports = Ref;
|