From 7380a861f0eecbf7a93c262a6ff137ccd73043b2 Mon Sep 17 00:00:00 2001 From: mareikeheuer Date: Wed, 26 Jun 2024 17:05:56 +0200 Subject: [PATCH] Display reference preview with sub reference Identify both parent and child reference (reference and extended reference) and display them in the reference preview popup. This is a very simple basic implementation so at least avoid that the sub referenc content is shown without context. Bug: T239228 Change-Id: I857e1be32db9fd72073015cbba1b1bd37e32085f --- .../createReferenceGateway.js | 56 +++++++++++++++---- .../referencePreview.less | 4 ++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/modules/ext.cite.referencePreviews/createReferenceGateway.js b/modules/ext.cite.referencePreviews/createReferenceGateway.js index ad6ce7dcf..8d165c26b 100644 --- a/modules/ext.cite.referencePreviews/createReferenceGateway.js +++ b/modules/ext.cite.referencePreviews/createReferenceGateway.js @@ -11,9 +11,9 @@ module.exports = function createReferenceGateway() { /** * @param {string} id - * @return {HTMLElement} + * @return {HTMLElement|null} */ - function scrapeReferenceText( id ) { + function findReferenceTextElement( id ) { const idSelector = `#${ CSS.escape( id ) }`; /** @@ -24,19 +24,49 @@ module.exports = function createReferenceGateway() { return document.querySelector( `${ idSelector } .mw-reference-text, ${ idSelector } .reference-text` ); } + /** + * @param {HTMLElement} el + * @return {HTMLElement|null} + */ + function findParentReferenceTextElement( el ) { + // This finds either the inner
    , or the outer + //
      + const ol = el.closest( 'ol' ); + + return ol && ol.classList.contains( 'mw-extended-references' ) ? + ol.parentElement.querySelector( '.mw-reference-text, .reference-text' ) : + null; + } + + /** + * @param {HTMLElement} referenceElement + * @param {(HTMLElement|null)} parentElement + * @return {string} + */ + function scrapeReferenceText( referenceElement, parentElement ) { + if ( !parentElement ) { + return referenceElement.innerHTML; + } + + return ` +
      ${ parentElement.innerHTML }
      +
      ${ referenceElement.innerHTML }
      + `; + } + /** * Attempts to find a single reference type identifier, limited to a list of known types. * - When a `class="…"` attribute mentions multiple known types, the last one is used, following * CSS semantics. * - When there are multiple tags, the first with a known type is used. * - * @param {HTMLElement} referenceText + * @param {HTMLElement} referenceElement * @return {string|null} */ - function scrapeReferenceType( referenceText ) { + function scrapeReferenceType( referenceElement ) { const KNOWN_TYPES = [ 'book', 'journal', 'news', 'note', 'web' ]; let type = null; - const citeTags = referenceText.querySelectorAll( 'cite[class]' ); + const citeTags = referenceElement.querySelectorAll( 'cite[class]' ); Array.prototype.forEach.call( citeTags, ( element ) => { // don't need to keep scanning if one is found. if ( type ) { @@ -60,24 +90,26 @@ module.exports = function createReferenceGateway() { */ function fetchPreviewForTitle( title, el ) { // Need to encode the fragment again as mw.Title returns it as decoded text - const id = title.getFragment().replace( / /g, '_' ), - referenceNode = scrapeReferenceText( id ); + const id = title.getFragment().replace( / /g, '_' ); + const referenceTextElement = findReferenceTextElement( id ); - if ( !referenceNode || + if ( !referenceTextElement || // Skip references that don't contain anything but whitespace, e.g. a single   - ( !referenceNode.textContent.trim() && !referenceNode.children.length ) + ( !referenceTextElement.textContent.trim() && !referenceTextElement.children.length ) ) { return Promise.reject( - // Required to set `showNullPreview` to false and not open an error popup + // Required to set showNullPreview to false and not open an error popup { textStatus: 'abort', textContext: 'Footnote not found or empty', xhr: { readyState: 0 } } ); } + const referenceParentTextElement = findParentReferenceTextElement( referenceTextElement ); + const model = { url: `#${ id }`, - extract: referenceNode.innerHTML, + extract: scrapeReferenceText( referenceTextElement, referenceParentTextElement ), type: TYPE_REFERENCE, - referenceType: scrapeReferenceType( referenceNode ), + referenceType: scrapeReferenceType( referenceParentTextElement || referenceTextElement ), // Note: Even the top-most HTMLHtmlElement is guaranteed to have a parent. sourceElementId: el.parentNode.id }; diff --git a/modules/ext.cite.referencePreviews/referencePreview.less b/modules/ext.cite.referencePreviews/referencePreview.less index 292b010f7..3853a562e 100644 --- a/modules/ext.cite.referencePreviews/referencePreview.less +++ b/modules/ext.cite.referencePreviews/referencePreview.less @@ -86,4 +86,8 @@ margin: 1em 0; position: relative; } + + .mw-reference-previews-parent { + margin-bottom: 1em; + } }