diff --git a/public/js/assistant/citations.js b/public/js/assistant/citations.js index 1d873979..9d806432 100644 --- a/public/js/assistant/citations.js +++ b/public/js/assistant/citations.js @@ -88,6 +88,20 @@ export function wrapTables(html) { .replace(/<\/table>/g, ''); } +// A citation marker names a source by its `number`, which dedupeSources assigns +// server-side. Resolving it by array position happens to work today because the +// two agree, but any future filtering or reordering of the list between the +// server and here would silently point citations at the wrong source. Matching +// on the number cannot drift. +function sourceByNumber(sources, n) { + var list = sources || []; + for (var i = 0; i < list.length; i++) { + if (list[i] && Number(list[i].number) === Number(n)) return list[i]; + } + // Sources predating the numbering, or a caller passing a bare list. + return list[n - 1]; +} + export function renderCitationLinks(html, sources, options) { var opts = options || {}; return String(html || '').replace(new RegExp( @@ -95,9 +109,9 @@ export function renderCitationLinks(html, sources, options) { 'gi'), function (match, tag, cluster) { if (!cluster) return match; var nums = cluster.split(',').map(function (n) { return Number(n.trim()); }).filter(function (n) { return Number.isInteger(n) && n > 0; }); - if (!nums.length || nums.some(function (n) { return !sources[n - 1]; })) return match; + if (!nums.length || nums.some(function (n) { return !sourceByNumber(sources, n); })) return match; return nums.map(function (n) { - var source = sources[n - 1]; + var source = sourceByNumber(sources, n); var title = source ? source.title || source.resource || 'Source' : 'Source'; var page = source && (source.page || source.page_number || source.pageNumber); var label = 'Source ' + n + ': ' + title + (page ? ', page ' + page : ''); @@ -136,7 +150,7 @@ function parseCitationCluster(cluster) { } function allCitationsAvailable(nums, sources) { - return nums.length > 0 && nums.every(function(n) { return sources[n - 1]; }); + return nums.length > 0 && nums.every(function(n) { return sourceByNumber(sources, n); }); } function formatCitationCluster(nums) {