diff --git a/lib/ext/Cite.js b/lib/ext/Cite.js index 2ae98a4e7..b28f0494b 100644 --- a/lib/ext/Cite.js +++ b/lib/ext/Cite.js @@ -96,6 +96,68 @@ Ref.prototype.tokenHandler = function(manager, pipelineOpts, refTok, cb) { }); }; +Ref.prototype.serialHandler = { + handle: Promise.method(function(node, state, wrapperUnmodified) { + return state.serializer.serializeExtensionStartTag(node, state) + .then(function(startTagSrc) { + var dataMw = DU.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 is inside another + // extension, most commonly inside a . + bodyElt = env.page.editedDoc.getElementById(dataMw.body.id); + } + if (bodyElt) { + html = bodyElt.innerHTML; + } 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'); + 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 + ']'; + } + 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! + } + return state.serializer.serializeHTML({ + env: state.env, + extName: dataMw.name, + }, html).then(function(src) { + return startTagSrc + src + ''; + }); + }); + }), +}; + /** * Helper class used by implementation */ @@ -325,7 +387,7 @@ References.prototype.extractRefFromNode = function(node, refsData, var doc = node.ownerDocument; var span = doc.createElement('span'); var content = dp.content; - var dataMW = Util.clone(DU.getDataMw(node)); + var dataMw = Util.clone(DU.getDataMw(node)); var body; if (dp.hasRefInRef) { @@ -352,8 +414,8 @@ References.prototype.extractRefFromNode = function(node, refsData, // data-mw will not be empty in scenarios where the is also templated. // In those cases, the transclusion markup takes precedence over the markup. // So, we aren't updating data-mw. - if (!Object.keys(dataMW).length) { - dataMW = { + if (!Object.keys(dataMw).length) { + dataMw = { 'name': 'ref', // Dont set body if this is a reused reference // like with empty content. @@ -384,7 +446,7 @@ References.prototype.extractRefFromNode = function(node, refsData, pi: dp.pi, }; DU.setDataParsoid(span, dataParsoid); - DU.setDataMw(span, dataMW); + DU.setDataMw(span, dataMw); // refLink is the link to the citation var refLink = doc.createElement('a'); @@ -409,7 +471,7 @@ References.prototype.extractRefFromNode = function(node, refsData, node.parentNode.insertBefore(span, node); } else { DU.storeDataParsoid(span, dataParsoid); - DU.storeDataMw(span, dataMW); + DU.storeDataMw(span, dataMw); refsInReferencesHTML.push(DU.serializeNode(span).str, '\n'); } @@ -427,8 +489,8 @@ References.prototype.insertReferencesIntoDOM = function(refsNode, refsData, refs var body = Util.extractExtBody("references", src).trim(); var refGroup = refsData.getRefGroup(group); - var dataMW = DU.getDataMw(refsNode); - if (!Object.keys(dataMW).length) { + var dataMw = DU.getDataMw(refsNode); + if (!Object.keys(dataMw).length) { var datamwBody; // We'll have to output data-mw.body.extsrc in // scenarios where original wikitext was of the form: @@ -440,7 +502,7 @@ References.prototype.insertReferencesIntoDOM = function(refsNode, refsData, refs 'html': refsInReferencesHTML.join(''), }; } - dataMW = { + dataMw = { 'name': 'references', 'body': datamwBody, 'attrs': { @@ -448,7 +510,7 @@ References.prototype.insertReferencesIntoDOM = function(refsNode, refsData, refs 'group': group || undefined, }, }; - DU.setDataMw(refsNode, dataMW); + DU.setDataMw(refsNode, dataMw); } // Remove all children from the references node @@ -501,15 +563,31 @@ References.prototype.insertMissingReferencesIntoDOM = function(env, refsData, no }); }; -var serialHandler = { +References.prototype.serialHandler = { handle: Promise.method(function(node, state, wrapperUnmodified) { var dp = DU.getDataParsoid(node); - var dataMW = DU.getDataMw(node); if (dp.autoInsertedRefs && state.rtTestMode) { // Eliminate auto-inserted noise in rt-testing return Promise.resolve(''); } else { - return state.serializer.defaultExtensionHandler(node, dataMW); + return state.serializer.serializeExtensionStartTag(node, state) + .then(function(startTagSrc) { + var dataMw = DU.getDataMw(node); + if (!dataMw.body) { + return startTagSrc; // We self-closed this already. + } else if (typeof dataMw.body.html === 'string') { + return state.serializer.serializeHTML({ + env: state.env, + extName: dataMw.name, + }, dataMw.body.html).then(function(src) { + return startTagSrc + src + ''; + }); + } else { + state.env.log('error', + 'References body unavailable for: ' + node.outerHTML); + return ''; // Drop it! + } + }); } }), before: function(node, otherNode, state) { @@ -631,10 +709,11 @@ var Cite = function() { { name: 'ref', tokenHandler: this.ref.tokenHandler.bind(this.ref), + serialHandler: this.ref.serialHandler, }, { name: 'references', tokenHandler: this.references.tokenHandler.bind(this.references), - serialHandler: serialHandler, + serialHandler: this.references.serialHandler, }, ], };