const candidateArray = []; class Candidate { constructor(firstName, lastName, color, image, center) { this.firstName = firstName; this.lastName = lastName; this.fullName = `${firstName} ${lastName}`; this.color = color || '#bebebe'; this.image = image || '/us-election/images/anon-cutout.png'; this.center = center || [50,50]; candidateArray.push(this) } } let Biden = new Candidate('Joe', 'Biden', '#B5AC7A', 'https://www.ctvnews.ca/polopoly_fs/1.4828724.1582743278!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 50]); let Sanders = new Candidate('Bernie', 'Sanders', '#0D6991', 'https://www.ctvnews.ca/polopoly_fs/1.4828736.1582743599!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 0]); let Warren = new Candidate('Elizabeth', 'Warren', '#B6C6CE', 'https://www.ctvnews.ca/polopoly_fs/1.4828732.1582743462!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 50]); let Buttigieg = new Candidate('Pete', 'Buttigieg', '#5490AA', 'https://www.ctvnews.ca/polopoly_fs/1.4828731.1582743398!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 50]); let Bloomberg = new Candidate('Michael', 'Bloomberg', '#E3D291', 'https://www.ctvnews.ca/polopoly_fs/1.4828778.1582744803!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 70]); let Klobuchar = new Candidate('Amy', 'Klobuchar', '#C19Fb1', 'https://www.ctvnews.ca/polopoly_fs/1.4828735.1582743518!/httpImage/image.png_gen/derivatives/landscape_190/image.png', [50, 50]); let Gabbard = new Candidate('Tulsi', 'Gabbard', '#949494', 'https://www.ctvnews.ca/polopoly_fs/1.4828729.1582743347!/httpImage/image.png_gen/derivatives/landscape_190/image.png'); let Steyer = new Candidate('Tom', 'Steyer', '#EBA677') let Yang = new Candidate('Andrew', 'Yang'); let Bennet = new Candidate('Michael', 'Bennet') let Delaney = new Candidate('John', 'Delaney') let Patrick = new Candidate('Deval', 'Patrick') let candidateCap = 4; //candidateArray.length; let primaryCap; async function getScheduleData () { scheduleDataRaw = await fetch ('https://beta.ctvnews.ca/content/dam/common/exceltojson/primary-results.txt'); scheduleData = await scheduleDataRaw.json(); //Organize candidate data into its own object scheduleData.forEach(state => { state.Candidates = {}; state.CandidatesPct = {}; const ignore = ['State', 'StateShort', 'TalliedPct', 'Winner', 'Date', 'Candidates', 'CandidatesPct', 'Delegates'] Object.keys(state).forEach(key => { if (!ignore.includes(key)) { if (key.slice(key.length-3, key.length) === 'Pct') { state.CandidatesPct[key.slice(0, key.length-3)] = Number(state[key]); delete state[key]; } else { state.Candidates[key] = Number(state[key]); delete state[key]; } } }) }) //console.log(scheduleData) let results = []; let toCome = [] for (let i = 0; i < scheduleData.length ; i++) { let totalDelegates = 0; Object.keys(scheduleData[i].Candidates).forEach(candidate => { totalDelegates += scheduleData[i].Candidates[candidate]; //console.log(candidate) }) if (totalDelegates > 0) { results.push(scheduleData[i]) } else { toCome.push(scheduleData[i]) } } let newOrder = [results, toCome].flat(); scheduleData = newOrder; primaryCap = scheduleData.length; let leaderboardContainer = document.getElementById('election-leaderboard'); let scheduleContainer = document.getElementById('election-schedule'); //Create Candidates object, delgates at zero to start -- actually, array of objects let candidateTotals = [] Object.keys(scheduleData[0].Candidates).forEach(candidate => { candidateTotals.push({ name: candidate, electors: 0 }) }); //Run through all states to get total delegates scheduleData.forEach(state => { Object.keys(state.Candidates).forEach(candidate => { candidateTotals.forEach(cand => { if (cand.name === candidate) { cand.electors += state.Candidates[candidate] } }) }) }) candidateTotals.sort((a, b) => b.electors - a.electors); //console.log("sorted:", candidateTotals) //Set cap to candidates who have at least one delegate //candidateCap = candidateTotals.filter(candidate => candidate.electors > 0).length //if page has container div for leaderboard, add leaderboard if (leaderboardContainer) { makeLeaderboard(candidateTotals); } else { //console.log('no leaderboard container') } //if page has container div for schedule, add schedule if (scheduleContainer) { makeSchedule(scheduleData, candidateTotals); } else { //console.log('no schedule container') } } function makeLeaderboard(totals) { let leaderboardContainer = document.getElementById('election-leaderboard'); let leaderboard = make('div', leaderboardContainer, 'leaderboard'); totals.forEach((candidate, i) => { if (i < candidateCap) { //let candidateDiv = make('div', leaderboard, 'candidateDiv') //let candidateNameDiv = make('div', leaderboard, 'candidateNameDiv') let candidateName = make('div', leaderboard, 'candidateName') candidateName.innerText = candidate.name; let candidateDelegates = make('div', leaderboard, 'candidateDelegates') candidateDelegates.innerText = candidate.electors; let candidateBarDiv = make('div', leaderboard, 'candidateBarDiv') let candidateBar = make('div', candidateBarDiv, 'candidateBar') //Max bar width is 80% of width -- could change it to be out of 1990 (goal post) candidateBar.style.width = `${100*candidate.electors/1991}%`; let candidateBarColor = make('div', candidateBar, 'candidateBarColor') candidateArray.forEach(cand => { if (candidate.name === cand.lastName) { candidateBarColor.style.background = cand.color; } }) } }) let winConditionDiv = make('div', leaderboardContainer, 'winConditionDiv'); let winCondition= make('div', winConditionDiv, 'winCondition'); winCondition.innerText = '1,991 to win'; winCondition.style.letterSpacing = '0.5px'; } function makeSchedule(sched, totals) { let scheduleContainer = document.getElementById('election-schedule') let scheduleDiv = make('div', scheduleContainer, 'scheduleDiv') let blank = make('div', scheduleDiv, 'headerStateLink sticky-header') let resultContainer = make('div', scheduleDiv, 'resultContainer sticky-header') let countContainer = make('div', resultContainer, 'countContainer'); totals.forEach((candTot, i) => { if (i < candidateCap) { candidateArray.forEach(cand => { if (candTot.name === cand.lastName) { let face = make('div', countContainer, 'countDiv faceDiv') face.style.background = cand.color; let faceImg = make('img', face, 'candidateImg'); faceImg.src = cand.image; faceImg.style.objectPosition = `${cand.center[0]}% ${cand.center[1]}%` } }) } }) sched.forEach( primary => { //let primaryContainer = make('div', scheduleDiv, 'primaryContainer') let headerStateLink = make('a', scheduleDiv, 'headerStateLink') //headerStateLink.href=`/us-election/states?state=${primary.State.split(' ').join('-')}` let stateDiv = make('div', headerStateLink, 'stateDiv') let swidth = document.querySelector('.scheduleDiv').offsetWidth; stateDiv.innerText = swidth < 400 ? primary.StateShort : primary.State; stateDiv.style.letterSpacing = primary.State.length > 11 ? '-0.5px' : 'normal' ; let totalDelegates = 0; //let winner = 0; Object.keys(primary.Candidates).forEach(candidate => { totalDelegates += primary.Candidates[candidate]; }) if (totalDelegates > 0) { let resultContainer = make('div', scheduleDiv, 'resultContainer') let countContainer = make('div', resultContainer, 'countContainer'); totals.forEach((candTot, i) => { if (i < candidateCap) { Object.keys(primary.Candidates).forEach(candidate => { if (candTot.name === candidate) { let countDiv = make('div', countContainer, 'countDiv') countDiv.innerText = primary.Candidates[candidate]; countDiv.style.color = candidate === primary.Winner /*&& candidate !== "Warren" */? '#fff' : '#333'; countDiv.style.fontFamily = candidate === primary.Winner ? 'CTVSans-Bold, CTV Sans, sans-serif' : 'CTVSans-Regular, CTV Sans, sans-serif'; candidateArray.forEach(cand => { if (candidate === cand.lastName) { countDiv.style.background = candidate === primary.Winner ? cand.color : `${cand.color}00`; } }) } }) } }) } else { let resultContainer = make('div', scheduleDiv, 'resultContainer') let dateDiv = make('div', resultContainer, 'dateDiv'); let convertDate = SerialDateToJSDate(primary.Date, -5); let day = convertDate.getDay(); let month = convertDate.getMonth(); let date = convertDate.getDate(); let year = convertDate.getFullYear(); let dayArray = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; let monthArray = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] dateDiv.innerText = swidth < 400 ? `${monthArray[month]} ${date}, ${year}` : `${dayArray[day]}, ${monthArray[month]} ${date}, ${year}`; window.addEventListener('resize', () => { swidth = document.querySelector('.scheduleDiv').offsetWidth; dateDiv.innerText = swidth < 400 ? `${monthArray[month]} ${date}, ${year}` : `${dayArray[day]}, ${monthArray[month]} ${date}, ${year}` }) } window.addEventListener('resize', () => { swidth = document.querySelector('.scheduleDiv').offsetWidth; stateDiv.innerText = swidth < 400 ? primary.StateShort : primary.State; }) }) } window.addEventListener('load', () => { getScheduleData(); }) ////// function make(type, parent, CLASS, ID) { let element = document.createElement(type); if (typeof CLASS !== 'undefined') { element.setAttribute('class', CLASS); } if (typeof ID !== 'undefined') { element.setAttribute('id', ID); } return parent.appendChild(element); } function SerialDateToJSDate(serialDate, offsetUTC) { return new Date(Date.UTC(0, 0, serialDate, offsetUTC)); }