Skip to main content
Loading...

More JavaScript Posts

//Disclaimer: I do not condone mass web scraping of ESPN websites, and this is just theoretical code that hasn't been used.

//Scrape player stats off of https://www.espn.com/nfl/team/roster/_/name/phi/philadelphia-eagles

//Example player JS object
/*
{
	"shortName": "S. Opeta",
	"name": "Sua Opeta",
	"href": "http://www.espn.com/nfl/player/_/id/3121009/sua-opeta",
	"uid": "s:20~l:28~a:3121009",
	"guid": "dec19157-e984-f981-3724-498281328c97",
	"id": "3121009",
	"height": "6' 4\"",
	"weight": "305 lbs",
	"age": 27,
	"position": "G",
	"jersey": "78",
	"birthDate": "08/15/96",
	"headshot": "https://a.espncdn.com/i/headshots/nfl/players/full/3121009.png",
	"lastName": "Sua Opeta",
	"experience": 4,
	"college": "Weber State"
}
*/

//The page needs to be focused, so you have to put this code in a bookmarklet and click the page before clicking it. Allegedly.
navigator.clipboard.writeText(JSON.stringify(
    window["__espnfitt__"].page.content.roster.groups.flatMap((group)=>{
        return group.athletes.map((athlete)=>{
            //We can assume all football players are above 100 lbs and below 1000 lbs.
            athlete.weight = parseInt(athlete.weight.substring(0,3));
            
            if(athlete.experience == "R") athlete.experience = 0;
            else athlete.experience = parseInt(athlete.experience);

            //We can assume players are at least 1 foot, or under 10 feet tall.
            athlete.inches = 12 * parseInt(athlete.height.substring(0, 1)); //Add feet in inches
            athlete.inches += parseInt(athlete.height.substring(2).replaceAll("\"", "").trim()); //Add remaining inches

            const monthDayYear = athlete.birthDate.split("/");
            athlete.birthMonth = parseInt(monthDayYear[0]);
            athlete.birthDay = parseInt(monthDayYear[1]);
            athlete.birthYear = parseInt(monthDayYear[2]);
            
            //The only really useful stuff we get from this is Height, weight, left handedness, age, position, and birthday
            return athlete;
        });
    })
, null, "\t")).then(null, ()=>{alert("That failed.")});

//Changes all the team links on this page https://www.espn.com/nfl/stats/team to the team's roster for easier scraping
$$("table > tbody > tr > td > div > div > a").forEach((elm)=>{
	elm.setAttribute("href", elm.getAttribute("href").replace("team/", "team/roster/"));
});
const rectSize = 15; // Size of the color box
        const spacing = 5; // Space between legend items
        let legend = canvas.append('g').attr('class','chart-legend')
        .attr('transform', `translate(${0}, ${height + 3})`);

        // Sample data for the legend
        const legendData = [
            { label: 'low', color: '#1f77b4' },
            { label: 'medium', color: '#ff7f0e' },
            { label: 'high', color: '#2ca02c' },
            { label: 'very high', color: '#2ca02c' }
        ];


        legend.selectAll('g')
            .data(legendData)
            .join('g')
            .attr('class', 'legend-item')
            
            .each(function (d, i) {
                const legendItem = d3.select(this);
                console.log(d.label)
                console.log(d.label?.length)
                let labelWidth = (d.label.length * 5) + 7 + 15
                

                var bbox = legendItem.node().getBBox()
                var width = bbox.width
                console.log(width)
                
                // Add rectangle for color
                legendItem.append('rect')
                    .attr('width', rectSize)
                    .attr('height', rectSize)
                    .attr('fill', d.color);
                    
                    // Add label
                    legendItem.append('text')
                    .attr('x', rectSize + spacing) // Position text to the right of the rectangle
                    .attr('y', rectSize / 2) // Align text vertically with the rectangle
                    .attr('dy', '.35em') // Adjust vertical alignment
                    .attr('text-anchor', 'start') // Align text to the start
                    .text(d.label)
                    .attr('fill', 'black'); // Optional: Text color
                // legendItem.attr('transform', `translate(${i * (rectSize + spacing + labelWidth)}, 0)`) // Position each item horizontally
            });

            const legendItems_spacing = 12
            var legendLength = 0
            legend.selectAll('.legend-item').each(function(d,i) {
              d3.select(this).attr('transform',function() {
                var bbox = d3.select(this).node().getBBox()
                var width = bbox.width
                // if (width == 0) width = (d.text.length * 5) +15  // not in dom yet
                let startingPosition = 0 
                if (i > 0) {
                    startingPosition = legendLength
                }
                legendLength += width + legendItems_spacing
                
                // return `translate(${xx - (width + 30)},0$)`
                return `translate(${startingPosition},0)`
            })
            })
results =
{
 "11345240":"Media Comment-80e47004-1719-493a-af69-a43b2652b41c",
 "11345282":"zoom_3_22_21.mp4",
 "11345303":"Recording-2.m4a",
 "11345348":"February 12%2C 2021.mp4",
 "11345826":"Lecture_Rousso_Chapter_06-2.pptx",
 "11345933":"Themes_InClassAssigment.pptx",
 "11346193":"Agenda – Week of March23.pptx",
 "11346318":"MUJS 2370 SP2021 Updated Rhythm Changes Drills Bb - Score.pdf",
 "11346319":"MUJS 2370 SP2021 Updated Rhythm Changes Drills C - Score.pdf",
 "11346322":"MUJS 2370 SP2021 Updated Rhythm Changes Drills Eb - Score.pdf",
 "11346323":"MUJS 2370 Updated Rhythm Changes Drills Bass - Score.pdf",
 "11346381":"Anthropology with colored highlighter analysis - Mar 22 2021 - 10-36 AM.pdf",
 "11346449":"Anthropology with colored highlighter analysis - Mar 22 2021 - 10-36 AM.pdf",
 "11346450":"MUJS 2370 SP2021 Updated Rhythm Changes Drills Bb - Score.pdf",
 "11346451":"MUJS 2370 SP2021 Updated Rhythm Changes Drills C - Score.pdf",
 "11346453":"MUJS 2370 SP2021 Updated Rhythm Changes Drills Eb - Score.pdf",
 "11346454":"MUJS 2370 Updated Rhythm Changes Drills Bass - Score.pdf",
 "11346495":"Steinel chromatic ornamentation étude rhythm changes - Aug 23 2020 - 12-19 PM.pdf",
 "11346532":"Media Comment-136242b4-7f2c-4d5d-8aa4-091f07e797cf",
 "11346581":"2019 Trust Based Relationship Intervention Manual.pdf",
 "11346584":"TBRI ppt.pptx",
 "11346593":"Trauma informed care.ppt"
}

function NewTab(testing) {
  window.open(testing, "_blank");
}

for(test in results) {
  var testing = 'https://unt.instructure.com/files/' + test + '/download';
  NewTab(testing);
  //window.open = 'https://unt.instructure.com/files/' + test + '/download';
}
//Retries maxRetries number of times, meaning that if you have 0, then it'll run the function once.
async function retryPromise(promiseFn, maxRetries = 3, delayOffset = 0, delayRandomRange = 0) {
	return new Promise((resolve, reject) => {
		promiseFn()
			.then(resolve, (error) => { //On error
				if (maxRetries <= 0) {
					return reject(error);
				} else {
					if(delayRandomRange * Math.random() + delayOffset < 1.0){
						return retryPromise(promiseFn, maxRetries - 1, delayOffset, delayRandomRange).then(resolve, reject);
					}else{
						return new Promise((resolveTwo, rejectTwo) => {
							setTimeout(() => {
								return retryPromise(promiseFn, maxRetries - 1, delayOffset, delayRandomRange).then(resolveTwo, rejectTwo);
							}, delayRandomRange * Math.random() + delayOffset);
						}).then(resolve, reject);
					}
				}
			});
	});
}

//Tests for that function.

//Returns a function that will fail numTimes times, then will return aValue
function functionThatReturnsAFunctionThatFailsACoupleTimes(numTimes, aValue){
	return async function(){
		if(numTimes == 0){
			return aValue;
		}
		numTimes--;
		throw false;
	};
}

const SMALL_NUMBER = 10;

function testTest(failNumberOfTimes){

	let functionThatFailsACoupleTimes = functionThatReturnsAFunctionThatFailsACoupleTimes(failNumberOfTimes, true);

	//Test my test
	for(let i = 0; i < (failNumberOfTimes * 2); ++i){
		function evalTest(val){
			let testTestFailedReason = "";
			if(val === undefined){
				testTestFailedReason = "Test test returned undefined for some reason.";
			}
			if((i + 1) > failNumberOfTimes){
				if(val === true){
					//We're good
				}else{
					testTestFailedReason = "Test test didn't return true when it should have.";
				}
			}else{
				if(val === false){
					//We're good
				}else{
					testTestFailedReason = "Test test didn't return false when it should have.";
				}
			}
			testTestFailedReason = testTestFailedReason || "Test test passed test case";
			console.log(testTestFailedReason, "at index", i, "where the function returned", val);
		}
		functionThatFailsACoupleTimes().then(evalTest, evalTest)
	};
}

testTest(SMALL_NUMBER);

let testCaseCounter = 1;

const throwsNoError = [
	(val)=>{
		if(val == true){
			console.log("Passed test case " + (testCaseCounter++));
		}else{
			console.error("Unexpected return value", val);
		}
	},
	()=>{
		console.error("It wasn't supposed to fail!")
	}
]

const throwsError = [
	(val)=>{
		console.error("It wasn't supposed to succeed!", val);
	},
	(val)=>{
		if(val == false){
			console.log("Passed test case " + (testCaseCounter++));
		}else{
			console.error("Unexpected return value", val);
		}
	}
];

//Runs SMALL_NUMBER times, because SMALL_NUMBER - 1 is the number of retries after the first one
let functionThatFailsACoupleTimes = functionThatReturnsAFunctionThatFailsACoupleTimes(SMALL_NUMBER - 1, true);

retryPromise(functionThatFailsACoupleTimes, SMALL_NUMBER - 1).then(...throwsNoError)

functionThatFailsACoupleTimes = functionThatReturnsAFunctionThatFailsACoupleTimes(SMALL_NUMBER - 1, true);

//Runs SMALL_NUMBER - 1 times, because SMALL_NUMBER - 2 is the number of retries after the first one
retryPromise(functionThatFailsACoupleTimes, SMALL_NUMBER - 2).then(...throwsError)

//Testing the delay. You'll have to wait a bit too.

functionThatFailsACoupleTimes = functionThatReturnsAFunctionThatFailsACoupleTimes(SMALL_NUMBER - 1, true);

//Runs SMALL_NUMBER times, because SMALL_NUMBER - 1 is the number of retries after the first one
retryPromise(functionThatFailsACoupleTimes, SMALL_NUMBER - 1, 500, 500).then(...throwsNoError)

functionThatFailsACoupleTimes = functionThatReturnsAFunctionThatFailsACoupleTimes(SMALL_NUMBER - 1, true);

//Runs SMALL_NUMBER - 1 times, because SMALL_NUMBER - 2 is the number of retries after the first one
retryPromise(functionThatFailsACoupleTimes, SMALL_NUMBER - 2, 500, 500).then(...throwsError)