window.data ??= {}
for (const day of [1,2])
window.data[day]??=await (await fetch("https://wpgiegzkbrhj5mlsdxnipboepm.appsync-api.eu-west-1.amazonaws.com/graphql", {
"headers": {
"x-api-key": "da2-juounigq4vhkvg5ac47mezxqge" // note API key is intentionally public
},
"body": JSON.stringify({
"operationName": "getCalendarCompetitionResults",
"variables": {
"competitionId": 7176907,
"day": day,
"eventId": null
},
"query": `query getCalendarCompetitionResults($competitionId: Int, $day: Int, $eventId: Int) {
getCalendarCompetitionResults(competitionId: $competitionId, day: $day, eventId: $eventId) {
competition {
dateRange
endDate
name
rankingCategory
startDate
venue
__typename
}
eventTitles {
rankingCategory
eventTitle
events {
event
eventId
gender
isRelay
perResultWind
withWind
summary {
competitor {
teamMembers {
id
name
iaafId
urlSlug
__typename
}
id
name
iaafId
urlSlug
birthDate
__typename
}
mark
nationality
placeInRace
placeInRound
points
raceNumber
records
wind
__typename
}
races {
date
day
race
raceId
raceNumber
results {
competitor {
teamMembers {
id
name
iaafId
urlSlug
__typename
}
id
name
iaafId
urlSlug
birthDate
hasProfile
__typename
}
mark
nationality
place
points
qualified
records
wind
remark
details {
event
eventId
raceNumber
mark
wind
placeInRound
placeInRace
points
overallPoints
placeInRoundByPoints
overallPlaceByPoints
__typename
}
__typename
}
startList {
competitor {
birthDate
country
id
name
urlSlug
__typename
}
order
pb
sb
bib
__typename
}
wind
__typename
}
__typename
}
__typename
}
options {
days {
date
day
__typename
}
events {
gender
id
name
combined
__typename
}
__typename
}
parameters {
competitionId
day
eventId
__typename
}
__typename
}
}`
}),
"method": "POST",
})).json();
window.cache??={};
if (typeof nameFixer === 'undefined') {
const script = Object.assign(document.createElement('script'), { src: 'https://unpkg.com/name-fixer@1.0.0' });
document.body.appendChild(script);
await new Promise(res => script.addEventListener('load', res));
}
titleExists=async (name)=>{
const enLabelTitleMatch = await fetch(pre+'https://en.wikipedia.org/wiki/' + name.replace('|', ''));
return enLabelTitleMatch.status === 200;
}
getTitle=async (id,name,evt='',year)=>{
const words = name.split(' ');
const lnameStart = words.findIndex(w => w.toUpperCase() === w);
const fname = words.slice(0, lnameStart).join(' ');
const lname = words.slice(lnameStart).join(' ');
name = fname + ' ' + nameFixer.nameFixer(lname);
name = name.replace('LI', 'Li').replace('XI', 'Xi');
if (cache[id]) return cache[id];
const pages = await (await fetch('https://www.wikidata.org/w/api.php?' + new URLSearchParams({
action: 'query',
format: 'json',
list: 'search',
srsearch: `haswbstatement:P1146=${id}`,
}))).json();
const qid = pages.query.search[0]?.title;
if (qid) {
const entity = await (await fetch('https://www.wikidata.org/w/api.php?' + new URLSearchParams({
action: 'wbgetentities',
format: 'json',
ids: qid,
}))).json();
const sitelinks = entity.entities[qid].sitelinks;
const enTitle = sitelinks.enwiki?.title;
if (enTitle) {
cache[id] = `[[${enTitle}${enTitle.includes('(') ? '|' : ''}]]`;
return cache[id];
}
let enLabel = entity.entities[qid].labels.en?.value ?? name;
const enLabelNoParens = enLabel;
if (await titleExists(enLabel)) enLabel += ' (athlete)'; // todo awb job?
const otherWikis = Object.keys(sitelinks).filter(key => !key.startsWith('commons') && key.endsWith('wiki'));
if (otherWikis.length) {
const positionals = otherWikis.map(ow => `|${ow.replace('wiki', '')}|${sitelinks[ow].title}`).join('');
cache[id] = `{{ill|${enLabel}${positionals}${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
return cache[id];
}
cache[id] = `{{ill|${enLabel}|wd=${qid}|s=1${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
return cache[id];
}
if (await titleExists(name)) {
name += ' (athlete)|';
if (await titleExists(name)) {
const el = evt.toLowerCase();
const parens = el.includes('mH') ? 'hurdler' : el.includes('high jump') ? 'high jumper' : el.includes('long jump') ? 'long jumper' : el.includes('triple jump') ? 'triple jumper' : el.includes('shot put') ? 'shot putter' : el.includes('discus') ? 'discus thrower' : el.includes('hammer') ? 'hammer thrower' : el.includes('javelin') ? 'javelin thrower' : el.includes('steeplchase') ? 'steeplechase runner' : 'runner';
name = name.replace('(athlete)', `(${parens})`);
if (await titleExists(name)) name = name.replace(`(${parens})`, `(${parens}, born ${year})`);
}
}
cache[id] = `[[${name}]]`;
return cache[id];
}
mark2secs=(mark, isField = false)=>{
const parts = mark.split(':');
let ret;
if (parts.length === 1) ret = +mark;
else if (parts.length === 2) ret = +parts[0] * 60 + +parts[1];
else ret = +parts[0] * 60 * 60 + +parts[1] * 60 + +parts[2];
if (Number.isNaN(ret)) return isField ? -Infinity : Infinity;
return ret;
}
const medalRows = [];
for (const dayData of Object.values(data))
for (const eventTitle of dayData.data.getCalendarCompetitionResults?.eventTitles ?? []) {
if (![null].includes(eventTitle.eventTitle)) continue;
for (const evt of eventTitle.events) {
const isLastEvt = eventTitle.events.indexOf(evt) === eventTitle.events.length - 1;
const isField = ['jump', 'throw', 'vault', 'discus', 'put'].some(s => evt.event?.toLowerCase().includes(s));
const stages = Object.values(evt.races.reduce((acc, r) => {
acc[r.race] ??= [];
acc[r.race].push(r);
return acc;
}, {}));
for (const stage of stages) {
const isLastStage = stages.indexOf(stage) === stages.length - 1;
const isFinal = stage[0].race === 'Final';
if (!isFinal) continue;
const isMulti = stage.length > 1;
let medalRow = `|-\n| ${evt.event.replace(' indoor', '')}\n`;
const results = stage.flatMap(race => race.results.map(res => ({...res, raceNumber: race.raceNumber}))).sort((a, b) => isField ? mark2secs(b.mark, true) - mark2secs(a.mark, true) : mark2secs(a.mark) - mark2secs(b.mark)).filter((r, i, arr) => r.competitor.teamMembers?.length || arr.findIndex(r2 => r.competitor.urlSlug === r2.competitor.urlSlug) === i);
for (const result of results.slice(0, 3)) {
const pl = ['DNS', 'DNF', 'DQ', 'NM'].includes(result.mark) ? '' : results.indexOf(result) + 1;
const name = result.competitor.name;
const dob = new Date(result.competitor.birthDate);
const id = result.competitor.urlSlug?.split('-').at(-1).replace(/^0/, '');
medalRow += `| ${id ? `{{flagmedalist|${await getTitle(id, name, evt.event, dob.getFullYear())}|${result.nationality}}}` : `{{flagmedalist|${result.competitor.name}|${result.nationality}}}<br>${(await Promise.all(result.competitor.teamMembers.map(async tm => await getTitle(tm.id, tm.name)))).join('<br>')}`} || ${result.mark + (isField && pl ? ' m' : '')}\n`;
}
medalRows.push(medalRow);
}
}
}
let out = '';
for (const gen of [`Men's `, `Women's `]) {
out += `===${gen.replace(`'s `, '')}===\n{| {{MedalistTable|type=Event|columns=2}}\n`;
out += medalRows.filter(row => row.includes(gen)).map(row => row.replace(gen, '')).sort((a, b) => {
const evtOrder = ['100m', '200m', '400m', '800m', '1500m', '5000m', '10,000m', 'Marathon', '110mH', '100mH', '400mH', '3000mSC'].reverse();
const bScore = evtOrder.findIndex(evt => b.includes(evt) && !b.includes('x' + evt));
const aScore = evtOrder.findIndex(evt => a.includes(evt) && !a.includes('x' + evt));
if (bScore === -1 && aScore === -1) return a.localeCompare(b);
return bScore - aScore;
}).join('');
out += '|}\n';
}