Jump to content

MediaWiki:Gadget-resign-calculator.js: Difference between revisions

From 1TC Rules
Re-sign calculator
 
Omit blank names, Points wording, copy button
Line 32: Line 32:
nonPositive: capped <= 0
nonPositive: capped <= 0
};
};
}
/** A monospace post block with a copy button. */
function postBlock( text ) {
var wrap = document.createElement( 'div' );
wrap.className = 'tc-calc-postwrap';
var pre = document.createElement( 'div' );
pre.className = 'tc-calc-post';
pre.textContent = text;
wrap.appendChild( pre );
var btn = document.createElement( 'button' );
btn.className = 'tc-calc-copy';
btn.type = 'button';
btn.textContent = 'Copy';
btn.addEventListener( 'click', function () {
function done( ok ) {
btn.textContent = ok ? 'Copied' : 'Press \u2318C / Ctrl+C';
setTimeout( function () { btn.textContent = 'Copy'; }, 2000 );
}
if ( navigator.clipboard && navigator.clipboard.writeText ) {
navigator.clipboard.writeText( text ).then( function () { done( true ); },
function () { selectFallback( pre ); done( false ); } );
} else {
selectFallback( pre );
done( false );
}
} );
wrap.appendChild( btn );
return wrap;
}
/** Clipboard API unavailable or refused: select the text so the reader can copy it. */
function selectFallback( el ) {
var range = document.createRange();
range.selectNodeContents( el );
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange( range );
}
}


Line 168: Line 209:
first = parseInt( season.value, 10 ),
first = parseInt( season.value, 10 ),
endYear = isNaN( first ) ? null : first + years - 1,
endYear = isNaN( first ) ? null : first + years - 1,
who = name.value.trim() || 'Player Name';
who = name.value.trim();


var post = document.createElement( 'div' );
var lines = [];
post.className = 'tc-calc-post';
if ( who ) {
post.textContent = who + '\n' +
lines.push( who );
'(' + scores.join( ', ' ) + ' = ' + ( Number.isInteger( r.average ) ? r.average : r.average.toFixed( 2 ) ) + ')\n' +
}
money( r.salary ) + ( endYear ? ' (' + endYear + ')' : ' (' + years + ' years)' );
lines.push( '(' + scores.join( ', ' ) + ' = ' +
out.appendChild( post );
( Number.isInteger( r.average ) ? r.average : r.average.toFixed( 2 ) ) + ')' );
lines.push( money( r.salary ) + ( endYear ? ' (' + endYear + ')' : ' (' + years + ' years)' ) );
out.appendChild( postBlock( lines.join( '\n' ) ) );


var note = document.createElement( 'div' );
var note = document.createElement( 'div' );

Revision as of 21:46, 23 September 2026

/**
 * 1TC RealGM — re-sign salary calculator.
 * Implements the formula on [[NBA:Re-Signing]]. If the two ever disagree,
 * the wiki page is the rule and this script is the bug.
 */
( function () {
	'use strict';

	var MAX = 11500000,
		DISCOUNT = 1500000,
		CUTOFF = 85;

	function money( n ) {
		return '$' + n.toLocaleString( 'en-US' );
	}

	/** The seven steps, in the order the rules state them. */
	function calculate( scores ) {
		var avg = scores.reduce( function ( a, b ) { return a + b; }, 0 ) / scores.length,
			raw = ( avg / 100 ) * MAX,
			rounded = Math.round( raw / 100000 ) * 100000,
			discounted = avg < CUTOFF ? rounded - DISCOUNT : rounded,
			capped = Math.min( discounted, MAX );

		return {
			average: avg,
			raw: raw,
			rounded: rounded,
			discountApplied: avg < CUTOFF,
			salary: capped,
			cappedAtMax: discounted > MAX,
			nonPositive: capped <= 0
		};
	}


	/** A monospace post block with a copy button. */
	function postBlock( text ) {
		var wrap = document.createElement( 'div' );
		wrap.className = 'tc-calc-postwrap';

		var pre = document.createElement( 'div' );
		pre.className = 'tc-calc-post';
		pre.textContent = text;
		wrap.appendChild( pre );

		var btn = document.createElement( 'button' );
		btn.className = 'tc-calc-copy';
		btn.type = 'button';
		btn.textContent = 'Copy';
		btn.addEventListener( 'click', function () {
			function done( ok ) {
				btn.textContent = ok ? 'Copied' : 'Press \u2318C / Ctrl+C';
				setTimeout( function () { btn.textContent = 'Copy'; }, 2000 );
			}
			if ( navigator.clipboard && navigator.clipboard.writeText ) {
				navigator.clipboard.writeText( text ).then( function () { done( true ); },
					function () { selectFallback( pre ); done( false ); } );
			} else {
				selectFallback( pre );
				done( false );
			}
		} );
		wrap.appendChild( btn );
		return wrap;
	}

	/** Clipboard API unavailable or refused: select the text so the reader can copy it. */
	function selectFallback( el ) {
		var range = document.createRange();
		range.selectNodeContents( el );
		var sel = window.getSelection();
		sel.removeAllRanges();
		sel.addRange( range );
	}

	function build( container ) {
		var scoreInputs = [];

		var form = document.createElement( 'div' );
		form.className = 'tc-calc';

		// --- Fantrax scores ---
		var scoreRow = document.createElement( 'div' );
		scoreRow.className = 'tc-calc-row tc-scores';
		var scoreLabel = document.createElement( 'label' );
		scoreLabel.textContent = 'Fantrax scores';
		scoreRow.appendChild( scoreLabel );
		for ( var i = 0; i < 3; i++ ) {
			var input = document.createElement( 'input' );
			input.type = 'number';
			input.step = '0.01';
			input.min = '0';
			input.placeholder = [ 'season 1', 'season 2', 'season 3' ][ i ];
			scoreRow.appendChild( input );
			scoreInputs.push( input );
		}
		form.appendChild( scoreRow );

		var hint = document.createElement( 'div' );
		hint.className = 'tc-calc-note';
		hint.textContent = 'Only seasons with 40+ games played count. If he has never had three, ' +
			'fill in the ones he did reach 40 games in and leave the rest blank.';
		form.appendChild( hint );

		// --- term ---
		var termRow = document.createElement( 'div' );
		termRow.className = 'tc-calc-row';
		var termLabel = document.createElement( 'label' );
		termLabel.textContent = 'Contract length';
		var term = document.createElement( 'select' );
		[ 3, 4, 5 ].forEach( function ( y ) {
			var o = document.createElement( 'option' );
			o.value = String( y );
			o.textContent = y + ' years';
			term.appendChild( o );
		} );
		termRow.appendChild( termLabel );
		termRow.appendChild( term );
		form.appendChild( termRow );

		// --- optional first season, to generate the contract line ---
		var seasonRow = document.createElement( 'div' );
		seasonRow.className = 'tc-calc-row';
		var seasonLabel = document.createElement( 'label' );
		seasonLabel.textContent = 'First season (optional)';
		var season = document.createElement( 'input' );
		season.type = 'number';
		season.step = '1';
		season.placeholder = 'e.g. 2028';
		seasonRow.appendChild( seasonLabel );
		seasonRow.appendChild( season );
		form.appendChild( seasonRow );

		// --- player name, for the post block ---
		var nameRow = document.createElement( 'div' );
		nameRow.className = 'tc-calc-row';
		var nameLabel = document.createElement( 'label' );
		nameLabel.textContent = 'Player (optional)';
		var name = document.createElement( 'input' );
		name.type = 'text';
		name.placeholder = 'Larry Bird';
		name.style.width = '14em';
		nameRow.appendChild( nameLabel );
		nameRow.appendChild( name );
		form.appendChild( nameRow );

		var out = document.createElement( 'div' );
		out.className = 'tc-calc-out';
		form.appendChild( out );

		function render() {
			var scores = scoreInputs
				.map( function ( el ) { return el.value.trim() === '' ? null : Number( el.value ); } )
				.filter( function ( v ) { return v !== null && !isNaN( v ); } );

			out.textContent = '';

			if ( !scores.length ) {
				var empty = document.createElement( 'p' );
				empty.className = 'tc-calc-note';
				empty.textContent = 'Enter at least one Fantrax score.';
				out.appendChild( empty );
				return;
			}

			var r = calculate( scores );

			var big = document.createElement( 'div' );
			big.className = 'tc-calc-big';
			big.textContent = money( r.salary );
			out.appendChild( big );

			var steps = document.createElement( 'ol' );
			steps.className = 'tc-calc-steps';
			[
				'Average of ' + scores.length + ' score' + ( scores.length === 1 ? '' : 's' ) +
					': ' + r.average.toFixed( 2 ),
				'× ' + money( MAX ) + ' = ' + money( Math.round( r.raw ) ),
				'Rounded to nearest $100,000 = ' + money( r.rounded ),
				r.discountApplied ?
					'Average is below ' + CUTOFF + ', so − ' + money( DISCOUNT ) + ' = ' + money( r.rounded - DISCOUNT ) :
					'Average is ' + CUTOFF + ' or above, so no discount'
			].forEach( function ( t ) {
				var li = document.createElement( 'li' );
				li.textContent = t;
				steps.appendChild( li );
			} );
			out.appendChild( steps );

			if ( r.cappedAtMax ) {
				var cap = document.createElement( 'div' );
				cap.className = 'tc-calc-warn';
				cap.textContent = 'Capped at the ' + money( MAX ) + ' maximum re-sign contract.';
				out.appendChild( cap );
			}

			if ( r.nonPositive ) {
				var warn = document.createElement( 'div' );
				warn.className = 'tc-calc-warn';
				warn.textContent = 'The formula produces ' + money( r.salary ) + ', which is not a usable ' +
					'salary. The rules do not say what happens below this point — ask TAB before posting.';
				out.appendChild( warn );
				return;
			}

			// --- ready-to-paste post ---
			var years = Number( term.value ),
				first = parseInt( season.value, 10 ),
				endYear = isNaN( first ) ? null : first + years - 1,
				who = name.value.trim();

			var lines = [];
			if ( who ) {
				lines.push( who );
			}
			lines.push( '(' + scores.join( ', ' ) + ' = ' +
				( Number.isInteger( r.average ) ? r.average : r.average.toFixed( 2 ) ) + ')' );
			lines.push( money( r.salary ) + ( endYear ? ' (' + endYear + ')' : ' (' + years + ' years)' ) );
			out.appendChild( postBlock( lines.join( '\n' ) ) );

			var note = document.createElement( 'div' );
			note.className = 'tc-calc-note';
			note.textContent = endYear ?
				years + ' years, ' + first + ' through ' + endYear + '. Post this in the NBA: Re-Signing forum.' :
				'Add a first season above to get the contract’s end year.';
			out.appendChild( note );
		}

		[].concat( scoreInputs, [ term, season, name ] ).forEach( function ( el ) {
			el.addEventListener( 'input', render );
			el.addEventListener( 'change', render );
		} );

		container.appendChild( form );
		render();
	}

	mw.hook( 'wikipage.content' ).add( function () {
		var el = document.getElementById( 'tc-resign-calculator' );
		if ( el && !el.dataset.tcBuilt ) {
			el.dataset.tcBuilt = '1';
			build( el );
		}
	} );
}() );