const timeline = (function () { var d3data; var d3referenceCount; var d3references; var dataSelector; var height = 100; var computeYearRange = true; var maxFrequency = 0; var maxReferenceCount = 0; var maxBlocks = 0; var maxIncomingCount = 0; var minIncomingCount = 1000; var drawSize = 11; var references = {}; var selectorColors = ['#1a8e6a', '#f9ba02', '#6762a2', '#eb298d', '#7cc522', '#ec6502']; var niceIntervals = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000]; var maxYearIntervals = 10; var maxFrequencyIntervals = 5; var minYear = 3000; var maxYear = 0; return { updateTimeline: function (skipDataUpdate) { var displayHeight = height; var timelineDiv = $('#timeline'); timelineDiv.empty(); $('
', { class: 'label', text: 'publications per year' }).appendTo(timelineDiv); if (!skipDataUpdate) { dataSelector = computeData(); } drawTimeline(displayHeight, timelineDiv, dataSelector); initCitationControls(timelineDiv); } }; function computeData() { var data = {}; var dataSelector = {}; var referenceCount = {}; references = {}; computeYearlyData(data, referenceCount, dataSelector); for (var year = minYear; year <= maxYear; year++) { if (!data[year]) { data[year] = 0; } else { if (computeYearRange) { maxFrequency = Math.max(data[year], maxFrequency); maxReferenceCount = Math.max(referenceCount[year], maxReferenceCount); } } } computeYearRange = false; d3data = d3.entries(data); d3data = d3data.sort(function (a, b) { return d3.ascending(a.key, b.key); }); if (citations) { computeCitationData(referenceCount); } return dataSelector; } function computeSelectedReferencesColors(referenceYears) { // find color for selected references $.each(selectors.getSelectors(), function (i, selector) { if (selector && !selector['lock'] && selector['text'] && Object.keys(bib.filteredEntries).indexOf(selector['text']) >= 0) { var id = selector['text']; if (selector['type'] == 'citations_incoming') { if (!references[id]["counted"]) { // only count again, if we have not counted it yet referenceYears[references[id]["year"]] = (referenceYears[references[id]["year"]] ? referenceYears[references[id]["year"]] + 1 : 1); references[id]["counted"] = true; } references[id]["color"] = selectorColors[i]; references[id]["selected"] = true; if (references[id]["referencesIncoming"]) { references[id]["referencesIncoming"].forEach(function (id2) { if (!references[id2]["counted"]) { // only count again, if we have not counted it yet referenceYears[references[id2]["year"]] = (referenceYears[references[id2]["year"]] ? referenceYears[references[id2]["year"]] + 1 : 1); references[id2]["counted"] = true; } references[id2]["colorOutgoing"].push(d3.rgb(selectorColors[i])); }); } } else if (selector['type'] == 'citations_outgoing') { if (!references[id]["counted"]) { // only count again, if we have not counted it yet referenceYears[references[id]["year"]] = (referenceYears[references[id]["year"]] ? referenceYears[references[id]["year"]] + 1 : 1); references[id]["counted"] = true; } references[id]["color"] = selectorColors[i]; references[id]["selected"] = true; if (references[id]["referencesOutgoing"]) { references[id]["referencesOutgoing"].forEach(function (id2) { if (!references[id2]["counted"]) { // only count again, if we have not counted it yet referenceYears[references[id2]["year"]] = (referenceYears[references[id2]["year"]] ? referenceYears[references[id2]["year"]] + 1 : 1); references[id2]["counted"] = true; } references[id2]["colorIncoming"].push(d3.rgb(selectorColors[i])); }); } } } }); $.each(references, function (id, value) { references[id]["hidden"] = references[id]["belowMinCitations"] && (references[id]["colorOutgoing"].length == 0) && (references[id]["colorIncoming"].length == 0) && !references[id]["selected"]; }); } function computeReferenceFrequencyColors(referenceYears) { $.each(references, function (id, value) { value["color"] = d3.rgb("white"); if (value["referencesIncoming"] && value["referencesIncomingCount"] && value["referencesIncomingCount"] >= citations.minCitationCount) { var refVal = 255 * Math.pow(1.0 - (references[id]["referencesIncomingCount"] - 1) / (maxIncomingCount - 1), 0.9); refVal = Math.max(refVal, 1); references[id]["color"] = d3.rgb(refVal, refVal, refVal); referenceYears[value["year"]] = (referenceYears[references[id]["year"]] ? referenceYears[references[id]["year"]] + 1 : 1); references[id]["counted"] = true; } value["colorIncoming"] = []; value["colorOutgoing"] = []; value["selected"] = false; }); } function computeMaxBlocks(referenceYears) { maxBlocks = 0; for (var year = minYear; year <= maxYear; year++) { if (!referenceYears[year]) { referenceYears[year] = 0; } else { maxBlocks = Math.max(referenceYears[year], maxBlocks); } // console.log("found maximum of" + maxBlocks); } } function computeCitationData(referenceCount) { // set color of all reference blocks var referenceYears = {}; computeReferenceFrequencyColors(referenceYears); computeSelectedReferencesColors(referenceYears); computeMaxBlocks(referenceYears); d3referenceCount = d3.entries(referenceCount); d3referenceCount = d3referenceCount.sort(function (a, b) { return d3.ascending(a.key, b.key); }); var filteredReferences = {}; $.each(bib.filteredEntries, function (id, entry) { if (!references[id]['hidden']) { filteredReferences[id] = references[id]; } }); d3references = d3.entries(filteredReferences); d3references = d3references.sort(function (a, b) { var av = a.value["referencesIncomingCount"]; var bv = b.value["referencesIncomingCount"]; if (av == bv) { av = a.value["referencesOutgoingCount"]; bv = b.value["referencesOutgoingCount"]; } if (av == bv) { if (a.key > b.key) { av = 1.0; bv = 0.0; } else { av = 0.0; bv = 1.0; } } return d3.descending(av, bv); }); } function computeYearlyData(data, referenceCount, dataSelector) { $.each(bib.filteredEntries, function (id, entry) { var passedFilter = bib.filteredEntries[id] ? true : false; var year = parseFloat(entry['year']); if (!year) { return; } if (isNaN(year)) { return; } if (computeYearRange) { minYear = Math.min(year, minYear); maxYear = Math.max(year, maxYear); } if (passedFilter) { if (year in data) { data[year] += 1; } else { data[year] = 1; } } if (citations) { var referencesOutgoing = bib.filteredReferences[id].referencesOutgoing; var referencesIncoming = bib.filteredReferences[id].referencesIncoming; if (passedFilter) { if (year in referenceCount) { referenceCount[year] += (referencesIncoming ? referencesIncoming.length : 0); referenceCount[year] += ((!referencesIncoming && referencesOutgoing) ? 1 : 0); } else { referenceCount[year] = (referencesIncoming ? referencesIncoming.length : 0); referenceCount[year] += ((!referencesIncoming && referencesOutgoing) ? 1 : 0); } } references[id] = {}; references[id]["year"] = year; references[id]["referencesIncoming"] = referencesIncoming; references[id]["referencesIncomingCount"] = (passedFilter && referencesIncoming ? referencesIncoming.length : 0); maxIncomingCount = Math.max(maxIncomingCount, references[id]["referencesIncomingCount"]); minIncomingCount = Math.min(minIncomingCount, references[id]["referencesIncomingCount"]); references[id]["referencesOutgoing"] = referencesOutgoing; references[id]["referencesOutgoingCount"] = (passedFilter && referencesOutgoing ? referencesOutgoing.length : 0); references[id]["belowMinCitations"] = references[id]["referencesIncomingCount"] < citations.minCitationCount; } if (passedFilter) { $.each(selectors.getSelectors(), function (i, selector) { if (selector) { if (!dataSelector[i]) { dataSelector[i] = {}; } if (!dataSelector[i][year]) { dataSelector[i][year] = 0; } var sim = bib.entrySelectorSimilarities[id][i]; if (sim) { dataSelector[i][year] += sim; } } }); } }); // prevent bogus years from creating really awful errors var someFutureYear = ( new Date().getFullYear() ) + 2; var somePastYear = 1950; if (maxYear > someFutureYear) maxYear = someFutureYear; if (minYear < somePastYear) minYear = somePastYear; } function toggleCitationSelector(id) { selectors.toggleSelector('search', id, d3.event); } function drawTimeline(displayHeight, timelineDiv, dataSelector) { if (citations) { displayHeight = height + (drawSize * maxBlocks); // console.log("full height " + displayHeight); displayHeight = Math.max(displayHeight, 150); } var chart = d3.select('#timeline').append('svg') .attr('class', 'chart') .style('border', '1px solid black') .attr('height', displayHeight + 'px'); var width = timelineDiv.width() - 3; chart.attr('width', width + 'px'); var barWidth = width / (maxYear - minYear + 1); var publicationHeight = height / (maxFrequency + 1); var referenceHeight = (drawSize * maxFrequency) / (maxFrequency + 1); drawBackground(barWidth, chart, displayHeight, publicationHeight, width); drawFrequencyBars(chart, barWidth, publicationHeight); drawSelectorFrequencyBars(barWidth, dataSelector, chart, publicationHeight); if (citations) { drawCitations(chart, barWidth, referenceHeight); } generateTooltips(timelineDiv, barWidth); } function drawBackground(barWidth, chart, displayHeight, publicationHeight, width) { var yearIntervalIndex = 0; while (yearIntervalIndex < niceIntervals.length - 1 && (maxYear - minYear) / niceIntervals[yearIntervalIndex] > maxYearIntervals) { yearIntervalIndex++; } var yearIntervalLength = niceIntervals[yearIntervalIndex]; for (var intervalYear = minYear - minYear % yearIntervalLength; intervalYear <= maxYear; intervalYear += yearIntervalLength) { var x = (intervalYear - minYear) * barWidth; var even = intervalYear % (2 * yearIntervalLength) == 0 ? 'Even' : 'Uneven'; chart.append('rect').attr('class', 'period' + even) .attr('shape-rendering', 'crispEdges') .attr('x', x) .attr('y', -1) .attr('width', yearIntervalLength * barWidth) .attr('height', displayHeight + 2) .style('fill', even == 'Even' ? '#FFFFFF' : '#CCCCCC'); chart.append('text').attr('class', 'period' + even) .attr('x', x + 1) .attr('y', height / 5).text(intervalYear) .style('font-size', '14pt') .style('fill', even != 'Even' ? '#FFFFFF' : '#CCCCCC'); } var frequencyIntervalIndex = 0; while (frequencyIntervalIndex < niceIntervals.length - 1 && maxFrequency / niceIntervals[frequencyIntervalIndex] > maxFrequencyIntervals) { frequencyIntervalIndex++; } var frequencyIntervalLength = niceIntervals[frequencyIntervalIndex]; for (var i = frequencyIntervalLength; i <= maxFrequency; i += frequencyIntervalLength) { var y = height - publicationHeight * i; chart.append('line') .attr('x1', 0) .attr('y1', y) .attr('x2', width) .attr('y2', y) .style('stroke', 'black') .attr('shape-rendering', 'crispEdges') .attr('stroke-opacity', 0.15) .style('stroke-width', '1px'); chart.append('text') .attr('x', 0) .attr('y', y + 12) .style('font-size', '12pt') .text(i); } return {x: x, y: y}; } function drawFrequencyBars(chart, barWidth, publicationHeight) { chart.selectAll('svg').data(d3data).enter().append('rect') .attr('class', 'bar invisible tooltip') .attr('fill-opacity', 0.0) .attr('x', function (d) { return (d.key - minYear) * barWidth; }) .attr('y', function (d) { return 0; }) .attr('width', barWidth) .attr('height', function (d) { return height; }) .attr('title', function (d) { return d.key + ': ' + d.value + ' publications'; }) .on('click', function (d) { selectors.toggleSelector('year', d.key, d3.event); }); chart.selectAll('svg').data(d3data).enter().append('rect') .attr('class', 'bar total tooltip') .style('fill', 'var(--bgColor3)') .style('stroke', 'black') .attr('shape-rendering', 'crispEdges') .attr('x', function (d) { return (d.key - minYear) * barWidth; }) .attr('y', function (d) { return height - publicationHeight * d.value; }) .attr('width', barWidth) .attr('height', function (d) { return publicationHeight * d.value + 1; }) .attr('title', function (d) { return d.key + ': ' + d.value + ' publications'; }) .on('click', function (d) { selectors.toggleSelector('year', d.key, d3.event); }); } function drawSelectorFrequencyBars(barWidth, dataSelector, chart, publicationHeight) { var j = 0; var nActiveSelectors = selectors.getNActiveSelectors(); var barWidthSelector = (barWidth - 1) / nActiveSelectors; $.each(selectors.getSelectors(), function (i, selector) { if (selector && !selector['lock']) { var d3dataSelector = d3.entries(dataSelector[i]); d3dataSelector = d3dataSelector.sort(function (a, b) { return d3.ascending(a.key, b.key); }); chart.selectAll('svg').data(d3dataSelector).enter().append('rect') .attr('class', 'bar fill') .attr('shape-rendering', 'crispEdges') .style('fill', selectorColors[i]) .attr('x', function (d) { return 0.5 + (d.key - minYear) * barWidth + j * barWidthSelector; }) .attr('y', function (d) { return height - publicationHeight * d.value + 0.5; }) .attr('width', barWidthSelector) .attr('height', function (d) { return publicationHeight * d.value; }) .on('click', function (d) { selectors.toggleSelector('year', d.key, d3.event); }); j++; } } ); } function drawCitations(chart, barWidth, referenceHeight) { // draw bars, save position of center of references var currentHeight = {}; var linkPoints = {}; chart.selectAll('svg').data(d3references).enter().append('rect') .attr('class', function (d) { return 'citation tooltip' + (d.value["belowMinCitations"] ? ' tmp' : '') }) .attr('shape-rendering', 'crispEdges') .style('stroke', function (d) { return d.value["belowMinCitations"] ? '#AAAAAA' : 'black'; }) .style('fill', function (d) { return (d.value["color"] ? d.value["color"] : '#FFFFFF'); }) .attr('x', function (d) { var pos = (d.value["year"] - minYear) * barWidth; d.value["linkX"] = pos; return pos; }) .attr('y', function (d) { if (!(d.value["year"] in currentHeight)) { currentHeight[d.value["year"]] = 0; } var drawStart = (height + 1 + currentHeight[d.value["year"]]); d.value["linkY"] = drawStart; currentHeight[d.value["year"]] += referenceHeight; return drawStart; }) .attr('width', barWidth) .attr('height', referenceHeight) .on('click', function (d) { toggleCitationSelector(d.key); }) .attr("title", function (d) { return d.key + ' (cited by ' + d.value["referencesIncomingCount"] + ')'; }); chart.selectAll('.citation').sort(function (a, b) { return a.value['belowMinCitations'] ? -1 : 1; }); // draw links as bars // find color for selected references $.each(references, function (id, value) { function drawSelectedOutgoingCitations() { var y = value["linkY"] + 0.5; var x = value["linkX"] + 0.5; var w = 1.0 / 3.0 * (barWidth - 1.0); var h = (referenceHeight - 1.0) / value["colorOutgoing"].length; value["colorOutgoing"].forEach(function (color) { chart.append('rect') .style('fill', color) .attr('shape-rendering', 'crispEdges') .attr('y', y) .attr('x', x) .attr('height', h) .attr('width', w) .on('click', function (d) { toggleCitationSelector(id); }) .append("svg:title") .text(function (d) { return id + ' (cited by ' + value["referencesIncomingCount"] + ')'; }); y += h; }); } function drawSelectedIncomingCitations() { var y = value["linkY"] + 0.5; var x = value["linkX"] + 2.0 / 3.0 * barWidth - 0.5; var w = 1.0 / 3.0 * barWidth; var h = (referenceHeight - 1.0) / value["colorIncoming"].length; // console.log("Incoming " + id + " " + x + " " + y + " " + h + " " // + value["colorIncoming"].length); value["colorIncoming"].forEach(function (color) { chart.append('rect') .attr('shape-rendering', 'crispEdges') .style('fill', color) .attr('y', y) .attr('x', x) .attr('height', h) .attr('width', w) .on('click', function (d) { toggleCitationSelector(id); }) .append("svg:title") .text(function (d) { return id + ' (cited by ' + value["referencesIncomingCount"] + ')'; }); y += h; }); } if (value["colorOutgoing"] && value["colorOutgoing"].length > 0) { drawSelectedOutgoingCitations(); } if (value["colorIncoming"] && value["colorIncoming"].length > 0) { drawSelectedIncomingCitations(); } }); } function generateTooltips(timelineDiv, barWidth) { timelineDiv.find('.tooltip').tooltipster({ theme: 'tooltipster-survis', offsetX: (barWidth / 2) + 'px', offsetY: '-3px' }); } function initCitationControls(timelineDiv) { if (citations) { $('
', { class: 'label', text: '#citations per publication' }).appendTo(timelineDiv); var citationsColorsDiv = $('
', { id: 'citation_colors' }).appendTo(timelineDiv); $('
', { class: 'label', text: '1' }).appendTo(citationsColorsDiv); $('
', { class: 'color_scale' }).appendTo(citationsColorsDiv); $('
', { class: 'label', text: maxIncomingCount }).appendTo(citationsColorsDiv); var citationOccurrenceDiv = $('
', { class: 'cit_occurrence', text: 'min #citations ', title: 'choose the minimum number of citations (publications with fewer citations will not be displayed in the above citation representation)' }).appendTo(timelineDiv); citationOccurrenceDiv.tooltipster({ theme: 'tooltipster-survis' }); var buttonDec = $('
', { class: 'button dec small', text: '-' }).appendTo(citationOccurrenceDiv); buttonDec.click(function (event) { if (citations.minCitationCount > 1) { citations.minCitationCount--; page.updateTimeline(); } }); if (citations.minCitationCount < 1) { citations.minCitationCount = 1 } $('', { text: citations.minCitationCount }).appendTo(citationOccurrenceDiv); var buttonInc = $('
', { class: 'button inc small', text: '+' }).appendTo(citationOccurrenceDiv); buttonInc.click(function (event) { citations.minCitationCount++; page.updateTimeline(); }); } } })();