User:Habst/makeTeam.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Habst/makeTeam.
//<nowiki> for (const gen in res) for (const meet in res[gen]) for (const evt in res[gen][meet]) if (!res[gen][meet][evt]?.body.innerHTML.length) { console.log('deleting', gen, meet, evt); delete res[gen][meet][evt]; }
(async () => {
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));
}
const collegeName = 'United States Air Force Academy';
const teamKey = 'Air Force';
const teamName = 'Air Force Falcons';
const nickname = 'Falcons';
const conference = 'Mountain West Conference';
const confAbbr = 'MW';
const coach = '[[Ryan Cole]]';
const location = 'Air Force Academy, Colorado';
const stateabb = 'CO';
const track = 'Cadet Outdoor Track Complex';
const mediaGuide = 'https://s3.us-east-2.amazonaws.com/sidearm.nextgen.sites/goairforcefalcons.com/documents/2025/12/6/Record_Book_for_2026.pdf';
const coachRef = 'https://goairforcefalcons.com/sports/track-and-field/roster/coaches/ryan-cole/2714';
const website = 'https://goairforcefalcons.com/sports/track-and-field';
const dom = async url => {
let r = await fetch(url);
while (!r.ok) {
console.log('retrying');
r = await fetch(url);
};
if (!r.ok) throw Error();
return new DOMParser().parseFromString(await r.text(), 'text/html');
}
const uniq = arr => arr.filter(x => !x.isRelay).reduce((acc, x) => acc.find(ath => ath.names[0] === x.names[0]) ? acc : [...acc, x], []);
const fixName = fullName => {
fullName = fullName.split(' ').filter(x => [...x].some(c => !'0123456789.:'.includes(c))).join(' ');
const fname = fullName?.split(' ').filter(n => n !== n.toUpperCase()).join(' ');
const lname = nameFixer.nameFixer(fullName.split(' ').filter(n => n === n.toUpperCase()).join(' '));
const fixedName = `${fname} ${lname}`.trim();
return fixedName ? `[[${fixedName}]]` : '';
}
const getRelay = s => {
const members = s ? s.replaceAll(/\(.+?(,|$)/g, ',').split(',').map(n => fixName(n.trim())).filter(x => x) : [];
while (members.length < 4) members.push('');
return members;
}
const refCache = {};
const getRef = (path, title) => {
if (refCache[title]) return `<ref name="${title}" />`;
let suffix = '';
if (title.includes('relay') && title.includes('Outdoor')) {
const evtAbbr = title.includes('110') || title.includes('100') ? '4x1' : '4x4';
const evtGen = title.toLowerCase().includes('women') ? 'w' : 'm';
suffix = ` {{Cite web |url=https://trackandfieldnews.com/wp-content/uploads/2018/06/ncaa${evtGen}${evtAbbr}.pdf |title=Track and Field News NCAA Outdoor ${evtGen}${evtAbbr} |website=[[Track and Field News]] |access-date=28 December 2024}}`;
}
refCache[title] = `<ref name="${title}">{{cite web |url=${'https://www.ustfccca.org' + path} |title=${title} |website=[[USTFCCCA]] |access-date=28 December 2024}}${suffix}</ref>`;
return refCache[title];
}
const state = location.split(', ').at(-1);
window.trpc ??= await dom('/team-rankings-polls-central/polls-and-rankings-week-by-week?pritype=515&tm=0');
const teams = Object.fromEntries([...trpc.querySelectorAll('div.menu')[3].querySelectorAll('a.item')].map(a => [a.innerText, a.href.split('&tm=')[1].split('&')[0]]));
const id = teams[teamKey];
if (!id) throw Error("no teamKey");
window.mhbe ??= await dom('/records-lists/meet-history-by-event?series=3369');
window.res ??= {};
const exclude = {
3369: ['55 meters', '55 meters hurdles', '60 meters', '60 meters hurdles', 'Distance medley relay', 'Shuttle hurdle relay', 'Weight throw', '3000m race walk', '5000m race walk', '4 × 1500 meters relay'],
3368: ['100 meters', '100 meters hurdles', '110 meters hurdles', '300 meters hurdles', '400 meters hurdles', '2000 meters steeplechase', '3000 meters steeplechase', '10,000 meters', '3000m race walk', '5000m race walk', '4 × 100 meters relay', 'Shuttle hurdle relay', 'Discus throw', 'Hammer throw', 'Javelin throw', 'Decathlon', 'Sprint medley relay', '4 × 1500 meters relay'],
};
const fixEvt = e => (e[0] + e.slice(1).replace('Meter ', 'Meters ').replace('Yard ', 'Yards ').toLowerCase().replace('2000m', '2000 meters')).replace(/^Steeplechase$/, '3000 meters steeplechase').replace(/^Mile$/, 'Mile run').replace(/^(Discus|Hammer|Javelin)$/, '$1 throw').replace('4x', '4 × ').replace('0 relay', '0 meters relay').split(' (')[0].split(', ')[0];
const evts = Object.fromEntries([...mhbe.querySelectorAll('select[name=events] option')].slice(1).map(o => [fixEvt(o.innerText), o.value]));
let allAm = `{| class="wikitable sortable"
! colspan="6" style="{{CollegePrimaryStyle|${teamName}|border=0|color=white}}" | First team NCAA All-Americans
|-
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" | Team
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" | Championships
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" | Name
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" | Event
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" | Place
! scope="col" style="{{CollegeSecondaryStyle|${teamName}|border=0}}" class="unsortable" | {{Abbr|Ref.|References}}
|-\n`;
let allAths = [];
for (const gen of [1, 2]) {
res[gen] ??= {};
for (const meet of [3368, 3369]) {
const env = meet === 3368 ? 'Indoor' : 'Outdoor';
res[gen][meet] ??= {};
for (const evt in evts) {
const isRelay = evt.includes('relay');
if (exclude[meet].includes(evt)) continue;
const eid = evts[evt];
const url = `/records-lists/meet-history-by-event?gender=${gen}&series=${meet}&event=${eid}`;
console.log(gen, meet, evt, eid, url);
res[gen][meet][evt] ??= await dom(url);
if (!res[gen][meet][evt]) console.log('Error:', url);
const aths = [...res[gen][meet][evt].querySelectorAll(isRelay ? `img[title="${teamKey}"]` : `a[href$="=${id}"]`)].map(a => {
let row = a.parentElement.parentElement;
if (isRelay) row = row.parentElement;
const [pl, fullName] = row.querySelector('td span').innerText.slice(1).split(') ');
const names = isRelay ? getRelay(row.parentElement.querySelector('td[colspan]')?.innerText) : [fixName(fullName)];
const athLink = row.querySelector('td a')?.href;
const tbl = row.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
const year = tbl.querySelector('td').innerText.split(' ')[0];
return { pl, athLink, names, year, gen: gen === 1 ? "Men's" : "Women's", env, evt, isRelay, url };
});
allAths.push(...aths);
}
}
}
allAths = allAths.filter(a => +a.pl <= 8);
allAm += allAths.sort((a, b) => `${a.year} ${a.env}`.localeCompare(`${b.year} ${b.env}`)).map(a => {
const rs = a.isRelay ? 'rowspan=4|' : '';
const ref = getRef(a.url, `${a.evt} at the NCAA Division I ${a.gen} ${a.env} Track and Field Championships`);
let row = `|${rs} ${a.gen} ||${rs} {{tfch|NCAA|${a.year}|${a.env}}} || ${a.names[0]} ||${rs} [[${a.evt} at the NCAA Division I ${a.env} Track and Field Championships|${a.evt}]] ||${rs} ${+a.pl < 999 ? `{{ord|${a.pl}}}` : `{{AthAbbr|DNF}}`} ||${rs}${ref}`;
if (a.isRelay) row += '\n|-\n' + a.names.slice(1).map(n => `| ${n}`).join('\n|-\n');
return row;
}).join('\n|-\n') + '\n';
allAm += '|}';
const out = `{{Short description|American college track and field team}}
{{for|information on all ${collegeName} sports|${teamName}}}
{{use dmy dates|date=December 2024}}
{{Infobox college track and field team
|name=${teamName} track and field
|nickname=[[${teamName}|${nickname}]]
|conference_indoor=
|NCAAindoortourneys=
|NCAAoutdoortourneys=
|NCAAindoorchampion=<!--<ref>{{cite web |title=NCAA Division I Indoor Championships ::: USTFCCCA InfoZone: Meet History ::: USTFCCCA |url=https://www.ustfccca.org/meets-results/meet-history?series=3368 |website=[[USTFCCCA]] |access-date=29 December 2024}}</ref>-->
|NCAAoutdoorchampion=<!--<ref>{{cite web |title=NCAA Division I Outdoor Championships ::: USTFCCCA InfoZone: Meet History ::: USTFCCCA |url=https://www.ustfccca.org/meets-results/meet-history?series=3369 |website=[[USTFCCCA]] |access-date=29 December 2024}}</ref>-->
|outdoortrack=[[${track}]]
|logo=${teamName} logo.svg
|indoortrack=
|stateabb=${stateabb}
|state=${state}
|location=[[${location}]]
|division=
|conference_short=${confAbbr}
|conference=${conference}
|tenure=
|coach=${coach}
|athletic_director=
|university=${collegeName}
|founded=
|logo_size=200
|conference_outdoor=
}}
The '''${teamName} track and field''' team is the [[track and field]] program that represents [[${collegeName}]]. The ${nickname} compete in [[NCAA Division I]] as a member of the [[${conference}]]. The team is based in [[${location}]], at the [[${track}]].<ref name=mg>{{Cite web |title=${teamName} Media Guide |url=${mediaGuide} |access-date=27 December 2024 |website=${collegeName} Athletics |language=en}}</ref>
The program is coached by ${coach.split('<br>').join(' and ')}.${coachRef ? `<ref>{{Cite web|url=${coachRef}|title=${teamName} Track and Field Coach|website=${collegeName} Athletics|access-date=27 December 2024}}</ref>` : '<ref name=mg />'} The track and field program officially encompasses four teams because the NCAA considers men's and women's indoor track and field and outdoor track and field as separate sports.<ref>{{Cite web|url=https://www.ncaa.com/news/trackfield-outdoor-women/article/2024-12-05/here-are-differences-between-indoor-and-outdoor-track-seasons|title=Here are the differences between the indoor and outdoor track seasons|website=ncaa.com|access-date=27 December 2024}}</ref>
==Postseason==
{{As of|August 2025}}, a total of ${uniq(allAths.filter(a => a.gen === "Men's")).length} men and ${uniq(allAths.filter(a => a.gen === "Women's")).length} women have achieved individual first-team [[All-America#Track and field|All-America]]n status for the team at the Division I [[NCAA Division I Men's Outdoor Track and Field Championships|men's outdoor]], [[NCAA Division I Women's Outdoor Track and Field Championships|women's outdoor]], [[NCAA Division I Men's Indoor Track and Field Championships|men's indoor]], or [[NCAA Division I Women's Indoor Track and Field Championships|women's indoor]] national championships (using the modern criteria of top-8 placing regardless of athlete nationality).<ref>{{Cite web|url=https://www.ustfccca.org/records-lists/meet-history-by-event?series=3368|title=USTFCCCA InfoZone: Meet History by Event: NCAA Division I Indoor Championships|website=[[USTFCCCA]]|access-date=27 December 2024}}</ref><ref>{{Cite web|url=https://www.ustfccca.org/records-lists/meet-history-by-event?series=3369|title=USTFCCCA InfoZone: Meet History by Event: NCAA Division I Outdoor Championships|website=[[USTFCCCA]]|access-date=27 December 2024}}</ref>
${allAm}
==See also==
* {{clc|${teamName} men's track and field athletes}}
* {{clc|${teamName} women's track and field athletes}}
==References==
{{reflist}}
==External links==
${website.includes('/mens') || website.includes('/m-') ? `* [${website} Men's official website]\n* [${website.replace('/mens', '/womens').replace('/m-', '/w-')} Women's official website]` : `* {{official website|${website}}}`}
{{${conference} track and field navbox}}
{{${collegeName}}}
{{DEFAULTSORT:${teamName} track and field}}
[[Category:${teamName} track and field| ]]
[[Category:${conference} track and field]]
[[Category:College track and field teams in the United States]]
[[Category:Women's sports in ${state}]]
`
console.log(out);
out;
})();
//</nowiki>