//Global variable declaration
//Site data so you only have to load it one time
var siteData;
var entitiesOrInterests = 'entities';
//Donald Trump, Elon Musk, etc.
var currentEntity;
//Trends, Linguistics, Entities, Geography
var currentView;
//1 year ago, 1 month ago, 1 week ago
var currentTimeFrame;
//Subdata views for each current view
var currentDataView;
var trendsChart;
var wordCloud;
var myWordsCached;
var globalMap = null;
var topicChart
//entityCardOptions
var entityCardOptions = {
showPoint: false,
showLine: true,
showArea: false,
fullWidth: false,
showLabel: false,
axisX: {
showGrid: false,
showLabel: true,
},
axisY: {
showGrid: false,
showLabel: false,
},
chartPadding: 2,
low: 0
};
//Global options for each of the chartist trends charts
var options = {
showPoint: false,
showLine: true,
showArea: false,
fullWidth: false,
showLabel: true,
axisX: {
showGrid: false,
showLabel: false,
},
axisY: {
showGrid: false,
showLabel: false,
},
chartPadding: 2,
low: 0
};
//chartJS options
var chartJSOptions = {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'day',
},
grid: {
display: false,
},
title: {
display: true,
text: 'Date'
}
},
y: {
grid: {
display: false,
},
title: {
display: true,
text: 'Net Appearance'
}
}
},
plugins: {
tooltip: {
enabled: false
}
}
}
function buildTrendsChart(ctx, data, datatype) {
if (datatype == 'universe') {
chartJSOptions['scales']['y']['title']['text'] = "Net Appearance";
var tempChart = new Chart(ctx, {
type: 'line', // Specify the chart type (e.g., bar, line, pie, etc.)
data: {
labels: data.labels,
datasets: [{
label: 'Universe',
data: data.series[0],
backgroundColor: '#22aaff',
borderColor: '#22aaff',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
}]
},
options: chartJSOptions
});
}
if (datatype == 'sentiment') {
chartJSOptions['scales']['y']['title']['text'] = "Net Sentiment";
var tempChart = new Chart(ctx, {
type: 'line', // Specify the chart type (e.g., bar, line, pie, etc.)
data: {
labels: data.labels,
datasets: [{
label: 'Positive',
data: data.series[0],
backgroundColor: '#88dd11',
borderColor: '#88dd11',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Negative',
data: data.series[1],
backgroundColor: '#ff3366',
borderColor: '#ff3366',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
]
},
options: chartJSOptions
});
}
if (datatype == 'emotions') {
chartJSOptions['scales']['y']['title']['text'] = "Percentage (%) of Collected Posts";
var tempChart = new Chart(ctx, {
type: 'line', // Specify the chart type (e.g., bar, line, pie, etc.)
data: {
labels: data.labels,
datasets: [{
label: 'Trust',
data: data.series[0],
backgroundColor: '#22aaff',
borderColor: '#22aaff',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Joy',
data: data.series[1],
backgroundColor: '#22dddd',
borderColor: '#22dddd',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Love',
data: data.series[2],
backgroundColor: '#88dd11',
borderColor: '#88dd11',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Anticipation',
data: data.series[3],
backgroundColor: '#ffdd00',
borderColor: '#ffdd00',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Surprise',
data: data.series[4],
backgroundColor: '#ff9922',
borderColor: '#ff9922',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Anger',
data: data.series[5],
backgroundColor: '#ff3366',
borderColor: '#ff3366',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Fear',
data: data.series[6],
backgroundColor: '#ee44ee',
borderColor: '#ee44ee',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Hate',
data: data.series[7],
backgroundColor: '#aa77ff',
borderColor: '#aa77ff',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Disgust',
data: data.series[8],
backgroundColor: '#ccaa66',
borderColor: '#ccaa66',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
{
label: 'Sadness',
data: data.series[9],
backgroundColor: '#8899bb',
borderColor: '#8899bb',
borderWidth: 3,
pointRadius: 0,
lineTension:.25,
},
]
},
options: chartJSOptions
});
}
return tempChart;
}
//code that handles view switching (Trends, Linguistics, etc.)
function handleViewButtonSelection() {
const buttons = document.querySelectorAll('.view-button');
const views = document.querySelectorAll('.view');
// Add 'active' class to the first button and its corresponding view
buttons[0].classList.add('active');
const defaultViewId = buttons[0].getAttribute('id').replace('button', 'view');
const defaultView = document.getElementById(defaultViewId);
defaultView.classList.add('active');
buttons.forEach(button => {
button.addEventListener('click', () => {
// Remove 'active' class from all buttons and views
buttons.forEach(btn => btn.classList.remove('active'));
views.forEach(view => view.classList.remove('active'));
// Add 'active' class to the clicked button and corresponding view
button.classList.add('active');
const targetViewId = button.getAttribute('id').replace('button', 'view');
const targetView = document.getElementById(targetViewId);
targetView.classList.add('active');
const timeButtons = document.querySelectorAll('.time-button');
timeButtons.forEach(btn => btn.classList.remove('active'));
const activeTimeButtons = document.querySelectorAll('.view.active')[0].querySelectorAll('.time-button');
activeTimeButtons[0].classList.add('active');
currentTimeFrame = activeTimeButtons[0].textContent.toLowerCase();
const activeDataButtons = document.querySelectorAll('.view.active')[0].querySelectorAll('.data-button');
activeDataButtons.forEach(btn => btn.classList.remove('active'));
activeDataButtons[0].classList.add('active');
currentDataView = activeDataButtons[0].textContent.toLowerCase();
currentView = button.textContent.toLowerCase();
if (currentView == "trends") {
var newData = buildChartData(siteData['key'][entitiesOrInterests][currentEntity], currentView, currentTimeFrame, currentDataView)
//trendsChart.update(newData);
var newData = buildChartData(siteData['key'][entitiesOrInterests][currentEntity], currentView, currentTimeFrame, currentDataView)
trendsChart.destroy();
var ctx = document.getElementById('ct-chart-trends').getContext('2d');
trendsChart = buildTrendsChart(ctx, newData, currentDataView);
}
if (currentView =='linguistics') {
var words = siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]
wordCloud = drawWordCloud(words)
}
if ((currentView == 'geography') && (globalMap == null)) {
globalMap = drawMap("map", siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]);
} else if ((currentView == 'geography') && (globalMap != null)){
globalMap = updateMap("map", globalMap, siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]);
}
});
});
}
//code that handles time frame switching
function handleTimeButtonSelection() {
const buttons = document.querySelectorAll('.time-button');
const views = document.querySelectorAll('.time');
// Add 'active' class to the first button and its corresponding view
buttons[0].classList.add('active');
const defaultViewId = buttons[0].getAttribute('id').replace('button', 'view');
const defaultView = document.getElementById(defaultViewId);
defaultView.classList.add('active');
buttons.forEach(button => {
button.addEventListener('click', () => {
// Remove 'active' class from all buttons and views
buttons.forEach(btn => btn.classList.remove('active'));
views.forEach(view => view.classList.remove('active'));
// Add 'active' class to the clicked button and corresponding view
button.classList.add('active');
currentTimeFrame = button.textContent;
if (currentView == "trends") {
var newData = buildChartData(siteData['key'][entitiesOrInterests][currentEntity], currentView, currentTimeFrame, currentDataView)
trendsChart.destroy();
var ctx = document.getElementById('ct-chart-trends').getContext('2d');
trendsChart = buildTrendsChart(ctx, newData, currentDataView);
}
if (currentView =='linguistics') {
var words = siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]
wordCloud = drawWordCloud(words)
}
if (currentView == 'geography') {
globalMap = updateMap("map", globalMap, siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]);
}
});
});
}
//This function handles the button selection and allows users to select various tabs within each entity subpage
function handleDataButtonSelection() {
const buttons = document.querySelectorAll('.data-button');
const views = document.querySelectorAll('.data');
// Add 'active' class to the first button and its corresponding view
buttons[0].classList.add('active');
const defaultViewId = buttons[0].getAttribute('id').replace('button', 'view');
const defaultView = document.getElementById(defaultViewId);
defaultView.classList.add('active');
buttons.forEach(button => {
button.addEventListener('click', () => {
// Remove 'active' class from all buttons and views
buttons.forEach(btn => btn.classList.remove('active'));
views.forEach(view => view.classList.remove('active'));
// Add 'active' class to the clicked button and corresponding view
button.classList.add('active');
currentDataView = button.textContent.toLowerCase();
if (currentView == "trends") {
var newData = buildChartData(siteData['key'][entitiesOrInterests][currentEntity], currentView, currentTimeFrame, currentDataView)
trendsChart.destroy();
var ctx = document.getElementById('ct-chart-trends').getContext('2d');
trendsChart = buildTrendsChart(ctx, newData, currentDataView);
}
if (currentView =='linguistics') {
var words = siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]
wordCloud = drawWordCloud(words)
if (currentDataView == 'mentions') {
document.getElementById("linguistics-info-box").innerHTML = "Sentiment";
document.getElementById("linguistics-color-gradient").className = "color-gradient-sentiment";
} else if (currentDataView == 'topics') {
document.getElementById("linguistics-info-box").innerHTML = "Sentiment";
document.getElementById("linguistics-color-gradient").className = "color-gradient-sentiment";
} else if (currentDataView == 'hashtags') {
document.getElementById("linguistics-info-box").innerHTML = "Sentiment";
document.getElementById("linguistics-color-gradient").className = "color-gradient-sentiment";
}
}
if (currentView == 'geography') {
globalMap = updateMap("map", globalMap, siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView]);
if (currentDataView == 'universe') {
document.getElementById("geography-info-box").innerHTML = "Universe";
document.getElementById("geography-color-gradient").className = "color-gradient-universe";
} else if (currentDataView == 'sentiment') {
document.getElementById("geography-info-box").innerHTML = "Sentiment";
document.getElementById("geography-color-gradient").className = "color-gradient-sentiment";
} else if (currentDataView == 'trust score') {
document.getElementById("geography-info-box").innerHTML = "Trust Score";
document.getElementById("geography-color-gradient").className = "color-gradient-score";
}
}
});
});
}
//This function inserts the infegy branding logo to each title card (depending on where you need it)
function insertInfegyLightbulbLogo(imageName) {
urlPath = "https://1716283.fs1.hubspotusercontent-na1.net/hubfs/1716283/Top_Brands_2024_Refresh/logos/"
const image = document.createElement('img')
image.classList.add("imgLogo")
image.src = urlPath+imageName+".png"
image.alt = 'Image alt'
return image;
}
//This bit of code reads the entity summary from the json and puts it in the entity card along with the image
function createEntityNameSummary(data, box, i) {
// Create the name element
const name = document.createElement('h2');
name.textContent = (i+1) + ". " + data[entitiesOrInterests][i].name;
//Add Image/Branding
name.appendChild(insertInfegyLightbulbLogo("logo"));
//Add Name
box.appendChild(name);
return box;
}
//This bit of code builds each entity box
//TODO - Clean it up and abstract it out some
function buildEntityBox(data, i) {
// Create a new box div
const box = document.createElement('div');
box.classList.add('entity_box');
box.setAttribute("onclick",'loadEntityPage("'+data[entitiesOrInterests][i].name+'")');
createEntityNameSummary(data, box, i);
const flex_table = document.createElement("div");
flex_table.classList.add("flex-table");
//Post Volume Label
const postVolumeCell = document.createElement("div");
postVolumeCell.className = 'most_trusted_cell';
postVolumeCell.id = 'volume'
postVolumeCell.innerHTML ="
Trust
" + "" + numberWithCommas(Math.round(data[entitiesOrInterests][i].positive_trust)) +"
";
//Post Volume Label
const posSentimentCell = document.createElement("div");
posSentimentCell.className = 'most_trusted_cell';
posSentimentCell.id = 'posSentiment'
posSentimentCell.innerHTML = "Positive
"+ ""+ data[entitiesOrInterests][i].positive_sentiment +"%
";
//Post Volume Label
const negSentimentCell = document.createElement("div");
negSentimentCell.className = 'most_trusted_cell';
negSentimentCell.id = 'negSentiment'
negSentimentCell.innerHTML = "Negative
"+ ""+ data[entitiesOrInterests][i].negative_sentiment +"%
";
flex_table.appendChild(posSentimentCell);
flex_table.appendChild(postVolumeCell);
flex_table.appendChild(negSentimentCell);
box.appendChild(flex_table);
//Add Entity Summary
const summary = document.createElement('p');
summary.append(data[entitiesOrInterests][i].summary);
box.appendChild(summary);
return box;
}
//This function adds the navigation buttons to navigate in between trends, linguistics, posts, and geography
function addEntityNavButtons() {
var listOfButtonsToAdd = ['Trends', 'Linguistics', 'Geography'];
var buttonContainer = document.createElement('div');
buttonContainer.classList.add("button-row");
buttonContainer.classList.add("navButtons");
for (let i = 0; i < listOfButtonsToAdd.length; i++) {
var tempButton = document.createElement("button");
tempButton.classList.add("view-button");
tempButton.id = "button" + (i+1);
tempButton.textContent=listOfButtonsToAdd[i];
buttonContainer.append(tempButton);
}
return buttonContainer;
}
//This function adds the timeframe buttons to navigate in between the different time frames in the dashboard
function addTimeFrameButtons() {
var listOfButtonsToAdd = ['1 year ago', '1 month ago', '1 week ago'];
var buttonContainer = document.createElement('div');
buttonContainer.classList.add("button-row");
buttonContainer.classList.add("timeButtons");
for (let i = 0; i < listOfButtonsToAdd.length; i++) {
var tempButton = document.createElement("button");
tempButton.classList.add("time-button");
tempButton.id = "button" + (i+1);
tempButton.textContent=listOfButtonsToAdd[i];
buttonContainer.append(tempButton);
}
return buttonContainer;
}
//This function adds the timeframe buttons to navigate in between the different time frames in the dashboard
function addDataButtons(listOfButtonsToAdd) {
var buttonContainer = document.createElement('div');
buttonContainer.classList.add("button-row");
buttonContainer.classList.add("dataButtons");
for (let i = 0; i < listOfButtonsToAdd.length; i++) {
var tempButton = document.createElement("button");
tempButton.classList.add("data-button");
tempButton.id = "button" + (i+1);
tempButton.textContent=listOfButtonsToAdd[i];
buttonContainer.append(tempButton);
}
return buttonContainer;
}
//This function will build the trends html skeleton of the entity page
function buildTrendsTab() {
const trendsTab = document.createElement('div');
trendsTab.classList.add('view');
trendsTab.id = 'view1';
const trendsParagraph = document.createElement('p');
trendsParagraph.textContent = "The Trends tab allows you to view how your research topic has trended over the selected timeframe. This tab is best for understanding volume fluctuations, percent change, trend events, and more.";
trendsTab.append(trendsParagraph);
trendsTab.append(addTimeFrameButtons());
trendsTab.append(addDataButtons(['Universe', 'Sentiment', 'Emotions']));
const chartDiv = document.createElement('div');
chartDiv.classList.add("trendsChartContainer");
const trendsChart = document.createElement('canvas');
trendsChart.id = 'ct-chart-trends';
chartDiv.append(trendsChart);
trendsTab.append(chartDiv);
return trendsTab;
}
function buildInfoBoxes(idType) {
//Overall Gradient Information Row
const informationContainer = document.createElement('div');
informationContainer.classList.add('information-row');
//Gradient Box
const colorGradientBox = document.createElement('div');
colorGradientBox.classList.add('info-box');
//Gradient Description
const colorGradientDescription = document.createElement('p');
colorGradientDescription.classList.add('color-gradient-description');
colorGradientDescription.id = idType + "-info-box"
if (idType == 'geography') {
colorGradientDescription.textContent = "Universe";
} else {
colorGradientDescription.textContent = "Sentiment";
}
colorGradientBox.appendChild(colorGradientDescription);
//Actual Gradient
const colorGradient = document.createElement('div');
colorGradient.id = idType + "-color-gradient"
if (idType == 'linguistics') {
colorGradient.classList.add("color-gradient-sentiment");
} else {
colorGradient.classList.add("color-gradient-universe");
}
colorGradientBox.appendChild(colorGradient);
informationContainer.appendChild(colorGradientBox);
return informationContainer
}
//This function will build the linguistics html skeleton of the entity page
//abstract out the different gradients and trendlines
function buildLinguisticsTab() {
const linguisticsTab = document.createElement('div');
linguisticsTab.classList.add('view');
linguisticsTab.id = 'view2';
const linguisticsParagraph = document.createElement('p');
linguisticsParagraph.textContent = "The Linguistics tab shows the actual words and hashtags present within posts and documents. We also show mentions here showing which accounts frequently appear. They are colored by sentiment, showing how positive or negative the overall conversation is around the topics.";
linguisticsTab.append(linguisticsParagraph);
linguisticsTab.append(addTimeFrameButtons());
linguisticsTab.append(addDataButtons(['Topics', 'Hashtags', 'Mentions']));
const linguisticsWordCloud = document.createElement("div");
linguisticsWordCloud.classList.add('wordCloudContainer');
linguisticsWordCloud.id = "wordcloud";
linguisticsTab.appendChild(linguisticsWordCloud);
const informationContainer = buildInfoBoxes("linguistics")
linguisticsTab.append(informationContainer);
return linguisticsTab
}
//This function will build the geography html skeleton of the entity page
function buildGeographyTab() {
const geographyTab = document.createElement('div');
geographyTab.classList.add('view');
geographyTab.id = 'view3';
const geographyParagraph = document.createElement('p');
geographyParagraph.textContent = "Using a normalized approach, Infegy Starscape provides an accurate read on where interest in your research topic lies across the globe. Here, we rank countries by overall volume, sentiment, and trust score. With trust score, we are looking at the countries that have the highest number of positive documents containing trust.";
geographyTab.append(geographyParagraph);
geographyTab.append(addTimeFrameButtons());
geographyTab.append(addDataButtons(['Universe', 'Sentiment', 'Trust Score']));
const mapContainer = document.createElement('div');
mapContainer.id = 'map'
geographyTab.append(mapContainer);
const informationContainer = buildInfoBoxes("geography")
geographyTab.append(informationContainer);
return geographyTab
}
function getColor(country, countryCode) {
var color;
if (countryCode == undefined) {
color = "#ffffff"
} else {
try {
color = country[countryCode]['color']
}
catch {
color = "#ffffff"
}
}
return color
}
function updateMap(id, map, data) {
map.off();
map.remove();
map = drawMap(id, data);
return map;
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function drawMap(id, data) {
var container = L.DomUtil.get("map");
if (container != null) {
container._leaflet_id = null;
}
document.getElementById(id).innerHTML = "";
var map = L.map(id, {
center: [23, 0],
crs: L.CRS.EPSG3857,
zoom: 2,
maxZoom: 4,
zoomControl: false,
preferCanvas: false,
scrollWheelZoom: false,
dragging: false,
attributionControl: false,
}
);
function style(feature) {
return {
fillColor: getColor(data, feature.properties.iso2),
weight: 2,
opacity: .5,
color: 'lightgrey',
fillOpacity: 0.7
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
layer.bringToFront();
info.update(layer.feature.properties);
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
});
}
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
// method that we will use to update the control based on feature properties passed
info.update = function (props) {
if (currentDataView == 'universe') {
this._div.innerHTML = 'Universe by Country
' + (props ?
'' + props.ADMIN + '
' + numberWithCommas(Math.round(data[props.iso2].data)) + ' posts'
: 'Hover over a country');
} else if (currentDataView == 'sentiment') {
this._div.innerHTML = 'Sentiment by Country
' + (props ?
'' + props.ADMIN + '
' + Math.round(data[props.iso2].data*100) + '% positivity'
: 'Hover over a country');
} else if (currentDataView == 'trust score') {
this._div.innerHTML = 'Trust Score by Country
' + (props ?
'' + props.ADMIN + '
' + Math.round(data[props.iso2].data*100)/100
: 'Hover over a country');
}
};
info.addTo(map);
var geojson = L.geoJson(siteData['countries'], {style: style, onEachFeature: onEachFeature}).addTo(map);
return map;
}
//Thus function constructs the map for the trends chart.
function buildChartData(entityIndex, currentView, timeframe, dataView) {
let dataSeries = siteData[entitiesOrInterests][entityIndex][currentView][timeframe][dataView]
let dates = dataSeries['dates']
let dataForChart = []
var keys = Object.keys(dataSeries);
for (let i = 0; i < keys.length; i++) {
if (keys[i] != 'dates') {
dataForChart.push(dataSeries[keys[i]]);
}
}
var data = {
labels:dates,
series: dataForChart,
};
return data;
}
//This function draws the linguistics word clouds. It uses d3 as a dependency.
function drawWordCloud(myWords) {
myWordsCached = myWords;
document.getElementById("wordcloud").innerHTML = "";
// set the dimensions and margins of the graph
var margin = {top: 10, right: 10, bottom: 10, left: 10};
var width = document.getElementById('wordcloud').clientWidth - margin.left - margin.right;
var height = document.getElementById('wordcloud').clientHeight - margin.top - margin.bottom;
var fontscale = d3.scalePow()
.exponent(.25)
.domain([0, 100])
.range([0, 20]);
// append the svg object to the body of the page
var svg = d3.select("#wordcloud").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Constructs a new cloud layout instance. It run an algorithm to find the position of words that suits your requirements
// Wordcloud features that are different from one word to the other must be here
var layout = d3.layout.cloud()
.size([width, height])
.words(myWords.map(function(d) { return {text: d.word, size:d.size, color:d.color, timeline:d.timeline}; }))
.padding(15) //space between words
.rotate(function() { return ~~(Math.random() * 2) * 0; })
.fontSize(function(d) { return fontscale(d.size); }) // font size of words
.on("end", draw);
layout.start();
// This function takes the output of 'layout' above and draw the words
// Wordcloud features that are THE SAME from one word to the other can be here
function draw(words) {
svg
.append("g")
.attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size; })
.style("fill", function(d) { return d.color;})
.attr("text-anchor", "middle")
.style("font-family", "Roboto")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; })
.on("mouseover", function (d) {
// Generate the chart using Chartist
var seriesData = {
series: [d.timeline]
};
//topicChart.update(seriesData);
});
}
var seriesData = {
series: [myWords[0].timeline]
};
}
//This function handles the resizing for the linguistics word cloud
window.addEventListener('resize', function(event) {
if ((currentView == 'linguistics') && (document.getElementById("wordcloud").innerHTML != null)) {
drawWordCloud(myWordsCached);
}
}, true);
function titleCase(str) {
return str.toLowerCase().replace(/\b\w/g, s => s.toUpperCase());
}
//This function loads each Entity Page when the user clicks on an entity from the main page.
function loadEntityPage(entityName) {
clearCards();
var div = document.getElementById("boxContainer")
//Build Return Home Button
var buttonCenter = document.createElement("div");
buttonCenter.classList.add("text-center");
var returnHomeButton = document.createElement('button');
returnHomeButton.classList.add("return-button");
returnHomeButton.textContent="Return To List";
returnHomeButton.setAttribute("onclick",'populateSubPage("' + entitiesOrInterests + '")');
buttonCenter.appendChild(returnHomeButton);
div.appendChild(buttonCenter);
//Get Index key so you can hit the data appropriately
var entityIndex = siteData['key'][entitiesOrInterests][entityName];
//Build Main Entity Page
var entityPage = document.createElement('div');
entityPage.classList.add("entityPage");
//Build Title (H2)
const title = document.createElement('h2');
title.textContent = (entityIndex + 1) + ". " + entityName;
title.classList.add('entityTitle');
//Insert Infegy Logo
title.appendChild(insertInfegyLightbulbLogo("logo"));
entityPage.appendChild(title);
//Build positive trust
const positive_trust = document.createElement('h4');
positive_trust.textContent = "Positive Trust Score: " + numberWithCommas(Math.round(siteData[entitiesOrInterests][entityIndex].positive_trust)) + " posts";
entityPage.appendChild(positive_trust);
//Build Summary
const summary = document.createElement('p');
summary.textContent = siteData[entitiesOrInterests][entityIndex].summary;
summary.classList.add('entitySummary');
entityPage.append(summary);
currentEntity = entityName;
currentView = "trends";
currentTimeFrame = "1 year ago";
currentDataView = "universe";
//Build analysis tabs for each entity
entityPage.append(addEntityNavButtons());
entityPage.appendChild(buildTrendsTab());
entityPage.appendChild(buildLinguisticsTab());
entityPage.appendChild(buildGeographyTab());
//Build Return Home Button
//var buttonCenter = document.createElement("div");
//buttonCenter.classList.add("text-center");
//var returnHomeButton = document.createElement('button');
//returnHomeButton.classList.add("return-button");
//returnHomeButton.textContent="Download this data";
//returnHomeButton.setAttribute("onclick",'downloadData()');
//buttonCenter.appendChild(returnHomeButton);
//entityPage.appendChild(buttonCenter);
div.append(entityPage);
handleViewButtonSelection();
handleTimeButtonSelection();
handleDataButtonSelection();
//Build first graph (trends default view)
var data = buildChartData(entityIndex, 'trends', '1 year ago', 'universe');
var ctx = document.getElementById('ct-chart-trends').getContext('2d');
trendsChart = buildTrendsChart(ctx, data, 'universe');
var elementToScrollTo = document.getElementById('boxContainer');;
if (elementToScrollTo) {
// Scroll to the top of the element
elementToScrollTo.scrollIntoView();
}
}
//this function loads the initial page (entity card tiles)
function loadInitialPage(url) {
fetch(url).then(response => {return response.json();}).then(data => {
siteData = data;
populateSubPage("entities");
//load updated page thing
var last_updated = document.getElementById("last_updated")
//
last_updated.textContent = "Updated on: " + siteData['time_updated'];
});
}
//This function loads each of the entity cards in the main view
function populateSubPage(textLabel) {
clearCards();
entitiesOrInterests = textLabel;
var div = document.getElementById("boxContainer")
const container = document.createElement('div');
container.classList.add('most_trusted_brands_container');
// Loop through the data array and create a box for each item
for (let i = 0; i < siteData[entitiesOrInterests].length; i++) {
//Add each entity box to the container
container.appendChild(buildEntityBox(siteData, i));
document.getElementById("boxContainer").appendChild(container);
}
var elementToScrollTo = document.querySelector('.most_trusted_brands');
if (elementToScrollTo) {
// Scroll to the top of the element
elementToScrollTo.scrollIntoView();
}
}
//This function deletes all the entity cards
function clearCards(){
// Get a reference to the div element
var div = document.getElementById("boxContainer");
// Clear the contents by setting the innerHTML property to an empty string
div.innerHTML = "";
}
//loadInitialPage("siteData.json")
loadInitialPage("https://1716283.fs1.hubspotusercontent-na1.net/hubfs/1716283/Top_Brands_2024_Refresh/siteData.json")
function jsonToCsv(items, currentEntity, currentView, currentTimeFrame, currentDataView) {
let mainArray = []
if (currentView == 'trends') {
const header = Object.keys(items);
//initialize first list
let headerArray = []
for (let i = 0; i < header.length; i++){
headerArray.push(header[i]);
}
mainArray.push(headerArray);
//Loop through each row of JSON data
for (let i = 0; i < items[header[0]].length; i++) {
var rowArray = []
for (let j = 0; j < header.length; j++) {
rowArray.push(items[header[j]][i]);
}
mainArray.push(rowArray);
}
} else if (currentView == 'linguistics') {
mainArray = items;
} else if (currentView == 'geography') {
const countries = Object.keys(items);
let headerArray = ['name', 'data', 'timeline'];
mainArray.push(headerArray);
for (let i = 0; i < countries.length; i++) {
var rowArray = []
rowArray.push(CountryLookup[countries[i]]);
rowArray.push(items[countries[i]]['data']);
rowArray.push(items[countries[i]]['timeline']);
mainArray.push(rowArray);
}
}
var csv = Papa.unparse(mainArray);
// Start file download.
download(currentEntity + " - " + currentView + " - " + currentTimeFrame + " - " + currentDataView + ".csv", csv);
return "hello";
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function downloadData() {
jsonToCsv(siteData[entitiesOrInterests][siteData['key'][entitiesOrInterests][currentEntity]][currentView][currentTimeFrame][currentDataView], currentEntity, currentView, currentTimeFrame, currentDataView);
}
var CountryLookup = {
"AF": "Afghanistan",
"AX": "Aland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "Andorra",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua And Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BS": "Bahamas",
"BH": "Bahrain",
"BD": "Bangladesh",
"BB": "Barbados",
"BY": "Belarus",
"BE": "Belgium",
"BZ": "Belize",
"BJ": "Benin",
"BM": "Bermuda",
"BT": "Bhutan",
"BO": "Bolivia",
"BA": "Bosnia And Herzegovina",
"BW": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"BN": "Brunei Darussalam",
"BG": "Bulgaria",
"BF": "Burkina Faso",
"BI": "Burundi",
"KH": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"KY": "Cayman Islands",
"CF": "Central African Republic",
"TD": "Chad",
"CL": "Chile",
"CN": "China",
"CX": "Christmas Island",
"CC": "Cocos (Keeling) Islands",
"CO": "Colombia",
"KM": "Comoros",
"CG": "Congo",
"CD": "Congo, Democratic Republic",
"CK": "Cook Islands",
"CR": "Costa Rica",
"CI": "Cote D\"Ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DK": "Denmark",
"DJ": "Djibouti",
"DM": "Dominica",
"DO": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"SV": "El Salvador",
"GQ": "Equatorial Guinea",
"ER": "Eritrea",
"EE": "Estonia",
"ET": "Ethiopia",
"FK": "Falkland Islands (Malvinas)",
"FO": "Faroe Islands",
"FJ": "Fiji",
"FI": "Finland",
"FR": "France",
"GF": "French Guiana",
"PF": "French Polynesia",
"TF": "French Southern Territories",
"GA": "Gabon",
"GM": "Gambia",
"GE": "Georgia",
"DE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GR": "Greece",
"GL": "Greenland",
"GD": "Grenada",
"GP": "Guadeloupe",
"GU": "Guam",
"GT": "Guatemala",
"GG": "Guernsey",
"GN": "Guinea",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HT": "Haiti",
"HM": "Heard Island & Mcdonald Islands",
"VA": "Holy See (Vatican City State)",
"HN": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IS": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran, Islamic Republic Of",
"IQ": "Iraq",
"IE": "Ireland",
"IM": "Isle Of Man",
"IL": "Israel",
"IT": "Italy",
"JM": "Jamaica",
"JP": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KI": "Kiribati",
"KR": "Korea",
"KP": "North Korea",
"KW": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Lao People\"s Democratic Republic",
"LV": "Latvia",
"LB": "Lebanon",
"LS": "Lesotho",
"LR": "Liberia",
"LY": "Libyan Arab Jamahiriya",
"LI": "Liechtenstein",
"LT": "Lithuania",
"LU": "Luxembourg",
"MO": "Macao",
"MK": "Macedonia",
"MG": "Madagascar",
"MW": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MH": "Marshall Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MU": "Mauritius",
"YT": "Mayotte",
"MX": "Mexico",
"FM": "Micronesia, Federated States Of",
"MD": "Moldova",
"MC": "Monaco",
"MN": "Mongolia",
"ME": "Montenegro",
"MS": "Montserrat",
"MA": "Morocco",
"MZ": "Mozambique",
"MM": "Myanmar",
"NA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"AN": "Netherlands Antilles",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NI": "Nicaragua",
"NE": "Niger",
"NG": "Nigeria",
"NU": "Niue",
"NF": "Norfolk Island",
"MP": "Northern Mariana Islands",
"NO": "Norway",
"OM": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PS": "Palestinian Territory, Occupied",
"PA": "Panama",
"PG": "Papua New Guinea",
"PY": "Paraguay",
"PE": "Peru",
"PH": "Philippines",
"PN": "Pitcairn",
"PL": "Poland",
"PT": "Portugal",
"PR": "Puerto Rico",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"BL": "Saint Barthelemy",
"SH": "Saint Helena",
"KN": "Saint Kitts And Nevis",
"LC": "Saint Lucia",
"MF": "Saint Martin",
"PM": "Saint Pierre And Miquelon",
"VC": "Saint Vincent And Grenadines",
"WS": "Samoa",
"SM": "San Marino",
"ST": "Sao Tome And Principe",
"SA": "Saudi Arabia",
"SN": "Senegal",
"RS": "Serbia",
"SC": "Seychelles",
"SL": "Sierra Leone",
"SG": "Singapore",
"SK": "Slovakia",
"SI": "Slovenia",
"SB": "Solomon Islands",
"SO": "Somalia",
"ZA": "South Africa",
"GS": "South Georgia And Sandwich Isl.",
"ES": "Spain",
"LK": "Sri Lanka",
"SD": "Sudan",
"SR": "Suriname",
"SJ": "Svalbard And Jan Mayen",
"SZ": "Swaziland",
"SE": "Sweden",
"CH": "Switzerland",
"SY": "Syrian Arab Republic",
"TW": "Taiwan",
"TJ": "Tajikistan",
"TZ": "Tanzania",
"TH": "Thailand",
"TL": "Timor-Leste",
"TG": "Togo",
"TK": "Tokelau",
"TO": "Tonga",
"TT": "Trinidad And Tobago",
"TN": "Tunisia",
"TR": "Turkey",
"TM": "Turkmenistan",
"TC": "Turks And Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UA": "Ukraine",
"AE": "United Arab Emirates",
"GB": "United Kingdom",
"US": "United States",
"UM": "United States Outlying Islands",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VU": "Vanuatu",
"VE": "Venezuela",
"VN": "Vietnam",
"VG": "Virgin Islands, British",
"VI": "Virgin Islands, U.S.",
"WF": "Wallis And Futuna",
"EH": "Western Sahara",
"YE": "Yemen",
"ZM": "Zambia",
"ZW": "Zimbabwe"
}